adaptive-memory-multi-model-router 2.13.8 → 2.13.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/articles/POSTING_KIT_2026_05.md +39 -124
- package/articles/hn_show_2026_05.md +8 -18
- package/articles/twitter-thread-cost-savings.md +39 -59
- package/dist/tui/dashboard.d.ts +4 -5
- package/dist/tui/dashboard.js +229 -208
- package/dist/tui/dashboard.js.map +1 -1
- package/package.json +1 -1
- package/src/tui/dashboard.ts +250 -172
package/src/tui/dashboard.ts
CHANGED
|
@@ -1,209 +1,287 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* A3M Router
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* Then type queries or /slash commands.
|
|
3
|
+
* A3M Router — Terminal Overlay Box
|
|
4
|
+
* Draws a centered overlay ON TOP of existing terminal content.
|
|
5
|
+
* Does NOT clear the screen. Restores terminal when done.
|
|
6
|
+
* Pure ANSI — no fullscreen, no alt-buffer.
|
|
8
7
|
*/
|
|
9
8
|
|
|
10
9
|
import * as readline from 'readline';
|
|
11
|
-
// @ts-ignore
|
|
12
|
-
import chalk from 'chalk';
|
|
13
|
-
// @ts-ignore
|
|
14
|
-
import boxen from 'boxen';
|
|
15
10
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
11
|
+
|
|
12
|
+
const ansi = (n: number) => `\x1b[${n}m`;
|
|
13
|
+
const R = ansi(0);
|
|
14
|
+
const BOLD = ansi(1);
|
|
15
|
+
const DIM = ansi(2);
|
|
16
|
+
|
|
17
|
+
const C = {
|
|
18
|
+
bg: '\x1b[48;5;234m', // #1a1b26
|
|
19
|
+
surface: '\x1b[48;5;236m', // #24283b
|
|
20
|
+
border: '\x1b[38;5;60m', // #3b4261
|
|
21
|
+
dim: '\x1b[38;5;60m', // #565f89
|
|
22
|
+
text: '\x1b[38;5;189m', // #c0caf5
|
|
23
|
+
blue: '\x1b[38;5;111m', // #7aa2f7
|
|
24
|
+
purple: '\x1b[38;5;183m', // #bb9af7
|
|
25
|
+
green: '\x1b[38;5;114m', // #9ece6a
|
|
26
|
+
yellow: '\x1b[38;5;180m', // #e0af68
|
|
27
|
+
red: '\x1b[38;5;204m', // #f7768e
|
|
28
|
+
cyan: '\x1b[38;5;117m', // #7dcfff
|
|
29
|
+
orange: '\x1b[38;5;216m', // #ff9e64
|
|
30
|
+
};
|
|
31
|
+
|
|
19
32
|
|
|
20
33
|
let activeModel = 'nvidia/llama-3.1-8b';
|
|
21
34
|
let totalCost = 0.000087;
|
|
22
35
|
let reqCount = 4;
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
function dim(s: string) { return chalk.dim(s); }
|
|
30
|
-
function badge(t: string) { return chalk.dim(`[${t}]`); }
|
|
31
|
-
|
|
32
|
-
function headerLine(): string {
|
|
33
|
-
return [
|
|
34
|
-
chalk.bold.hex('#bb9af7')('⚡ A3M Router'),
|
|
35
|
-
dim('·'),
|
|
36
|
-
chalk.hex('#9ece6a')(activeModel),
|
|
37
|
-
dim('·'),
|
|
38
|
-
dim(`${reqCount} req`),
|
|
39
|
-
dim('·'),
|
|
40
|
-
dim(`$${totalCost.toFixed(6)}`),
|
|
41
|
-
].join(' ');
|
|
36
|
+
const log: string[] = [];
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
function getSize(): [number, number] {
|
|
40
|
+
return [process.stdout.columns || 80, process.stdout.rows || 24];
|
|
42
41
|
}
|
|
43
42
|
|
|
44
|
-
function
|
|
45
|
-
|
|
43
|
+
function saveCursor() { process.stdout.write('\x1b[s'); }
|
|
44
|
+
function restoreCursor() { process.stdout.write('\x1b[u'); }
|
|
45
|
+
function moveTo(row: number, col: number) { process.stdout.write(`\x1b[${row};${col}H`); }
|
|
46
|
+
function clearLine() { process.stdout.write('\x1b[2K'); }
|
|
47
|
+
function hideCursor() { process.stdout.write('\x1b[?25l'); }
|
|
48
|
+
function showCursor() { process.stdout.write('\x1b[?25h'); }
|
|
49
|
+
|
|
50
|
+
const B = { tl: '╭', tr: '╮', bl: '╰', br: '╯', h: '─', v: '│' };
|
|
51
|
+
|
|
52
|
+
function drawOverlay(rows: number, cols: number, top: number, left: number, lines: string[]) {
|
|
53
|
+
const w = cols - 2;
|
|
54
|
+
|
|
55
|
+
// Top border
|
|
56
|
+
moveTo(top, left);
|
|
57
|
+
process.stdout.write(C.bg + C.purple + B.tl + B.h.repeat(w) + B.tr + R);
|
|
58
|
+
|
|
59
|
+
// Content rows
|
|
60
|
+
for (let i = 0; i < rows - 2; i++) {
|
|
61
|
+
moveTo(top + 1 + i, left);
|
|
62
|
+
const content = (i < lines.length ? lines[i] : '').padEnd(w, ' ');
|
|
63
|
+
process.stdout.write(C.bg + C.purple + B.v + R + C.bg + content + C.purple + B.v + R);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Bottom border
|
|
67
|
+
moveTo(top + rows - 1, left);
|
|
68
|
+
process.stdout.write(C.bg + C.purple + B.bl + B.h.repeat(w) + B.br + R);
|
|
46
69
|
}
|
|
47
70
|
|
|
48
|
-
function
|
|
49
|
-
|
|
50
|
-
|
|
71
|
+
function drawPrompt(top: number, left: number, w: number, text: string) {
|
|
72
|
+
moveTo(top, left);
|
|
73
|
+
clearLine();
|
|
74
|
+
process.stdout.write(C.surface + C.text + BOLD + ' ▸ ' + R + C.surface + C.text + text + R);
|
|
75
|
+
moveTo(top, left + 3); // cursor after " ▸ "
|
|
51
76
|
}
|
|
52
77
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
78
|
+
|
|
79
|
+
let inputBuf = '';
|
|
80
|
+
let cursorPos = 3;
|
|
81
|
+
|
|
82
|
+
function handleInput(char: string) {
|
|
83
|
+
if (char === '\r' || char === '\n') {
|
|
84
|
+
// Submit
|
|
85
|
+
const cmd = inputBuf.trim();
|
|
86
|
+
processCommand(cmd);
|
|
87
|
+
inputBuf = '';
|
|
88
|
+
cursorPos = 3;
|
|
89
|
+
render();
|
|
90
|
+
} else if (char === '\x7f' || char === '\b') {
|
|
91
|
+
// Backspace
|
|
92
|
+
if (inputBuf.length > 0) {
|
|
93
|
+
inputBuf = inputBuf.slice(0, -1);
|
|
94
|
+
cursorPos = Math.max(3, cursorPos - 1);
|
|
95
|
+
renderPromptLine();
|
|
96
|
+
}
|
|
97
|
+
} else if (char === '\x1b') {
|
|
98
|
+
// Escape — handled separately
|
|
99
|
+
} else if (char >= ' ') {
|
|
100
|
+
inputBuf += char;
|
|
101
|
+
cursorPos++;
|
|
102
|
+
renderPromptLine();
|
|
103
|
+
}
|
|
61
104
|
}
|
|
62
105
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
if (
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
} else if (cmd === '/cost') {
|
|
99
|
-
printA3M('Cost breakdown:', '—', 0, 0);
|
|
100
|
-
printSystem(` nvidia $0.000000 (free)`);
|
|
101
|
-
printSystem(` deepseek $0.000009 ($9.46 remaining)`);
|
|
102
|
-
printSystem(` groq $0.000000 (free)`);
|
|
103
|
-
printSystem(` cerebras $0.000000 (free)`);
|
|
104
|
-
printSystem(` ───────────────────────`);
|
|
105
|
-
printSystem(` TOTAL $${totalCost.toFixed(6)} (${reqCount} requests)`);
|
|
106
|
-
printSystem(` Savings 99.97% vs all-premium`);
|
|
107
|
-
} else if (cmd === '/health') {
|
|
108
|
-
printA3M('Provider health:', '—', 0, 0);
|
|
109
|
-
printSystem(` ${chalk.hex('#9ece6a')('●')} nvidia llama-3.1-8b 85ms free`);
|
|
110
|
-
printSystem(` ${chalk.hex('#9ece6a')('●')} deepseek v4-flash 210ms mid`);
|
|
111
|
-
printSystem(` ${chalk.hex('#9ece6a')('●')} groq 8b-instant 150ms cheap`);
|
|
112
|
-
printSystem(` ${chalk.hex('#9ece6a')('●')} cerebras 3.3-70b 320ms cheap`);
|
|
113
|
-
printSystem(` ${chalk.hex('#f7768e')('✕')} mistral small OFFLINE`);
|
|
114
|
-
printSystem(` ${chalk.hex('#9ece6a')('●')} ollama llama3 50ms local`);
|
|
115
|
-
printSystem(` ${dim('4/6 healthy · 45ms avg')}`);
|
|
116
|
-
} else if (cmd === '/models') {
|
|
117
|
-
printA3M('Available providers (47+):', '—', 0, 0);
|
|
118
|
-
printSystem(` ${chalk.hex('#9ece6a')('● nvidia')} (free, default) ${chalk.hex('#7dcfff')('● groq')} (free) ${chalk.hex('#e0af68')('● deepseek')} (cheap)`);
|
|
119
|
-
printSystem(` ${chalk.hex('#bb9af7')('● cerebras')} (free) ${chalk.hex('#7aa2f7')('● mistral')} (mid) ${chalk.hex('#f7768e')('● openai')} (premium)`);
|
|
120
|
-
printSystem(` ${chalk.hex('#9ece6a')('● ollama')} (local) ${chalk.hex('#7dcfff')('● google')} (free)`);
|
|
121
|
-
printSystem(` ${dim('Use /model <name> to switch')}`);
|
|
122
|
-
} else if (cmd.startsWith('/model ')) {
|
|
123
|
-
const wanted = cmd.replace('/model ', '').trim();
|
|
106
|
+
|
|
107
|
+
function processCommand(cmd: string) {
|
|
108
|
+
const c = cmd.trim();
|
|
109
|
+
if (!c) return;
|
|
110
|
+
|
|
111
|
+
log.push(`${C.cyan + BOLD}▸${R} ${c}`);
|
|
112
|
+
|
|
113
|
+
if (c === '/exit' || c === '/q' || c === ':q') {
|
|
114
|
+
cleanup();
|
|
115
|
+
return;
|
|
116
|
+
} else if (c === '/help' || c === '/h') {
|
|
117
|
+
log.push(`${C.dim} /route /cost /health /models /model <p> /clear /exit${R}`);
|
|
118
|
+
} else if (c === '/clear') {
|
|
119
|
+
log.length = 0;
|
|
120
|
+
} else if (c === '/cost') {
|
|
121
|
+
log.push(`${C.purple + BOLD}A3M${R}${C.dim} Cost breakdown:${R}`);
|
|
122
|
+
log.push(`${C.dim} nvidia $0.000000 (free)${R}`);
|
|
123
|
+
log.push(`${C.dim} deepseek $0.000009 ($9.46 left)${R}`);
|
|
124
|
+
log.push(`${C.dim} groq $0.000000 (free)${R}`);
|
|
125
|
+
log.push(`${C.dim} ───────────────────────${R}`);
|
|
126
|
+
log.push(`${C.dim} TOTAL $${totalCost.toFixed(6)} (${reqCount} req)${R}`);
|
|
127
|
+
log.push(`${C.green} Savings: 99.97% vs all-premium${R}`);
|
|
128
|
+
} else if (c === '/health') {
|
|
129
|
+
log.push(`${C.purple + BOLD}A3M${R}${C.dim} Provider health:${R}`);
|
|
130
|
+
log.push(` ${C.green}●${R} nvidia llama-3.1-8b ${C.dim}85ms free${R}`);
|
|
131
|
+
log.push(` ${C.green}●${R} deepseek v4-flash ${C.dim}210ms mid${R}`);
|
|
132
|
+
log.push(` ${C.green}●${R} groq 8b-instant ${C.dim}150ms cheap${R}`);
|
|
133
|
+
log.push(` ${C.green}●${R} cerebras 3.3-70b ${C.dim}320ms cheap${R}`);
|
|
134
|
+
log.push(` ${C.red}✕${R} mistral small ${C.dim}OFFLINE${R}`);
|
|
135
|
+
} else if (c === '/models') {
|
|
136
|
+
log.push(`${C.purple + BOLD}A3M${R}${C.dim} Available (47+):${R}`);
|
|
137
|
+
log.push(` ${C.green}● nvidia${R}(free) ${C.cyan}● groq${R}(free) ${C.yellow}● deepseek${R}(cheap)`);
|
|
138
|
+
log.push(` ${C.purple}● cerebras${R}(free) ${C.blue}● mistral${R}(mid) ${C.red}● openai${R}(premium)`);
|
|
139
|
+
} else if (c.startsWith('/model ')) {
|
|
140
|
+
const w = c.replace('/model ', '').trim();
|
|
124
141
|
const valid = ['nvidia', 'deepseek', 'groq', 'cerebras', 'mistral', 'openai', 'ollama', 'google'];
|
|
125
|
-
if (valid.includes(
|
|
126
|
-
activeModel = `${
|
|
127
|
-
|
|
142
|
+
if (valid.includes(w)) {
|
|
143
|
+
activeModel = `${w}/auto`;
|
|
144
|
+
log.push(`${C.dim} Switched to ${C.green}${activeModel}${R}`);
|
|
128
145
|
} else {
|
|
129
|
-
|
|
146
|
+
log.push(`${C.dim} Unknown: ${w}${R}`);
|
|
130
147
|
}
|
|
131
|
-
} else if (cmd.startsWith('/route ') || cmd.startsWith('/r ')) {
|
|
132
|
-
const query = cmd.replace(/^\/r(oute)?\s*/, '');
|
|
133
|
-
const ms = Math.floor(Math.random() * 120) + 35;
|
|
134
|
-
const cost = Math.random() * 0.00008;
|
|
135
|
-
totalCost += cost;
|
|
136
|
-
reqCount++;
|
|
137
|
-
printA3M(query, activeModel, ms, cost);
|
|
138
148
|
} else {
|
|
139
|
-
// Plain text = auto-route
|
|
140
149
|
const ms = Math.floor(Math.random() * 100) + 30;
|
|
141
150
|
const cost = Math.random() * 0.00005;
|
|
142
151
|
totalCost += cost;
|
|
143
152
|
reqCount++;
|
|
144
|
-
|
|
153
|
+
log.push(`${C.purple + BOLD}A3M${R} ${C.green}${activeModel}${R} ${C.dim}·${R} ${C.yellow}${ms}ms${R} ${C.dim}·${R} ${C.orange}$${cost.toFixed(6)}${R}`);
|
|
154
|
+
log.push(` ${c}`);
|
|
145
155
|
}
|
|
146
156
|
|
|
147
|
-
//
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
}
|
|
157
|
+
// Trim log if too long
|
|
158
|
+
const maxLog = 14;
|
|
159
|
+
while (log.length > maxLog) log.shift();
|
|
151
160
|
}
|
|
152
161
|
|
|
153
|
-
// ═══════════════════════════════════════════════
|
|
154
|
-
// STARTUP
|
|
155
|
-
// ═══════════════════════════════════════════════
|
|
156
|
-
|
|
157
|
-
console.clear();
|
|
158
|
-
|
|
159
|
-
// Welcome banner
|
|
160
|
-
console.log(boxen(
|
|
161
|
-
[
|
|
162
|
-
chalk.bold.hex('#bb9af7')('⚡ A3M Router'),
|
|
163
|
-
'',
|
|
164
|
-
dim('One prompt in. The right model out.'),
|
|
165
|
-
'',
|
|
166
|
-
dim('Type anything — auto-routed to cheapest model.'),
|
|
167
|
-
dim('Commands: /route /cost /health /models /help /exit'),
|
|
168
|
-
'',
|
|
169
|
-
chalk.hex('#9ece6a')('nvidia (free)') + dim(' · ') +
|
|
170
|
-
chalk.hex('#7dcfff')('groq (free)') + dim(' · ') +
|
|
171
|
-
chalk.hex('#e0af68')('deepseek ($9.46)'),
|
|
172
|
-
].join('\n'),
|
|
173
|
-
{
|
|
174
|
-
padding: 1,
|
|
175
|
-
margin: { top: 1, bottom: 1 },
|
|
176
|
-
borderStyle: 'round',
|
|
177
|
-
borderColor: 'magenta',
|
|
178
|
-
dimBorder: true,
|
|
179
|
-
}
|
|
180
|
-
));
|
|
181
162
|
|
|
182
|
-
|
|
183
|
-
|
|
163
|
+
function buildOverlayLines(): string[] {
|
|
164
|
+
const lines: string[] = [];
|
|
165
|
+
|
|
166
|
+
// Header
|
|
167
|
+
lines.push(`${C.purple + BOLD}⚡ A3M Router${R} ${C.dim}·${R} ${C.green}${activeModel}${R} ${C.dim}·${R} ${C.dim}${reqCount} req${R} ${C.dim}·${R} ${C.dim}$${totalCost.toFixed(6)}${R}`);
|
|
168
|
+
lines.push(`${C.dim}${'─'.repeat(78)}${R}`);
|
|
169
|
+
lines.push('');
|
|
170
|
+
|
|
171
|
+
// Log lines
|
|
172
|
+
for (const l of log) {
|
|
173
|
+
lines.push(l);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// If no log, show welcome
|
|
177
|
+
if (log.length === 0) {
|
|
178
|
+
lines.push(` ${C.dim}Type a query — auto-routed to cheapest model.${R}`);
|
|
179
|
+
lines.push('');
|
|
180
|
+
lines.push(` ${C.dim}Commands:${R}`);
|
|
181
|
+
lines.push(` ${C.blue}/route${R} ${C.dim}<query>${R} ${C.blue}/cost${R} ${C.blue}/model nvidia${R}`);
|
|
182
|
+
lines.push(` ${C.blue}/health${R} ${C.blue}/models${R} ${C.blue}/clear${R}`);
|
|
183
|
+
lines.push(` ${C.blue}/exit${R} ${C.blue}/help${R}`);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Fill remaining with empty
|
|
187
|
+
while (lines.length < 16) lines.push('');
|
|
188
|
+
|
|
189
|
+
return lines;
|
|
190
|
+
}
|
|
184
191
|
|
|
185
|
-
|
|
186
|
-
const
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
+
function render() {
|
|
193
|
+
const [w, h] = getSize();
|
|
194
|
+
const BOX_W = 82;
|
|
195
|
+
const BOX_H = 18;
|
|
196
|
+
const left = Math.max(0, Math.floor((w - BOX_W) / 2));
|
|
197
|
+
const top = Math.max(0, Math.floor((h - BOX_H) / 2));
|
|
198
|
+
|
|
199
|
+
const overlayLines = buildOverlayLines();
|
|
200
|
+
|
|
201
|
+
hideCursor();
|
|
202
|
+
drawOverlay(BOX_H, BOX_W, top, left, overlayLines);
|
|
203
|
+
|
|
204
|
+
// Prompt line at bottom of box
|
|
205
|
+
drawPrompt(top + BOX_H - 1, left, BOX_W, inputBuf);
|
|
206
|
+
showCursor();
|
|
207
|
+
}
|
|
192
208
|
|
|
193
|
-
|
|
209
|
+
function renderPromptLine() {
|
|
210
|
+
const [w, h] = getSize();
|
|
211
|
+
const BOX_W = 82;
|
|
212
|
+
const BOX_H = 18;
|
|
213
|
+
const left = Math.max(0, Math.floor((w - BOX_W) / 2));
|
|
214
|
+
const top = Math.max(0, Math.floor((h - BOX_H) / 2));
|
|
215
|
+
drawPrompt(top + BOX_H - 1, left, BOX_W, inputBuf);
|
|
216
|
+
showCursor();
|
|
217
|
+
}
|
|
194
218
|
|
|
195
|
-
rl.on('line', (line: string) => {
|
|
196
|
-
handle(line);
|
|
197
|
-
rl.prompt();
|
|
198
|
-
});
|
|
199
219
|
|
|
200
|
-
|
|
201
|
-
|
|
220
|
+
let cleanedUp = false;
|
|
221
|
+
function cleanup() {
|
|
222
|
+
if (cleanedUp) return;
|
|
223
|
+
cleanedUp = true;
|
|
224
|
+
|
|
225
|
+
const [w, h] = getSize();
|
|
226
|
+
// Clear overlay area
|
|
227
|
+
const BOX_W = 82;
|
|
228
|
+
const BOX_H = 18;
|
|
229
|
+
const left = Math.max(0, Math.floor((w - BOX_W) / 2));
|
|
230
|
+
const top = Math.max(0, Math.floor((h - BOX_H) / 2));
|
|
231
|
+
|
|
232
|
+
for (let i = 0; i < BOX_H; i++) {
|
|
233
|
+
moveTo(top + i, left);
|
|
234
|
+
clearLine();
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
moveTo(h, 0);
|
|
238
|
+
showCursor();
|
|
239
|
+
process.stdout.write('\n');
|
|
240
|
+
|
|
241
|
+
// Restore stdin
|
|
242
|
+
if (process.stdin.isTTY) {
|
|
243
|
+
process.stdin.setRawMode(false);
|
|
244
|
+
}
|
|
245
|
+
process.stdin.pause();
|
|
202
246
|
process.exit(0);
|
|
203
|
-
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
function main() {
|
|
251
|
+
if (!process.stdin.isTTY) {
|
|
252
|
+
console.log('A3M Router requires a terminal.');
|
|
253
|
+
process.exit(1);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
process.stdin.setRawMode(true);
|
|
257
|
+
process.stdin.resume();
|
|
258
|
+
process.stdin.setEncoding('utf8');
|
|
259
|
+
|
|
260
|
+
process.stdin.on('data', (data: string) => {
|
|
261
|
+
if (data === '\x03') {
|
|
262
|
+
// Ctrl+C
|
|
263
|
+
cleanup();
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
if (data === '\x1b') {
|
|
267
|
+
// Escape
|
|
268
|
+
if (inputBuf) {
|
|
269
|
+
inputBuf = '';
|
|
270
|
+
cursorPos = 3;
|
|
271
|
+
render();
|
|
272
|
+
} else {
|
|
273
|
+
cleanup();
|
|
274
|
+
}
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
handleInput(data);
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
process.on('SIGINT', cleanup);
|
|
281
|
+
process.on('SIGTERM', cleanup);
|
|
282
|
+
|
|
283
|
+
hideCursor();
|
|
284
|
+
render();
|
|
285
|
+
}
|
|
204
286
|
|
|
205
|
-
|
|
206
|
-
process.on('SIGINT', () => {
|
|
207
|
-
console.log(dim('\n Use /exit to quit.\n'));
|
|
208
|
-
rl.prompt();
|
|
209
|
-
});
|
|
287
|
+
main();
|