import Table from 'cli-table3'; import chalk from 'chalk'; import * as readline from 'readline'; import { IndemnClient } from '../../sdk/client.js'; export function formatTable(headers: string[], rows: string[][]): string { const table = new Table({ head: headers.map((h) => chalk.cyan(h)), style: { head: [], border: [] }, }); for (const row of rows) { table.push(row); } return table.toString(); } export function printJson(data: unknown): void { console.log(JSON.stringify(data, null, 2)); } export function printError(error: unknown): void { const message = error instanceof Error ? error.message : String(error); console.error(chalk.red(`Error: ${message}`)); } export function printSuccess(message: string): void { console.log(chalk.green(message)); } /** * Render the active-org banner: [Voice Demos · 69a40cd9…6807f · dev] * * Printed to stderr so it never pollutes stdout pipes (JSON output stays clean * without --json suppression). Rendered red for prod to reinforce blast-radius * awareness. Silent if not logged in or if the caller opts out. * * Self-heals when org_name is missing from config (e.g., after upgrading from * a pre-1.4 install) by fetching /organization/:id once and caching. */ export async function printBanner(): Promise { if (process.env.INDEMN_NO_BANNER === '1') return; const config = IndemnClient.loadConfig(); if (!config || !config.api_key || !config.org_id) return; let orgName = config.org_name; if (!orgName) { // Lazy one-time fetch so post-upgrade installs don't render "(unknown)" forever. try { const client = new IndemnClient(); const orgRes = await fetch(`${client.getHosts().copilot}/organization/${config.org_id}`, { headers: { 'Authorization': `Bearer ${client.getApiKey()}`, 'Content-Type': 'application/json', }, }); if (orgRes.ok) { const data = await orgRes.json() as { organization?: { name?: string } }; orgName = data.organization?.name; if (orgName) IndemnClient.saveConfig({ org_name: orgName }); } } catch { // Silent — banner degrades to ID-only display } } const shortId = config.org_id.length > 12 ? `${config.org_id.slice(0, 8)}…${config.org_id.slice(-4)}` : config.org_id; const label = orgName || '(unknown org)'; const text = `[${label} · ${shortId} · ${config.environment}]`; const colored = config.environment === 'production' ? chalk.red.bold(text) : chalk.yellow.dim(text); process.stderr.write(colored + '\n'); } export async function confirm(message: string): Promise { const rl = readline.createInterface({ input: process.stdin, output: process.stdout, }); return new Promise((resolve) => { rl.question(`${message} (y/N) `, (answer) => { rl.close(); resolve(answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes'); }); }); }