/**
* 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