-
Notifications
You must be signed in to change notification settings - Fork 139
/
io.ts
77 lines (71 loc) · 1.97 KB
/
io.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
import readline from "node:readline/promises";
import { stdin, stdout } from "node:process";
import picocolors from "picocolors";
import * as R from "remeda";
import stripAnsi from "strip-ansi";
interface ReadFromConsoleInput {
fallback?: string;
input?: string;
allowEmpty?: boolean;
}
export function createConsoleReader({
fallback,
input = "User 👤 : ",
allowEmpty = false,
}: ReadFromConsoleInput = {}) {
const rl = readline.createInterface({ input: stdin, output: stdout, terminal: true, prompt: "" });
let isActive = true;
return {
write(role: string, data: string) {
rl.write(
[role && R.piped(picocolors.red, picocolors.bold)(role), stripAnsi(data ?? "")]
.filter(Boolean)
.join(" ")
.concat("\n"),
);
},
async prompt(): Promise<string> {
for await (const { prompt } of this) {
return prompt;
}
process.exit(0);
},
close() {
stdin.pause();
rl.close();
},
async *[Symbol.asyncIterator]() {
if (!isActive) {
return;
}
try {
rl.write(
`${picocolors.dim(`Interactive session has started. To escape, input 'q' and submit.\n`)}`,
);
for (let iteration = 1, prompt = ""; isActive; iteration++) {
prompt = await rl.question(R.piped(picocolors.cyan, picocolors.bold)(input));
prompt = stripAnsi(prompt);
if (prompt === "q") {
break;
}
if (!prompt.trim() || prompt === "\n") {
prompt = fallback ?? "";
}
if (allowEmpty !== false && !prompt.trim()) {
rl.write("Error: Empty prompt is not allowed. Please try again.\n");
iteration -= 1;
continue;
}
yield { prompt, iteration };
}
} catch (e) {
if (e.code === "ERR_USE_AFTER_CLOSE") {
return;
}
} finally {
isActive = false;
rl.close();
}
},
};
}