toolcraft 0.0.39 → 0.0.41

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 (70) hide show
  1. package/README.md +41 -4
  2. package/dist/cli.js +40 -9
  3. package/dist/http-errors.d.ts +57 -0
  4. package/dist/http-errors.js +72 -0
  5. package/dist/index.d.ts +10 -0
  6. package/dist/index.js +15 -0
  7. package/dist/mcp-proxy.js +16 -1
  8. package/dist/mcp.js +224 -12
  9. package/dist/number-schema.js +9 -2
  10. package/node_modules/@poe-code/agent-defs/dist/agents/claude-code.js +1 -1
  11. package/node_modules/@poe-code/agent-defs/dist/agents/claude-desktop.js +6 -1
  12. package/node_modules/@poe-code/agent-defs/dist/agents/cursor.js +1 -1
  13. package/node_modules/@poe-code/agent-defs/dist/agents/kimi.js +1 -1
  14. package/node_modules/@poe-code/agent-defs/dist/agents/opencode.js +1 -1
  15. package/node_modules/@poe-code/agent-defs/dist/specifier.js +15 -6
  16. package/node_modules/@poe-code/agent-defs/dist/types.d.ts +5 -0
  17. package/node_modules/@poe-code/agent-mcp-config/dist/apply.js +36 -3
  18. package/node_modules/@poe-code/frontmatter/README.md +35 -0
  19. package/node_modules/@poe-code/frontmatter/dist/fences.d.ts +8 -0
  20. package/node_modules/@poe-code/frontmatter/dist/fences.js +72 -0
  21. package/node_modules/@poe-code/frontmatter/dist/index.d.ts +3 -0
  22. package/node_modules/@poe-code/frontmatter/dist/index.js +3 -0
  23. package/node_modules/@poe-code/frontmatter/dist/parse.d.ts +20 -0
  24. package/node_modules/@poe-code/frontmatter/dist/parse.js +137 -0
  25. package/node_modules/@poe-code/frontmatter/dist/stringify.d.ts +1 -0
  26. package/node_modules/@poe-code/frontmatter/dist/stringify.js +35 -0
  27. package/node_modules/@poe-code/frontmatter/package.json +25 -0
  28. package/node_modules/mcp-oauth/dist/server/jwks-token-verifier.d.ts +1 -0
  29. package/node_modules/mcp-oauth/dist/server/jwks-token-verifier.js +25 -1
  30. package/node_modules/tiny-mcp-client/README.md +7 -0
  31. package/node_modules/tiny-mcp-client/dist/internal.d.ts +2 -0
  32. package/node_modules/tiny-mcp-client/dist/internal.js +3 -0
  33. package/node_modules/tiny-mcp-client/dist/mcp-tool-types.compile-check.js +10 -0
  34. package/node_modules/tiny-mcp-client/src/http-oauth.test.ts +2 -8
  35. package/node_modules/tiny-mcp-client/src/internal.ts +6 -0
  36. package/node_modules/tiny-mcp-client/src/mcp-tool-types.compile-check.ts +10 -0
  37. package/node_modules/toolcraft-design/README.md +12 -0
  38. package/node_modules/toolcraft-design/dist/prompts/index.d.ts +39 -14
  39. package/node_modules/toolcraft-design/dist/prompts/index.js +10 -6
  40. package/node_modules/toolcraft-design/dist/prompts/interactive/cancel-symbol.d.ts +2 -0
  41. package/node_modules/toolcraft-design/dist/prompts/interactive/cancel-symbol.js +4 -0
  42. package/node_modules/toolcraft-design/dist/prompts/interactive/confirm.d.ts +9 -0
  43. package/node_modules/toolcraft-design/dist/prompts/interactive/confirm.js +47 -0
  44. package/node_modules/toolcraft-design/dist/prompts/interactive/core.d.ts +55 -0
  45. package/node_modules/toolcraft-design/dist/prompts/interactive/core.js +274 -0
  46. package/node_modules/toolcraft-design/dist/prompts/interactive/glyphs.d.ts +20 -0
  47. package/node_modules/toolcraft-design/dist/prompts/interactive/glyphs.js +53 -0
  48. package/node_modules/toolcraft-design/dist/prompts/interactive/index.d.ts +6 -0
  49. package/node_modules/toolcraft-design/dist/prompts/interactive/index.js +6 -0
  50. package/node_modules/toolcraft-design/dist/prompts/interactive/keys.d.ts +2 -0
  51. package/node_modules/toolcraft-design/dist/prompts/interactive/keys.js +28 -0
  52. package/node_modules/toolcraft-design/dist/prompts/interactive/multiselect.d.ts +13 -0
  53. package/node_modules/toolcraft-design/dist/prompts/interactive/multiselect.js +131 -0
  54. package/node_modules/toolcraft-design/dist/prompts/interactive/pagination.d.ts +10 -0
  55. package/node_modules/toolcraft-design/dist/prompts/interactive/pagination.js +52 -0
  56. package/node_modules/toolcraft-design/dist/prompts/interactive/password.d.ts +10 -0
  57. package/node_modules/toolcraft-design/dist/prompts/interactive/password.js +55 -0
  58. package/node_modules/toolcraft-design/dist/prompts/interactive/select.d.ts +18 -0
  59. package/node_modules/toolcraft-design/dist/prompts/interactive/select.js +89 -0
  60. package/node_modules/toolcraft-design/dist/prompts/interactive/test-helpers.d.ts +21 -0
  61. package/node_modules/toolcraft-design/dist/prompts/interactive/test-helpers.js +32 -0
  62. package/node_modules/toolcraft-design/dist/prompts/interactive/text.d.ts +12 -0
  63. package/node_modules/toolcraft-design/dist/prompts/interactive/text.js +60 -0
  64. package/node_modules/toolcraft-design/dist/prompts/interactive/wrap.d.ts +4 -0
  65. package/node_modules/toolcraft-design/dist/prompts/interactive/wrap.js +18 -0
  66. package/node_modules/toolcraft-design/dist/prompts/primitives/cancel.d.ts +1 -1
  67. package/node_modules/toolcraft-design/dist/prompts/primitives/cancel.js +1 -1
  68. package/node_modules/toolcraft-design/dist/terminal-markdown/parser/frontmatter.js +20 -453
  69. package/node_modules/toolcraft-design/package.json +6 -3
  70. package/package.json +9 -6
@@ -0,0 +1,274 @@
1
+ import { EventEmitter } from "node:events";
2
+ import * as readline from "node:readline";
3
+ import { CANCEL } from "./cancel-symbol.js";
4
+ import { mapKey } from "./keys.js";
5
+ import { wrapFrame } from "./wrap.js";
6
+ const cursor = {
7
+ hide: "\x1b[?25l",
8
+ show: "\x1b[?25h",
9
+ move: (x, y) => {
10
+ let output = "";
11
+ if (x < 0)
12
+ output += `\x1b[${-x}D`;
13
+ if (x > 0)
14
+ output += `\x1b[${x}C`;
15
+ if (y < 0)
16
+ output += `\x1b[${-y}A`;
17
+ if (y > 0)
18
+ output += `\x1b[${y}B`;
19
+ return output;
20
+ }
21
+ };
22
+ const erase = {
23
+ down: "\x1b[J"
24
+ };
25
+ export class Prompt extends EventEmitter {
26
+ state = "initial";
27
+ value;
28
+ error = "";
29
+ userInput = "";
30
+ _cursor = 0;
31
+ input;
32
+ output;
33
+ renderFrame;
34
+ validate;
35
+ signal;
36
+ trackValue;
37
+ previousFrame = "";
38
+ readlineInterface;
39
+ abortListener;
40
+ closed = false;
41
+ constructor(opts, trackValue = true) {
42
+ super();
43
+ this.input = (opts.input ?? process.stdin);
44
+ this.output = opts.output ?? process.stdout;
45
+ this.renderFrame = opts.render;
46
+ this.validate = opts.validate;
47
+ this.signal = opts.signal;
48
+ this.value = opts.initialValue;
49
+ this.trackValue = trackValue;
50
+ this.userInput = opts.initialUserInput ?? "";
51
+ this._cursor = this.userInput.length;
52
+ }
53
+ get cursor() {
54
+ return this._cursor;
55
+ }
56
+ prompt() {
57
+ if (this.signal?.aborted) {
58
+ this.state = "cancel";
59
+ return Promise.resolve(CANCEL);
60
+ }
61
+ if (this.input.isTTY !== true) {
62
+ return this.promptNonTty();
63
+ }
64
+ return new Promise((resolve) => {
65
+ const onSubmit = (value) => resolve(value);
66
+ const onCancel = () => resolve(CANCEL);
67
+ this.once("submit", onSubmit);
68
+ this.once("cancel", onCancel);
69
+ this.abortListener = () => {
70
+ this.state = "cancel";
71
+ this.emit("finalize");
72
+ this.render();
73
+ this.close();
74
+ };
75
+ this.signal?.addEventListener("abort", this.abortListener, { once: true });
76
+ this.readlineInterface = readline.createInterface({
77
+ input: this.input,
78
+ output: undefined,
79
+ tabSize: 2,
80
+ prompt: "",
81
+ escapeCodeTimeout: 50,
82
+ terminal: true
83
+ });
84
+ readline.emitKeypressEvents(this.input, this.readlineInterface);
85
+ this.readlineInterface.prompt();
86
+ this.input.on("keypress", this.onKeypress);
87
+ if (this.input.setRawMode) {
88
+ this.input.setRawMode(true);
89
+ }
90
+ this.output.on("resize", this.render);
91
+ this.render();
92
+ });
93
+ }
94
+ promptNonTty() {
95
+ return Promise.reject(new Error("Interactive prompt requires a TTY. Set POE_NO_PROMPT=1 to accept defaults non-interactively."));
96
+ }
97
+ readNonTtyLine() {
98
+ return new Promise((resolve) => {
99
+ const rl = readline.createInterface({ input: this.input, terminal: false });
100
+ let settled = false;
101
+ const settle = (value) => {
102
+ if (settled) {
103
+ return;
104
+ }
105
+ settled = true;
106
+ rl.close();
107
+ resolve(value);
108
+ };
109
+ rl.once("line", settle);
110
+ rl.once("close", () => settle(rl.line));
111
+ });
112
+ }
113
+ setValue(value) {
114
+ this.value = value;
115
+ this.emit("value", value);
116
+ }
117
+ setError(message) {
118
+ this.error = message;
119
+ }
120
+ setUserInput(value) {
121
+ this.userInput = value;
122
+ this._cursor = Math.min(this._cursor, this.userInput.length);
123
+ this.emit("userInput", this.userInput);
124
+ }
125
+ clearUserInput() {
126
+ this.userInput = "";
127
+ this._cursor = 0;
128
+ this.emit("userInput", this.userInput);
129
+ }
130
+ onKeypress = (char, key = {}) => {
131
+ let action = mapKey(key.name, char);
132
+ if (this.trackValue && char && char >= " " && key.name !== "return" && key.name !== "enter" && key.name !== "escape") {
133
+ action = undefined;
134
+ }
135
+ if (this.trackValue && action !== "enter") {
136
+ this.updateTrackedInput(char, key, action);
137
+ }
138
+ if (this.state === "error") {
139
+ this.state = "active";
140
+ this.error = "";
141
+ }
142
+ if (!this.trackValue && action) {
143
+ this.emit("cursor", action);
144
+ }
145
+ else if (this.trackValue && action && action !== "enter") {
146
+ this.emit("cursor", action);
147
+ }
148
+ if (char && /^[yn]$/i.test(char)) {
149
+ this.emit("confirm", char.toLowerCase() === "y");
150
+ }
151
+ if (char) {
152
+ this.emit("key", char.toLowerCase(), key);
153
+ }
154
+ if (action === "enter") {
155
+ const error = this.validate?.(this.value);
156
+ if (error) {
157
+ this.error = error instanceof Error ? error.message : error;
158
+ this.state = "error";
159
+ }
160
+ else {
161
+ this.state = "submit";
162
+ }
163
+ }
164
+ if (action === "cancel") {
165
+ this.state = "cancel";
166
+ }
167
+ if (this.state === "submit" || this.state === "cancel") {
168
+ this.emit("finalize");
169
+ }
170
+ this.render();
171
+ if (this.state === "submit" || this.state === "cancel") {
172
+ this.close();
173
+ }
174
+ };
175
+ updateTrackedInput(char, key, action) {
176
+ if (key.ctrl) {
177
+ if (key.name === "a") {
178
+ this._cursor = 0;
179
+ return;
180
+ }
181
+ if (key.name === "e") {
182
+ this._cursor = this.userInput.length;
183
+ return;
184
+ }
185
+ if (key.name === "u") {
186
+ this.userInput = this.userInput.slice(this._cursor);
187
+ this._cursor = 0;
188
+ this.emit("userInput", this.userInput);
189
+ return;
190
+ }
191
+ if (key.name === "k") {
192
+ this.userInput = this.userInput.slice(0, this._cursor);
193
+ this.emit("userInput", this.userInput);
194
+ return;
195
+ }
196
+ }
197
+ if (action === "left") {
198
+ this._cursor = Math.max(0, this._cursor - 1);
199
+ return;
200
+ }
201
+ if (action === "right") {
202
+ this._cursor = Math.min(this.userInput.length, this._cursor + 1);
203
+ return;
204
+ }
205
+ if (action === "cancel" || action === "up" || action === "down" || action === "space") {
206
+ return;
207
+ }
208
+ if (key.name === "backspace" || char === "\b" || char === "\x7f") {
209
+ if (this._cursor > 0) {
210
+ this.userInput = `${this.userInput.slice(0, this._cursor - 1)}${this.userInput.slice(this._cursor)}`;
211
+ this._cursor -= 1;
212
+ this.emit("userInput", this.userInput);
213
+ }
214
+ return;
215
+ }
216
+ if (key.name === "delete") {
217
+ if (this._cursor < this.userInput.length) {
218
+ this.userInput = `${this.userInput.slice(0, this._cursor)}${this.userInput.slice(this._cursor + 1)}`;
219
+ this.emit("userInput", this.userInput);
220
+ }
221
+ return;
222
+ }
223
+ if (!char || char < " " || key.ctrl) {
224
+ return;
225
+ }
226
+ this.userInput = `${this.userInput.slice(0, this._cursor)}${char}${this.userInput.slice(this._cursor)}`;
227
+ this._cursor += char.length;
228
+ this.emit("userInput", this.userInput);
229
+ }
230
+ render = () => {
231
+ if (this.closed) {
232
+ return;
233
+ }
234
+ const frame = wrapFrame(this.output, this.renderFrame(this) ?? "");
235
+ if (frame === this.previousFrame) {
236
+ return;
237
+ }
238
+ if (!this.previousFrame) {
239
+ this.output.write(`${cursor.hide}${frame}`);
240
+ this.previousFrame = frame;
241
+ if (this.state === "initial") {
242
+ this.state = "active";
243
+ }
244
+ return;
245
+ }
246
+ const previousLineCount = this.previousFrame.split("\n").length - 1;
247
+ this.output.write(`${cursor.move(-999, -previousLineCount)}${erase.down}${frame}`);
248
+ this.previousFrame = frame;
249
+ };
250
+ close() {
251
+ if (this.closed) {
252
+ return;
253
+ }
254
+ this.closed = true;
255
+ this.input.removeListener("keypress", this.onKeypress);
256
+ this.output.removeListener("resize", this.render);
257
+ if (this.abortListener) {
258
+ this.signal?.removeEventListener("abort", this.abortListener);
259
+ }
260
+ this.output.write(`${cursor.show}\n`);
261
+ if (!process.platform.startsWith("win") && this.input.setRawMode) {
262
+ this.input.setRawMode(false);
263
+ }
264
+ this.readlineInterface?.close();
265
+ this.input.unpipe?.();
266
+ if (this.state === "cancel") {
267
+ this.emit("cancel");
268
+ }
269
+ else {
270
+ this.emit("submit", this.value);
271
+ }
272
+ this.removeAllListeners();
273
+ }
274
+ }
@@ -0,0 +1,20 @@
1
+ import type { PromptStateName } from "./core.js";
2
+ export declare const UNICODE: boolean;
3
+ export declare const GLYPHS: {
4
+ readonly stepActive: string;
5
+ readonly stepCancel: string;
6
+ readonly stepError: string;
7
+ readonly stepSubmit: string;
8
+ readonly barStart: string;
9
+ readonly bar: string;
10
+ readonly barEnd: string;
11
+ readonly radioActive: string;
12
+ readonly radioInactive: string;
13
+ readonly checkboxActive: string;
14
+ readonly checkboxSelected: string;
15
+ readonly checkboxInactive: string;
16
+ readonly passwordMask: string;
17
+ readonly ellipsis: "...";
18
+ };
19
+ export declare function symbol(state: PromptStateName): string;
20
+ export declare function symbolBar(state: PromptStateName): string;
@@ -0,0 +1,53 @@
1
+ import { color } from "../../components/color.js";
2
+ function supportsUnicode() {
3
+ if (!process.platform.startsWith("win")) {
4
+ return process.env.TERM !== "linux";
5
+ }
6
+ return Boolean(process.env.CI ||
7
+ process.env.WT_SESSION ||
8
+ process.env.TERMINUS_SUBLIME ||
9
+ process.env.ConEmuTask === "{cmd::Cmder}" ||
10
+ process.env.TERM_PROGRAM === "Terminus-Sublime" ||
11
+ process.env.TERM_PROGRAM === "vscode" ||
12
+ process.env.TERM === "xterm-256color" ||
13
+ process.env.TERM === "alacritty" ||
14
+ process.env.TERMINAL_EMULATOR === "JetBrains-JediTerm");
15
+ }
16
+ export const UNICODE = supportsUnicode();
17
+ function glyph(unicode, ascii) {
18
+ return UNICODE ? unicode : ascii;
19
+ }
20
+ export const GLYPHS = {
21
+ stepActive: glyph("◆", "*"),
22
+ stepCancel: glyph("■", "x"),
23
+ stepError: glyph("▲", "x"),
24
+ stepSubmit: glyph("◇", "o"),
25
+ barStart: glyph("┌", "T"),
26
+ bar: glyph("│", "|"),
27
+ barEnd: glyph("└", "-"),
28
+ radioActive: glyph("●", ">"),
29
+ radioInactive: glyph("○", " "),
30
+ checkboxActive: glyph("◻", "[ ]"),
31
+ checkboxSelected: glyph("◼", "[+]"),
32
+ checkboxInactive: glyph("◻", "[ ]"),
33
+ passwordMask: glyph("•", "*"),
34
+ ellipsis: "..."
35
+ };
36
+ export function symbol(state) {
37
+ if (state === "cancel")
38
+ return color.red(GLYPHS.stepCancel);
39
+ if (state === "error")
40
+ return color.yellow(GLYPHS.stepError);
41
+ if (state === "submit")
42
+ return color.green(GLYPHS.stepSubmit);
43
+ return color.cyan(GLYPHS.stepActive);
44
+ }
45
+ export function symbolBar(state) {
46
+ if (state === "cancel")
47
+ return color.red(GLYPHS.bar);
48
+ if (state === "error")
49
+ return color.yellow(GLYPHS.bar);
50
+ if (state === "submit")
51
+ return color.green(GLYPHS.bar);
52
+ return color.cyan(GLYPHS.bar);
53
+ }
@@ -0,0 +1,6 @@
1
+ export { CANCEL, isCancel } from "./cancel-symbol.js";
2
+ export { textPrompt, type TextOptions } from "./text.js";
3
+ export { passwordPrompt, type PasswordOptions } from "./password.js";
4
+ export { confirmPrompt, type ConfirmOptions } from "./confirm.js";
5
+ export { selectPrompt, type SelectOption, type SelectOptions } from "./select.js";
6
+ export { multiselectPrompt, type MultiselectOptions } from "./multiselect.js";
@@ -0,0 +1,6 @@
1
+ export { CANCEL, isCancel } from "./cancel-symbol.js";
2
+ export { textPrompt } from "./text.js";
3
+ export { passwordPrompt } from "./password.js";
4
+ export { confirmPrompt } from "./confirm.js";
5
+ export { selectPrompt } from "./select.js";
6
+ export { multiselectPrompt } from "./multiselect.js";
@@ -0,0 +1,2 @@
1
+ export type Action = "up" | "down" | "left" | "right" | "space" | "enter" | "cancel";
2
+ export declare function mapKey(name: string | undefined, char: string | undefined): Action | undefined;
@@ -0,0 +1,28 @@
1
+ const aliases = {
2
+ k: "up",
3
+ j: "down",
4
+ h: "left",
5
+ l: "right"
6
+ };
7
+ const keyActions = {
8
+ up: "up",
9
+ down: "down",
10
+ left: "left",
11
+ right: "right",
12
+ space: "space",
13
+ return: "enter",
14
+ enter: "enter",
15
+ escape: "cancel"
16
+ };
17
+ export function mapKey(name, char) {
18
+ if (char === "\x03") {
19
+ return "cancel";
20
+ }
21
+ if (char === " ") {
22
+ return "space";
23
+ }
24
+ if (!name) {
25
+ return undefined;
26
+ }
27
+ return keyActions[name] ?? aliases[name];
28
+ }
@@ -0,0 +1,13 @@
1
+ import { CANCEL } from "./cancel-symbol.js";
2
+ import { type SelectOption } from "./select.js";
3
+ export interface MultiselectOptions<Value> {
4
+ message: string;
5
+ options: Array<SelectOption<Value>>;
6
+ initialValues?: Value[];
7
+ required?: boolean;
8
+ maxItems?: number;
9
+ signal?: AbortSignal;
10
+ input?: NodeJS.ReadableStream;
11
+ output?: NodeJS.WritableStream;
12
+ }
13
+ export declare function multiselectPrompt<Value>(opts: MultiselectOptions<Value>): Promise<Value[] | typeof CANCEL>;
@@ -0,0 +1,131 @@
1
+ import { color } from "../../components/color.js";
2
+ import { GLYPHS, symbol } from "./glyphs.js";
3
+ import { Prompt } from "./core.js";
4
+ import { limitOptions } from "./pagination.js";
5
+ import { findNonDisabled } from "./select.js";
6
+ class MultiselectPrompt extends Prompt {
7
+ options;
8
+ constructor(opts) {
9
+ if (opts.options.length === 0) {
10
+ throw new Error("Multiselect prompt requires at least one option.");
11
+ }
12
+ if (opts.options.every((option) => option.disabled)) {
13
+ throw new Error("Multiselect prompt requires at least one enabled option.");
14
+ }
15
+ const cursor = findNonDisabled(0, 1, opts.options);
16
+ super({
17
+ ...opts,
18
+ initialValue: [...(opts.initialValues ?? [])],
19
+ validate: (value) => {
20
+ if (opts.required && (!value || value.length === 0)) {
21
+ return "Please select at least one option.\nPress SPACE to select, ENTER to submit";
22
+ }
23
+ return undefined;
24
+ },
25
+ render: (prompt) => renderMultiselectPrompt(prompt, opts)
26
+ }, false);
27
+ this.options = opts.options;
28
+ this._cursor = cursor;
29
+ this.on("cursor", (action) => {
30
+ if (action === "up" || action === "left") {
31
+ this._cursor = findNonDisabled(this._cursor - 1, -1, this.options);
32
+ }
33
+ else if (action === "down" || action === "right") {
34
+ this._cursor = findNonDisabled(this._cursor + 1, 1, this.options);
35
+ }
36
+ else if (action === "space") {
37
+ this.toggleFocused();
38
+ }
39
+ });
40
+ this.on("key", (key) => {
41
+ if (key === "a") {
42
+ this.toggleAll();
43
+ }
44
+ else if (key === "i") {
45
+ this.invert();
46
+ }
47
+ });
48
+ }
49
+ get visibleOptions() {
50
+ return this.options;
51
+ }
52
+ enabledOptions() {
53
+ return this.options.filter((option) => !option.disabled);
54
+ }
55
+ toggleFocused() {
56
+ const option = this.options[this.cursor];
57
+ if (!option || option.disabled) {
58
+ return;
59
+ }
60
+ this.toggleValue(option.value);
61
+ }
62
+ toggleValue(value) {
63
+ const current = this.value ?? [];
64
+ this.setValue(current.includes(value)
65
+ ? current.filter((item) => item !== value)
66
+ : [...current, value]);
67
+ }
68
+ toggleAll() {
69
+ const enabledValues = this.enabledOptions().map((option) => option.value);
70
+ const current = this.value ?? [];
71
+ const allSelected = enabledValues.every((value) => current.includes(value));
72
+ this.setValue(allSelected ? [] : enabledValues);
73
+ }
74
+ invert() {
75
+ const current = this.value ?? [];
76
+ this.setValue(this.enabledOptions().map((option) => option.value).filter((value) => !current.includes(value)));
77
+ }
78
+ promptNonTty() {
79
+ if (process.env.POE_NO_PROMPT === "1") {
80
+ return Promise.resolve(this.value ?? []);
81
+ }
82
+ return super.promptNonTty();
83
+ }
84
+ }
85
+ function hasValue(values, value) {
86
+ return (values ?? []).includes(value);
87
+ }
88
+ function renderOption(option, values, active, submitted, cancelled) {
89
+ const selected = hasValue(values, option.value);
90
+ const hint = option.hint ? color.dim(` (${option.hint})`) : "";
91
+ if (submitted)
92
+ return color.dim(option.label);
93
+ if (cancelled)
94
+ return color.dim.strikethrough(option.label);
95
+ if (option.disabled)
96
+ return `${color.gray(GLYPHS.checkboxInactive)} ${color.gray.strikethrough(option.label)}${hint}`;
97
+ if (selected)
98
+ return `${color.green(GLYPHS.checkboxSelected)} ${active ? option.label : color.dim(option.label)}${hint}`;
99
+ if (active)
100
+ return `${color.cyan(GLYPHS.checkboxActive)} ${option.label}${hint}`;
101
+ return `${color.dim(GLYPHS.checkboxInactive)} ${color.dim(option.label)}${hint}`;
102
+ }
103
+ function renderMultiselectPrompt(prompt, opts) {
104
+ if (prompt.state === "submit" || prompt.state === "cancel") {
105
+ const labels = prompt.visibleOptions
106
+ .filter((option) => hasValue(prompt.value, option.value))
107
+ .map((option) => prompt.state === "submit" ? color.dim(option.label) : color.dim.strikethrough(option.label))
108
+ .join(", ");
109
+ const end = prompt.state === "submit" ? color.green(GLYPHS.barEnd) : color.red(GLYPHS.barEnd);
110
+ return `${color.gray(GLYPHS.barStart)} ${symbol(prompt.state)} ${opts.message}\n${color.gray(GLYPHS.bar)} ${labels}\n${end}`;
111
+ }
112
+ const lines = limitOptions({
113
+ cursor: prompt.cursor,
114
+ options: prompt.visibleOptions,
115
+ output: opts.output ?? process.stdout,
116
+ maxItems: opts.maxItems,
117
+ columnPadding: 3,
118
+ style: (option, active) => renderOption(option, prompt.value, active, false, false)
119
+ }).map((line) => `${prompt.state === "error" ? color.yellow(GLYPHS.bar) : color.cyan(GLYPHS.bar)} ${line}`);
120
+ const body = [`${color.gray(GLYPHS.barStart)} ${symbol(prompt.state)} ${opts.message}`, ...lines];
121
+ if (prompt.state === "error") {
122
+ body.push(`${color.yellow(GLYPHS.barEnd)} ${color.yellow(prompt.error)}`);
123
+ }
124
+ else {
125
+ body.push(color.cyan(GLYPHS.barEnd));
126
+ }
127
+ return body.join("\n");
128
+ }
129
+ export function multiselectPrompt(opts) {
130
+ return new MultiselectPrompt(opts).prompt();
131
+ }
@@ -0,0 +1,10 @@
1
+ export interface PaginationOptions<Option> {
2
+ cursor: number;
3
+ options: Option[];
4
+ style: (option: Option, active: boolean) => string;
5
+ output: NodeJS.WritableStream;
6
+ maxItems?: number;
7
+ columnPadding?: number;
8
+ rowPadding?: number;
9
+ }
10
+ export declare function limitOptions<Option>(opts: PaginationOptions<Option>): string[];
@@ -0,0 +1,52 @@
1
+ import { color } from "../../components/color.js";
2
+ import { GLYPHS } from "./glyphs.js";
3
+ import { getColumns, getRows } from "./wrap.js";
4
+ import { wrapAnsi } from "fast-wrap-ansi";
5
+ function countLines(values) {
6
+ return values.reduce((sum, value) => sum + value.split("\n").length, 0);
7
+ }
8
+ function trimToRows(values, cursorOffset, rows, hasTop, hasBottom) {
9
+ const output = [...values];
10
+ while (countLines(output) > rows && output.length > 1) {
11
+ const removeFromTop = hasTop && cursorOffset > 0;
12
+ const removeFromBottom = hasBottom && cursorOffset < output.length - 1;
13
+ if (removeFromTop) {
14
+ output.shift();
15
+ cursorOffset -= 1;
16
+ }
17
+ else if (removeFromBottom) {
18
+ output.pop();
19
+ }
20
+ else {
21
+ output.pop();
22
+ }
23
+ }
24
+ return output;
25
+ }
26
+ export function limitOptions(opts) {
27
+ const { cursor, options, style, output, maxItems = Number.POSITIVE_INFINITY, columnPadding = 0, rowPadding = 4 } = opts;
28
+ if (options.length === 0) {
29
+ return [];
30
+ }
31
+ const columns = Math.max(1, getColumns(output) - columnPadding);
32
+ const rowBudget = Math.max(getRows(output) - rowPadding, 0);
33
+ const visibleCount = Math.max(Math.min(maxItems, rowBudget), 5);
34
+ const cappedVisibleCount = Math.min(visibleCount, options.length);
35
+ let start = 0;
36
+ if (cursor >= cappedVisibleCount - 3) {
37
+ start = Math.max(Math.min(cursor - cappedVisibleCount + 3, options.length - cappedVisibleCount), 0);
38
+ }
39
+ const hasTopMarker = cappedVisibleCount < options.length && start > 0;
40
+ const hasBottomMarker = cappedVisibleCount < options.length && start + cappedVisibleCount < options.length;
41
+ const visible = options
42
+ .slice(start, start + cappedVisibleCount)
43
+ .map((option, index) => wrapAnsi(style(option, start + index === cursor), columns, { hard: true, trim: false }));
44
+ const trimmed = trimToRows(visible, Math.max(cursor - start, 0), Math.max(rowBudget - Number(hasTopMarker) - Number(hasBottomMarker), 1), hasTopMarker, hasBottomMarker);
45
+ if (hasTopMarker) {
46
+ trimmed.unshift(color.dim(GLYPHS.ellipsis));
47
+ }
48
+ if (hasBottomMarker) {
49
+ trimmed.push(color.dim(GLYPHS.ellipsis));
50
+ }
51
+ return trimmed;
52
+ }
@@ -0,0 +1,10 @@
1
+ import { CANCEL } from "./cancel-symbol.js";
2
+ export interface PasswordOptions {
3
+ message: string;
4
+ mask?: string;
5
+ validate?: (value: string) => string | Error | undefined;
6
+ signal?: AbortSignal;
7
+ input?: NodeJS.ReadableStream;
8
+ output?: NodeJS.WritableStream;
9
+ }
10
+ export declare function passwordPrompt(opts: PasswordOptions): Promise<string | typeof CANCEL>;
@@ -0,0 +1,55 @@
1
+ import { color } from "../../components/color.js";
2
+ import { GLYPHS, symbol, symbolBar } from "./glyphs.js";
3
+ import { Prompt } from "./core.js";
4
+ class PasswordPrompt extends Prompt {
5
+ mask;
6
+ constructor(opts) {
7
+ super({
8
+ ...opts,
9
+ initialValue: "",
10
+ initialUserInput: "",
11
+ render: (prompt) => renderPasswordPrompt(prompt, opts),
12
+ validate: opts.validate
13
+ });
14
+ this.mask = opts.mask ?? GLYPHS.passwordMask;
15
+ this.on("userInput", (value) => this.setValue(value));
16
+ }
17
+ get masked() {
18
+ return this.mask.repeat(this.userInput.length);
19
+ }
20
+ get userInputWithCursor() {
21
+ if (this.state === "submit") {
22
+ return this.masked;
23
+ }
24
+ const masked = this.masked;
25
+ const before = masked.slice(0, this.cursor);
26
+ const current = masked[this.cursor];
27
+ const after = masked.slice(this.cursor + 1);
28
+ if (current) {
29
+ return `${before}${color.inverse(current)}${after}`;
30
+ }
31
+ return `${before}${color.inverse("█")}`;
32
+ }
33
+ promptNonTty() {
34
+ return this.readNonTtyLine();
35
+ }
36
+ }
37
+ function renderHeader(prompt, message) {
38
+ return `${color.gray(GLYPHS.barStart)} ${symbol(prompt.state)} ${message}`;
39
+ }
40
+ function renderPasswordPrompt(prompt, opts) {
41
+ const value = prompt.masked;
42
+ if (prompt.state === "submit") {
43
+ return `${renderHeader(prompt, opts.message)}\n${color.gray(GLYPHS.bar)} ${color.dim(value)}\n${color.green(GLYPHS.barEnd)}`;
44
+ }
45
+ if (prompt.state === "cancel") {
46
+ return `${renderHeader(prompt, opts.message)}\n${color.gray(GLYPHS.bar)} ${color.dim.strikethrough(value)}\n${color.red(GLYPHS.barEnd)}`;
47
+ }
48
+ if (prompt.state === "error") {
49
+ return `${renderHeader(prompt, opts.message)}\n${symbolBar(prompt.state)} ${prompt.userInputWithCursor}\n${color.yellow(GLYPHS.barEnd)} ${color.yellow(prompt.error)}`;
50
+ }
51
+ return `${renderHeader(prompt, opts.message)}\n${symbolBar(prompt.state)} ${prompt.userInputWithCursor}\n${color.cyan(GLYPHS.barEnd)}`;
52
+ }
53
+ export function passwordPrompt(opts) {
54
+ return new PasswordPrompt(opts).prompt();
55
+ }