ktx/packages/cli/src/commands/sql-commands.ts
Andrey Avtomonov 00cdf2de90
refactor: enforce ktx naming and AGENTS.md compliance sweep (#289)
Align the tree with AGENTS.md/CLAUDE.md conventions:

- Rewrite user-facing strings, docs, and tests to lowercase `ktx`
  (no bare uppercase `KTX` tokens remain outside literal identifiers).
- Drop the legacy `historicSql` migration path and its now-unused
  helpers, per the no-backward-compat rule.
- Remove `as unknown as` / `any` casts: narrow `BaseTool` generics to
  `z.ZodObject`, add a typed `createLookerClient`, and delete the dead
  `getParametersSchema`/`toAnthropicFormat` pre-AI-SDK helpers.
- Use `InvalidArgumentError` for Commander parse failures.
- Finish the adapter→connector prose conversion in the `ktx.yaml` docs
  while keeping the literal `adapters` config key.
2026-06-11 13:49:45 +02:00

62 lines
2.2 KiB
TypeScript

import { type Command, InvalidArgumentError, Option } from '@commander-js/extra-typings';
import { type KtxCliCommandContext, resolveCommandProjectDir } from '../cli-program.js';
import type { KtxSqlArgs } from '../sql.js';
import { profileMark } from '../startup-profile.js';
profileMark('module:commands/sql-commands');
const DEFAULT_MAX_ROWS = 1000;
const MAX_ROWS_CAP = 10_000;
function parseSqlMaxRowsOption(value: string): number {
const parsed = Number(value);
if (!Number.isInteger(parsed) || parsed < 1 || parsed > MAX_ROWS_CAP) {
throw new InvalidArgumentError(`must be an integer between 1 and ${MAX_ROWS_CAP}`);
}
return parsed;
}
async function runSqlArgs(context: KtxCliCommandContext, args: KtxSqlArgs): Promise<void> {
const runner = context.deps.sql ?? (await import('../sql.js')).runKtxSql;
context.setExitCode(await runner(args, context.io));
}
export function registerSqlCommands(program: Command, context: KtxCliCommandContext): void {
program
.command('sql')
.description('Execute parser-validated read-only SQL against a configured connection')
.argument('<sql...>', 'SQL query to execute')
.requiredOption('-c, --connection <id>', 'ktx connection id')
.option('--max-rows <n>', 'Maximum rows to return', parseSqlMaxRowsOption, DEFAULT_MAX_ROWS)
.addOption(
new Option('--output <mode>', 'Output mode: pretty (default), plain (TSV), or json').choices([
'pretty',
'plain',
'json',
]),
)
.option('--json', 'Shortcut for --output=json (overrides --output)', false)
.action(
async (
sqlParts: string[],
options: {
connection: string;
maxRows: number;
output?: 'pretty' | 'plain' | 'json';
json?: boolean;
},
command,
) => {
await runSqlArgs(context, {
command: 'execute',
projectDir: resolveCommandProjectDir(command),
connectionId: options.connection,
sql: sqlParts.join(' '),
maxRows: options.maxRows,
output: options.output,
json: options.json === true,
cliVersion: context.packageInfo.version,
});
},
);
}