UNPKG

@vigilbase/cli

Version:

Vigilbase CLI — security scanners, agent tools, and Vigilbase One from the terminal.

171 lines (170 loc) • 7.2 kB
/** * The interactive terminal UI of the one command and `submit`: a step * timeline on stderr with inputs for the email and the code, an account * picker, Yes/No questions, spinners and a live collection progress bar. * * It is used only on an interactive terminal (`terminalFeatures`: stdin and * stderr are TTYs, TERM is not dumb, CI is not set, no `--json`); everywhere * else the commands keep their plain lines and prompts. What it shows follows * the same rules as the plain output: the masked address from vigilbase.com, * never the address, the code, the token or the session code once entered; * account names only through `displayText`; counts, never values, from the * snapshot. * * Every label is built from the session's symbols (`ellipsis`, `join`), so an * ASCII-only console (the Linux console, legacy Windows consoles) gets no * character outside ASCII. */ import { type Writer } from '../../lib/format.js'; import { Tui, type SignalSource, type TtyInput, type TtyOutput } from '../../lib/tui.js'; import type { CollectProgress } from './collector.js'; import type { EmailVerificationView } from './email-verification.js'; /** Sign-in attempts offered after the first one fails. */ export declare const MAX_SIGN_IN_RETRIES = 2; export type CollectionPlan = { zones: number; plannedReads: number; maxRequests: number; }; export type CollectedSummary = { zones: number; total: number; ok: number; notCollectable: number; denied: number; /** Other HTTP errors and reads that could not be collected. */ failed: number; path: string; bytes: number; /** `2 member emails`, `3 DNS names`, … (counts only). */ contents: string[]; inspect: string; }; export type UploadSummary = { replayed: boolean; passed?: number; warnings?: number; failed?: number; notCollected: number; resultsUrl?: string; opening: boolean; requester: string; }; export type CheckupTuiOptions = { mode: 'text' | 'json'; stdin: TtyInput; stderr: TtyOutput; /** stdout; when it is not a terminal, the result lines are written there too (plain). */ stdout?: { isTTY?: boolean; }; env: NodeJS.ProcessEnv; /** The command's writer, for those plain result lines. */ writer: Writer; platform?: NodeJS.Platform; signals?: SignalSource; exit?: (code: number) => void; frameMs?: number; now?: () => number; }; export declare class CheckupTui { readonly tui: Tui; private readonly writer; private readonly now; private readonly echoResults; private constructor(); /** The terminal UI, or null for the plain output. */ static open(options: CheckupTuiOptions): CheckupTui | null; /** What Ctrl-C prints at this stage. */ set cancelMessage(message: string); /** * A writer for the lines the commands print outside the steps below * (warnings, notes): `Warning: …` becomes `▲`, anything else a `│` line, * so nothing is written through the live frame. */ get lines(): Writer; intro(): void; /** `…`, or `...` on an ASCII-only console: for masked IDs and cut text built outside this class. */ get ellipsis(): string; /** `a · b · c`, or `a - b - c` on an ASCII-only console. */ join(parts: readonly string[]): string; /** `label…` (`label...` in ASCII): an activity in progress, without its ellipsis. */ private ongoing; /** `◒ label…` while `work` runs; `label` has no ellipsis. */ busy<T>(label: string, work: () => Promise<T>): Promise<T>; /** `◒ label…` until stopped; `label` (also in `update`) has no ellipsis. */ spinner(label: string): { update(label: string): void; stop(): void; }; /** `◇ title detail` */ step(title: string, detail?: string): void; warning(text: string): void; /** A Yes/No question; the answer stays on screen (`◇ question › Yes`). Ctrl-D answers no. */ confirm(question: string, options: { initial: boolean; detail?: string; }): Promise<boolean>; /** A question a flag answered: `◇ question › Yes (--yes)`. */ answeredByFlag(question: string, flag: string): void; /** * The verification's inputs: the address is shown while it is typed and * sent, then replaced by the masked address vigilbase.com returns; the code * is shown while it is typed, then masked (●●●●●●). A notice (a refused * address, a wrong code) appears inside the next input, which is drawn in * the same place. */ emailView(): EmailVerificationView; private retryClock; /** * cf's interactive sign-in with the terminal handed over (cf prints and * reads directly); after a failed or cancelled sign-in, "Try signing in * again?" up to MAX_SIGN_IN_RETRIES times. `check` is whoami afterwards. */ signIn<T>(run: () => Promise<number>, check: () => Promise<T>): Promise<T>; signedIn(summary: string): void; /** The account picker; `label` is already safe (`displayText`). Null on Ctrl-D. */ chooseAccount(accounts: ReadonlyArray<{ id: string; label: string; detail: string; }>): Promise<string | null>; /** `◇ Account Acme Corp a1b2…8f90`, the last line before the collection. */ account(label: string, detail: string): void; /** * `◒ Collecting ████░░ zones 1/2 · 40/97 reads · ~20 s left`, redrawn on * every progress event. The estimate is the larger of what the request * budget allows for the reads left and the pace so far. */ collecting(plan: CollectionPlan): { update(progress: CollectProgress): void; stop(): void; }; /** `◇ 84 reads · 72 ok · 10 not collectable · 2 refused`, the saved file and what it contains. */ collected(summary: CollectedSummary): void; /** `◆ Upload 13 KB to vigilbase.com? › Yes / No`; Enter on the initial state means No. */ confirmUpload(input: { bytes: number; where: string; requester: string; flag: string | null; }): Promise<boolean>; /** The upload and the scoring, which happen in one request. */ uploading<T>(bytes: number, work: () => Promise<T>): Promise<T>; /** * `└ ✓ Done. 10 passed · 2 failed`, the results link (underlined, an OSC 8 * link where supported) and the email note. `plainLines` are the plain * result lines, also written to stdout when stdout is not a terminal. */ uploaded(summary: UploadSummary, plainLines: readonly string[]): void; /** Dim lines under the end of the timeline (the logout hint), then the command on a line of its own. */ footnote(text: string, command?: string): void; /** Before an upload error is shown: the `submit` command for the saved snapshot, on a line of its own. */ uploadLater(command: string): void; /** * `■ message` with the hint under it, for an error that ends the command; * `runCli` then does not print it again. After Ctrl-C nothing more is shown. */ fail(error: unknown): void; close(): void; }