Initial commit
This commit is contained in:
commit
c903041474
4
bin/eagent
Executable file
4
bin/eagent
Executable file
@ -0,0 +1,4 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
require('../src/cli/index');
|
||||||
BIN
demo/.DS_Store
vendored
Normal file
BIN
demo/.DS_Store
vendored
Normal file
Binary file not shown.
157
demo/cli_demo.js
Normal file
157
demo/cli_demo.js
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const readline = require('readline');
|
||||||
|
|
||||||
|
const { WORKSPACE, WORKSPACE_NAME, PROMPT, COLOR_ENABLED, RESET } = require('./lib/env');
|
||||||
|
const { loadSystemPrompt } = require('./lib/system_prompt');
|
||||||
|
const { createState } = require('./lib/state');
|
||||||
|
const { handleCommand } = require('./lib/commands');
|
||||||
|
const { createAssistantSimulator } = require('./lib/assistant_sim');
|
||||||
|
const { openCommandMenu } = require('./lib/ui/command_menu');
|
||||||
|
|
||||||
|
const SHOW_REASONING = process.env.EA_REASONING !== '0';
|
||||||
|
|
||||||
|
const SYSTEM_PROMPT = loadSystemPrompt();
|
||||||
|
void SYSTEM_PROMPT; // demo only
|
||||||
|
|
||||||
|
const state = createState();
|
||||||
|
const simulateAssistantReply = createAssistantSimulator(state, {
|
||||||
|
showReasoning: SHOW_REASONING,
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('');
|
||||||
|
console.log('eagent demo');
|
||||||
|
console.log(`model: kimi | allow: full_access | cwd: ${WORKSPACE_NAME}`);
|
||||||
|
console.log('');
|
||||||
|
|
||||||
|
let rl = null;
|
||||||
|
let commandMenuActive = false;
|
||||||
|
let closingExplicit = false;
|
||||||
|
let menuSearchTerm = '';
|
||||||
|
let menuJustClosedAt = 0;
|
||||||
|
let menuInjectedCommand = null;
|
||||||
|
|
||||||
|
readline.emitKeypressEvents(process.stdin);
|
||||||
|
if (process.stdin.isTTY) process.stdin.setRawMode(true);
|
||||||
|
|
||||||
|
initReadline();
|
||||||
|
|
||||||
|
process.stdin.on('keypress', (str) => {
|
||||||
|
if (commandMenuActive) return;
|
||||||
|
if (str === '/' && (rl.line === '' || rl.line === '/')) {
|
||||||
|
commandMenuActive = true;
|
||||||
|
menuSearchTerm = '';
|
||||||
|
void openCommandMenu({
|
||||||
|
rl,
|
||||||
|
prompt: PROMPT,
|
||||||
|
pageSize: 6,
|
||||||
|
colorEnabled: COLOR_ENABLED,
|
||||||
|
resetAnsi: RESET,
|
||||||
|
onInput: (input) => {
|
||||||
|
menuSearchTerm = input || '';
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then((result) => {
|
||||||
|
if (result && result.chosen) {
|
||||||
|
menuInjectedCommand = result.chosen;
|
||||||
|
menuSearchTerm = '';
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
commandMenuActive = false;
|
||||||
|
menuJustClosedAt = Date.now();
|
||||||
|
drainStdin();
|
||||||
|
rl.line = '';
|
||||||
|
rl.cursor = 0;
|
||||||
|
rl.prompt();
|
||||||
|
if (menuInjectedCommand) {
|
||||||
|
const injected = menuInjectedCommand;
|
||||||
|
menuInjectedCommand = null;
|
||||||
|
setImmediate(() => injectLine(injected));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function initReadline() {
|
||||||
|
if (rl) {
|
||||||
|
try {
|
||||||
|
rl.removeAllListeners();
|
||||||
|
} catch (_) {}
|
||||||
|
}
|
||||||
|
rl = readline.createInterface({
|
||||||
|
input: process.stdin,
|
||||||
|
output: process.stdout,
|
||||||
|
terminal: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
process.stdin.resume();
|
||||||
|
rl.setPrompt(PROMPT);
|
||||||
|
rl.prompt();
|
||||||
|
|
||||||
|
rl.on('line', async (line) => {
|
||||||
|
if (commandMenuActive) {
|
||||||
|
rl.prompt();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const input = line.trim();
|
||||||
|
if (menuJustClosedAt) {
|
||||||
|
const tooOld = Date.now() - menuJustClosedAt > 800;
|
||||||
|
const normalizedMenu = String(menuSearchTerm).trim().replace(/^\/+/, '');
|
||||||
|
const normalizedInput = input.replace(/^\/+/, '');
|
||||||
|
if (!tooOld && (!normalizedMenu ? input === '' : normalizedInput === normalizedMenu)) {
|
||||||
|
menuJustClosedAt = 0;
|
||||||
|
rl.prompt();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
menuJustClosedAt = 0;
|
||||||
|
}
|
||||||
|
if (!input) {
|
||||||
|
rl.prompt();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.startsWith('/')) {
|
||||||
|
await handleCommand(input, state, {
|
||||||
|
rl,
|
||||||
|
workspacePath: WORKSPACE,
|
||||||
|
markClosing: () => {
|
||||||
|
closingExplicit = true;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
rl.prompt();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`用户:${line}`);
|
||||||
|
console.log('');
|
||||||
|
|
||||||
|
await simulateAssistantReply(input);
|
||||||
|
|
||||||
|
rl.prompt();
|
||||||
|
});
|
||||||
|
|
||||||
|
rl.on('close', () => {
|
||||||
|
process.stdout.write('\n');
|
||||||
|
if (closingExplicit) {
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
closingExplicit = false;
|
||||||
|
initReadline();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function drainStdin() {
|
||||||
|
if (!process.stdin.isTTY) return;
|
||||||
|
process.stdin.pause();
|
||||||
|
// Drain any buffered input from the command menu.
|
||||||
|
while (process.stdin.read() !== null) {}
|
||||||
|
process.stdin.resume();
|
||||||
|
}
|
||||||
|
|
||||||
|
function injectLine(text) {
|
||||||
|
if (!rl) return;
|
||||||
|
rl.write(text);
|
||||||
|
rl.write('\n');
|
||||||
|
}
|
||||||
455
demo/cli_demo_fixed.js
Normal file
455
demo/cli_demo_fixed.js
Normal file
@ -0,0 +1,455 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
const os = require('os');
|
||||||
|
const readline = require('readline');
|
||||||
|
const crypto = require('crypto');
|
||||||
|
|
||||||
|
const WORKSPACE = process.cwd();
|
||||||
|
const WORKSPACE_NAME = path.basename(WORKSPACE) || WORKSPACE;
|
||||||
|
const USERNAME = (os.userInfo().username || 'user');
|
||||||
|
const PROMPT = `${USERNAME}@${WORKSPACE_NAME} `;
|
||||||
|
|
||||||
|
const SHOW_REASONING = process.env.EA_REASONING !== '0';
|
||||||
|
const COLOR_ENABLED = !process.env.NO_COLOR;
|
||||||
|
const GREY = COLOR_ENABLED ? '\x1b[90m' : '';
|
||||||
|
const BLUE = COLOR_ENABLED ? '\x1b[94m' : '';
|
||||||
|
const RESET = COLOR_ENABLED ? '\x1b[0m' : '';
|
||||||
|
|
||||||
|
// ANSI 转义序列 - 用于精确光标控制
|
||||||
|
const ANSI = {
|
||||||
|
HIDE_CURSOR: '\x1b[?25l',
|
||||||
|
SHOW_CURSOR: '\x1b[?25h',
|
||||||
|
CLEAR_LINE: '\x1b[K', // 清除从光标到行尾
|
||||||
|
CLEAR_ENTIRE_LINE: '\x1b[2K', // 清除整行
|
||||||
|
MOVE_UP: (n) => `\x1b[${n}A`, // 光标上移n行
|
||||||
|
MOVE_DOWN: (n) => `\x1b[${n}B`, // 光标下移n行
|
||||||
|
MOVE_LEFT: (n) => `\x1b[${n}D`, // 光标左移n列
|
||||||
|
SAVE_CURSOR: '\x1b[s', // 保存光标位置(部分终端)
|
||||||
|
RESTORE_CURSOR: '\u001b[u', // 恢复光标位置
|
||||||
|
ALT_SCREEN: '\x1b[?1049h', // 切换到备用屏幕
|
||||||
|
MAIN_SCREEN: '\x1b[?1049l', // 返回主屏幕
|
||||||
|
};
|
||||||
|
|
||||||
|
const SPINNER_FRAMES = ['⠙','⠹','⠸','⠼','⠴','⠦','⠧','⠇','⠏'];
|
||||||
|
const SPINNER_INTERVAL_MS = 80;
|
||||||
|
const SPINNER_INDENT = ' ';
|
||||||
|
|
||||||
|
const SYSTEM_PROMPT = loadSystemPrompt();
|
||||||
|
void SYSTEM_PROMPT;
|
||||||
|
|
||||||
|
const COMMAND_MENU_ITEMS = [
|
||||||
|
{ cmd: '/new', desc: '创建新对话' },
|
||||||
|
{ cmd: '/resume', desc: '加载旧对话' },
|
||||||
|
{ cmd: '/allow', desc: '切换运行模式(只读/无限制)' },
|
||||||
|
{ cmd: '/model', desc: '切换模型和思考模式' },
|
||||||
|
{ cmd: '/status', desc: '查看当前对话状态' },
|
||||||
|
{ cmd: '/compact', desc: '压缩当前对话' },
|
||||||
|
{ cmd: '/config', desc: '查看当前配置' },
|
||||||
|
{ cmd: '/help', desc: '显示指令列表' },
|
||||||
|
{ cmd: '/exit', desc: '退出程序' },
|
||||||
|
];
|
||||||
|
const COMMAND_MENU_SIZE = 6;
|
||||||
|
|
||||||
|
const state = {
|
||||||
|
conversationId: crypto.randomUUID(),
|
||||||
|
allow: 'full_access',
|
||||||
|
model: 'kimi',
|
||||||
|
thinking: 'thinking',
|
||||||
|
tokenUsage: 0,
|
||||||
|
baseUrl: 'https://api.example.com/v1',
|
||||||
|
apiKey: 'sk-xxxxxxw2y',
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log('');
|
||||||
|
console.log('eagent demo');
|
||||||
|
console.log(`model: kimi | allow: full_access | cwd: ${WORKSPACE_NAME}`);
|
||||||
|
console.log('');
|
||||||
|
|
||||||
|
const rl = readline.createInterface({
|
||||||
|
input: process.stdin,
|
||||||
|
output: process.stdout,
|
||||||
|
terminal: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
readline.emitKeypressEvents(process.stdin, rl);
|
||||||
|
if (process.stdin.isTTY) process.stdin.setRawMode(true);
|
||||||
|
|
||||||
|
rl.setPrompt(PROMPT);
|
||||||
|
rl.prompt();
|
||||||
|
|
||||||
|
const menuState = {
|
||||||
|
active: false,
|
||||||
|
selected: 0,
|
||||||
|
offset: 0,
|
||||||
|
startRow: null, // 记录菜单开始的行号(相对位置)
|
||||||
|
};
|
||||||
|
|
||||||
|
process.stdin.on('keypress', async (str, key) => {
|
||||||
|
// 如果菜单处于激活状态,优先处理菜单导航
|
||||||
|
if (menuState.active) {
|
||||||
|
await handleMenuKeypress(str, key);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检测 / 键来打开菜单
|
||||||
|
if (str === '/' && (rl.line === '' || rl.line === '/')) {
|
||||||
|
openCommandMenu();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
async function handleMenuKeypress(str, key) {
|
||||||
|
if (!key) return;
|
||||||
|
|
||||||
|
switch (key.name) {
|
||||||
|
case 'up':
|
||||||
|
menuState.selected = Math.max(0, menuState.selected - 1);
|
||||||
|
renderCommandMenu();
|
||||||
|
break;
|
||||||
|
case 'down':
|
||||||
|
menuState.selected = Math.min(
|
||||||
|
COMMAND_MENU_ITEMS.length - 1,
|
||||||
|
menuState.selected + 1
|
||||||
|
);
|
||||||
|
renderCommandMenu();
|
||||||
|
break;
|
||||||
|
case 'return':
|
||||||
|
case 'enter':
|
||||||
|
const chosen = COMMAND_MENU_ITEMS[menuState.selected]?.cmd;
|
||||||
|
await closeCommandMenu();
|
||||||
|
if (chosen) {
|
||||||
|
await handleCommand(chosen);
|
||||||
|
process.stdout.write('\n');
|
||||||
|
}
|
||||||
|
rl.prompt();
|
||||||
|
break;
|
||||||
|
case 'escape':
|
||||||
|
await closeCommandMenu();
|
||||||
|
process.stdout.write('\n');
|
||||||
|
rl.prompt();
|
||||||
|
break;
|
||||||
|
case 'c':
|
||||||
|
if (key.ctrl) {
|
||||||
|
await closeCommandMenu();
|
||||||
|
rl.close();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rl.on('line', async (line) => {
|
||||||
|
const input = line.trim();
|
||||||
|
if (!input) {
|
||||||
|
rl.prompt();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.startsWith('/')) {
|
||||||
|
await handleCommand(input);
|
||||||
|
rl.prompt();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`用户:${line}`);
|
||||||
|
console.log('');
|
||||||
|
await simulateAssistantReply(input);
|
||||||
|
rl.prompt();
|
||||||
|
});
|
||||||
|
|
||||||
|
rl.on('close', () => {
|
||||||
|
process.stdout.write('\n');
|
||||||
|
process.exit(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
function loadSystemPrompt() {
|
||||||
|
const p = path.join(__dirname, '..', 'prompts', 'system.txt');
|
||||||
|
try {
|
||||||
|
return fs.readFileSync(p, 'utf8');
|
||||||
|
} catch (_) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function truncateText(text, maxLen) {
|
||||||
|
if (text.length <= maxLen) return text;
|
||||||
|
return text.slice(0, maxLen) + '...';
|
||||||
|
}
|
||||||
|
|
||||||
|
function sleep(ms) {
|
||||||
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function simulateAssistantReply(userInput) {
|
||||||
|
const spinner = startSpinner('');
|
||||||
|
await sleep(600);
|
||||||
|
spinner.setLabel(' 思考中...');
|
||||||
|
await sleep(1200);
|
||||||
|
stopSpinner(spinner, SHOW_REASONING ? '○ 思考完成' : '○');
|
||||||
|
|
||||||
|
if (SHOW_REASONING) {
|
||||||
|
const reasoning = `好的,用户让我根据输入「${userInput}」做出回应,并给出明确的下一步。`;
|
||||||
|
const snippet = truncateText(reasoning, 50);
|
||||||
|
await streamText('', `${GREY}${snippet}${RESET}`, 12);
|
||||||
|
}
|
||||||
|
|
||||||
|
const reply = `这是模拟的 Eagent 回复内容。后续会替换为真实模型流式输出。`;
|
||||||
|
await streamText('Eagent:', reply, 18);
|
||||||
|
state.tokenUsage += userInput.length + reply.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
function startSpinner(initialLabel) {
|
||||||
|
let i = 0;
|
||||||
|
let label = initialLabel;
|
||||||
|
const timer = setInterval(() => {
|
||||||
|
const frame = SPINNER_FRAMES[i % SPINNER_FRAMES.length];
|
||||||
|
process.stdout.write(`\r${SPINNER_INDENT}${frame}${label}`);
|
||||||
|
i += 1;
|
||||||
|
}, SPINNER_INTERVAL_MS);
|
||||||
|
return {
|
||||||
|
timer,
|
||||||
|
setLabel(nextLabel) {
|
||||||
|
label = nextLabel || '';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function stopSpinner(spinner, finalText) {
|
||||||
|
clearInterval(spinner.timer);
|
||||||
|
const clear = '\r' + ' '.repeat(80) + '\r';
|
||||||
|
process.stdout.write(clear + SPINNER_INDENT + finalText + '\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
async function streamText(prefix, text, delayMs) {
|
||||||
|
if (prefix) process.stdout.write(prefix);
|
||||||
|
for (const ch of text) {
|
||||||
|
process.stdout.write(ch);
|
||||||
|
await sleep(delayMs);
|
||||||
|
}
|
||||||
|
process.stdout.write('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleCommand(input) {
|
||||||
|
const [cmd, ...rest] = input.split(/\s+/);
|
||||||
|
const arg = rest.join(' ').trim();
|
||||||
|
|
||||||
|
if (cmd === '/exit') {
|
||||||
|
rl.close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cmd === '/help') {
|
||||||
|
printHelp();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (cmd) {
|
||||||
|
case '/new':
|
||||||
|
state.conversationId = crypto.randomUUID();
|
||||||
|
state.tokenUsage = 0;
|
||||||
|
console.log(`已创建新对话: ${state.conversationId}`);
|
||||||
|
return;
|
||||||
|
case '/resume':
|
||||||
|
if (arg) {
|
||||||
|
state.conversationId = arg;
|
||||||
|
console.log(`已加载对话: ${state.conversationId}`);
|
||||||
|
} else {
|
||||||
|
console.log('Updated Conversation');
|
||||||
|
console.log('2分钟前 帮我看看...');
|
||||||
|
console.log('14小时前 查看所有...');
|
||||||
|
console.log('13天前 ...');
|
||||||
|
console.log('提示:使用 /resume <id> 直接加载。');
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
case '/allow':
|
||||||
|
state.allow = state.allow === 'full_access' ? 'read_only' : 'full_access';
|
||||||
|
console.log(`运行模式已切换为: ${state.allow}`);
|
||||||
|
return;
|
||||||
|
case '/model': {
|
||||||
|
if (!arg) {
|
||||||
|
state.thinking = state.thinking === 'thinking' ? 'fast' : 'thinking';
|
||||||
|
console.log(`思考模式已切换为: ${state.thinking}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const parts = arg.split(/\s+/).filter(Boolean);
|
||||||
|
state.model = parts[0] || state.model;
|
||||||
|
if (parts[1]) state.thinking = parts[1];
|
||||||
|
console.log(`模型已切换为: ${state.model} | 思考模式: ${state.thinking}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case '/status':
|
||||||
|
console.log(`model: ${state.model} | 思考: ${state.thinking}`);
|
||||||
|
console.log(`workspace: ${WORKSPACE}`);
|
||||||
|
console.log(`allow: ${state.allow}`);
|
||||||
|
console.log(`conversation: ${state.conversationId}`);
|
||||||
|
console.log(`token usage: ${state.tokenUsage}`);
|
||||||
|
return;
|
||||||
|
case '/compact': {
|
||||||
|
const before = state.tokenUsage;
|
||||||
|
const after = Math.max(0, Math.floor(before * 0.6));
|
||||||
|
state.tokenUsage = after;
|
||||||
|
console.log(`压缩完成:${before} -> ${after}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case '/config':
|
||||||
|
console.log(`base_url: ${state.baseUrl}`);
|
||||||
|
console.log(`modelname: ${state.model}`);
|
||||||
|
console.log(`apikey: ${state.apiKey}`);
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
console.log(`未知指令: ${cmd},使用 /help 查看指令列表。`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function printHelp() {
|
||||||
|
console.log('/new 创建新对话');
|
||||||
|
console.log('/resume 加载旧对话');
|
||||||
|
console.log('/allow 切换运行模式(只读/无限制)');
|
||||||
|
console.log('/model 切换模型和思考模式');
|
||||||
|
console.log('/status 查看当前对话状态');
|
||||||
|
console.log('/compact 压缩当前对话');
|
||||||
|
console.log('/config 查看当前配置');
|
||||||
|
console.log('/exit 退出程序');
|
||||||
|
console.log('/help 显示指令列表');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 打开命令菜单
|
||||||
|
*
|
||||||
|
* 关键修复点:
|
||||||
|
* 1. 在输入 / 后,光标位于提示行末尾
|
||||||
|
* 2. 我们不写换行符,而是直接在下一行开始渲染菜单
|
||||||
|
* 3. 使用 ANSI 序列精确控制光标位置
|
||||||
|
*/
|
||||||
|
function openCommandMenu() {
|
||||||
|
if (menuState.active) return;
|
||||||
|
|
||||||
|
menuState.active = true;
|
||||||
|
menuState.selected = 0;
|
||||||
|
menuState.offset = 0;
|
||||||
|
|
||||||
|
// 清空当前输入行(保留提示符)
|
||||||
|
rl.line = '';
|
||||||
|
rl.cursor = 0;
|
||||||
|
|
||||||
|
// 隐藏光标,暂停 readline
|
||||||
|
rl.pause();
|
||||||
|
process.stdout.write(ANSI.HIDE_CURSOR);
|
||||||
|
|
||||||
|
// 移动到行尾(确保我们在输入行的末尾)
|
||||||
|
const currentLine = PROMPT + '/';
|
||||||
|
process.stdout.write(ANSI.CLEAR_LINE);
|
||||||
|
process.stdout.write('\r' + currentLine);
|
||||||
|
|
||||||
|
// 关键:移到下一行开始渲染菜单
|
||||||
|
process.stdout.write('\n');
|
||||||
|
|
||||||
|
renderCommandMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关闭命令菜单
|
||||||
|
*
|
||||||
|
* 关键修复点:
|
||||||
|
* 1. 向上移动到菜单开始的位置
|
||||||
|
* 2. 清除所有菜单行
|
||||||
|
* 3. 将光标移回输入行(在 / 后面)
|
||||||
|
* 4. 恢复 readline 状态
|
||||||
|
*/
|
||||||
|
async function closeCommandMenu() {
|
||||||
|
if (!menuState.active) return;
|
||||||
|
|
||||||
|
// 清除菜单(从当前位置向上清除)
|
||||||
|
clearCommandMenu();
|
||||||
|
|
||||||
|
// 向上移动到输入行(菜单有 COMMAND_MENU_SIZE 行)
|
||||||
|
process.stdout.write(ANSI.MOVE_UP(COMMAND_MENU_SIZE));
|
||||||
|
|
||||||
|
// 清除输入行并重新显示提示符
|
||||||
|
process.stdout.write('\r' + ANSI.CLEAR_LINE);
|
||||||
|
process.stdout.write(PROMPT);
|
||||||
|
|
||||||
|
// 显示光标
|
||||||
|
process.stdout.write(ANSI.SHOW_CURSOR);
|
||||||
|
|
||||||
|
menuState.active = false;
|
||||||
|
rl.resume();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 渲染命令菜单
|
||||||
|
*
|
||||||
|
* 关键修复点:
|
||||||
|
* 1. 菜单固定显示 6 行
|
||||||
|
* 2. 每次重新渲染时,先回到菜单起始位置
|
||||||
|
* 3. 使用 ANSI 序列清除并重写每一行
|
||||||
|
* 4. 保持光标位置稳定
|
||||||
|
*/
|
||||||
|
function renderCommandMenu() {
|
||||||
|
const total = COMMAND_MENU_ITEMS.length;
|
||||||
|
|
||||||
|
// 计算滚动偏移
|
||||||
|
if (menuState.selected < menuState.offset) {
|
||||||
|
menuState.offset = menuState.selected;
|
||||||
|
}
|
||||||
|
if (menuState.selected >= menuState.offset + COMMAND_MENU_SIZE) {
|
||||||
|
menuState.offset = menuState.selected - COMMAND_MENU_SIZE + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存当前光标位置(用于后续恢复)
|
||||||
|
// 实际上我们不需要保存,因为我们控制整个渲染过程
|
||||||
|
|
||||||
|
// 移到菜单开始位置(向上移动菜单行数)
|
||||||
|
// 注意:此时光标在最后一行的末尾
|
||||||
|
process.stdout.write(ANSI.MOVE_UP(COMMAND_MENU_SIZE));
|
||||||
|
|
||||||
|
// 渲染每一行
|
||||||
|
const end = Math.min(total, menuState.offset + COMMAND_MENU_SIZE);
|
||||||
|
let lineIndex = 0;
|
||||||
|
|
||||||
|
for (let i = menuState.offset; i < menuState.offset + COMMAND_MENU_SIZE; i++) {
|
||||||
|
// 移到行首并清除该行
|
||||||
|
process.stdout.write('\r' + ANSI.CLEAR_LINE);
|
||||||
|
|
||||||
|
if (i < end) {
|
||||||
|
const item = COMMAND_MENU_ITEMS[i];
|
||||||
|
const isSelected = i === menuState.selected;
|
||||||
|
const cmdText = item.cmd.padEnd(12, ' ');
|
||||||
|
const left = isSelected ? `${BLUE}▸ ${cmdText}${RESET}` : ` ${cmdText}`;
|
||||||
|
const right = `${GREY}${item.desc}${RESET}`;
|
||||||
|
process.stdout.write(`${left} ${right}`);
|
||||||
|
} else {
|
||||||
|
// 空白行(当项目少于 COMMAND_MENU_SIZE 时)
|
||||||
|
process.stdout.write('');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果不是最后一行,移到下一行
|
||||||
|
lineIndex++;
|
||||||
|
if (lineIndex < COMMAND_MENU_SIZE) {
|
||||||
|
process.stdout.write('\n');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将光标移回菜单第一行(这样下次渲染时从正确的位置开始)
|
||||||
|
process.stdout.write(ANSI.MOVE_UP(COMMAND_MENU_SIZE - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清除命令菜单
|
||||||
|
*
|
||||||
|
* 从当前位置向上清除所有菜单行
|
||||||
|
*/
|
||||||
|
function clearCommandMenu() {
|
||||||
|
// 移到菜单底部(向下移动菜单行数-1,因为我们当前在第一行)
|
||||||
|
process.stdout.write(ANSI.MOVE_DOWN(COMMAND_MENU_SIZE - 1));
|
||||||
|
|
||||||
|
// 从底部开始向上清除每一行
|
||||||
|
for (let i = 0; i < COMMAND_MENU_SIZE; i++) {
|
||||||
|
process.stdout.write('\r' + ANSI.CLEAR_LINE);
|
||||||
|
if (i < COMMAND_MENU_SIZE - 1) {
|
||||||
|
process.stdout.write(ANSI.MOVE_UP(1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
30
demo/lib/assistant_sim.js
Normal file
30
demo/lib/assistant_sim.js
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const { startSpinner, stopSpinner } = require('./ui/spinner');
|
||||||
|
const { truncateText, sleep, streamText } = require('./ui/stream');
|
||||||
|
const { GREY, RESET } = require('./env');
|
||||||
|
|
||||||
|
function createAssistantSimulator(state, options) {
|
||||||
|
const showReasoning = options.showReasoning;
|
||||||
|
return async function simulateAssistantReply(userInput) {
|
||||||
|
const spinner = startSpinner('');
|
||||||
|
|
||||||
|
await sleep(600);
|
||||||
|
spinner.setLabel(' 思考中...');
|
||||||
|
await sleep(1200);
|
||||||
|
|
||||||
|
stopSpinner(spinner, showReasoning ? '○ 思考完成' : '○');
|
||||||
|
|
||||||
|
if (showReasoning) {
|
||||||
|
const reasoning = `好的,用户让我根据输入「${userInput}」做出回应,并给出明确的下一步。`;
|
||||||
|
const snippet = truncateText(reasoning, 50);
|
||||||
|
await streamText('', `${GREY}${snippet}${RESET}`, 12);
|
||||||
|
}
|
||||||
|
|
||||||
|
const reply = '这是模拟的 Eagent 回复内容。后续会替换为真实模型流式输出。';
|
||||||
|
await streamText('Eagent:', reply, 18);
|
||||||
|
state.tokenUsage += userInput.length + reply.length;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { createAssistantSimulator };
|
||||||
92
demo/lib/commands.js
Normal file
92
demo/lib/commands.js
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const { newConversation } = require('./state');
|
||||||
|
|
||||||
|
async function handleCommand(input, state, context) {
|
||||||
|
const { rl, workspacePath } = context;
|
||||||
|
const [cmd, ...rest] = input.split(/\s+/);
|
||||||
|
const arg = rest.join(' ').trim();
|
||||||
|
|
||||||
|
if (cmd === '/exit') {
|
||||||
|
if (context && typeof context.markClosing === 'function') {
|
||||||
|
context.markClosing();
|
||||||
|
}
|
||||||
|
rl.close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cmd === '/help') {
|
||||||
|
printHelp();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (cmd) {
|
||||||
|
case '/new':
|
||||||
|
newConversation(state);
|
||||||
|
console.log(`已创建新对话: ${state.conversationId}`);
|
||||||
|
return;
|
||||||
|
case '/resume':
|
||||||
|
if (arg) {
|
||||||
|
state.conversationId = arg;
|
||||||
|
console.log(`已加载对话: ${state.conversationId}`);
|
||||||
|
} else {
|
||||||
|
console.log('Updated Conversation');
|
||||||
|
console.log('2分钟前 帮我看看...');
|
||||||
|
console.log('14小时前 查看所有...');
|
||||||
|
console.log('13天前 ...');
|
||||||
|
console.log('提示:使用 /resume <id> 直接加载。');
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
case '/allow':
|
||||||
|
state.allow = state.allow === 'full_access' ? 'read_only' : 'full_access';
|
||||||
|
console.log(`运行模式已切换为: ${state.allow}`);
|
||||||
|
return;
|
||||||
|
case '/model': {
|
||||||
|
if (!arg) {
|
||||||
|
state.thinking = state.thinking === 'thinking' ? 'fast' : 'thinking';
|
||||||
|
console.log(`思考模式已切换为: ${state.thinking}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const parts = arg.split(/\s+/).filter(Boolean);
|
||||||
|
state.model = parts[0] || state.model;
|
||||||
|
if (parts[1]) state.thinking = parts[1];
|
||||||
|
console.log(`模型已切换为: ${state.model} | 思考模式: ${state.thinking}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case '/status':
|
||||||
|
console.log(`model: ${state.model} | 思考: ${state.thinking}`);
|
||||||
|
console.log(`workspace: ${workspacePath}`);
|
||||||
|
console.log(`allow: ${state.allow}`);
|
||||||
|
console.log(`conversation: ${state.conversationId}`);
|
||||||
|
console.log(`token usage: ${state.tokenUsage}`);
|
||||||
|
return;
|
||||||
|
case '/compact': {
|
||||||
|
const before = state.tokenUsage;
|
||||||
|
const after = Math.max(0, Math.floor(before * 0.6));
|
||||||
|
state.tokenUsage = after;
|
||||||
|
console.log(`压缩完成:${before} -> ${after}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case '/config':
|
||||||
|
console.log(`base_url: ${state.baseUrl}`);
|
||||||
|
console.log(`modelname: ${state.model}`);
|
||||||
|
console.log(`apikey: ${state.apiKey}`);
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
console.log(`未知指令: ${cmd},使用 /help 查看指令列表。`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function printHelp() {
|
||||||
|
console.log('/new 创建新对话');
|
||||||
|
console.log('/resume 加载旧对话');
|
||||||
|
console.log('/allow 切换运行模式(只读/无限制)');
|
||||||
|
console.log('/model 切换模型和思考模式');
|
||||||
|
console.log('/status 查看当前对话状态');
|
||||||
|
console.log('/compact 压缩当前对话');
|
||||||
|
console.log('/config 查看当前配置');
|
||||||
|
console.log('/exit 退出程序');
|
||||||
|
console.log('/help 显示指令列表');
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { handleCommand, printHelp };
|
||||||
23
demo/lib/env.js
Normal file
23
demo/lib/env.js
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const os = require('os');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
const WORKSPACE = process.cwd();
|
||||||
|
const WORKSPACE_NAME = path.basename(WORKSPACE) || WORKSPACE;
|
||||||
|
const USERNAME = os.userInfo().username || 'user';
|
||||||
|
const PROMPT = `${USERNAME}@${WORKSPACE_NAME} `;
|
||||||
|
|
||||||
|
const COLOR_ENABLED = !process.env.NO_COLOR;
|
||||||
|
const GREY = COLOR_ENABLED ? '\x1b[90m' : '';
|
||||||
|
const RESET = COLOR_ENABLED ? '\x1b[0m' : '';
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
WORKSPACE,
|
||||||
|
WORKSPACE_NAME,
|
||||||
|
USERNAME,
|
||||||
|
PROMPT,
|
||||||
|
COLOR_ENABLED,
|
||||||
|
GREY,
|
||||||
|
RESET,
|
||||||
|
};
|
||||||
22
demo/lib/state.js
Normal file
22
demo/lib/state.js
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const crypto = require('crypto');
|
||||||
|
|
||||||
|
function createState() {
|
||||||
|
return {
|
||||||
|
conversationId: crypto.randomUUID(),
|
||||||
|
allow: 'full_access',
|
||||||
|
model: 'kimi',
|
||||||
|
thinking: 'thinking',
|
||||||
|
tokenUsage: 0,
|
||||||
|
baseUrl: 'https://api.example.com/v1',
|
||||||
|
apiKey: 'sk-xxxxxxw2y',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function newConversation(state) {
|
||||||
|
state.conversationId = crypto.randomUUID();
|
||||||
|
state.tokenUsage = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { createState, newConversation };
|
||||||
15
demo/lib/system_prompt.js
Normal file
15
demo/lib/system_prompt.js
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
function loadSystemPrompt() {
|
||||||
|
const p = path.join(__dirname, '..', '..', 'prompts', 'system.txt');
|
||||||
|
try {
|
||||||
|
return fs.readFileSync(p, 'utf8');
|
||||||
|
} catch (_) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { loadSystemPrompt };
|
||||||
83
demo/lib/ui/command_menu.js
Normal file
83
demo/lib/ui/command_menu.js
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const readline = require('readline');
|
||||||
|
|
||||||
|
const COMMAND_CHOICES = [
|
||||||
|
{ value: '/new', desc: '创建新对话' },
|
||||||
|
{ value: '/resume', desc: '加载旧对话' },
|
||||||
|
{ value: '/allow', desc: '切换运行模式(只读/无限制)' },
|
||||||
|
{ value: '/model', desc: '切换模型和思考模式' },
|
||||||
|
{ value: '/status', desc: '查看当前对话状态' },
|
||||||
|
{ value: '/compact', desc: '压缩当前对话' },
|
||||||
|
{ value: '/config', desc: '查看当前配置' },
|
||||||
|
{ value: '/help', desc: '显示指令列表' },
|
||||||
|
{ value: '/exit', desc: '退出程序' },
|
||||||
|
];
|
||||||
|
|
||||||
|
async function openCommandMenu(options) {
|
||||||
|
const { rl, prompt, pageSize, colorEnabled, resetAnsi, onInput } = options;
|
||||||
|
let latestInput = '';
|
||||||
|
|
||||||
|
rl.pause();
|
||||||
|
rl.line = '';
|
||||||
|
rl.cursor = 0;
|
||||||
|
readline.clearLine(process.stdout, 0);
|
||||||
|
readline.cursorTo(process.stdout, 0);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { default: search } = await import('@inquirer/search');
|
||||||
|
const chosen = await search({
|
||||||
|
message: prompt.trimEnd(),
|
||||||
|
pageSize,
|
||||||
|
theme: {
|
||||||
|
helpMode: 'never',
|
||||||
|
prefix: '',
|
||||||
|
icon: { cursor: '>' },
|
||||||
|
style: {
|
||||||
|
message: (text) => text,
|
||||||
|
searchTerm: (text) => `/${(text || '').replace(/^\/+/, '')}`,
|
||||||
|
highlight: (text) => (colorEnabled ? `\x1b[94m${text}${resetAnsi}` : text),
|
||||||
|
keysHelpTip: () => '',
|
||||||
|
description: (text) => text,
|
||||||
|
answer: () => '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
source: async (input) => {
|
||||||
|
latestInput = input || '';
|
||||||
|
if (typeof onInput === 'function') onInput(latestInput);
|
||||||
|
const term = latestInput.replace(/^\/+/, '').toLowerCase();
|
||||||
|
const maxCmdLen = Math.max(...COMMAND_CHOICES.map((c) => c.value.length));
|
||||||
|
const indentLen = Math.max(0, prompt.length - 2);
|
||||||
|
const indent = ' '.repeat(indentLen);
|
||||||
|
const format = (c) => {
|
||||||
|
const pad = ' '.repeat(Math.max(1, maxCmdLen - c.value.length + 2));
|
||||||
|
return `${indent}${c.value}${pad}${c.desc}`;
|
||||||
|
};
|
||||||
|
const filtered = term
|
||||||
|
? COMMAND_CHOICES.filter((c) => {
|
||||||
|
return (
|
||||||
|
c.value.toLowerCase().includes(term) ||
|
||||||
|
c.desc.toLowerCase().includes(term)
|
||||||
|
);
|
||||||
|
})
|
||||||
|
: COMMAND_CHOICES;
|
||||||
|
return filtered.map((c) => ({
|
||||||
|
name: format(c),
|
||||||
|
value: c.value,
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return { chosen, term: latestInput };
|
||||||
|
} catch (err) {
|
||||||
|
console.log('指令菜单不可用,请先安装依赖: npm i @inquirer/search');
|
||||||
|
return { chosen: null, term: '' };
|
||||||
|
} finally {
|
||||||
|
if (process.stdin.isTTY) process.stdin.setRawMode(true);
|
||||||
|
process.stdin.resume();
|
||||||
|
try {
|
||||||
|
rl.resume();
|
||||||
|
} catch (_) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { openCommandMenu };
|
||||||
29
demo/lib/ui/spinner.js
Normal file
29
demo/lib/ui/spinner.js
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const SPINNER_FRAMES = ['⠙','⠹','⠸','⠼','⠴','⠦','⠧','⠇','⠏'];
|
||||||
|
const SPINNER_INTERVAL_MS = 80;
|
||||||
|
const SPINNER_INDENT = ' ';
|
||||||
|
|
||||||
|
function startSpinner(initialLabel) {
|
||||||
|
let i = 0;
|
||||||
|
let label = initialLabel;
|
||||||
|
const timer = setInterval(() => {
|
||||||
|
const frame = SPINNER_FRAMES[i % SPINNER_FRAMES.length];
|
||||||
|
process.stdout.write(`\r${SPINNER_INDENT}${frame}${label}`);
|
||||||
|
i += 1;
|
||||||
|
}, SPINNER_INTERVAL_MS);
|
||||||
|
return {
|
||||||
|
timer,
|
||||||
|
setLabel(nextLabel) {
|
||||||
|
label = nextLabel || '';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function stopSpinner(spinner, finalText) {
|
||||||
|
clearInterval(spinner.timer);
|
||||||
|
const clear = '\r' + ' '.repeat(80) + '\r';
|
||||||
|
process.stdout.write(clear + SPINNER_INDENT + finalText + '\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { startSpinner, stopSpinner };
|
||||||
21
demo/lib/ui/stream.js
Normal file
21
demo/lib/ui/stream.js
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
function truncateText(text, maxLen) {
|
||||||
|
if (text.length <= maxLen) return text;
|
||||||
|
return text.slice(0, maxLen) + '...';
|
||||||
|
}
|
||||||
|
|
||||||
|
function sleep(ms) {
|
||||||
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function streamText(prefix, text, delayMs) {
|
||||||
|
if (prefix) process.stdout.write(prefix);
|
||||||
|
for (const ch of text) {
|
||||||
|
process.stdout.write(ch);
|
||||||
|
await sleep(delayMs);
|
||||||
|
}
|
||||||
|
process.stdout.write('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { truncateText, sleep, streamText };
|
||||||
70
doc/commands.md
Normal file
70
doc/commands.md
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
# 交互指令(CLI)
|
||||||
|
|
||||||
|
以下指令均以 `/` 开头。默认进入对话输入模式,输入指令用于控制程序状态。
|
||||||
|
|
||||||
|
## 指令清单
|
||||||
|
|
||||||
|
- `/help`:显示所有指令与简要说明。
|
||||||
|
- `/new`:创建新对话(生成新的对话 ID)。
|
||||||
|
- `/resume`:加载旧对话(仅当前工作区路径内的历史对话)。
|
||||||
|
- `/allow`:切换运行模式(`read_only` / `full_access`)。
|
||||||
|
- `/model`:切换模型与思考模式。
|
||||||
|
- `/status`:查看当前对话状态。
|
||||||
|
- `/compact`:触发对话压缩。
|
||||||
|
- `/config`:查看当前配置。
|
||||||
|
- `/exit`:退出程序。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## /resume 交互细节
|
||||||
|
|
||||||
|
`/resume` 有两种用法:
|
||||||
|
|
||||||
|
1) **交互列表模式**:
|
||||||
|
- 执行 `/resume` 后显示列表(最新在上)。
|
||||||
|
- 上下方向键移动选择光标 `>`。
|
||||||
|
- 兼容性兜底:支持**数字输入**直接选择第 N 条。
|
||||||
|
- 列表项格式示例:
|
||||||
|
```
|
||||||
|
Updated Conversation
|
||||||
|
2分钟前 帮我看看...(第一条用户消息前50字)
|
||||||
|
> 14小时前 查看所有...
|
||||||
|
13天前 ...
|
||||||
|
```
|
||||||
|
|
||||||
|
2) **直接加载模式**:
|
||||||
|
- 直接输入 `/resume <conversation_id>` 加载指定对话。
|
||||||
|
- 例如:
|
||||||
|
```
|
||||||
|
/resume 019c99ad-3177-7970-bcf5-0c5893e177a2
|
||||||
|
```
|
||||||
|
|
||||||
|
**作用域限制**:`/resume` 只加载**当前工作区路径**下的历史对话。不同路径的工作区对话相互隔离。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## /status 输出字段
|
||||||
|
|
||||||
|
- 当前模型 + 思考模式
|
||||||
|
- 工作区 path
|
||||||
|
- allow 状态(只读/无限制)
|
||||||
|
- 对话 ID
|
||||||
|
- Token usage(当前上下文长度,来自 API 返回并缓存)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## /config 输出字段
|
||||||
|
|
||||||
|
- base_url
|
||||||
|
- modelname
|
||||||
|
- apikey(脱敏,仅显示前缀+末尾,例如:`sk-xxxxxxw2y`)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## /compact 行为
|
||||||
|
|
||||||
|
- 对当前对话进行压缩。
|
||||||
|
- 输出示例:
|
||||||
|
```
|
||||||
|
压缩完成:<旧token> -> <新token>
|
||||||
|
```
|
||||||
177
doc/tool_results.md
Normal file
177
doc/tool_results.md
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
# 工具返回格式(高信息密度文本,非 JSON)
|
||||||
|
|
||||||
|
> 说明:工具真实执行会产生完整 JSON 结果,仅用于存储/审计;**返回给模型的内容必须是高信息密度文本**。参考项目中的“JSON -> 文本摘要”逻辑,但按 easyagent 需求进一步压缩。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## read_file
|
||||||
|
|
||||||
|
### type=read
|
||||||
|
**仅返回 content 原文**(不加标题、不加行号、不加路径)。
|
||||||
|
|
||||||
|
返回示例:
|
||||||
|
```
|
||||||
|
<文件内容原文>
|
||||||
|
```
|
||||||
|
|
||||||
|
### type=search
|
||||||
|
返回“命中片段拼接文本”,每个片段用极简头标识(保留行号对定位有价值),不返回其他噪声。
|
||||||
|
|
||||||
|
返回示例:
|
||||||
|
```
|
||||||
|
[match_1] L10-14 hits:12
|
||||||
|
<片段内容>
|
||||||
|
|
||||||
|
[match_2] L88-92 hits:90,91
|
||||||
|
<片段内容>
|
||||||
|
```
|
||||||
|
|
||||||
|
### type=extract
|
||||||
|
返回“抽取片段拼接文本”,每个片段带 label/行号(若有)。
|
||||||
|
|
||||||
|
返回示例:
|
||||||
|
```
|
||||||
|
[segment_init] L10-20
|
||||||
|
<片段内容>
|
||||||
|
|
||||||
|
[segment_2] L40-45
|
||||||
|
<片段内容>
|
||||||
|
```
|
||||||
|
|
||||||
|
### error
|
||||||
|
```
|
||||||
|
失败: <错误信息>
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## edit_file
|
||||||
|
|
||||||
|
成功:
|
||||||
|
```
|
||||||
|
已替换 <N> 处: <path>
|
||||||
|
```
|
||||||
|
|
||||||
|
失败:
|
||||||
|
```
|
||||||
|
失败: <错误信息>
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## web_search
|
||||||
|
|
||||||
|
仅返回搜索摘要文本(来自 search_with_summary 的 summary),不返回 JSON 结果。
|
||||||
|
|
||||||
|
返回示例:
|
||||||
|
```
|
||||||
|
🔍 搜索查询: xxx
|
||||||
|
📅 搜索时间: 2025-01-01T12:00:00
|
||||||
|
|
||||||
|
📝 AI摘要:
|
||||||
|
...
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
📊 搜索结果:
|
||||||
|
1. 标题...
|
||||||
|
🔗 URL
|
||||||
|
📄 摘要...
|
||||||
|
```
|
||||||
|
|
||||||
|
失败:
|
||||||
|
```
|
||||||
|
失败: <错误信息>
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## extract_webpage
|
||||||
|
|
||||||
|
### mode=read
|
||||||
|
返回网页正文为主,保留最小必要标识(URL)。
|
||||||
|
|
||||||
|
返回示例:
|
||||||
|
```
|
||||||
|
URL: https://example.com/...
|
||||||
|
<网页正文>
|
||||||
|
```
|
||||||
|
|
||||||
|
### mode=save
|
||||||
|
返回保存结果的简短确认信息。
|
||||||
|
|
||||||
|
成功:
|
||||||
|
```
|
||||||
|
已保存: <path> (chars=<N>, bytes=<N>)
|
||||||
|
```
|
||||||
|
|
||||||
|
失败:
|
||||||
|
```
|
||||||
|
失败: <错误信息>
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## run_command
|
||||||
|
|
||||||
|
仅返回命令输出;如超时/错误则在前缀标注状态。
|
||||||
|
|
||||||
|
成功:
|
||||||
|
```
|
||||||
|
<stdout/stderr 合并输出>
|
||||||
|
```
|
||||||
|
|
||||||
|
超时:
|
||||||
|
```
|
||||||
|
[timeout after <Ns>]
|
||||||
|
<已捕获输出>
|
||||||
|
```
|
||||||
|
|
||||||
|
失败:
|
||||||
|
```
|
||||||
|
[error rc=<code>] <错误信息或输出>
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## read_mediafile
|
||||||
|
|
||||||
|
成功(文字提示 + 附加媒体 payload):
|
||||||
|
```
|
||||||
|
已附加图片: <path>
|
||||||
|
```
|
||||||
|
或
|
||||||
|
```
|
||||||
|
已附加视频: <path>
|
||||||
|
```
|
||||||
|
|
||||||
|
失败:
|
||||||
|
```
|
||||||
|
失败: <错误信息>
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## search_workspace
|
||||||
|
|
||||||
|
### mode=file
|
||||||
|
```
|
||||||
|
命中文件(<N>):
|
||||||
|
1) <path>
|
||||||
|
2) <path>
|
||||||
|
```
|
||||||
|
|
||||||
|
### mode=content
|
||||||
|
```
|
||||||
|
<path>
|
||||||
|
- L12: <snippet>
|
||||||
|
- L30: <snippet>
|
||||||
|
|
||||||
|
<path>
|
||||||
|
- L5: <snippet>
|
||||||
|
```
|
||||||
|
|
||||||
|
失败:
|
||||||
|
```
|
||||||
|
失败: <错误信息>
|
||||||
|
```
|
||||||
275
doc/tools.json
Normal file
275
doc/tools.json
Normal file
@ -0,0 +1,275 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"type": "function",
|
||||||
|
"function": {
|
||||||
|
"name": "read_file",
|
||||||
|
"description": "读取/搜索/抽取 UTF-8 文本文件。type=read/search/extract,仅单文件操作。",
|
||||||
|
"parameters": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"path": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "文件路径:工作区内请用相对路径,工作区外可用绝对路径。"
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["read", "search", "extract"],
|
||||||
|
"description": "读取模式:read=按行读取;search=在文件内搜索;extract=按行段抽取。"
|
||||||
|
},
|
||||||
|
"max_chars": {
|
||||||
|
"type": "integer",
|
||||||
|
"description": "返回内容最大字符数,超出将截断。"
|
||||||
|
},
|
||||||
|
"start_line": {
|
||||||
|
"type": "integer",
|
||||||
|
"description": "[read] 起始行号(1-based)。"
|
||||||
|
},
|
||||||
|
"end_line": {
|
||||||
|
"type": "integer",
|
||||||
|
"description": "[read] 结束行号(>= start_line)。"
|
||||||
|
},
|
||||||
|
"query": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "[search] 搜索关键词。"
|
||||||
|
},
|
||||||
|
"max_matches": {
|
||||||
|
"type": "integer",
|
||||||
|
"description": "[search] 最多返回多少条命中窗口。"
|
||||||
|
},
|
||||||
|
"context_before": {
|
||||||
|
"type": "integer",
|
||||||
|
"description": "[search] 命中行向上追加行数。"
|
||||||
|
},
|
||||||
|
"context_after": {
|
||||||
|
"type": "integer",
|
||||||
|
"description": "[search] 命中行向下追加行数。"
|
||||||
|
},
|
||||||
|
"case_sensitive": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "[search] 是否区分大小写。"
|
||||||
|
},
|
||||||
|
"segments": {
|
||||||
|
"type": "array",
|
||||||
|
"description": "[extract] 需要抽取的行区间数组。",
|
||||||
|
"items": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"label": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "片段标签(可选)。"
|
||||||
|
},
|
||||||
|
"start_line": {
|
||||||
|
"type": "integer",
|
||||||
|
"description": "起始行号(>=1)。"
|
||||||
|
},
|
||||||
|
"end_line": {
|
||||||
|
"type": "integer",
|
||||||
|
"description": "结束行号(>= start_line)。"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["start_line", "end_line"]
|
||||||
|
},
|
||||||
|
"minItems": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["path", "type"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "function",
|
||||||
|
"function": {
|
||||||
|
"name": "edit_file",
|
||||||
|
"description": "在文件中执行精确字符串替换;建议先用 read_file 精确复制 old_string。",
|
||||||
|
"parameters": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"file_path": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "文件路径:工作区内请用相对路径,工作区外可用绝对路径。"
|
||||||
|
},
|
||||||
|
"old_string": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "要替换的旧文本(必须与文件内容精确匹配)。"
|
||||||
|
},
|
||||||
|
"new_string": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "替换的新文本(必须不同于 old_string)。"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["file_path", "old_string", "new_string"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "function",
|
||||||
|
"function": {
|
||||||
|
"name": "web_search",
|
||||||
|
"description": "网络搜索(Tavily)。用于外部资料或最新信息检索。",
|
||||||
|
"parameters": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"query": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "搜索关键词(不要包含时间范围)。"
|
||||||
|
},
|
||||||
|
"max_results": {
|
||||||
|
"type": "integer",
|
||||||
|
"description": "最大返回结果数。"
|
||||||
|
},
|
||||||
|
"topic": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["general", "news", "finance"],
|
||||||
|
"description": "搜索主题类型。"
|
||||||
|
},
|
||||||
|
"time_range": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["day", "week", "month", "year", "d", "w", "m", "y"],
|
||||||
|
"description": "相对时间范围(与 days / start_date+end_date 互斥)。"
|
||||||
|
},
|
||||||
|
"days": {
|
||||||
|
"type": "integer",
|
||||||
|
"description": "最近 N 天(仅 topic=news 可用)。"
|
||||||
|
},
|
||||||
|
"start_date": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "开始日期 YYYY-MM-DD(必须与 end_date 同时提供)。"
|
||||||
|
},
|
||||||
|
"end_date": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "结束日期 YYYY-MM-DD(必须与 start_date 同时提供)。"
|
||||||
|
},
|
||||||
|
"country": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "国家过滤,仅 topic=general 可用(英文小写国名)。"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["query"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "function",
|
||||||
|
"function": {
|
||||||
|
"name": "extract_webpage",
|
||||||
|
"description": "网页内容提取(Tavily)。mode=read 直接返回内容,mode=save 保存为文件。",
|
||||||
|
"parameters": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"mode": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["read", "save"],
|
||||||
|
"description": "read=直接返回内容;save=写入文件。"
|
||||||
|
},
|
||||||
|
"url": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "目标网页 URL。"
|
||||||
|
},
|
||||||
|
"target_path": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "[save] 保存路径:工作区内请用相对路径,工作区外可用绝对路径。必须是 .md 文件。mode=save 必填。"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["mode", "url"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "function",
|
||||||
|
"function": {
|
||||||
|
"name": "run_command",
|
||||||
|
"description": "在当前终端环境执行一次性命令(非交互式)。",
|
||||||
|
"parameters": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"command": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "要执行的命令。"
|
||||||
|
},
|
||||||
|
"timeout": {
|
||||||
|
"type": "number",
|
||||||
|
"description": "超时时间(秒),必填。"
|
||||||
|
},
|
||||||
|
"working_dir": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "工作目录:工作区内请用相对路径,工作区外可用绝对路径。"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["command", "timeout"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "function",
|
||||||
|
"function": {
|
||||||
|
"name": "search_workspace",
|
||||||
|
"description": "在本地目录内搜索文件名或文件内容(跨文件)。仅返回摘要。",
|
||||||
|
"parameters": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"mode": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["file", "content"],
|
||||||
|
"description": "file=搜索文件名;content=跨文件搜索内容。"
|
||||||
|
},
|
||||||
|
"query": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "搜索关键词或正则。"
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "搜索起点目录:工作区内请用相对路径,工作区外可用绝对路径。默认 '.'。"
|
||||||
|
},
|
||||||
|
"use_regex": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "是否使用正则匹配(默认 false)。"
|
||||||
|
},
|
||||||
|
"case_sensitive": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "是否区分大小写(默认 false)。"
|
||||||
|
},
|
||||||
|
"max_results": {
|
||||||
|
"type": "integer",
|
||||||
|
"description": "最大返回结果数(默认 20)。"
|
||||||
|
},
|
||||||
|
"max_matches_per_file": {
|
||||||
|
"type": "integer",
|
||||||
|
"description": "[content] 每个文件最多返回多少处匹配(默认 3)。"
|
||||||
|
},
|
||||||
|
"include_glob": {
|
||||||
|
"type": "array",
|
||||||
|
"description": "仅包含匹配这些 glob 的文件。",
|
||||||
|
"items": { "type": "string" }
|
||||||
|
},
|
||||||
|
"exclude_glob": {
|
||||||
|
"type": "array",
|
||||||
|
"description": "排除匹配这些 glob 的文件。",
|
||||||
|
"items": { "type": "string" }
|
||||||
|
},
|
||||||
|
"max_file_size": {
|
||||||
|
"type": "integer",
|
||||||
|
"description": "跳过超过该字节数的文件。"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["mode", "query"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "function",
|
||||||
|
"function": {
|
||||||
|
"name": "read_mediafile",
|
||||||
|
"description": "读取图片或视频文件并附加给模型。非媒体文件将拒绝。",
|
||||||
|
"parameters": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"path": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "媒体文件路径:工作区内请用相对路径,工作区外可用绝对路径。"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["path"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
423
node_modules/.package-lock.json
generated
vendored
Normal file
423
node_modules/.package-lock.json
generated
vendored
Normal file
@ -0,0 +1,423 @@
|
|||||||
|
{
|
||||||
|
"name": "easyagent",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"node_modules/@inquirer/ansi": {
|
||||||
|
"version": "2.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-2.0.3.tgz",
|
||||||
|
"integrity": "sha512-g44zhR3NIKVs0zUesa4iMzExmZpLUdTLRMCStqX3GE5NT6VkPcxQGJ+uC8tDgBUC/vB1rUhUd55cOf++4NZcmw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@inquirer/core": {
|
||||||
|
"version": "11.1.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@inquirer/core/-/core-11.1.5.tgz",
|
||||||
|
"integrity": "sha512-QQPAX+lka8GyLcZ7u7Nb1h6q72iZ/oy0blilC3IB2nSt1Qqxp7akt94Jqhi/DzARuN3Eo9QwJRvtl4tmVe4T5A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@inquirer/ansi": "^2.0.3",
|
||||||
|
"@inquirer/figures": "^2.0.3",
|
||||||
|
"@inquirer/type": "^4.0.3",
|
||||||
|
"cli-width": "^4.1.0",
|
||||||
|
"fast-wrap-ansi": "^0.2.0",
|
||||||
|
"mute-stream": "^3.0.0",
|
||||||
|
"signal-exit": "^4.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/node": ">=18"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/node": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@inquirer/figures": {
|
||||||
|
"version": "2.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-2.0.3.tgz",
|
||||||
|
"integrity": "sha512-y09iGt3JKoOCBQ3w4YrSJdokcD8ciSlMIWsD+auPu+OZpfxLuyz+gICAQ6GCBOmJJt4KEQGHuZSVff2jiNOy7g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@inquirer/search": {
|
||||||
|
"version": "4.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@inquirer/search/-/search-4.1.4.tgz",
|
||||||
|
"integrity": "sha512-9yPTxq7LPmYjrGn3DRuaPuPbmC6u3fiWcsE9ggfLcdgO/ICHYgxq7mEy1yJ39brVvgXhtOtvDVjDh9slJxE4LQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@inquirer/core": "^11.1.5",
|
||||||
|
"@inquirer/figures": "^2.0.3",
|
||||||
|
"@inquirer/type": "^4.0.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/node": ">=18"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/node": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@inquirer/select": {
|
||||||
|
"version": "5.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@inquirer/select/-/select-5.1.0.tgz",
|
||||||
|
"integrity": "sha512-OyYbKnchS1u+zRe14LpYrN8S0wH1vD0p2yKISvSsJdH2TpI87fh4eZdWnpdbrGauCRWDph3NwxRmM4Pcm/hx1Q==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@inquirer/ansi": "^2.0.3",
|
||||||
|
"@inquirer/core": "^11.1.5",
|
||||||
|
"@inquirer/figures": "^2.0.3",
|
||||||
|
"@inquirer/type": "^4.0.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/node": ">=18"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/node": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@inquirer/type": {
|
||||||
|
"version": "4.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@inquirer/type/-/type-4.0.3.tgz",
|
||||||
|
"integrity": "sha512-cKZN7qcXOpj1h+1eTTcGDVLaBIHNMT1Rz9JqJP5MnEJ0JhgVWllx7H/tahUp5YEK1qaByH2Itb8wLG/iScD5kw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/node": ">=18"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/node": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@nodelib/fs.scandir": {
|
||||||
|
"version": "2.1.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||||
|
"integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@nodelib/fs.stat": "2.0.5",
|
||||||
|
"run-parallel": "^1.1.9"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@nodelib/fs.stat": {
|
||||||
|
"version": "2.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
|
||||||
|
"integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@nodelib/fs.walk": {
|
||||||
|
"version": "1.2.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
|
||||||
|
"integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@nodelib/fs.scandir": "2.1.5",
|
||||||
|
"fastq": "^1.6.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/braces": {
|
||||||
|
"version": "3.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
|
||||||
|
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"fill-range": "^7.1.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/cli-width": {
|
||||||
|
"version": "4.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz",
|
||||||
|
"integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==",
|
||||||
|
"license": "ISC",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/diff": {
|
||||||
|
"version": "5.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/diff/-/diff-5.2.2.tgz",
|
||||||
|
"integrity": "sha512-vtcDfH3TOjP8UekytvnHH1o1P4FcUdt4eQ1Y+Abap1tk/OB2MWQvcwS2ClCd1zuIhc3JKOx6p3kod8Vfys3E+A==",
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.3.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/fast-glob": {
|
||||||
|
"version": "3.3.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
|
||||||
|
"integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@nodelib/fs.stat": "^2.0.2",
|
||||||
|
"@nodelib/fs.walk": "^1.2.3",
|
||||||
|
"glob-parent": "^5.1.2",
|
||||||
|
"merge2": "^1.3.0",
|
||||||
|
"micromatch": "^4.0.8"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8.6.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/fast-string-truncated-width": {
|
||||||
|
"version": "3.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/fast-string-truncated-width/-/fast-string-truncated-width-3.0.3.tgz",
|
||||||
|
"integrity": "sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/fast-string-width": {
|
||||||
|
"version": "3.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/fast-string-width/-/fast-string-width-3.0.2.tgz",
|
||||||
|
"integrity": "sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"fast-string-truncated-width": "^3.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/fast-wrap-ansi": {
|
||||||
|
"version": "0.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/fast-wrap-ansi/-/fast-wrap-ansi-0.2.0.tgz",
|
||||||
|
"integrity": "sha512-rLV8JHxTyhVmFYhBJuMujcrHqOT2cnO5Zxj37qROj23CP39GXubJRBUFF0z8KFK77Uc0SukZUf7JZhsVEQ6n8w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"fast-string-width": "^3.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/fastq": {
|
||||||
|
"version": "1.20.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz",
|
||||||
|
"integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"reusify": "^1.0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/fill-range": {
|
||||||
|
"version": "7.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
|
||||||
|
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"to-regex-range": "^5.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/glob-parent": {
|
||||||
|
"version": "5.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
|
||||||
|
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"is-glob": "^4.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/is-extglob": {
|
||||||
|
"version": "2.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
|
||||||
|
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/is-glob": {
|
||||||
|
"version": "4.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
|
||||||
|
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"is-extglob": "^2.1.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/is-number": {
|
||||||
|
"version": "7.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
|
||||||
|
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.12.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/merge2": {
|
||||||
|
"version": "1.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
|
||||||
|
"integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/micromatch": {
|
||||||
|
"version": "4.0.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
|
||||||
|
"integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"braces": "^3.0.3",
|
||||||
|
"picomatch": "^2.3.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/mime-db": {
|
||||||
|
"version": "1.52.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||||
|
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/mime-types": {
|
||||||
|
"version": "2.1.35",
|
||||||
|
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
||||||
|
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"mime-db": "1.52.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/mute-stream": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-dkEJPVvun4FryqBmZ5KhDo0K9iDXAwn08tMLDinNdRBNPcYEDiWYysLcc6k3mjTMlbP9KyylvRpd4wFtwrT9rw==",
|
||||||
|
"license": "ISC",
|
||||||
|
"engines": {
|
||||||
|
"node": "^20.17.0 || >=22.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/picomatch": {
|
||||||
|
"version": "2.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
|
||||||
|
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8.6"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/jonschlinkert"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/queue-microtask": {
|
||||||
|
"version": "1.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
|
||||||
|
"integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "patreon",
|
||||||
|
"url": "https://www.patreon.com/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "consulting",
|
||||||
|
"url": "https://feross.org/support"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/reusify": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"iojs": ">=1.0.0",
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/run-parallel": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "patreon",
|
||||||
|
"url": "https://www.patreon.com/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "consulting",
|
||||||
|
"url": "https://feross.org/support"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"queue-microtask": "^1.2.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/signal-exit": {
|
||||||
|
"version": "4.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
|
||||||
|
"integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
|
||||||
|
"license": "ISC",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/to-regex-range": {
|
||||||
|
"version": "5.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||||
|
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"is-number": "^7.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
22
node_modules/@inquirer/ansi/LICENSE
generated
vendored
Normal file
22
node_modules/@inquirer/ansi/LICENSE
generated
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
Copyright (c) 2025 Simon Boudrias
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person
|
||||||
|
obtaining a copy of this software and associated documentation
|
||||||
|
files (the "Software"), to deal in the Software without
|
||||||
|
restriction, including without limitation the rights to use,
|
||||||
|
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the
|
||||||
|
Software is furnished to do so, subject to the following
|
||||||
|
conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||||
|
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||||
|
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||||
|
OTHER DEALINGS IN THE SOFTWARE.
|
||||||
89
node_modules/@inquirer/ansi/README.md
generated
vendored
Normal file
89
node_modules/@inquirer/ansi/README.md
generated
vendored
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
# @inquirer/ansi
|
||||||
|
|
||||||
|
A lightweight package providing ANSI escape sequences for terminal cursor manipulation and screen clearing.
|
||||||
|
|
||||||
|
# Installation
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th>npm</th>
|
||||||
|
<th>yarn</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm install @inquirer/ansi
|
||||||
|
```
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
```sh
|
||||||
|
yarn add @inquirer/ansi
|
||||||
|
```
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```js
|
||||||
|
import {
|
||||||
|
cursorUp,
|
||||||
|
cursorDown,
|
||||||
|
cursorTo,
|
||||||
|
cursorLeft,
|
||||||
|
cursorHide,
|
||||||
|
cursorShow,
|
||||||
|
eraseLines,
|
||||||
|
} from '@inquirer/ansi';
|
||||||
|
|
||||||
|
// Move cursor up 3 lines
|
||||||
|
process.stdout.write(cursorUp(3));
|
||||||
|
|
||||||
|
// Move cursor to specific position (x: 10, y: 5)
|
||||||
|
process.stdout.write(cursorTo(10, 5));
|
||||||
|
|
||||||
|
// Hide/show cursor
|
||||||
|
process.stdout.write(cursorHide);
|
||||||
|
process.stdout.write(cursorShow);
|
||||||
|
|
||||||
|
// Clear 5 lines
|
||||||
|
process.stdout.write(eraseLines(5));
|
||||||
|
```
|
||||||
|
|
||||||
|
Or when used inside an inquirer prompt:
|
||||||
|
|
||||||
|
```js
|
||||||
|
import { cursorHide } from '@inquirer/ansi';
|
||||||
|
import { createPrompt } from '@inquirer/core';
|
||||||
|
|
||||||
|
export default createPrompt((config, done: (value: void) => void) => {
|
||||||
|
return `Choose an option${cursorHide}`;
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
|
### Cursor Movement
|
||||||
|
|
||||||
|
- **`cursorUp(count?: number)`** - Move cursor up by `count` lines (default: 1)
|
||||||
|
- **`cursorDown(count?: number)`** - Move cursor down by `count` lines (default: 1)
|
||||||
|
- **`cursorTo(x: number, y?: number)`** - Move cursor to position (x, y). If y is omitted, only moves horizontally
|
||||||
|
- **`cursorLeft`** - Move cursor to beginning of line
|
||||||
|
|
||||||
|
### Cursor Visibility
|
||||||
|
|
||||||
|
- **`cursorHide`** - Hide the cursor
|
||||||
|
- **`cursorShow`** - Show the cursor
|
||||||
|
|
||||||
|
### Screen Manipulation
|
||||||
|
|
||||||
|
- **`eraseLines(count: number)`** - Clear `count` lines and position cursor at the beginning of the first cleared line
|
||||||
|
|
||||||
|
# License
|
||||||
|
|
||||||
|
Copyright (c) 2025 Simon Boudrias (twitter: [@vaxilart](https://twitter.com/Vaxilart))<br/>
|
||||||
|
Licensed under the MIT license.
|
||||||
14
node_modules/@inquirer/ansi/dist/index.d.ts
generated
vendored
Normal file
14
node_modules/@inquirer/ansi/dist/index.d.ts
generated
vendored
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
/** Move cursor to first column */
|
||||||
|
export declare const cursorLeft: string;
|
||||||
|
/** Hide the cursor */
|
||||||
|
export declare const cursorHide: string;
|
||||||
|
/** Show the cursor */
|
||||||
|
export declare const cursorShow: string;
|
||||||
|
/** Move cursor up by count rows */
|
||||||
|
export declare const cursorUp: (rows?: number) => string;
|
||||||
|
/** Move cursor down by count rows */
|
||||||
|
export declare const cursorDown: (rows?: number) => string;
|
||||||
|
/** Move cursor to position (x, y) */
|
||||||
|
export declare const cursorTo: (x: number, y?: number) => string;
|
||||||
|
/** Erase the specified number of lines above the cursor */
|
||||||
|
export declare const eraseLines: (lines: number) => string;
|
||||||
21
node_modules/@inquirer/ansi/dist/index.js
generated
vendored
Normal file
21
node_modules/@inquirer/ansi/dist/index.js
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
const ESC = '\u001B[';
|
||||||
|
/** Move cursor to first column */
|
||||||
|
export const cursorLeft = ESC + 'G';
|
||||||
|
/** Hide the cursor */
|
||||||
|
export const cursorHide = ESC + '?25l';
|
||||||
|
/** Show the cursor */
|
||||||
|
export const cursorShow = ESC + '?25h';
|
||||||
|
/** Move cursor up by count rows */
|
||||||
|
export const cursorUp = (rows = 1) => (rows > 0 ? `${ESC}${rows}A` : '');
|
||||||
|
/** Move cursor down by count rows */
|
||||||
|
export const cursorDown = (rows = 1) => rows > 0 ? `${ESC}${rows}B` : '';
|
||||||
|
/** Move cursor to position (x, y) */
|
||||||
|
export const cursorTo = (x, y) => {
|
||||||
|
if (typeof y === 'number' && !Number.isNaN(y)) {
|
||||||
|
return `${ESC}${y + 1};${x + 1}H`;
|
||||||
|
}
|
||||||
|
return `${ESC}${x + 1}G`;
|
||||||
|
};
|
||||||
|
const eraseLine = ESC + '2K';
|
||||||
|
/** Erase the specified number of lines above the cursor */
|
||||||
|
export const eraseLines = (lines) => lines > 0 ? (eraseLine + cursorUp(1)).repeat(lines - 1) + eraseLine + cursorLeft : '';
|
||||||
78
node_modules/@inquirer/ansi/package.json
generated
vendored
Normal file
78
node_modules/@inquirer/ansi/package.json
generated
vendored
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
{
|
||||||
|
"name": "@inquirer/ansi",
|
||||||
|
"version": "2.0.3",
|
||||||
|
"keywords": [
|
||||||
|
"ansi",
|
||||||
|
"answer",
|
||||||
|
"answers",
|
||||||
|
"ask",
|
||||||
|
"base",
|
||||||
|
"cli",
|
||||||
|
"command",
|
||||||
|
"command-line",
|
||||||
|
"confirm",
|
||||||
|
"enquirer",
|
||||||
|
"generate",
|
||||||
|
"generator",
|
||||||
|
"hyper",
|
||||||
|
"input",
|
||||||
|
"inquire",
|
||||||
|
"inquirer",
|
||||||
|
"interface",
|
||||||
|
"iterm",
|
||||||
|
"javascript",
|
||||||
|
"menu",
|
||||||
|
"node",
|
||||||
|
"nodejs",
|
||||||
|
"prompt",
|
||||||
|
"promptly",
|
||||||
|
"prompts",
|
||||||
|
"question",
|
||||||
|
"readline",
|
||||||
|
"scaffold",
|
||||||
|
"scaffolder",
|
||||||
|
"scaffolding",
|
||||||
|
"stdin",
|
||||||
|
"stdout",
|
||||||
|
"terminal",
|
||||||
|
"tty",
|
||||||
|
"ui",
|
||||||
|
"yeoman",
|
||||||
|
"yo",
|
||||||
|
"zsh"
|
||||||
|
],
|
||||||
|
"homepage": "https://github.com/SBoudrias/Inquirer.js/blob/main/packages/ansi/README.md",
|
||||||
|
"license": "MIT",
|
||||||
|
"author": "Simon Boudrias <admin@simonboudrias.com>",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/SBoudrias/Inquirer.js.git"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"dist"
|
||||||
|
],
|
||||||
|
"type": "module",
|
||||||
|
"sideEffects": false,
|
||||||
|
"exports": {
|
||||||
|
".": {
|
||||||
|
"types": "./dist/index.d.ts",
|
||||||
|
"default": "./dist/index.js"
|
||||||
|
},
|
||||||
|
"./package.json": "./package.json"
|
||||||
|
},
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"tsc": "tsc"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"typescript": "^5.9.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0"
|
||||||
|
},
|
||||||
|
"main": "./dist/index.js",
|
||||||
|
"types": "./dist/index.d.ts",
|
||||||
|
"gitHead": "99d00a9adc53be8b7edf5926b2ec4ba0b792f68f"
|
||||||
|
}
|
||||||
22
node_modules/@inquirer/core/LICENSE
generated
vendored
Normal file
22
node_modules/@inquirer/core/LICENSE
generated
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
Copyright (c) 2025 Simon Boudrias
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person
|
||||||
|
obtaining a copy of this software and associated documentation
|
||||||
|
files (the "Software"), to deal in the Software without
|
||||||
|
restriction, including without limitation the rights to use,
|
||||||
|
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the
|
||||||
|
Software is furnished to do so, subject to the following
|
||||||
|
conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||||
|
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||||
|
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||||
|
OTHER DEALINGS IN THE SOFTWARE.
|
||||||
383
node_modules/@inquirer/core/README.md
generated
vendored
Normal file
383
node_modules/@inquirer/core/README.md
generated
vendored
Normal file
@ -0,0 +1,383 @@
|
|||||||
|
# `@inquirer/core`
|
||||||
|
|
||||||
|
The `@inquirer/core` package is the library enabling the creation of Inquirer prompts.
|
||||||
|
|
||||||
|
It aims to implements a lightweight API similar to React hooks - but without JSX.
|
||||||
|
|
||||||
|
# Installation
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th>npm</th>
|
||||||
|
<th>yarn</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm install @inquirer/core
|
||||||
|
```
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
```sh
|
||||||
|
yarn add @inquirer/core
|
||||||
|
```
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
# Usage
|
||||||
|
|
||||||
|
## Basic concept
|
||||||
|
|
||||||
|
Visual terminal apps are at their core strings rendered onto the terminal.
|
||||||
|
|
||||||
|
The most basic prompt is a function returning a string that'll be rendered in the terminal. This function will run every time the prompt state change, and the new returned string will replace the previously rendered one. The prompt cursor appears after the string.
|
||||||
|
|
||||||
|
Wrapping the rendering function with `createPrompt()` will setup the rendering layer, inject the state management utilities, and wait until the `done` callback is called.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { createPrompt } from '@inquirer/core';
|
||||||
|
|
||||||
|
const input = createPrompt((config, done) => {
|
||||||
|
// Implement logic
|
||||||
|
|
||||||
|
return '? My question';
|
||||||
|
});
|
||||||
|
|
||||||
|
// And it is then called as
|
||||||
|
const answer = await input({
|
||||||
|
/* config */
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## Hooks
|
||||||
|
|
||||||
|
State management and user interactions are handled through hooks. Hooks are common [within the React ecosystem](https://react.dev/reference/react/hooks), and Inquirer reimplement the common ones.
|
||||||
|
|
||||||
|
### State hook
|
||||||
|
|
||||||
|
State lets a component “remember” information like user input. For example, an input prompt can use state to store the input value, while a list prompt can use state to track the cursor index.
|
||||||
|
|
||||||
|
`useState` declares a state variable that you can update directly.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { createPrompt, useState } from '@inquirer/core';
|
||||||
|
|
||||||
|
const input = createPrompt((config, done) => {
|
||||||
|
const [index, setIndex] = useState(0);
|
||||||
|
|
||||||
|
// ...
|
||||||
|
```
|
||||||
|
|
||||||
|
### Keypress hook
|
||||||
|
|
||||||
|
Almost all prompts need to react to user actions. In a terminal, this is done through typing.
|
||||||
|
|
||||||
|
`useKeypress` allows you to react to keypress events, and access the prompt line.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const input = createPrompt((config, done) => {
|
||||||
|
useKeypress((key) => {
|
||||||
|
if (key.name === 'enter') {
|
||||||
|
done(answer);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// ...
|
||||||
|
```
|
||||||
|
|
||||||
|
Behind the scenes, Inquirer prompts are wrappers around [readlines](https://nodejs.org/api/readline.html). Aside the keypress event object, the hook also pass the active readline instance to the event handler.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const input = createPrompt((config, done) => {
|
||||||
|
useKeypress((key, readline) => {
|
||||||
|
setValue(readline.line);
|
||||||
|
});
|
||||||
|
|
||||||
|
// ...
|
||||||
|
```
|
||||||
|
|
||||||
|
### Ref hook
|
||||||
|
|
||||||
|
Refs let a prompt hold some information that isn’t used for rendering, like a class instance or a timeout ID. Unlike with state, updating a ref does not re-render your prompt. Refs are an “escape hatch” from the rendering paradigm.
|
||||||
|
|
||||||
|
`useRef` declares a ref. You can hold any value in it, but most often it’s used to hold a timeout ID.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const input = createPrompt((config, done) => {
|
||||||
|
const timeout = useRef(null);
|
||||||
|
|
||||||
|
// ...
|
||||||
|
```
|
||||||
|
|
||||||
|
### Effect Hook
|
||||||
|
|
||||||
|
Effects let a prompt connect to and synchronize with external systems. This includes dealing with network or animations.
|
||||||
|
|
||||||
|
`useEffect` connects a component to an external system.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const chat = createPrompt((config, done) => {
|
||||||
|
useEffect(() => {
|
||||||
|
const connection = createConnection(roomId);
|
||||||
|
connection.connect();
|
||||||
|
return () => connection.disconnect();
|
||||||
|
}, [roomId]);
|
||||||
|
|
||||||
|
// ...
|
||||||
|
```
|
||||||
|
|
||||||
|
### Performance hook
|
||||||
|
|
||||||
|
A common way to optimize re-rendering performance is to skip unnecessary work. For example, you can tell Inquirer to reuse a cached calculation or to skip a re-render if the data has not changed since the previous render.
|
||||||
|
|
||||||
|
`useMemo` lets you cache the result of an expensive calculation.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const todoSelect = createPrompt((config, done) => {
|
||||||
|
const visibleTodos = useMemo(() => filterTodos(todos, tab), [todos, tab]);
|
||||||
|
|
||||||
|
// ...
|
||||||
|
```
|
||||||
|
|
||||||
|
### Rendering hooks
|
||||||
|
|
||||||
|
#### Prefix / loading
|
||||||
|
|
||||||
|
All default prompts, and most custom ones, uses a prefix at the beginning of the prompt line. This helps visually delineate different questions, and provides a convenient area to render a loading spinner.
|
||||||
|
|
||||||
|
`usePrefix` is a built-in hook to do this.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const input = createPrompt((config, done) => {
|
||||||
|
const prefix = usePrefix({ status });
|
||||||
|
|
||||||
|
return `${prefix} My question`;
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Pagination
|
||||||
|
|
||||||
|
When looping through a long list of options (like in the `select` prompt), paginating the results appearing on the screen at once can be necessary. The `usePagination` hook is the utility used within the `select` and `checkbox` prompts to cycle through the list of options.
|
||||||
|
|
||||||
|
Pagination works by taking in the list of options and returning a subset of the rendered items that fit within the page. The hook takes in a few options. It needs a list of options (`items`), and a `pageSize` which is the number of lines to be rendered. The `active` index is the index of the currently selected/selectable item. The `loop` option is a boolean that indicates if the list should loop around when reaching the end: this is the default behavior. The pagination hook renders items only as necessary, so it takes a function that can render an item at an index, including an `active` state, called `renderItem`.
|
||||||
|
|
||||||
|
```js
|
||||||
|
export default createPrompt((config, done) => {
|
||||||
|
const [active, setActive] = useState(0);
|
||||||
|
|
||||||
|
const allChoices = config.choices.map((choice) => choice.name);
|
||||||
|
|
||||||
|
const page = usePagination({
|
||||||
|
items: allChoices,
|
||||||
|
active: active,
|
||||||
|
renderItem: ({ item, index, isActive }) => `${isActive ? ">" : " "}${index}. ${item.toString()}`
|
||||||
|
pageSize: config.pageSize,
|
||||||
|
loop: config.loop,
|
||||||
|
});
|
||||||
|
|
||||||
|
return `... ${page}`;
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## `createPrompt()` API
|
||||||
|
|
||||||
|
As we saw earlier, the rendering function should return a string, and eventually call `done` to close the prompt and return the answer.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const input = createPrompt((config, done) => {
|
||||||
|
const [value, setValue] = useState();
|
||||||
|
|
||||||
|
useKeypress((key, readline) => {
|
||||||
|
if (key.name === 'enter') {
|
||||||
|
done(answer);
|
||||||
|
} else {
|
||||||
|
setValue(readline.line);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return `? ${config.message} ${value}`;
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
The rendering function can also return a tuple of 2 string (`[string, string]`.) The first string represents the prompt. The second one is content to render under the prompt, like an error message. The text input cursor will appear after the first string.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const number = createPrompt((config, done) => {
|
||||||
|
// Add some logic here
|
||||||
|
|
||||||
|
return [`? My question ${input}`, `! The input must be a number`];
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Typescript
|
||||||
|
|
||||||
|
If using typescript, `createPrompt` takes 2 generic arguments.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// createPrompt<Value, Config>
|
||||||
|
const input = createPrompt<string, { message: string }>(// ...
|
||||||
|
```
|
||||||
|
|
||||||
|
The first one is the type of the resolved value
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const answer: string = await input();
|
||||||
|
```
|
||||||
|
|
||||||
|
The second one is the type of the prompt config; in other words the interface the created prompt will provide to users.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const answer = await input({
|
||||||
|
message: 'My question',
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## Key utilities
|
||||||
|
|
||||||
|
Listening for keypress events inside an inquirer prompt is a very common pattern. To ease this, we export a few utility functions taking in the keypress event object and return a boolean:
|
||||||
|
|
||||||
|
- `isEnterKey()`
|
||||||
|
- `isBackspaceKey()`
|
||||||
|
- `isSpaceKey()`
|
||||||
|
- `isUpKey()` - Note: this utility will handle vim and emacs keybindings (up, `k`, and `ctrl+p`)
|
||||||
|
- `isDownKey()` - Note: this utility will handle vim and emacs keybindings (down, `j`, and `ctrl+n`)
|
||||||
|
- `isNumberKey()` one of 1, 2, 3, 4, 5, 6, 7, 8, 9, 0
|
||||||
|
|
||||||
|
## Theming
|
||||||
|
|
||||||
|
Theming utilities will allow you to expose customization of the prompt style. Inquirer also has a few standard theme values shared across all the official prompts.
|
||||||
|
|
||||||
|
To allow standard customization:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { createPrompt, usePrefix, makeTheme, type Theme } from '@inquirer/core';
|
||||||
|
import type { PartialDeep } from '@inquirer/type';
|
||||||
|
|
||||||
|
type PromptConfig = {
|
||||||
|
theme?: PartialDeep<Theme>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default createPrompt<string, PromptConfig>((config, done) => {
|
||||||
|
const theme = makeTheme(config.theme);
|
||||||
|
|
||||||
|
const prefix = usePrefix({ status, theme });
|
||||||
|
|
||||||
|
return `${prefix} ${theme.style.highlight('hello')}`;
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
To setup a custom theme:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { createPrompt, makeTheme, type Theme } from '@inquirer/core';
|
||||||
|
import type { PartialDeep } from '@inquirer/type';
|
||||||
|
|
||||||
|
type PromptTheme = {};
|
||||||
|
|
||||||
|
const promptTheme: PromptTheme = {
|
||||||
|
icon: '!',
|
||||||
|
};
|
||||||
|
|
||||||
|
type PromptConfig = {
|
||||||
|
theme?: PartialDeep<Theme<PromptTheme>>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default createPrompt<string, PromptConfig>((config, done) => {
|
||||||
|
const theme = makeTheme(promptTheme, config.theme);
|
||||||
|
|
||||||
|
const prefix = usePrefix({ status, theme });
|
||||||
|
|
||||||
|
return `${prefix} ${theme.icon}`;
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
The [default theme keys cover](https://github.com/SBoudrias/Inquirer.js/blob/main/packages/core/src/lib/theme.ts):
|
||||||
|
|
||||||
|
```ts
|
||||||
|
type DefaultTheme = {
|
||||||
|
prefix: string | { idle: string; done: string };
|
||||||
|
spinner: {
|
||||||
|
interval: number;
|
||||||
|
frames: string[];
|
||||||
|
};
|
||||||
|
style: {
|
||||||
|
answer: (text: string) => string;
|
||||||
|
message: (text: string, status: 'idle' | 'done' | 'loading') => string;
|
||||||
|
error: (text: string) => string;
|
||||||
|
defaultAnswer: (text: string) => string;
|
||||||
|
help: (text: string) => string;
|
||||||
|
highlight: (text: string) => string;
|
||||||
|
key: (text: string) => string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
# Examples
|
||||||
|
|
||||||
|
You can refer to any `@inquirer/prompts` prompts for real examples:
|
||||||
|
|
||||||
|
- [Confirm Prompt](https://github.com/SBoudrias/Inquirer.js/blob/main/packages/confirm/src/index.ts)
|
||||||
|
- [Input Prompt](https://github.com/SBoudrias/Inquirer.js/blob/main/packages/input/src/index.ts)
|
||||||
|
- [Password Prompt](https://github.com/SBoudrias/Inquirer.js/blob/main/packages/password/src/index.ts)
|
||||||
|
- [Editor Prompt](https://github.com/SBoudrias/Inquirer.js/blob/main/packages/editor/src/index.ts)
|
||||||
|
- [Select Prompt](https://github.com/SBoudrias/Inquirer.js/blob/main/packages/select/src/index.ts)
|
||||||
|
- [Checkbox Prompt](https://github.com/SBoudrias/Inquirer.js/blob/main/packages/checkbox/src/index.ts)
|
||||||
|
- [Rawlist Prompt](https://github.com/SBoudrias/Inquirer.js/blob/main/packages/rawlist/src/index.ts)
|
||||||
|
- [Expand Prompt](https://github.com/SBoudrias/Inquirer.js/blob/main/packages/expand/src/index.ts)
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { styleText } from 'node:util';
|
||||||
|
import {
|
||||||
|
createPrompt,
|
||||||
|
useState,
|
||||||
|
useKeypress,
|
||||||
|
isEnterKey,
|
||||||
|
usePrefix,
|
||||||
|
type Status,
|
||||||
|
} from '@inquirer/core';
|
||||||
|
|
||||||
|
const confirm = createPrompt<boolean, { message: string; default?: boolean }>(
|
||||||
|
(config, done) => {
|
||||||
|
const [status, setStatus] = useState<Status>('idle');
|
||||||
|
const [value, setValue] = useState('');
|
||||||
|
const prefix = usePrefix({});
|
||||||
|
|
||||||
|
useKeypress((key, rl) => {
|
||||||
|
if (isEnterKey(key)) {
|
||||||
|
const answer = value ? /^y(es)?/i.test(value) : config.default !== false;
|
||||||
|
setValue(answer ? 'yes' : 'no');
|
||||||
|
setStatus('done');
|
||||||
|
done(answer);
|
||||||
|
} else {
|
||||||
|
setValue(rl.line);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let formattedValue = value;
|
||||||
|
let defaultValue = '';
|
||||||
|
if (status === 'done') {
|
||||||
|
formattedValue = styleText('cyan', value);
|
||||||
|
} else {
|
||||||
|
defaultValue = styleText('dim', config.default === false ? ' (y/N)' : ' (Y/n)');
|
||||||
|
}
|
||||||
|
|
||||||
|
const message = styleText('bold', config.message);
|
||||||
|
return `${prefix} ${message}${defaultValue} ${formattedValue}`;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Which then can be used like this:
|
||||||
|
*/
|
||||||
|
const answer = await confirm({ message: 'Do you want to continue?' });
|
||||||
|
```
|
||||||
|
|
||||||
|
# License
|
||||||
|
|
||||||
|
Copyright (c) 2023 Simon Boudrias (twitter: [@vaxilart](https://twitter.com/Vaxilart))<br/>
|
||||||
|
Licensed under the MIT license.
|
||||||
13
node_modules/@inquirer/core/dist/index.d.ts
generated
vendored
Normal file
13
node_modules/@inquirer/core/dist/index.d.ts
generated
vendored
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
export { isUpKey, isDownKey, isSpaceKey, isBackspaceKey, isTabKey, isNumberKey, isEnterKey, isShiftKey, type KeypressEvent, type Keybinding, } from './lib/key.ts';
|
||||||
|
export * from './lib/errors.ts';
|
||||||
|
export { usePrefix } from './lib/use-prefix.ts';
|
||||||
|
export { useState } from './lib/use-state.ts';
|
||||||
|
export { useEffect } from './lib/use-effect.ts';
|
||||||
|
export { useMemo } from './lib/use-memo.ts';
|
||||||
|
export { useRef } from './lib/use-ref.ts';
|
||||||
|
export { useKeypress } from './lib/use-keypress.ts';
|
||||||
|
export { makeTheme } from './lib/make-theme.ts';
|
||||||
|
export type { Theme, Status } from './lib/theme.ts';
|
||||||
|
export { usePagination } from './lib/pagination/use-pagination.ts';
|
||||||
|
export { createPrompt } from './lib/create-prompt.ts';
|
||||||
|
export { Separator } from './lib/Separator.ts';
|
||||||
12
node_modules/@inquirer/core/dist/index.js
generated
vendored
Normal file
12
node_modules/@inquirer/core/dist/index.js
generated
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
export { isUpKey, isDownKey, isSpaceKey, isBackspaceKey, isTabKey, isNumberKey, isEnterKey, isShiftKey, } from "./lib/key.js";
|
||||||
|
export * from "./lib/errors.js";
|
||||||
|
export { usePrefix } from "./lib/use-prefix.js";
|
||||||
|
export { useState } from "./lib/use-state.js";
|
||||||
|
export { useEffect } from "./lib/use-effect.js";
|
||||||
|
export { useMemo } from "./lib/use-memo.js";
|
||||||
|
export { useRef } from "./lib/use-ref.js";
|
||||||
|
export { useKeypress } from "./lib/use-keypress.js";
|
||||||
|
export { makeTheme } from "./lib/make-theme.js";
|
||||||
|
export { usePagination } from "./lib/pagination/use-pagination.js";
|
||||||
|
export { createPrompt } from "./lib/create-prompt.js";
|
||||||
|
export { Separator } from "./lib/Separator.js";
|
||||||
10
node_modules/@inquirer/core/dist/lib/Separator.d.ts
generated
vendored
Normal file
10
node_modules/@inquirer/core/dist/lib/Separator.d.ts
generated
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
/**
|
||||||
|
* Separator object
|
||||||
|
* Used to space/separate choices group
|
||||||
|
*/
|
||||||
|
export declare class Separator {
|
||||||
|
readonly separator: string;
|
||||||
|
readonly type: string;
|
||||||
|
constructor(separator?: string);
|
||||||
|
static isSeparator(choice: unknown): choice is Separator;
|
||||||
|
}
|
||||||
21
node_modules/@inquirer/core/dist/lib/Separator.js
generated
vendored
Normal file
21
node_modules/@inquirer/core/dist/lib/Separator.js
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import { styleText } from 'node:util';
|
||||||
|
import figures from '@inquirer/figures';
|
||||||
|
/**
|
||||||
|
* Separator object
|
||||||
|
* Used to space/separate choices group
|
||||||
|
*/
|
||||||
|
export class Separator {
|
||||||
|
separator = styleText('dim', Array.from({ length: 15 }).join(figures.line));
|
||||||
|
type = 'separator';
|
||||||
|
constructor(separator) {
|
||||||
|
if (separator) {
|
||||||
|
this.separator = separator;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static isSeparator(choice) {
|
||||||
|
return Boolean(choice &&
|
||||||
|
typeof choice === 'object' &&
|
||||||
|
'type' in choice &&
|
||||||
|
choice.type === 'separator');
|
||||||
|
}
|
||||||
|
}
|
||||||
4
node_modules/@inquirer/core/dist/lib/create-prompt.d.ts
generated
vendored
Normal file
4
node_modules/@inquirer/core/dist/lib/create-prompt.d.ts
generated
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
import { type Prompt, type Prettify } from '@inquirer/type';
|
||||||
|
type ViewFunction<Value, Config> = (config: Prettify<Config>, done: (value: Value) => void) => string | [string, string | undefined];
|
||||||
|
export declare function createPrompt<Value, Config>(view: ViewFunction<Value, Config>): Prompt<Value, Config>;
|
||||||
|
export {};
|
||||||
143
node_modules/@inquirer/core/dist/lib/create-prompt.js
generated
vendored
Normal file
143
node_modules/@inquirer/core/dist/lib/create-prompt.js
generated
vendored
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
import * as readline from 'node:readline';
|
||||||
|
import { AsyncResource } from 'node:async_hooks';
|
||||||
|
import MuteStream from 'mute-stream';
|
||||||
|
import { onExit as onSignalExit } from 'signal-exit';
|
||||||
|
import ScreenManager from "./screen-manager.js";
|
||||||
|
import { PromisePolyfill } from "./promise-polyfill.js";
|
||||||
|
import { withHooks, effectScheduler } from "./hook-engine.js";
|
||||||
|
import { AbortPromptError, CancelPromptError, ExitPromptError } from "./errors.js";
|
||||||
|
// Capture the real setImmediate at module load time so it works even when test
|
||||||
|
// frameworks mock timers with vi.useFakeTimers() or similar.
|
||||||
|
const nativeSetImmediate = globalThis.setImmediate;
|
||||||
|
function getCallSites() {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/unbound-method
|
||||||
|
const _prepareStackTrace = Error.prepareStackTrace;
|
||||||
|
let result = [];
|
||||||
|
try {
|
||||||
|
Error.prepareStackTrace = (_, callSites) => {
|
||||||
|
const callSitesWithoutCurrent = callSites.slice(1);
|
||||||
|
result = callSitesWithoutCurrent;
|
||||||
|
return callSitesWithoutCurrent;
|
||||||
|
};
|
||||||
|
// oxlint-disable-next-line no-unused-expressions
|
||||||
|
new Error().stack;
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
// An error will occur if the Node flag --frozen-intrinsics is used.
|
||||||
|
// https://nodejs.org/api/cli.html#--frozen-intrinsics
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
Error.prepareStackTrace = _prepareStackTrace;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
export function createPrompt(view) {
|
||||||
|
const callSites = getCallSites();
|
||||||
|
const prompt = (config, context = {}) => {
|
||||||
|
// Default `input` to stdin
|
||||||
|
const { input = process.stdin, signal } = context;
|
||||||
|
const cleanups = new Set();
|
||||||
|
// Add mute capabilities to the output
|
||||||
|
const output = new MuteStream();
|
||||||
|
output.pipe(context.output ?? process.stdout);
|
||||||
|
// Pre-mute the output so that readline doesn't echo stale keystrokes
|
||||||
|
// to the terminal before the first render. ScreenManager will unmute/mute
|
||||||
|
// the output around each render call as needed.
|
||||||
|
output.mute();
|
||||||
|
const rl = readline.createInterface({
|
||||||
|
terminal: true,
|
||||||
|
input,
|
||||||
|
output,
|
||||||
|
});
|
||||||
|
const screen = new ScreenManager(rl);
|
||||||
|
const { promise, resolve, reject } = PromisePolyfill.withResolver();
|
||||||
|
const cancel = () => reject(new CancelPromptError());
|
||||||
|
if (signal) {
|
||||||
|
const abort = () => reject(new AbortPromptError({ cause: signal.reason }));
|
||||||
|
if (signal.aborted) {
|
||||||
|
abort();
|
||||||
|
return Object.assign(promise, { cancel });
|
||||||
|
}
|
||||||
|
signal.addEventListener('abort', abort);
|
||||||
|
cleanups.add(() => signal.removeEventListener('abort', abort));
|
||||||
|
}
|
||||||
|
cleanups.add(onSignalExit((code, signal) => {
|
||||||
|
reject(new ExitPromptError(`User force closed the prompt with ${code} ${signal}`));
|
||||||
|
}));
|
||||||
|
// SIGINT must be explicitly handled by the prompt so the ExitPromptError can be handled.
|
||||||
|
// Otherwise, the prompt will stop and in some scenarios never resolve.
|
||||||
|
// Ref issue #1741
|
||||||
|
const sigint = () => reject(new ExitPromptError(`User force closed the prompt with SIGINT`));
|
||||||
|
rl.on('SIGINT', sigint);
|
||||||
|
cleanups.add(() => rl.removeListener('SIGINT', sigint));
|
||||||
|
return withHooks(rl, (cycle) => {
|
||||||
|
// The close event triggers immediately when the user press ctrl+c. SignalExit on the other hand
|
||||||
|
// triggers after the process is done (which happens after timeouts are done triggering.)
|
||||||
|
// We triggers the hooks cleanup phase on rl `close` so active timeouts can be cleared.
|
||||||
|
const hooksCleanup = AsyncResource.bind(() => effectScheduler.clearAll());
|
||||||
|
rl.on('close', hooksCleanup);
|
||||||
|
cleanups.add(() => rl.removeListener('close', hooksCleanup));
|
||||||
|
const startCycle = () => {
|
||||||
|
// Re-renders only happen when the state change; but the readline cursor could
|
||||||
|
// change position and that also requires a re-render (and a manual one because
|
||||||
|
// we mute the streams). We set the listener after the initial workLoop to avoid
|
||||||
|
// a double render if render triggered by a state change sets the cursor to the
|
||||||
|
// right position.
|
||||||
|
const checkCursorPos = () => screen.checkCursorPos();
|
||||||
|
rl.input.on('keypress', checkCursorPos);
|
||||||
|
cleanups.add(() => rl.input.removeListener('keypress', checkCursorPos));
|
||||||
|
cycle(() => {
|
||||||
|
try {
|
||||||
|
const nextView = view(config, (value) => {
|
||||||
|
setImmediate(() => resolve(value));
|
||||||
|
});
|
||||||
|
// Typescript won't allow this, but not all users rely on typescript.
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||||
|
if (nextView === undefined) {
|
||||||
|
const callerFilename = callSites[1]?.getFileName();
|
||||||
|
throw new Error(`Prompt functions must return a string.\n at ${callerFilename}`);
|
||||||
|
}
|
||||||
|
const [content, bottomContent] = typeof nextView === 'string' ? [nextView] : nextView;
|
||||||
|
screen.render(content, bottomContent);
|
||||||
|
effectScheduler.run();
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
// Proper Readable streams (like process.stdin) may have OS-level buffered
|
||||||
|
// data that arrives in the poll phase when readline resumes the stream.
|
||||||
|
// Deferring the first render by one setImmediate tick (check phase, after
|
||||||
|
// poll) lets that stale data flow through readline harmlessly—no keypress
|
||||||
|
// handlers are registered yet and the output is muted, so the stale
|
||||||
|
// keystrokes are silently discarded.
|
||||||
|
// Old-style streams (like MuteStream) have no such buffering, so the
|
||||||
|
// render cycle starts immediately.
|
||||||
|
//
|
||||||
|
// @see https://github.com/SBoudrias/Inquirer.js/issues/1303
|
||||||
|
if ('readableFlowing' in input) {
|
||||||
|
nativeSetImmediate(startCycle);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
startCycle();
|
||||||
|
}
|
||||||
|
return Object.assign(promise
|
||||||
|
.then((answer) => {
|
||||||
|
effectScheduler.clearAll();
|
||||||
|
return answer;
|
||||||
|
}, (error) => {
|
||||||
|
effectScheduler.clearAll();
|
||||||
|
throw error;
|
||||||
|
})
|
||||||
|
// Wait for the promise to settle, then cleanup.
|
||||||
|
.finally(() => {
|
||||||
|
cleanups.forEach((cleanup) => cleanup());
|
||||||
|
screen.done({ clearContent: Boolean(context.clearPromptOnDone) });
|
||||||
|
output.end();
|
||||||
|
})
|
||||||
|
// Once cleanup is done, let the expose promise resolve/reject to the internal one.
|
||||||
|
.then(() => promise), { cancel });
|
||||||
|
});
|
||||||
|
};
|
||||||
|
return prompt;
|
||||||
|
}
|
||||||
20
node_modules/@inquirer/core/dist/lib/errors.d.ts
generated
vendored
Normal file
20
node_modules/@inquirer/core/dist/lib/errors.d.ts
generated
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
export declare class AbortPromptError extends Error {
|
||||||
|
name: string;
|
||||||
|
message: string;
|
||||||
|
constructor(options?: {
|
||||||
|
cause?: unknown;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
export declare class CancelPromptError extends Error {
|
||||||
|
name: string;
|
||||||
|
message: string;
|
||||||
|
}
|
||||||
|
export declare class ExitPromptError extends Error {
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
export declare class HookError extends Error {
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
export declare class ValidationError extends Error {
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
21
node_modules/@inquirer/core/dist/lib/errors.js
generated
vendored
Normal file
21
node_modules/@inquirer/core/dist/lib/errors.js
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
export class AbortPromptError extends Error {
|
||||||
|
name = 'AbortPromptError';
|
||||||
|
message = 'Prompt was aborted';
|
||||||
|
constructor(options) {
|
||||||
|
super();
|
||||||
|
this.cause = options?.cause;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export class CancelPromptError extends Error {
|
||||||
|
name = 'CancelPromptError';
|
||||||
|
message = 'Prompt was canceled';
|
||||||
|
}
|
||||||
|
export class ExitPromptError extends Error {
|
||||||
|
name = 'ExitPromptError';
|
||||||
|
}
|
||||||
|
export class HookError extends Error {
|
||||||
|
name = 'HookError';
|
||||||
|
}
|
||||||
|
export class ValidationError extends Error {
|
||||||
|
name = 'ValidationError';
|
||||||
|
}
|
||||||
23
node_modules/@inquirer/core/dist/lib/hook-engine.d.ts
generated
vendored
Normal file
23
node_modules/@inquirer/core/dist/lib/hook-engine.d.ts
generated
vendored
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import type { InquirerReadline } from '@inquirer/type';
|
||||||
|
export declare function withHooks<T>(rl: InquirerReadline, cb: (cycle: (render: () => void) => void) => T): T;
|
||||||
|
export declare function readline(): InquirerReadline;
|
||||||
|
export declare function withUpdates<Args extends unknown[], R>(fn: (...args: Args) => R): (...args: Args) => R;
|
||||||
|
type SetPointer<Value> = {
|
||||||
|
get(): Value;
|
||||||
|
set(value: Value): void;
|
||||||
|
initialized: true;
|
||||||
|
};
|
||||||
|
type UnsetPointer<Value> = {
|
||||||
|
get(): void;
|
||||||
|
set(value: Value): void;
|
||||||
|
initialized: false;
|
||||||
|
};
|
||||||
|
type Pointer<Value> = SetPointer<Value> | UnsetPointer<Value>;
|
||||||
|
export declare function withPointer<Value, ReturnValue>(cb: (pointer: Pointer<Value>) => ReturnValue): ReturnValue;
|
||||||
|
export declare function handleChange(): void;
|
||||||
|
export declare const effectScheduler: {
|
||||||
|
queue(cb: (readline: InquirerReadline) => void | (() => void)): void;
|
||||||
|
run(): void;
|
||||||
|
clearAll(): void;
|
||||||
|
};
|
||||||
|
export {};
|
||||||
110
node_modules/@inquirer/core/dist/lib/hook-engine.js
generated
vendored
Normal file
110
node_modules/@inquirer/core/dist/lib/hook-engine.js
generated
vendored
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
/* eslint @typescript-eslint/no-explicit-any: ["off"] */
|
||||||
|
import { AsyncLocalStorage, AsyncResource } from 'node:async_hooks';
|
||||||
|
import { HookError, ValidationError } from "./errors.js";
|
||||||
|
const hookStorage = new AsyncLocalStorage();
|
||||||
|
function createStore(rl) {
|
||||||
|
const store = {
|
||||||
|
rl,
|
||||||
|
hooks: [],
|
||||||
|
hooksCleanup: [],
|
||||||
|
hooksEffect: [],
|
||||||
|
index: 0,
|
||||||
|
handleChange() { },
|
||||||
|
};
|
||||||
|
return store;
|
||||||
|
}
|
||||||
|
// Run callback in with the hook engine setup.
|
||||||
|
export function withHooks(rl, cb) {
|
||||||
|
const store = createStore(rl);
|
||||||
|
return hookStorage.run(store, () => {
|
||||||
|
function cycle(render) {
|
||||||
|
store.handleChange = () => {
|
||||||
|
store.index = 0;
|
||||||
|
render();
|
||||||
|
};
|
||||||
|
store.handleChange();
|
||||||
|
}
|
||||||
|
return cb(cycle);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Safe getStore utility that'll return the store or throw if undefined.
|
||||||
|
function getStore() {
|
||||||
|
const store = hookStorage.getStore();
|
||||||
|
if (!store) {
|
||||||
|
throw new HookError('[Inquirer] Hook functions can only be called from within a prompt');
|
||||||
|
}
|
||||||
|
return store;
|
||||||
|
}
|
||||||
|
export function readline() {
|
||||||
|
return getStore().rl;
|
||||||
|
}
|
||||||
|
// Merge state updates happening within the callback function to avoid multiple renders.
|
||||||
|
export function withUpdates(fn) {
|
||||||
|
const wrapped = (...args) => {
|
||||||
|
const store = getStore();
|
||||||
|
let shouldUpdate = false;
|
||||||
|
const oldHandleChange = store.handleChange;
|
||||||
|
store.handleChange = () => {
|
||||||
|
shouldUpdate = true;
|
||||||
|
};
|
||||||
|
const returnValue = fn(...args);
|
||||||
|
if (shouldUpdate) {
|
||||||
|
oldHandleChange();
|
||||||
|
}
|
||||||
|
store.handleChange = oldHandleChange;
|
||||||
|
return returnValue;
|
||||||
|
};
|
||||||
|
return AsyncResource.bind(wrapped);
|
||||||
|
}
|
||||||
|
export function withPointer(cb) {
|
||||||
|
const store = getStore();
|
||||||
|
const { index } = store;
|
||||||
|
const pointer = {
|
||||||
|
get() {
|
||||||
|
return store.hooks[index];
|
||||||
|
},
|
||||||
|
set(value) {
|
||||||
|
store.hooks[index] = value;
|
||||||
|
},
|
||||||
|
initialized: index in store.hooks,
|
||||||
|
};
|
||||||
|
const returnValue = cb(pointer);
|
||||||
|
store.index++;
|
||||||
|
return returnValue;
|
||||||
|
}
|
||||||
|
export function handleChange() {
|
||||||
|
getStore().handleChange();
|
||||||
|
}
|
||||||
|
export const effectScheduler = {
|
||||||
|
queue(cb) {
|
||||||
|
const store = getStore();
|
||||||
|
const { index } = store;
|
||||||
|
store.hooksEffect.push(() => {
|
||||||
|
store.hooksCleanup[index]?.();
|
||||||
|
const cleanFn = cb(readline());
|
||||||
|
if (cleanFn != null && typeof cleanFn !== 'function') {
|
||||||
|
throw new ValidationError('useEffect return value must be a cleanup function or nothing.');
|
||||||
|
}
|
||||||
|
store.hooksCleanup[index] = cleanFn;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
run() {
|
||||||
|
const store = getStore();
|
||||||
|
withUpdates(() => {
|
||||||
|
store.hooksEffect.forEach((effect) => {
|
||||||
|
effect();
|
||||||
|
});
|
||||||
|
// Warning: Clean the hooks before exiting the `withUpdates` block.
|
||||||
|
// Failure to do so means an updates would hit the same effects again.
|
||||||
|
store.hooksEffect.length = 0;
|
||||||
|
})();
|
||||||
|
},
|
||||||
|
clearAll() {
|
||||||
|
const store = getStore();
|
||||||
|
store.hooksCleanup.forEach((cleanFn) => {
|
||||||
|
cleanFn?.();
|
||||||
|
});
|
||||||
|
store.hooksEffect.length = 0;
|
||||||
|
store.hooksCleanup.length = 0;
|
||||||
|
},
|
||||||
|
};
|
||||||
14
node_modules/@inquirer/core/dist/lib/key.d.ts
generated
vendored
Normal file
14
node_modules/@inquirer/core/dist/lib/key.d.ts
generated
vendored
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
export type KeypressEvent = {
|
||||||
|
name: string;
|
||||||
|
ctrl: boolean;
|
||||||
|
shift: boolean;
|
||||||
|
};
|
||||||
|
export type Keybinding = 'emacs' | 'vim';
|
||||||
|
export declare const isUpKey: (key: KeypressEvent, keybindings?: ReadonlyArray<Keybinding>) => boolean;
|
||||||
|
export declare const isDownKey: (key: KeypressEvent, keybindings?: ReadonlyArray<Keybinding>) => boolean;
|
||||||
|
export declare const isSpaceKey: (key: KeypressEvent) => boolean;
|
||||||
|
export declare const isBackspaceKey: (key: KeypressEvent) => boolean;
|
||||||
|
export declare const isTabKey: (key: KeypressEvent) => boolean;
|
||||||
|
export declare const isNumberKey: (key: KeypressEvent) => boolean;
|
||||||
|
export declare const isEnterKey: (key: KeypressEvent) => boolean;
|
||||||
|
export declare const isShiftKey: (key: KeypressEvent) => boolean;
|
||||||
20
node_modules/@inquirer/core/dist/lib/key.js
generated
vendored
Normal file
20
node_modules/@inquirer/core/dist/lib/key.js
generated
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
export const isUpKey = (key, keybindings = []) =>
|
||||||
|
// The up key
|
||||||
|
key.name === 'up' ||
|
||||||
|
// Vim keybinding: hjkl keys map to left/down/up/right
|
||||||
|
(keybindings.includes('vim') && key.name === 'k') ||
|
||||||
|
// Emacs keybinding: Ctrl+P means "previous" in Emacs navigation conventions
|
||||||
|
(keybindings.includes('emacs') && key.ctrl && key.name === 'p');
|
||||||
|
export const isDownKey = (key, keybindings = []) =>
|
||||||
|
// The down key
|
||||||
|
key.name === 'down' ||
|
||||||
|
// Vim keybinding: hjkl keys map to left/down/up/right
|
||||||
|
(keybindings.includes('vim') && key.name === 'j') ||
|
||||||
|
// Emacs keybinding: Ctrl+N means "next" in Emacs navigation conventions
|
||||||
|
(keybindings.includes('emacs') && key.ctrl && key.name === 'n');
|
||||||
|
export const isSpaceKey = (key) => key.name === 'space';
|
||||||
|
export const isBackspaceKey = (key) => key.name === 'backspace';
|
||||||
|
export const isTabKey = (key) => key.name === 'tab';
|
||||||
|
export const isNumberKey = (key) => '1234567890'.includes(key.name);
|
||||||
|
export const isEnterKey = (key) => key.name === 'enter' || key.name === 'return';
|
||||||
|
export const isShiftKey = (key) => key.shift;
|
||||||
3
node_modules/@inquirer/core/dist/lib/make-theme.d.ts
generated
vendored
Normal file
3
node_modules/@inquirer/core/dist/lib/make-theme.d.ts
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import type { Prettify, PartialDeep } from '@inquirer/type';
|
||||||
|
import { type Theme } from './theme.ts';
|
||||||
|
export declare function makeTheme<SpecificTheme extends object>(...themes: ReadonlyArray<undefined | PartialDeep<Theme<SpecificTheme>>>): Prettify<Theme<SpecificTheme>>;
|
||||||
30
node_modules/@inquirer/core/dist/lib/make-theme.js
generated
vendored
Normal file
30
node_modules/@inquirer/core/dist/lib/make-theme.js
generated
vendored
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import { defaultTheme } from "./theme.js";
|
||||||
|
function isPlainObject(value) {
|
||||||
|
if (typeof value !== 'object' || value === null)
|
||||||
|
return false;
|
||||||
|
let proto = value;
|
||||||
|
while (Object.getPrototypeOf(proto) !== null) {
|
||||||
|
proto = Object.getPrototypeOf(proto);
|
||||||
|
}
|
||||||
|
return Object.getPrototypeOf(value) === proto;
|
||||||
|
}
|
||||||
|
function deepMerge(...objects) {
|
||||||
|
const output = {};
|
||||||
|
for (const obj of objects) {
|
||||||
|
for (const [key, value] of Object.entries(obj)) {
|
||||||
|
const prevValue = output[key];
|
||||||
|
output[key] =
|
||||||
|
isPlainObject(prevValue) && isPlainObject(value)
|
||||||
|
? deepMerge(prevValue, value)
|
||||||
|
: value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
export function makeTheme(...themes) {
|
||||||
|
const themesToMerge = [
|
||||||
|
defaultTheme,
|
||||||
|
...themes.filter((theme) => theme != null),
|
||||||
|
];
|
||||||
|
return deepMerge(...themesToMerge);
|
||||||
|
}
|
||||||
16
node_modules/@inquirer/core/dist/lib/pagination/use-pagination.d.ts
generated
vendored
Normal file
16
node_modules/@inquirer/core/dist/lib/pagination/use-pagination.d.ts
generated
vendored
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import type { Prettify } from '@inquirer/type';
|
||||||
|
export declare function usePagination<T>({ items, active, renderItem, pageSize, loop, }: {
|
||||||
|
items: ReadonlyArray<T>;
|
||||||
|
/** The index of the active item. */
|
||||||
|
active: number;
|
||||||
|
/** Renders an item as part of a page. */
|
||||||
|
renderItem: (layout: Prettify<{
|
||||||
|
item: T;
|
||||||
|
index: number;
|
||||||
|
isActive: boolean;
|
||||||
|
}>) => string;
|
||||||
|
/** The size of the page. */
|
||||||
|
pageSize: number;
|
||||||
|
/** Allows creating an infinitely looping list. `true` if unspecified. */
|
||||||
|
loop?: boolean;
|
||||||
|
}): string;
|
||||||
121
node_modules/@inquirer/core/dist/lib/pagination/use-pagination.js
generated
vendored
Normal file
121
node_modules/@inquirer/core/dist/lib/pagination/use-pagination.js
generated
vendored
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
import { useRef } from "../use-ref.js";
|
||||||
|
import { readlineWidth, breakLines } from "../utils.js";
|
||||||
|
function usePointerPosition({ active, renderedItems, pageSize, loop, }) {
|
||||||
|
const state = useRef({
|
||||||
|
lastPointer: active,
|
||||||
|
lastActive: undefined,
|
||||||
|
});
|
||||||
|
const { lastPointer, lastActive } = state.current;
|
||||||
|
const middle = Math.floor(pageSize / 2);
|
||||||
|
const renderedLength = renderedItems.reduce((acc, item) => acc + item.length, 0);
|
||||||
|
const defaultPointerPosition = renderedItems
|
||||||
|
.slice(0, active)
|
||||||
|
.reduce((acc, item) => acc + item.length, 0);
|
||||||
|
let pointer = defaultPointerPosition;
|
||||||
|
if (renderedLength > pageSize) {
|
||||||
|
if (loop) {
|
||||||
|
/**
|
||||||
|
* Creates the next position for the pointer considering an infinitely
|
||||||
|
* looping list of items to be rendered on the page.
|
||||||
|
*
|
||||||
|
* The goal is to progressively move the cursor to the middle position as the user move down, and then keep
|
||||||
|
* the cursor there. When the user move up, maintain the cursor position.
|
||||||
|
*/
|
||||||
|
// By default, keep the cursor position as-is.
|
||||||
|
pointer = lastPointer;
|
||||||
|
if (
|
||||||
|
// First render, skip this logic.
|
||||||
|
lastActive != null &&
|
||||||
|
// Only move the pointer down when the user moves down.
|
||||||
|
lastActive < active &&
|
||||||
|
// Check user didn't move up across page boundary.
|
||||||
|
active - lastActive < pageSize) {
|
||||||
|
pointer = Math.min(
|
||||||
|
// Furthest allowed position for the pointer is the middle of the list
|
||||||
|
middle, Math.abs(active - lastActive) === 1
|
||||||
|
? Math.min(
|
||||||
|
// Move the pointer at most the height of the last active item.
|
||||||
|
lastPointer + (renderedItems[lastActive]?.length ?? 0),
|
||||||
|
// If the user moved by one item, move the pointer to the natural position of the active item as
|
||||||
|
// long as it doesn't move the cursor up.
|
||||||
|
Math.max(defaultPointerPosition, lastPointer))
|
||||||
|
: // Otherwise, move the pointer down by the difference between the active and last active item.
|
||||||
|
lastPointer + active - lastActive);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/**
|
||||||
|
* Creates the next position for the pointer considering a finite list of
|
||||||
|
* items to be rendered on a page.
|
||||||
|
*
|
||||||
|
* The goal is to keep the pointer in the middle of the page whenever possible, until
|
||||||
|
* we reach the bounds of the list (top or bottom). In which case, the cursor moves progressively
|
||||||
|
* to the bottom or top of the list.
|
||||||
|
*/
|
||||||
|
const spaceUnderActive = renderedItems
|
||||||
|
.slice(active)
|
||||||
|
.reduce((acc, item) => acc + item.length, 0);
|
||||||
|
pointer =
|
||||||
|
spaceUnderActive < pageSize - middle
|
||||||
|
? // If the active item is near the end of the list, progressively move the cursor towards the end.
|
||||||
|
pageSize - spaceUnderActive
|
||||||
|
: // Otherwise, progressively move the pointer to the middle of the list.
|
||||||
|
Math.min(defaultPointerPosition, middle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Save state for the next render
|
||||||
|
state.current.lastPointer = pointer;
|
||||||
|
state.current.lastActive = active;
|
||||||
|
return pointer;
|
||||||
|
}
|
||||||
|
export function usePagination({ items, active, renderItem, pageSize, loop = true, }) {
|
||||||
|
const width = readlineWidth();
|
||||||
|
const bound = (num) => ((num % items.length) + items.length) % items.length;
|
||||||
|
const renderedItems = items.map((item, index) => {
|
||||||
|
if (item == null)
|
||||||
|
return [];
|
||||||
|
return breakLines(renderItem({ item, index, isActive: index === active }), width).split('\n');
|
||||||
|
});
|
||||||
|
const renderedLength = renderedItems.reduce((acc, item) => acc + item.length, 0);
|
||||||
|
const renderItemAtIndex = (index) => renderedItems[index] ?? [];
|
||||||
|
const pointer = usePointerPosition({ active, renderedItems, pageSize, loop });
|
||||||
|
// Render the active item to decide the position.
|
||||||
|
// If the active item fits under the pointer, we render it there.
|
||||||
|
// Otherwise, we need to render it to fit at the bottom of the page; moving the pointer up.
|
||||||
|
const activeItem = renderItemAtIndex(active).slice(0, pageSize);
|
||||||
|
const activeItemPosition = pointer + activeItem.length <= pageSize ? pointer : pageSize - activeItem.length;
|
||||||
|
// Create an array of lines for the page, and add the lines of the active item into the page
|
||||||
|
const pageBuffer = Array.from({ length: pageSize });
|
||||||
|
pageBuffer.splice(activeItemPosition, activeItem.length, ...activeItem);
|
||||||
|
// Store to prevent rendering the same item twice
|
||||||
|
const itemVisited = new Set([active]);
|
||||||
|
// Fill the page under the active item
|
||||||
|
let bufferPointer = activeItemPosition + activeItem.length;
|
||||||
|
let itemPointer = bound(active + 1);
|
||||||
|
while (bufferPointer < pageSize &&
|
||||||
|
!itemVisited.has(itemPointer) &&
|
||||||
|
(loop && renderedLength > pageSize ? itemPointer !== active : itemPointer > active)) {
|
||||||
|
const lines = renderItemAtIndex(itemPointer);
|
||||||
|
const linesToAdd = lines.slice(0, pageSize - bufferPointer);
|
||||||
|
pageBuffer.splice(bufferPointer, linesToAdd.length, ...linesToAdd);
|
||||||
|
// Move pointers for next iteration
|
||||||
|
itemVisited.add(itemPointer);
|
||||||
|
bufferPointer += linesToAdd.length;
|
||||||
|
itemPointer = bound(itemPointer + 1);
|
||||||
|
}
|
||||||
|
// Fill the page over the active item
|
||||||
|
bufferPointer = activeItemPosition - 1;
|
||||||
|
itemPointer = bound(active - 1);
|
||||||
|
while (bufferPointer >= 0 &&
|
||||||
|
!itemVisited.has(itemPointer) &&
|
||||||
|
(loop && renderedLength > pageSize ? itemPointer !== active : itemPointer < active)) {
|
||||||
|
const lines = renderItemAtIndex(itemPointer);
|
||||||
|
const linesToAdd = lines.slice(Math.max(0, lines.length - bufferPointer - 1));
|
||||||
|
pageBuffer.splice(bufferPointer - linesToAdd.length + 1, linesToAdd.length, ...linesToAdd);
|
||||||
|
// Move pointers for next iteration
|
||||||
|
itemVisited.add(itemPointer);
|
||||||
|
bufferPointer -= linesToAdd.length;
|
||||||
|
itemPointer = bound(itemPointer - 1);
|
||||||
|
}
|
||||||
|
return pageBuffer.filter((line) => typeof line === 'string').join('\n');
|
||||||
|
}
|
||||||
7
node_modules/@inquirer/core/dist/lib/promise-polyfill.d.ts
generated
vendored
Normal file
7
node_modules/@inquirer/core/dist/lib/promise-polyfill.d.ts
generated
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
export declare class PromisePolyfill<T> extends Promise<T> {
|
||||||
|
static withResolver<T>(): {
|
||||||
|
promise: Promise<T>;
|
||||||
|
resolve: (value: T) => void;
|
||||||
|
reject: (error: unknown) => void;
|
||||||
|
};
|
||||||
|
}
|
||||||
14
node_modules/@inquirer/core/dist/lib/promise-polyfill.js
generated
vendored
Normal file
14
node_modules/@inquirer/core/dist/lib/promise-polyfill.js
generated
vendored
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
// TODO: Remove this class once Node 22 becomes the minimum supported version.
|
||||||
|
export class PromisePolyfill extends Promise {
|
||||||
|
// Available starting from Node 22
|
||||||
|
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/withResolvers
|
||||||
|
static withResolver() {
|
||||||
|
let resolve;
|
||||||
|
let reject;
|
||||||
|
const promise = new Promise((res, rej) => {
|
||||||
|
resolve = res;
|
||||||
|
reject = rej;
|
||||||
|
});
|
||||||
|
return { promise, resolve: resolve, reject: reject };
|
||||||
|
}
|
||||||
|
}
|
||||||
14
node_modules/@inquirer/core/dist/lib/screen-manager.d.ts
generated
vendored
Normal file
14
node_modules/@inquirer/core/dist/lib/screen-manager.d.ts
generated
vendored
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import type { InquirerReadline } from '@inquirer/type';
|
||||||
|
export default class ScreenManager {
|
||||||
|
private height;
|
||||||
|
private extraLinesUnderPrompt;
|
||||||
|
private cursorPos;
|
||||||
|
private readonly rl;
|
||||||
|
constructor(rl: InquirerReadline);
|
||||||
|
write(content: string): void;
|
||||||
|
render(content: string, bottomContent?: string): void;
|
||||||
|
checkCursorPos(): void;
|
||||||
|
done({ clearContent }: {
|
||||||
|
clearContent: boolean;
|
||||||
|
}): void;
|
||||||
|
}
|
||||||
79
node_modules/@inquirer/core/dist/lib/screen-manager.js
generated
vendored
Normal file
79
node_modules/@inquirer/core/dist/lib/screen-manager.js
generated
vendored
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
import { stripVTControlCharacters } from 'node:util';
|
||||||
|
import { breakLines, readlineWidth } from "./utils.js";
|
||||||
|
import { cursorDown, cursorUp, cursorTo, cursorShow, eraseLines } from '@inquirer/ansi';
|
||||||
|
const height = (content) => content.split('\n').length;
|
||||||
|
const lastLine = (content) => content.split('\n').pop() ?? '';
|
||||||
|
export default class ScreenManager {
|
||||||
|
// These variables are keeping information to allow correct prompt re-rendering
|
||||||
|
height = 0;
|
||||||
|
extraLinesUnderPrompt = 0;
|
||||||
|
cursorPos;
|
||||||
|
rl;
|
||||||
|
constructor(rl) {
|
||||||
|
this.rl = rl;
|
||||||
|
this.cursorPos = rl.getCursorPos();
|
||||||
|
}
|
||||||
|
write(content) {
|
||||||
|
this.rl.output.unmute();
|
||||||
|
this.rl.output.write(content);
|
||||||
|
this.rl.output.mute();
|
||||||
|
}
|
||||||
|
render(content, bottomContent = '') {
|
||||||
|
// Write message to screen and setPrompt to control backspace
|
||||||
|
const promptLine = lastLine(content);
|
||||||
|
const rawPromptLine = stripVTControlCharacters(promptLine);
|
||||||
|
// Remove the rl.line from our prompt. We can't rely on the content of
|
||||||
|
// rl.line (mainly because of the password prompt), so just rely on it's
|
||||||
|
// length.
|
||||||
|
let prompt = rawPromptLine;
|
||||||
|
if (this.rl.line.length > 0) {
|
||||||
|
prompt = prompt.slice(0, -this.rl.line.length);
|
||||||
|
}
|
||||||
|
this.rl.setPrompt(prompt);
|
||||||
|
// SetPrompt will change cursor position, now we can get correct value
|
||||||
|
this.cursorPos = this.rl.getCursorPos();
|
||||||
|
const width = readlineWidth();
|
||||||
|
content = breakLines(content, width);
|
||||||
|
bottomContent = breakLines(bottomContent, width);
|
||||||
|
// Manually insert an extra line if we're at the end of the line.
|
||||||
|
// This prevent the cursor from appearing at the beginning of the
|
||||||
|
// current line.
|
||||||
|
if (rawPromptLine.length % width === 0) {
|
||||||
|
content += '\n';
|
||||||
|
}
|
||||||
|
let output = content + (bottomContent ? '\n' + bottomContent : '');
|
||||||
|
/**
|
||||||
|
* Re-adjust the cursor at the correct position.
|
||||||
|
*/
|
||||||
|
// We need to consider parts of the prompt under the cursor as part of the bottom
|
||||||
|
// content in order to correctly cleanup and re-render.
|
||||||
|
const promptLineUpDiff = Math.floor(rawPromptLine.length / width) - this.cursorPos.rows;
|
||||||
|
const bottomContentHeight = promptLineUpDiff + (bottomContent ? height(bottomContent) : 0);
|
||||||
|
// Return cursor to the input position (on top of the bottomContent)
|
||||||
|
if (bottomContentHeight > 0)
|
||||||
|
output += cursorUp(bottomContentHeight);
|
||||||
|
// Return cursor to the initial left offset.
|
||||||
|
output += cursorTo(this.cursorPos.cols);
|
||||||
|
/**
|
||||||
|
* Render and store state for future re-rendering
|
||||||
|
*/
|
||||||
|
this.write(cursorDown(this.extraLinesUnderPrompt) + eraseLines(this.height) + output);
|
||||||
|
this.extraLinesUnderPrompt = bottomContentHeight;
|
||||||
|
this.height = height(output);
|
||||||
|
}
|
||||||
|
checkCursorPos() {
|
||||||
|
const cursorPos = this.rl.getCursorPos();
|
||||||
|
if (cursorPos.cols !== this.cursorPos.cols) {
|
||||||
|
this.write(cursorTo(cursorPos.cols));
|
||||||
|
this.cursorPos = cursorPos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
done({ clearContent }) {
|
||||||
|
this.rl.setPrompt('');
|
||||||
|
let output = cursorDown(this.extraLinesUnderPrompt);
|
||||||
|
output += clearContent ? eraseLines(this.height) : '\n';
|
||||||
|
output += cursorShow;
|
||||||
|
this.write(output);
|
||||||
|
this.rl.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
155
node_modules/@inquirer/core/dist/lib/theme.d.ts
generated
vendored
Normal file
155
node_modules/@inquirer/core/dist/lib/theme.d.ts
generated
vendored
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
import type { Prettify } from '@inquirer/type';
|
||||||
|
/**
|
||||||
|
* Union type representing the possible statuses of a prompt.
|
||||||
|
*
|
||||||
|
* - `'loading'`: The prompt is currently loading.
|
||||||
|
* - `'idle'`: The prompt is loaded and currently waiting for the user to
|
||||||
|
* submit an answer.
|
||||||
|
* - `'done'`: The user has submitted an answer and the prompt is finished.
|
||||||
|
* - `string`: Any other string: The prompt is in a custom state.
|
||||||
|
*/
|
||||||
|
export type Status = 'loading' | 'idle' | 'done' | (string & {});
|
||||||
|
type DefaultTheme = {
|
||||||
|
/**
|
||||||
|
* Prefix to prepend to the message. If a function is provided, it will be
|
||||||
|
* called with the current status of the prompt, and the return value will be
|
||||||
|
* used as the prefix.
|
||||||
|
*
|
||||||
|
* @remarks
|
||||||
|
* If `status === 'loading'`, this property is ignored and the spinner (styled
|
||||||
|
* by the `spinner` property) will be displayed instead.
|
||||||
|
*
|
||||||
|
* @defaultValue
|
||||||
|
* ```ts
|
||||||
|
* // import { styleText } from 'node:util';
|
||||||
|
* (status) => status === 'done' ? styleText('green', '✔') : styleText('blue', '?')
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
prefix: string | Prettify<Omit<Record<Status, string>, 'loading'>>;
|
||||||
|
/**
|
||||||
|
* Configuration for the spinner that is displayed when the prompt is in the
|
||||||
|
* `'loading'` state.
|
||||||
|
*
|
||||||
|
* We recommend the use of {@link https://github.com/sindresorhus/cli-spinners|cli-spinners} for a list of available spinners.
|
||||||
|
*/
|
||||||
|
spinner: {
|
||||||
|
/**
|
||||||
|
* The time interval between frames, in milliseconds.
|
||||||
|
*
|
||||||
|
* @defaultValue
|
||||||
|
* ```ts
|
||||||
|
* 80
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
interval: number;
|
||||||
|
/**
|
||||||
|
* A list of frames to show for the spinner.
|
||||||
|
*
|
||||||
|
* @defaultValue
|
||||||
|
* ```ts
|
||||||
|
* ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏']
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
frames: string[];
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Object containing functions to style different parts of the prompt.
|
||||||
|
*/
|
||||||
|
style: {
|
||||||
|
/**
|
||||||
|
* Style to apply to the user's answer once it has been submitted.
|
||||||
|
*
|
||||||
|
* @param text - The user's answer.
|
||||||
|
* @returns The styled answer.
|
||||||
|
*
|
||||||
|
* @defaultValue
|
||||||
|
* ```ts
|
||||||
|
* // import { styleText } from 'node:util';
|
||||||
|
* (text) => styleText('cyan', text)
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
answer: (text: string) => string;
|
||||||
|
/**
|
||||||
|
* Style to apply to the message displayed to the user.
|
||||||
|
*
|
||||||
|
* @param text - The message to style.
|
||||||
|
* @param status - The current status of the prompt.
|
||||||
|
* @returns The styled message.
|
||||||
|
*
|
||||||
|
* @defaultValue
|
||||||
|
* ```ts
|
||||||
|
* // import { styleText } from 'node:util';
|
||||||
|
* (text, status) => styleText('bold', text)
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
message: (text: string, status: Status) => string;
|
||||||
|
/**
|
||||||
|
* Style to apply to error messages.
|
||||||
|
*
|
||||||
|
* @param text - The error message.
|
||||||
|
* @returns The styled error message.
|
||||||
|
*
|
||||||
|
* @defaultValue
|
||||||
|
* ```ts
|
||||||
|
* // import { styleText } from 'node:util';
|
||||||
|
* (text) => styleText('red', `> ${text}`)
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
error: (text: string) => string;
|
||||||
|
/**
|
||||||
|
* Style to apply to the default answer when one is provided.
|
||||||
|
*
|
||||||
|
* @param text - The default answer.
|
||||||
|
* @returns The styled default answer.
|
||||||
|
*
|
||||||
|
* @defaultValue
|
||||||
|
* ```ts
|
||||||
|
* // import { styleText } from 'node:util';
|
||||||
|
* (text) => styleText('dim', `(${text})`)
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
defaultAnswer: (text: string) => string;
|
||||||
|
/**
|
||||||
|
* Style to apply to help text.
|
||||||
|
*
|
||||||
|
* @param text - The help text.
|
||||||
|
* @returns The styled help text.
|
||||||
|
*
|
||||||
|
* @defaultValue
|
||||||
|
* ```ts
|
||||||
|
* // import { styleText } from 'node:util';
|
||||||
|
* (text) => styleText('dim', text)
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
help: (text: string) => string;
|
||||||
|
/**
|
||||||
|
* Style to apply to highlighted text.
|
||||||
|
*
|
||||||
|
* @param text - The text to highlight.
|
||||||
|
* @returns The highlighted text.
|
||||||
|
*
|
||||||
|
* @defaultValue
|
||||||
|
* ```ts
|
||||||
|
* // import { styleText } from 'node:util';
|
||||||
|
* (text) => styleText('cyan', text)
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
highlight: (text: string) => string;
|
||||||
|
/**
|
||||||
|
* Style to apply to keyboard keys referred to in help texts.
|
||||||
|
*
|
||||||
|
* @param text - The key to style.
|
||||||
|
* @returns The styled key.
|
||||||
|
*
|
||||||
|
* @defaultValue
|
||||||
|
* ```ts
|
||||||
|
* // import { styleText } from 'node:util';
|
||||||
|
* (text) => styleText('cyan', styleText('bold', `<${text}>`))
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
key: (text: string) => string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
export type Theme<Extension extends object = object> = Prettify<Extension & DefaultTheme>;
|
||||||
|
export declare const defaultTheme: DefaultTheme;
|
||||||
|
export {};
|
||||||
21
node_modules/@inquirer/core/dist/lib/theme.js
generated
vendored
Normal file
21
node_modules/@inquirer/core/dist/lib/theme.js
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import { styleText } from 'node:util';
|
||||||
|
import figures from '@inquirer/figures';
|
||||||
|
export const defaultTheme = {
|
||||||
|
prefix: {
|
||||||
|
idle: styleText('blue', '?'),
|
||||||
|
done: styleText('green', figures.tick),
|
||||||
|
},
|
||||||
|
spinner: {
|
||||||
|
interval: 80,
|
||||||
|
frames: ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'].map((frame) => styleText('yellow', frame)),
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
answer: (text) => styleText('cyan', text),
|
||||||
|
message: (text) => styleText('bold', text),
|
||||||
|
error: (text) => styleText('red', `> ${text}`),
|
||||||
|
defaultAnswer: (text) => styleText('dim', `(${text})`),
|
||||||
|
help: (text) => styleText('dim', text),
|
||||||
|
highlight: (text) => styleText('cyan', text),
|
||||||
|
key: (text) => styleText('cyan', styleText('bold', `<${text}>`)),
|
||||||
|
},
|
||||||
|
};
|
||||||
2
node_modules/@inquirer/core/dist/lib/use-effect.d.ts
generated
vendored
Normal file
2
node_modules/@inquirer/core/dist/lib/use-effect.d.ts
generated
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
import type { InquirerReadline } from '@inquirer/type';
|
||||||
|
export declare function useEffect(cb: (rl: InquirerReadline) => void | (() => void), depArray: ReadonlyArray<unknown>): void;
|
||||||
11
node_modules/@inquirer/core/dist/lib/use-effect.js
generated
vendored
Normal file
11
node_modules/@inquirer/core/dist/lib/use-effect.js
generated
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import { withPointer, effectScheduler } from "./hook-engine.js";
|
||||||
|
export function useEffect(cb, depArray) {
|
||||||
|
withPointer((pointer) => {
|
||||||
|
const oldDeps = pointer.get();
|
||||||
|
const hasChanged = !Array.isArray(oldDeps) || depArray.some((dep, i) => !Object.is(dep, oldDeps[i]));
|
||||||
|
if (hasChanged) {
|
||||||
|
effectScheduler.queue(cb);
|
||||||
|
}
|
||||||
|
pointer.set(depArray);
|
||||||
|
});
|
||||||
|
}
|
||||||
3
node_modules/@inquirer/core/dist/lib/use-keypress.d.ts
generated
vendored
Normal file
3
node_modules/@inquirer/core/dist/lib/use-keypress.d.ts
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import { type InquirerReadline } from '@inquirer/type';
|
||||||
|
import { type KeypressEvent } from './key.ts';
|
||||||
|
export declare function useKeypress(userHandler: (event: KeypressEvent, rl: InquirerReadline) => void | Promise<void>): void;
|
||||||
20
node_modules/@inquirer/core/dist/lib/use-keypress.js
generated
vendored
Normal file
20
node_modules/@inquirer/core/dist/lib/use-keypress.js
generated
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import { useRef } from "./use-ref.js";
|
||||||
|
import { useEffect } from "./use-effect.js";
|
||||||
|
import { withUpdates } from "./hook-engine.js";
|
||||||
|
export function useKeypress(userHandler) {
|
||||||
|
const signal = useRef(userHandler);
|
||||||
|
signal.current = userHandler;
|
||||||
|
useEffect((rl) => {
|
||||||
|
let ignore = false;
|
||||||
|
const handler = withUpdates((_input, event) => {
|
||||||
|
if (ignore)
|
||||||
|
return;
|
||||||
|
void signal.current(event, rl);
|
||||||
|
});
|
||||||
|
rl.input.on('keypress', handler);
|
||||||
|
return () => {
|
||||||
|
ignore = true;
|
||||||
|
rl.input.removeListener('keypress', handler);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
}
|
||||||
1
node_modules/@inquirer/core/dist/lib/use-memo.d.ts
generated
vendored
Normal file
1
node_modules/@inquirer/core/dist/lib/use-memo.d.ts
generated
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
export declare function useMemo<Value>(fn: () => Value, dependencies: ReadonlyArray<unknown>): Value;
|
||||||
14
node_modules/@inquirer/core/dist/lib/use-memo.js
generated
vendored
Normal file
14
node_modules/@inquirer/core/dist/lib/use-memo.js
generated
vendored
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { withPointer } from "./hook-engine.js";
|
||||||
|
export function useMemo(fn, dependencies) {
|
||||||
|
return withPointer((pointer) => {
|
||||||
|
const prev = pointer.get();
|
||||||
|
if (!prev ||
|
||||||
|
prev.dependencies.length !== dependencies.length ||
|
||||||
|
prev.dependencies.some((dep, i) => dep !== dependencies[i])) {
|
||||||
|
const value = fn();
|
||||||
|
pointer.set({ value, dependencies });
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
return prev.value;
|
||||||
|
});
|
||||||
|
}
|
||||||
5
node_modules/@inquirer/core/dist/lib/use-prefix.d.ts
generated
vendored
Normal file
5
node_modules/@inquirer/core/dist/lib/use-prefix.d.ts
generated
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import type { Theme, Status } from './theme.ts';
|
||||||
|
export declare function usePrefix({ status, theme, }: {
|
||||||
|
status?: Status;
|
||||||
|
theme?: Theme;
|
||||||
|
}): string;
|
||||||
35
node_modules/@inquirer/core/dist/lib/use-prefix.js
generated
vendored
Normal file
35
node_modules/@inquirer/core/dist/lib/use-prefix.js
generated
vendored
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import { useState } from "./use-state.js";
|
||||||
|
import { useEffect } from "./use-effect.js";
|
||||||
|
import { makeTheme } from "./make-theme.js";
|
||||||
|
export function usePrefix({ status = 'idle', theme, }) {
|
||||||
|
const [showLoader, setShowLoader] = useState(false);
|
||||||
|
const [tick, setTick] = useState(0);
|
||||||
|
const { prefix, spinner } = makeTheme(theme);
|
||||||
|
useEffect(() => {
|
||||||
|
if (status === 'loading') {
|
||||||
|
let tickInterval;
|
||||||
|
let inc = -1;
|
||||||
|
// Delay displaying spinner by 300ms, to avoid flickering
|
||||||
|
const delayTimeout = setTimeout(() => {
|
||||||
|
setShowLoader(true);
|
||||||
|
tickInterval = setInterval(() => {
|
||||||
|
inc = inc + 1;
|
||||||
|
setTick(inc % spinner.frames.length);
|
||||||
|
}, spinner.interval);
|
||||||
|
}, 300);
|
||||||
|
return () => {
|
||||||
|
clearTimeout(delayTimeout);
|
||||||
|
clearInterval(tickInterval);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
setShowLoader(false);
|
||||||
|
}
|
||||||
|
}, [status]);
|
||||||
|
if (showLoader) {
|
||||||
|
return spinner.frames[tick];
|
||||||
|
}
|
||||||
|
// There's a delay before we show the loader. So we want to ignore `loading` here, and pass idle instead.
|
||||||
|
const iconName = status === 'loading' ? 'idle' : status;
|
||||||
|
return typeof prefix === 'string' ? prefix : (prefix[iconName] ?? prefix['idle']);
|
||||||
|
}
|
||||||
6
node_modules/@inquirer/core/dist/lib/use-ref.d.ts
generated
vendored
Normal file
6
node_modules/@inquirer/core/dist/lib/use-ref.d.ts
generated
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
export declare function useRef<Value>(val: Value): {
|
||||||
|
current: Value;
|
||||||
|
};
|
||||||
|
export declare function useRef<Value>(val?: Value): {
|
||||||
|
current: Value | undefined;
|
||||||
|
};
|
||||||
4
node_modules/@inquirer/core/dist/lib/use-ref.js
generated
vendored
Normal file
4
node_modules/@inquirer/core/dist/lib/use-ref.js
generated
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
import { useState } from "./use-state.js";
|
||||||
|
export function useRef(val) {
|
||||||
|
return useState({ current: val })[0];
|
||||||
|
}
|
||||||
4
node_modules/@inquirer/core/dist/lib/use-state.d.ts
generated
vendored
Normal file
4
node_modules/@inquirer/core/dist/lib/use-state.d.ts
generated
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
type NotFunction<T> = T extends (...args: never) => unknown ? never : T;
|
||||||
|
export declare function useState<Value>(defaultValue: NotFunction<Value> | (() => Value)): [Value, (newValue: Value) => void];
|
||||||
|
export declare function useState<Value>(defaultValue?: NotFunction<Value> | (() => Value)): [Value | undefined, (newValue?: Value) => void];
|
||||||
|
export {};
|
||||||
20
node_modules/@inquirer/core/dist/lib/use-state.js
generated
vendored
Normal file
20
node_modules/@inquirer/core/dist/lib/use-state.js
generated
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import { AsyncResource } from 'node:async_hooks';
|
||||||
|
import { withPointer, handleChange } from "./hook-engine.js";
|
||||||
|
export function useState(defaultValue) {
|
||||||
|
return withPointer((pointer) => {
|
||||||
|
const setState = AsyncResource.bind(function setState(newValue) {
|
||||||
|
// Noop if the value is still the same.
|
||||||
|
if (pointer.get() !== newValue) {
|
||||||
|
pointer.set(newValue);
|
||||||
|
// Trigger re-render
|
||||||
|
handleChange();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (pointer.initialized) {
|
||||||
|
return [pointer.get(), setState];
|
||||||
|
}
|
||||||
|
const value = typeof defaultValue === 'function' ? defaultValue() : defaultValue;
|
||||||
|
pointer.set(value);
|
||||||
|
return [value, setState];
|
||||||
|
});
|
||||||
|
}
|
||||||
13
node_modules/@inquirer/core/dist/lib/utils.d.ts
generated
vendored
Normal file
13
node_modules/@inquirer/core/dist/lib/utils.d.ts
generated
vendored
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
/**
|
||||||
|
* Force line returns at specific width. This function is ANSI code friendly and it'll
|
||||||
|
* ignore invisible codes during width calculation.
|
||||||
|
* @param {string} content
|
||||||
|
* @param {number} width
|
||||||
|
* @return {string}
|
||||||
|
*/
|
||||||
|
export declare function breakLines(content: string, width: number): string;
|
||||||
|
/**
|
||||||
|
* Returns the width of the active readline, or 80 as default value.
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
export declare function readlineWidth(): number;
|
||||||
25
node_modules/@inquirer/core/dist/lib/utils.js
generated
vendored
Normal file
25
node_modules/@inquirer/core/dist/lib/utils.js
generated
vendored
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import cliWidth from 'cli-width';
|
||||||
|
import { wrapAnsi } from 'fast-wrap-ansi';
|
||||||
|
import { readline } from "./hook-engine.js";
|
||||||
|
/**
|
||||||
|
* Force line returns at specific width. This function is ANSI code friendly and it'll
|
||||||
|
* ignore invisible codes during width calculation.
|
||||||
|
* @param {string} content
|
||||||
|
* @param {number} width
|
||||||
|
* @return {string}
|
||||||
|
*/
|
||||||
|
export function breakLines(content, width) {
|
||||||
|
return content
|
||||||
|
.split('\n')
|
||||||
|
.flatMap((line) => wrapAnsi(line, width, { trim: false, hard: true })
|
||||||
|
.split('\n')
|
||||||
|
.map((str) => str.trimEnd()))
|
||||||
|
.join('\n');
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Returns the width of the active readline, or 80 as default value.
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
export function readlineWidth() {
|
||||||
|
return cliWidth({ defaultWidth: 80, output: readline().output });
|
||||||
|
}
|
||||||
98
node_modules/@inquirer/core/package.json
generated
vendored
Normal file
98
node_modules/@inquirer/core/package.json
generated
vendored
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
{
|
||||||
|
"name": "@inquirer/core",
|
||||||
|
"version": "11.1.5",
|
||||||
|
"description": "Core Inquirer prompt API",
|
||||||
|
"keywords": [
|
||||||
|
"answer",
|
||||||
|
"answers",
|
||||||
|
"ask",
|
||||||
|
"base",
|
||||||
|
"cli",
|
||||||
|
"command",
|
||||||
|
"command-line",
|
||||||
|
"confirm",
|
||||||
|
"enquirer",
|
||||||
|
"generate",
|
||||||
|
"generator",
|
||||||
|
"hyper",
|
||||||
|
"input",
|
||||||
|
"inquire",
|
||||||
|
"inquirer",
|
||||||
|
"interface",
|
||||||
|
"iterm",
|
||||||
|
"javascript",
|
||||||
|
"menu",
|
||||||
|
"node",
|
||||||
|
"nodejs",
|
||||||
|
"prompt",
|
||||||
|
"promptly",
|
||||||
|
"prompts",
|
||||||
|
"question",
|
||||||
|
"readline",
|
||||||
|
"scaffold",
|
||||||
|
"scaffolder",
|
||||||
|
"scaffolding",
|
||||||
|
"stdin",
|
||||||
|
"stdout",
|
||||||
|
"terminal",
|
||||||
|
"tty",
|
||||||
|
"ui",
|
||||||
|
"yeoman",
|
||||||
|
"yo",
|
||||||
|
"zsh"
|
||||||
|
],
|
||||||
|
"homepage": "https://github.com/SBoudrias/Inquirer.js/blob/main/packages/core/README.md",
|
||||||
|
"license": "MIT",
|
||||||
|
"author": "Simon Boudrias <admin@simonboudrias.com>",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/SBoudrias/Inquirer.js.git"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"dist"
|
||||||
|
],
|
||||||
|
"type": "module",
|
||||||
|
"sideEffects": false,
|
||||||
|
"exports": {
|
||||||
|
".": {
|
||||||
|
"types": "./dist/index.d.ts",
|
||||||
|
"default": "./dist/index.js"
|
||||||
|
},
|
||||||
|
"./package.json": "./package.json"
|
||||||
|
},
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"tsc": "tsc"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@inquirer/ansi": "^2.0.3",
|
||||||
|
"@inquirer/figures": "^2.0.3",
|
||||||
|
"@inquirer/type": "^4.0.3",
|
||||||
|
"cli-width": "^4.1.0",
|
||||||
|
"fast-wrap-ansi": "^0.2.0",
|
||||||
|
"mute-stream": "^3.0.0",
|
||||||
|
"signal-exit": "^4.1.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@inquirer/testing": "^3.3.0",
|
||||||
|
"@types/mute-stream": "^0.0.4",
|
||||||
|
"@types/node": "^25.0.2",
|
||||||
|
"typescript": "^5.9.3"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/node": ">=18"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/node": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0"
|
||||||
|
},
|
||||||
|
"main": "./dist/index.js",
|
||||||
|
"types": "./dist/index.d.ts",
|
||||||
|
"gitHead": "526eca2e64853510821ffd457561840ec0cbfb93"
|
||||||
|
}
|
||||||
22
node_modules/@inquirer/figures/LICENSE
generated
vendored
Normal file
22
node_modules/@inquirer/figures/LICENSE
generated
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
Copyright (c) 2025 Simon Boudrias
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person
|
||||||
|
obtaining a copy of this software and associated documentation
|
||||||
|
files (the "Software"), to deal in the Software without
|
||||||
|
restriction, including without limitation the rights to use,
|
||||||
|
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the
|
||||||
|
Software is furnished to do so, subject to the following
|
||||||
|
conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||||
|
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||||
|
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||||
|
OTHER DEALINGS IN THE SOFTWARE.
|
||||||
275
node_modules/@inquirer/figures/dist/index.d.ts
generated
vendored
Normal file
275
node_modules/@inquirer/figures/dist/index.d.ts
generated
vendored
Normal file
@ -0,0 +1,275 @@
|
|||||||
|
declare const common: {
|
||||||
|
circleQuestionMark: string;
|
||||||
|
questionMarkPrefix: string;
|
||||||
|
square: string;
|
||||||
|
squareDarkShade: string;
|
||||||
|
squareMediumShade: string;
|
||||||
|
squareLightShade: string;
|
||||||
|
squareTop: string;
|
||||||
|
squareBottom: string;
|
||||||
|
squareLeft: string;
|
||||||
|
squareRight: string;
|
||||||
|
squareCenter: string;
|
||||||
|
bullet: string;
|
||||||
|
dot: string;
|
||||||
|
ellipsis: string;
|
||||||
|
pointerSmall: string;
|
||||||
|
triangleUp: string;
|
||||||
|
triangleUpSmall: string;
|
||||||
|
triangleDown: string;
|
||||||
|
triangleDownSmall: string;
|
||||||
|
triangleLeftSmall: string;
|
||||||
|
triangleRightSmall: string;
|
||||||
|
home: string;
|
||||||
|
heart: string;
|
||||||
|
musicNote: string;
|
||||||
|
musicNoteBeamed: string;
|
||||||
|
arrowUp: string;
|
||||||
|
arrowDown: string;
|
||||||
|
arrowLeft: string;
|
||||||
|
arrowRight: string;
|
||||||
|
arrowLeftRight: string;
|
||||||
|
arrowUpDown: string;
|
||||||
|
almostEqual: string;
|
||||||
|
notEqual: string;
|
||||||
|
lessOrEqual: string;
|
||||||
|
greaterOrEqual: string;
|
||||||
|
identical: string;
|
||||||
|
infinity: string;
|
||||||
|
subscriptZero: string;
|
||||||
|
subscriptOne: string;
|
||||||
|
subscriptTwo: string;
|
||||||
|
subscriptThree: string;
|
||||||
|
subscriptFour: string;
|
||||||
|
subscriptFive: string;
|
||||||
|
subscriptSix: string;
|
||||||
|
subscriptSeven: string;
|
||||||
|
subscriptEight: string;
|
||||||
|
subscriptNine: string;
|
||||||
|
oneHalf: string;
|
||||||
|
oneThird: string;
|
||||||
|
oneQuarter: string;
|
||||||
|
oneFifth: string;
|
||||||
|
oneSixth: string;
|
||||||
|
oneEighth: string;
|
||||||
|
twoThirds: string;
|
||||||
|
twoFifths: string;
|
||||||
|
threeQuarters: string;
|
||||||
|
threeFifths: string;
|
||||||
|
threeEighths: string;
|
||||||
|
fourFifths: string;
|
||||||
|
fiveSixths: string;
|
||||||
|
fiveEighths: string;
|
||||||
|
sevenEighths: string;
|
||||||
|
line: string;
|
||||||
|
lineBold: string;
|
||||||
|
lineDouble: string;
|
||||||
|
lineDashed0: string;
|
||||||
|
lineDashed1: string;
|
||||||
|
lineDashed2: string;
|
||||||
|
lineDashed3: string;
|
||||||
|
lineDashed4: string;
|
||||||
|
lineDashed5: string;
|
||||||
|
lineDashed6: string;
|
||||||
|
lineDashed7: string;
|
||||||
|
lineDashed8: string;
|
||||||
|
lineDashed9: string;
|
||||||
|
lineDashed10: string;
|
||||||
|
lineDashed11: string;
|
||||||
|
lineDashed12: string;
|
||||||
|
lineDashed13: string;
|
||||||
|
lineDashed14: string;
|
||||||
|
lineDashed15: string;
|
||||||
|
lineVertical: string;
|
||||||
|
lineVerticalBold: string;
|
||||||
|
lineVerticalDouble: string;
|
||||||
|
lineVerticalDashed0: string;
|
||||||
|
lineVerticalDashed1: string;
|
||||||
|
lineVerticalDashed2: string;
|
||||||
|
lineVerticalDashed3: string;
|
||||||
|
lineVerticalDashed4: string;
|
||||||
|
lineVerticalDashed5: string;
|
||||||
|
lineVerticalDashed6: string;
|
||||||
|
lineVerticalDashed7: string;
|
||||||
|
lineVerticalDashed8: string;
|
||||||
|
lineVerticalDashed9: string;
|
||||||
|
lineVerticalDashed10: string;
|
||||||
|
lineVerticalDashed11: string;
|
||||||
|
lineDownLeft: string;
|
||||||
|
lineDownLeftArc: string;
|
||||||
|
lineDownBoldLeftBold: string;
|
||||||
|
lineDownBoldLeft: string;
|
||||||
|
lineDownLeftBold: string;
|
||||||
|
lineDownDoubleLeftDouble: string;
|
||||||
|
lineDownDoubleLeft: string;
|
||||||
|
lineDownLeftDouble: string;
|
||||||
|
lineDownRight: string;
|
||||||
|
lineDownRightArc: string;
|
||||||
|
lineDownBoldRightBold: string;
|
||||||
|
lineDownBoldRight: string;
|
||||||
|
lineDownRightBold: string;
|
||||||
|
lineDownDoubleRightDouble: string;
|
||||||
|
lineDownDoubleRight: string;
|
||||||
|
lineDownRightDouble: string;
|
||||||
|
lineUpLeft: string;
|
||||||
|
lineUpLeftArc: string;
|
||||||
|
lineUpBoldLeftBold: string;
|
||||||
|
lineUpBoldLeft: string;
|
||||||
|
lineUpLeftBold: string;
|
||||||
|
lineUpDoubleLeftDouble: string;
|
||||||
|
lineUpDoubleLeft: string;
|
||||||
|
lineUpLeftDouble: string;
|
||||||
|
lineUpRight: string;
|
||||||
|
lineUpRightArc: string;
|
||||||
|
lineUpBoldRightBold: string;
|
||||||
|
lineUpBoldRight: string;
|
||||||
|
lineUpRightBold: string;
|
||||||
|
lineUpDoubleRightDouble: string;
|
||||||
|
lineUpDoubleRight: string;
|
||||||
|
lineUpRightDouble: string;
|
||||||
|
lineUpDownLeft: string;
|
||||||
|
lineUpBoldDownBoldLeftBold: string;
|
||||||
|
lineUpBoldDownBoldLeft: string;
|
||||||
|
lineUpDownLeftBold: string;
|
||||||
|
lineUpBoldDownLeftBold: string;
|
||||||
|
lineUpDownBoldLeftBold: string;
|
||||||
|
lineUpDownBoldLeft: string;
|
||||||
|
lineUpBoldDownLeft: string;
|
||||||
|
lineUpDoubleDownDoubleLeftDouble: string;
|
||||||
|
lineUpDoubleDownDoubleLeft: string;
|
||||||
|
lineUpDownLeftDouble: string;
|
||||||
|
lineUpDownRight: string;
|
||||||
|
lineUpBoldDownBoldRightBold: string;
|
||||||
|
lineUpBoldDownBoldRight: string;
|
||||||
|
lineUpDownRightBold: string;
|
||||||
|
lineUpBoldDownRightBold: string;
|
||||||
|
lineUpDownBoldRightBold: string;
|
||||||
|
lineUpDownBoldRight: string;
|
||||||
|
lineUpBoldDownRight: string;
|
||||||
|
lineUpDoubleDownDoubleRightDouble: string;
|
||||||
|
lineUpDoubleDownDoubleRight: string;
|
||||||
|
lineUpDownRightDouble: string;
|
||||||
|
lineDownLeftRight: string;
|
||||||
|
lineDownBoldLeftBoldRightBold: string;
|
||||||
|
lineDownLeftBoldRightBold: string;
|
||||||
|
lineDownBoldLeftRight: string;
|
||||||
|
lineDownBoldLeftBoldRight: string;
|
||||||
|
lineDownBoldLeftRightBold: string;
|
||||||
|
lineDownLeftRightBold: string;
|
||||||
|
lineDownLeftBoldRight: string;
|
||||||
|
lineDownDoubleLeftDoubleRightDouble: string;
|
||||||
|
lineDownDoubleLeftRight: string;
|
||||||
|
lineDownLeftDoubleRightDouble: string;
|
||||||
|
lineUpLeftRight: string;
|
||||||
|
lineUpBoldLeftBoldRightBold: string;
|
||||||
|
lineUpLeftBoldRightBold: string;
|
||||||
|
lineUpBoldLeftRight: string;
|
||||||
|
lineUpBoldLeftBoldRight: string;
|
||||||
|
lineUpBoldLeftRightBold: string;
|
||||||
|
lineUpLeftRightBold: string;
|
||||||
|
lineUpLeftBoldRight: string;
|
||||||
|
lineUpDoubleLeftDoubleRightDouble: string;
|
||||||
|
lineUpDoubleLeftRight: string;
|
||||||
|
lineUpLeftDoubleRightDouble: string;
|
||||||
|
lineUpDownLeftRight: string;
|
||||||
|
lineUpBoldDownBoldLeftBoldRightBold: string;
|
||||||
|
lineUpDownBoldLeftBoldRightBold: string;
|
||||||
|
lineUpBoldDownLeftBoldRightBold: string;
|
||||||
|
lineUpBoldDownBoldLeftRightBold: string;
|
||||||
|
lineUpBoldDownBoldLeftBoldRight: string;
|
||||||
|
lineUpBoldDownLeftRight: string;
|
||||||
|
lineUpDownBoldLeftRight: string;
|
||||||
|
lineUpDownLeftBoldRight: string;
|
||||||
|
lineUpDownLeftRightBold: string;
|
||||||
|
lineUpBoldDownBoldLeftRight: string;
|
||||||
|
lineUpDownLeftBoldRightBold: string;
|
||||||
|
lineUpBoldDownLeftBoldRight: string;
|
||||||
|
lineUpBoldDownLeftRightBold: string;
|
||||||
|
lineUpDownBoldLeftBoldRight: string;
|
||||||
|
lineUpDownBoldLeftRightBold: string;
|
||||||
|
lineUpDoubleDownDoubleLeftDoubleRightDouble: string;
|
||||||
|
lineUpDoubleDownDoubleLeftRight: string;
|
||||||
|
lineUpDownLeftDoubleRightDouble: string;
|
||||||
|
lineCross: string;
|
||||||
|
lineBackslash: string;
|
||||||
|
lineSlash: string;
|
||||||
|
};
|
||||||
|
declare const specialMainSymbols: {
|
||||||
|
tick: string;
|
||||||
|
info: string;
|
||||||
|
warning: string;
|
||||||
|
cross: string;
|
||||||
|
squareSmall: string;
|
||||||
|
squareSmallFilled: string;
|
||||||
|
circle: string;
|
||||||
|
circleFilled: string;
|
||||||
|
circleDotted: string;
|
||||||
|
circleDouble: string;
|
||||||
|
circleCircle: string;
|
||||||
|
circleCross: string;
|
||||||
|
circlePipe: string;
|
||||||
|
radioOn: string;
|
||||||
|
radioOff: string;
|
||||||
|
checkboxOn: string;
|
||||||
|
checkboxOff: string;
|
||||||
|
checkboxCircleOn: string;
|
||||||
|
checkboxCircleOff: string;
|
||||||
|
pointer: string;
|
||||||
|
triangleUpOutline: string;
|
||||||
|
triangleLeft: string;
|
||||||
|
triangleRight: string;
|
||||||
|
lozenge: string;
|
||||||
|
lozengeOutline: string;
|
||||||
|
hamburger: string;
|
||||||
|
smiley: string;
|
||||||
|
mustache: string;
|
||||||
|
star: string;
|
||||||
|
play: string;
|
||||||
|
nodejs: string;
|
||||||
|
oneSeventh: string;
|
||||||
|
oneNinth: string;
|
||||||
|
oneTenth: string;
|
||||||
|
};
|
||||||
|
declare const specialFallbackSymbols: {
|
||||||
|
tick: string;
|
||||||
|
info: string;
|
||||||
|
warning: string;
|
||||||
|
cross: string;
|
||||||
|
squareSmall: string;
|
||||||
|
squareSmallFilled: string;
|
||||||
|
circle: string;
|
||||||
|
circleFilled: string;
|
||||||
|
circleDotted: string;
|
||||||
|
circleDouble: string;
|
||||||
|
circleCircle: string;
|
||||||
|
circleCross: string;
|
||||||
|
circlePipe: string;
|
||||||
|
radioOn: string;
|
||||||
|
radioOff: string;
|
||||||
|
checkboxOn: string;
|
||||||
|
checkboxOff: string;
|
||||||
|
checkboxCircleOn: string;
|
||||||
|
checkboxCircleOff: string;
|
||||||
|
pointer: string;
|
||||||
|
triangleUpOutline: string;
|
||||||
|
triangleLeft: string;
|
||||||
|
triangleRight: string;
|
||||||
|
lozenge: string;
|
||||||
|
lozengeOutline: string;
|
||||||
|
hamburger: string;
|
||||||
|
smiley: string;
|
||||||
|
mustache: string;
|
||||||
|
star: string;
|
||||||
|
play: string;
|
||||||
|
nodejs: string;
|
||||||
|
oneSeventh: string;
|
||||||
|
oneNinth: string;
|
||||||
|
oneTenth: string;
|
||||||
|
};
|
||||||
|
export declare const mainSymbols: typeof common & typeof specialMainSymbols;
|
||||||
|
export declare const fallbackSymbols: (typeof common & typeof specialFallbackSymbols) & Record<string, string>;
|
||||||
|
declare const figures: typeof mainSymbols | typeof fallbackSymbols;
|
||||||
|
export default figures;
|
||||||
|
export declare const replaceSymbols: (string: string, { useFallback }?: {
|
||||||
|
useFallback?: boolean;
|
||||||
|
}) => string;
|
||||||
314
node_modules/@inquirer/figures/dist/index.js
generated
vendored
Normal file
314
node_modules/@inquirer/figures/dist/index.js
generated
vendored
Normal file
@ -0,0 +1,314 @@
|
|||||||
|
// process.env dot-notation access prints:
|
||||||
|
// Property 'TERM' comes from an index signature, so it must be accessed with ['TERM'].ts(4111)
|
||||||
|
/* eslint dot-notation: ["off"] */
|
||||||
|
import process from 'node:process';
|
||||||
|
// Ported from is-unicode-supported
|
||||||
|
function isUnicodeSupported() {
|
||||||
|
if (process.platform !== 'win32') {
|
||||||
|
return process.env['TERM'] !== 'linux'; // Linux console (kernel)
|
||||||
|
}
|
||||||
|
return (Boolean(process.env['WT_SESSION']) || // Windows Terminal
|
||||||
|
Boolean(process.env['TERMINUS_SUBLIME']) || // Terminus (<0.2.27)
|
||||||
|
process.env['ConEmuTask'] === '{cmd::Cmder}' || // ConEmu and cmder
|
||||||
|
process.env['TERM_PROGRAM'] === 'Terminus-Sublime' ||
|
||||||
|
process.env['TERM_PROGRAM'] === 'vscode' ||
|
||||||
|
process.env['TERM'] === 'xterm-256color' ||
|
||||||
|
process.env['TERM'] === 'alacritty' ||
|
||||||
|
process.env['TERMINAL_EMULATOR'] === 'JetBrains-JediTerm');
|
||||||
|
}
|
||||||
|
// Ported from figures
|
||||||
|
const common = {
|
||||||
|
circleQuestionMark: '(?)',
|
||||||
|
questionMarkPrefix: '(?)',
|
||||||
|
square: '█',
|
||||||
|
squareDarkShade: '▓',
|
||||||
|
squareMediumShade: '▒',
|
||||||
|
squareLightShade: '░',
|
||||||
|
squareTop: '▀',
|
||||||
|
squareBottom: '▄',
|
||||||
|
squareLeft: '▌',
|
||||||
|
squareRight: '▐',
|
||||||
|
squareCenter: '■',
|
||||||
|
bullet: '●',
|
||||||
|
dot: '․',
|
||||||
|
ellipsis: '…',
|
||||||
|
pointerSmall: '›',
|
||||||
|
triangleUp: '▲',
|
||||||
|
triangleUpSmall: '▴',
|
||||||
|
triangleDown: '▼',
|
||||||
|
triangleDownSmall: '▾',
|
||||||
|
triangleLeftSmall: '◂',
|
||||||
|
triangleRightSmall: '▸',
|
||||||
|
home: '⌂',
|
||||||
|
heart: '♥',
|
||||||
|
musicNote: '♪',
|
||||||
|
musicNoteBeamed: '♫',
|
||||||
|
arrowUp: '↑',
|
||||||
|
arrowDown: '↓',
|
||||||
|
arrowLeft: '←',
|
||||||
|
arrowRight: '→',
|
||||||
|
arrowLeftRight: '↔',
|
||||||
|
arrowUpDown: '↕',
|
||||||
|
almostEqual: '≈',
|
||||||
|
notEqual: '≠',
|
||||||
|
lessOrEqual: '≤',
|
||||||
|
greaterOrEqual: '≥',
|
||||||
|
identical: '≡',
|
||||||
|
infinity: '∞',
|
||||||
|
subscriptZero: '₀',
|
||||||
|
subscriptOne: '₁',
|
||||||
|
subscriptTwo: '₂',
|
||||||
|
subscriptThree: '₃',
|
||||||
|
subscriptFour: '₄',
|
||||||
|
subscriptFive: '₅',
|
||||||
|
subscriptSix: '₆',
|
||||||
|
subscriptSeven: '₇',
|
||||||
|
subscriptEight: '₈',
|
||||||
|
subscriptNine: '₉',
|
||||||
|
oneHalf: '½',
|
||||||
|
oneThird: '⅓',
|
||||||
|
oneQuarter: '¼',
|
||||||
|
oneFifth: '⅕',
|
||||||
|
oneSixth: '⅙',
|
||||||
|
oneEighth: '⅛',
|
||||||
|
twoThirds: '⅔',
|
||||||
|
twoFifths: '⅖',
|
||||||
|
threeQuarters: '¾',
|
||||||
|
threeFifths: '⅗',
|
||||||
|
threeEighths: '⅜',
|
||||||
|
fourFifths: '⅘',
|
||||||
|
fiveSixths: '⅚',
|
||||||
|
fiveEighths: '⅝',
|
||||||
|
sevenEighths: '⅞',
|
||||||
|
line: '─',
|
||||||
|
lineBold: '━',
|
||||||
|
lineDouble: '═',
|
||||||
|
lineDashed0: '┄',
|
||||||
|
lineDashed1: '┅',
|
||||||
|
lineDashed2: '┈',
|
||||||
|
lineDashed3: '┉',
|
||||||
|
lineDashed4: '╌',
|
||||||
|
lineDashed5: '╍',
|
||||||
|
lineDashed6: '╴',
|
||||||
|
lineDashed7: '╶',
|
||||||
|
lineDashed8: '╸',
|
||||||
|
lineDashed9: '╺',
|
||||||
|
lineDashed10: '╼',
|
||||||
|
lineDashed11: '╾',
|
||||||
|
lineDashed12: '−',
|
||||||
|
lineDashed13: '–',
|
||||||
|
lineDashed14: '‐',
|
||||||
|
lineDashed15: '⁃',
|
||||||
|
lineVertical: '│',
|
||||||
|
lineVerticalBold: '┃',
|
||||||
|
lineVerticalDouble: '║',
|
||||||
|
lineVerticalDashed0: '┆',
|
||||||
|
lineVerticalDashed1: '┇',
|
||||||
|
lineVerticalDashed2: '┊',
|
||||||
|
lineVerticalDashed3: '┋',
|
||||||
|
lineVerticalDashed4: '╎',
|
||||||
|
lineVerticalDashed5: '╏',
|
||||||
|
lineVerticalDashed6: '╵',
|
||||||
|
lineVerticalDashed7: '╷',
|
||||||
|
lineVerticalDashed8: '╹',
|
||||||
|
lineVerticalDashed9: '╻',
|
||||||
|
lineVerticalDashed10: '╽',
|
||||||
|
lineVerticalDashed11: '╿',
|
||||||
|
lineDownLeft: '┐',
|
||||||
|
lineDownLeftArc: '╮',
|
||||||
|
lineDownBoldLeftBold: '┓',
|
||||||
|
lineDownBoldLeft: '┒',
|
||||||
|
lineDownLeftBold: '┑',
|
||||||
|
lineDownDoubleLeftDouble: '╗',
|
||||||
|
lineDownDoubleLeft: '╖',
|
||||||
|
lineDownLeftDouble: '╕',
|
||||||
|
lineDownRight: '┌',
|
||||||
|
lineDownRightArc: '╭',
|
||||||
|
lineDownBoldRightBold: '┏',
|
||||||
|
lineDownBoldRight: '┎',
|
||||||
|
lineDownRightBold: '┍',
|
||||||
|
lineDownDoubleRightDouble: '╔',
|
||||||
|
lineDownDoubleRight: '╓',
|
||||||
|
lineDownRightDouble: '╒',
|
||||||
|
lineUpLeft: '┘',
|
||||||
|
lineUpLeftArc: '╯',
|
||||||
|
lineUpBoldLeftBold: '┛',
|
||||||
|
lineUpBoldLeft: '┚',
|
||||||
|
lineUpLeftBold: '┙',
|
||||||
|
lineUpDoubleLeftDouble: '╝',
|
||||||
|
lineUpDoubleLeft: '╜',
|
||||||
|
lineUpLeftDouble: '╛',
|
||||||
|
lineUpRight: '└',
|
||||||
|
lineUpRightArc: '╰',
|
||||||
|
lineUpBoldRightBold: '┗',
|
||||||
|
lineUpBoldRight: '┖',
|
||||||
|
lineUpRightBold: '┕',
|
||||||
|
lineUpDoubleRightDouble: '╚',
|
||||||
|
lineUpDoubleRight: '╙',
|
||||||
|
lineUpRightDouble: '╘',
|
||||||
|
lineUpDownLeft: '┤',
|
||||||
|
lineUpBoldDownBoldLeftBold: '┫',
|
||||||
|
lineUpBoldDownBoldLeft: '┨',
|
||||||
|
lineUpDownLeftBold: '┥',
|
||||||
|
lineUpBoldDownLeftBold: '┩',
|
||||||
|
lineUpDownBoldLeftBold: '┪',
|
||||||
|
lineUpDownBoldLeft: '┧',
|
||||||
|
lineUpBoldDownLeft: '┦',
|
||||||
|
lineUpDoubleDownDoubleLeftDouble: '╣',
|
||||||
|
lineUpDoubleDownDoubleLeft: '╢',
|
||||||
|
lineUpDownLeftDouble: '╡',
|
||||||
|
lineUpDownRight: '├',
|
||||||
|
lineUpBoldDownBoldRightBold: '┣',
|
||||||
|
lineUpBoldDownBoldRight: '┠',
|
||||||
|
lineUpDownRightBold: '┝',
|
||||||
|
lineUpBoldDownRightBold: '┡',
|
||||||
|
lineUpDownBoldRightBold: '┢',
|
||||||
|
lineUpDownBoldRight: '┟',
|
||||||
|
lineUpBoldDownRight: '┞',
|
||||||
|
lineUpDoubleDownDoubleRightDouble: '╠',
|
||||||
|
lineUpDoubleDownDoubleRight: '╟',
|
||||||
|
lineUpDownRightDouble: '╞',
|
||||||
|
lineDownLeftRight: '┬',
|
||||||
|
lineDownBoldLeftBoldRightBold: '┳',
|
||||||
|
lineDownLeftBoldRightBold: '┯',
|
||||||
|
lineDownBoldLeftRight: '┰',
|
||||||
|
lineDownBoldLeftBoldRight: '┱',
|
||||||
|
lineDownBoldLeftRightBold: '┲',
|
||||||
|
lineDownLeftRightBold: '┮',
|
||||||
|
lineDownLeftBoldRight: '┭',
|
||||||
|
lineDownDoubleLeftDoubleRightDouble: '╦',
|
||||||
|
lineDownDoubleLeftRight: '╥',
|
||||||
|
lineDownLeftDoubleRightDouble: '╤',
|
||||||
|
lineUpLeftRight: '┴',
|
||||||
|
lineUpBoldLeftBoldRightBold: '┻',
|
||||||
|
lineUpLeftBoldRightBold: '┷',
|
||||||
|
lineUpBoldLeftRight: '┸',
|
||||||
|
lineUpBoldLeftBoldRight: '┹',
|
||||||
|
lineUpBoldLeftRightBold: '┺',
|
||||||
|
lineUpLeftRightBold: '┶',
|
||||||
|
lineUpLeftBoldRight: '┵',
|
||||||
|
lineUpDoubleLeftDoubleRightDouble: '╩',
|
||||||
|
lineUpDoubleLeftRight: '╨',
|
||||||
|
lineUpLeftDoubleRightDouble: '╧',
|
||||||
|
lineUpDownLeftRight: '┼',
|
||||||
|
lineUpBoldDownBoldLeftBoldRightBold: '╋',
|
||||||
|
lineUpDownBoldLeftBoldRightBold: '╈',
|
||||||
|
lineUpBoldDownLeftBoldRightBold: '╇',
|
||||||
|
lineUpBoldDownBoldLeftRightBold: '╊',
|
||||||
|
lineUpBoldDownBoldLeftBoldRight: '╉',
|
||||||
|
lineUpBoldDownLeftRight: '╀',
|
||||||
|
lineUpDownBoldLeftRight: '╁',
|
||||||
|
lineUpDownLeftBoldRight: '┽',
|
||||||
|
lineUpDownLeftRightBold: '┾',
|
||||||
|
lineUpBoldDownBoldLeftRight: '╂',
|
||||||
|
lineUpDownLeftBoldRightBold: '┿',
|
||||||
|
lineUpBoldDownLeftBoldRight: '╃',
|
||||||
|
lineUpBoldDownLeftRightBold: '╄',
|
||||||
|
lineUpDownBoldLeftBoldRight: '╅',
|
||||||
|
lineUpDownBoldLeftRightBold: '╆',
|
||||||
|
lineUpDoubleDownDoubleLeftDoubleRightDouble: '╬',
|
||||||
|
lineUpDoubleDownDoubleLeftRight: '╫',
|
||||||
|
lineUpDownLeftDoubleRightDouble: '╪',
|
||||||
|
lineCross: '╳',
|
||||||
|
lineBackslash: '╲',
|
||||||
|
lineSlash: '╱',
|
||||||
|
};
|
||||||
|
const specialMainSymbols = {
|
||||||
|
tick: '✔',
|
||||||
|
info: 'ℹ',
|
||||||
|
warning: '⚠',
|
||||||
|
cross: '✘',
|
||||||
|
squareSmall: '◻',
|
||||||
|
squareSmallFilled: '◼',
|
||||||
|
circle: '◯',
|
||||||
|
circleFilled: '◉',
|
||||||
|
circleDotted: '◌',
|
||||||
|
circleDouble: '◎',
|
||||||
|
circleCircle: 'ⓞ',
|
||||||
|
circleCross: 'ⓧ',
|
||||||
|
circlePipe: 'Ⓘ',
|
||||||
|
radioOn: '◉',
|
||||||
|
radioOff: '◯',
|
||||||
|
checkboxOn: '☒',
|
||||||
|
checkboxOff: '☐',
|
||||||
|
checkboxCircleOn: 'ⓧ',
|
||||||
|
checkboxCircleOff: 'Ⓘ',
|
||||||
|
pointer: '❯',
|
||||||
|
triangleUpOutline: '△',
|
||||||
|
triangleLeft: '◀',
|
||||||
|
triangleRight: '▶',
|
||||||
|
lozenge: '◆',
|
||||||
|
lozengeOutline: '◇',
|
||||||
|
hamburger: '☰',
|
||||||
|
smiley: '㋡',
|
||||||
|
mustache: '෴',
|
||||||
|
star: '★',
|
||||||
|
play: '▶',
|
||||||
|
nodejs: '⬢',
|
||||||
|
oneSeventh: '⅐',
|
||||||
|
oneNinth: '⅑',
|
||||||
|
oneTenth: '⅒',
|
||||||
|
};
|
||||||
|
const specialFallbackSymbols = {
|
||||||
|
tick: '√',
|
||||||
|
info: 'i',
|
||||||
|
warning: '‼',
|
||||||
|
cross: '×',
|
||||||
|
squareSmall: '□',
|
||||||
|
squareSmallFilled: '■',
|
||||||
|
circle: '( )',
|
||||||
|
circleFilled: '(*)',
|
||||||
|
circleDotted: '( )',
|
||||||
|
circleDouble: '( )',
|
||||||
|
circleCircle: '(○)',
|
||||||
|
circleCross: '(×)',
|
||||||
|
circlePipe: '(│)',
|
||||||
|
radioOn: '(*)',
|
||||||
|
radioOff: '( )',
|
||||||
|
checkboxOn: '[×]',
|
||||||
|
checkboxOff: '[ ]',
|
||||||
|
checkboxCircleOn: '(×)',
|
||||||
|
checkboxCircleOff: '( )',
|
||||||
|
pointer: '>',
|
||||||
|
triangleUpOutline: '∆',
|
||||||
|
triangleLeft: '◄',
|
||||||
|
triangleRight: '►',
|
||||||
|
lozenge: '♦',
|
||||||
|
lozengeOutline: '◊',
|
||||||
|
hamburger: '≡',
|
||||||
|
smiley: '☺',
|
||||||
|
mustache: '┌─┐',
|
||||||
|
star: '✶',
|
||||||
|
play: '►',
|
||||||
|
nodejs: '♦',
|
||||||
|
oneSeventh: '1/7',
|
||||||
|
oneNinth: '1/9',
|
||||||
|
oneTenth: '1/10',
|
||||||
|
};
|
||||||
|
export const mainSymbols = {
|
||||||
|
...common,
|
||||||
|
...specialMainSymbols,
|
||||||
|
};
|
||||||
|
export const fallbackSymbols = {
|
||||||
|
...common,
|
||||||
|
...specialFallbackSymbols,
|
||||||
|
};
|
||||||
|
const shouldUseMain = isUnicodeSupported();
|
||||||
|
const figures = shouldUseMain
|
||||||
|
? mainSymbols
|
||||||
|
: fallbackSymbols;
|
||||||
|
export default figures;
|
||||||
|
const replacements = Object.entries(specialMainSymbols);
|
||||||
|
// On terminals which do not support Unicode symbols, substitute them to other symbols
|
||||||
|
export const replaceSymbols = (string, { useFallback = !shouldUseMain } = {}) => {
|
||||||
|
if (useFallback) {
|
||||||
|
for (const [key, mainSymbol] of replacements) {
|
||||||
|
const fallbackSymbol = fallbackSymbols[key];
|
||||||
|
if (!fallbackSymbol) {
|
||||||
|
throw new Error(`Unable to find fallback for ${key}`);
|
||||||
|
}
|
||||||
|
string = string.replaceAll(mainSymbol, fallbackSymbol);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return string;
|
||||||
|
};
|
||||||
79
node_modules/@inquirer/figures/package.json
generated
vendored
Normal file
79
node_modules/@inquirer/figures/package.json
generated
vendored
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
{
|
||||||
|
"name": "@inquirer/figures",
|
||||||
|
"version": "2.0.3",
|
||||||
|
"description": "Vendored version of figures, for CJS compatibility",
|
||||||
|
"keywords": [
|
||||||
|
"answer",
|
||||||
|
"answers",
|
||||||
|
"ask",
|
||||||
|
"base",
|
||||||
|
"cli",
|
||||||
|
"command",
|
||||||
|
"command-line",
|
||||||
|
"confirm",
|
||||||
|
"enquirer",
|
||||||
|
"generate",
|
||||||
|
"generator",
|
||||||
|
"hyper",
|
||||||
|
"input",
|
||||||
|
"inquire",
|
||||||
|
"inquirer",
|
||||||
|
"interface",
|
||||||
|
"iterm",
|
||||||
|
"javascript",
|
||||||
|
"menu",
|
||||||
|
"node",
|
||||||
|
"nodejs",
|
||||||
|
"prompt",
|
||||||
|
"promptly",
|
||||||
|
"prompts",
|
||||||
|
"question",
|
||||||
|
"readline",
|
||||||
|
"scaffold",
|
||||||
|
"scaffolder",
|
||||||
|
"scaffolding",
|
||||||
|
"stdin",
|
||||||
|
"stdout",
|
||||||
|
"terminal",
|
||||||
|
"tty",
|
||||||
|
"types",
|
||||||
|
"typescript",
|
||||||
|
"ui",
|
||||||
|
"yeoman",
|
||||||
|
"yo",
|
||||||
|
"zsh"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"author": "Simon Boudrias <admin@simonboudrias.com>",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/SBoudrias/Inquirer.js.git"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"dist"
|
||||||
|
],
|
||||||
|
"type": "module",
|
||||||
|
"sideEffects": false,
|
||||||
|
"exports": {
|
||||||
|
".": {
|
||||||
|
"types": "./dist/index.d.ts",
|
||||||
|
"default": "./dist/index.js"
|
||||||
|
},
|
||||||
|
"./package.json": "./package.json"
|
||||||
|
},
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"tsc": "tsc"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"typescript": "^5.9.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0"
|
||||||
|
},
|
||||||
|
"main": "./dist/index.js",
|
||||||
|
"types": "./dist/index.d.ts",
|
||||||
|
"gitHead": "99d00a9adc53be8b7edf5926b2ec4ba0b792f68f"
|
||||||
|
}
|
||||||
22
node_modules/@inquirer/search/LICENSE
generated
vendored
Normal file
22
node_modules/@inquirer/search/LICENSE
generated
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
Copyright (c) 2025 Simon Boudrias
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person
|
||||||
|
obtaining a copy of this software and associated documentation
|
||||||
|
files (the "Software"), to deal in the Software without
|
||||||
|
restriction, including without limitation the rights to use,
|
||||||
|
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the
|
||||||
|
Software is furnished to do so, subject to the following
|
||||||
|
conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||||
|
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||||
|
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||||
|
OTHER DEALINGS IN THE SOFTWARE.
|
||||||
213
node_modules/@inquirer/search/README.md
generated
vendored
Normal file
213
node_modules/@inquirer/search/README.md
generated
vendored
Normal file
@ -0,0 +1,213 @@
|
|||||||
|
# `@inquirer/search`
|
||||||
|
|
||||||
|
Interactive search prompt component for command line interfaces.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
# Installation
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th>npm</th>
|
||||||
|
<th>yarn</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm install @inquirer/prompts
|
||||||
|
```
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
```sh
|
||||||
|
yarn add @inquirer/prompts
|
||||||
|
```
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm install @inquirer/search
|
||||||
|
```
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
```sh
|
||||||
|
yarn add @inquirer/search
|
||||||
|
```
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
# Usage
|
||||||
|
|
||||||
|
```js
|
||||||
|
import { search, Separator } from '@inquirer/prompts';
|
||||||
|
// Or
|
||||||
|
// import search, { Separator } from '@inquirer/search';
|
||||||
|
|
||||||
|
const answer = await search({
|
||||||
|
message: 'Select an npm package',
|
||||||
|
source: async (input, { signal }) => {
|
||||||
|
if (!input) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await fetch(
|
||||||
|
`https://registry.npmjs.org/-/v1/search?text=${encodeURIComponent(input)}&size=20`,
|
||||||
|
{ signal },
|
||||||
|
);
|
||||||
|
const data = await response.json();
|
||||||
|
|
||||||
|
return data.objects.map((pkg) => ({
|
||||||
|
name: pkg.package.name,
|
||||||
|
value: pkg.package.name,
|
||||||
|
description: pkg.package.description,
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## Options
|
||||||
|
|
||||||
|
| Property | Type | Required | Description |
|
||||||
|
| -------- | ---------------------------------------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
|
| message | `string` | yes | The question to ask |
|
||||||
|
| source | `(term: string \| void) => Promise<Choice[]>` | yes | This function returns the choices relevant to the search term. |
|
||||||
|
| pageSize | `number` | no | By default, lists of choice longer than 7 will be paginated. Use this option to control how many choices will appear on the screen at once. |
|
||||||
|
| default | `Value` | no | Defines in front of which item the cursor will initially appear. When omitted, the cursor will appear on the first selectable item. |
|
||||||
|
| validate | `Value => boolean \| string \| Promise<boolean \| string>` | no | On submit, validate the answer. When returning a string, it'll be used as the error message displayed to the user. Note: returning a rejected promise, we'll assume a code error happened and crash. |
|
||||||
|
| theme | [See Theming](#Theming) | no | Customize look of the prompt. |
|
||||||
|
|
||||||
|
### `source` function
|
||||||
|
|
||||||
|
The full signature type of `source` is as follow:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
function(
|
||||||
|
term: string | void,
|
||||||
|
opt: { signal: AbortSignal },
|
||||||
|
): Promise<ReadonlyArray<Choice<Value> | Separator>>;
|
||||||
|
```
|
||||||
|
|
||||||
|
When `term` is `undefined`, it means the search term input is empty. You can use this to return default choices, or return an empty array.
|
||||||
|
|
||||||
|
Aside from returning the choices:
|
||||||
|
|
||||||
|
1. An `AbortSignal` is passed in to cancel ongoing network calls when the search term change.
|
||||||
|
2. `Separator`s can be used to organize the list.
|
||||||
|
|
||||||
|
### `Choice` object
|
||||||
|
|
||||||
|
The `Choice` object is typed as
|
||||||
|
|
||||||
|
```ts
|
||||||
|
type Choice<Value> = {
|
||||||
|
value: Value;
|
||||||
|
name?: string;
|
||||||
|
description?: string;
|
||||||
|
short?: string;
|
||||||
|
disabled?: boolean | string;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
Here's each property:
|
||||||
|
|
||||||
|
- `value`: The value is what will be returned by `await search()`.
|
||||||
|
- `name`: This is the string displayed in the choice list.
|
||||||
|
- `description`: Option for a longer description string that'll appear under the list when the cursor highlight a given choice.
|
||||||
|
- `short`: Once the prompt is done (press enter), we'll use `short` if defined to render next to the question. By default we'll use `name`.
|
||||||
|
- `disabled`: Disallow the option from being selected. If `disabled` is a string, it'll be used as a help tip explaining why the choice isn't available.
|
||||||
|
|
||||||
|
Choices can also be an array of string, in which case the string will be used both as the `value` and the `name`.
|
||||||
|
|
||||||
|
### Validation & autocomplete interaction
|
||||||
|
|
||||||
|
The validation within the search prompt acts as a signal for the autocomplete feature.
|
||||||
|
|
||||||
|
When a list value is submitted and fail validation, the prompt will compare it to the search term. If they're the same, the prompt display the error. If they're not the same, we'll autocomplete the search term to match the value. Doing this will trigger a new search.
|
||||||
|
|
||||||
|
You can rely on this behavior to implement progressive autocomplete searches. Where you want the user to narrow the search in a progressive manner.
|
||||||
|
|
||||||
|
Pressing `tab` also triggers the term autocomplete.
|
||||||
|
|
||||||
|
You can see this behavior in action in [our search demo](https://github.com/SBoudrias/Inquirer.js/blob/main/packages/demo/src/demos/search.ts).
|
||||||
|
|
||||||
|
## Theming
|
||||||
|
|
||||||
|
You can theme a prompt by passing a `theme` object option. The theme object only need to includes the keys you wish to modify, we'll fallback on the defaults for the rest.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
type Theme = {
|
||||||
|
prefix: string | { idle: string; done: string };
|
||||||
|
spinner: {
|
||||||
|
interval: number;
|
||||||
|
frames: string[];
|
||||||
|
};
|
||||||
|
style: {
|
||||||
|
answer: (text: string) => string;
|
||||||
|
message: (text: string, status: 'idle' | 'done' | 'loading') => string;
|
||||||
|
error: (text: string) => string;
|
||||||
|
help: (text: string) => string;
|
||||||
|
highlight: (text: string) => string;
|
||||||
|
description: (text: string) => string;
|
||||||
|
disabled: (text: string) => string;
|
||||||
|
searchTerm: (text: string) => string;
|
||||||
|
keysHelpTip: (keys: [key: string, action: string][]) => string | undefined;
|
||||||
|
};
|
||||||
|
icon: {
|
||||||
|
cursor: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### `theme.style.keysHelpTip`
|
||||||
|
|
||||||
|
This function allows you to customize the keyboard shortcuts help tip displayed below the prompt. It receives an array of key-action pairs and should return a formatted string. You can also hook here to localize the labels to different languages.
|
||||||
|
|
||||||
|
It can also returns `undefined` to hide the help tip entirely.
|
||||||
|
|
||||||
|
```js
|
||||||
|
theme: {
|
||||||
|
style: {
|
||||||
|
keysHelpTip: (keys) => {
|
||||||
|
// Return undefined to hide the help tip completely.
|
||||||
|
return undefined;
|
||||||
|
|
||||||
|
// Or customize the formatting. Or localize the labels.
|
||||||
|
return keys.map(([key, action]) => `${key}: ${action}`).join(' | ');
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Recipes
|
||||||
|
|
||||||
|
### Debounce search
|
||||||
|
|
||||||
|
```js
|
||||||
|
import { setTimeout } from 'node:timers/promises';
|
||||||
|
import { search } from '@inquirer/prompts';
|
||||||
|
|
||||||
|
const answer = await search({
|
||||||
|
message: 'Select an npm package',
|
||||||
|
source: async (input, { signal }) => {
|
||||||
|
await setTimeout(300);
|
||||||
|
if (signal.aborted) return [];
|
||||||
|
|
||||||
|
// Do the search
|
||||||
|
fetch(...)
|
||||||
|
},
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
# License
|
||||||
|
|
||||||
|
Copyright (c) 2024 Simon Boudrias (twitter: [@vaxilart](https://twitter.com/Vaxilart))<br/>
|
||||||
|
Licensed under the MIT license.
|
||||||
33
node_modules/@inquirer/search/dist/index.d.ts
generated
vendored
Normal file
33
node_modules/@inquirer/search/dist/index.d.ts
generated
vendored
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import { Separator, type Theme } from '@inquirer/core';
|
||||||
|
import type { PartialDeep } from '@inquirer/type';
|
||||||
|
type SearchTheme = {
|
||||||
|
icon: {
|
||||||
|
cursor: string;
|
||||||
|
};
|
||||||
|
style: {
|
||||||
|
disabled: (text: string) => string;
|
||||||
|
searchTerm: (text: string) => string;
|
||||||
|
description: (text: string) => string;
|
||||||
|
keysHelpTip: (keys: [key: string, action: string][]) => string | undefined;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
type Choice<Value> = {
|
||||||
|
value: Value;
|
||||||
|
name?: string;
|
||||||
|
description?: string;
|
||||||
|
short?: string;
|
||||||
|
disabled?: boolean | string;
|
||||||
|
type?: never;
|
||||||
|
};
|
||||||
|
declare const _default: <Value>(config: {
|
||||||
|
message: string;
|
||||||
|
source: (term: string | undefined, opt: {
|
||||||
|
signal: AbortSignal;
|
||||||
|
}) => readonly (string | Separator)[] | readonly (Separator | Choice<Value>)[] | Promise<readonly (string | Separator)[]> | Promise<readonly (Separator | Choice<Value>)[]>;
|
||||||
|
validate?: ((value: Value) => boolean | string | Promise<string | boolean>) | undefined;
|
||||||
|
pageSize?: number | undefined;
|
||||||
|
default?: NoInfer<Value> | undefined;
|
||||||
|
theme?: PartialDeep<Theme<SearchTheme>> | undefined;
|
||||||
|
}, context?: import("@inquirer/type").Context) => Promise<Value>;
|
||||||
|
export default _default;
|
||||||
|
export { Separator } from '@inquirer/core';
|
||||||
193
node_modules/@inquirer/search/dist/index.js
generated
vendored
Normal file
193
node_modules/@inquirer/search/dist/index.js
generated
vendored
Normal file
@ -0,0 +1,193 @@
|
|||||||
|
import { createPrompt, useState, useKeypress, usePrefix, usePagination, useEffect, useMemo, useRef, isDownKey, isEnterKey, isTabKey, isUpKey, Separator, makeTheme, } from '@inquirer/core';
|
||||||
|
import { styleText } from 'node:util';
|
||||||
|
import figures from '@inquirer/figures';
|
||||||
|
const searchTheme = {
|
||||||
|
icon: { cursor: figures.pointer },
|
||||||
|
style: {
|
||||||
|
disabled: (text) => styleText('dim', `- ${text}`),
|
||||||
|
searchTerm: (text) => styleText('cyan', text),
|
||||||
|
description: (text) => styleText('cyan', text),
|
||||||
|
keysHelpTip: (keys) => keys
|
||||||
|
.map(([key, action]) => `${styleText('bold', key)} ${styleText('dim', action)}`)
|
||||||
|
.join(styleText('dim', ' • ')),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
function isSelectable(item) {
|
||||||
|
return !Separator.isSeparator(item) && !item.disabled;
|
||||||
|
}
|
||||||
|
function normalizeChoices(choices) {
|
||||||
|
return choices.map((choice) => {
|
||||||
|
if (Separator.isSeparator(choice))
|
||||||
|
return choice;
|
||||||
|
if (typeof choice === 'string') {
|
||||||
|
return {
|
||||||
|
value: choice,
|
||||||
|
name: choice,
|
||||||
|
short: choice,
|
||||||
|
disabled: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const name = choice.name ?? String(choice.value);
|
||||||
|
const normalizedChoice = {
|
||||||
|
value: choice.value,
|
||||||
|
name,
|
||||||
|
short: choice.short ?? name,
|
||||||
|
disabled: choice.disabled ?? false,
|
||||||
|
};
|
||||||
|
if (choice.description) {
|
||||||
|
normalizedChoice.description = choice.description;
|
||||||
|
}
|
||||||
|
return normalizedChoice;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
export default createPrompt((config, done) => {
|
||||||
|
const { pageSize = 7, validate = () => true } = config;
|
||||||
|
const theme = makeTheme(searchTheme, config.theme);
|
||||||
|
const [status, setStatus] = useState('loading');
|
||||||
|
const [searchTerm, setSearchTerm] = useState('');
|
||||||
|
const [searchResults, setSearchResults] = useState([]);
|
||||||
|
const [searchError, setSearchError] = useState();
|
||||||
|
const defaultApplied = useRef(false);
|
||||||
|
const prefix = usePrefix({ status, theme });
|
||||||
|
const bounds = useMemo(() => {
|
||||||
|
const first = searchResults.findIndex(isSelectable);
|
||||||
|
const last = searchResults.findLastIndex(isSelectable);
|
||||||
|
return { first, last };
|
||||||
|
}, [searchResults]);
|
||||||
|
const [active = bounds.first, setActive] = useState();
|
||||||
|
useEffect(() => {
|
||||||
|
const controller = new AbortController();
|
||||||
|
setStatus('loading');
|
||||||
|
setSearchError(undefined);
|
||||||
|
const fetchResults = async () => {
|
||||||
|
try {
|
||||||
|
const results = await config.source(searchTerm || undefined, {
|
||||||
|
signal: controller.signal,
|
||||||
|
});
|
||||||
|
if (!controller.signal.aborted) {
|
||||||
|
const normalized = normalizeChoices(results);
|
||||||
|
let initialActive;
|
||||||
|
if (!defaultApplied.current && 'default' in config) {
|
||||||
|
const defaultIndex = normalized.findIndex((item) => isSelectable(item) && item.value === config.default);
|
||||||
|
initialActive = defaultIndex === -1 ? undefined : defaultIndex;
|
||||||
|
defaultApplied.current = true;
|
||||||
|
}
|
||||||
|
setActive(initialActive);
|
||||||
|
setSearchError(undefined);
|
||||||
|
setSearchResults(normalized);
|
||||||
|
setStatus('idle');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
if (!controller.signal.aborted && error instanceof Error) {
|
||||||
|
setSearchError(error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
void fetchResults();
|
||||||
|
return () => {
|
||||||
|
controller.abort();
|
||||||
|
};
|
||||||
|
}, [searchTerm]);
|
||||||
|
// Safe to assume the cursor position never points to a Separator.
|
||||||
|
const selectedChoice = searchResults[active];
|
||||||
|
useKeypress(async (key, rl) => {
|
||||||
|
if (isEnterKey(key)) {
|
||||||
|
if (selectedChoice) {
|
||||||
|
setStatus('loading');
|
||||||
|
const isValid = await validate(selectedChoice.value);
|
||||||
|
setStatus('idle');
|
||||||
|
if (isValid === true) {
|
||||||
|
setStatus('done');
|
||||||
|
done(selectedChoice.value);
|
||||||
|
}
|
||||||
|
else if (selectedChoice.name === searchTerm) {
|
||||||
|
setSearchError(isValid || 'You must provide a valid value');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Reset line with new search term
|
||||||
|
rl.write(selectedChoice.name);
|
||||||
|
setSearchTerm(selectedChoice.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Reset the readline line value to the previous value. On line event, the value
|
||||||
|
// get cleared, forcing the user to re-enter the value instead of fixing it.
|
||||||
|
rl.write(searchTerm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (isTabKey(key) && selectedChoice) {
|
||||||
|
rl.clearLine(0); // Remove the tab character.
|
||||||
|
rl.write(selectedChoice.name);
|
||||||
|
setSearchTerm(selectedChoice.name);
|
||||||
|
}
|
||||||
|
else if (status !== 'loading' && (isUpKey(key) || isDownKey(key))) {
|
||||||
|
rl.clearLine(0);
|
||||||
|
if ((isUpKey(key) && active !== bounds.first) ||
|
||||||
|
(isDownKey(key) && active !== bounds.last)) {
|
||||||
|
const offset = isUpKey(key) ? -1 : 1;
|
||||||
|
let next = active;
|
||||||
|
do {
|
||||||
|
next = (next + offset + searchResults.length) % searchResults.length;
|
||||||
|
} while (!isSelectable(searchResults[next]));
|
||||||
|
setActive(next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
setSearchTerm(rl.line);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const message = theme.style.message(config.message, status);
|
||||||
|
const helpLine = theme.style.keysHelpTip([
|
||||||
|
['↑↓', 'navigate'],
|
||||||
|
['⏎', 'select'],
|
||||||
|
]);
|
||||||
|
const page = usePagination({
|
||||||
|
items: searchResults,
|
||||||
|
active,
|
||||||
|
renderItem({ item, isActive }) {
|
||||||
|
if (Separator.isSeparator(item)) {
|
||||||
|
return ` ${item.separator}`;
|
||||||
|
}
|
||||||
|
if (item.disabled) {
|
||||||
|
const disabledLabel = typeof item.disabled === 'string' ? item.disabled : '(disabled)';
|
||||||
|
return theme.style.disabled(`${item.name} ${disabledLabel}`);
|
||||||
|
}
|
||||||
|
const color = isActive ? theme.style.highlight : (x) => x;
|
||||||
|
const cursor = isActive ? theme.icon.cursor : ` `;
|
||||||
|
return color(`${cursor} ${item.name}`);
|
||||||
|
},
|
||||||
|
pageSize,
|
||||||
|
loop: false,
|
||||||
|
});
|
||||||
|
let error;
|
||||||
|
if (searchError) {
|
||||||
|
error = theme.style.error(searchError);
|
||||||
|
}
|
||||||
|
else if (searchResults.length === 0 && searchTerm !== '' && status === 'idle') {
|
||||||
|
error = theme.style.error('No results found');
|
||||||
|
}
|
||||||
|
let searchStr;
|
||||||
|
if (status === 'done' && selectedChoice) {
|
||||||
|
return [prefix, message, theme.style.answer(selectedChoice.short)]
|
||||||
|
.filter(Boolean)
|
||||||
|
.join(' ')
|
||||||
|
.trimEnd();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
searchStr = theme.style.searchTerm(searchTerm);
|
||||||
|
}
|
||||||
|
const description = selectedChoice?.description;
|
||||||
|
const header = [prefix, message, searchStr].filter(Boolean).join(' ').trimEnd();
|
||||||
|
const body = [
|
||||||
|
error ?? page,
|
||||||
|
' ',
|
||||||
|
description ? theme.style.description(description) : '',
|
||||||
|
helpLine,
|
||||||
|
]
|
||||||
|
.filter(Boolean)
|
||||||
|
.join('\n')
|
||||||
|
.trimEnd();
|
||||||
|
return [header, body];
|
||||||
|
});
|
||||||
|
export { Separator } from '@inquirer/core';
|
||||||
92
node_modules/@inquirer/search/package.json
generated
vendored
Normal file
92
node_modules/@inquirer/search/package.json
generated
vendored
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
{
|
||||||
|
"name": "@inquirer/search",
|
||||||
|
"version": "4.1.4",
|
||||||
|
"description": "Inquirer search prompt",
|
||||||
|
"keywords": [
|
||||||
|
"answer",
|
||||||
|
"answers",
|
||||||
|
"ask",
|
||||||
|
"base",
|
||||||
|
"cli",
|
||||||
|
"command",
|
||||||
|
"command-line",
|
||||||
|
"confirm",
|
||||||
|
"enquirer",
|
||||||
|
"generate",
|
||||||
|
"generator",
|
||||||
|
"hyper",
|
||||||
|
"input",
|
||||||
|
"inquire",
|
||||||
|
"inquirer",
|
||||||
|
"interface",
|
||||||
|
"iterm",
|
||||||
|
"javascript",
|
||||||
|
"menu",
|
||||||
|
"node",
|
||||||
|
"nodejs",
|
||||||
|
"prompt",
|
||||||
|
"promptly",
|
||||||
|
"prompts",
|
||||||
|
"question",
|
||||||
|
"readline",
|
||||||
|
"scaffold",
|
||||||
|
"scaffolder",
|
||||||
|
"scaffolding",
|
||||||
|
"stdin",
|
||||||
|
"stdout",
|
||||||
|
"terminal",
|
||||||
|
"tty",
|
||||||
|
"ui",
|
||||||
|
"yeoman",
|
||||||
|
"yo",
|
||||||
|
"zsh"
|
||||||
|
],
|
||||||
|
"homepage": "https://github.com/SBoudrias/Inquirer.js/blob/main/packages/search/README.md",
|
||||||
|
"license": "MIT",
|
||||||
|
"author": "Simon Boudrias <admin@simonboudrias.com>",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/SBoudrias/Inquirer.js.git"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"dist"
|
||||||
|
],
|
||||||
|
"type": "module",
|
||||||
|
"sideEffects": false,
|
||||||
|
"exports": {
|
||||||
|
".": {
|
||||||
|
"types": "./dist/index.d.ts",
|
||||||
|
"default": "./dist/index.js"
|
||||||
|
},
|
||||||
|
"./package.json": "./package.json"
|
||||||
|
},
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"tsc": "tsc"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@inquirer/core": "^11.1.5",
|
||||||
|
"@inquirer/figures": "^2.0.3",
|
||||||
|
"@inquirer/type": "^4.0.3"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@inquirer/testing": "^3.3.0",
|
||||||
|
"typescript": "^5.9.3"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/node": ">=18"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/node": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0"
|
||||||
|
},
|
||||||
|
"main": "./dist/index.js",
|
||||||
|
"types": "./dist/index.d.ts",
|
||||||
|
"gitHead": "526eca2e64853510821ffd457561840ec0cbfb93"
|
||||||
|
}
|
||||||
22
node_modules/@inquirer/select/LICENSE
generated
vendored
Normal file
22
node_modules/@inquirer/select/LICENSE
generated
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
Copyright (c) 2025 Simon Boudrias
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person
|
||||||
|
obtaining a copy of this software and associated documentation
|
||||||
|
files (the "Software"), to deal in the Software without
|
||||||
|
restriction, including without limitation the rights to use,
|
||||||
|
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the
|
||||||
|
Software is furnished to do so, subject to the following
|
||||||
|
conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||||
|
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||||
|
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||||
|
OTHER DEALINGS IN THE SOFTWARE.
|
||||||
181
node_modules/@inquirer/select/README.md
generated
vendored
Normal file
181
node_modules/@inquirer/select/README.md
generated
vendored
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
# `@inquirer/select`
|
||||||
|
|
||||||
|
Simple interactive command line prompt to display a list of choices (single select.)
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
# Installation
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th>npm</th>
|
||||||
|
<th>yarn</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm install @inquirer/prompts
|
||||||
|
```
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
```sh
|
||||||
|
yarn add @inquirer/prompts
|
||||||
|
```
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colSpan="2" align="center">Or</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm install @inquirer/select
|
||||||
|
```
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
```sh
|
||||||
|
yarn add @inquirer/select
|
||||||
|
```
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
# Usage
|
||||||
|
|
||||||
|
```js
|
||||||
|
import { select, Separator } from '@inquirer/prompts';
|
||||||
|
// Or
|
||||||
|
// import select, { Separator } from '@inquirer/select';
|
||||||
|
|
||||||
|
const answer = await select({
|
||||||
|
message: 'Select a package manager',
|
||||||
|
choices: [
|
||||||
|
{
|
||||||
|
name: 'npm',
|
||||||
|
value: 'npm',
|
||||||
|
description: 'npm is the most popular package manager',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'yarn',
|
||||||
|
value: 'yarn',
|
||||||
|
description: 'yarn is an awesome package manager',
|
||||||
|
},
|
||||||
|
new Separator(),
|
||||||
|
{
|
||||||
|
name: 'jspm',
|
||||||
|
value: 'jspm',
|
||||||
|
disabled: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'pnpm',
|
||||||
|
value: 'pnpm',
|
||||||
|
disabled: '(pnpm is not available)',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## Options
|
||||||
|
|
||||||
|
| Property | Type | Required | Description |
|
||||||
|
| -------- | ----------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
|
| message | `string` | yes | The question to ask |
|
||||||
|
| choices | `Choice[]` | yes | List of the available choices. |
|
||||||
|
| default | `string` | no | Defines in front of which item the cursor will initially appear. When omitted, the cursor will appear on the first selectable item. |
|
||||||
|
| pageSize | `number` | no | By default, lists of choice longer than 7 will be paginated. Use this option to control how many choices will appear on the screen at once. |
|
||||||
|
| loop | `boolean` | no | Defaults to `true`. When set to `false`, the cursor will be constrained to the top and bottom of the choice list without looping. |
|
||||||
|
| theme | [See Theming](#Theming) | no | Customize look of the prompt. |
|
||||||
|
|
||||||
|
`Separator` objects can be used in the `choices` array to render non-selectable lines in the choice list. By default it'll render a line, but you can provide the text as argument (`new Separator('-- Dependencies --')`). This option is often used to add labels to groups within long list of options.
|
||||||
|
|
||||||
|
### `Choice` object
|
||||||
|
|
||||||
|
The `Choice` object is typed as
|
||||||
|
|
||||||
|
```ts
|
||||||
|
type Choice<Value> = {
|
||||||
|
value: Value;
|
||||||
|
name?: string;
|
||||||
|
description?: string;
|
||||||
|
short?: string;
|
||||||
|
disabled?: boolean | string;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
Here's each property:
|
||||||
|
|
||||||
|
- `value`: The value is what will be returned by `await select()`.
|
||||||
|
- `name`: This is the string displayed in the choice list.
|
||||||
|
- `description`: Option for a longer description string that'll appear under the list when the cursor highlight a given choice.
|
||||||
|
- `short`: Once the prompt is done (press enter), we'll use `short` if defined to render next to the question. By default we'll use `name`.
|
||||||
|
- `disabled`: Disallow the option from being selected. If `disabled` is a string, it'll be used as a help tip explaining why the choice isn't available.
|
||||||
|
|
||||||
|
`choices` can also be an array of string, in which case the string will be used both as the `value` and the `name`.
|
||||||
|
|
||||||
|
## Theming
|
||||||
|
|
||||||
|
You can theme a prompt by passing a `theme` object option. The theme object only need to includes the keys you wish to modify, we'll fallback on the defaults for the rest.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
type Theme = {
|
||||||
|
prefix: string | { idle: string; done: string };
|
||||||
|
spinner: {
|
||||||
|
interval: number;
|
||||||
|
frames: string[];
|
||||||
|
};
|
||||||
|
style: {
|
||||||
|
answer: (text: string) => string;
|
||||||
|
message: (text: string, status: 'idle' | 'done' | 'loading') => string;
|
||||||
|
error: (text: string) => string;
|
||||||
|
help: (text: string) => string;
|
||||||
|
highlight: (text: string) => string;
|
||||||
|
description: (text: string) => string;
|
||||||
|
disabled: (text: string) => string;
|
||||||
|
keysHelpTip: (keys: [key: string, action: string][]) => string | undefined;
|
||||||
|
};
|
||||||
|
icon: {
|
||||||
|
cursor: string;
|
||||||
|
};
|
||||||
|
indexMode: 'hidden' | 'number';
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### `theme.style.keysHelpTip`
|
||||||
|
|
||||||
|
This function allows you to customize the keyboard shortcuts help tip displayed below the prompt. It receives an array of key-action pairs and should return a formatted string. You can also hook here to localize the labels to different languages.
|
||||||
|
|
||||||
|
It can also returns `undefined` to hide the help tip entirely.
|
||||||
|
|
||||||
|
```js
|
||||||
|
theme: {
|
||||||
|
style: {
|
||||||
|
keysHelpTip: (keys) => {
|
||||||
|
// Return undefined to hide the help tip completely.
|
||||||
|
return undefined;
|
||||||
|
|
||||||
|
// Or customize the formatting. Or localize the labels.
|
||||||
|
return keys.map(([key, action]) => `${key}: ${action}`).join(' | ');
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### `theme.indexMode`
|
||||||
|
|
||||||
|
Controls how indices are displayed before each choice:
|
||||||
|
|
||||||
|
- `hidden` (default): No indices are shown
|
||||||
|
- `number`: Display a number before each choice (e.g. "1. Option A")
|
||||||
|
|
||||||
|
# License
|
||||||
|
|
||||||
|
Copyright (c) 2023 Simon Boudrias (twitter: [@vaxilart](https://twitter.com/Vaxilart))<br/>
|
||||||
|
Licensed under the MIT license.
|
||||||
35
node_modules/@inquirer/select/dist/index.d.ts
generated
vendored
Normal file
35
node_modules/@inquirer/select/dist/index.d.ts
generated
vendored
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import { Separator, type Theme, type Keybinding } from '@inquirer/core';
|
||||||
|
import type { PartialDeep } from '@inquirer/type';
|
||||||
|
type SelectTheme = {
|
||||||
|
icon: {
|
||||||
|
cursor: string;
|
||||||
|
};
|
||||||
|
style: {
|
||||||
|
disabled: (text: string) => string;
|
||||||
|
description: (text: string) => string;
|
||||||
|
keysHelpTip: (keys: [key: string, action: string][]) => string | undefined;
|
||||||
|
};
|
||||||
|
i18n: {
|
||||||
|
disabledError: string;
|
||||||
|
};
|
||||||
|
indexMode: 'hidden' | 'number';
|
||||||
|
keybindings: ReadonlyArray<Keybinding>;
|
||||||
|
};
|
||||||
|
type Choice<Value> = {
|
||||||
|
value: Value;
|
||||||
|
name?: string;
|
||||||
|
description?: string;
|
||||||
|
short?: string;
|
||||||
|
disabled?: boolean | string;
|
||||||
|
type?: never;
|
||||||
|
};
|
||||||
|
declare const _default: <Value>(config: {
|
||||||
|
message: string;
|
||||||
|
choices: readonly (Separator | Value | Choice<Value>)[];
|
||||||
|
pageSize?: number | undefined;
|
||||||
|
loop?: boolean | undefined;
|
||||||
|
default?: NoInfer<Value> | undefined;
|
||||||
|
theme?: PartialDeep<Theme<SelectTheme>> | undefined;
|
||||||
|
}, context?: import("@inquirer/type").Context) => Promise<Value>;
|
||||||
|
export default _default;
|
||||||
|
export { Separator } from '@inquirer/core';
|
||||||
191
node_modules/@inquirer/select/dist/index.js
generated
vendored
Normal file
191
node_modules/@inquirer/select/dist/index.js
generated
vendored
Normal file
@ -0,0 +1,191 @@
|
|||||||
|
import { createPrompt, useState, useKeypress, usePrefix, usePagination, useRef, useMemo, useEffect, isBackspaceKey, isEnterKey, isUpKey, isDownKey, isNumberKey, Separator, ValidationError, makeTheme, } from '@inquirer/core';
|
||||||
|
import { cursorHide } from '@inquirer/ansi';
|
||||||
|
import { styleText } from 'node:util';
|
||||||
|
import figures from '@inquirer/figures';
|
||||||
|
const selectTheme = {
|
||||||
|
icon: { cursor: figures.pointer },
|
||||||
|
style: {
|
||||||
|
disabled: (text) => styleText('dim', text),
|
||||||
|
description: (text) => styleText('cyan', text),
|
||||||
|
keysHelpTip: (keys) => keys
|
||||||
|
.map(([key, action]) => `${styleText('bold', key)} ${styleText('dim', action)}`)
|
||||||
|
.join(styleText('dim', ' • ')),
|
||||||
|
},
|
||||||
|
i18n: { disabledError: 'This option is disabled and cannot be selected.' },
|
||||||
|
indexMode: 'hidden',
|
||||||
|
keybindings: [],
|
||||||
|
};
|
||||||
|
function isSelectable(item) {
|
||||||
|
return !Separator.isSeparator(item) && !item.disabled;
|
||||||
|
}
|
||||||
|
function isNavigable(item) {
|
||||||
|
return !Separator.isSeparator(item);
|
||||||
|
}
|
||||||
|
function normalizeChoices(choices) {
|
||||||
|
return choices.map((choice) => {
|
||||||
|
if (Separator.isSeparator(choice))
|
||||||
|
return choice;
|
||||||
|
if (typeof choice !== 'object' || choice === null || !('value' in choice)) {
|
||||||
|
// It's a raw value (string, number, etc.)
|
||||||
|
const name = String(choice);
|
||||||
|
return {
|
||||||
|
value: choice,
|
||||||
|
name,
|
||||||
|
short: name,
|
||||||
|
disabled: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const name = choice.name ?? String(choice.value);
|
||||||
|
const normalizedChoice = {
|
||||||
|
value: choice.value,
|
||||||
|
name,
|
||||||
|
short: choice.short ?? name,
|
||||||
|
disabled: choice.disabled ?? false,
|
||||||
|
};
|
||||||
|
if (choice.description) {
|
||||||
|
normalizedChoice.description = choice.description;
|
||||||
|
}
|
||||||
|
return normalizedChoice;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
export default createPrompt((config, done) => {
|
||||||
|
const { loop = true, pageSize = 7 } = config;
|
||||||
|
const theme = makeTheme(selectTheme, config.theme);
|
||||||
|
const { keybindings } = theme;
|
||||||
|
const [status, setStatus] = useState('idle');
|
||||||
|
const prefix = usePrefix({ status, theme });
|
||||||
|
const searchTimeoutRef = useRef();
|
||||||
|
// Vim keybindings (j/k) conflict with typing those letters in search,
|
||||||
|
// so search must be disabled when vim bindings are enabled
|
||||||
|
const searchEnabled = !keybindings.includes('vim');
|
||||||
|
const items = useMemo(() => normalizeChoices(config.choices), [config.choices]);
|
||||||
|
const bounds = useMemo(() => {
|
||||||
|
const first = items.findIndex(isNavigable);
|
||||||
|
const last = items.findLastIndex(isNavigable);
|
||||||
|
if (first === -1) {
|
||||||
|
throw new ValidationError('[select prompt] No selectable choices. All choices are disabled.');
|
||||||
|
}
|
||||||
|
return { first, last };
|
||||||
|
}, [items]);
|
||||||
|
const defaultItemIndex = useMemo(() => {
|
||||||
|
if (!('default' in config))
|
||||||
|
return -1;
|
||||||
|
return items.findIndex((item) => isSelectable(item) && item.value === config.default);
|
||||||
|
}, [config.default, items]);
|
||||||
|
const [active, setActive] = useState(defaultItemIndex === -1 ? bounds.first : defaultItemIndex);
|
||||||
|
// Safe to assume the cursor position always point to a Choice.
|
||||||
|
const selectedChoice = items[active];
|
||||||
|
const [errorMsg, setError] = useState();
|
||||||
|
useKeypress((key, rl) => {
|
||||||
|
clearTimeout(searchTimeoutRef.current);
|
||||||
|
if (errorMsg) {
|
||||||
|
setError(undefined);
|
||||||
|
}
|
||||||
|
if (isEnterKey(key)) {
|
||||||
|
if (selectedChoice.disabled) {
|
||||||
|
setError(theme.i18n.disabledError);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
setStatus('done');
|
||||||
|
done(selectedChoice.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (isUpKey(key, keybindings) || isDownKey(key, keybindings)) {
|
||||||
|
rl.clearLine(0);
|
||||||
|
if (loop ||
|
||||||
|
(isUpKey(key, keybindings) && active !== bounds.first) ||
|
||||||
|
(isDownKey(key, keybindings) && active !== bounds.last)) {
|
||||||
|
const offset = isUpKey(key, keybindings) ? -1 : 1;
|
||||||
|
let next = active;
|
||||||
|
do {
|
||||||
|
next = (next + offset + items.length) % items.length;
|
||||||
|
} while (!isNavigable(items[next]));
|
||||||
|
setActive(next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (isNumberKey(key) && !Number.isNaN(Number(rl.line))) {
|
||||||
|
const selectedIndex = Number(rl.line) - 1;
|
||||||
|
// Find the nth item (ignoring separators)
|
||||||
|
let selectableIndex = -1;
|
||||||
|
const position = items.findIndex((item) => {
|
||||||
|
if (Separator.isSeparator(item))
|
||||||
|
return false;
|
||||||
|
selectableIndex++;
|
||||||
|
return selectableIndex === selectedIndex;
|
||||||
|
});
|
||||||
|
const item = items[position];
|
||||||
|
if (item != null && isSelectable(item)) {
|
||||||
|
setActive(position);
|
||||||
|
}
|
||||||
|
searchTimeoutRef.current = setTimeout(() => {
|
||||||
|
rl.clearLine(0);
|
||||||
|
}, 700);
|
||||||
|
}
|
||||||
|
else if (isBackspaceKey(key)) {
|
||||||
|
rl.clearLine(0);
|
||||||
|
}
|
||||||
|
else if (searchEnabled) {
|
||||||
|
const searchTerm = rl.line.toLowerCase();
|
||||||
|
const matchIndex = items.findIndex((item) => {
|
||||||
|
if (Separator.isSeparator(item) || !isSelectable(item))
|
||||||
|
return false;
|
||||||
|
return item.name.toLowerCase().startsWith(searchTerm);
|
||||||
|
});
|
||||||
|
if (matchIndex !== -1) {
|
||||||
|
setActive(matchIndex);
|
||||||
|
}
|
||||||
|
searchTimeoutRef.current = setTimeout(() => {
|
||||||
|
rl.clearLine(0);
|
||||||
|
}, 700);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
useEffect(() => () => {
|
||||||
|
clearTimeout(searchTimeoutRef.current);
|
||||||
|
}, []);
|
||||||
|
const message = theme.style.message(config.message, status);
|
||||||
|
const helpLine = theme.style.keysHelpTip([
|
||||||
|
['↑↓', 'navigate'],
|
||||||
|
['⏎', 'select'],
|
||||||
|
]);
|
||||||
|
let separatorCount = 0;
|
||||||
|
const page = usePagination({
|
||||||
|
items,
|
||||||
|
active,
|
||||||
|
renderItem({ item, isActive, index }) {
|
||||||
|
if (Separator.isSeparator(item)) {
|
||||||
|
separatorCount++;
|
||||||
|
return ` ${item.separator}`;
|
||||||
|
}
|
||||||
|
const cursor = isActive ? theme.icon.cursor : ' ';
|
||||||
|
const indexLabel = theme.indexMode === 'number' ? `${index + 1 - separatorCount}. ` : '';
|
||||||
|
if (item.disabled) {
|
||||||
|
const disabledLabel = typeof item.disabled === 'string' ? item.disabled : '(disabled)';
|
||||||
|
const disabledCursor = isActive ? theme.icon.cursor : '-';
|
||||||
|
return theme.style.disabled(`${disabledCursor} ${indexLabel}${item.name} ${disabledLabel}`);
|
||||||
|
}
|
||||||
|
const color = isActive ? theme.style.highlight : (x) => x;
|
||||||
|
return color(`${cursor} ${indexLabel}${item.name}`);
|
||||||
|
},
|
||||||
|
pageSize,
|
||||||
|
loop,
|
||||||
|
});
|
||||||
|
if (status === 'done') {
|
||||||
|
return [prefix, message, theme.style.answer(selectedChoice.short)]
|
||||||
|
.filter(Boolean)
|
||||||
|
.join(' ');
|
||||||
|
}
|
||||||
|
const { description } = selectedChoice;
|
||||||
|
const lines = [
|
||||||
|
[prefix, message].filter(Boolean).join(' '),
|
||||||
|
page,
|
||||||
|
' ',
|
||||||
|
description ? theme.style.description(description) : '',
|
||||||
|
errorMsg ? theme.style.error(errorMsg) : '',
|
||||||
|
helpLine,
|
||||||
|
]
|
||||||
|
.filter(Boolean)
|
||||||
|
.join('\n')
|
||||||
|
.trimEnd();
|
||||||
|
return `${lines}${cursorHide}`;
|
||||||
|
});
|
||||||
|
export { Separator } from '@inquirer/core';
|
||||||
93
node_modules/@inquirer/select/package.json
generated
vendored
Normal file
93
node_modules/@inquirer/select/package.json
generated
vendored
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
{
|
||||||
|
"name": "@inquirer/select",
|
||||||
|
"version": "5.1.0",
|
||||||
|
"description": "Inquirer select/list prompt",
|
||||||
|
"keywords": [
|
||||||
|
"answer",
|
||||||
|
"answers",
|
||||||
|
"ask",
|
||||||
|
"base",
|
||||||
|
"cli",
|
||||||
|
"command",
|
||||||
|
"command-line",
|
||||||
|
"confirm",
|
||||||
|
"enquirer",
|
||||||
|
"generate",
|
||||||
|
"generator",
|
||||||
|
"hyper",
|
||||||
|
"input",
|
||||||
|
"inquire",
|
||||||
|
"inquirer",
|
||||||
|
"interface",
|
||||||
|
"iterm",
|
||||||
|
"javascript",
|
||||||
|
"menu",
|
||||||
|
"node",
|
||||||
|
"nodejs",
|
||||||
|
"prompt",
|
||||||
|
"promptly",
|
||||||
|
"prompts",
|
||||||
|
"question",
|
||||||
|
"readline",
|
||||||
|
"scaffold",
|
||||||
|
"scaffolder",
|
||||||
|
"scaffolding",
|
||||||
|
"stdin",
|
||||||
|
"stdout",
|
||||||
|
"terminal",
|
||||||
|
"tty",
|
||||||
|
"ui",
|
||||||
|
"yeoman",
|
||||||
|
"yo",
|
||||||
|
"zsh"
|
||||||
|
],
|
||||||
|
"homepage": "https://github.com/SBoudrias/Inquirer.js/blob/main/packages/select/README.md",
|
||||||
|
"license": "MIT",
|
||||||
|
"author": "Simon Boudrias <admin@simonboudrias.com>",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/SBoudrias/Inquirer.js.git"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"dist"
|
||||||
|
],
|
||||||
|
"type": "module",
|
||||||
|
"sideEffects": false,
|
||||||
|
"exports": {
|
||||||
|
".": {
|
||||||
|
"types": "./dist/index.d.ts",
|
||||||
|
"default": "./dist/index.js"
|
||||||
|
},
|
||||||
|
"./package.json": "./package.json"
|
||||||
|
},
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"tsc": "tsc"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@inquirer/ansi": "^2.0.3",
|
||||||
|
"@inquirer/core": "^11.1.5",
|
||||||
|
"@inquirer/figures": "^2.0.3",
|
||||||
|
"@inquirer/type": "^4.0.3"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@inquirer/testing": "^3.3.0",
|
||||||
|
"typescript": "^5.9.3"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/node": ">=18"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/node": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0"
|
||||||
|
},
|
||||||
|
"main": "./dist/index.js",
|
||||||
|
"types": "./dist/index.d.ts",
|
||||||
|
"gitHead": "526eca2e64853510821ffd457561840ec0cbfb93"
|
||||||
|
}
|
||||||
22
node_modules/@inquirer/type/LICENSE
generated
vendored
Normal file
22
node_modules/@inquirer/type/LICENSE
generated
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
Copyright (c) 2025 Simon Boudrias
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person
|
||||||
|
obtaining a copy of this software and associated documentation
|
||||||
|
files (the "Software"), to deal in the Software without
|
||||||
|
restriction, including without limitation the rights to use,
|
||||||
|
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the
|
||||||
|
Software is furnished to do so, subject to the following
|
||||||
|
conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||||
|
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||||
|
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||||
|
OTHER DEALINGS IN THE SOFTWARE.
|
||||||
2
node_modules/@inquirer/type/dist/index.d.ts
generated
vendored
Normal file
2
node_modules/@inquirer/type/dist/index.d.ts
generated
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export * from './inquirer.ts';
|
||||||
|
export * from './utils.ts';
|
||||||
2
node_modules/@inquirer/type/dist/index.js
generated
vendored
Normal file
2
node_modules/@inquirer/type/dist/index.js
generated
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export * from "./inquirer.js";
|
||||||
|
export * from "./utils.js";
|
||||||
35
node_modules/@inquirer/type/dist/inquirer.d.ts
generated
vendored
Normal file
35
node_modules/@inquirer/type/dist/inquirer.d.ts
generated
vendored
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import { Duplex } from 'node:stream';
|
||||||
|
/**
|
||||||
|
* `InquirerReadline` is a re-implementation of `readline.Interface` from Node.js.
|
||||||
|
* We're reimplementing it because of 3 reasons:
|
||||||
|
* 1. The `readline.Interface` API is not complete; it's missing for example `clearLine`.
|
||||||
|
* 2. The input/output streams are not generics, meaning they're inexact.
|
||||||
|
* 3. Since ReadLine isn't built-in Typescript Global NodeJS type, it'd force us to ship `@types/node` as a dependency to all users.
|
||||||
|
*/
|
||||||
|
export type InquirerReadline = {
|
||||||
|
output: Duplex & {
|
||||||
|
mute: () => void;
|
||||||
|
unmute: () => void;
|
||||||
|
};
|
||||||
|
input: NodeJS.ReadableStream;
|
||||||
|
clearLine: (dir: 0 | 1 | -1) => void;
|
||||||
|
getCursorPos: () => {
|
||||||
|
rows: number;
|
||||||
|
cols: number;
|
||||||
|
};
|
||||||
|
setPrompt: (prompt: string) => void;
|
||||||
|
line: string;
|
||||||
|
write: (data: string) => void;
|
||||||
|
on: (event: string, listener: (...args: unknown[]) => void) => void;
|
||||||
|
removeListener: (event: string, listener: (...args: unknown[]) => void) => void;
|
||||||
|
pause: () => void;
|
||||||
|
resume: () => void;
|
||||||
|
close: () => void;
|
||||||
|
};
|
||||||
|
export type Context = {
|
||||||
|
input?: NodeJS.ReadableStream;
|
||||||
|
output?: NodeJS.WritableStream;
|
||||||
|
clearPromptOnDone?: boolean;
|
||||||
|
signal?: AbortSignal;
|
||||||
|
};
|
||||||
|
export type Prompt<Value, Config> = (config: Config, context?: Context) => Promise<Value>;
|
||||||
1
node_modules/@inquirer/type/dist/inquirer.js
generated
vendored
Normal file
1
node_modules/@inquirer/type/dist/inquirer.js
generated
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
export {};
|
||||||
29
node_modules/@inquirer/type/dist/utils.d.ts
generated
vendored
Normal file
29
node_modules/@inquirer/type/dist/utils.d.ts
generated
vendored
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
type Key = string | number | symbol;
|
||||||
|
export type Prettify<T> = {
|
||||||
|
[K in keyof T]: T[K];
|
||||||
|
} & {};
|
||||||
|
export type PartialDeep<T> = T extends object ? {
|
||||||
|
[P in keyof T]?: PartialDeep<T[P]>;
|
||||||
|
} : T;
|
||||||
|
export type LiteralUnion<T extends F, F = string> = T | (F & {});
|
||||||
|
export type KeyUnion<T> = LiteralUnion<Extract<keyof T, string>>;
|
||||||
|
export type DistributiveMerge<A, B> = A extends any ? Prettify<Omit<A, keyof B> & B> : never;
|
||||||
|
export type UnionToIntersection<T> = (T extends any ? (input: T) => void : never) extends (input: infer Intersection) => void ? Intersection : never;
|
||||||
|
/**
|
||||||
|
* @hidden
|
||||||
|
*/
|
||||||
|
type __Pick<O extends object, K extends keyof O> = {
|
||||||
|
[P in K]: O[P];
|
||||||
|
} & {};
|
||||||
|
/**
|
||||||
|
* @hidden
|
||||||
|
*/
|
||||||
|
export type _Pick<O extends object, K extends Key> = __Pick<O, keyof O & K>;
|
||||||
|
/**
|
||||||
|
* Extract out of `O` the fields of key `K`
|
||||||
|
* @param O to extract from
|
||||||
|
* @param K to chose fields
|
||||||
|
* @returns [[Object]]
|
||||||
|
*/
|
||||||
|
export type Pick<O extends object, K extends Key> = O extends unknown ? _Pick<O, K> : never;
|
||||||
|
export {};
|
||||||
2
node_modules/@inquirer/type/dist/utils.js
generated
vendored
Normal file
2
node_modules/@inquirer/type/dist/utils.js
generated
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
|
export {};
|
||||||
87
node_modules/@inquirer/type/package.json
generated
vendored
Normal file
87
node_modules/@inquirer/type/package.json
generated
vendored
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
{
|
||||||
|
"name": "@inquirer/type",
|
||||||
|
"version": "4.0.3",
|
||||||
|
"description": "Inquirer core TS types",
|
||||||
|
"keywords": [
|
||||||
|
"answer",
|
||||||
|
"answers",
|
||||||
|
"ask",
|
||||||
|
"base",
|
||||||
|
"cli",
|
||||||
|
"command",
|
||||||
|
"command-line",
|
||||||
|
"confirm",
|
||||||
|
"enquirer",
|
||||||
|
"generate",
|
||||||
|
"generator",
|
||||||
|
"hyper",
|
||||||
|
"input",
|
||||||
|
"inquire",
|
||||||
|
"inquirer",
|
||||||
|
"interface",
|
||||||
|
"iterm",
|
||||||
|
"javascript",
|
||||||
|
"menu",
|
||||||
|
"node",
|
||||||
|
"nodejs",
|
||||||
|
"prompt",
|
||||||
|
"promptly",
|
||||||
|
"prompts",
|
||||||
|
"question",
|
||||||
|
"readline",
|
||||||
|
"scaffold",
|
||||||
|
"scaffolder",
|
||||||
|
"scaffolding",
|
||||||
|
"stdin",
|
||||||
|
"stdout",
|
||||||
|
"terminal",
|
||||||
|
"tty",
|
||||||
|
"types",
|
||||||
|
"typescript",
|
||||||
|
"ui",
|
||||||
|
"yeoman",
|
||||||
|
"yo",
|
||||||
|
"zsh"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"author": "Simon Boudrias <admin@simonboudrias.com>",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/SBoudrias/Inquirer.js.git"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"dist"
|
||||||
|
],
|
||||||
|
"type": "module",
|
||||||
|
"sideEffects": false,
|
||||||
|
"exports": {
|
||||||
|
".": {
|
||||||
|
"types": "./dist/index.d.ts",
|
||||||
|
"default": "./dist/index.js"
|
||||||
|
},
|
||||||
|
"./package.json": "./package.json"
|
||||||
|
},
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"tsc": "tsc"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"typescript": "^5.9.3"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/node": ">=18"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/node": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0"
|
||||||
|
},
|
||||||
|
"main": "./dist/index.js",
|
||||||
|
"types": "./dist/index.d.ts",
|
||||||
|
"gitHead": "99d00a9adc53be8b7edf5926b2ec4ba0b792f68f"
|
||||||
|
}
|
||||||
21
node_modules/@nodelib/fs.scandir/LICENSE
generated
vendored
Normal file
21
node_modules/@nodelib/fs.scandir/LICENSE
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) Denis Malinochkin
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
171
node_modules/@nodelib/fs.scandir/README.md
generated
vendored
Normal file
171
node_modules/@nodelib/fs.scandir/README.md
generated
vendored
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
# @nodelib/fs.scandir
|
||||||
|
|
||||||
|
> List files and directories inside the specified directory.
|
||||||
|
|
||||||
|
## :bulb: Highlights
|
||||||
|
|
||||||
|
The package is aimed at obtaining information about entries in the directory.
|
||||||
|
|
||||||
|
* :moneybag: Returns useful information: `name`, `path`, `dirent` and `stats` (optional).
|
||||||
|
* :gear: On Node.js 10.10+ uses the mechanism without additional calls to determine the entry type. See [`old` and `modern` mode](#old-and-modern-mode).
|
||||||
|
* :link: Can safely work with broken symbolic links.
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
```console
|
||||||
|
npm install @nodelib/fs.scandir
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import * as fsScandir from '@nodelib/fs.scandir';
|
||||||
|
|
||||||
|
fsScandir.scandir('path', (error, stats) => { /* … */ });
|
||||||
|
```
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
|
### .scandir(path, [optionsOrSettings], callback)
|
||||||
|
|
||||||
|
Returns an array of plain objects ([`Entry`](#entry)) with information about entry for provided path with standard callback-style.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
fsScandir.scandir('path', (error, entries) => { /* … */ });
|
||||||
|
fsScandir.scandir('path', {}, (error, entries) => { /* … */ });
|
||||||
|
fsScandir.scandir('path', new fsScandir.Settings(), (error, entries) => { /* … */ });
|
||||||
|
```
|
||||||
|
|
||||||
|
### .scandirSync(path, [optionsOrSettings])
|
||||||
|
|
||||||
|
Returns an array of plain objects ([`Entry`](#entry)) with information about entry for provided path.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const entries = fsScandir.scandirSync('path');
|
||||||
|
const entries = fsScandir.scandirSync('path', {});
|
||||||
|
const entries = fsScandir.scandirSync(('path', new fsScandir.Settings());
|
||||||
|
```
|
||||||
|
|
||||||
|
#### path
|
||||||
|
|
||||||
|
* Required: `true`
|
||||||
|
* Type: `string | Buffer | URL`
|
||||||
|
|
||||||
|
A path to a file. If a URL is provided, it must use the `file:` protocol.
|
||||||
|
|
||||||
|
#### optionsOrSettings
|
||||||
|
|
||||||
|
* Required: `false`
|
||||||
|
* Type: `Options | Settings`
|
||||||
|
* Default: An instance of `Settings` class
|
||||||
|
|
||||||
|
An [`Options`](#options) object or an instance of [`Settings`](#settingsoptions) class.
|
||||||
|
|
||||||
|
> :book: When you pass a plain object, an instance of the `Settings` class will be created automatically. If you plan to call the method frequently, use a pre-created instance of the `Settings` class.
|
||||||
|
|
||||||
|
### Settings([options])
|
||||||
|
|
||||||
|
A class of full settings of the package.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const settings = new fsScandir.Settings({ followSymbolicLinks: false });
|
||||||
|
|
||||||
|
const entries = fsScandir.scandirSync('path', settings);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Entry
|
||||||
|
|
||||||
|
* `name` — The name of the entry (`unknown.txt`).
|
||||||
|
* `path` — The path of the entry relative to call directory (`root/unknown.txt`).
|
||||||
|
* `dirent` — An instance of [`fs.Dirent`](./src/types/index.ts) class. On Node.js below 10.10 will be emulated by [`DirentFromStats`](./src/utils/fs.ts) class.
|
||||||
|
* `stats` (optional) — An instance of `fs.Stats` class.
|
||||||
|
|
||||||
|
For example, the `scandir` call for `tools` directory with one directory inside:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
{
|
||||||
|
dirent: Dirent { name: 'typedoc', /* … */ },
|
||||||
|
name: 'typedoc',
|
||||||
|
path: 'tools/typedoc'
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Options
|
||||||
|
|
||||||
|
### stats
|
||||||
|
|
||||||
|
* Type: `boolean`
|
||||||
|
* Default: `false`
|
||||||
|
|
||||||
|
Adds an instance of `fs.Stats` class to the [`Entry`](#entry).
|
||||||
|
|
||||||
|
> :book: Always use `fs.readdir` without the `withFileTypes` option. ??TODO??
|
||||||
|
|
||||||
|
### followSymbolicLinks
|
||||||
|
|
||||||
|
* Type: `boolean`
|
||||||
|
* Default: `false`
|
||||||
|
|
||||||
|
Follow symbolic links or not. Call `fs.stat` on symbolic link if `true`.
|
||||||
|
|
||||||
|
### `throwErrorOnBrokenSymbolicLink`
|
||||||
|
|
||||||
|
* Type: `boolean`
|
||||||
|
* Default: `true`
|
||||||
|
|
||||||
|
Throw an error when symbolic link is broken if `true` or safely use `lstat` call if `false`.
|
||||||
|
|
||||||
|
### `pathSegmentSeparator`
|
||||||
|
|
||||||
|
* Type: `string`
|
||||||
|
* Default: `path.sep`
|
||||||
|
|
||||||
|
By default, this package uses the correct path separator for your OS (`\` on Windows, `/` on Unix-like systems). But you can set this option to any separator character(s) that you want to use instead.
|
||||||
|
|
||||||
|
### `fs`
|
||||||
|
|
||||||
|
* Type: [`FileSystemAdapter`](./src/adapters/fs.ts)
|
||||||
|
* Default: A default FS methods
|
||||||
|
|
||||||
|
By default, the built-in Node.js module (`fs`) is used to work with the file system. You can replace any method with your own.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
interface FileSystemAdapter {
|
||||||
|
lstat?: typeof fs.lstat;
|
||||||
|
stat?: typeof fs.stat;
|
||||||
|
lstatSync?: typeof fs.lstatSync;
|
||||||
|
statSync?: typeof fs.statSync;
|
||||||
|
readdir?: typeof fs.readdir;
|
||||||
|
readdirSync?: typeof fs.readdirSync;
|
||||||
|
}
|
||||||
|
|
||||||
|
const settings = new fsScandir.Settings({
|
||||||
|
fs: { lstat: fakeLstat }
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## `old` and `modern` mode
|
||||||
|
|
||||||
|
This package has two modes that are used depending on the environment and parameters of use.
|
||||||
|
|
||||||
|
### old
|
||||||
|
|
||||||
|
* Node.js below `10.10` or when the `stats` option is enabled
|
||||||
|
|
||||||
|
When working in the old mode, the directory is read first (`fs.readdir`), then the type of entries is determined (`fs.lstat` and/or `fs.stat` for symbolic links).
|
||||||
|
|
||||||
|
### modern
|
||||||
|
|
||||||
|
* Node.js 10.10+ and the `stats` option is disabled
|
||||||
|
|
||||||
|
In the modern mode, reading the directory (`fs.readdir` with the `withFileTypes` option) is combined with obtaining information about its entries. An additional call for symbolic links (`fs.stat`) is still present.
|
||||||
|
|
||||||
|
This mode makes fewer calls to the file system. It's faster.
|
||||||
|
|
||||||
|
## Changelog
|
||||||
|
|
||||||
|
See the [Releases section of our GitHub project](https://github.com/nodelib/nodelib/releases) for changelog for each release version.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
This software is released under the terms of the MIT license.
|
||||||
20
node_modules/@nodelib/fs.scandir/out/adapters/fs.d.ts
generated
vendored
Normal file
20
node_modules/@nodelib/fs.scandir/out/adapters/fs.d.ts
generated
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import type * as fsStat from '@nodelib/fs.stat';
|
||||||
|
import type { Dirent, ErrnoException } from '../types';
|
||||||
|
export interface ReaddirAsynchronousMethod {
|
||||||
|
(filepath: string, options: {
|
||||||
|
withFileTypes: true;
|
||||||
|
}, callback: (error: ErrnoException | null, files: Dirent[]) => void): void;
|
||||||
|
(filepath: string, callback: (error: ErrnoException | null, files: string[]) => void): void;
|
||||||
|
}
|
||||||
|
export interface ReaddirSynchronousMethod {
|
||||||
|
(filepath: string, options: {
|
||||||
|
withFileTypes: true;
|
||||||
|
}): Dirent[];
|
||||||
|
(filepath: string): string[];
|
||||||
|
}
|
||||||
|
export declare type FileSystemAdapter = fsStat.FileSystemAdapter & {
|
||||||
|
readdir: ReaddirAsynchronousMethod;
|
||||||
|
readdirSync: ReaddirSynchronousMethod;
|
||||||
|
};
|
||||||
|
export declare const FILE_SYSTEM_ADAPTER: FileSystemAdapter;
|
||||||
|
export declare function createFileSystemAdapter(fsMethods?: Partial<FileSystemAdapter>): FileSystemAdapter;
|
||||||
19
node_modules/@nodelib/fs.scandir/out/adapters/fs.js
generated
vendored
Normal file
19
node_modules/@nodelib/fs.scandir/out/adapters/fs.js
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
"use strict";
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
exports.createFileSystemAdapter = exports.FILE_SYSTEM_ADAPTER = void 0;
|
||||||
|
const fs = require("fs");
|
||||||
|
exports.FILE_SYSTEM_ADAPTER = {
|
||||||
|
lstat: fs.lstat,
|
||||||
|
stat: fs.stat,
|
||||||
|
lstatSync: fs.lstatSync,
|
||||||
|
statSync: fs.statSync,
|
||||||
|
readdir: fs.readdir,
|
||||||
|
readdirSync: fs.readdirSync
|
||||||
|
};
|
||||||
|
function createFileSystemAdapter(fsMethods) {
|
||||||
|
if (fsMethods === undefined) {
|
||||||
|
return exports.FILE_SYSTEM_ADAPTER;
|
||||||
|
}
|
||||||
|
return Object.assign(Object.assign({}, exports.FILE_SYSTEM_ADAPTER), fsMethods);
|
||||||
|
}
|
||||||
|
exports.createFileSystemAdapter = createFileSystemAdapter;
|
||||||
4
node_modules/@nodelib/fs.scandir/out/constants.d.ts
generated
vendored
Normal file
4
node_modules/@nodelib/fs.scandir/out/constants.d.ts
generated
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
/**
|
||||||
|
* IS `true` for Node.js 10.10 and greater.
|
||||||
|
*/
|
||||||
|
export declare const IS_SUPPORT_READDIR_WITH_FILE_TYPES: boolean;
|
||||||
17
node_modules/@nodelib/fs.scandir/out/constants.js
generated
vendored
Normal file
17
node_modules/@nodelib/fs.scandir/out/constants.js
generated
vendored
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
"use strict";
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
exports.IS_SUPPORT_READDIR_WITH_FILE_TYPES = void 0;
|
||||||
|
const NODE_PROCESS_VERSION_PARTS = process.versions.node.split('.');
|
||||||
|
if (NODE_PROCESS_VERSION_PARTS[0] === undefined || NODE_PROCESS_VERSION_PARTS[1] === undefined) {
|
||||||
|
throw new Error(`Unexpected behavior. The 'process.versions.node' variable has invalid value: ${process.versions.node}`);
|
||||||
|
}
|
||||||
|
const MAJOR_VERSION = Number.parseInt(NODE_PROCESS_VERSION_PARTS[0], 10);
|
||||||
|
const MINOR_VERSION = Number.parseInt(NODE_PROCESS_VERSION_PARTS[1], 10);
|
||||||
|
const SUPPORTED_MAJOR_VERSION = 10;
|
||||||
|
const SUPPORTED_MINOR_VERSION = 10;
|
||||||
|
const IS_MATCHED_BY_MAJOR = MAJOR_VERSION > SUPPORTED_MAJOR_VERSION;
|
||||||
|
const IS_MATCHED_BY_MAJOR_AND_MINOR = MAJOR_VERSION === SUPPORTED_MAJOR_VERSION && MINOR_VERSION >= SUPPORTED_MINOR_VERSION;
|
||||||
|
/**
|
||||||
|
* IS `true` for Node.js 10.10 and greater.
|
||||||
|
*/
|
||||||
|
exports.IS_SUPPORT_READDIR_WITH_FILE_TYPES = IS_MATCHED_BY_MAJOR || IS_MATCHED_BY_MAJOR_AND_MINOR;
|
||||||
12
node_modules/@nodelib/fs.scandir/out/index.d.ts
generated
vendored
Normal file
12
node_modules/@nodelib/fs.scandir/out/index.d.ts
generated
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import type { FileSystemAdapter, ReaddirAsynchronousMethod, ReaddirSynchronousMethod } from './adapters/fs';
|
||||||
|
import * as async from './providers/async';
|
||||||
|
import Settings, { Options } from './settings';
|
||||||
|
import type { Dirent, Entry } from './types';
|
||||||
|
declare type AsyncCallback = async.AsyncCallback;
|
||||||
|
declare function scandir(path: string, callback: AsyncCallback): void;
|
||||||
|
declare function scandir(path: string, optionsOrSettings: Options | Settings, callback: AsyncCallback): void;
|
||||||
|
declare namespace scandir {
|
||||||
|
function __promisify__(path: string, optionsOrSettings?: Options | Settings): Promise<Entry[]>;
|
||||||
|
}
|
||||||
|
declare function scandirSync(path: string, optionsOrSettings?: Options | Settings): Entry[];
|
||||||
|
export { scandir, scandirSync, Settings, AsyncCallback, Dirent, Entry, FileSystemAdapter, ReaddirAsynchronousMethod, ReaddirSynchronousMethod, Options };
|
||||||
26
node_modules/@nodelib/fs.scandir/out/index.js
generated
vendored
Normal file
26
node_modules/@nodelib/fs.scandir/out/index.js
generated
vendored
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
"use strict";
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
exports.Settings = exports.scandirSync = exports.scandir = void 0;
|
||||||
|
const async = require("./providers/async");
|
||||||
|
const sync = require("./providers/sync");
|
||||||
|
const settings_1 = require("./settings");
|
||||||
|
exports.Settings = settings_1.default;
|
||||||
|
function scandir(path, optionsOrSettingsOrCallback, callback) {
|
||||||
|
if (typeof optionsOrSettingsOrCallback === 'function') {
|
||||||
|
async.read(path, getSettings(), optionsOrSettingsOrCallback);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
async.read(path, getSettings(optionsOrSettingsOrCallback), callback);
|
||||||
|
}
|
||||||
|
exports.scandir = scandir;
|
||||||
|
function scandirSync(path, optionsOrSettings) {
|
||||||
|
const settings = getSettings(optionsOrSettings);
|
||||||
|
return sync.read(path, settings);
|
||||||
|
}
|
||||||
|
exports.scandirSync = scandirSync;
|
||||||
|
function getSettings(settingsOrOptions = {}) {
|
||||||
|
if (settingsOrOptions instanceof settings_1.default) {
|
||||||
|
return settingsOrOptions;
|
||||||
|
}
|
||||||
|
return new settings_1.default(settingsOrOptions);
|
||||||
|
}
|
||||||
7
node_modules/@nodelib/fs.scandir/out/providers/async.d.ts
generated
vendored
Normal file
7
node_modules/@nodelib/fs.scandir/out/providers/async.d.ts
generated
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
/// <reference types="node" />
|
||||||
|
import type Settings from '../settings';
|
||||||
|
import type { Entry } from '../types';
|
||||||
|
export declare type AsyncCallback = (error: NodeJS.ErrnoException, entries: Entry[]) => void;
|
||||||
|
export declare function read(directory: string, settings: Settings, callback: AsyncCallback): void;
|
||||||
|
export declare function readdirWithFileTypes(directory: string, settings: Settings, callback: AsyncCallback): void;
|
||||||
|
export declare function readdir(directory: string, settings: Settings, callback: AsyncCallback): void;
|
||||||
104
node_modules/@nodelib/fs.scandir/out/providers/async.js
generated
vendored
Normal file
104
node_modules/@nodelib/fs.scandir/out/providers/async.js
generated
vendored
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
"use strict";
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
exports.readdir = exports.readdirWithFileTypes = exports.read = void 0;
|
||||||
|
const fsStat = require("@nodelib/fs.stat");
|
||||||
|
const rpl = require("run-parallel");
|
||||||
|
const constants_1 = require("../constants");
|
||||||
|
const utils = require("../utils");
|
||||||
|
const common = require("./common");
|
||||||
|
function read(directory, settings, callback) {
|
||||||
|
if (!settings.stats && constants_1.IS_SUPPORT_READDIR_WITH_FILE_TYPES) {
|
||||||
|
readdirWithFileTypes(directory, settings, callback);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
readdir(directory, settings, callback);
|
||||||
|
}
|
||||||
|
exports.read = read;
|
||||||
|
function readdirWithFileTypes(directory, settings, callback) {
|
||||||
|
settings.fs.readdir(directory, { withFileTypes: true }, (readdirError, dirents) => {
|
||||||
|
if (readdirError !== null) {
|
||||||
|
callFailureCallback(callback, readdirError);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const entries = dirents.map((dirent) => ({
|
||||||
|
dirent,
|
||||||
|
name: dirent.name,
|
||||||
|
path: common.joinPathSegments(directory, dirent.name, settings.pathSegmentSeparator)
|
||||||
|
}));
|
||||||
|
if (!settings.followSymbolicLinks) {
|
||||||
|
callSuccessCallback(callback, entries);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const tasks = entries.map((entry) => makeRplTaskEntry(entry, settings));
|
||||||
|
rpl(tasks, (rplError, rplEntries) => {
|
||||||
|
if (rplError !== null) {
|
||||||
|
callFailureCallback(callback, rplError);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
callSuccessCallback(callback, rplEntries);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
exports.readdirWithFileTypes = readdirWithFileTypes;
|
||||||
|
function makeRplTaskEntry(entry, settings) {
|
||||||
|
return (done) => {
|
||||||
|
if (!entry.dirent.isSymbolicLink()) {
|
||||||
|
done(null, entry);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
settings.fs.stat(entry.path, (statError, stats) => {
|
||||||
|
if (statError !== null) {
|
||||||
|
if (settings.throwErrorOnBrokenSymbolicLink) {
|
||||||
|
done(statError);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
done(null, entry);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
entry.dirent = utils.fs.createDirentFromStats(entry.name, stats);
|
||||||
|
done(null, entry);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
function readdir(directory, settings, callback) {
|
||||||
|
settings.fs.readdir(directory, (readdirError, names) => {
|
||||||
|
if (readdirError !== null) {
|
||||||
|
callFailureCallback(callback, readdirError);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const tasks = names.map((name) => {
|
||||||
|
const path = common.joinPathSegments(directory, name, settings.pathSegmentSeparator);
|
||||||
|
return (done) => {
|
||||||
|
fsStat.stat(path, settings.fsStatSettings, (error, stats) => {
|
||||||
|
if (error !== null) {
|
||||||
|
done(error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const entry = {
|
||||||
|
name,
|
||||||
|
path,
|
||||||
|
dirent: utils.fs.createDirentFromStats(name, stats)
|
||||||
|
};
|
||||||
|
if (settings.stats) {
|
||||||
|
entry.stats = stats;
|
||||||
|
}
|
||||||
|
done(null, entry);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
});
|
||||||
|
rpl(tasks, (rplError, entries) => {
|
||||||
|
if (rplError !== null) {
|
||||||
|
callFailureCallback(callback, rplError);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
callSuccessCallback(callback, entries);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
exports.readdir = readdir;
|
||||||
|
function callFailureCallback(callback, error) {
|
||||||
|
callback(error);
|
||||||
|
}
|
||||||
|
function callSuccessCallback(callback, result) {
|
||||||
|
callback(null, result);
|
||||||
|
}
|
||||||
1
node_modules/@nodelib/fs.scandir/out/providers/common.d.ts
generated
vendored
Normal file
1
node_modules/@nodelib/fs.scandir/out/providers/common.d.ts
generated
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
export declare function joinPathSegments(a: string, b: string, separator: string): string;
|
||||||
13
node_modules/@nodelib/fs.scandir/out/providers/common.js
generated
vendored
Normal file
13
node_modules/@nodelib/fs.scandir/out/providers/common.js
generated
vendored
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
"use strict";
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
exports.joinPathSegments = void 0;
|
||||||
|
function joinPathSegments(a, b, separator) {
|
||||||
|
/**
|
||||||
|
* The correct handling of cases when the first segment is a root (`/`, `C:/`) or UNC path (`//?/C:/`).
|
||||||
|
*/
|
||||||
|
if (a.endsWith(separator)) {
|
||||||
|
return a + b;
|
||||||
|
}
|
||||||
|
return a + separator + b;
|
||||||
|
}
|
||||||
|
exports.joinPathSegments = joinPathSegments;
|
||||||
5
node_modules/@nodelib/fs.scandir/out/providers/sync.d.ts
generated
vendored
Normal file
5
node_modules/@nodelib/fs.scandir/out/providers/sync.d.ts
generated
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import type Settings from '../settings';
|
||||||
|
import type { Entry } from '../types';
|
||||||
|
export declare function read(directory: string, settings: Settings): Entry[];
|
||||||
|
export declare function readdirWithFileTypes(directory: string, settings: Settings): Entry[];
|
||||||
|
export declare function readdir(directory: string, settings: Settings): Entry[];
|
||||||
54
node_modules/@nodelib/fs.scandir/out/providers/sync.js
generated
vendored
Normal file
54
node_modules/@nodelib/fs.scandir/out/providers/sync.js
generated
vendored
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
"use strict";
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
exports.readdir = exports.readdirWithFileTypes = exports.read = void 0;
|
||||||
|
const fsStat = require("@nodelib/fs.stat");
|
||||||
|
const constants_1 = require("../constants");
|
||||||
|
const utils = require("../utils");
|
||||||
|
const common = require("./common");
|
||||||
|
function read(directory, settings) {
|
||||||
|
if (!settings.stats && constants_1.IS_SUPPORT_READDIR_WITH_FILE_TYPES) {
|
||||||
|
return readdirWithFileTypes(directory, settings);
|
||||||
|
}
|
||||||
|
return readdir(directory, settings);
|
||||||
|
}
|
||||||
|
exports.read = read;
|
||||||
|
function readdirWithFileTypes(directory, settings) {
|
||||||
|
const dirents = settings.fs.readdirSync(directory, { withFileTypes: true });
|
||||||
|
return dirents.map((dirent) => {
|
||||||
|
const entry = {
|
||||||
|
dirent,
|
||||||
|
name: dirent.name,
|
||||||
|
path: common.joinPathSegments(directory, dirent.name, settings.pathSegmentSeparator)
|
||||||
|
};
|
||||||
|
if (entry.dirent.isSymbolicLink() && settings.followSymbolicLinks) {
|
||||||
|
try {
|
||||||
|
const stats = settings.fs.statSync(entry.path);
|
||||||
|
entry.dirent = utils.fs.createDirentFromStats(entry.name, stats);
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
if (settings.throwErrorOnBrokenSymbolicLink) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return entry;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
exports.readdirWithFileTypes = readdirWithFileTypes;
|
||||||
|
function readdir(directory, settings) {
|
||||||
|
const names = settings.fs.readdirSync(directory);
|
||||||
|
return names.map((name) => {
|
||||||
|
const entryPath = common.joinPathSegments(directory, name, settings.pathSegmentSeparator);
|
||||||
|
const stats = fsStat.statSync(entryPath, settings.fsStatSettings);
|
||||||
|
const entry = {
|
||||||
|
name,
|
||||||
|
path: entryPath,
|
||||||
|
dirent: utils.fs.createDirentFromStats(name, stats)
|
||||||
|
};
|
||||||
|
if (settings.stats) {
|
||||||
|
entry.stats = stats;
|
||||||
|
}
|
||||||
|
return entry;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
exports.readdir = readdir;
|
||||||
20
node_modules/@nodelib/fs.scandir/out/settings.d.ts
generated
vendored
Normal file
20
node_modules/@nodelib/fs.scandir/out/settings.d.ts
generated
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import * as fsStat from '@nodelib/fs.stat';
|
||||||
|
import * as fs from './adapters/fs';
|
||||||
|
export interface Options {
|
||||||
|
followSymbolicLinks?: boolean;
|
||||||
|
fs?: Partial<fs.FileSystemAdapter>;
|
||||||
|
pathSegmentSeparator?: string;
|
||||||
|
stats?: boolean;
|
||||||
|
throwErrorOnBrokenSymbolicLink?: boolean;
|
||||||
|
}
|
||||||
|
export default class Settings {
|
||||||
|
private readonly _options;
|
||||||
|
readonly followSymbolicLinks: boolean;
|
||||||
|
readonly fs: fs.FileSystemAdapter;
|
||||||
|
readonly pathSegmentSeparator: string;
|
||||||
|
readonly stats: boolean;
|
||||||
|
readonly throwErrorOnBrokenSymbolicLink: boolean;
|
||||||
|
readonly fsStatSettings: fsStat.Settings;
|
||||||
|
constructor(_options?: Options);
|
||||||
|
private _getValue;
|
||||||
|
}
|
||||||
24
node_modules/@nodelib/fs.scandir/out/settings.js
generated
vendored
Normal file
24
node_modules/@nodelib/fs.scandir/out/settings.js
generated
vendored
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
"use strict";
|
||||||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
|
const path = require("path");
|
||||||
|
const fsStat = require("@nodelib/fs.stat");
|
||||||
|
const fs = require("./adapters/fs");
|
||||||
|
class Settings {
|
||||||
|
constructor(_options = {}) {
|
||||||
|
this._options = _options;
|
||||||
|
this.followSymbolicLinks = this._getValue(this._options.followSymbolicLinks, false);
|
||||||
|
this.fs = fs.createFileSystemAdapter(this._options.fs);
|
||||||
|
this.pathSegmentSeparator = this._getValue(this._options.pathSegmentSeparator, path.sep);
|
||||||
|
this.stats = this._getValue(this._options.stats, false);
|
||||||
|
this.throwErrorOnBrokenSymbolicLink = this._getValue(this._options.throwErrorOnBrokenSymbolicLink, true);
|
||||||
|
this.fsStatSettings = new fsStat.Settings({
|
||||||
|
followSymbolicLink: this.followSymbolicLinks,
|
||||||
|
fs: this.fs,
|
||||||
|
throwErrorOnBrokenSymbolicLink: this.throwErrorOnBrokenSymbolicLink
|
||||||
|
});
|
||||||
|
}
|
||||||
|
_getValue(option, value) {
|
||||||
|
return option !== null && option !== void 0 ? option : value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exports.default = Settings;
|
||||||
20
node_modules/@nodelib/fs.scandir/out/types/index.d.ts
generated
vendored
Normal file
20
node_modules/@nodelib/fs.scandir/out/types/index.d.ts
generated
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
/// <reference types="node" />
|
||||||
|
import type * as fs from 'fs';
|
||||||
|
export interface Entry {
|
||||||
|
dirent: Dirent;
|
||||||
|
name: string;
|
||||||
|
path: string;
|
||||||
|
stats?: Stats;
|
||||||
|
}
|
||||||
|
export declare type Stats = fs.Stats;
|
||||||
|
export declare type ErrnoException = NodeJS.ErrnoException;
|
||||||
|
export interface Dirent {
|
||||||
|
isBlockDevice: () => boolean;
|
||||||
|
isCharacterDevice: () => boolean;
|
||||||
|
isDirectory: () => boolean;
|
||||||
|
isFIFO: () => boolean;
|
||||||
|
isFile: () => boolean;
|
||||||
|
isSocket: () => boolean;
|
||||||
|
isSymbolicLink: () => boolean;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user