interactive.ts•6.44 kB
/**
* Interactive REPL mode
*/
import inquirer from 'inquirer';
import chalk from 'chalk';
import { WhatapClient } from '../../core/client/WhatapClient';
import { MxqlExecutor, TimeRanges } from '../../core/executor/MxqlExecutor';
import { requireAuth } from '../utils/session';
import { formatOutput, formatQuerySummary, printSuccess, printError, printInfo, printWarning } from '../utils/formatters';
import type { Project } from '../../core/types';
interface InteractiveOptions {
project?: string;
}
/**
* Interactive command handler
*/
export async function interactiveCommand(options: InteractiveOptions): Promise<void> {
try {
console.log('');
console.log(chalk.cyan.bold('WhaTap MXQL Interactive Mode'));
console.log(chalk.gray('Type "help" for available commands, "exit" to quit'));
console.log('');
// Require authentication
const authManager = await requireAuth();
// Create client and executor
const client = new WhatapClient(authManager);
const executor = new MxqlExecutor(client);
// Get projects
printInfo('Loading projects...');
const projects = await client.getProjects();
console.log('');
printSuccess(`Loaded ${projects.length} projects`);
console.log('');
// Select default project
let currentProject: Project | undefined;
if (options.project) {
const pcode = parseInt(options.project, 10);
currentProject = projects.find(p => p.projectCode === pcode);
if (!currentProject) {
printWarning(`Project ${options.project} not found`);
} else {
console.log(chalk.gray('Default project:'), chalk.cyan(currentProject.projectName), chalk.gray(`(${currentProject.projectCode})`));
console.log('');
}
}
// REPL loop
let running = true;
while (running) {
const { command } = await inquirer.prompt([
{
type: 'input',
name: 'command',
message: currentProject
? chalk.cyan(`[${currentProject.projectCode}]`) + chalk.gray('>')
: chalk.gray('>'),
prefix: '',
},
]);
const trimmed = command.trim();
if (!trimmed) {
continue;
}
// Parse command
const parts = trimmed.split(/\s+/);
const cmd = parts[0].toLowerCase();
try {
switch (cmd) {
case 'help':
showHelp();
break;
case 'exit':
case 'quit':
console.log('');
printInfo('Goodbye!');
console.log('');
running = false;
break;
case 'projects':
case 'list':
listProjects(projects);
break;
case 'use':
case 'select':
if (parts.length < 2) {
printError('Usage: use <project_code>');
break;
}
const pcode = parseInt(parts[1], 10);
const project = projects.find(p => p.projectCode === pcode);
if (!project) {
printError(`Project ${parts[1]} not found. Use "projects" to list all projects`);
} else {
currentProject = project;
printSuccess(`Selected project: ${project.projectName} (${project.projectCode})`);
}
break;
case 'query':
case 'exec':
if (!currentProject) {
printError('No project selected. Use "use <project_code>" to select a project');
break;
}
const mxql = parts.slice(1).join(' ');
if (!mxql) {
printError('Usage: query <mxql>');
printInfo('Or enter multi-line MXQL directly');
break;
}
await executeQuery(executor, currentProject.projectCode, mxql);
break;
default:
// Treat as MXQL query if project is selected
if (currentProject) {
await executeQuery(executor, currentProject.projectCode, trimmed);
} else {
printError(`Unknown command: ${cmd}. Type "help" for available commands`);
}
break;
}
} catch (error: any) {
printError('Command failed', error);
}
console.log('');
}
} catch (error: any) {
console.log('');
printError('Interactive mode failed', error);
process.exit(1);
}
}
/**
* Show help message
*/
function showHelp(): void {
console.log('');
console.log(chalk.cyan.bold('Available Commands:'));
console.log('');
console.log(chalk.white(' help'), chalk.gray('- Show this help message'));
console.log(chalk.white(' projects'), chalk.gray('or'), chalk.white('list'), chalk.gray('- List all projects'));
console.log(chalk.white(' use <pcode>'), chalk.gray('- Select a project'));
console.log(chalk.white(' query <mxql>'), chalk.gray('- Execute MXQL query'));
console.log(chalk.white(' exit'), chalk.gray('or'), chalk.white('quit'), chalk.gray('- Exit interactive mode'));
console.log('');
console.log(chalk.cyan.bold('MXQL Shortcuts:'));
console.log('');
console.log(chalk.gray(' When a project is selected, you can type MXQL directly:'));
console.log(chalk.white(' > CATEGORY app_counter'));
console.log(chalk.white(' > TAGLOAD'));
console.log('');
}
/**
* List projects
*/
function listProjects(projects: Project[]): void {
console.log('');
console.log(chalk.cyan.bold(`Projects (${projects.length}):`));
console.log('');
projects.forEach((p, i) => {
const typeColor = p.productType === 'MOBILE' ? chalk.blue : p.productType === 'BROWSER' ? chalk.magenta : chalk.green;
console.log(
chalk.gray(` ${i + 1}.`),
chalk.white(p.projectCode),
chalk.gray('-'),
chalk.cyan(p.projectName),
typeColor(`[${p.productType}]`)
);
});
console.log('');
}
/**
* Execute MXQL query
*/
async function executeQuery(executor: MxqlExecutor, pcode: number, mxql: string): Promise<void> {
printInfo('Executing...');
const result = await executor.execute(
pcode,
mxql,
TimeRanges.lastHour(),
50
);
console.log('');
printSuccess('Query executed');
console.log('');
console.log(formatQuerySummary(result));
console.log('');
if (result.rowCount === 0) {
printWarning('No data returned');
} else {
console.log(formatOutput(result.data, 'table'));
}
}