diff --git a/.coderabbit.yaml b/.coderabbit.yaml index 238a282..3653755 100644 --- a/.coderabbit.yaml +++ b/.coderabbit.yaml @@ -2,12 +2,7 @@ language: "en-US" tone_instructions: | - You are a Senior Software Engineer and Security Specialist. - When reviewing, focus on: - 1. **Security**: Ensure no secrets are leaked and input validation is robust (especially for URLs and ports). - 2. **Performance**: Look for inefficient async operations (e.g., Promise.all vs. Promise.allSettled). - 3. **TUI/UX**: Since this is a CLI tool, ensure the interactive flows are intuitive and handle edge cases like cancellations. - 4. **Test Coverage**: Ensure all new logic is covered by vitest unit tests. + Senior Engineer & Security Specialist. Review for: 1. Security (no leaks, URL/port validation). 2. Performance (prefer Promise.allSettled). 3. TUI/UX (intuitive CLI flows, handle cancellations). 4. Tests (full vitest coverage for new logic). reviews: profile: "assertive" diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 9b3f2b7..2ce5871 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -23,7 +23,7 @@ jobs: - name: Checkout code uses: actions/checkout@v4 with: - token: ${{ secrets.GH_PAT }} + fetch-depth: 0 - name: Setup Node.js uses: actions/setup-node@v4 diff --git a/package-lock.json b/package-lock.json index 197ec0e..6406afb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,7 +19,6 @@ "dockerode": "^4.0.0", "ink": "^4.4.1", "nodemailer": "^8.0.7", - "ora": "^8.0.1", "react": "^18.2.0" }, "bin": { @@ -1764,18 +1763,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/cli-spinners": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", - "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", - "license": "MIT", - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/cli-table3": { "version": "0.6.5", "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.5.tgz", @@ -2660,18 +2647,6 @@ "node": "6.* || 8.* || >= 10.*" } }, - "node_modules/get-east-asian-width": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.6.0.tgz", - "integrity": "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/get-func-name": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", @@ -2996,18 +2971,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-interactive": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", - "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/is-lower-case": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-lower-case/-/is-lower-case-2.0.2.tgz", @@ -3036,18 +2999,6 @@ "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", "license": "MIT" }, - "node_modules/is-unicode-supported": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", - "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/is-upper-case": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-upper-case/-/is-upper-case-2.0.2.tgz", @@ -3235,34 +3186,6 @@ "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", "license": "MIT" }, - "node_modules/log-symbols": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-6.0.0.tgz", - "integrity": "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==", - "license": "MIT", - "dependencies": { - "chalk": "^5.3.0", - "is-unicode-supported": "^1.3.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/log-symbols/node_modules/is-unicode-supported": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", - "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/long": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", @@ -3598,110 +3521,6 @@ "url": "https://github.com/sponsors/panva" } }, - "node_modules/ora": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/ora/-/ora-8.2.0.tgz", - "integrity": "sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==", - "license": "MIT", - "dependencies": { - "chalk": "^5.3.0", - "cli-cursor": "^5.0.0", - "cli-spinners": "^2.9.2", - "is-interactive": "^2.0.0", - "is-unicode-supported": "^2.0.0", - "log-symbols": "^6.0.0", - "stdin-discarder": "^0.2.2", - "string-width": "^7.2.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ora/node_modules/cli-cursor": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", - "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", - "license": "MIT", - "dependencies": { - "restore-cursor": "^5.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ora/node_modules/emoji-regex": { - "version": "10.6.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", - "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", - "license": "MIT" - }, - "node_modules/ora/node_modules/onetime": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", - "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", - "license": "MIT", - "dependencies": { - "mimic-function": "^5.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ora/node_modules/restore-cursor": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", - "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", - "license": "MIT", - "dependencies": { - "onetime": "^7.0.0", - "signal-exit": "^4.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ora/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/ora/node_modules/string-width": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", - "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^10.3.0", - "get-east-asian-width": "^1.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/p-limit": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-5.0.0.tgz", @@ -4401,18 +4220,6 @@ "dev": true, "license": "MIT" }, - "node_modules/stdin-discarder": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz", - "integrity": "sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/stream-buffers": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/stream-buffers/-/stream-buffers-3.0.3.tgz", diff --git a/package.json b/package.json index 2dea4b5..0fc40be 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,6 @@ "dockerode": "^4.0.0", "ink": "^4.4.1", "nodemailer": "^8.0.7", - "ora": "^8.0.1", "react": "^18.2.0" }, "devDependencies": { diff --git a/src/__tests__/health.test.ts b/src/__tests__/health.test.ts index 0c524bd..78ae0f4 100644 --- a/src/__tests__/health.test.ts +++ b/src/__tests__/health.test.ts @@ -9,6 +9,13 @@ vi.mock('../utils/logger', () => ({ }, })); +vi.mock('../ui/spinner', () => ({ + createSpinner: vi.fn(() => ({ + start: vi.fn().mockReturnThis(), + stop: vi.fn().mockReturnThis(), + })), +})); + describe('health command', () => { let program: Command; diff --git a/src/__tests__/logs.test.ts b/src/__tests__/logs.test.ts index 240cc6f..e17a044 100644 --- a/src/__tests__/logs.test.ts +++ b/src/__tests__/logs.test.ts @@ -9,6 +9,13 @@ vi.mock('../utils/logger', () => ({ }, })); +vi.mock('../ui/spinner', () => ({ + createSpinner: vi.fn(() => ({ + start: vi.fn().mockReturnThis(), + stop: vi.fn().mockReturnThis(), + })), +})); + describe('logs command', () => { let program: Command; diff --git a/src/__tests__/show.test.ts b/src/__tests__/show.test.ts index 7d5ec82..f84a55f 100644 --- a/src/__tests__/show.test.ts +++ b/src/__tests__/show.test.ts @@ -23,6 +23,10 @@ vi.mock('../ui/spinner', () => ({ createSpinner: vi.fn(() => ({ start: vi.fn().mockReturnThis(), stop: vi.fn().mockReturnThis(), + fail: vi.fn().mockReturnThis(), + warn: vi.fn().mockReturnThis(), + info: vi.fn().mockReturnThis(), + update: vi.fn().mockReturnThis(), })), })); vi.mock('../ui/table', () => ({ diff --git a/src/commands/health.ts b/src/commands/health.ts index 2a5f297..d662144 100644 --- a/src/commands/health.ts +++ b/src/commands/health.ts @@ -1,11 +1,22 @@ import { Command } from 'commander'; import { logger } from '../utils/logger'; +import { createSpinner } from '../ui/spinner'; export const registerHealthCommand = (program: Command) => { program .command('health ') .description('Show health status for pods or containers') - .action((target) => { - logger.info(`Showing health for ${target}...`); + .action(async (target) => { + const spinner = createSpinner(`Checking health for ${target}...`).start(); + try { + // TODO: Implement actual health check logic + spinner.stop(`Health check for ${target} complete`); + logger.info(`Showing health for ${target}...`); + } catch (error) { + const errorMessage = (error as Error).message; + spinner.fail(`Health check for ${target} failed: ${errorMessage}`); + logger.error(`Health check for ${target} failed: ${errorMessage}`, error); + throw error; + } }); }; diff --git a/src/commands/logs.ts b/src/commands/logs.ts index 172e585..1fbf3a6 100644 --- a/src/commands/logs.ts +++ b/src/commands/logs.ts @@ -1,11 +1,22 @@ import { Command } from 'commander'; import { logger } from '../utils/logger'; +import { createSpinner } from '../ui/spinner'; export const registerLogsCommand = (program: Command) => { program .command('logs ') .description('Show logs for a container or pod') - .action((name) => { - logger.info(`Showing logs for ${name}...`); + .action(async (name) => { + const spinner = createSpinner(`Fetching logs for ${name}...`).start(); + try { + // TODO: Implement actual log fetching logic + spinner.stop(`Logs for ${name} fetched`); + logger.info(`Showing logs for ${name}...`); + } catch (error) { + const errorMessage = (error as Error).message; + spinner.fail(`Failed to fetch logs for ${name}: ${errorMessage}`); + logger.error(`Failed to fetch logs for ${name}: ${errorMessage}`, error); + throw error; + } }); }; diff --git a/src/commands/root.ts b/src/commands/root.ts index b79afbe..21c9099 100644 --- a/src/commands/root.ts +++ b/src/commands/root.ts @@ -10,6 +10,7 @@ import { registerLogsCommand } from './logs'; import { registerConfigCommand } from './config'; import { logger } from '../utils/logger'; import { showWelcomeBanner } from '../ui/banner'; +import { createSpinner } from '../ui/spinner'; program .name('kdm') @@ -27,41 +28,48 @@ const run = async () => { if (!process.argv.slice(2).length) { showWelcomeBanner('1.1.0'); - const [dockerStatus, k8sStatus, minikubeStatus] = await Promise.all([ - checkDockerConnection(), - checkK8sConnection(), - checkMinikubeConnection() - ]); + const spinner = createSpinner('Checking connections...').start(); + try { + const [dockerStatus, k8sStatus, minikubeStatus] = await Promise.all([ + checkDockerConnection(), + checkK8sConnection(), + checkMinikubeConnection() + ]); + spinner.stop('Connection check complete'); + console.log(); - const badge = (text: string, color: 'green' | 'red' | 'yellow') => { - const styles = { - green: chalk.bgGreen.black.bold, - red: chalk.bgRed.white.bold, - yellow: chalk.bgYellow.black.bold, + const badge = (text: string, color: 'green' | 'red' | 'yellow') => { + const styles = { + green: chalk.bgGreen.black.bold, + red: chalk.bgRed.white.bold, + yellow: chalk.bgYellow.black.bold, + }; + return styles[color](` ${text} `); }; - return styles[color](` ${text} `); - }; - const dockerStr = dockerStatus.connected ? badge('CONNECTED', 'green') : badge('DISCONNECTED', 'red'); - const k8sStr = k8sStatus.connected ? badge('CONNECTED', 'green') : badge('DISCONNECTED', 'red'); - - let minikubeStr = badge('NOT INSTALLED', 'red'); - if (minikubeStatus.installed) { - minikubeStr = minikubeStatus.running ? badge('RUNNING', 'green') : badge('STOPPED', 'yellow'); - } + const dockerStr = dockerStatus.connected ? badge('CONNECTED', 'green') : badge('DISCONNECTED', 'red'); + const k8sStr = k8sStatus.connected ? badge('CONNECTED', 'green') : badge('DISCONNECTED', 'red'); + + let minikubeStr = badge('NOT INSTALLED', 'red'); + if (minikubeStatus.installed) { + minikubeStr = minikubeStatus.running ? badge('RUNNING', 'green') : badge('STOPPED', 'yellow'); + } - console.log(`${chalk.bold('Docker:')} ${dockerStr}`); - console.log(`${chalk.bold('Kubernetes:')} ${k8sStr}`); - console.log(`${chalk.bold('Minikube:')} ${minikubeStr}\n`); - - console.log(`${chalk.cyan('󰡨')} Running Containers: ${chalk.yellow.bold(dockerStatus.containerCount)}`); - console.log(`${chalk.blue('󱔎')} Running Pods: ${chalk.yellow.bold(k8sStatus.podCount)}`); - console.log(`${chalk.red('󰒑')} Unhealthy Services: ${chalk.yellow.bold('0')} (Mocked)\n`); - console.log(chalk.bold('Commands:\n')); - console.log(` kdm show runners\n kdm health all\n kdm watch\n kdm logs \n`); - - program.outputHelp(); - process.exit(0); + console.log(`${chalk.bold('Docker:')} ${dockerStr}`); + console.log(`${chalk.bold('Kubernetes:')} ${k8sStr}`); + console.log(`${chalk.bold('Minikube:')} ${minikubeStr}\n`); + + console.log(`${chalk.cyan('󰡨')} Running Containers: ${chalk.yellow.bold(dockerStatus.containerCount)}`); + console.log(`${chalk.blue('󱔎')} Running Pods: ${chalk.yellow.bold(k8sStatus.podCount)}`); + console.log(`${chalk.red('󰒑')} Unhealthy Services: ${chalk.yellow.bold('0')} (Mocked)\n`); + console.log(chalk.bold('Commands:\n')); + console.log(` kdm show runners\n kdm health all\n kdm watch\n kdm logs \n`); + } catch (error) { + spinner.fail(`Connection check failed: ${(error as Error).message}`); + } finally { + program.outputHelp(); + process.exit(0); + } } program.parse(process.argv); diff --git a/src/commands/show.ts b/src/commands/show.ts index 8cf997e..c5ad322 100644 --- a/src/commands/show.ts +++ b/src/commands/show.ts @@ -30,7 +30,7 @@ export const showContainers = async () => { const spinner = createSpinner('Fetching Docker containers...').start(); try { const containers = await getRunningContainers(); - spinner.stop(); + spinner.stop('Docker containers fetched successfully'); if (containers.length === 0) { logger.warn('No running Docker containers found.'); @@ -48,7 +48,7 @@ export const showContainers = async () => { ]), }); } catch (error) { - spinner.stop(); + spinner.fail('Failed to fetch Docker containers'); // Error is already logged by getRunningContainers } }; @@ -57,7 +57,7 @@ export const showPods = async () => { const spinner = createSpinner('Fetching Kubernetes pods...').start(); try { const pods = await getRunningPods(); - spinner.stop(); + spinner.stop('Kubernetes pods fetched successfully'); if (pods.length === 0) { logger.warn('No running Kubernetes pods found.'); @@ -75,7 +75,7 @@ export const showPods = async () => { ]), }); } catch (error) { - spinner.stop(); + spinner.fail('Failed to fetch Kubernetes pods'); // Error is already logged by getRunningPods } }; @@ -88,7 +88,12 @@ export const showRunners = async () => { getRunningPods() ]); - spinner.stop(); + const anyFailed = containerRes.status === 'rejected' || podRes.status === 'rejected'; + if (anyFailed) { + spinner.warn('Some runners could not be fetched'); + } else { + spinner.stop('Runners fetched successfully'); + } const containers = containerRes.status === 'fulfilled' ? containerRes.value : []; const pods = podRes.status === 'fulfilled' ? podRes.value : []; @@ -128,29 +133,32 @@ export const showRunners = async () => { const showMinikube = async () => { const spinner = createSpinner('Fetching Minikube status...').start(); - const conn = await checkMinikubeConnection(); - if (!conn.installed) { - spinner.stop(); - logger.warn('Minikube is not installed on this system.'); - return; - } - - const statusList = await getMinikubeStatus(); - spinner.stop(); + try { + const conn = await checkMinikubeConnection(); + if (!conn.installed) { + spinner.fail('Minikube is not installed on this system'); + return; + } + + const statusList = await getMinikubeStatus(); + spinner.stop('Minikube status fetched successfully'); - if (statusList.length === 0) { - logger.warn('No Minikube profiles found or status is unknown.'); - return; - } + if (statusList.length === 0) { + logger.warn('No Minikube profiles found or status is unknown.'); + return; + } - renderTable({ - head: ['NAME', 'HOST', 'KUBELET', 'APISERVER', 'MESSAGE'], - rows: statusList.map((s) => [ - s.Name || '-', - s.Host === 'Running' ? chalk.green(s.Host) : (s.Host === 'Stopped' ? chalk.red(s.Host) : chalk.yellow(s.Host || '-')), - s.Kubelet === 'Running' ? chalk.green(s.Kubelet) : chalk.yellow(s.Kubelet || '-'), - s.APIServer === 'Running' ? chalk.green(s.APIServer) : chalk.yellow(s.APIServer || '-'), - s.Message || '-', - ]), - }); + renderTable({ + head: ['NAME', 'HOST', 'KUBELET', 'APISERVER', 'MESSAGE'], + rows: statusList.map((s) => [ + s.Name || '-', + s.Host === 'Running' ? chalk.green(s.Host) : (s.Host === 'Stopped' ? chalk.red(s.Host) : chalk.yellow(s.Host || '-')), + s.Kubelet === 'Running' ? chalk.green(s.Kubelet) : chalk.yellow(s.Kubelet || '-'), + s.APIServer === 'Running' ? chalk.green(s.APIServer) : chalk.yellow(s.APIServer || '-'), + s.Message || '-', + ]), + }); + } catch (error) { + spinner.fail(`Failed to fetch Minikube status: ${(error as Error).message}`); + } }; diff --git a/src/ui/spinner.ts b/src/ui/spinner.ts index 6e4d39d..6b3e0c2 100644 --- a/src/ui/spinner.ts +++ b/src/ui/spinner.ts @@ -1,9 +1,9 @@ -import ora from 'ora'; +import { Spinner } from '@vr_patel/tui'; export const createSpinner = (text: string) => { - return ora({ + const spinner = new Spinner({ text, - color: 'cyan', - spinner: 'dots', + style: 'dots', }); + return spinner; }; diff --git a/src/utils/logger.ts b/src/utils/logger.ts index 348b123..73f74c1 100644 --- a/src/utils/logger.ts +++ b/src/utils/logger.ts @@ -4,7 +4,12 @@ export const logger = { info: (message: string) => console.log(chalk.blue(message)), success: (message: string) => console.log(chalk.green(`✔ ${message}`)), warn: (message: string) => console.log(chalk.yellow(`⚠ ${message}`)), - error: (message: string) => console.error(chalk.red(`✖ ${message}`)), + error: (message: string, error?: unknown) => { + console.error(chalk.red(`✖ ${message}`)); + if (error) { + console.error(error); + } + }, dim: (message: string) => console.log(chalk.dim(message)), newline: () => console.log(), };