/** * The native AWS read transport (#1206) — the read half of what * `op/activities/aws-apply.ts` already does for writes. * * Every AWS observer shelled `aws …` once per entity, serially, and parsed * stderr to find out what went wrong (#1085). The applier does not: `awsApply` * speaks the CloudFormation Query protocol over `fetch`, honours an endpoint * override, and is injectable for tests. This module is that same transport * pointed at the two APIs the read path needs: * * - **CloudFormation Query** (form-encoded POST, XML back) — the stack reads * `describeResources` and the deep pass both start from. * - **Cloud Control** (AWS JSON 1.0, `X-Amz-Target: CloudApiService.*`) — the * property-level reads (#1015). * * Both take the endpoint the same way the applier does, so `chant emulator` / * behold `--local` keep working by construction rather than by each observer * remembering to inject `--endpoint-url`. * * ## Signing * * Requests are signed with SigV4 (#1686) when credentials resolve — see * `./sigv4.ts`, which holds the implementation so that cedar's AVP client can * adopt the same one rather than growing a second. Two cases stay unsigned, and * both are deliberate: * * - **No credentials.** The caller's existing no-credentials path is * untouched: the request still goes out carrying only the credential scope * of {@link regionScope}, which is what the emulator lanes have always * sent. * - **An endpoint override.** Floci does not verify signatures, and signing * against it would mean every local lane suddenly needs credentials to read * what it just deployed. `signEndpointOverride` opts back in for an * override that *is* real AWS — a VPC endpoint, a signing proxy. * * What counts as an override is one rule, {@link resolveEndpointOverride}: the * `endpoint` option, else the ambient `AWS_ENDPOINT_URL_` the AWS SDK * honours per service, else `AWS_ENDPOINT_URL` (#1694). Cedar's AVP client * (`lexicons/cedar/src/avp/client.ts`) applies the same rule; it restates it * rather than importing it, because cedar does not depend on this lexicon and * a lexicon build compiles against the published core, so a helper hoisted * into core would not be visible to either until the next core release. */ import { type AwsCredentialSource } from "./sigv4.js"; export type { AwsCredentials, AwsCredentialResolver, AwsCredentialSource } from "./sigv4.js"; /** Injectable HTTP, mirroring `AwsHttp` in the applier so tests avoid the network. */ export type AwsReadHttp = (url: string, init: { headers: Record; body: string; }, signal?: AbortSignal) => Promise<{ status: number; text: string; }>; /** A failed read, carrying enough to classify it without parsing prose. */ export declare class AwsReadError extends Error { readonly status: number; /** The API's own error code (`ValidationError`, `UnsupportedOperation`, …) when it sent one. */ readonly code?: string | undefined; constructor(message: string, status: number, /** The API's own error code (`ValidationError`, `UnsupportedOperation`, …) when it sent one. */ code?: string | undefined); } export interface AwsReadClientOptions { /** * Endpoint override (Floci `http://localhost:4566`). Omitted, the environment * answers through {@link resolveEndpointOverride}; when it names nothing * either, the target is the real AWS host. */ endpoint?: string; /** Region for the real-AWS host and the Query `Version` context. */ region?: string; http?: AwsReadHttp; signal?: AbortSignal; /** * What to sign with: literal credentials, or a resolver that decides. Omitted, * the environment answers; when it has nothing, the request goes out unsigned * exactly as it did before signing existed. */ credentials?: AwsCredentialSource; /** Environment the credential fallback reads. Defaults to `process.env`; injectable for tests. */ env?: Record; /** Sign even against an endpoint override — for an override that is real AWS. */ signEndpointOverride?: boolean; /** Signing clock. Injected by tests so a signature is reproducible. */ now?: Date; } /** The name of the service-specific endpoint variable the AWS SDK reads for `service`. */ export declare function serviceEndpointEnvVar(service: string): string; /** * The one rule for what an endpoint override is (#1694): the `endpoint` option * when given, else the service-specific `AWS_ENDPOINT_URL_`, else the * ambient `AWS_ENDPOINT_URL` — the same precedence the AWS SDK applies. The * result is the target the request goes to and, through {@link requestHeaders}, * the fact that decides whether it is signed. Returns `undefined` for real AWS. */ export declare function resolveEndpointOverride(service: string, endpoint: string | undefined, env?: Record): string | undefined; /** * `options` with its endpoint settled by {@link resolveEndpointOverride}, so * the URL builder and the signing decision read the same answer. */ export declare function withEndpointOverride(service: string, options: AwsReadClientOptions): AwsReadClientOptions; /** Service host for `service`, honouring an endpoint override. */ export declare function serviceUrl(service: string, endpoint?: string, region?: string): string; /** * The headers one request goes out with — signed when there is something to * sign with and the target is real AWS, scope-only otherwise. * * Signing needs a region even when the caller named none, because the scope * string has a slot for one; it borrows the same `us-east-1` default that * {@link serviceUrl} already used to build the host, so the signature agrees * with the endpoint it is sent to. * * Exported because this decision — sign, or carry the scope and no signature — * belongs to the lexicon's read transport rather than to any one API on it. * `agentcore/trace-fetch.ts` reads `bedrock-agentcore` through the same seam, * and a second copy of this would be a second place for the emulator carve-out * to drift. * * Callers pass options already settled by {@link withEndpointOverride}, so an * override the environment named is skipped exactly like one the option did. */ export declare function requestHeaders(service: string, url: string, body: string, base: Record, options: AwsReadClientOptions): Record; /** * The `text` pairs of one XML fragment, as a flat record. Repeated * tags keep the first occurrence, which is what a `` body wants — * nested lists inside a member are not modelled, because nothing the read path * needs from `DescribeStackResources` / `DescribeStacks` is nested that deep. */ export declare function xmlLeaves(fragment: string): Record; /** * Every `` under `listTag`, each flattened by * {@link xmlLeaves}. The Query protocol renders a list as repeated `` * elements, and `xmlField` in the applier deliberately does not handle them — * it was written for scalar status fields. */ export declare function xmlMembers(xml: string, listTag: string): Array>; /** ``, when the response carries one. */ export declare function xmlError(xml: string): { code?: string; message?: string; } | undefined; /** * One CloudFormation Query call. Returns the raw XML; throws * {@link AwsReadError} for a non-2xx or an `` body, so a caller * classifies a typed failure instead of matching on stderr. */ export declare function cfnQuery(action: string, params: Record, options?: AwsReadClientOptions): Promise; /** One stack resource, as `DescribeStackResources` reports it. */ export interface StackResource { logicalId: string; type: string; physicalId?: string; status?: string; timestamp?: string; } /** `DescribeStackResources` for one stack, mapped off the Query XML. */ export declare function describeStackResources(stackName: string, options?: AwsReadClientOptions): Promise; /** `DescribeStacks` outputs for one stack, as `key → value`. */ export declare function describeStackOutputs(stackName: string, options?: AwsReadClientOptions): Promise>; /** One live resource, as Cloud Control describes it. */ export interface CloudControlDescription { identifier: string; properties: Record; } /** * Cloud Control returns a resource model as a JSON *string* inside the * envelope, so every description unwraps twice. Returns null when either level * does not parse to an object — an unparseable body is a failed read, not an * empty resource. */ export declare function parseResourceDescription(raw: unknown): CloudControlDescription | null; /** `GetResource` — the full live model for one identifier. */ export declare function getResource(typeName: string, identifier: string, options?: AwsReadClientOptions): Promise; /** * `ListResources` — every live resource of one type, paginated to exhaustion. * * `resourceModel` is Cloud Control's `ResourceModel`: the scope a child-typed * listing requires (`{ RoleName }` for `AWS::IAM::RolePolicy`, * `{ TopicArn }` for `AWS::SNS::Subscription`). The API takes it as a JSON * *string*, the same double encoding `GetResource` answers with; this takes * the object and encodes it so no caller repeats that detail. */ export declare function listResources(typeName: string, options?: AwsReadClientOptions, resourceModel?: Record): Promise; //# sourceMappingURL=read-client.d.ts.map