moderado 0.4.46 → 0.4.50

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.
@@ -12,6 +12,8 @@ export interface WelcomeLayoutOptions {
12
12
  input?: string;
13
13
  inputCaret?: number;
14
14
  width?: number;
15
+ /** Input rows the composer may paint before it scrolls; defaults to the terminal height. */
16
+ maxInputRows?: number;
15
17
  /** When set, render the full chat window (question row 1 + answer) instead of the logo header. */
16
18
  chatQuestion?: string;
17
19
  chatAnswer?: string;
@@ -50,10 +52,55 @@ export declare function stripAnsi(str: string): string;
50
52
  export declare const MOUSE_REPORT_PATTERN: RegExp;
51
53
  export declare const MOUSE_REPORT_ON = "\u001B[?1000h\u001B[?1006h";
52
54
  export declare const MOUSE_REPORT_OFF = "\u001B[?1000l\u001B[?1006l";
53
- export declare function composerInputWindow(input: string, caret: number, width: number): {
55
+ /**
56
+ * Ask the terminal to report keys whose legacy encoding is ambiguous
57
+ * (XTMODKEYS level 1), which is the only way Shift+Enter can be told apart from
58
+ * Enter. Level 1 leaves unambiguous keys — plain Ctrl+C, Shift+Tab, shifted
59
+ * letters — on their legacy bytes, so nothing else changes behaviour.
60
+ */
61
+ export declare const KEY_MODIFIER_REPORT_ON = "\u001B[>4;1m";
62
+ export declare const KEY_MODIFIER_REPORT_OFF = "\u001B[>4;0m";
63
+ /**
64
+ * One visual row of the composer input, as a slice of the raw text. Keeping the
65
+ * offsets means the caret, a click, and a repaint all address the same string
66
+ * instead of a re-flowed copy of it.
67
+ */
68
+ export interface ComposerRow {
54
69
  text: string;
70
+ /** Index in the raw input where the row starts. */
71
+ start: number;
72
+ /** Index where the row ends (exclusive). */
73
+ end: number;
74
+ }
75
+ /** Split the composer text into the rows the card paints, wrapping at words. */
76
+ export declare function wrapComposerInput(input: string, width: number): ComposerRow[];
77
+ export interface ComposerView {
78
+ /** Rows to paint, already windowed to the rows the terminal can spare. */
79
+ rows: ComposerRow[];
80
+ /** Row index of the caret inside `rows`. */
81
+ caretRow: number;
82
+ /** Cell offset of the caret inside its row, 0 being the first text cell. */
55
83
  caretColumn: number;
56
- };
84
+ /** Rows scrolled out of view above and below the window. */
85
+ hiddenAbove: number;
86
+ hiddenBelow: number;
87
+ }
88
+ /**
89
+ * Resolve the composer text into painted rows plus the caret's place in them.
90
+ * The window follows the caret, so a long prompt scrolls inside the box instead
91
+ * of growing the card past the bottom of the terminal.
92
+ */
93
+ export declare function composerView(input: string, caret: number, width: number, maxRows?: number): ComposerView;
94
+ /** Caret index for a click `cells` into a painted row. */
95
+ export declare function composerCaretIndex(input: string, rows: ComposerRow[], rowOffset: number, cells: number): number;
96
+ /**
97
+ * Input rows the composer may show before it scrolls in place.
98
+ *
99
+ * The chat frame needs eight rows above the card (header, question, status,
100
+ * answer) and the card keeps four rows of its own furniture, so anything taller
101
+ * than this would scroll the composer off the screen.
102
+ */
103
+ export declare function getComposerInputMaxRows(height?: number): number;
57
104
  /** Card width: nearly full terminal width, like OpenCode/Cline. */
58
105
  export declare function getWelcomeCardWidth(width?: number): number;
59
106
  /** Chat fills the available columns; the startup Welcome card keeps its own width. */
@@ -148,11 +195,12 @@ export declare function wrapText(text: string, maxWidth: number): string[];
148
195
  */
149
196
  export declare function renderChatScreen(options: WelcomeLayoutOptions, height?: number): string;
150
197
  /**
151
- * Place the terminal cursor on the input row. Passing the layout's
152
- * `composerTop` addresses the row absolutely, which stays correct when a
153
- * queued-commands box grows the card; the relative form is the legacy path.
198
+ * Place the terminal cursor on the caret's row inside the input box. Passing
199
+ * the layout's `composerTop` addresses the row absolutely, which stays correct
200
+ * when the card grows (a queued-commands box or a wrapped prompt); the relative
201
+ * form is the legacy path.
154
202
  */
155
- export declare function renderChatComposerCursor(options: Pick<WelcomeLayoutOptions, 'width'>, inputLength: number, composerTop?: number): string;
203
+ export declare function renderChatComposerCursor(options: Pick<WelcomeLayoutOptions, 'width'>, input: string, composerTop?: number): string;
156
204
  export declare function navigateQuestionHistory(history: string[], direction: -1 | 1, index: number, draft: string): {
157
205
  input: string;
158
206
  index: number;
@@ -315,7 +363,9 @@ export declare function getWelcomeBottomPadding(options: WelcomeLayoutOptions, h
315
363
  export declare function renderCenteredWelcomeScreen(options: WelcomeLayoutOptions, height?: number): string;
316
364
  /** Compose an OpenCode-style popup layer without losing the welcome background. */
317
365
  export declare function renderWelcomePopupLayer(options: WelcomeLayoutOptions, popupLines: string[], width?: number, height?: number): string;
318
- export declare function renderHelpPopupBox(version: string, workspace: string, width?: number): string[];
366
+ export declare function renderHelpPopupBox(_version: string, workspace: string, width?: number): string[];
367
+ /** Render a centered "About" popup with version and copyright info. */
368
+ export declare function renderAboutPopupBox(version: string): string[];
319
369
  export declare let terminalCleanExitDone: boolean;
320
370
  /** Mark that a clean exit already restored + cleared the terminal (guards the process 'exit' hook). */
321
371
  export declare function notifyCleanExit(): void;
@@ -1 +1 @@
1
- {"version":3,"file":"welcome.d.ts","sourceRoot":"","sources":["../../src/ui/welcome.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"welcome.d.ts","sourceRoot":"","sources":["../../src/ui/welcome.ts"],"names":[],"mappings":"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"}
@@ -7,7 +7,7 @@ import { overlayCentered, dimLines, shadowUnder } from './popup.js';
7
7
  import { getHelpCommandGroups } from './help_content.js';
8
8
  export const MODERADO_ASCII_LOGO = [
9
9
  '\x1b[38;5;75m __ _______ ____ __________ ___ ____ ____ \x1b[0m',
10
- '\x1b[38;5;75m / |/ / __ \\/ __ \\/ ____/ __ \\/ | / __ \\/ __ \\\x1b[0m',
10
+ '\x1b[38;5;75m / |/ / __ \\/ __ \\/ ____/ __ \\/ | / __ \\/ __ \x1b[0m',
11
11
  '\x1b[38;5;81m / /|_/ / / / / / / / __/ / /_/ / /| | / / / / / / /\x1b[0m',
12
12
  '\x1b[38;5;81m / / / / /_/ / /_/ / /___/ _, _/ ___ |/ /_/ / /_/ / \x1b[0m',
13
13
  '\x1b[38;5;87m/_/ /_/\\____/_____/_____/_/ |_/_/ |_/_____/\\____/ \x1b[0m',
@@ -31,10 +31,125 @@ function visibleLen(str) {
31
31
  export const MOUSE_REPORT_PATTERN = /\x1b\[<(\d+);(\d+);(\d+)([Mm])/g;
32
32
  export const MOUSE_REPORT_ON = '\x1b[?1000h\x1b[?1006h';
33
33
  export const MOUSE_REPORT_OFF = '\x1b[?1000l\x1b[?1006l';
34
- export function composerInputWindow(input, caret, width) {
35
- const capacity = Math.max(1, width - 3);
36
- const start = Math.max(0, Math.min(caret, input.length - capacity));
37
- return { text: input.slice(start, start + capacity), caretColumn: caret - start };
34
+ /**
35
+ * Ask the terminal to report keys whose legacy encoding is ambiguous
36
+ * (XTMODKEYS level 1), which is the only way Shift+Enter can be told apart from
37
+ * Enter. Level 1 leaves unambiguous keys — plain Ctrl+C, Shift+Tab, shifted
38
+ * letters — on their legacy bytes, so nothing else changes behaviour.
39
+ */
40
+ export const KEY_MODIFIER_REPORT_ON = '\x1b[>4;1m';
41
+ export const KEY_MODIFIER_REPORT_OFF = '\x1b[>4;0m';
42
+ /**
43
+ * Cells a composer row spends on its `❯ ` marker, plus one spare so a full row
44
+ * never reaches the last column (a terminal that wraps on the final cell would
45
+ * push the whole card down a row).
46
+ */
47
+ const COMPOSER_ROW_OVERHEAD = 3;
48
+ /** Split the composer text into the rows the card paints, wrapping at words. */
49
+ export function wrapComposerInput(input, width) {
50
+ const capacity = Math.max(1, width - COMPOSER_ROW_OVERHEAD);
51
+ const rows = [];
52
+ let segmentStart = 0;
53
+ for (;;) {
54
+ const newline = input.indexOf('\n', segmentStart);
55
+ const segmentEnd = newline === -1 ? input.length : newline;
56
+ if (segmentStart === segmentEnd) {
57
+ // An empty line (a bare line feed, or the tail after a trailing one) is
58
+ // still a row: the caret has to have somewhere to sit.
59
+ rows.push({ text: '', start: segmentStart, end: segmentStart });
60
+ }
61
+ else {
62
+ let cursor = segmentStart;
63
+ while (cursor < segmentEnd) {
64
+ let end = cursor;
65
+ let cells = 0;
66
+ let lastSpace = -1;
67
+ while (end < segmentEnd) {
68
+ const char = String.fromCodePoint(input.codePointAt(end));
69
+ const charWidth = displayWidth(char);
70
+ if (cells + charWidth > capacity)
71
+ break;
72
+ cells += charWidth;
73
+ end += char.length;
74
+ if (char === ' ')
75
+ lastSpace = end;
76
+ }
77
+ if (end >= segmentEnd) {
78
+ rows.push({ text: input.slice(cursor, segmentEnd), start: cursor, end: segmentEnd });
79
+ break;
80
+ }
81
+ // Break after the last space so words stay whole; the space itself is
82
+ // swallowed by the break rather than trailing the row. A row that would
83
+ // come out empty (a space at the wrap point) falls through to a hard
84
+ // break instead of emitting a blank row.
85
+ if (lastSpace - 1 > cursor) {
86
+ rows.push({ text: input.slice(cursor, lastSpace - 1), start: cursor, end: lastSpace - 1 });
87
+ cursor = lastSpace;
88
+ }
89
+ else {
90
+ rows.push({ text: input.slice(cursor, end), start: cursor, end });
91
+ cursor = end;
92
+ }
93
+ }
94
+ }
95
+ if (newline === -1)
96
+ return rows;
97
+ segmentStart = newline + 1;
98
+ }
99
+ }
100
+ /**
101
+ * Resolve the composer text into painted rows plus the caret's place in them.
102
+ * The window follows the caret, so a long prompt scrolls inside the box instead
103
+ * of growing the card past the bottom of the terminal.
104
+ */
105
+ export function composerView(input, caret, width, maxRows = Number.POSITIVE_INFINITY) {
106
+ const all = wrapComposerInput(input, width);
107
+ const clampedCaret = Math.max(0, Math.min(caret, input.length));
108
+ // The last row whose slice contains the caret: at a hard wrap the caret
109
+ // belongs to the row that starts there, at a swallowed space to the row that
110
+ // ends there.
111
+ let caretRow = 0;
112
+ for (let index = 0; index < all.length; index += 1) {
113
+ if (all[index].start <= clampedCaret && clampedCaret <= all[index].end)
114
+ caretRow = index;
115
+ }
116
+ const limit = Math.max(1, Math.floor(maxRows));
117
+ const start = Math.max(0, Math.min(caretRow - limit + 1, all.length - limit));
118
+ const rows = all.slice(start, start + limit);
119
+ const row = all[caretRow];
120
+ return {
121
+ rows,
122
+ caretRow: caretRow - start,
123
+ caretColumn: Math.min(Math.max(1, width - COMPOSER_ROW_OVERHEAD), displayWidth(input.slice(row.start, clampedCaret))),
124
+ hiddenAbove: start,
125
+ hiddenBelow: all.length - start - rows.length,
126
+ };
127
+ }
128
+ /** Caret index for a click `cells` into a painted row. */
129
+ export function composerCaretIndex(input, rows, rowOffset, cells) {
130
+ const row = rows[Math.max(0, Math.min(rows.length - 1, rowOffset))];
131
+ let index = row.start;
132
+ let used = 0;
133
+ while (index < row.end) {
134
+ const char = String.fromCodePoint(input.codePointAt(index));
135
+ const charWidth = displayWidth(char);
136
+ if (used + charWidth > cells)
137
+ break;
138
+ used += charWidth;
139
+ index += char.length;
140
+ }
141
+ return index;
142
+ }
143
+ /**
144
+ * Input rows the composer may show before it scrolls in place.
145
+ *
146
+ * The chat frame needs eight rows above the card (header, question, status,
147
+ * answer) and the card keeps four rows of its own furniture, so anything taller
148
+ * than this would scroll the composer off the screen.
149
+ */
150
+ export function getComposerInputMaxRows(height) {
151
+ const terminalHeight = height ?? process.stdout.rows ?? 24;
152
+ return Math.max(1, terminalHeight - 13);
38
153
  }
39
154
  /** Card width: nearly full terminal width, like OpenCode/Cline. */
40
155
  export function getWelcomeCardWidth(width) {
@@ -249,11 +364,14 @@ export function renderWelcomeCard(options) {
249
364
  ? getWelcomeCardWidth(options.width)
250
365
  : getChatCardWidth(options.width);
251
366
  const indent = ' '.repeat(getWelcomeIndent(width, options.width));
252
- const displayInput = options.input && options.input.length > 0
253
- ? composerInputWindow(options.input, options.inputCaret ?? options.input.length, width).text
254
- : '\x1b[38;5;242mAsk anything, I am all ears...\x1b[0m';
367
+ const inputText = options.input ?? '';
368
+ const inputView = composerView(inputText, options.inputCaret ?? inputText.length, width, options.maxInputRows ?? getComposerInputMaxRows());
369
+ // The placeholder owns the first row only; a typed prompt is wrapped into as
370
+ // many rows as it needs, so the box grows instead of scrolling sideways.
371
+ const inputRows = inputText.length > 0
372
+ ? inputView.rows.map((row) => row.text)
373
+ : ['\x1b[38;5;242mAsk anything, I am all ears...\x1b[0m'];
255
374
  const promptMarker = '\x1b[1;38;5;75m' + String.fromCodePoint(0x276F) + '\x1b[0;48;5;236m';
256
- const textBox = `${promptMarker} ${displayInput}`;
257
375
  const surface = '\x1b[48;5;236m';
258
376
  const surfaceLine = (content = '') => {
259
377
  const preserved = content.replace(/\x1b\[0m/g, '\x1b[0;48;5;236m');
@@ -341,7 +459,10 @@ export function renderWelcomeCard(options) {
341
459
  if (matching.length)
342
460
  cardLines.push(...renderSuggestionsBox(matching, width, options.commandSelection ?? 0).map(line => indent + line));
343
461
  }
344
- cardLines.push(indent + surfaceLine(), indent + surfaceLine(textBox), indent + surfaceLine(), indent + line4, indent + line5);
462
+ cardLines.push(indent + surfaceLine(),
463
+ // Continuation rows keep the marker's two cells, so wrapped text lines up
464
+ // under the first row's text instead of under the ❯.
465
+ ...inputRows.map((rowText, index) => indent + surfaceLine(`${index === 0 ? promptMarker : ' '} ${rowText}`)), indent + surfaceLine(), indent + line4, indent + line5);
345
466
  if (options.mentionFiles && options.mentionFiles.length > 0) {
346
467
  cardLines.push(...renderMentionSuggestionsBox(options.mentionFiles, options.mentionSelection ?? 0, width).map((line) => indent + line));
347
468
  }
@@ -361,7 +482,8 @@ export const SLASH_COMMANDS = [
361
482
  { name: '/budget', desc: 'Set a per-task token limit' },
362
483
  { name: '/skills', desc: 'List skills; enable or disable user skills' },
363
484
  { name: '/clear', desc: 'Reset conversation memory' },
364
- { name: '/help', desc: 'Display commands, shortcuts & version' },
485
+ { name: '/about', desc: 'Show version and copyright info' },
486
+ { name: '/help', desc: 'Display commands & shortcuts' },
365
487
  { name: '/exit', desc: 'Exit Moderado' },
366
488
  ];
367
489
  export function getMatchingCommands(input) {
@@ -517,7 +639,10 @@ export function renderChatScreen(options, height) {
517
639
  // The composer is measured before the layout: a queued-commands box or a
518
640
  // mention box makes the card taller, and the answer area must give up those
519
641
  // rows instead of pushing the card past the bottom of the viewport.
520
- const composer = renderWelcomeCard(options).split('\n');
642
+ const cardOptions = options.maxInputRows === undefined
643
+ ? { ...options, maxInputRows: getComposerInputMaxRows(terminalHeight) }
644
+ : options;
645
+ const composer = renderWelcomeCard(cardOptions).split('\n');
521
646
  const layout = getChatLayout(terminalWidth, terminalHeight, question, composer.length);
522
647
  const area = layout.area;
523
648
  const indent = ' '.repeat(area.indent);
@@ -594,17 +719,19 @@ export function renderChatScreen(options, height) {
594
719
  return lines.join('\n') + '\n';
595
720
  }
596
721
  /**
597
- * Place the terminal cursor on the input row. Passing the layout's
598
- * `composerTop` addresses the row absolutely, which stays correct when a
599
- * queued-commands box grows the card; the relative form is the legacy path.
722
+ * Place the terminal cursor on the caret's row inside the input box. Passing
723
+ * the layout's `composerTop` addresses the row absolutely, which stays correct
724
+ * when the card grows (a queued-commands box or a wrapped prompt); the relative
725
+ * form is the legacy path.
600
726
  */
601
- export function renderChatComposerCursor(options, inputLength, composerTop) {
727
+ export function renderChatComposerCursor(options, input, composerTop) {
602
728
  const width = getChatCardWidth(options.width);
603
729
  const indent = getWelcomeIndent(width, options.width);
604
- const caretColumn = indent + 3 + Math.min(inputLength, Math.max(0, width - 3));
730
+ const view = composerView(input, input.length, width, getComposerInputMaxRows());
731
+ const caretColumn = indent + 2 + view.caretColumn;
605
732
  if (composerTop !== undefined)
606
- return `\x1b[1 q\x1b[?25h\x1b[${composerTop + 1};${caretColumn}H`;
607
- return `\x1b[1 q\x1b[?25h\x1b[4A\r\x1b[${indent + 2 + Math.min(inputLength, Math.max(0, width - 3))}C`;
733
+ return `\x1b[1 q\x1b[?25h\x1b[${composerTop + 1 + view.caretRow};${caretColumn + 1}H`;
734
+ return `\x1b[1 q\x1b[?25h\x1b[${4 + view.caretRow}A\r\x1b[${caretColumn}C`;
608
735
  }
609
736
  export function navigateQuestionHistory(history, direction, index, draft) {
610
737
  const nextIndex = Math.max(0, Math.min(history.length, index + direction));
@@ -934,7 +1061,7 @@ export function renderWelcomePopupLayer(options, popupLines, width, height) {
934
1061
  const lightPopup = popupLines.map((line) => surface + line.replace(/\x1b\[0m/g, `\x1b[0m${surface}`) + '\x1b[0m');
935
1062
  return background + shadowUnder(lightPopup, cols, rows) + overlayCentered(lightPopup, cols, rows);
936
1063
  }
937
- export function renderHelpPopupBox(version, workspace, width) {
1064
+ export function renderHelpPopupBox(_version, workspace, width) {
938
1065
  const terminalWidth = width ?? (process.stdout.columns || 80);
939
1066
  const targetWidth = 82;
940
1067
  const boxWidth = Math.max(76, Math.min(terminalWidth, targetWidth));
@@ -952,6 +1079,7 @@ export function renderHelpPopupBox(version, workspace, width) {
952
1079
  '\x1b[1;38;5;114mKeyboard Shortcuts:\x1b[0m',
953
1080
  '\x1b[1mTab\x1b[0m Toggle between [Plan] and [Execute] mode',
954
1081
  '\x1b[1mShift+Tab\x1b[0m Toggle Auto-approval on / off for actions',
1082
+ '\x1b[1mShift+Enter\x1b[0m Insert a line break (Ctrl+J works everywhere)',
955
1083
  '\x1b[1mCtrl+C\x1b[0m Cancel active inference or exit session',
956
1084
  '\x1b[1mEsc\x1b[0m Stop the active task',
957
1085
  '\x1b[1mLeft/Right\x1b[0m Move the caret inside the bottom input line',
@@ -959,7 +1087,6 @@ export function renderHelpPopupBox(version, workspace, width) {
959
1087
  '\x1b[1mMouse click\x1b[0m Place the caret on the input line',
960
1088
  '',
961
1089
  `\x1b[38;5;245mWorkspace:\x1b[0m \x1b[38;5;253m${shortWs}\x1b[0m`,
962
- `\x1b[38;5;245mVersion:\x1b[0m \x1b[1;38;5;75m${version}\x1b[0m`,
963
1090
  '',
964
1091
  '\x1b[38;5;244mPress \x1b[1;38;5;75m[Esc]\x1b[0;38;5;244m or \x1b[1;38;5;75m[Enter]\x1b[0;38;5;244m to close\x1b[0m',
965
1092
  ];
@@ -978,6 +1105,31 @@ export function renderHelpPopupBox(version, workspace, width) {
978
1105
  lines.push('\x1b[38;5;240m╰' + '─'.repeat(boxWidth - 2) + '╯\x1b[0m');
979
1106
  return lines;
980
1107
  }
1108
+ /** Render a centered "About" popup with version and copyright info. */
1109
+ export function renderAboutPopupBox(version) {
1110
+ const boxWidth = 52;
1111
+ const innerW = boxWidth - 4;
1112
+ const titleStr = 'About Moderado';
1113
+ const remainingDashes = Math.max(0, boxWidth - titleStr.length - 5);
1114
+ // Strip the Alfazen build suffix (`+261024h`) so only `v<major.minor.patch>` shows.
1115
+ const semverOnly = version.split('+')[0];
1116
+ const content = [
1117
+ 'Moderado CLI',
1118
+ semverOnly,
1119
+ '',
1120
+ '(c) 2026 Moderado AI, an Alfazen Product',
1121
+ ];
1122
+ const lines = [];
1123
+ lines.push('\x1b[38;5;240m╭─ \x1b[1;38;5;75m' + titleStr + '\x1b[0;38;5;240m ' + '─'.repeat(remainingDashes) + '╮\x1b[0m');
1124
+ for (const item of content) {
1125
+ const plain = item.replace(/\x1b\\[[0-9;]*[a-zA-Z]/g, '');
1126
+ const leftPad = Math.max(0, Math.floor((innerW - plain.length) / 2));
1127
+ const rightPad = Math.max(0, innerW - plain.length - leftPad);
1128
+ lines.push('\x1b[38;5;240m│\x1b[0m' + ' '.repeat(leftPad) + item + ' '.repeat(rightPad) + '\x1b[38;5;240m│\x1b[0m');
1129
+ }
1130
+ lines.push('\x1b[38;5;240m╰' + '─'.repeat(boxWidth - 2) + '╯\x1b[0m');
1131
+ return lines;
1132
+ }
981
1133
  export let terminalCleanExitDone = false;
982
1134
  /** Mark that a clean exit already restored + cleared the terminal (guards the process 'exit' hook). */
983
1135
  export function notifyCleanExit() {
@@ -1005,7 +1157,7 @@ export async function exitCleanly(message) {
1005
1157
  notifyCleanExit();
1006
1158
  const stdout = process.stdout;
1007
1159
  const payload = stdout.isTTY
1008
- ? MOUSE_REPORT_OFF + '\x1b[?25h\x1b[?1049l\x1b[0 q\x1b[0m\x1b[2J\x1b[3J\x1b[H' + renderExitMessage(message)
1160
+ ? KEY_MODIFIER_REPORT_OFF + MOUSE_REPORT_OFF + '\x1b[?25h\x1b[?1049l\x1b[0 q\x1b[0m\x1b[2J\x1b[3J\x1b[H' + renderExitMessage(message)
1009
1161
  : renderExitMessage(message);
1010
1162
  try {
1011
1163
  fs.writeSync(1, payload);