hedgequantx 2.9.59 → 2.9.61
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/package.json
CHANGED
|
@@ -216,9 +216,9 @@ const decodeProductCodes = (buffer) => {
|
|
|
216
216
|
} else if (wireType === 2) {
|
|
217
217
|
const [val, newOff] = readString(data, offset);
|
|
218
218
|
offset = newOff;
|
|
219
|
-
// Field IDs from
|
|
219
|
+
// Field IDs from Rithmic API
|
|
220
220
|
if (fieldNumber === 110101) result.exchange = val; // exchange
|
|
221
|
-
if (fieldNumber ===
|
|
221
|
+
if (fieldNumber === 100749) result.productCode = val; // product_code (ES, MES, MNQ, etc.)
|
|
222
222
|
if (fieldNumber === 110103) result.productName = val; // product_name
|
|
223
223
|
} else {
|
|
224
224
|
break;
|
|
@@ -30,8 +30,9 @@ const PNL_FIELDS = {
|
|
|
30
30
|
const SYMBOL_FIELDS = {
|
|
31
31
|
TEMPLATE_ID: 154467,
|
|
32
32
|
RP_CODE: 132766,
|
|
33
|
+
RP_CODE_2: 132764, // Another rp_code field
|
|
33
34
|
EXCHANGE: 110101,
|
|
34
|
-
PRODUCT_CODE:
|
|
35
|
+
PRODUCT_CODE: 100749, // Base symbol (ES, NQ, MNQ) - actual field ID from Rithmic
|
|
35
36
|
PRODUCT_NAME: 110103, // Product name
|
|
36
37
|
SYMBOL: 110100, // Full contract symbol (ESH26)
|
|
37
38
|
TRADING_SYMBOL: 157095, // Trading symbol
|
|
@@ -262,43 +263,45 @@ function decodeInstrumentPnL(buffer) {
|
|
|
262
263
|
|
|
263
264
|
/**
|
|
264
265
|
* Decode ResponseProductCodes (template 112) - list of available symbols
|
|
265
|
-
* @param {Buffer} buffer - Raw protobuf buffer
|
|
266
|
+
* @param {Buffer} buffer - Raw protobuf buffer (with 4-byte length prefix)
|
|
266
267
|
* @returns {Object} Decoded product codes
|
|
267
268
|
*/
|
|
268
269
|
function decodeProductCodes(buffer) {
|
|
270
|
+
// Skip 4-byte length prefix
|
|
271
|
+
const data = buffer.length > 4 ? buffer.slice(4) : buffer;
|
|
269
272
|
const result = { rpCode: [] };
|
|
270
273
|
let offset = 0;
|
|
271
274
|
|
|
272
|
-
while (offset <
|
|
275
|
+
while (offset < data.length) {
|
|
273
276
|
try {
|
|
274
|
-
const [tag, tagOffset] = readVarint(
|
|
277
|
+
const [tag, tagOffset] = readVarint(data, offset);
|
|
275
278
|
const wireType = tag & 0x7;
|
|
276
279
|
const fieldNumber = tag >>> 3;
|
|
277
280
|
offset = tagOffset;
|
|
278
281
|
|
|
279
282
|
switch (fieldNumber) {
|
|
280
283
|
case SYMBOL_FIELDS.TEMPLATE_ID:
|
|
281
|
-
[result.templateId, offset] = readVarint(
|
|
284
|
+
[result.templateId, offset] = readVarint(data, offset);
|
|
282
285
|
break;
|
|
283
286
|
case SYMBOL_FIELDS.RP_CODE:
|
|
284
287
|
let rpCode;
|
|
285
|
-
[rpCode, offset] = readLengthDelimited(
|
|
288
|
+
[rpCode, offset] = readLengthDelimited(data, offset);
|
|
286
289
|
result.rpCode.push(rpCode);
|
|
287
290
|
break;
|
|
288
291
|
case SYMBOL_FIELDS.EXCHANGE:
|
|
289
|
-
[result.exchange, offset] = readLengthDelimited(
|
|
292
|
+
[result.exchange, offset] = readLengthDelimited(data, offset);
|
|
290
293
|
break;
|
|
291
294
|
case SYMBOL_FIELDS.PRODUCT_CODE:
|
|
292
|
-
[result.productCode, offset] = readLengthDelimited(
|
|
295
|
+
[result.productCode, offset] = readLengthDelimited(data, offset);
|
|
293
296
|
break;
|
|
294
297
|
case SYMBOL_FIELDS.PRODUCT_NAME:
|
|
295
|
-
[result.productName, offset] = readLengthDelimited(
|
|
298
|
+
[result.productName, offset] = readLengthDelimited(data, offset);
|
|
296
299
|
break;
|
|
297
300
|
case SYMBOL_FIELDS.USER_MSG:
|
|
298
|
-
[result.userMsg, offset] = readLengthDelimited(
|
|
301
|
+
[result.userMsg, offset] = readLengthDelimited(data, offset);
|
|
299
302
|
break;
|
|
300
303
|
default:
|
|
301
|
-
offset = skipField(
|
|
304
|
+
offset = skipField(data, offset, wireType);
|
|
302
305
|
}
|
|
303
306
|
} catch (error) {
|
|
304
307
|
break;
|
package/src/services/session.js
CHANGED
|
@@ -112,27 +112,36 @@ const connections = {
|
|
|
112
112
|
},
|
|
113
113
|
|
|
114
114
|
saveToStorage() {
|
|
115
|
-
|
|
115
|
+
// Load existing sessions to preserve AI agents
|
|
116
|
+
const existingSessions = storage.load();
|
|
117
|
+
const aiSessions = existingSessions.filter(s => s.type === 'ai');
|
|
118
|
+
|
|
119
|
+
// Build Rithmic sessions
|
|
120
|
+
const rithmicSessions = this.services.map(conn => ({
|
|
116
121
|
type: conn.type,
|
|
117
122
|
propfirm: conn.propfirm,
|
|
118
123
|
propfirmKey: conn.service.propfirmKey || conn.propfirmKey,
|
|
119
124
|
credentials: conn.service.credentials,
|
|
120
125
|
}));
|
|
121
126
|
|
|
122
|
-
|
|
127
|
+
// Merge: AI sessions + Rithmic sessions
|
|
128
|
+
storage.save([...aiSessions, ...rithmicSessions]);
|
|
123
129
|
},
|
|
124
130
|
|
|
125
131
|
async restoreFromStorage() {
|
|
126
132
|
loadServices();
|
|
127
133
|
const sessions = storage.load();
|
|
128
134
|
|
|
129
|
-
|
|
135
|
+
// Filter only Rithmic sessions (AI sessions are managed by ai-agents.js)
|
|
136
|
+
const rithmicSessions = sessions.filter(s => s.type === 'rithmic');
|
|
137
|
+
|
|
138
|
+
if (!rithmicSessions.length) {
|
|
130
139
|
return false;
|
|
131
140
|
}
|
|
132
141
|
|
|
133
|
-
log.info('Restoring sessions', { count:
|
|
142
|
+
log.info('Restoring sessions', { count: rithmicSessions.length });
|
|
134
143
|
|
|
135
|
-
for (const session of
|
|
144
|
+
for (const session of rithmicSessions) {
|
|
136
145
|
try {
|
|
137
146
|
await this._restoreSession(session);
|
|
138
147
|
} catch (err) {
|
package/src/utils/prompts.js
CHANGED
|
@@ -1,19 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fileoverview Centralized prompts utility
|
|
2
|
+
* @fileoverview Centralized prompts utility
|
|
3
3
|
* @module utils/prompts
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* prompts (password, list selection).
|
|
5
|
+
* Simple prompts using inquirer for user input.
|
|
6
|
+
* No spinners on prompts - spinners are only for data loading (use ora).
|
|
8
7
|
*/
|
|
9
8
|
|
|
10
9
|
const inquirer = require('inquirer');
|
|
11
10
|
const readline = require('readline');
|
|
12
11
|
const chalk = require('chalk');
|
|
13
12
|
|
|
14
|
-
// Spinner frames for yellow waiting indicator
|
|
15
|
-
const SPINNER_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
16
|
-
|
|
17
13
|
/** @type {readline.Interface|null} */
|
|
18
14
|
let rl = null;
|
|
19
15
|
|
|
@@ -48,305 +44,45 @@ const closeReadline = () => {
|
|
|
48
44
|
};
|
|
49
45
|
|
|
50
46
|
/**
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
* @param {string} message - Prompt message
|
|
54
|
-
* @returns {Promise<string>}
|
|
55
|
-
*/
|
|
56
|
-
const animatedPrompt = (message) => {
|
|
57
|
-
return new Promise((resolve) => {
|
|
58
|
-
prepareStdin();
|
|
59
|
-
closeReadline();
|
|
60
|
-
|
|
61
|
-
let frameIndex = 0;
|
|
62
|
-
let userInput = '';
|
|
63
|
-
let cursorPos = 0;
|
|
64
|
-
|
|
65
|
-
// Enable raw mode for character-by-character input
|
|
66
|
-
if (process.stdin.isTTY) {
|
|
67
|
-
process.stdin.setRawMode(true);
|
|
68
|
-
}
|
|
69
|
-
process.stdin.resume();
|
|
70
|
-
|
|
71
|
-
const render = () => {
|
|
72
|
-
const spinner = chalk.yellow(SPINNER_FRAMES[frameIndex]);
|
|
73
|
-
const line = `\r${spinner} ${message} ${userInput}`;
|
|
74
|
-
process.stdout.write('\r\x1b[K'); // Clear line
|
|
75
|
-
process.stdout.write(line);
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
// Animate spinner every 80ms
|
|
79
|
-
const spinnerInterval = setInterval(() => {
|
|
80
|
-
frameIndex = (frameIndex + 1) % SPINNER_FRAMES.length;
|
|
81
|
-
render();
|
|
82
|
-
}, 80);
|
|
83
|
-
|
|
84
|
-
render();
|
|
85
|
-
|
|
86
|
-
const onData = (key) => {
|
|
87
|
-
const char = key.toString();
|
|
88
|
-
|
|
89
|
-
// Enter key
|
|
90
|
-
if (char === '\r' || char === '\n') {
|
|
91
|
-
clearInterval(spinnerInterval);
|
|
92
|
-
process.stdin.removeListener('data', onData);
|
|
93
|
-
if (process.stdin.isTTY) {
|
|
94
|
-
process.stdin.setRawMode(false);
|
|
95
|
-
}
|
|
96
|
-
process.stdout.write('\n');
|
|
97
|
-
resolve(userInput);
|
|
98
|
-
return;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// Ctrl+C
|
|
102
|
-
if (char === '\x03') {
|
|
103
|
-
clearInterval(spinnerInterval);
|
|
104
|
-
process.stdin.removeListener('data', onData);
|
|
105
|
-
if (process.stdin.isTTY) {
|
|
106
|
-
process.stdin.setRawMode(false);
|
|
107
|
-
}
|
|
108
|
-
process.stdout.write('\n');
|
|
109
|
-
process.exit(0);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// Backspace
|
|
113
|
-
if (char === '\x7f' || char === '\b') {
|
|
114
|
-
if (userInput.length > 0) {
|
|
115
|
-
userInput = userInput.slice(0, -1);
|
|
116
|
-
render();
|
|
117
|
-
}
|
|
118
|
-
return;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
// Regular printable character
|
|
122
|
-
if (char >= ' ' && char <= '~') {
|
|
123
|
-
userInput += char;
|
|
124
|
-
render();
|
|
125
|
-
}
|
|
126
|
-
};
|
|
127
|
-
|
|
128
|
-
process.stdin.on('data', onData);
|
|
129
|
-
});
|
|
130
|
-
};
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* Animated Y/N confirm prompt
|
|
134
|
-
* Shows [Y/n] or [y/N] based on default
|
|
135
|
-
* @param {string} message - Prompt message
|
|
136
|
-
* @param {boolean} defaultVal - Default value
|
|
137
|
-
* @returns {Promise<boolean>}
|
|
138
|
-
*/
|
|
139
|
-
const animatedConfirm = (message, defaultVal = true) => {
|
|
140
|
-
return new Promise((resolve) => {
|
|
141
|
-
prepareStdin();
|
|
142
|
-
closeReadline();
|
|
143
|
-
|
|
144
|
-
let frameIndex = 0;
|
|
145
|
-
const hint = defaultVal ? '[Y/n]' : '[y/N]';
|
|
146
|
-
|
|
147
|
-
if (process.stdin.isTTY) {
|
|
148
|
-
process.stdin.setRawMode(true);
|
|
149
|
-
}
|
|
150
|
-
process.stdin.resume();
|
|
151
|
-
|
|
152
|
-
const render = () => {
|
|
153
|
-
const spinner = chalk.yellow(SPINNER_FRAMES[frameIndex]);
|
|
154
|
-
process.stdout.write('\r\x1b[K');
|
|
155
|
-
process.stdout.write(`${spinner} ${message} ${chalk.dim(hint)} `);
|
|
156
|
-
};
|
|
157
|
-
|
|
158
|
-
const spinnerInterval = setInterval(() => {
|
|
159
|
-
frameIndex = (frameIndex + 1) % SPINNER_FRAMES.length;
|
|
160
|
-
render();
|
|
161
|
-
}, 80);
|
|
162
|
-
|
|
163
|
-
render();
|
|
164
|
-
|
|
165
|
-
const onData = (key) => {
|
|
166
|
-
const char = key.toString().toLowerCase();
|
|
167
|
-
|
|
168
|
-
// Enter = use default
|
|
169
|
-
if (char === '\r' || char === '\n') {
|
|
170
|
-
cleanup();
|
|
171
|
-
process.stdout.write(defaultVal ? 'Yes' : 'No');
|
|
172
|
-
process.stdout.write('\n');
|
|
173
|
-
resolve(defaultVal);
|
|
174
|
-
return;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
// Y = yes
|
|
178
|
-
if (char === 'y') {
|
|
179
|
-
cleanup();
|
|
180
|
-
process.stdout.write('Yes');
|
|
181
|
-
process.stdout.write('\n');
|
|
182
|
-
resolve(true);
|
|
183
|
-
return;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
// N = no
|
|
187
|
-
if (char === 'n') {
|
|
188
|
-
cleanup();
|
|
189
|
-
process.stdout.write('No');
|
|
190
|
-
process.stdout.write('\n');
|
|
191
|
-
resolve(false);
|
|
192
|
-
return;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
// Ctrl+C
|
|
196
|
-
if (char === '\x03') {
|
|
197
|
-
cleanup();
|
|
198
|
-
process.stdout.write('\n');
|
|
199
|
-
process.exit(0);
|
|
200
|
-
}
|
|
201
|
-
};
|
|
202
|
-
|
|
203
|
-
const cleanup = () => {
|
|
204
|
-
clearInterval(spinnerInterval);
|
|
205
|
-
process.stdin.removeListener('data', onData);
|
|
206
|
-
if (process.stdin.isTTY) {
|
|
207
|
-
process.stdin.setRawMode(false);
|
|
208
|
-
}
|
|
209
|
-
};
|
|
210
|
-
|
|
211
|
-
process.stdin.on('data', onData);
|
|
212
|
-
});
|
|
213
|
-
};
|
|
214
|
-
|
|
215
|
-
/**
|
|
216
|
-
* Animated list selection with arrow keys
|
|
217
|
-
* @param {string} message - Prompt message
|
|
218
|
-
* @param {Array<{name: string, value: any}>} choices - Options
|
|
219
|
-
* @returns {Promise<any>}
|
|
220
|
-
*/
|
|
221
|
-
const animatedSelect = (message, choices) => {
|
|
222
|
-
return new Promise((resolve) => {
|
|
223
|
-
prepareStdin();
|
|
224
|
-
closeReadline();
|
|
225
|
-
|
|
226
|
-
let frameIndex = 0;
|
|
227
|
-
let selectedIndex = 0;
|
|
228
|
-
const validChoices = choices.filter(c => !c.disabled);
|
|
229
|
-
|
|
230
|
-
if (process.stdin.isTTY) {
|
|
231
|
-
process.stdin.setRawMode(true);
|
|
232
|
-
}
|
|
233
|
-
process.stdin.resume();
|
|
234
|
-
|
|
235
|
-
const render = () => {
|
|
236
|
-
const spinner = chalk.yellow(SPINNER_FRAMES[frameIndex]);
|
|
237
|
-
// Move cursor to start and clear down
|
|
238
|
-
process.stdout.write('\r\x1b[K');
|
|
239
|
-
process.stdout.write(`${spinner} ${message}\n`);
|
|
240
|
-
|
|
241
|
-
validChoices.forEach((choice, i) => {
|
|
242
|
-
process.stdout.write('\x1b[K'); // Clear line
|
|
243
|
-
if (i === selectedIndex) {
|
|
244
|
-
process.stdout.write(`${chalk.cyan('❯')} ${chalk.cyan(choice.name)}\n`);
|
|
245
|
-
} else {
|
|
246
|
-
process.stdout.write(` ${choice.name}\n`);
|
|
247
|
-
}
|
|
248
|
-
});
|
|
249
|
-
|
|
250
|
-
// Move cursor back up
|
|
251
|
-
process.stdout.write(`\x1b[${validChoices.length + 1}A`);
|
|
252
|
-
};
|
|
253
|
-
|
|
254
|
-
const spinnerInterval = setInterval(() => {
|
|
255
|
-
frameIndex = (frameIndex + 1) % SPINNER_FRAMES.length;
|
|
256
|
-
render();
|
|
257
|
-
}, 80);
|
|
258
|
-
|
|
259
|
-
render();
|
|
260
|
-
|
|
261
|
-
let escapeSeq = '';
|
|
262
|
-
|
|
263
|
-
const onData = (key) => {
|
|
264
|
-
const char = key.toString();
|
|
265
|
-
|
|
266
|
-
// Handle escape sequences (arrow keys)
|
|
267
|
-
if (char === '\x1b') {
|
|
268
|
-
escapeSeq = char;
|
|
269
|
-
return;
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
if (escapeSeq === '\x1b' && char === '[') {
|
|
273
|
-
escapeSeq += char;
|
|
274
|
-
return;
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
if (escapeSeq === '\x1b[') {
|
|
278
|
-
escapeSeq = '';
|
|
279
|
-
// Up arrow
|
|
280
|
-
if (char === 'A') {
|
|
281
|
-
selectedIndex = Math.max(0, selectedIndex - 1);
|
|
282
|
-
render();
|
|
283
|
-
return;
|
|
284
|
-
}
|
|
285
|
-
// Down arrow
|
|
286
|
-
if (char === 'B') {
|
|
287
|
-
selectedIndex = Math.min(validChoices.length - 1, selectedIndex + 1);
|
|
288
|
-
render();
|
|
289
|
-
return;
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
// Enter = select
|
|
294
|
-
if (char === '\r' || char === '\n') {
|
|
295
|
-
cleanup();
|
|
296
|
-
// Clear the menu lines
|
|
297
|
-
process.stdout.write('\r\x1b[K');
|
|
298
|
-
for (let i = 0; i < validChoices.length; i++) {
|
|
299
|
-
process.stdout.write('\x1b[B\x1b[K');
|
|
300
|
-
}
|
|
301
|
-
process.stdout.write(`\x1b[${validChoices.length}A`);
|
|
302
|
-
process.stdout.write(`${chalk.yellow('⠋')} ${message} ${chalk.cyan(validChoices[selectedIndex].name)}\n`);
|
|
303
|
-
resolve(validChoices[selectedIndex].value);
|
|
304
|
-
return;
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
// Ctrl+C
|
|
308
|
-
if (char === '\x03') {
|
|
309
|
-
cleanup();
|
|
310
|
-
process.stdout.write('\n');
|
|
311
|
-
process.exit(0);
|
|
312
|
-
}
|
|
313
|
-
};
|
|
314
|
-
|
|
315
|
-
const cleanup = () => {
|
|
316
|
-
clearInterval(spinnerInterval);
|
|
317
|
-
process.stdin.removeListener('data', onData);
|
|
318
|
-
if (process.stdin.isTTY) {
|
|
319
|
-
process.stdin.setRawMode(false);
|
|
320
|
-
}
|
|
321
|
-
};
|
|
322
|
-
|
|
323
|
-
process.stdin.on('data', onData);
|
|
324
|
-
});
|
|
325
|
-
};
|
|
326
|
-
|
|
327
|
-
/**
|
|
328
|
-
* Wait for Enter key with animated spinner
|
|
329
|
-
* @param {string} [message='Press Enter to continue...'] - Message to display
|
|
47
|
+
* Wait for Enter key
|
|
48
|
+
* @param {string} [message='Press Enter to continue...'] - Message
|
|
330
49
|
* @returns {Promise<void>}
|
|
331
50
|
*/
|
|
332
51
|
const waitForEnter = async (message = 'Press Enter to continue...') => {
|
|
333
|
-
|
|
52
|
+
closeReadline();
|
|
53
|
+
prepareStdin();
|
|
54
|
+
|
|
55
|
+
await inquirer.prompt([{
|
|
56
|
+
type: 'input',
|
|
57
|
+
name: 'continue',
|
|
58
|
+
message,
|
|
59
|
+
prefix: '',
|
|
60
|
+
}]);
|
|
334
61
|
};
|
|
335
62
|
|
|
336
63
|
/**
|
|
337
|
-
* Text input
|
|
64
|
+
* Text input
|
|
338
65
|
* @param {string} message - Prompt message
|
|
339
66
|
* @param {string} [defaultVal=''] - Default value
|
|
340
67
|
* @returns {Promise<string>}
|
|
341
68
|
*/
|
|
342
69
|
const textInput = async (message, defaultVal = '') => {
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
70
|
+
closeReadline();
|
|
71
|
+
prepareStdin();
|
|
72
|
+
|
|
73
|
+
const { value } = await inquirer.prompt([{
|
|
74
|
+
type: 'input',
|
|
75
|
+
name: 'value',
|
|
76
|
+
message,
|
|
77
|
+
default: defaultVal,
|
|
78
|
+
prefix: '',
|
|
79
|
+
}]);
|
|
80
|
+
|
|
81
|
+
return value;
|
|
346
82
|
};
|
|
347
83
|
|
|
348
84
|
/**
|
|
349
|
-
* Password input (masked)
|
|
85
|
+
* Password input (masked)
|
|
350
86
|
* @param {string} message - Prompt message
|
|
351
87
|
* @returns {Promise<string>}
|
|
352
88
|
*/
|
|
@@ -357,7 +93,7 @@ const passwordInput = async (message) => {
|
|
|
357
93
|
const { value } = await inquirer.prompt([{
|
|
358
94
|
type: 'password',
|
|
359
95
|
name: 'value',
|
|
360
|
-
message
|
|
96
|
+
message,
|
|
361
97
|
mask: '*',
|
|
362
98
|
prefix: '',
|
|
363
99
|
}]);
|
|
@@ -366,17 +102,33 @@ const passwordInput = async (message) => {
|
|
|
366
102
|
};
|
|
367
103
|
|
|
368
104
|
/**
|
|
369
|
-
* Confirm prompt with
|
|
105
|
+
* Confirm prompt with Yes/No selection
|
|
370
106
|
* @param {string} message - Prompt message
|
|
371
107
|
* @param {boolean} [defaultVal=true] - Default value
|
|
372
108
|
* @returns {Promise<boolean>}
|
|
373
109
|
*/
|
|
374
110
|
const confirmPrompt = async (message, defaultVal = true) => {
|
|
375
|
-
|
|
111
|
+
closeReadline();
|
|
112
|
+
prepareStdin();
|
|
113
|
+
|
|
114
|
+
const choices = defaultVal
|
|
115
|
+
? [{ name: 'Yes', value: true }, { name: 'No', value: false }]
|
|
116
|
+
: [{ name: 'No', value: false }, { name: 'Yes', value: true }];
|
|
117
|
+
|
|
118
|
+
const { value } = await inquirer.prompt([{
|
|
119
|
+
type: 'list',
|
|
120
|
+
name: 'value',
|
|
121
|
+
message,
|
|
122
|
+
choices,
|
|
123
|
+
prefix: '',
|
|
124
|
+
loop: false,
|
|
125
|
+
}]);
|
|
126
|
+
|
|
127
|
+
return value;
|
|
376
128
|
};
|
|
377
129
|
|
|
378
130
|
/**
|
|
379
|
-
* Number input with
|
|
131
|
+
* Number input with validation
|
|
380
132
|
* @param {string} message - Prompt message
|
|
381
133
|
* @param {number} [defaultVal=1] - Default value
|
|
382
134
|
* @param {number} [min=1] - Minimum value
|
|
@@ -384,40 +136,55 @@ const confirmPrompt = async (message, defaultVal = true) => {
|
|
|
384
136
|
* @returns {Promise<number>}
|
|
385
137
|
*/
|
|
386
138
|
const numberInput = async (message, defaultVal = 1, min = 1, max = 1000) => {
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
|
|
139
|
+
closeReadline();
|
|
140
|
+
prepareStdin();
|
|
141
|
+
|
|
142
|
+
const { value } = await inquirer.prompt([{
|
|
143
|
+
type: 'input',
|
|
144
|
+
name: 'value',
|
|
145
|
+
message: `${message} (${min}-${max})`,
|
|
146
|
+
default: String(defaultVal),
|
|
147
|
+
prefix: '',
|
|
148
|
+
validate: (v) => {
|
|
149
|
+
const n = parseInt(v, 10);
|
|
150
|
+
if (isNaN(n)) return 'Enter a number';
|
|
151
|
+
if (n < min) return `Min: ${min}`;
|
|
152
|
+
if (n > max) return `Max: ${max}`;
|
|
153
|
+
return true;
|
|
154
|
+
},
|
|
155
|
+
}]);
|
|
156
|
+
|
|
157
|
+
return parseInt(value, 10) || defaultVal;
|
|
405
158
|
};
|
|
406
159
|
|
|
407
160
|
/**
|
|
408
|
-
* Select from options with
|
|
161
|
+
* Select from options with arrow keys
|
|
409
162
|
* @param {string} message - Prompt message
|
|
410
163
|
* @param {Array<{label: string, value: any, disabled?: boolean}>} options - Options
|
|
411
164
|
* @returns {Promise<any>}
|
|
412
165
|
*/
|
|
413
166
|
const selectOption = async (message, options) => {
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
167
|
+
closeReadline();
|
|
168
|
+
prepareStdin();
|
|
169
|
+
|
|
170
|
+
const choices = options.map(opt => {
|
|
171
|
+
if (opt.disabled) {
|
|
172
|
+
return new inquirer.Separator(opt.label);
|
|
173
|
+
}
|
|
174
|
+
return { name: opt.label, value: opt.value };
|
|
175
|
+
});
|
|
419
176
|
|
|
420
|
-
|
|
177
|
+
const { value } = await inquirer.prompt([{
|
|
178
|
+
type: 'list',
|
|
179
|
+
name: 'value',
|
|
180
|
+
message,
|
|
181
|
+
choices,
|
|
182
|
+
prefix: '',
|
|
183
|
+
loop: false,
|
|
184
|
+
pageSize: 15,
|
|
185
|
+
}]);
|
|
186
|
+
|
|
187
|
+
return value;
|
|
421
188
|
};
|
|
422
189
|
|
|
423
190
|
module.exports = {
|