UNPKG

@capgo/cli

Version:
147 lines (146 loc) 7.39 kB
import type { AscCredentials, AscEventLine, AscLogLine } from './protocol'; import type { CodesignRunner } from '../macos-signing'; /** * Where a resolved helper binary came from. `package` is the signed npm bundle — * the ONLY source we spawn-time signature-verify. `override`/`cache`/`local` are * dev/CI paths (unsigned or ad-hoc) that skip verification. */ export type AscHelperSource = 'override' | 'package' | 'cache' | 'local'; export interface ResolvedAscHelper { /** Inner executable to spawn. */ binary: string; /** Where it came from (drives whether we verify the signature at spawn). */ source: AscHelperSource; /** * The enclosing `.app` bundle path — present only for the signed `package` * source, where it is the target of the designated-requirement check. */ bundlePath?: string; } /** Thrown when the helper is requested on a non-macOS host. */ export declare class NotMacOSError extends Error { constructor(); } export declare function isMacOS(): boolean; /** * Locate the precompiled Swift helper, in priority order: * 1. `CAPGO_ASC_KEY_HELPER_PATH` — explicit override (dev / CI / tests). Only * honored in dev builds (DCE'd from release bundles, like the keychain * helper's CAPGO_KEYCHAIN_HELPER_PATH); skips signature verification. * 2. The signed `CapgoAscKeyHelper.app` in the arch-matching * `@capgo/cli-helper-darwin-*` package — verified at spawn time. * 3. `~/.capgo/asc-key-helper/<binary>` — the legacy cached download location. * 4. A local `swift build` of the vendored package (dev, running from src). * Returns `null` (caller shows install / manual guidance) when none exists OR on * macOS < 14 — the packaged SwiftUI/WKWebView app requires macOS 14, so the * guided path must never be offered where it can't launch. * * Synchronous on purpose: it gates the guided path in several call sites that * cannot await. Signature verification of the package source is deferred to * {@link runAscKeyHelper} (just before spawn), which can await. */ export declare function resolveAscHelper(): ResolvedAscHelper | null; /** * Path-only resolver kept for the many synchronous gating call sites. Returns * the inner executable to spawn, or `null`. See {@link resolveAscHelper} for the * source metadata that drives spawn-time signature verification. */ export declare function resolveHelperBinary(): string | null; /** Why the guided helper can't be offered. See {@link probeGuidedHelper}. */ export type GuidedHelperUnusableReason = 'not-macos' /** Not a Mac — the helper is a macOS app (Linux/Windows). */ | 'unsupported-os' /** macOS < 14 — the SwiftUI/WKWebView app can't launch. */ | 'not-installed' /** No helper resolved (optional package absent, no cache/build). */ | 'untrusted'; /** Helper present but its Developer-ID signature/team didn't verify. */ export type GuidedHelperProbe = { usable: true; source: AscHelperSource; } | { usable: false; reason: GuidedHelperUnusableReason; detail?: string; }; export interface ProbeGuidedHelperOptions { /** * Inject a pre-resolved helper instead of calling {@link resolveAscHelper} * (tests). `null` models "nothing resolved". Omit in production. */ resolved?: ResolvedAscHelper | null; /** Forward an injected codesign runner to signature verification (tests). */ codesignRunner?: CodesignRunner; } /** * Decide — once, up front — whether guided ASC-key creation can ACTUALLY run on * this machine, so the onboarding flow never offers the guided path it would * then have to reject. Unlike the sync {@link resolveHelperBinary} existence * check, this also verifies the Developer-ID signature + Capgo team of a * packaged bundle, so a helper that is installed but wrongly signed / wrong-team / * tampered is reported `untrusted` (treated exactly like not-installed: the * guided option is withheld and the user goes straight to manual instructions, * the same as on Linux). * * Async because the signature check spawns `codesign`. Mirrors the spawn-time * verification in {@link runAscKeyHelper} (same bundle id, same verifier), so a * `usable` verdict here means the later spawn won't be refused for trust. */ export declare function probeGuidedHelper(options?: ProbeGuidedHelperOptions): Promise<GuidedHelperProbe>; /** * Dismiss the helper window. The outcome resolves as soon as the helper delivers * its terminal `result` line — BEFORE the process exits — so the helper can keep * its window open (showing a success screen) while the CLI advances. The caller * invokes `close()` once the flow has moved on (e.g. the key verified) to close * the window; until then it stays open. Safe to call more than once / after the * window already closed. */ export type CloseHelper = () => void; export interface AscHelperSuccess { ok: true; credentials: AscCredentials; runId: string; /** Number of stats events the helper emitted during the run. */ eventCount: number; /** Number of diagnostic `log` lines routed to the internal support log. */ logCount: number; /** Dismiss the still-open helper window. See {@link CloseHelper}. */ close: CloseHelper; } export interface AscHelperFailure { ok: false; errorCode: string; message: string; runId: string; /** Number of diagnostic `log` lines routed to the internal support log. */ logCount: number; /** Dismiss the helper window (no-op once it has exited). See {@link CloseHelper}. */ close: CloseHelper; } export type AscHelperOutcome = AscHelperSuccess | AscHelperFailure; export interface RunAscKeyHelperOptions { /** Pre-resolved helper binary path (tests inject a fake; prod auto-resolves). */ helperPathOverride?: string; /** API key for analytics attribution; falls back to the saved key. */ apikey?: string; /** Optional observer for every event line (UI progress / tests). */ onEvent?: (event: AscEventLine) => void; /** Optional observer for every diagnostic log line (UI / tests). */ onLog?: (line: AscLogLine) => void; /** Forward events to PostHog via trackEvent. Defaults to true. */ forwardToAnalytics?: boolean; /** * Append diagnostic `log` lines (and a per-run summary) to the CLI's internal * support log via {@link appendInternalLog}. Defaults to true. The append is * best-effort and no-ops when no internal log has been started for this run. */ forwardToInternalLog?: boolean; /** * Abort signal. When it fires — e.g. the onboarding TUI unmounts because the * user quit — the helper child is terminated (SIGTERM, then SIGKILL) so its * stdio pipes stop keeping the CLI process alive. Without this the CLI hangs * after exit while the helper window is still open. */ signal?: AbortSignal; } /** * Launch the precompiled helper, stream its NDJSON stats protocol, forward each * `event` line to PostHog, and resolve with the credentials from the terminal * `result` line. The private key is returned to the caller but NEVER forwarded * to analytics. Never rejects on a helper-side failure — returns a structured * failure outcome instead (it throws only for {@link NotMacOSError}). */ export declare function runAscKeyHelper(options?: RunAscKeyHelperOptions): Promise<AscHelperOutcome>;