/** * The credential-demand seam for credential-free `alchemy dev`. * * A dev run whose plan is entirely local must never touch (or prompt for) * cloud credentials. When the plan DOES need the cloud — a resource opted * out of local emulation via `Alchemy.remote()`, a local Worker binding * that proxies to a remote resource (`dev: { remote: true }`), or the * deletion of a row that was last reconciled live — credentials are * demanded exactly once, up front, in the exec process (which owns the * tty), BEFORE apply begins: * * - interactive: the provider's existing `configure` flow runs, prefixed * with a human-readable reason naming the demanding resources * (threaded via {@link ConfigureContext}'s `reason`). * - non-interactive: fail with the typed {@link CredentialsRequired} * error naming the demanding resources and pointing at `alchemy login`. * * The RPC sidecar never prompts — its stdio is piped and it only ever * reads credentials persisted by this seam (or a prior `alchemy login`). * * Non-dev runs (`alchemy deploy` / `destroy`) never enter this seam: * state-store init and live providers drive the pre-existing lazy * credential-resolution flow unchanged. */ import * as Config from "effect/Config"; import * as Effect from "effect/Effect"; import type { Plan } from "../Plan.ts"; /** Why a plan row demands live (cloud) credentials during a dev run. */ export type CredentialDemandReason = /** The resource's resolved provider mode is `"live"` (`Alchemy.remote()`). */ "remote" /** Binding data carries a truthy `devRemote` entry — the local runtime proxies this binding to the real cloud. */ | "remote-binding" /** The plan deletes a row stamped `providerMode: "live"` — the live provider must run to delete it. */ | "live-delete"; export interface DemandingResource { /** FQN of the demanding resource within the stack. */ readonly fqn: string; readonly reason: CredentialDemandReason; } /** * All the resources of one cloud provider that demand live credentials. * `provider` is the auth-provider name, derived from the resource Type's * leading namespace segment (`"AWS.S3.Bucket"` → `"AWS"`, matching the * name the cloud's auth provider registers under). */ export interface CredentialDemand { readonly provider: string; readonly resources: readonly DemandingResource[]; } declare const CredentialsRequired_base: new = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & { readonly _tag: "CredentialsRequired"; } & Readonly; /** * A dev-mode plan needs cloud credentials, none are configured for the * active profile, and the process is non-interactive so the configure flow * cannot run. The message names the demanding resources and the fix. */ export declare class CredentialsRequired extends CredentialsRequired_base<{ message: string; /** Auth-provider name whose credentials are missing (e.g. `"AWS"`). */ provider: string; /** FQNs of the resources demanding the credentials. */ resources: string[]; /** Short summary of why the credentials are needed. */ reason: string; }> { } /** * Build the typed {@link CredentialsRequired} failure for a demand. * Exported so tests can pin the message format. */ export declare const credentialsRequired: (demand: CredentialDemand, profileName: string) => CredentialsRequired; /** * Scan a plan for rows that need live (cloud) credentials during a dev * run, grouped by cloud provider: * * 1. resources whose resolved provider mode is `"live"` (`Alchemy.remote()`) * 2. resources whose binding data carries a truthy `devRemote` entry * (the local runtime proxies that binding to the real cloud) * 3. planned deletions of rows stamped `providerMode: "live"` * * Pure and side-effect-free — callers gate on the run being a dev run * (in a live run every dual-provider row resolves `"live"` and the * pre-existing lazy credential flow applies instead). * * Mode-agnostic rows (`mode === undefined`, single-implementation * providers that run live even in dev) are deliberately NOT collected: * they resolve credentials lazily exactly as they do today. */ export declare const collectCredentialDemands: (plan: Plan) => CredentialDemand[]; export interface DemandCredentialsOptions { /** * Overrides {@link isNonInteractive} — a test seam. When `true`, a * missing profile fails with {@link CredentialsRequired} instead of * driving the interactive configure flow. */ readonly nonInteractive?: boolean; } /** * Ensure credentials exist for every demand, prompting at most once per * provider and only when nothing is configured yet: * * - already configured for the active profile → no-op (never re-prompts) * - missing + interactive → the provider's `configure` flow runs (via * `AlchemyProfile.loadOrConfigure`), prefixed with a reason naming the * demanding resources * - missing + non-interactive (and not CI) → typed * {@link CredentialsRequired} failure * - CI → `loadOrConfigure` picks the provider's non-interactive default * (env-var credentials), matching every other CI path * * Demands whose cloud has no registered auth provider are skipped (bare * engine runs with test providers demand nothing). All context is * resolved optionally, so the effect is safe to run in any environment. */ export declare const demandCredentials: (demands: readonly CredentialDemand[], options?: DemandCredentialsOptions | undefined) => Effect.Effect; /** * The one-call seam wired into the dev path: scan the plan for live * demand and, if any, run {@link demandCredentials} BEFORE apply begins. */ export declare const demandPlanCredentials: (plan: Plan, options?: DemandCredentialsOptions) => Effect.Effect; export {}; //# sourceMappingURL=Demand.d.ts.map