terminal-pilot 0.0.5 → 0.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/README.md +40 -0
  2. package/dist/ansi.js +68 -63
  3. package/dist/ansi.js.map +7 -0
  4. package/dist/cli.d.ts +2 -0
  5. package/dist/cli.js +6254 -0
  6. package/dist/cli.js.map +7 -0
  7. package/dist/commands/close-session.d.ts +12 -0
  8. package/dist/commands/close-session.js +1509 -0
  9. package/dist/commands/close-session.js.map +7 -0
  10. package/dist/commands/create-session.d.ts +26 -0
  11. package/dist/commands/create-session.js +1516 -0
  12. package/dist/commands/create-session.js.map +7 -0
  13. package/dist/commands/fill.d.ts +10 -0
  14. package/dist/commands/fill.js +1512 -0
  15. package/dist/commands/fill.js.map +7 -0
  16. package/dist/commands/get-session.d.ts +18 -0
  17. package/dist/commands/get-session.js +1514 -0
  18. package/dist/commands/get-session.js.map +7 -0
  19. package/dist/commands/index.d.ts +242 -0
  20. package/dist/commands/index.js +3973 -0
  21. package/dist/commands/index.js.map +7 -0
  22. package/dist/commands/install.d.ts +20 -0
  23. package/dist/commands/install.js +1480 -0
  24. package/dist/commands/install.js.map +7 -0
  25. package/dist/commands/installer.d.ts +43 -0
  26. package/dist/commands/installer.js +289 -0
  27. package/dist/commands/installer.js.map +7 -0
  28. package/dist/commands/list-sessions.d.ts +16 -0
  29. package/dist/commands/list-sessions.js +1513 -0
  30. package/dist/commands/list-sessions.js.map +7 -0
  31. package/dist/commands/press-key.d.ts +10 -0
  32. package/dist/commands/press-key.js +1512 -0
  33. package/dist/commands/press-key.js.map +7 -0
  34. package/dist/commands/read-history.d.ts +16 -0
  35. package/dist/commands/read-history.js +1511 -0
  36. package/dist/commands/read-history.js.map +7 -0
  37. package/dist/commands/read-screen.d.ts +21 -0
  38. package/dist/commands/read-screen.js +1515 -0
  39. package/dist/commands/read-screen.js.map +7 -0
  40. package/dist/commands/resize.d.ts +12 -0
  41. package/dist/commands/resize.js +1512 -0
  42. package/dist/commands/resize.js.map +7 -0
  43. package/dist/commands/runtime.d.ts +36 -0
  44. package/dist/commands/runtime.js +1262 -0
  45. package/dist/commands/runtime.js.map +7 -0
  46. package/dist/commands/screenshot.d.ts +14 -0
  47. package/dist/commands/screenshot.js +2280 -0
  48. package/dist/commands/screenshot.js.map +7 -0
  49. package/dist/commands/send-signal.d.ts +10 -0
  50. package/dist/commands/send-signal.js +1512 -0
  51. package/dist/commands/send-signal.js.map +7 -0
  52. package/dist/commands/type.d.ts +10 -0
  53. package/dist/commands/type.js +1512 -0
  54. package/dist/commands/type.js.map +7 -0
  55. package/dist/commands/uninstall.d.ts +14 -0
  56. package/dist/commands/uninstall.js +529 -0
  57. package/dist/commands/uninstall.js.map +7 -0
  58. package/dist/commands/wait-for-exit.d.ts +14 -0
  59. package/dist/commands/wait-for-exit.js +1513 -0
  60. package/dist/commands/wait-for-exit.js.map +7 -0
  61. package/dist/commands/wait-for.d.ts +20 -0
  62. package/dist/commands/wait-for.js +1520 -0
  63. package/dist/commands/wait-for.js.map +7 -0
  64. package/dist/exports.compile-check.js +1 -1
  65. package/dist/exports.compile-check.js.map +7 -0
  66. package/dist/index.js +1118 -5
  67. package/dist/index.js.map +7 -0
  68. package/dist/keys.js +58 -49
  69. package/dist/keys.js.map +7 -0
  70. package/dist/templates/terminal-pilot.md +45 -0
  71. package/dist/terminal-buffer.d.ts +9 -1
  72. package/dist/terminal-buffer.js +525 -425
  73. package/dist/terminal-buffer.js.map +7 -0
  74. package/dist/terminal-pilot.js +1105 -35
  75. package/dist/terminal-pilot.js.map +7 -0
  76. package/dist/terminal-screen.js +108 -26
  77. package/dist/terminal-screen.js.map +7 -0
  78. package/dist/terminal-session.js +1020 -292
  79. package/dist/terminal-session.js.map +7 -0
  80. package/dist/testing/cli-repl.d.ts +15 -0
  81. package/dist/testing/cli-repl.js +6359 -0
  82. package/dist/testing/cli-repl.js.map +7 -0
  83. package/dist/testing/qa-cli.d.ts +1 -0
  84. package/dist/testing/qa-cli.js +7276 -0
  85. package/dist/testing/qa-cli.js.map +7 -0
  86. package/package.json +37 -7
@@ -0,0 +1,2280 @@
1
+ // ../cmdkit/src/index.ts
2
+ import { fileURLToPath } from "node:url";
3
+
4
+ // ../cmdkit-schema/src/index.ts
5
+ function assertValidEnumValues(values) {
6
+ if (values.length === 0) {
7
+ throw new Error("Enum schema requires at least one value");
8
+ }
9
+ const uniqueValues = new Set(values);
10
+ if (uniqueValues.size !== values.length) {
11
+ throw new Error("Enum schema values must be unique");
12
+ }
13
+ }
14
+ var S = {
15
+ String(options = {}) {
16
+ return {
17
+ kind: "string",
18
+ ...options
19
+ };
20
+ },
21
+ Number(options = {}) {
22
+ return {
23
+ kind: "number",
24
+ ...options
25
+ };
26
+ },
27
+ Boolean(options = {}) {
28
+ return {
29
+ kind: "boolean",
30
+ ...options
31
+ };
32
+ },
33
+ Enum(values, options = {}) {
34
+ assertValidEnumValues(values);
35
+ return {
36
+ kind: "enum",
37
+ values,
38
+ ...options
39
+ };
40
+ },
41
+ Array(item, options = {}) {
42
+ return {
43
+ kind: "array",
44
+ item,
45
+ ...options
46
+ };
47
+ },
48
+ Object(shape) {
49
+ return {
50
+ kind: "object",
51
+ shape
52
+ };
53
+ },
54
+ Optional(inner) {
55
+ return {
56
+ kind: "optional",
57
+ inner
58
+ };
59
+ }
60
+ };
61
+
62
+ // ../cmdkit/src/index.ts
63
+ var commandConfigSymbol = /* @__PURE__ */ Symbol("cmdkit.command.config");
64
+ var commandSourcePathSymbol = /* @__PURE__ */ Symbol("cmdkit.command.sourcePath");
65
+ var UserError = class extends Error {
66
+ constructor(message) {
67
+ super(message);
68
+ this.name = "UserError";
69
+ }
70
+ };
71
+ function cloneScope(scope) {
72
+ return scope === void 0 ? void 0 : [...scope];
73
+ }
74
+ function cloneSecretDefinition(secret) {
75
+ return {
76
+ env: secret.env,
77
+ description: secret.description,
78
+ optional: secret.optional
79
+ };
80
+ }
81
+ function cloneSecrets(secrets) {
82
+ if (secrets === void 0) {
83
+ return {};
84
+ }
85
+ return Object.fromEntries(
86
+ Object.entries(secrets).map(([key, secret]) => [key, cloneSecretDefinition(secret)])
87
+ );
88
+ }
89
+ function cloneRequires(requires) {
90
+ if (requires === void 0) {
91
+ return void 0;
92
+ }
93
+ return {
94
+ auth: requires.auth,
95
+ apiVersion: requires.apiVersion,
96
+ check: requires.check
97
+ };
98
+ }
99
+ function parseStackPath(candidate) {
100
+ if (candidate.startsWith("file://")) {
101
+ try {
102
+ return fileURLToPath(candidate);
103
+ } catch {
104
+ return void 0;
105
+ }
106
+ }
107
+ if (candidate.startsWith("/")) {
108
+ return candidate;
109
+ }
110
+ return void 0;
111
+ }
112
+ function extractStackPath(line) {
113
+ const trimmed = line.trim();
114
+ const fileIndex = trimmed.indexOf("file://");
115
+ if (fileIndex >= 0) {
116
+ const location2 = trimmed.slice(fileIndex);
117
+ const separatorIndex2 = location2.lastIndexOf(":");
118
+ const previousSeparatorIndex2 = separatorIndex2 >= 0 ? location2.lastIndexOf(":", separatorIndex2 - 1) : -1;
119
+ const candidate2 = separatorIndex2 >= 0 && previousSeparatorIndex2 >= 0 ? location2.slice(0, previousSeparatorIndex2) : location2;
120
+ return parseStackPath(candidate2);
121
+ }
122
+ const slashIndex = trimmed.indexOf("/");
123
+ if (slashIndex < 0) {
124
+ return void 0;
125
+ }
126
+ const location = trimmed.slice(slashIndex);
127
+ const separatorIndex = location.lastIndexOf(":");
128
+ const previousSeparatorIndex = separatorIndex >= 0 ? location.lastIndexOf(":", separatorIndex - 1) : -1;
129
+ const candidate = separatorIndex >= 0 && previousSeparatorIndex >= 0 ? location.slice(0, previousSeparatorIndex) : location;
130
+ return parseStackPath(candidate);
131
+ }
132
+ function inferCommandSourcePath() {
133
+ const stack = new Error().stack;
134
+ if (typeof stack !== "string") {
135
+ return void 0;
136
+ }
137
+ for (const line of stack.split("\n").slice(1)) {
138
+ const candidate = extractStackPath(line);
139
+ if (candidate === void 0) {
140
+ continue;
141
+ }
142
+ if (candidate.includes("/packages/cmdkit/src/index.ts") || candidate.includes("/packages/cmdkit/dist/index.js")) {
143
+ continue;
144
+ }
145
+ return candidate;
146
+ }
147
+ return void 0;
148
+ }
149
+ function composeChecks(parentCheck, childCheck) {
150
+ if (parentCheck === void 0) {
151
+ return childCheck;
152
+ }
153
+ if (childCheck === void 0) {
154
+ return parentCheck;
155
+ }
156
+ return async (ctx) => {
157
+ const parentResult = await parentCheck(ctx);
158
+ if (!parentResult.ok) {
159
+ return parentResult;
160
+ }
161
+ return childCheck(ctx);
162
+ };
163
+ }
164
+ function mergeRequires(parent, child) {
165
+ if (parent === void 0 && child === void 0) {
166
+ return void 0;
167
+ }
168
+ const merged = {
169
+ auth: child?.auth ?? parent?.auth,
170
+ apiVersion: child?.apiVersion ?? parent?.apiVersion,
171
+ check: composeChecks(parent?.check, child?.check)
172
+ };
173
+ if (merged.auth === void 0 && merged.apiVersion === void 0 && merged.check === void 0) {
174
+ return void 0;
175
+ }
176
+ return merged;
177
+ }
178
+ function mergeSecrets(parent, child) {
179
+ return cloneSecrets({
180
+ ...parent,
181
+ ...child
182
+ });
183
+ }
184
+ function resolveCommandScope(ownScope, inheritedScope) {
185
+ return cloneScope(ownScope ?? inheritedScope) ?? ["cli", "sdk"];
186
+ }
187
+ function createBaseCommand(config) {
188
+ const command = {
189
+ kind: "command",
190
+ name: config.name,
191
+ description: config.description,
192
+ aliases: [...config.aliases ?? []],
193
+ positional: [...config.positional ?? []],
194
+ params: config.params,
195
+ secrets: cloneSecrets(config.secrets),
196
+ scope: resolveCommandScope(config.scope, void 0),
197
+ confirm: config.confirm ?? false,
198
+ requires: cloneRequires(config.requires),
199
+ handler: config.handler,
200
+ render: config.render
201
+ };
202
+ Object.defineProperty(command, commandConfigSymbol, {
203
+ value: {
204
+ scope: cloneScope(config.scope),
205
+ secrets: cloneSecrets(config.secrets),
206
+ requires: cloneRequires(config.requires),
207
+ sourcePath: inferCommandSourcePath()
208
+ }
209
+ });
210
+ return command;
211
+ }
212
+ function getInternalCommandConfig(command) {
213
+ return command[commandConfigSymbol];
214
+ }
215
+ function materializeCommand(command, inherited) {
216
+ const internal = getInternalCommandConfig(command);
217
+ const materialized = {
218
+ kind: "command",
219
+ name: command.name,
220
+ description: command.description,
221
+ aliases: [...command.aliases],
222
+ positional: [...command.positional],
223
+ params: command.params,
224
+ secrets: mergeSecrets(inherited.secrets, internal.secrets),
225
+ scope: resolveCommandScope(internal.scope, inherited.scope),
226
+ confirm: command.confirm,
227
+ requires: mergeRequires(inherited.requires, internal.requires),
228
+ handler: command.handler,
229
+ render: command.render
230
+ };
231
+ Object.defineProperty(materialized, commandConfigSymbol, {
232
+ value: {
233
+ scope: cloneScope(internal.scope),
234
+ secrets: cloneSecrets(internal.secrets),
235
+ requires: cloneRequires(internal.requires),
236
+ sourcePath: internal.sourcePath
237
+ }
238
+ });
239
+ Object.defineProperty(materialized, commandSourcePathSymbol, {
240
+ value: internal.sourcePath
241
+ });
242
+ return materialized;
243
+ }
244
+ function defineCommand(config) {
245
+ return materializeCommand(createBaseCommand(config), {
246
+ scope: void 0,
247
+ secrets: {},
248
+ requires: void 0
249
+ });
250
+ }
251
+
252
+ // ../terminal-png/src/index.ts
253
+ import { writeFile } from "node:fs/promises";
254
+
255
+ // ../terminal-png/src/ansi-parser.ts
256
+ var ESC = "\x1B";
257
+ function createDefaultStyle() {
258
+ return {
259
+ fg: null,
260
+ bg: null,
261
+ bold: false,
262
+ italic: false,
263
+ underline: false,
264
+ strikethrough: false,
265
+ dim: false
266
+ };
267
+ }
268
+ function cloneStyle(style) {
269
+ return { ...style };
270
+ }
271
+ function stylesEqual(left, right) {
272
+ return left.bold === right.bold && left.italic === right.italic && left.underline === right.underline && left.strikethrough === right.strikethrough && left.dim === right.dim && colorsEqual(left.fg, right.fg) && colorsEqual(left.bg, right.bg);
273
+ }
274
+ function colorsEqual(left, right) {
275
+ if (left === right) {
276
+ return true;
277
+ }
278
+ if (!left || !right || left.type !== right.type) {
279
+ return false;
280
+ }
281
+ if (left.type === "rgb" && right.type === "rgb") {
282
+ return left.r === right.r && left.g === right.g && left.b === right.b;
283
+ }
284
+ if ((left.type === "ansi4" || left.type === "ansi8") && left.type === right.type) {
285
+ return left.index === right.index;
286
+ }
287
+ return false;
288
+ }
289
+ function pushRun(runs, style, text) {
290
+ if (text.length === 0) {
291
+ return;
292
+ }
293
+ const previous = runs.at(-1);
294
+ if (previous && text !== "\n" && previous.text !== "\n" && stylesEqual(previous, style)) {
295
+ previous.text += text;
296
+ return;
297
+ }
298
+ runs.push({
299
+ text,
300
+ fg: style.fg,
301
+ bg: style.bg,
302
+ bold: style.bold,
303
+ italic: style.italic,
304
+ underline: style.underline,
305
+ strikethrough: style.strikethrough,
306
+ dim: style.dim
307
+ });
308
+ }
309
+ function parseCsi(input, start) {
310
+ let index = start + 2;
311
+ while (index < input.length) {
312
+ const code = input.charCodeAt(index);
313
+ if (code >= 64 && code <= 126) {
314
+ return {
315
+ end: index + 1,
316
+ final: input[index],
317
+ params: input.slice(start + 2, index)
318
+ };
319
+ }
320
+ index += 1;
321
+ }
322
+ return {
323
+ end: input.length,
324
+ final: null,
325
+ params: input.slice(start + 2)
326
+ };
327
+ }
328
+ function toInteger(value) {
329
+ if (!value || value.length === 0) {
330
+ return null;
331
+ }
332
+ for (const char of value) {
333
+ if (char < "0" || char > "9") {
334
+ return null;
335
+ }
336
+ }
337
+ return Number.parseInt(value, 10);
338
+ }
339
+ function clampByte(value) {
340
+ if (value < 0) {
341
+ return 0;
342
+ }
343
+ if (value > 255) {
344
+ return 255;
345
+ }
346
+ return value;
347
+ }
348
+ function applyExtendedColor(params2, start) {
349
+ const mode = params2[start];
350
+ if (mode === 5) {
351
+ const index = params2[start + 1];
352
+ if (index === void 0) {
353
+ return null;
354
+ }
355
+ return {
356
+ color: { type: "ansi8", index: clampByte(index) },
357
+ consumed: 2
358
+ };
359
+ }
360
+ if (mode === 2) {
361
+ const r = params2[start + 1];
362
+ const g = params2[start + 2];
363
+ const b = params2[start + 3];
364
+ if (r === void 0 || g === void 0 || b === void 0) {
365
+ return null;
366
+ }
367
+ return {
368
+ color: { type: "rgb", r: clampByte(r), g: clampByte(g), b: clampByte(b) },
369
+ consumed: 4
370
+ };
371
+ }
372
+ return null;
373
+ }
374
+ function applySgr(style, paramsText) {
375
+ const nextStyle = cloneStyle(style);
376
+ const rawParams = paramsText.length === 0 ? ["0"] : paramsText.split(";");
377
+ const params2 = [];
378
+ for (const rawParam of rawParams) {
379
+ const value = toInteger(rawParam);
380
+ if (value === null) {
381
+ return nextStyle;
382
+ }
383
+ params2.push(value);
384
+ }
385
+ for (let index = 0; index < params2.length; index += 1) {
386
+ const value = params2[index];
387
+ if (value === 0) {
388
+ Object.assign(nextStyle, createDefaultStyle());
389
+ continue;
390
+ }
391
+ if (value === 1) {
392
+ nextStyle.bold = true;
393
+ continue;
394
+ }
395
+ if (value === 2) {
396
+ nextStyle.dim = true;
397
+ continue;
398
+ }
399
+ if (value === 22) {
400
+ nextStyle.bold = false;
401
+ nextStyle.dim = false;
402
+ continue;
403
+ }
404
+ if (value === 3) {
405
+ nextStyle.italic = true;
406
+ continue;
407
+ }
408
+ if (value === 23) {
409
+ nextStyle.italic = false;
410
+ continue;
411
+ }
412
+ if (value === 4) {
413
+ nextStyle.underline = true;
414
+ continue;
415
+ }
416
+ if (value === 24) {
417
+ nextStyle.underline = false;
418
+ continue;
419
+ }
420
+ if (value === 9) {
421
+ nextStyle.strikethrough = true;
422
+ continue;
423
+ }
424
+ if (value === 29) {
425
+ nextStyle.strikethrough = false;
426
+ continue;
427
+ }
428
+ if (value === 39) {
429
+ nextStyle.fg = null;
430
+ continue;
431
+ }
432
+ if (value === 49) {
433
+ nextStyle.bg = null;
434
+ continue;
435
+ }
436
+ if (value >= 30 && value <= 37) {
437
+ nextStyle.fg = { type: "ansi4", index: value - 30 };
438
+ continue;
439
+ }
440
+ if (value >= 90 && value <= 97) {
441
+ nextStyle.fg = { type: "ansi4", index: value - 90 + 8 };
442
+ continue;
443
+ }
444
+ if (value >= 40 && value <= 47) {
445
+ nextStyle.bg = { type: "ansi4", index: value - 40 };
446
+ continue;
447
+ }
448
+ if (value >= 100 && value <= 107) {
449
+ nextStyle.bg = { type: "ansi4", index: value - 100 + 8 };
450
+ continue;
451
+ }
452
+ if (value === 38 || value === 48) {
453
+ const extended = applyExtendedColor(params2, index + 1);
454
+ if (!extended) {
455
+ continue;
456
+ }
457
+ if (value === 38) {
458
+ nextStyle.fg = extended.color;
459
+ } else {
460
+ nextStyle.bg = extended.color;
461
+ }
462
+ index += extended.consumed;
463
+ }
464
+ }
465
+ return nextStyle;
466
+ }
467
+ function parseAnsi(input) {
468
+ const runs = [];
469
+ let style = createDefaultStyle();
470
+ let textStart = 0;
471
+ let index = 0;
472
+ while (index < input.length) {
473
+ const char = input[index];
474
+ if (char === "\n") {
475
+ pushRun(runs, style, input.slice(textStart, index));
476
+ pushRun(runs, style, "\n");
477
+ index += 1;
478
+ textStart = index;
479
+ continue;
480
+ }
481
+ if (char === ESC && input[index + 1] === "[") {
482
+ pushRun(runs, style, input.slice(textStart, index));
483
+ const sequence = parseCsi(input, index);
484
+ if (sequence.final === "m") {
485
+ style = applySgr(style, sequence.params);
486
+ }
487
+ index = sequence.end;
488
+ textStart = index;
489
+ continue;
490
+ }
491
+ index += 1;
492
+ }
493
+ pushRun(runs, style, input.slice(textStart));
494
+ return runs;
495
+ }
496
+
497
+ // ../terminal-png/src/png-renderer.ts
498
+ import { Resvg } from "@resvg/resvg-js";
499
+
500
+ // ../terminal-png/src/font.ts
501
+ import { readFileSync } from "node:fs";
502
+ import { createRequire } from "node:module";
503
+ import { dirname, join } from "node:path";
504
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
505
+ var require2 = createRequire(import.meta.url);
506
+ var fontPackageRoot = dirname(require2.resolve("jetbrains-mono/package.json"));
507
+ var webfontRoot = join(fontPackageRoot, "fonts/webfonts");
508
+ function readWebfontBase64(filename) {
509
+ return readFileSync(join(webfontRoot, filename)).toString("base64");
510
+ }
511
+ function resolveAssetPath(filename) {
512
+ return fileURLToPath2(new URL(`../assets/${filename}`, import.meta.url));
513
+ }
514
+ function createFontFace(base64, weight, style) {
515
+ return `@font-face {
516
+ font-family: 'JetBrains Mono';
517
+ font-style: ${style};
518
+ font-weight: ${weight};
519
+ src: url('data:font/woff2;base64,${base64}') format('woff2');
520
+ }`;
521
+ }
522
+ var JETBRAINS_MONO_BASE64 = readWebfontBase64("JetBrainsMono-Regular.woff2");
523
+ var JETBRAINS_MONO_FONT_FILES = [
524
+ resolveAssetPath("jetbrains-mono-400-normal.ttf"),
525
+ resolveAssetPath("jetbrains-mono-700-normal.ttf"),
526
+ resolveAssetPath("jetbrains-mono-400-italic.ttf"),
527
+ resolveAssetPath("jetbrains-mono-700-italic.ttf")
528
+ ];
529
+ var JETBRAINS_MONO_TTF_PATH = JETBRAINS_MONO_FONT_FILES[0];
530
+ var FONT_FACE_CSS = [
531
+ createFontFace(JETBRAINS_MONO_BASE64, 400, "normal"),
532
+ createFontFace(readWebfontBase64("JetBrainsMono-Bold.woff2"), 700, "normal"),
533
+ createFontFace(readWebfontBase64("JetBrainsMono-Italic.woff2"), 400, "italic"),
534
+ createFontFace(readWebfontBase64("JetBrainsMono-BoldItalic.woff2"), 700, "italic")
535
+ ].join("\n");
536
+
537
+ // ../terminal-png/src/png-renderer.ts
538
+ function renderPng(svg) {
539
+ const resvg = new Resvg(svg, {
540
+ font: {
541
+ defaultFontFamily: "JetBrains Mono",
542
+ fontFiles: [...JETBRAINS_MONO_FONT_FILES],
543
+ loadSystemFonts: false,
544
+ monospaceFamily: "JetBrains Mono"
545
+ },
546
+ fitTo: {
547
+ mode: "zoom",
548
+ value: 4
549
+ }
550
+ });
551
+ const png = resvg.render();
552
+ return Buffer.from(png.asPng());
553
+ }
554
+
555
+ // ../terminal-png/src/svg-renderer.ts
556
+ var ANSI_16_PALETTE = [
557
+ "#282a2e",
558
+ "#D74E6F",
559
+ "#31BB71",
560
+ "#D3E561",
561
+ "#8056FF",
562
+ "#ED61D7",
563
+ "#04D7D7",
564
+ "#C5C8C6",
565
+ "#4B4B4B",
566
+ "#FE5F86",
567
+ "#00D787",
568
+ "#EBFF71",
569
+ "#8F69FF",
570
+ "#FF7AEA",
571
+ "#00FEFE",
572
+ "#FFFFFF"
573
+ ];
574
+ var XTERM_256_PALETTE = [
575
+ "#000000",
576
+ "#800000",
577
+ "#008000",
578
+ "#808000",
579
+ "#000080",
580
+ "#800080",
581
+ "#008080",
582
+ "#c0c0c0",
583
+ "#808080",
584
+ "#ff0000",
585
+ "#00ff00",
586
+ "#ffff00",
587
+ "#0000ff",
588
+ "#ff00ff",
589
+ "#00ffff",
590
+ "#ffffff",
591
+ "#000000",
592
+ "#00005f",
593
+ "#000087",
594
+ "#0000af",
595
+ "#0000d7",
596
+ "#0000ff",
597
+ "#005f00",
598
+ "#005f5f",
599
+ "#005f87",
600
+ "#005faf",
601
+ "#005fd7",
602
+ "#005fff",
603
+ "#008700",
604
+ "#00875f",
605
+ "#008787",
606
+ "#0087af",
607
+ "#0087d7",
608
+ "#0087ff",
609
+ "#00af00",
610
+ "#00af5f",
611
+ "#00af87",
612
+ "#00afaf",
613
+ "#00afd7",
614
+ "#00afff",
615
+ "#00d700",
616
+ "#00d75f",
617
+ "#00d787",
618
+ "#00d7af",
619
+ "#00d7d7",
620
+ "#00d7ff",
621
+ "#00ff00",
622
+ "#00ff5f",
623
+ "#00ff87",
624
+ "#00ffaf",
625
+ "#00ffd7",
626
+ "#00ffff",
627
+ "#5f0000",
628
+ "#5f005f",
629
+ "#5f0087",
630
+ "#5f00af",
631
+ "#5f00d7",
632
+ "#5f00ff",
633
+ "#5f5f00",
634
+ "#5f5f5f",
635
+ "#5f5f87",
636
+ "#5f5faf",
637
+ "#5f5fd7",
638
+ "#5f5fff",
639
+ "#5f8700",
640
+ "#5f875f",
641
+ "#5f8787",
642
+ "#5f87af",
643
+ "#5f87d7",
644
+ "#5f87ff",
645
+ "#5faf00",
646
+ "#5faf5f",
647
+ "#5faf87",
648
+ "#5fafaf",
649
+ "#5fafd7",
650
+ "#5fafff",
651
+ "#5fd700",
652
+ "#5fd75f",
653
+ "#5fd787",
654
+ "#5fd7af",
655
+ "#5fd7d7",
656
+ "#5fd7ff",
657
+ "#5fff00",
658
+ "#5fff5f",
659
+ "#5fff87",
660
+ "#5fffaf",
661
+ "#5fffd7",
662
+ "#5fffff",
663
+ "#870000",
664
+ "#87005f",
665
+ "#870087",
666
+ "#8700af",
667
+ "#8700d7",
668
+ "#8700ff",
669
+ "#875f00",
670
+ "#875f5f",
671
+ "#875f87",
672
+ "#875faf",
673
+ "#875fd7",
674
+ "#875fff",
675
+ "#878700",
676
+ "#87875f",
677
+ "#878787",
678
+ "#8787af",
679
+ "#8787d7",
680
+ "#8787ff",
681
+ "#87af00",
682
+ "#87af5f",
683
+ "#87af87",
684
+ "#87afaf",
685
+ "#87afd7",
686
+ "#87afff",
687
+ "#87d700",
688
+ "#87d75f",
689
+ "#87d787",
690
+ "#87d7af",
691
+ "#87d7d7",
692
+ "#87d7ff",
693
+ "#87ff00",
694
+ "#87ff5f",
695
+ "#87ff87",
696
+ "#87ffaf",
697
+ "#87ffd7",
698
+ "#87ffff",
699
+ "#af0000",
700
+ "#af005f",
701
+ "#af0087",
702
+ "#af00af",
703
+ "#af00d7",
704
+ "#af00ff",
705
+ "#af5f00",
706
+ "#af5f5f",
707
+ "#af5f87",
708
+ "#af5faf",
709
+ "#af5fd7",
710
+ "#af5fff",
711
+ "#af8700",
712
+ "#af875f",
713
+ "#af8787",
714
+ "#af87af",
715
+ "#af87d7",
716
+ "#af87ff",
717
+ "#afaf00",
718
+ "#afaf5f",
719
+ "#afaf87",
720
+ "#afafaf",
721
+ "#afafd7",
722
+ "#afafff",
723
+ "#afd700",
724
+ "#afd75f",
725
+ "#afd787",
726
+ "#afd7af",
727
+ "#afd7d7",
728
+ "#afd7ff",
729
+ "#afff00",
730
+ "#afff5f",
731
+ "#afff87",
732
+ "#afffaf",
733
+ "#afffd7",
734
+ "#afffff",
735
+ "#d70000",
736
+ "#d7005f",
737
+ "#d70087",
738
+ "#d700af",
739
+ "#d700d7",
740
+ "#d700ff",
741
+ "#d75f00",
742
+ "#d75f5f",
743
+ "#d75f87",
744
+ "#d75faf",
745
+ "#d75fd7",
746
+ "#d75fff",
747
+ "#d78700",
748
+ "#d7875f",
749
+ "#d78787",
750
+ "#d787af",
751
+ "#d787d7",
752
+ "#d787ff",
753
+ "#d7af00",
754
+ "#d7af5f",
755
+ "#d7af87",
756
+ "#d7afaf",
757
+ "#d7afd7",
758
+ "#d7afff",
759
+ "#d7d700",
760
+ "#d7d75f",
761
+ "#d7d787",
762
+ "#d7d7af",
763
+ "#d7d7d7",
764
+ "#d7d7ff",
765
+ "#d7ff00",
766
+ "#d7ff5f",
767
+ "#d7ff87",
768
+ "#d7ffaf",
769
+ "#d7ffd7",
770
+ "#d7ffff",
771
+ "#ff0000",
772
+ "#ff005f",
773
+ "#ff0087",
774
+ "#ff00af",
775
+ "#ff00d7",
776
+ "#ff00ff",
777
+ "#ff5f00",
778
+ "#ff5f5f",
779
+ "#ff5f87",
780
+ "#ff5faf",
781
+ "#ff5fd7",
782
+ "#ff5fff",
783
+ "#ff8700",
784
+ "#ff875f",
785
+ "#ff8787",
786
+ "#ff87af",
787
+ "#ff87d7",
788
+ "#ff87ff",
789
+ "#ffaf00",
790
+ "#ffaf5f",
791
+ "#ffaf87",
792
+ "#ffafaf",
793
+ "#ffafd7",
794
+ "#ffafff",
795
+ "#ffd700",
796
+ "#ffd75f",
797
+ "#ffd787",
798
+ "#ffd7af",
799
+ "#ffd7d7",
800
+ "#ffd7ff",
801
+ "#ffff00",
802
+ "#ffff5f",
803
+ "#ffff87",
804
+ "#ffffaf",
805
+ "#ffffd7",
806
+ "#ffffff",
807
+ "#080808",
808
+ "#121212",
809
+ "#1c1c1c",
810
+ "#262626",
811
+ "#303030",
812
+ "#3a3a3a",
813
+ "#444444",
814
+ "#4e4e4e",
815
+ "#585858",
816
+ "#606060",
817
+ "#666666",
818
+ "#767676",
819
+ "#808080",
820
+ "#8a8a8a",
821
+ "#949494",
822
+ "#9e9e9e",
823
+ "#a8a8a8",
824
+ "#b2b2b2",
825
+ "#bcbcbc",
826
+ "#c6c6c6",
827
+ "#d0d0d0",
828
+ "#dadada",
829
+ "#e4e4e4",
830
+ "#eeeeee"
831
+ ];
832
+ var BACKGROUND = "#171717";
833
+ var DEFAULT_FOREGROUND = "#c4c4c4";
834
+ var FONT_FAMILY = "JetBrains Mono";
835
+ var FONT_SIZE = 14;
836
+ var LINE_HEIGHT = 1.2;
837
+ var CHARACTER_WIDTH = 8.412666666666667;
838
+ var TEXT_BOTTOM_PADDING = 11.6;
839
+ var DEFAULT_PADDING = {
840
+ top: 20,
841
+ right: 40,
842
+ bottom: 20,
843
+ left: 20
844
+ };
845
+ var WINDOW_BAR_HEIGHT = 15;
846
+ function renderSvg(runs, options = {}) {
847
+ const padding = options.padding === void 0 ? DEFAULT_PADDING : {
848
+ top: options.padding,
849
+ right: options.padding,
850
+ bottom: options.padding,
851
+ left: options.padding
852
+ };
853
+ const showWindow = options.window ?? true;
854
+ const titleBarHeight = showWindow ? WINDOW_BAR_HEIGHT : 0;
855
+ const textStartX = padding.left;
856
+ const lineHeightPx = FONT_SIZE * LINE_HEIGHT;
857
+ const textStartY = titleBarHeight + padding.top + lineHeightPx;
858
+ const lines = splitIntoLines(runs);
859
+ const contentWidth = measureLines(lines);
860
+ const width = padding.left + contentWidth + padding.right;
861
+ const height = titleBarHeight + padding.top + lines.length * lineHeightPx + padding.bottom + TEXT_BOTTOM_PADDING;
862
+ const svgWidth = formatNumber(width);
863
+ const svgHeight = formatNumber(height);
864
+ const viewBox = `0 0 ${svgWidth} ${svgHeight}`;
865
+ const textElements = renderLines(lines, textStartX, textStartY, lineHeightPx);
866
+ return [
867
+ `<svg xmlns="http://www.w3.org/2000/svg" width="${svgWidth}" height="${svgHeight}" viewBox="${viewBox}">`,
868
+ "<defs>",
869
+ "<style><![CDATA[",
870
+ FONT_FACE_CSS,
871
+ "]]></style>",
872
+ "</defs>",
873
+ `<rect x="0" y="0" width="${svgWidth}" height="${svgHeight}" fill="${BACKGROUND}" />`,
874
+ showWindow ? renderWindowControls() : "",
875
+ `<g font-family="${FONT_FAMILY}" font-size="${formatNumber(FONT_SIZE)}px" fill="${DEFAULT_FOREGROUND}">`,
876
+ textElements,
877
+ "</g>",
878
+ "</svg>"
879
+ ].join("");
880
+ }
881
+ function splitIntoLines(runs) {
882
+ const lines = [[]];
883
+ for (const run of runs) {
884
+ if (run.text === "\n") {
885
+ lines.push([]);
886
+ continue;
887
+ }
888
+ lines[lines.length - 1]?.push(run);
889
+ }
890
+ return lines;
891
+ }
892
+ function measureLines(lines) {
893
+ return Math.max(
894
+ ...lines.map((line) => line.reduce((width, run) => width + displayWidth(run.text) * CHARACTER_WIDTH, 0)),
895
+ 0
896
+ );
897
+ }
898
+ function displayWidth(text) {
899
+ const segmenter = new Intl.Segmenter();
900
+ let width = 0;
901
+ for (const { segment } of segmenter.segment(text)) {
902
+ const cp = segment.codePointAt(0);
903
+ width += cp !== void 0 && isWideCodePoint(cp) ? 2 : 1;
904
+ }
905
+ return width;
906
+ }
907
+ function isWideCodePoint(cp) {
908
+ return cp >= 4352 && cp <= 4447 || // Hangul Jamo
909
+ cp === 9001 || cp === 9002 || // Angle brackets
910
+ cp >= 11904 && cp <= 12350 || // CJK Radicals, Kangxi
911
+ cp >= 12353 && cp <= 13247 || // Hiragana, Katakana, CJK symbols
912
+ cp >= 13312 && cp <= 19903 || // CJK Extension A
913
+ cp >= 19968 && cp <= 42191 || // CJK Unified Ideographs
914
+ cp >= 43360 && cp <= 43391 || // Hangul Jamo Extended-A
915
+ cp >= 44032 && cp <= 55215 || // Hangul Syllables
916
+ cp >= 63744 && cp <= 64255 || // CJK Compatibility Ideographs
917
+ cp >= 65040 && cp <= 65049 || // Vertical Forms
918
+ cp >= 65072 && cp <= 65135 || // CJK Compatibility Forms
919
+ cp >= 65280 && cp <= 65376 || // Fullwidth Latin, Halfwidth Katakana
920
+ cp >= 65504 && cp <= 65510 || // Fullwidth Signs
921
+ cp >= 110592 && cp <= 110847 || // Kana Supplement
922
+ cp >= 126980 && cp <= 126980 || // Mahjong tile
923
+ cp >= 127183 && cp <= 127183 || // Playing card black joker
924
+ cp >= 127488 && cp <= 131069 || // Enclosed CJK + Emoji
925
+ cp >= 131072 && cp <= 196605 || // CJK Extension B–F
926
+ cp >= 196608 && cp <= 262141;
927
+ }
928
+ function renderLines(lines, textStartX, textStartY, lineHeightPx) {
929
+ return lines.map((line, index) => {
930
+ const y = formatNumber(textStartY + index * lineHeightPx);
931
+ if (line.length === 0) {
932
+ return `<text x="${formatNumber(textStartX)}" y="${y}" xml:space="preserve"/>`;
933
+ }
934
+ return [
935
+ `<text x="${formatNumber(textStartX)}" y="${y}" xml:space="preserve">`,
936
+ line.map(renderRun).join(""),
937
+ "</text>"
938
+ ].join("");
939
+ }).join("");
940
+ }
941
+ function renderRun(run) {
942
+ const attributes = ['xml:space="preserve"'];
943
+ const color = resolveColor(run.fg);
944
+ const textDecorations = [];
945
+ if (color !== DEFAULT_FOREGROUND) {
946
+ attributes.push(`fill="${escapeXmlAttribute(color)}"`);
947
+ }
948
+ if (run.bold) {
949
+ attributes.push('font-weight="bold"');
950
+ }
951
+ if (run.italic) {
952
+ attributes.push('font-style="italic"');
953
+ }
954
+ if (run.underline) {
955
+ textDecorations.push("underline");
956
+ }
957
+ if (run.strikethrough) {
958
+ textDecorations.push("line-through");
959
+ }
960
+ if (textDecorations.length > 0) {
961
+ attributes.push(`text-decoration="${textDecorations.join(" ")}"`);
962
+ }
963
+ if (run.dim) {
964
+ attributes.push('opacity="0.7"');
965
+ }
966
+ return `<tspan ${attributes.join(" ")}>${escapeXmlText(run.text)}</tspan>`;
967
+ }
968
+ function renderWindowControls() {
969
+ return [
970
+ '<circle cx="13.5" cy="12" r="5.5" fill="#FF5A54" />',
971
+ '<circle cx="32.5" cy="12" r="5.5" fill="#E6BF29" />',
972
+ '<circle cx="51.5" cy="12" r="5.5" fill="#52C12B" />'
973
+ ].join("");
974
+ }
975
+ function resolveColor(color) {
976
+ if (color === null) {
977
+ return DEFAULT_FOREGROUND;
978
+ }
979
+ if (color.type === "ansi4") {
980
+ return ANSI_16_PALETTE[color.index] ?? DEFAULT_FOREGROUND;
981
+ }
982
+ if (color.type === "ansi8") {
983
+ if (color.index < ANSI_16_PALETTE.length) {
984
+ return ANSI_16_PALETTE[color.index] ?? DEFAULT_FOREGROUND;
985
+ }
986
+ return XTERM_256_PALETTE[color.index] ?? DEFAULT_FOREGROUND;
987
+ }
988
+ return `rgb(${color.r},${color.g},${color.b})`;
989
+ }
990
+ function escapeXmlText(value) {
991
+ return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
992
+ }
993
+ function escapeXmlAttribute(value) {
994
+ return escapeXmlText(value).replaceAll('"', "&quot;").replaceAll("'", "&apos;");
995
+ }
996
+ function formatNumber(value) {
997
+ return value.toFixed(2);
998
+ }
999
+
1000
+ // ../terminal-png/src/index.ts
1001
+ async function renderTerminalPng(ansiText, options = {}) {
1002
+ const runs = parseAnsi(ansiText);
1003
+ const svg = renderSvg(runs, {
1004
+ padding: options.padding,
1005
+ window: options.window
1006
+ });
1007
+ const png = renderPng(svg);
1008
+ if (options.output) {
1009
+ await writeFile(options.output, png);
1010
+ }
1011
+ return png;
1012
+ }
1013
+
1014
+ // src/terminal-pilot.ts
1015
+ import { randomUUID } from "node:crypto";
1016
+
1017
+ // src/terminal-session.ts
1018
+ import { spawn as spawnChildProcess } from "node:child_process";
1019
+ import { EventEmitter } from "node:events";
1020
+ import * as nodePty from "node-pty";
1021
+
1022
+ // src/ansi.ts
1023
+ var ESC2 = 27;
1024
+ var BEL = 7;
1025
+ var ST = 156;
1026
+ var CSI = 155;
1027
+ var OSC = 157;
1028
+ var DCS = 144;
1029
+ var SOS = 152;
1030
+ var PM = 158;
1031
+ var APC = 159;
1032
+ function stripAnsi(input) {
1033
+ let output = "";
1034
+ for (let index = 0; index < input.length; index += 1) {
1035
+ const code = input.charCodeAt(index);
1036
+ if (code === ESC2) {
1037
+ const nextCode = input.charCodeAt(index + 1);
1038
+ if (nextCode === 91) {
1039
+ index = consumeCsi(input, index + 2);
1040
+ continue;
1041
+ }
1042
+ if (nextCode === 93) {
1043
+ index = consumeTerminatedString(input, index + 2, true);
1044
+ continue;
1045
+ }
1046
+ if (nextCode === 80 || nextCode === 88 || nextCode === 94 || nextCode === 95) {
1047
+ index = consumeTerminatedString(input, index + 2, false);
1048
+ continue;
1049
+ }
1050
+ if (!Number.isNaN(nextCode)) {
1051
+ index += 1;
1052
+ }
1053
+ continue;
1054
+ }
1055
+ if (code === CSI) {
1056
+ index = consumeCsi(input, index + 1);
1057
+ continue;
1058
+ }
1059
+ if (code === OSC) {
1060
+ index = consumeTerminatedString(input, index + 1, true);
1061
+ continue;
1062
+ }
1063
+ if (code === DCS || code === SOS || code === PM || code === APC) {
1064
+ index = consumeTerminatedString(input, index + 1, false);
1065
+ continue;
1066
+ }
1067
+ output += input[index];
1068
+ }
1069
+ return output;
1070
+ }
1071
+ function consumeCsi(input, index) {
1072
+ while (index < input.length) {
1073
+ const code = input.charCodeAt(index);
1074
+ if (code >= 64 && code <= 126) {
1075
+ return index;
1076
+ }
1077
+ index += 1;
1078
+ }
1079
+ return input.length;
1080
+ }
1081
+ function consumeTerminatedString(input, index, allowBellTerminator) {
1082
+ while (index < input.length) {
1083
+ const code = input.charCodeAt(index);
1084
+ if (code === ST || allowBellTerminator && code === BEL) {
1085
+ return index;
1086
+ }
1087
+ if (code === ESC2 && input.charCodeAt(index + 1) === 92) {
1088
+ return index + 1;
1089
+ }
1090
+ index += 1;
1091
+ }
1092
+ return input.length;
1093
+ }
1094
+
1095
+ // src/terminal-buffer.ts
1096
+ var RESET_SGR = "\x1B[0m";
1097
+ var TerminalBuffer = class {
1098
+ _cols;
1099
+ _rows;
1100
+ _screen;
1101
+ _cursorX = 0;
1102
+ _cursorY = 0;
1103
+ _savedCursor = { x: 0, y: 0 };
1104
+ _scrollTop = 0;
1105
+ _scrollBottom;
1106
+ _state = 0 /* Normal */;
1107
+ _csiParams = "";
1108
+ _csiPrivate = "";
1109
+ _style = createDefaultStyleState();
1110
+ _styleSequence = "";
1111
+ displayBuffer;
1112
+ constructor(cols, rows) {
1113
+ this._cols = cols;
1114
+ this._rows = rows;
1115
+ this._scrollBottom = rows - 1;
1116
+ this._screen = this._makeScreen(cols, rows);
1117
+ this.displayBuffer = Object.defineProperties(
1118
+ {},
1119
+ {
1120
+ cursorX: { get: () => this._cursorX, enumerable: true },
1121
+ cursorY: { get: () => this._cursorY, enumerable: true },
1122
+ data: { get: () => this._screen, enumerable: true }
1123
+ }
1124
+ );
1125
+ }
1126
+ write(data) {
1127
+ for (const ch of data) {
1128
+ this._feed(ch);
1129
+ }
1130
+ }
1131
+ renderLine(row) {
1132
+ const cells = this._screen[row] ?? [];
1133
+ let lastVisibleCell = -1;
1134
+ for (let index = cells.length - 1; index >= 0; index -= 1) {
1135
+ if (cells[index] !== null) {
1136
+ lastVisibleCell = index;
1137
+ break;
1138
+ }
1139
+ }
1140
+ if (lastVisibleCell === -1) {
1141
+ return "";
1142
+ }
1143
+ let line = "";
1144
+ let activeStyle = "";
1145
+ for (let index = 0; index <= lastVisibleCell; index += 1) {
1146
+ const cell = cells[index];
1147
+ const cellStyle = cell?.style ?? "";
1148
+ if (cellStyle !== activeStyle) {
1149
+ line += cellStyle.length > 0 ? cellStyle : RESET_SGR;
1150
+ activeStyle = cellStyle;
1151
+ }
1152
+ line += cell?.[1] ?? " ";
1153
+ }
1154
+ if (activeStyle.length > 0) {
1155
+ line += RESET_SGR;
1156
+ }
1157
+ return line;
1158
+ }
1159
+ resize(cols, rows) {
1160
+ while (this._screen.length < rows) {
1161
+ this._screen.push(this._makeRow(cols));
1162
+ }
1163
+ this._screen.length = rows;
1164
+ for (let y = 0; y < rows; y++) {
1165
+ const row = this._screen[y] ?? this._makeRow(cols);
1166
+ while (row.length < cols) row.push(null);
1167
+ row.length = cols;
1168
+ this._screen[y] = row;
1169
+ }
1170
+ this._cols = cols;
1171
+ this._rows = rows;
1172
+ this._scrollTop = 0;
1173
+ this._scrollBottom = rows - 1;
1174
+ this._cursorX = this._clamp(this._cursorX, 0, cols - 1);
1175
+ this._cursorY = this._clamp(this._cursorY, 0, rows - 1);
1176
+ }
1177
+ _makeScreen(cols, rows) {
1178
+ return Array.from({ length: rows }, () => this._makeRow(cols));
1179
+ }
1180
+ _makeRow(cols) {
1181
+ return Array(cols).fill(null);
1182
+ }
1183
+ _clamp(value, min, max) {
1184
+ return Math.max(min, Math.min(max, value));
1185
+ }
1186
+ _setChar(y, x, ch) {
1187
+ const row = this._screen[y];
1188
+ if (row && x >= 0 && x < this._cols) {
1189
+ const cell = [ch.charCodeAt(0), ch];
1190
+ if (this._styleSequence.length > 0) {
1191
+ Object.defineProperty(cell, "style", {
1192
+ value: this._styleSequence,
1193
+ writable: true,
1194
+ configurable: true
1195
+ });
1196
+ }
1197
+ row[x] = cell;
1198
+ }
1199
+ }
1200
+ _eraseLine(y, fromX, toX) {
1201
+ const row = this._screen[y];
1202
+ if (!row) return;
1203
+ for (let x = fromX; x <= toX && x < this._cols; x++) {
1204
+ row[x] = null;
1205
+ }
1206
+ }
1207
+ _scrollUp(count) {
1208
+ for (let i = 0; i < count; i++) {
1209
+ this._screen.splice(this._scrollTop, 1);
1210
+ this._screen.splice(this._scrollBottom, 0, this._makeRow(this._cols));
1211
+ }
1212
+ }
1213
+ _scrollDown(count) {
1214
+ for (let i = 0; i < count; i++) {
1215
+ this._screen.splice(this._scrollBottom, 1);
1216
+ this._screen.splice(this._scrollTop, 0, this._makeRow(this._cols));
1217
+ }
1218
+ }
1219
+ _newline() {
1220
+ if (this._cursorY === this._scrollBottom) {
1221
+ this._scrollUp(1);
1222
+ } else {
1223
+ this._cursorY = Math.min(this._cursorY + 1, this._rows - 1);
1224
+ }
1225
+ }
1226
+ _parseCsiParams() {
1227
+ if (!this._csiParams) return [];
1228
+ return this._csiParams.split(";").map((s) => s === "" ? 0 : parseInt(s, 10));
1229
+ }
1230
+ _execCsi(final) {
1231
+ const params2 = this._parseCsiParams();
1232
+ const p0 = params2[0] ?? 0;
1233
+ const p1 = params2[1] ?? 0;
1234
+ if (this._csiPrivate === "?") {
1235
+ if (final === "h" || final === "l") {
1236
+ if (params2.includes(1049)) {
1237
+ if (final === "h") {
1238
+ this._screen = this._makeScreen(this._cols, this._rows);
1239
+ this._cursorX = 0;
1240
+ this._cursorY = 0;
1241
+ } else {
1242
+ this._screen = this._makeScreen(this._cols, this._rows);
1243
+ this._cursorX = 0;
1244
+ this._cursorY = 0;
1245
+ }
1246
+ this._resetStyle();
1247
+ }
1248
+ }
1249
+ return;
1250
+ }
1251
+ switch (final) {
1252
+ case "A":
1253
+ this._cursorY = this._clamp(this._cursorY - Math.max(1, p0), 0, this._rows - 1);
1254
+ break;
1255
+ case "B":
1256
+ this._cursorY = this._clamp(this._cursorY + Math.max(1, p0), 0, this._rows - 1);
1257
+ break;
1258
+ case "C":
1259
+ // cursor forward
1260
+ case "a":
1261
+ this._cursorX = this._clamp(this._cursorX + Math.max(1, p0), 0, this._cols - 1);
1262
+ break;
1263
+ case "D":
1264
+ this._cursorX = this._clamp(this._cursorX - Math.max(1, p0), 0, this._cols - 1);
1265
+ break;
1266
+ case "E":
1267
+ this._cursorY = this._clamp(this._cursorY + Math.max(1, p0), 0, this._rows - 1);
1268
+ this._cursorX = 0;
1269
+ break;
1270
+ case "F":
1271
+ this._cursorY = this._clamp(this._cursorY - Math.max(1, p0), 0, this._rows - 1);
1272
+ this._cursorX = 0;
1273
+ break;
1274
+ case "G":
1275
+ // cursor horizontal absolute
1276
+ case "`":
1277
+ this._cursorX = this._clamp(Math.max(1, p0) - 1, 0, this._cols - 1);
1278
+ break;
1279
+ case "H":
1280
+ // cursor position
1281
+ case "f":
1282
+ this._cursorY = this._clamp(Math.max(1, p0) - 1, 0, this._rows - 1);
1283
+ this._cursorX = this._clamp(Math.max(1, p1) - 1, 0, this._cols - 1);
1284
+ break;
1285
+ case "I":
1286
+ for (let i = 0; i < Math.max(1, p0); i++) {
1287
+ this._cursorX = Math.min(this._cols - 1, (Math.floor(this._cursorX / 8) + 1) * 8);
1288
+ }
1289
+ break;
1290
+ case "J":
1291
+ if (p0 === 0) {
1292
+ this._eraseLine(this._cursorY, this._cursorX, this._cols - 1);
1293
+ for (let y = this._cursorY + 1; y < this._rows; y++) this._eraseLine(y, 0, this._cols - 1);
1294
+ } else if (p0 === 1) {
1295
+ for (let y = 0; y < this._cursorY; y++) this._eraseLine(y, 0, this._cols - 1);
1296
+ this._eraseLine(this._cursorY, 0, this._cursorX);
1297
+ } else if (p0 === 2 || p0 === 3) {
1298
+ for (let y = 0; y < this._rows; y++) this._eraseLine(y, 0, this._cols - 1);
1299
+ }
1300
+ break;
1301
+ case "K":
1302
+ if (p0 === 0) this._eraseLine(this._cursorY, this._cursorX, this._cols - 1);
1303
+ else if (p0 === 1) this._eraseLine(this._cursorY, 0, this._cursorX);
1304
+ else if (p0 === 2) this._eraseLine(this._cursorY, 0, this._cols - 1);
1305
+ break;
1306
+ case "X":
1307
+ this._eraseLine(this._cursorY, this._cursorX, this._cursorX + Math.max(1, p0) - 1);
1308
+ break;
1309
+ case "L": {
1310
+ const n = Math.max(1, p0);
1311
+ for (let i = 0; i < n; i++) {
1312
+ this._screen.splice(this._scrollBottom, 1);
1313
+ this._screen.splice(this._cursorY, 0, this._makeRow(this._cols));
1314
+ }
1315
+ break;
1316
+ }
1317
+ case "M": {
1318
+ const n = Math.max(1, p0);
1319
+ for (let i = 0; i < n; i++) {
1320
+ this._screen.splice(this._cursorY, 1);
1321
+ this._screen.splice(this._scrollBottom, 0, this._makeRow(this._cols));
1322
+ }
1323
+ break;
1324
+ }
1325
+ case "P": {
1326
+ const row = this._screen[this._cursorY];
1327
+ if (row) {
1328
+ const n = Math.max(1, p0);
1329
+ row.splice(this._cursorX, n);
1330
+ while (row.length < this._cols) row.push(null);
1331
+ }
1332
+ break;
1333
+ }
1334
+ case "@": {
1335
+ const row = this._screen[this._cursorY];
1336
+ if (row) {
1337
+ const n = Math.max(1, p0);
1338
+ for (let i = 0; i < n; i++) row.splice(this._cursorX, 0, null);
1339
+ row.splice(this._cols);
1340
+ }
1341
+ break;
1342
+ }
1343
+ case "S":
1344
+ this._scrollUp(Math.max(1, p0));
1345
+ break;
1346
+ case "T":
1347
+ if (params2.length <= 1) this._scrollDown(Math.max(1, p0));
1348
+ break;
1349
+ case "Z": {
1350
+ const n = Math.max(1, p0);
1351
+ for (let i = 0; i < n; i++) {
1352
+ this._cursorX = Math.max(0, (Math.ceil(this._cursorX / 8) - 1) * 8);
1353
+ }
1354
+ break;
1355
+ }
1356
+ case "d":
1357
+ this._cursorY = this._clamp(Math.max(1, p0) - 1, 0, this._rows - 1);
1358
+ break;
1359
+ case "e":
1360
+ this._cursorY = this._clamp(this._cursorY + Math.max(1, p0), 0, this._rows - 1);
1361
+ break;
1362
+ case "r": {
1363
+ const top = this._clamp(Math.max(1, p0) - 1, 0, this._rows - 1);
1364
+ const bottom = this._clamp((p1 === 0 ? this._rows : p1) - 1, 0, this._rows - 1);
1365
+ if (top < bottom) {
1366
+ this._scrollTop = top;
1367
+ this._scrollBottom = bottom;
1368
+ }
1369
+ this._cursorX = 0;
1370
+ this._cursorY = 0;
1371
+ break;
1372
+ }
1373
+ case "s":
1374
+ this._savedCursor = { x: this._cursorX, y: this._cursorY };
1375
+ break;
1376
+ case "u":
1377
+ this._cursorX = this._clamp(this._savedCursor.x, 0, this._cols - 1);
1378
+ this._cursorY = this._clamp(this._savedCursor.y, 0, this._rows - 1);
1379
+ break;
1380
+ case "m":
1381
+ this._applySgr(params2);
1382
+ break;
1383
+ default:
1384
+ break;
1385
+ }
1386
+ }
1387
+ _feed(ch) {
1388
+ const code = ch.charCodeAt(0);
1389
+ switch (this._state) {
1390
+ case 0 /* Normal */:
1391
+ this._feedNormal(ch, code);
1392
+ break;
1393
+ case 1 /* Escape */:
1394
+ this._feedEscape(ch, code);
1395
+ break;
1396
+ case 2 /* Csi */:
1397
+ this._feedCsi(ch, code);
1398
+ break;
1399
+ case 3 /* Osc */:
1400
+ if (code === 7 || code === 156) {
1401
+ this._state = 0 /* Normal */;
1402
+ } else if (code === 27) {
1403
+ this._state = 0 /* Normal */;
1404
+ }
1405
+ break;
1406
+ case 4 /* Str */:
1407
+ if (code === 156 || code === 7) {
1408
+ this._state = 0 /* Normal */;
1409
+ } else if (code === 27) {
1410
+ this._state = 0 /* Normal */;
1411
+ }
1412
+ break;
1413
+ case 5 /* EscCharset */:
1414
+ this._state = 0 /* Normal */;
1415
+ break;
1416
+ case 6 /* EscHash */:
1417
+ this._state = 0 /* Normal */;
1418
+ break;
1419
+ }
1420
+ }
1421
+ _feedNormal(ch, code) {
1422
+ if (code === 27) {
1423
+ this._state = 1 /* Escape */;
1424
+ } else if (code === 155) {
1425
+ this._csiParams = "";
1426
+ this._csiPrivate = "";
1427
+ this._state = 2 /* Csi */;
1428
+ } else if (code === 157) {
1429
+ this._state = 3 /* Osc */;
1430
+ } else if (code === 144 || code === 152 || code === 158 || code === 159) {
1431
+ this._state = 4 /* Str */;
1432
+ } else if (code === 7 || code === 5 || code === 6) {
1433
+ } else if (code === 8) {
1434
+ if (this._cursorX > 0) this._cursorX--;
1435
+ } else if (code === 127) {
1436
+ if (this._cursorX > 0) {
1437
+ this._cursorX--;
1438
+ this._setChar(this._cursorY, this._cursorX, " ");
1439
+ }
1440
+ } else if (code === 9) {
1441
+ this._cursorX = Math.min(this._cols - 1, (Math.floor(this._cursorX / 8) + 1) * 8);
1442
+ } else if (code === 10 || code === 11 || code === 12) {
1443
+ this._newline();
1444
+ } else if (code === 13) {
1445
+ this._cursorX = 0;
1446
+ } else if (code === 14 || code === 15) {
1447
+ } else if (code >= 32 && code !== 127) {
1448
+ this._setChar(this._cursorY, this._cursorX, ch);
1449
+ this._cursorX++;
1450
+ if (this._cursorX >= this._cols) {
1451
+ this._cursorX = 0;
1452
+ this._newline();
1453
+ }
1454
+ }
1455
+ }
1456
+ _feedEscape(ch, code) {
1457
+ this._state = 0 /* Normal */;
1458
+ if (code === 91) {
1459
+ this._csiParams = "";
1460
+ this._csiPrivate = "";
1461
+ this._state = 2 /* Csi */;
1462
+ } else if (code === 93) {
1463
+ this._state = 3 /* Osc */;
1464
+ } else if (code === 80 || code === 88 || code === 94 || code === 95) {
1465
+ this._state = 4 /* Str */;
1466
+ } else if (code === 40 || code === 41 || code === 42 || code === 43 || code === 45 || code === 46) {
1467
+ this._state = 5 /* EscCharset */;
1468
+ } else if (code === 35) {
1469
+ this._state = 6 /* EscHash */;
1470
+ } else if (code === 55) {
1471
+ this._savedCursor = { x: this._cursorX, y: this._cursorY };
1472
+ } else if (code === 56) {
1473
+ this._cursorX = this._clamp(this._savedCursor.x, 0, this._cols - 1);
1474
+ this._cursorY = this._clamp(this._savedCursor.y, 0, this._rows - 1);
1475
+ } else if (code === 68) {
1476
+ this._newline();
1477
+ } else if (code === 69) {
1478
+ this._cursorX = 0;
1479
+ this._newline();
1480
+ } else if (code === 77) {
1481
+ if (this._cursorY === this._scrollTop) {
1482
+ this._scrollDown(1);
1483
+ } else {
1484
+ this._cursorY = Math.max(0, this._cursorY - 1);
1485
+ }
1486
+ } else if (code === 72) {
1487
+ } else if (code === 99) {
1488
+ this._screen = this._makeScreen(this._cols, this._rows);
1489
+ this._cursorX = 0;
1490
+ this._cursorY = 0;
1491
+ this._savedCursor = { x: 0, y: 0 };
1492
+ this._scrollTop = 0;
1493
+ this._scrollBottom = this._rows - 1;
1494
+ this._resetStyle();
1495
+ }
1496
+ }
1497
+ _feedCsi(ch, code) {
1498
+ if (code >= 64 && code <= 126) {
1499
+ this._execCsi(ch);
1500
+ this._state = 0 /* Normal */;
1501
+ } else if (code === 63 || code === 33 || code === 62 || code === 32) {
1502
+ this._csiPrivate = ch;
1503
+ } else if (code >= 48 && code <= 57 || code === 59) {
1504
+ this._csiParams += ch;
1505
+ }
1506
+ }
1507
+ _resetStyle() {
1508
+ this._style = createDefaultStyleState();
1509
+ this._styleSequence = "";
1510
+ }
1511
+ _applySgr(params2) {
1512
+ const normalizedParams = params2.length === 0 ? [0] : params2;
1513
+ for (let index = 0; index < normalizedParams.length; index += 1) {
1514
+ const value = normalizedParams[index] ?? 0;
1515
+ switch (value) {
1516
+ case 0:
1517
+ this._resetStyle();
1518
+ break;
1519
+ case 1:
1520
+ this._style.bold = true;
1521
+ break;
1522
+ case 2:
1523
+ this._style.dim = true;
1524
+ break;
1525
+ case 3:
1526
+ this._style.italic = true;
1527
+ break;
1528
+ case 4:
1529
+ this._style.underline = true;
1530
+ break;
1531
+ case 7:
1532
+ this._style.inverse = true;
1533
+ break;
1534
+ case 9:
1535
+ this._style.strikethrough = true;
1536
+ break;
1537
+ case 21:
1538
+ case 22:
1539
+ this._style.bold = false;
1540
+ this._style.dim = false;
1541
+ break;
1542
+ case 23:
1543
+ this._style.italic = false;
1544
+ break;
1545
+ case 24:
1546
+ this._style.underline = false;
1547
+ break;
1548
+ case 27:
1549
+ this._style.inverse = false;
1550
+ break;
1551
+ case 29:
1552
+ this._style.strikethrough = false;
1553
+ break;
1554
+ case 39:
1555
+ this._style.fg = void 0;
1556
+ break;
1557
+ case 49:
1558
+ this._style.bg = void 0;
1559
+ break;
1560
+ case 38:
1561
+ case 48:
1562
+ index = this._applyExtendedColor(value, normalizedParams, index);
1563
+ break;
1564
+ default:
1565
+ if (value >= 30 && value <= 37 || value >= 90 && value <= 97) {
1566
+ this._style.fg = [value];
1567
+ } else if (value >= 40 && value <= 47 || value >= 100 && value <= 107) {
1568
+ this._style.bg = [value];
1569
+ }
1570
+ break;
1571
+ }
1572
+ }
1573
+ this._styleSequence = serializeStyleState(this._style);
1574
+ }
1575
+ _applyExtendedColor(control, params2, index) {
1576
+ const mode = params2[index + 1];
1577
+ const target = control === 38 ? "fg" : "bg";
1578
+ if (mode === 5) {
1579
+ const paletteIndex = params2[index + 2];
1580
+ if (paletteIndex !== void 0) {
1581
+ this._style[target] = [control, 5, paletteIndex];
1582
+ return index + 2;
1583
+ }
1584
+ return index;
1585
+ }
1586
+ if (mode === 2) {
1587
+ const red = params2[index + 2];
1588
+ const green = params2[index + 3];
1589
+ const blue = params2[index + 4];
1590
+ if (red !== void 0 && green !== void 0 && blue !== void 0) {
1591
+ this._style[target] = [control, 2, red, green, blue];
1592
+ return index + 4;
1593
+ }
1594
+ }
1595
+ return index;
1596
+ }
1597
+ };
1598
+ function createDefaultStyleState() {
1599
+ return {
1600
+ bold: false,
1601
+ dim: false,
1602
+ italic: false,
1603
+ underline: false,
1604
+ inverse: false,
1605
+ strikethrough: false
1606
+ };
1607
+ }
1608
+ function serializeStyleState(state) {
1609
+ const codes = [];
1610
+ if (state.bold) {
1611
+ codes.push(1);
1612
+ }
1613
+ if (state.dim) {
1614
+ codes.push(2);
1615
+ }
1616
+ if (state.italic) {
1617
+ codes.push(3);
1618
+ }
1619
+ if (state.underline) {
1620
+ codes.push(4);
1621
+ }
1622
+ if (state.inverse) {
1623
+ codes.push(7);
1624
+ }
1625
+ if (state.strikethrough) {
1626
+ codes.push(9);
1627
+ }
1628
+ if (state.fg !== void 0) {
1629
+ codes.push(...state.fg);
1630
+ }
1631
+ if (state.bg !== void 0) {
1632
+ codes.push(...state.bg);
1633
+ }
1634
+ return codes.length === 0 ? "" : `\x1B[${codes.join(";")}m`;
1635
+ }
1636
+
1637
+ // src/keys.ts
1638
+ var NAMED_KEY_SEQUENCES = {
1639
+ Enter: "\r",
1640
+ Tab: " ",
1641
+ Escape: "\x1B",
1642
+ Backspace: "\x7F",
1643
+ Delete: "\x1B[3~",
1644
+ ArrowUp: "\x1B[A",
1645
+ ArrowDown: "\x1B[B",
1646
+ ArrowRight: "\x1B[C",
1647
+ ArrowLeft: "\x1B[D",
1648
+ Home: "\x1B[H",
1649
+ End: "\x1B[F",
1650
+ PageUp: "\x1B[5~",
1651
+ PageDown: "\x1B[6~",
1652
+ Space: " "
1653
+ };
1654
+ var NAMED_KEY_LOWER = new Map(
1655
+ Object.entries(NAMED_KEY_SEQUENCES).map(([k, v]) => [k.toLowerCase(), v])
1656
+ );
1657
+ var VALID_KEYS_HINT = `Valid keys: ${Object.keys(NAMED_KEY_SEQUENCES).join(", ")}, Control+<letter>, Alt+<key>`;
1658
+ function unknownKeyError(key) {
1659
+ return new Error(`Unknown terminal key: ${key}. ${VALID_KEYS_HINT}`);
1660
+ }
1661
+ function keyToSequence(key) {
1662
+ const lowerKey = key.toLowerCase();
1663
+ const namedSequence = NAMED_KEY_LOWER.get(lowerKey);
1664
+ if (namedSequence !== void 0) {
1665
+ return namedSequence;
1666
+ }
1667
+ if (lowerKey.startsWith("control+")) {
1668
+ return controlKeyToSequence(key.slice("control+".length));
1669
+ }
1670
+ if (lowerKey.startsWith("alt+")) {
1671
+ const nestedKey = key.slice("alt+".length);
1672
+ if (nestedKey.length === 0) {
1673
+ throw unknownKeyError(key);
1674
+ }
1675
+ if (nestedKey.length === 1) {
1676
+ return "\x1B" + nestedKey;
1677
+ }
1678
+ try {
1679
+ return "\x1B" + keyToSequence(nestedKey);
1680
+ } catch {
1681
+ throw unknownKeyError(key);
1682
+ }
1683
+ }
1684
+ if (key.length === 1) {
1685
+ return key;
1686
+ }
1687
+ throw unknownKeyError(key);
1688
+ }
1689
+ function controlKeyToSequence(controlKey) {
1690
+ if (controlKey.length !== 1) {
1691
+ throw unknownKeyError(`Control+${controlKey}`);
1692
+ }
1693
+ const uppercaseLetter = controlKey.toUpperCase();
1694
+ const charCode = uppercaseLetter.charCodeAt(0);
1695
+ if (charCode < 65 || charCode > 90) {
1696
+ throw unknownKeyError(`Control+${controlKey}`);
1697
+ }
1698
+ return String.fromCharCode(charCode - 64);
1699
+ }
1700
+
1701
+ // src/terminal-screen.ts
1702
+ var TerminalScreen = class {
1703
+ lines;
1704
+ rawLines;
1705
+ cursor;
1706
+ size;
1707
+ constructor({
1708
+ lines,
1709
+ rawLines,
1710
+ cursor,
1711
+ size
1712
+ }) {
1713
+ this.lines = Object.freeze(lines.map((line) => stripAnsi(line)));
1714
+ this.rawLines = Object.freeze([...rawLines]);
1715
+ this.cursor = Object.freeze({ ...cursor });
1716
+ this.size = Object.freeze({ ...size });
1717
+ Object.freeze(this);
1718
+ }
1719
+ get text() {
1720
+ return this.lines.join("\n");
1721
+ }
1722
+ contains(substring) {
1723
+ return this.text.includes(substring);
1724
+ }
1725
+ line(index) {
1726
+ const normalizedIndex = index < 0 ? this.lines.length + index : index;
1727
+ const line = this.lines[normalizedIndex];
1728
+ if (line === void 0) {
1729
+ throw new RangeError(`Line index out of bounds: ${index}`);
1730
+ }
1731
+ return line;
1732
+ }
1733
+ };
1734
+
1735
+ // src/terminal-session.ts
1736
+ var DEFAULT_COLS = 120;
1737
+ var DEFAULT_ROWS = 40;
1738
+ var DEFAULT_TIMEOUT_MS = 1e4;
1739
+ var WAIT_FOR_POLL_MS = 10;
1740
+ var TYPE_DELAY_MS = 15;
1741
+ var CLOSE_AFTER_SIGNAL_GRACE_MS = 250;
1742
+ var CLOSE_AFTER_SIGTERM_MS = 1e3;
1743
+ var TerminalSession = class {
1744
+ id;
1745
+ command;
1746
+ pid;
1747
+ exitCode = null;
1748
+ pty;
1749
+ terminal;
1750
+ emitter = new EventEmitter();
1751
+ exitPromise;
1752
+ rawBuffer = "";
1753
+ lastDataAt = Date.now();
1754
+ currentCols;
1755
+ currentRows;
1756
+ closeRequested = false;
1757
+ signalRequested = false;
1758
+ constructor({
1759
+ id,
1760
+ command,
1761
+ args = [],
1762
+ cwd = process.cwd(),
1763
+ env = process.env,
1764
+ cols = DEFAULT_COLS,
1765
+ rows = DEFAULT_ROWS,
1766
+ observe = false
1767
+ }) {
1768
+ this.id = id;
1769
+ this.command = command;
1770
+ this.currentCols = cols;
1771
+ this.currentRows = rows;
1772
+ this.terminal = new TerminalBuffer(cols, rows);
1773
+ this.pty = createPtyProcess({ command, args, cwd, env, cols, rows });
1774
+ this.pid = this.pty.pid;
1775
+ const dataSubscription = this.pty.onData((chunk) => {
1776
+ this.rawBuffer += chunk;
1777
+ this.lastDataAt = Date.now();
1778
+ this.terminal.write(chunk);
1779
+ if (observe) {
1780
+ process.stderr.write(chunk);
1781
+ }
1782
+ });
1783
+ let exitSubscription;
1784
+ this.exitPromise = new Promise((resolve) => {
1785
+ exitSubscription = this.pty.onExit(({ exitCode }) => {
1786
+ if (this.exitCode !== null) {
1787
+ resolve(this.exitCode);
1788
+ return;
1789
+ }
1790
+ this.exitCode = exitCode;
1791
+ dataSubscription.dispose();
1792
+ exitSubscription?.dispose();
1793
+ this.emitter.emit("exit", exitCode);
1794
+ resolve(exitCode);
1795
+ });
1796
+ });
1797
+ }
1798
+ async type(text) {
1799
+ for (const character of text) {
1800
+ await this.send(character);
1801
+ await sleep(TYPE_DELAY_MS);
1802
+ }
1803
+ }
1804
+ async fill(text) {
1805
+ await this.send(text.replace(/\r?\n/g, "\r"));
1806
+ }
1807
+ async press(key) {
1808
+ await this.send(keyToSequence(key));
1809
+ }
1810
+ async send(raw) {
1811
+ if (this.exitCode !== null) {
1812
+ return;
1813
+ }
1814
+ this.pty.write(raw);
1815
+ }
1816
+ async signal(sig) {
1817
+ if (this.exitCode !== null) {
1818
+ return;
1819
+ }
1820
+ this.signalRequested = true;
1821
+ this.pty.kill(sig);
1822
+ }
1823
+ async waitFor(pattern, opts) {
1824
+ const timeout = opts?.timeout ?? DEFAULT_TIMEOUT_MS;
1825
+ const startedAt = Date.now();
1826
+ while (Date.now() - startedAt <= timeout) {
1827
+ const matched = matchPattern(this.rawBuffer, pattern);
1828
+ if (matched !== null) {
1829
+ return matched;
1830
+ }
1831
+ await sleep(WAIT_FOR_POLL_MS);
1832
+ }
1833
+ throw new Error(`Timed out waiting for pattern after ${timeout}ms: ${String(pattern)}`);
1834
+ }
1835
+ async waitForQuiet(ms) {
1836
+ while (true) {
1837
+ const remaining = ms - (Date.now() - this.lastDataAt);
1838
+ if (remaining <= 0) {
1839
+ return;
1840
+ }
1841
+ await sleep(remaining);
1842
+ }
1843
+ }
1844
+ async screen() {
1845
+ const rawLines = [];
1846
+ for (let row = 0; row < this.currentRows; row += 1) {
1847
+ rawLines.push(this.terminal.renderLine(row));
1848
+ }
1849
+ return new TerminalScreen({
1850
+ lines: rawLines,
1851
+ rawLines,
1852
+ cursor: {
1853
+ row: this.terminal.displayBuffer.cursorY,
1854
+ col: this.terminal.displayBuffer.cursorX
1855
+ },
1856
+ size: {
1857
+ rows: this.currentRows,
1858
+ cols: this.currentCols
1859
+ }
1860
+ });
1861
+ }
1862
+ async history(opts) {
1863
+ const normalized = normalizeHistoryBuffer(stripAnsi(this.rawBuffer));
1864
+ const lines = splitHistoryLines(normalized);
1865
+ if (opts?.last === void 0) {
1866
+ return lines;
1867
+ }
1868
+ const start = Math.max(0, lines.length - opts.last);
1869
+ return lines.slice(start);
1870
+ }
1871
+ async resize(cols, rows) {
1872
+ this.currentCols = cols;
1873
+ this.currentRows = rows;
1874
+ if (this.exitCode === null) {
1875
+ this.pty.resize(cols, rows);
1876
+ }
1877
+ this.terminal.resize(cols, rows);
1878
+ }
1879
+ async waitForExit(opts) {
1880
+ if (this.exitCode !== null) {
1881
+ return this.exitCode;
1882
+ }
1883
+ if (opts?.timeout !== void 0) {
1884
+ const result = await waitForExit(this.exitPromise, opts.timeout);
1885
+ if (result === null) {
1886
+ throw new Error(`Timed out waiting for process to exit after ${opts.timeout}ms`);
1887
+ }
1888
+ return result;
1889
+ }
1890
+ return this.exitPromise;
1891
+ }
1892
+ async close() {
1893
+ if (this.exitCode !== null) {
1894
+ return this.exitCode;
1895
+ }
1896
+ if (!this.closeRequested) {
1897
+ this.closeRequested = true;
1898
+ const gracefulExitCode = await waitForExit(this.exitPromise, CLOSE_AFTER_SIGNAL_GRACE_MS);
1899
+ if (gracefulExitCode !== null) {
1900
+ return gracefulExitCode;
1901
+ }
1902
+ if (this.signalRequested) {
1903
+ return this.exitPromise;
1904
+ }
1905
+ if (this.exitCode === null) {
1906
+ this.pty.kill("SIGTERM");
1907
+ const afterSigterm = await waitForExit(this.exitPromise, CLOSE_AFTER_SIGTERM_MS);
1908
+ if (afterSigterm !== null) {
1909
+ return afterSigterm;
1910
+ }
1911
+ }
1912
+ if (this.exitCode === null) {
1913
+ this.pty.kill("SIGKILL");
1914
+ }
1915
+ }
1916
+ return this.exitPromise;
1917
+ }
1918
+ on(event, cb) {
1919
+ this.emitter.on(event, cb);
1920
+ }
1921
+ };
1922
+ function createPtyProcess({
1923
+ command,
1924
+ args,
1925
+ cwd,
1926
+ env,
1927
+ cols,
1928
+ rows
1929
+ }) {
1930
+ try {
1931
+ return nodePty.spawn(command, args, {
1932
+ cwd,
1933
+ env,
1934
+ cols,
1935
+ rows,
1936
+ encoding: "utf8"
1937
+ });
1938
+ } catch {
1939
+ return createChildProcessFallback({ command, args, cwd, env });
1940
+ }
1941
+ }
1942
+ function createChildProcessFallback({
1943
+ command,
1944
+ args,
1945
+ cwd,
1946
+ env
1947
+ }) {
1948
+ const child = spawnChildProcess(command, args, {
1949
+ cwd,
1950
+ env,
1951
+ stdio: ["pipe", "pipe", "pipe"]
1952
+ });
1953
+ return new ChildProcessFallback(child);
1954
+ }
1955
+ var ChildProcessFallback = class {
1956
+ pid;
1957
+ child;
1958
+ dataEmitter = new EventEmitter();
1959
+ exitEmitter = new EventEmitter();
1960
+ constructor(child) {
1961
+ this.child = child;
1962
+ this.pid = child.pid ?? -1;
1963
+ child.stdout.setEncoding("utf8");
1964
+ child.stderr.setEncoding("utf8");
1965
+ child.stdout.on("data", this.handleData);
1966
+ child.stderr.on("data", this.handleData);
1967
+ child.on("exit", (exitCode, signal) => {
1968
+ this.exitEmitter.emit("exit", {
1969
+ exitCode: exitCode ?? signalToExitCode(signal),
1970
+ signal: void 0
1971
+ });
1972
+ });
1973
+ }
1974
+ write(data) {
1975
+ this.child.stdin.write(data);
1976
+ }
1977
+ resize() {
1978
+ }
1979
+ kill(signal) {
1980
+ this.child.kill(signal);
1981
+ }
1982
+ onData(listener) {
1983
+ this.dataEmitter.on("data", listener);
1984
+ return {
1985
+ dispose: () => {
1986
+ this.dataEmitter.off("data", listener);
1987
+ }
1988
+ };
1989
+ }
1990
+ onExit(listener) {
1991
+ this.exitEmitter.on("exit", listener);
1992
+ return {
1993
+ dispose: () => {
1994
+ this.exitEmitter.off("exit", listener);
1995
+ }
1996
+ };
1997
+ }
1998
+ handleData = (chunk) => {
1999
+ this.dataEmitter.emit("data", String(chunk));
2000
+ };
2001
+ };
2002
+ function signalToExitCode(signal) {
2003
+ if (signal === null) {
2004
+ return 0;
2005
+ }
2006
+ const signalNumbers = {
2007
+ SIGTERM: 15,
2008
+ SIGINT: 2,
2009
+ SIGHUP: 1,
2010
+ SIGKILL: 9
2011
+ };
2012
+ const signalNumber = signalNumbers[signal];
2013
+ if (signalNumber === void 0) {
2014
+ return 1;
2015
+ }
2016
+ return 128 + signalNumber;
2017
+ }
2018
+ function matchPattern(buffer, pattern) {
2019
+ const clean = normalizeHistoryBuffer(stripAnsi(buffer));
2020
+ for (const line of clean.split("\n")) {
2021
+ if (typeof pattern === "string") {
2022
+ if (line.includes(pattern)) return line;
2023
+ } else {
2024
+ const flags = removeCharacter(pattern.flags, "g");
2025
+ if (new RegExp(pattern.source, flags).test(line)) return line;
2026
+ }
2027
+ }
2028
+ return null;
2029
+ }
2030
+ function removeCharacter(input, charToRemove) {
2031
+ let output = "";
2032
+ for (const character of input) {
2033
+ if (character !== charToRemove) {
2034
+ output += character;
2035
+ }
2036
+ }
2037
+ return output;
2038
+ }
2039
+ function splitHistoryLines(input) {
2040
+ const lines = input.split("\n");
2041
+ if (lines.length > 0 && lines[lines.length - 1] === "") {
2042
+ lines.pop();
2043
+ }
2044
+ return lines;
2045
+ }
2046
+ function normalizeHistoryBuffer(input) {
2047
+ let output = "";
2048
+ for (const character of input) {
2049
+ if (character === "\r" || character === "\b") {
2050
+ continue;
2051
+ }
2052
+ output += character;
2053
+ }
2054
+ return output;
2055
+ }
2056
+ function sleep(ms) {
2057
+ return new Promise((resolve) => {
2058
+ setTimeout(resolve, ms);
2059
+ });
2060
+ }
2061
+ async function waitForExit(exitPromise, timeout) {
2062
+ return new Promise((resolve) => {
2063
+ let settled = false;
2064
+ const timer = setTimeout(() => {
2065
+ if (settled) {
2066
+ return;
2067
+ }
2068
+ settled = true;
2069
+ resolve(null);
2070
+ }, timeout);
2071
+ void exitPromise.then((code) => {
2072
+ if (settled) {
2073
+ return;
2074
+ }
2075
+ settled = true;
2076
+ clearTimeout(timer);
2077
+ resolve(code);
2078
+ });
2079
+ });
2080
+ }
2081
+
2082
+ // src/terminal-pilot.ts
2083
+ var DEFAULT_COLS2 = 120;
2084
+ var DEFAULT_ROWS2 = 40;
2085
+ var TerminalPilot = class _TerminalPilot {
2086
+ sessionMap = /* @__PURE__ */ new Map();
2087
+ static async launch() {
2088
+ return new _TerminalPilot();
2089
+ }
2090
+ async newSession(opts) {
2091
+ const session = new TerminalSession({
2092
+ id: randomUUID(),
2093
+ command: opts.command,
2094
+ args: opts.args,
2095
+ cwd: opts.cwd,
2096
+ env: opts.env,
2097
+ cols: opts.cols ?? DEFAULT_COLS2,
2098
+ rows: opts.rows ?? DEFAULT_ROWS2,
2099
+ observe: opts.observe ?? false
2100
+ });
2101
+ this.sessionMap.set(session.id, session);
2102
+ return session;
2103
+ }
2104
+ getSession(id) {
2105
+ const session = this.sessionMap.get(id);
2106
+ if (session === void 0) {
2107
+ throw new Error(`Session not found: ${id}`);
2108
+ }
2109
+ return session;
2110
+ }
2111
+ deleteSession(id) {
2112
+ this.sessionMap.delete(id);
2113
+ }
2114
+ sessions() {
2115
+ return [...this.sessionMap.values()].filter((s) => s.exitCode === null);
2116
+ }
2117
+ async close() {
2118
+ const sessions = [...this.sessionMap.values()];
2119
+ try {
2120
+ await Promise.all(sessions.map((session) => session.close()));
2121
+ } finally {
2122
+ this.sessionMap.clear();
2123
+ }
2124
+ }
2125
+ };
2126
+
2127
+ // src/commands/runtime.ts
2128
+ var SESSION_ENV_VAR = "TERMINAL_PILOT_SESSION";
2129
+ var sharedRuntime;
2130
+ function getTerminalPilotRuntime(runtime) {
2131
+ if (runtime !== void 0) {
2132
+ return runtime;
2133
+ }
2134
+ sharedRuntime ??= createTerminalPilotRuntime();
2135
+ return sharedRuntime;
2136
+ }
2137
+ function createTerminalPilotRuntime(options = {}) {
2138
+ const launchPilot = options.launchPilot ?? TerminalPilot.launch;
2139
+ const nameToId = /* @__PURE__ */ new Map();
2140
+ const idToName = /* @__PURE__ */ new Map();
2141
+ let pilotPromise;
2142
+ function getRequestedName(name, env) {
2143
+ return name ?? env?.get(SESSION_ENV_VAR);
2144
+ }
2145
+ async function getPilot() {
2146
+ pilotPromise ??= launchPilot();
2147
+ return pilotPromise;
2148
+ }
2149
+ function nextSessionName() {
2150
+ let index = 1;
2151
+ while (nameToId.has(`s${index}`)) {
2152
+ index += 1;
2153
+ }
2154
+ return `s${index}`;
2155
+ }
2156
+ function rememberSession(name, session) {
2157
+ nameToId.set(name, session.id);
2158
+ idToName.set(session.id, name);
2159
+ return { name, session };
2160
+ }
2161
+ function forgetSession(name, sessionId) {
2162
+ nameToId.delete(name);
2163
+ idToName.delete(sessionId);
2164
+ }
2165
+ function formatAvailableSessions(names) {
2166
+ if (names.length === 0) {
2167
+ return "No active sessions are available.";
2168
+ }
2169
+ return `Available sessions: ${names.join(", ")}.`;
2170
+ }
2171
+ async function lookupNamedSession(name) {
2172
+ const sessionId = nameToId.get(name);
2173
+ if (sessionId === void 0) {
2174
+ const active = await listSessions();
2175
+ throw new UserError(`Session "${name}" was not found. ${formatAvailableSessions(active.map((entry) => entry.name))}`);
2176
+ }
2177
+ const pilot = await getPilot();
2178
+ try {
2179
+ return { name, session: pilot.getSession(sessionId) };
2180
+ } catch {
2181
+ forgetSession(name, sessionId);
2182
+ const active = await listSessions();
2183
+ throw new UserError(`Session "${name}" was not found. ${formatAvailableSessions(active.map((entry) => entry.name))}`);
2184
+ }
2185
+ }
2186
+ async function listSessions() {
2187
+ const pilot = await getPilot();
2188
+ return pilot.sessions().flatMap((session) => {
2189
+ const name = idToName.get(session.id);
2190
+ if (name === void 0) {
2191
+ return [];
2192
+ }
2193
+ return [{ name, session }];
2194
+ });
2195
+ }
2196
+ return {
2197
+ async createSession(params2, env) {
2198
+ const requestedName = getRequestedName(params2.session, env) ?? nextSessionName();
2199
+ if (nameToId.has(requestedName)) {
2200
+ throw new UserError(`Session "${requestedName}" already exists.`);
2201
+ }
2202
+ const pilot = await getPilot();
2203
+ const session = await pilot.newSession({
2204
+ command: params2.command,
2205
+ args: params2.args,
2206
+ cwd: params2.cwd,
2207
+ cols: params2.cols,
2208
+ rows: params2.rows,
2209
+ observe: params2.observe
2210
+ });
2211
+ return rememberSession(requestedName, session);
2212
+ },
2213
+ async resolveSession(name, env) {
2214
+ const requestedName = getRequestedName(name, env);
2215
+ if (requestedName !== void 0) {
2216
+ return lookupNamedSession(requestedName);
2217
+ }
2218
+ const active = await listSessions();
2219
+ if (active.length === 1) {
2220
+ return active[0];
2221
+ }
2222
+ if (active.length === 0) {
2223
+ throw new UserError("No active sessions. Create one with create-session.");
2224
+ }
2225
+ throw new UserError(
2226
+ `Multiple active sessions require an explicit session name. Pass --session or set ${SESSION_ENV_VAR}. ${formatAvailableSessions(active.map((entry) => entry.name))}`
2227
+ );
2228
+ },
2229
+ async closeSession(name, env) {
2230
+ const namedSession = await this.resolveSession(name, env);
2231
+ const exitCode = await namedSession.session.close();
2232
+ const pilot = await getPilot();
2233
+ pilot.deleteSession(namedSession.session.id);
2234
+ forgetSession(namedSession.name, namedSession.session.id);
2235
+ return {
2236
+ exitCode,
2237
+ name: namedSession.name
2238
+ };
2239
+ },
2240
+ listSessions,
2241
+ async close() {
2242
+ if (pilotPromise === void 0) {
2243
+ return;
2244
+ }
2245
+ const pilot = await pilotPromise;
2246
+ await pilot.close();
2247
+ pilotPromise = void 0;
2248
+ nameToId.clear();
2249
+ idToName.clear();
2250
+ }
2251
+ };
2252
+ }
2253
+
2254
+ // src/commands/screenshot.ts
2255
+ var params = S.Object({
2256
+ session: S.Optional(S.String({ short: "s", description: "Session name" })),
2257
+ output: S.String({ short: "o", description: "Path to the output PNG file" }),
2258
+ window: S.Optional(S.Boolean({ description: "Include terminal window chrome", default: true })),
2259
+ padding: S.Optional(S.Number({ short: "p", description: "Padding around terminal content" }))
2260
+ });
2261
+ var screenshot = defineCommand({
2262
+ name: "screenshot",
2263
+ description: "Capture the current terminal screen as a PNG image",
2264
+ scope: ["cli"],
2265
+ params,
2266
+ handler: async ({ params: params2, env, terminalPilotRuntime }) => {
2267
+ const namedSession = await getTerminalPilotRuntime(terminalPilotRuntime).resolveSession(params2.session, env);
2268
+ const screen = await namedSession.session.screen();
2269
+ await renderTerminalPng(screen.rawLines.join("\n"), {
2270
+ output: params2.output,
2271
+ window: params2.window,
2272
+ padding: params2.padding
2273
+ });
2274
+ return void 0;
2275
+ }
2276
+ });
2277
+ export {
2278
+ screenshot
2279
+ };
2280
+ //# sourceMappingURL=screenshot.js.map