/** * Init command — interactive onboarding wizard (T3a + T3b — Plan A). * * This module owns the interactive `scoutline init` flow: * - State detection (absent / corrupt / already-onboarded / fresh). * - Provider checklist (registry-derived; none pre-checked — equal weight). * - Per-provider ask-key-first → hidden input → inline single-attempt * validation through `DiagnosticsCapability.invoke({probe:true})` against * an EPHEMERAL resolved env (never persists, never mutates `process.env`). * - Honest broad classification of probe failures: key-problems * (`AuthError`/`ApiError`) reject + re-prompt; `NetworkError` offers * save-unverified. No false-precise "wrong/disabled/mismatch" subtypes * are inferred from message text — the taxonomy only distinguishes * `AuthError`/`ApiError`/`NetworkError`. * - Credit-cost disclosure before any paid-provider probe. * - Fallback preference question. * - Journaling disclosure confirm (history-journal merge T7, PRD * AC9 / ADR-0008): default enabled, writes top-level `"journal"`. * - Atomic config write (T1 `writeConfig` primitive) + redacted summary. * * T3b additions: * - **Re-config menu** when an already-valid config exists: edit a * Provider key, add a Provider, remove a Provider, change the * fallback preference, re-run the full wizard, or cancel. Editing * a Provider key resets `verification.status` to `unverified` * (Doctor re-promotes after a successful probe). * - **Corrupt-config repair**: tolerant `inspectConfig` distinguishes * `absent` / `valid` / `corrupt`. On `corrupt` the wizard offers to * back up the live file and rewrite a fresh config — `init` is the * recovery path, not a victim of corruption. * - **Formal non-TTY refuse**: without an interactive terminal the * wizard refuses before any prompt, prints env instructions and * the `init` hint, and exits. * - **Stale-env-after-import warning**: when the user imports a key * from env, the wizard notes that the env value will continue to * take precedence (env > file in the runtime precedence rule) — * the wizard cannot turn off the env value for the user. * - **`hintShown` reset on re-init**: a re-config or fresh write * resets the env-only hint marker so a user who later switches to * env-only usage sees the hint once again. * * Boundary rules: * - No Provider transport is constructed outside the per-provider probe; * the candidate credential lives only in the ephemeral env until the * final atomic write. * - All prompt IO flows through the {@link InitPrompts} seam; no direct * `process.stdin` / `process.stdout` reads in this module. Production * wires `@inquirer/prompts`; tests inject scripted doubles. * - Registration links render BOTH a terminal hyperlink AND the literal * URL text so captured / non-hyperlink output stays usable. * - No selection work (Plan B); the wizard writes at most one * `fallbackEnabled` flag and per-Provider records. */ import type { ConfigInspection, ScoutlineConfig, WriteConfigOptions } from "../lib/config-store.js"; import type { ProviderDescriptor, ProviderId } from "../providers/types.js"; /** Agent-registration home/config roots (injectable for hermetic tests). */ export interface AgentRegistrationRoots { home: string; configRoot: string; } /** * Help text for `scoutline init --help`. T3b completes the wizard's * lifecycle (re-config menu, corrupt-config repair, formal non-TTY * refuse), so the T3a PREVIEW caveat is dropped — the public claim of * a complete `init` is now accurate. */ export declare const INIT_HELP: string; /** * Per-provider static metadata for the prompt surface. The canonical * provider list / order is the registry (`BUILT_IN_PROVIDER_DESCRIPTORS`); * this map only carries prompt-side presentation fields (registration URL, * human-readable env-var label, credit-cost disclosure). Adding a Provider * to the registry without an entry here is a coding error caught at the * `providerMeta` lookup below. */ interface ProviderPromptMeta { readonly label: string; /** * Canonical env-var name. Optional: the keyless science suppliers * (arxiv, crossref, europepmc) have no credential model, so no env * var to advertise. Renderers must guard on absence * (`getDetectedEnvVar` / the non-TTY example line). */ readonly envVar?: string; readonly envAliases?: readonly string[]; /** * Registration page. Optional for the same reason — a keyless * supplier has no key page, and `renderRegistrationLine` must not * emit a broken hyperlink when the field is absent. */ readonly registrationUrl?: string; /** * True when the probe is billable (~1 credit). Z.AI and MiniMax probe * through free endpoints (tool discovery / raw quota probe); the other * four charge ~1 credit per probe. Surfaced before the user enters a * key so they can opt out before the charge occurs. Keyless science * suppliers probe keyless — always false for them. */ readonly probeCostsCredit: boolean; /** * Keyless-economics note rendered in the checklist description * column. Present for the five science suppliers (keyless by * default); absent for keyed-only providers. */ readonly keylessNote?: string; } export declare const PROVIDER_PROMPT_META: Record; /** * Multi-select prompt choice for the provider checklist. `value` is the * provider id; `description` carries env-detected and credit-cost hints * surfaced in the choice description column. */ export interface InitChoice { readonly value: T; readonly name: string; readonly description?: string; /** * Pre-checked state. The provider checklist NEVER pre-checks (equal * weight); this field exists for forward compatibility with T3b. */ readonly checked?: boolean; } /** * Injectable prompt IO seam. Production wires `@inquirer/prompts` via * {@link createInquirerPrompts}; tests inject scripted doubles that * never touch a real TTY. * * The shape mirrors the four prompt types the wizard uses: multi-select * checkbox, yes/no confirm, hidden password, and free-text input. */ export interface InitPrompts { /** * Multi-select. Returns the chosen values in selection order. Throws * on user cancel (Ctrl+C / EOF) — the caller catches and treats as * no-write. */ checkbox(message: string, choices: readonly InitChoice[]): Promise; /** * Single-select menu. Returns the chosen value. Throws on user * cancel. Used by the T3b re-config menu (edit/add/remove/fallback/ * rerun/cancel) and the corrupt-config-repair prompt. */ select(message: string, choices: readonly InitChoice[]): Promise; /** Yes/no. `defaultYes` carries the documented default ([Y/n] vs [y/N]). */ confirm(message: string, defaultYes: boolean): Promise; /** Hidden password input. Returns the trimmed value (possibly empty). */ password(message: string): Promise; /** Free-text input. Used for follow-up re-prompt loops. */ input(message: string): Promise; } /** * Injectable config-store seam. Production wires the real * {@link inspectConfig} / {@link writeConfig}; tests inject in-memory or * temp-dir-backed doubles so the wizard is hermetic — no real * `~/.scoutline/config.json` I/O during tests. */ export interface InitConfigStore { inspect(): Promise; write(config: ScoutlineConfig, options?: WriteConfigOptions): Promise; } /** * Wrap the real `inspectConfig` / `writeConfig` pair as an * {@link InitConfigStore}. The default options object is captured per-call * so the wizard can pass a temp `filePath` / atomic options through * unchanged in tests. */ export declare function createDefaultConfigStore(options?: WriteConfigOptions): InitConfigStore; /** * Injectable dependencies for the init wizard. Production supplies the * real defaults via the composition root in `src/index.ts`; tests pass * in-memory / temp-dir doubles for hermeticity. */ export interface InitDependencies { /** The provider registry — canonical checklist source/order. */ readonly descriptors: readonly ProviderDescriptor[]; /** Injectable prompt IO seam. */ readonly prompts: InitPrompts; /** Injectable config-store seam. */ readonly configStore: InitConfigStore; /** Injectable environment view (for env-key-import detection). */ readonly env: NodeJS.ProcessEnv; /** * Injectable clock, used for verification `checkedAt` timestamps. * Production wires `Date.now`; tests inject a fixed clock. */ readonly now: () => number; /** Whether stdin is an interactive TTY. Non-TTY gets a graceful guard. */ readonly stdinIsTTY: boolean; /** Progress / disclosure sink. */ readonly writeStderr: (value: string) => void; /** Final-summary sink (the only stdout write in the wizard). */ readonly writeStdout: (value: string) => void; /** * Agent-registration home/config roots (agent registration D4). * Production wires `os.homedir()` + `resolveConfigRoot()`; tests inject * temp roots so the agent step never probes the real HOME. */ readonly agentRegistrationRoots?: AgentRegistrationRoots; } /** * Run the interactive init wizard. Performs the T3b state dispatch: * - non-TTY → refuse before any prompt + exit 1. * - corrupt config → offer backup + rewrite (init is the recovery path). * - valid + already-onboarded → re-config menu. * - valid + empty / absent → fresh-onboarding flow. * * Returns the exit code: * - 0 on success, re-config applied, or already-onboarded + Cancel. * - 1 on user cancel, non-TTY refuse, write failure, or declined repair. */ export declare function runFreshOnboarding(deps: InitDependencies): Promise; /** * Build the production {@link InitPrompts} from `@inquirer/prompts`. The * adapter: * - Rewires the inquirer output stream to `process.stderr` so the * wizard keeps the codebase's data-only-stdout contract (the final * summary line is the only stdout write). * - Maps the four wizard prompt shapes onto the inquirer calls. * * Tests never call this — they inject a scripted {@link InitPrompts} * double instead, so test runs are fully hermetic and do not need a TTY. */ export declare function createInquirerPrompts(): InitPrompts; /** * Top-level init handler. Wired into the dispatch switch in `index.ts`. * * - `--help` / `-h` prints {@link INIT_HELP} to stdout and returns 0. * - Otherwise dispatches to {@link runFreshOnboarding}. * * The CLI arg list is the same shape every other handler receives; the * handler only inspects it for the help flag. */ export declare function handleInitWithHelp(args: readonly string[], deps: InitDependencies): Promise; /** * Backwards-compatible alias used by the dispatch switch. Tests that drive * the wizard directly pass an empty arg list. */ export declare function handleInit(deps: InitDependencies): Promise; export {}; //# sourceMappingURL=init.d.ts.map