@vigilbase/cli
Version:
Vigilbase CLI — security scanners, agent tools, and Vigilbase One from the terminal.
80 lines (79 loc) • 2.85 kB
JavaScript
import { sanitizeTerminal } from './format.js';
const SPINNER = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
const BAR_WIDTH = 24;
function renderBar(ratio) {
const clamped = Math.max(0, Math.min(1, ratio));
const filled = Math.round(clamped * BAR_WIDTH);
const empty = BAR_WIDTH - filled;
return `${'█'.repeat(filled)}${'░'.repeat(empty)}`;
}
/**
* Live indeterminate progress on stderr while a scan/request is in flight.
* Disabled for --json, non-TTY, CI, and NO_COLOR-style quiet environments.
*/
export function startProgress(label, options) {
label = sanitizeTerminal(label).replace(/[\n\t]/g, ' ');
const stream = options.stderr ?? process.stderr;
if (!options.isEnabled) {
return { stop: () => undefined };
}
const expectedMs = options.expectedMs ?? 8_000;
const started = Date.now();
let frame = 0;
let closed = false;
const draw = () => {
if (closed)
return;
const elapsedMs = Date.now() - started;
const elapsedSec = (elapsedMs / 1000).toFixed(1);
// Ease toward ~90% at expectedMs, then creep slowly so we never hit 100% early.
const ratio = 1 - Math.exp(-elapsedMs / expectedMs);
const soft = Math.min(0.92, ratio);
const spin = SPINNER[frame % SPINNER.length];
frame += 1;
const line = `\r${spin} ${label} ${renderBar(soft)} ${elapsedSec}s`;
stream.write(line);
};
draw();
const timer = setInterval(draw, 80);
timer.unref?.();
return {
stop: (finalMessage) => {
if (closed)
return;
closed = true;
clearInterval(timer);
const elapsedSec = ((Date.now() - started) / 1000).toFixed(1);
const clear = `\r${' '.repeat(Math.max(60, label.length + BAR_WIDTH + 20))}\r`;
stream.write(clear);
if (finalMessage) {
stream.write(`${sanitizeTerminal(finalMessage).replace(/[\n\t]/g, ' ')} (${elapsedSec}s)\n`);
}
},
};
}
export function shouldShowProgress(mode, env = process.env, stderr = process.stderr) {
if (mode === 'json')
return false;
if (env.CI === '1' || env.CI === 'true')
return false;
if (env.VIGILBASE_CLI_NO_PROGRESS === '1')
return false;
return Boolean(stderr.isTTY);
}
/** Tiny helper so call sites stay readable. */
export async function withProgress(label, mode, work, writer) {
const progress = startProgress(label, {
isEnabled: shouldShowProgress(mode),
stderr: writer ? undefined : process.stderr,
});
try {
const result = await work();
progress.stop(`✓ ${label}`);
return result;
}
catch (error) {
progress.stop(`✗ ${label}`);
throw error;
}
}