UNPKG

@capgo/cli

Version:
161 lines (160 loc) 8.66 kB
import type { CiSecretSetupAdvice, CiSecretTarget } from '../ci-secrets.js'; import type { IosEffectDeps } from '../ios/flow.js'; import type { TailEffectDeps } from '../tail/flow.js'; import type { OnboardingStep } from '../types.js'; import type { Platform } from './contract.js'; /** * The iOS driver-held transient state — the exact `IosEffectDeps['carried']` * shape, plus the MCP-only `parkedImportStep` (S12): the interactive import * sub-flow prompt the driver parked between tool calls — the headless mirror * of the TUI's React `step` state for the EPHEMERAL import prompts, which * resume routing can never reproduce (see engine.ts iosParkedStep). NON-SECRET * (a step name). Wiped with the session; a restart self-heals via a fresh * import-scanning that re-derives the inventory and re-renders the picker. */ export type IosCarried = NonNullable<IosEffectDeps['carried']> & { parkedImportStep?: OnboardingStep; /** * The chosen identity's Apple cert resource id (import-checking-apple-cert's * transient — typed on IosStepCtx, not on the engine's carried shape). The * registry really holds it after the transient merge; typing it here lets * the driver DROP it when the user re-picks a different identity (the TUI * clears its appleCertId mirror the same way). NON-SECRET (an Apple id). */ _appleCertIdForChosen?: string; }; /** * The tail driver-held transient state — the exact `TailEffectDeps['carried']` * shape, plus the NON-SECRET tail OUTCOME facts the outcome-aware terminal * summary harvests (engine.ts harvestTailOutcomes → tailCompleteResult): the * exact upload summary line (counts/labels only), the written workflow path * and the exported .env path. These three are non-secret BY CONSTRUCTION and * are the one carried subset allowed to surface verbatim in a tool result — * secret VALUES (savedCredentials / ciSecretEntries) must still never leave * this registry. */ export type TailCarried = NonNullable<TailEffectDeps['carried']> & { ciSecretUploadSummary?: string; workflowFilePath?: string; envExportPath?: string; }; /** * S9-S11: the MCP's parked interactive TAIL step + the NON-SECRET view context * it was rendered with (option inventories / labels — never credential values). * The TUI holds the current tail step in React state; the MCP mirrors it here so * (a) the strict tail gate validates an answer against the step that actually * asked, and (b) a re-render (corrective message, plain re-check) re-asks the * SAME parked question instead of drifting forward through the resume router — * which would collapse past consent gates like preview-workflow-file. A server * restart loses the park; resume routing then takes over (the frozen * tailResumeStep contract). EXPLICITLY NO SECRETS: ciSecretEntries (values) * stay in tailCarried; only derived key NAMES may surface in tool results. */ export interface TailParkedState { step: string; ciSecretTargets?: CiSecretTarget[]; ciSecretSetupAdvice?: CiSecretSetupAdvice[]; ciSecretRepoLabel?: string | null; ciSecretError?: string; availableScripts?: Record<string, string>; recommendedScript?: string | null; } export interface OnboardingSessionState { iosCarried: IosCarried; tailCarried: TailCarried; tailParked?: TailParkedState; /** * The platform this session is setting up, as CHOSEN by the user (platform * picker answer / single-platform auto-route / explicit `{ platform }`), NOT * inferred from on-disk progress. This is what lets a bare `next_step({})` * resume the right platform AND lets two concurrent server processes onboard * the same app for different platforms without reading each other's progress * files. Process-local: a restart loses it and the flow re-asks the picker. */ activePlatform?: Platform; /** * The platform whose continue-vs-restart resume prompt has already been * resolved THIS session — shown and answered, or skipped as unnecessary (no * resumable on-disk progress). While it differs from the committed platform, a * fresh platform entry first shows the resume prompt (mirroring the TUI's * `resume-prompt` fork) instead of silently resuming. Process-local: a restart * loses it and the prompt is re-offered. Reset by runStart so a fresh "start * onboarding" always re-asks. */ resumeResolvedFor?: Platform; } /** * Get the session state for `appId`, creating an empty one on demand. * The returned object is the current SNAPSHOT: merges replace the carried * objects rather than mutating them, so re-read after merging. */ export declare function getSession(appId: string): OnboardingSessionState; /** * Read the platform this session committed to (via setSessionPlatform), or * undefined when none has been chosen yet (fresh session, or after a restart). * Deliberately does NOT consult disk progress — the platform is a session * decision, not a property of what happens to be saved on disk. */ export declare function getSessionPlatform(appId: string): Platform | undefined; /** * Record (or, with `undefined`, clear) the platform this session is setting up. * Called when the user picks at the platform gate, when a single-platform * project auto-routes, or on any explicit `{ platform }`. Cleared by runStart so * a fresh "start onboarding" always re-asks instead of silently resuming. */ export declare function setSessionPlatform(appId: string, platform: Platform | undefined): void; /** * Read which platform's resume prompt has already been resolved this session * (shown+answered, or determined unnecessary), or undefined when none has. * Process-local; deliberately ignores disk — the resume decision is a session * decision, exactly like the platform itself. */ export declare function getResumeResolvedFor(appId: string): Platform | undefined; /** * Record (or, with `undefined`, clear) which platform's continue-vs-restart * resume prompt has been resolved this session. Set once the prompt is answered * or skipped as unnecessary; cleared by runStart so a fresh start re-asks. */ export declare function setResumeResolvedFor(appId: string, platform: Platform | undefined): void; /** * Drop the per-flow carried transients (iOS carried, tail carried, tail park) * for `appId` while PRESERVING the session-level decisions (activePlatform, * resumeResolvedFor). Used by the resume prompt's "restart" arm: the on-disk * progress is wiped, so the in-memory carried state from the old run must go * too — but the session stays committed to the same platform and must not * re-ask the resume prompt it just answered. Immutable: builds a NEW entry. */ export declare function clearSessionCarried(appId: string): void; /** * Merge `partial` into the iOS carried state for `appId` and return the new * merged carried object. `undefined` values leave the prior value intact. */ export declare function mergeIosCarried(appId: string, partial: Partial<IosCarried>): IosCarried; /** * Merge `partial` into the tail carried state for `appId` and return the new * merged carried object. `undefined` values leave the prior value intact. */ export declare function mergeTailCarried(appId: string, partial: Partial<TailCarried>): TailCarried; /** * Park the current interactive tail step (+ its non-secret view context) for * `appId`. REPLACES any prior park — each render re-parks the step it shows, * so the park always mirrors the question currently in front of the user. * Immutable: builds a NEW session entry; prior snapshots are untouched. */ export declare function setTailParked(appId: string, parked: TailParkedState): void; /** * Drop `keys` from the iOS carried state for `appId` and return the new * carried object. The complement of mergeIosCarried for one-shot consumable * fields (e.g. the verify-app `verifyAction` pick, which the driver MUST clear * after the resolver effect ran so a later re-entry runs the initial fetch — * merge semantics skip `undefined`, so a merge can never clear). Immutable: * builds a NEW carried object; prior snapshots are untouched. */ export declare function dropIosCarried(appId: string, keys: (keyof IosCarried)[]): IosCarried; /** * Drop the session entry for `appId`. Idempotent: safe on an absent appId and * safe to call twice. The next getSession() recreates a fresh empty session. */ export declare function clearSession(appId: string): void; /** Drop every session (test isolation only — production code clears per appId). */ export declare function clearAllSessions(): void;