@go-to-k/cdkd 0.283.13 → 0.283.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
- import { $ as coerceCfnBoolean, $t as resolveStateBucketWithDefault, A as cyan, An as MissingCdkCliError, At as AssetModeResolver, B as findSilentDropProperties, Bt as getDockerCmd, C as makeCanonicalizePropertiesFn, Ct as stringifyValue, D as renderStatefulReason, Dn as LocalMigrateError, Dt as loadPublishableAssetManifest, E as isStatefulRecreateTargetSync, En as LocalInvokeBuildError$1, Et as createAssetRedirectResolver, F as IAMRoleProvider, Fn as ResourceUpdateNotSupportedError, Ft as validateAssetBucketName, G as IntrinsicFunctionResolver, Gn as isRetryableTransientError, Gt as Synthesizer, H as slowCcOperationTimeoutMs, Hn as normalizeAwsError, Ht as runDockerStreaming, I as collectInlinePolicyNamesManagedBySiblings, In as StackHasActiveImportsError, It as validateContainerRepoName, J as refStateLookupFromResource, Jt as getLegacyStateBucketName, K as cfnRefValueFromPhysicalId, Kn as isThrottlingError, Kt as synthesisStatusMessage, L as clearOnUpdateRemoval, Ln as StackTerminationProtectionError, Lt as buildDenyExternalAccessPolicy, M as green, Mn as PartialFailureError, Mt as ensureAssetStorage, N as red, Nn as ProvisioningError, Nt as getBootstrapMarkerKey, O as formatResourceLine, On as LocalStartServiceError, Ot as rewriteTemplateAssetReferences, P as yellow, Pn as ResourceTimeoutError, Pt as parseBootstrapMarker, Q as assertRegionMatch, Qt as resolveSkipPrefix, R as ProviderRegistry, Rt as buildDockerImage, S as unsupportedFinalSnapshotError, Sn as CdkdError, St as AssetPublisher, T as MULTI_REGION_RECREATE_BLOCKED_TYPES, Tn as DeployCancelledError, Tt as buildAssetRedirectMap, U as disableInstanceApiTermination, Un as withErrorHandling, Ut as AssetManifestLoader, V as CloudControlProvider, Vt as runDockerForeground, W as isTerminationProtectionPropagationError, Wn as isMarkedNonRetryable, Wt as getDockerImageBySourceHash, X as normalizeAwsTagsToCfn, Xt as resolveAutoAssetStorage, Y as WAFv2WebACLProvider, Yt as resolveApp, Z as resolveExplicitPhysicalId, Zt as resolveCaptureObservedState, _ as buildFinalSnapshotIdentifier, _n as AwsClients, _t as TemplateParser, a as DeploymentEventsStore, an as CFN_TEMPLATE_URL_LIMIT, at as requireConfigObject, b as isFinalSnapshotError, bn as setAwsClients, bt as rebuildClientForBucketRegion, c as replayFailedOperations, cn as uploadCfnTemplate, ct as s3BucketDomainName, d as deleteSkipReason, dn as canonicalizeRegion, dt as s3BucketWebsiteUrl, en as resolveStateBucketWithDefaultAndSource, et as configBooleanRefusal, f as withResourceDeadline, fn as derivePartitionAndUrlSuffix, ft as applyRoleArnIfSet, g as PRE_DELETE_SNAPSHOT_TYPES, gn as resolveBucketRegion, gt as DagBuilder, h as ATOMIC_FINAL_SNAPSHOT_TYPES, ht as withRetry, i as DeploymentEventsReader, in as CFN_TEMPLATE_BODY_LIMIT, it as requireConfigArray, j as gray, jn as NestedStackChildDirectDestroyError, jt as BOOTSTRAP_MARKER_PREFIX, k as bold, kt as escapeRegExp, l as replayRollback, ln as expectedOwnerParam, lt as s3BucketDualStackDomainName, m as computeImplicitDeleteEdges, mn as processStackMessages, mt as describeTypeWithThrottleRetry, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nn as stateBucketExistenceConfirmed, nt as readConfigString, o as planFailedOps, on as MIGRATE_TMP_PREFIX, ot as requireConfigString, p as IMPLICIT_DELETE_DEPENDENCIES, pn as AssemblyReader, pt as DiffCalculator, q as getAccountInfo, qn as markNonRetryable, qt as getDefaultStateBucketName, r as DeployEngine, rn as warnDeprecatedNoPrefixCliFlag, rt as replayWarn, s as planRollback, sn as findLargeInlineResources, st as s3BucketArn, t as DEFAULT_RESOURCE_TIMEOUT_MS, tn as resolveUseCdkBootstrapAssets, tt as configStringRefusal, u as UNSPECIFIED_SKIP_REASON, un as PARTITION_TABLE, ut as s3BucketRegionalDomainName, v as ccRoutedFinalSnapshotError, vn as getAwsClients, vt as LockManager, w as extractDeploymentEventError, wt as WorkGraph, x as refusesFinalSnapshot, xt as shouldRetainResource, y as createPreDeleteFinalSnapshot, yn as resetAwsClients, yt as S3StateBackend, z as findActionableSilentDrops, zt as formatDockerLoginError } from "./deploy-engine-BdKa1DA4.js";
2
+ import { $ as coerceCfnBoolean, $t as resolveStateBucketWithDefault, A as cyan, An as MissingCdkCliError, At as AssetModeResolver, B as findSilentDropProperties, Bt as getDockerCmd, C as makeCanonicalizePropertiesFn, Ct as stringifyValue, D as renderStatefulReason, Dn as LocalMigrateError, Dt as loadPublishableAssetManifest, E as isStatefulRecreateTargetSync, En as LocalInvokeBuildError$1, Et as createAssetRedirectResolver, F as IAMRoleProvider, Fn as ResourceUpdateNotSupportedError, Ft as validateAssetBucketName, G as IntrinsicFunctionResolver, Gn as isRetryableTransientError, Gt as Synthesizer, H as slowCcOperationTimeoutMs, Hn as normalizeAwsError, Ht as runDockerStreaming, I as collectInlinePolicyNamesManagedBySiblings, In as StackHasActiveImportsError, It as validateContainerRepoName, J as refStateLookupFromResource, Jt as getLegacyStateBucketName, K as cfnRefValueFromPhysicalId, Kn as isThrottlingError, Kt as synthesisStatusMessage, L as clearOnUpdateRemoval, Ln as StackTerminationProtectionError, Lt as buildDenyExternalAccessPolicy, M as green, Mn as PartialFailureError, Mt as ensureAssetStorage, N as red, Nn as ProvisioningError, Nt as getBootstrapMarkerKey, O as formatResourceLine, On as LocalStartServiceError, Ot as rewriteTemplateAssetReferences, P as yellow, Pn as ResourceTimeoutError, Pt as parseBootstrapMarker, Q as assertRegionMatch, Qt as resolveSkipPrefix, R as ProviderRegistry, Rt as buildDockerImage, S as unsupportedFinalSnapshotError, Sn as CdkdError, St as AssetPublisher, T as MULTI_REGION_RECREATE_BLOCKED_TYPES, Tn as DeployCancelledError, Tt as buildAssetRedirectMap, U as disableInstanceApiTermination, Un as withErrorHandling, Ut as AssetManifestLoader, V as CloudControlProvider, Vt as runDockerForeground, W as isTerminationProtectionPropagationError, Wn as isMarkedNonRetryable, Wt as getDockerImageBySourceHash, X as normalizeAwsTagsToCfn, Xt as resolveAutoAssetStorage, Y as WAFv2WebACLProvider, Yt as resolveApp, Z as resolveExplicitPhysicalId, Zt as resolveCaptureObservedState, _ as buildFinalSnapshotIdentifier, _n as AwsClients, _t as TemplateParser, a as DeploymentEventsStore, an as CFN_TEMPLATE_URL_LIMIT, at as requireConfigObject, b as isFinalSnapshotError, bn as setAwsClients, bt as rebuildClientForBucketRegion, c as replayFailedOperations, cn as uploadCfnTemplate, ct as s3BucketDomainName, d as deleteSkipReason, dn as canonicalizeRegion, dt as s3BucketWebsiteUrl, en as resolveStateBucketWithDefaultAndSource, et as configBooleanRefusal, f as withResourceDeadline, fn as derivePartitionAndUrlSuffix, ft as applyRoleArnIfSet, g as PRE_DELETE_SNAPSHOT_TYPES, gn as resolveBucketRegion, gt as DagBuilder, h as ATOMIC_FINAL_SNAPSHOT_TYPES, ht as withRetry, i as DeploymentEventsReader, in as CFN_TEMPLATE_BODY_LIMIT, it as requireConfigArray, j as gray, jn as NestedStackChildDirectDestroyError, jt as BOOTSTRAP_MARKER_PREFIX, k as bold, kt as escapeRegExp, l as replayRollback, ln as expectedOwnerParam, lt as s3BucketDualStackDomainName, m as computeImplicitDeleteEdges, mn as processStackMessages, mt as describeTypeWithThrottleRetry, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nn as stateBucketExistenceConfirmed, nt as readConfigString, o as planFailedOps, on as MIGRATE_TMP_PREFIX, ot as requireConfigString, p as IMPLICIT_DELETE_DEPENDENCIES, pn as AssemblyReader, pt as DiffCalculator, q as getAccountInfo, qn as markNonRetryable, qt as getDefaultStateBucketName, r as DeployEngine, rn as warnDeprecatedNoPrefixCliFlag, rt as replayWarn, s as planRollback, sn as findLargeInlineResources, st as s3BucketArn, t as DEFAULT_RESOURCE_TIMEOUT_MS, tn as resolveUseCdkBootstrapAssets, tt as configStringRefusal, u as UNSPECIFIED_SKIP_REASON, un as PARTITION_TABLE, ut as s3BucketRegionalDomainName, v as ccRoutedFinalSnapshotError, vn as getAwsClients, vt as LockManager, w as extractDeploymentEventError, wt as WorkGraph, x as refusesFinalSnapshot, xt as shouldRetainResource, y as createPreDeleteFinalSnapshot, yn as resetAwsClients, yt as S3StateBackend, z as findActionableSilentDrops, zt as formatDockerLoginError } from "./deploy-engine-B8gQpv93.js";
3
3
  import { a as getLiveRenderer, c as PATTERN_B_RESOURCE_TYPES, d as generateResourceNameWithFallback, h as withStackName, i as runStackBuffered, m as withSkipPrefix, n as getLogger, o as PATTERN_B_NAME_OPTIONS, s as PATTERN_B_NAME_PROPERTIES, u as generateResourceName } from "./logger-zRrlbaQt.js";
4
- import { t as ASGProvider } from "./asg-provider-CrVgG0Pj.js";
4
+ import { t as ASGProvider } from "./asg-provider-CgnPBnOS.js";
5
5
  import { AsyncLocalStorage } from "node:async_hooks";
6
6
  import { createHash, randomBytes, randomUUID } from "node:crypto";
7
7
  import { CopyObjectCommand, CreateBucketCommand, DeleteBucketAnalyticsConfigurationCommand, DeleteBucketCommand, DeleteBucketCorsCommand, DeleteBucketEncryptionCommand, DeleteBucketIntelligentTieringConfigurationCommand, DeleteBucketInventoryConfigurationCommand, DeleteBucketLifecycleCommand, DeleteBucketMetricsConfigurationCommand, DeleteBucketOwnershipControlsCommand, DeleteBucketPolicyCommand, DeleteBucketReplicationCommand, DeleteBucketTaggingCommand, DeleteBucketWebsiteCommand, DeleteObjectsCommand, GetBucketAccelerateConfigurationCommand, GetBucketCorsCommand, GetBucketEncryptionCommand, GetBucketLifecycleConfigurationCommand, GetBucketLocationCommand, GetBucketLoggingCommand, GetBucketNotificationConfigurationCommand, GetBucketOwnershipControlsCommand, GetBucketPolicyCommand, GetBucketReplicationCommand, GetBucketTaggingCommand, GetBucketVersioningCommand, GetBucketWebsiteCommand, GetObjectCommand, GetObjectLockConfigurationCommand, GetPublicAccessBlockCommand, HeadBucketCommand, ListBucketAnalyticsConfigurationsCommand, ListBucketIntelligentTieringConfigurationsCommand, ListBucketInventoryConfigurationsCommand, ListBucketMetricsConfigurationsCommand, ListObjectVersionsCommand, ListObjectsV2Command, NoSuchBucket, PutBucketAccelerateConfigurationCommand, PutBucketAnalyticsConfigurationCommand, PutBucketCorsCommand, PutBucketEncryptionCommand, PutBucketIntelligentTieringConfigurationCommand, PutBucketInventoryConfigurationCommand, PutBucketLifecycleConfigurationCommand, PutBucketLoggingCommand, PutBucketMetricsConfigurationCommand, PutBucketNotificationConfigurationCommand, PutBucketOwnershipControlsCommand, PutBucketPolicyCommand, PutBucketReplicationCommand, PutBucketTaggingCommand, PutBucketVersioningCommand, PutBucketWebsiteCommand, PutObjectCommand, PutObjectLockConfigurationCommand, PutPublicAccessBlockCommand, S3Client, S3ServiceException } from "@aws-sdk/client-s3";
@@ -68858,12 +68858,21 @@ function indexGrandchildTemplatePaths(childTemplate, childTemplatePath) {
68858
68858
  * `--stack-region`, bucket resolution failure) logs at warn and returns
68859
68859
  * `undefined`. Auth failures and other genuine errors propagate.
68860
68860
  *
68861
- * Read-only against state no lock acquisition or save path here.
68861
+ * No lock acquisition and no `state.json` write path here. That is NOT the
68862
+ * same as "read-only against the bucket", and the difference was mis-stated
68863
+ * until issue #1836: {@link buildCrossStackResolver}'s exports-index lookup
68864
+ * goes through {@link ExportIndexStore}, whose `load` REBUILDS the index from
68865
+ * the per-stack state files on a miss and PUTs the rebuilt object
68866
+ * (`cdkd/_index/{region}/exports.json`). So an `Fn::ImportValue` lookup from a
68867
+ * `cdkd local` command can write that one derived key. Keeping the index KEY
68868
+ * the same spelling the state records carry is what keeps that write CORRECT
68869
+ * rather than an empty index — see the note on `consumerRegion` below.
68862
68870
  */
68863
68871
  async function loadStateForStack(stackName, synthRegion, opts) {
68864
68872
  const logger = getLogger();
68865
68873
  const prefix = opts.logPrefix ?? "--from-state";
68866
- const region = opts.region ?? process.env["AWS_REGION"] ?? process.env["AWS_DEFAULT_REGION"] ?? synthRegion ?? "us-east-1";
68874
+ const region = canonicalizeRegion(opts.region ?? process.env["AWS_REGION"] ?? process.env["AWS_DEFAULT_REGION"] ?? synthRegion ?? "us-east-1");
68875
+ const clientRegion = canonicalizeRegion(opts.region) || void 0;
68867
68876
  let stateBucket;
68868
68877
  try {
68869
68878
  stateBucket = await resolveStateBucketWithDefault(opts.stateBucket, region);
@@ -68872,7 +68881,7 @@ async function loadStateForStack(stackName, synthRegion, opts) {
68872
68881
  return;
68873
68882
  }
68874
68883
  const awsClients = new AwsClients({
68875
- ...opts.region !== void 0 && { region: opts.region },
68884
+ ...clientRegion !== void 0 && { region: clientRegion },
68876
68885
  ...opts.profile !== void 0 && { profile: opts.profile }
68877
68886
  });
68878
68887
  setAwsClients(awsClients);
@@ -68882,7 +68891,7 @@ async function loadStateForStack(stackName, synthRegion, opts) {
68882
68891
  prefix: opts.statePrefix
68883
68892
  };
68884
68893
  const stateBackend = new S3StateBackend(awsClients.s3, stateConfig, {
68885
- ...opts.region !== void 0 && { region: opts.region },
68894
+ ...clientRegion !== void 0 && { region: clientRegion },
68886
68895
  ...opts.profile !== void 0 && { profile: opts.profile }
68887
68896
  });
68888
68897
  await stateBackend.verifyBucketExists();
@@ -68891,15 +68900,58 @@ async function loadStateForStack(stackName, synthRegion, opts) {
68891
68900
  logger.warn(`${prefix}: no cdkd state found for stack '${stackName}' in bucket '${stateBucket}'. Was it deployed via 'cdkd deploy'? Falling back.`);
68892
68901
  return;
68893
68902
  }
68903
+ /**
68904
+ * Issue #1836: EXACT match first, case-insensitive second.
68905
+ *
68906
+ * `S3StateBackend.listStacks` dedupes on the EXACT `{stack}\0{region}` pair,
68907
+ * so `cdkd/MyStack/US-EAST-1/state.json` and
68908
+ * `cdkd/MyStack/us-east-1/state.json` are two DISTINCT refs — and
68909
+ * ListObjectsV2 returns them in ASCII order, i.e. the upper-cased one FIRST.
68910
+ * A fold-only `find` therefore hands `--stack-region us-east-1` the OTHER
68911
+ * record, silently reading state the user did not name; the pre-fold `===`
68912
+ * got that case right. So the fold is the RECOVERY for a case mismatch, never
68913
+ * an override of a spelling that exists verbatim.
68914
+ *
68915
+ * `candidate` is therefore the spelling the USER supplied, not the folded
68916
+ * one (round 3 of the #1836 review): the fold at each command's handler
68917
+ * entry and at the `--from-state` factory is what an SDK client needs, but
68918
+ * feeding it to this compare made the exact-match rule unreachable from any
68919
+ * real CLI invocation — every candidate was already canonical, so `exact`
68920
+ * could only ever match an already-canonical record and `--stack-region
68921
+ * US-EAST-1` read the `us-east-1` one while REPORTING it as the exact
68922
+ * spelling. `opts.rawStackRegion` carries the unfolded value here; the
68923
+ * recovery arm folds both sides itself, so nothing upstream has to.
68924
+ *
68925
+ * When more than one ref is canonical-equal the choice is announced, because
68926
+ * either answer reads some record the user did not spell out in full and the
68927
+ * silence is what made the original miss hard to diagnose. The wording
68928
+ * states which of the two rules decided it — and does so from the same
68929
+ * `exact` binding the choice was made from, so it cannot claim an exact
68930
+ * match the compare did not make.
68931
+ */
68932
+ const findByRegion = (candidate, source) => {
68933
+ const exact = refs.find((r) => r.region === candidate);
68934
+ const folded = refs.filter((r) => canonicalizeRegion(r.region) === canonicalizeRegion(candidate));
68935
+ const chosen = exact ?? folded[0];
68936
+ if (folded.length > 1 && chosen) {
68937
+ const how = exact ? `matches ${source} '${candidate}' exactly` : `no record spells ${source} '${candidate}' exactly, so this is a case-insensitive recovery`;
68938
+ logger.warn(`${prefix}: stack '${stackName}' has state under ${folded.length} case-variant spellings of ${source} '${candidate}' (${folded.map((r) => r.region ?? "(legacy)").join(", ")}). Reading '${chosen.region ?? "(legacy)"}' — ${how}.`);
68939
+ }
68940
+ return chosen;
68941
+ };
68942
+ let synthMatch;
68943
+ if (!opts.stackRegion && synthRegion) synthMatch = findByRegion(synthRegion, "the synth-derived stack region");
68894
68944
  let targetRegion;
68895
68945
  if (opts.stackRegion) {
68896
- if (!refs.find((r) => r.region === opts.stackRegion)) {
68946
+ const requestedRegion = opts.rawStackRegion ?? opts.stackRegion;
68947
+ const found = findByRegion(requestedRegion, "--stack-region");
68948
+ if (!found) {
68897
68949
  const seen = refs.map((r) => r.region ?? "(legacy)").join(", ");
68898
- logger.warn(`${prefix}: stack '${stackName}' has no state in region '${opts.stackRegion}' (available: ${seen}). Falling back.`);
68950
+ logger.warn(`${prefix}: stack '${stackName}' has no state in region '${requestedRegion}' (available: ${seen}). Falling back.`);
68899
68951
  return;
68900
68952
  }
68901
- targetRegion = opts.stackRegion;
68902
- } else if (synthRegion && refs.some((r) => r.region === synthRegion)) targetRegion = synthRegion;
68953
+ targetRegion = found.region ?? requestedRegion;
68954
+ } else if (synthRegion && synthMatch) targetRegion = synthMatch.region ?? synthRegion;
68903
68955
  else if (refs.length === 1) targetRegion = refs[0].region ?? synthRegion ?? region;
68904
68956
  else {
68905
68957
  const seen = refs.map((r) => r.region ?? "(legacy)").join(", ");
@@ -68948,7 +69000,8 @@ async function loadStateForStack(stackName, synthRegion, opts) {
68948
69000
  async function loadBootstrapContainerRepo(synthRegion, opts) {
68949
69001
  const logger = getLogger();
68950
69002
  const prefix = opts.logPrefix ?? "--from-state";
68951
- const region = opts.region ?? opts.stackRegion ?? synthRegion ?? process.env["AWS_REGION"] ?? process.env["AWS_DEFAULT_REGION"] ?? "us-east-1";
69003
+ const rawRegion = opts.region ?? opts.rawStackRegion ?? opts.stackRegion ?? synthRegion ?? process.env["AWS_REGION"] ?? process.env["AWS_DEFAULT_REGION"] ?? "us-east-1";
69004
+ const region = canonicalizeRegion(rawRegion);
68952
69005
  let stateBucket;
68953
69006
  try {
68954
69007
  stateBucket = await resolveStateBucketWithDefault(opts.stateBucket, region);
@@ -68956,8 +69009,9 @@ async function loadBootstrapContainerRepo(synthRegion, opts) {
68956
69009
  logger.debug(`${prefix}: could not resolve state bucket for the bootstrap-marker read: ${err instanceof Error ? err.message : String(err)}. Falling back to conventional asset-repo names.`);
68957
69010
  return;
68958
69011
  }
69012
+ const clientRegion = canonicalizeRegion(opts.region) || void 0;
68959
69013
  const awsClients = new AwsClients({
68960
- ...opts.region !== void 0 && { region: opts.region },
69014
+ ...clientRegion !== void 0 && { region: clientRegion },
68961
69015
  ...opts.profile !== void 0 && { profile: opts.profile }
68962
69016
  });
68963
69017
  setAwsClients(awsClients);
@@ -68967,16 +69021,25 @@ async function loadBootstrapContainerRepo(synthRegion, opts) {
68967
69021
  prefix: opts.statePrefix
68968
69022
  };
68969
69023
  const stateBackend = new S3StateBackend(awsClients.s3, stateConfig, {
68970
- ...opts.region !== void 0 && { region: opts.region },
69024
+ ...clientRegion !== void 0 && { region: clientRegion },
68971
69025
  ...opts.profile !== void 0 && { profile: opts.profile }
68972
69026
  });
68973
69027
  const markerKey = getBootstrapMarkerKey(region);
68974
- const body = await stateBackend.getRawObject(markerKey);
69028
+ let resolvedKey = markerKey;
69029
+ let body = await stateBackend.getRawObject(markerKey);
69030
+ if (body === null && rawRegion !== region) {
69031
+ const rawKey = getBootstrapMarkerKey(rawRegion);
69032
+ body = await stateBackend.getRawObject(rawKey);
69033
+ if (body !== null) {
69034
+ resolvedKey = rawKey;
69035
+ logger.debug(`${prefix}: bootstrap marker found at the un-folded key '${rawKey}' (no '${markerKey}') — an upper-cased region was used at 'cdkd bootstrap' time.`);
69036
+ }
69037
+ }
68975
69038
  if (body === null) {
68976
- logger.debug(`${prefix}: no bootstrap marker at '${markerKey}' in bucket '${stateBucket}' — assuming conventional asset-repo names.`);
69039
+ logger.debug(`${prefix}: no bootstrap marker at '${markerKey}'${rawRegion !== region ? ` (nor '${getBootstrapMarkerKey(rawRegion)}')` : ""} in bucket '${stateBucket}' — assuming conventional asset-repo names.`);
68977
69040
  return;
68978
69041
  }
68979
- const marker = parseBootstrapMarker(body, markerKey);
69042
+ const marker = parseBootstrapMarker(body, resolvedKey);
68980
69043
  logger.debug(`${prefix}: bootstrap marker for ${region} names container repo '${marker.containerRepo}'.`);
68981
69044
  return marker.containerRepo;
68982
69045
  } catch (err) {
@@ -69015,13 +69078,14 @@ async function buildCrossStackResolver(consumerRegion, opts) {
69015
69078
  const prefix = opts.logPrefix ?? "--from-state";
69016
69079
  let stateBucket;
69017
69080
  try {
69018
- stateBucket = await resolveStateBucketWithDefault(opts.stateBucket, consumerRegion);
69081
+ stateBucket = await resolveStateBucketWithDefault(opts.stateBucket, canonicalizeRegion(consumerRegion));
69019
69082
  } catch (err) {
69020
69083
  logger.warn(`${prefix}: cross-stack resolver could not resolve state bucket: ${err instanceof Error ? err.message : String(err)}. Fn::ImportValue / Fn::GetStackOutput env entries will warn-and-drop.`);
69021
69084
  return;
69022
69085
  }
69086
+ const clientRegion = canonicalizeRegion(opts.region) || void 0;
69023
69087
  const awsClients = new AwsClients({
69024
- ...opts.region !== void 0 && { region: opts.region },
69088
+ ...clientRegion !== void 0 && { region: clientRegion },
69025
69089
  ...opts.profile !== void 0 && { profile: opts.profile }
69026
69090
  });
69027
69091
  const stateConfig = {
@@ -69029,7 +69093,7 @@ async function buildCrossStackResolver(consumerRegion, opts) {
69029
69093
  prefix: opts.statePrefix
69030
69094
  };
69031
69095
  const stateBackend = new S3StateBackend(awsClients.s3, stateConfig, {
69032
- ...opts.region !== void 0 && { region: opts.region },
69096
+ ...clientRegion !== void 0 && { region: clientRegion },
69033
69097
  ...opts.profile !== void 0 && { profile: opts.profile }
69034
69098
  });
69035
69099
  try {
@@ -69061,9 +69125,10 @@ async function buildCrossStackResolver(consumerRegion, opts) {
69061
69125
  logger.debug(`${prefix}: failed to list stacks during Fn::ImportValue fallback for '${exportName}': ${err instanceof Error ? err.message : String(err)}`);
69062
69126
  return;
69063
69127
  }
69128
+ const canonicalConsumerRegion = canonicalizeRegion(consumerRegion);
69064
69129
  for (const ref of refs) {
69065
69130
  const region = ref.region ?? consumerRegion;
69066
- if (region !== consumerRegion) continue;
69131
+ if (canonicalizeRegion(region) !== canonicalConsumerRegion) continue;
69067
69132
  try {
69068
69133
  const got = await stateBackend.getState(ref.stackName, region);
69069
69134
  if (!got || !got.state.outputs) continue;
@@ -69080,14 +69145,31 @@ async function buildCrossStackResolver(consumerRegion, opts) {
69080
69145
  }
69081
69146
  },
69082
69147
  async resolveGetStackOutput(producerStack, producerRegion, outputName) {
69083
- try {
69084
- const got = await stateBackend.getState(producerStack, producerRegion);
69148
+ const readOutput = (got) => {
69085
69149
  if (!got || !got.state.outputs) return void 0;
69086
69150
  if (!(outputName in got.state.outputs)) return void 0;
69087
69151
  const value = got.state.outputs[outputName];
69088
69152
  if (typeof value === "string") return value;
69089
69153
  if (typeof value === "number" || typeof value === "boolean") return String(value);
69090
69154
  return JSON.stringify(value);
69155
+ };
69156
+ try {
69157
+ const got = await stateBackend.getState(producerStack, producerRegion);
69158
+ if (got) return readOutput(got);
69159
+ const canonicalProducerRegion = canonicalizeRegion(producerRegion);
69160
+ const refs = await stateBackend.listStacks();
69161
+ for (const ref of refs) {
69162
+ if (ref.stackName !== producerStack) continue;
69163
+ const region = ref.region ?? producerRegion;
69164
+ if (region === producerRegion) continue;
69165
+ if (canonicalizeRegion(region) !== canonicalProducerRegion) continue;
69166
+ const recovered = readOutput(await stateBackend.getState(producerStack, region));
69167
+ if (recovered !== void 0) {
69168
+ logger.debug(`${prefix}: Fn::GetStackOutput '${producerStack}.${outputName}' resolved from the case-variant state record '${region}' (no record spells '${producerRegion}' exactly).`);
69169
+ return recovered;
69170
+ }
69171
+ }
69172
+ return;
69091
69173
  } catch (err) {
69092
69174
  logger.debug(`${prefix}: state read failed for Fn::GetStackOutput '${producerStack}.${outputName}' (${producerRegion}): ${err instanceof Error ? err.message : String(err)}`);
69093
69175
  return;
@@ -69127,6 +69209,7 @@ var S3LocalStateProvider = class {
69127
69209
  const loaded = await loadStateForStack(stackName, synthRegion, {
69128
69210
  statePrefix: this.opts.statePrefix,
69129
69211
  ...this.opts.stackRegion !== void 0 && { stackRegion: this.opts.stackRegion },
69212
+ ...this.opts.rawStackRegion !== void 0 && { rawStackRegion: this.opts.rawStackRegion },
69130
69213
  ...this.opts.stateBucket !== void 0 && { stateBucket: this.opts.stateBucket },
69131
69214
  ...this.opts.region !== void 0 && { region: this.opts.region },
69132
69215
  ...this.opts.profile !== void 0 && { profile: this.opts.profile }
@@ -69193,12 +69276,16 @@ var S3LocalStateProvider = class {
69193
69276
  */
69194
69277
  const fromStateFactory = (options) => {
69195
69278
  const opts = options;
69279
+ const clientRegion = canonicalizeRegion(opts.region) || void 0;
69280
+ const stackRegion = canonicalizeRegion(opts.stackRegion) || void 0;
69281
+ const rawStackRegion = opts.rawStackRegion || opts.stackRegion || void 0;
69196
69282
  return new S3LocalStateProvider({
69197
69283
  statePrefix: opts.statePrefix,
69198
69284
  ...opts.stateBucket !== void 0 && { stateBucket: opts.stateBucket },
69199
- ...opts.region !== void 0 && { region: opts.region },
69285
+ ...clientRegion !== void 0 && { region: clientRegion },
69200
69286
  ...opts.profile !== void 0 && { profile: opts.profile },
69201
- ...opts.stackRegion !== void 0 && { stackRegion: opts.stackRegion }
69287
+ ...stackRegion !== void 0 && { stackRegion },
69288
+ ...rawStackRegion !== void 0 && { rawStackRegion }
69202
69289
  });
69203
69290
  };
69204
69291
  /**
@@ -72380,6 +72467,10 @@ async function localStartApiCommand(target, options) {
72380
72467
  const logger = getLogger();
72381
72468
  if (options.verbose) logger.setLevel("debug");
72382
72469
  if (options.region !== void 0) options.region = canonicalizeRegion(options.region);
72470
+ if (options.stackRegion !== void 0) {
72471
+ options.rawStackRegion = options.stackRegion;
72472
+ options.stackRegion = canonicalizeRegion(options.stackRegion);
72473
+ }
72383
72474
  let apiFilter = target;
72384
72475
  if (options.api !== void 0) {
72385
72476
  if (target !== void 0) throw new Error(`Cannot specify both positional target ('${target}') and --api flag ('${options.api}'). Use one or the other. The positional form is preferred — '--api' is a deprecated alias.`);
@@ -74722,6 +74813,10 @@ async function localRunTaskCommand(target, options) {
74722
74813
  if (options.verbose) logger.setLevel("debug");
74723
74814
  warnIfDeprecatedRegion(options);
74724
74815
  if (options.region !== void 0) options.region = canonicalizeRegion(options.region);
74816
+ if (options.stackRegion !== void 0) {
74817
+ options.rawStackRegion = options.stackRegion;
74818
+ options.stackRegion = canonicalizeRegion(options.stackRegion);
74819
+ }
74725
74820
  const state = createEcsRunState();
74726
74821
  let sigintHandler;
74727
74822
  let sigintCount = 0;
@@ -74766,12 +74861,12 @@ async function localRunTaskCommand(target, options) {
74766
74861
  const { stacks } = await synthesizer.synthesize(synthOpts);
74767
74862
  const candidate = pickCandidateStack(parseEcsTarget(target).stackPattern, stacks);
74768
74863
  stateProvider = createLocalStateProvider$1(options, candidate?.stackName ?? "", candidate?.region);
74769
- const imageContext = await buildEcsImageResolutionContext$1(candidate, stateProvider, options);
74864
+ const { context: imageContext, stateRecordRegion } = await buildEcsImageResolutionContext$1(candidate, stateProvider, options);
74770
74865
  const task = resolveEcsTaskTarget(target, stacks, imageContext);
74771
74866
  logger.info(`Target: ${task.stack.stackName}/${task.taskDefinitionLogicalId} (family=${task.family}, containers=${task.containers.length})`);
74772
74867
  const taskNeeds = detectEcsImageResolutionNeeds(stacks.find((s) => s.stackName === task.stack.stackName) ?? task.stack);
74773
74868
  if (stateProvider && taskNeeds.needsCrossStackResolver) {
74774
- const consumerRegion = options.region ?? process.env["AWS_REGION"] ?? process.env["AWS_DEFAULT_REGION"] ?? task.stack.region ?? "us-east-1";
74869
+ const consumerRegion = resolveEcsConsumerRegion(stateRecordRegion, options, task.stack.region);
74775
74870
  const resolver = await stateProvider.buildCrossStackResolver(consumerRegion);
74776
74871
  if (resolver) await applyCrossStackResolverToTask(task, {
74777
74872
  resources: imageContext?.stateResources ?? {},
@@ -74881,9 +74976,48 @@ async function assumeTaskRole(roleArn, region) {
74881
74976
  }
74882
74977
  }
74883
74978
  /**
74979
+ * Region the cross-stack resolver is scoped to for `cdkd local run-task`
74980
+ * (issue #1836).
74981
+ *
74982
+ * This value does THREE things (round 3 said TWO and was wrong — corrected in
74983
+ * round 4), and the first is why the state record's own spelling outranks the env
74984
+ * chain:
74985
+ *
74986
+ * 1. it becomes the exports-index KEY `cdkd/_index/{region}/exports.json`;
74987
+ * 2. it is the same-region filter of the index-miss per-stack scan; and
74988
+ * 3. via `SubstitutionContext.consumerRegion`, it is cdk-local's DEFAULT producer
74989
+ * region for an `Fn::GetStackOutput` that carries no explicit `Region` — i.e.
74990
+ * it becomes the `cdkd/{producer}/{region}/state.json` key that intrinsic
74991
+ * reads.
74992
+ *
74993
+ * All three land in `buildCrossStackResolver` (`local-state-loader.ts`), whose
74994
+ * `resolveImport` scan (2) and — since round 4 — `resolveGetStackOutput` (3) fold
74995
+ * BOTH sides for the comparison / recovery, while (1) is deliberately raw.
74996
+ *
74997
+ * `local invoke` / `local invoke-agentcore` both pass `loaded.region` there; this
74998
+ * command passed a FOLDED env-chain value instead, so the two commands keyed one
74999
+ * index differently — and a key the state records do not spell makes
75000
+ * `ExportIndexStore`'s rebuild filter match zero refs and PUT an EMPTY index,
75001
+ * permanently degrading every `Fn::ImportValue` to the O(N) scan. Keyed by a
75002
+ * record's spelling the filter always matches.
75003
+ *
75004
+ * The env-chain arm remains for the case where no state record was loaded at all
75005
+ * (nothing in the task needed one), and it stays FOLDED for the reason the rest
75006
+ * of this file folds: both env-var links escape the handler-entry `--region`
75007
+ * fold, and a raw upper-cased spelling reaches an S3 key and an SDK client.
75008
+ *
75009
+ * @internal exported for unit tests.
75010
+ */
75011
+ function resolveEcsConsumerRegion(stateRecordRegion, options, synthRegion) {
75012
+ if (stateRecordRegion) return stateRecordRegion;
75013
+ return canonicalizeRegion((options.region || process.env["AWS_REGION"]) ?? process.env["AWS_DEFAULT_REGION"] ?? synthRegion ?? "us-east-1");
75014
+ }
75015
+ /**
74884
75016
  * Build the substitution context the ECS task resolver consumes (issue
74885
- * #264). Returns `undefined` when no container's `Image` field needs
74886
- * substitution the resolver behaves as before in that case.
75017
+ * #264), plus the region of the state record it loaded (if any — issue #1836;
75018
+ * see {@link resolveEcsConsumerRegion} for what reads it). `context` is
75019
+ * `undefined` when no container's `Image` field needs substitution — the
75020
+ * resolver behaves as before in that case.
74887
75021
  *
74888
75022
  * Tier 1 (pseudo parameters) fires `sts:GetCallerIdentity` once for
74889
75023
  * `${AWS::AccountId}`; region / partition / URL suffix come from the CLI
@@ -74902,10 +75036,11 @@ async function assumeTaskRole(roleArn, region) {
74902
75036
  */
74903
75037
  async function buildEcsImageResolutionContext$1(candidate, stateProvider, options) {
74904
75038
  const logger = getLogger();
74905
- if (!candidate) return void 0;
75039
+ if (!candidate) return {};
74906
75040
  const needs = detectEcsImageResolutionNeeds(candidate);
74907
- if (!needs.needsPseudoParameters && !needs.needsStateResources && !needs.needsEnvOrSecretSubstitution && !needs.needsAssetRepoMarker) return;
75041
+ if (!needs.needsPseudoParameters && !needs.needsStateResources && !needs.needsEnvOrSecretSubstitution && !needs.needsAssetRepoMarker) return {};
74908
75042
  const ctx = {};
75043
+ let stateRecordRegion;
74909
75044
  const wantsPseudoForEnvOrSecret = !!stateProvider && needs.needsEnvOrSecretSubstitution;
74910
75045
  if (needs.needsPseudoParameters || wantsPseudoForEnvOrSecret) {
74911
75046
  const region = canonicalizeRegion(options.region ?? process.env["AWS_REGION"] ?? process.env["AWS_DEFAULT_REGION"] ?? candidate.region);
@@ -74929,13 +75064,17 @@ async function buildEcsImageResolutionContext$1(candidate, stateProvider, option
74929
75064
  const wantsState = needs.needsStateResources || needs.needsEnvOrSecretSubstitution;
74930
75065
  if (stateProvider && wantsState) {
74931
75066
  const loaded = await stateProvider.load(candidate.stackName, candidate.region);
74932
- if (loaded) ctx.stateResources = loaded.resources;
75067
+ if (loaded) {
75068
+ ctx.stateResources = loaded.resources;
75069
+ stateRecordRegion = loaded.region;
75070
+ }
74933
75071
  } else if (!stateProvider && needs.needsStateResources) logger.warn("Container Image references a same-stack AWS::ECR::Repository. Pass --from-state (cdkd-deployed) or --from-cfn-stack (cdk-deployed) to substitute the deployed repository URI. Otherwise the resolver will surface its existing error.");
74934
75072
  else if (!stateProvider && needs.needsEnvOrSecretSubstitution) logger.warn("Container Environment / Secrets entries contain CloudFormation intrinsics (Ref / Fn::GetAtt / Fn::Sub / Fn::Join). Pass --from-state (cdkd-deployed) or --from-cfn-stack (cdk-deployed) to substitute them against deployed state. Without a state source these entries are dropped (per-key warnings will follow).");
74935
75073
  if (needs.needsAssetRepoMarker && options.fromState) {
74936
75074
  const containerRepo = await loadBootstrapContainerRepo(candidate.region, {
74937
75075
  statePrefix: options.statePrefix,
74938
75076
  ...options.stackRegion !== void 0 && { stackRegion: options.stackRegion },
75077
+ ...options.rawStackRegion !== void 0 && { rawStackRegion: options.rawStackRegion },
74939
75078
  ...options.stateBucket !== void 0 && { stateBucket: options.stateBucket },
74940
75079
  ...options.region !== void 0 && { region: options.region },
74941
75080
  ...options.profile !== void 0 && { profile: options.profile },
@@ -74943,7 +75082,10 @@ async function buildEcsImageResolutionContext$1(candidate, stateProvider, option
74943
75082
  });
74944
75083
  if (containerRepo !== void 0) ctx.cdkAssetContainerRepo = containerRepo;
74945
75084
  }
74946
- return ctx;
75085
+ return {
75086
+ context: ctx,
75087
+ ...stateRecordRegion !== void 0 && { stateRecordRegion }
75088
+ };
74947
75089
  }
74948
75090
  function pickCandidateStack(stackPattern, stacks) {
74949
75091
  if (stackPattern === null) {
@@ -75376,6 +75518,10 @@ async function localInvokeAgentCoreCommand(target, options) {
75376
75518
  if (options.verbose) logger.setLevel("debug");
75377
75519
  warnIfDeprecatedRegion(options);
75378
75520
  if (options.region !== void 0) options.region = canonicalizeRegion(options.region);
75521
+ if (options.stackRegion !== void 0) {
75522
+ options.rawStackRegion = options.stackRegion;
75523
+ options.stackRegion = canonicalizeRegion(options.stackRegion);
75524
+ }
75379
75525
  let containerId;
75380
75526
  let stopLogs;
75381
75527
  let sigintHandler;
@@ -76266,16 +76412,37 @@ function emitA2aResult(result) {
76266
76412
  function platformToArchitecture(platform) {
76267
76413
  return platform === "linux/amd64" ? "x86_64" : "arm64";
76268
76414
  }
76415
+ /**
76416
+ * Copy the developer's AWS credentials + region into the container env for the
76417
+ * DEFAULT (no `--assume-role`) path.
76418
+ *
76419
+ * Issue [#1836](https://github.com/go-to-k/cdkd/issues/1836): the two REGION
76420
+ * entries are folded through `canonicalizeRegion` on the way in; the credentials
76421
+ * are copied verbatim (an AKID / session token is case-SENSITIVE, so lower-casing
76422
+ * the whole pass-through list would corrupt every one of them). The value lands
76423
+ * in the container's own `AWS_REGION`, i.e. in every SDK client the agent builds,
76424
+ * and SDK endpoint resolution is case-SENSITIVE (`CN-NORTH-1` resolves the
76425
+ * COMMERCIAL partition).
76426
+ *
76427
+ * Folding only inside `applyAgentCoreCredentialEnv`'s assume-role arm — which is
76428
+ * where issue #1814 stopped, and what round 3 of the #1836 review found still
76429
+ * open here after the same gap was closed in `local-invoke.ts` — made ONE command
76430
+ * yield two different container regions for the same shell: `cn-north-1` with
76431
+ * `--assume-role`, `CN-NORTH-1` without.
76432
+ */
76269
76433
  function forwardAwsEnv$1(env) {
76270
- for (const key of [
76434
+ const passThrough = [
76271
76435
  "AWS_ACCESS_KEY_ID",
76272
76436
  "AWS_SECRET_ACCESS_KEY",
76273
76437
  "AWS_SESSION_TOKEN",
76274
76438
  "AWS_REGION",
76275
76439
  "AWS_DEFAULT_REGION"
76276
- ]) {
76440
+ ];
76441
+ const regionKeys = /* @__PURE__ */ new Set(["AWS_REGION", "AWS_DEFAULT_REGION"]);
76442
+ for (const key of passThrough) {
76277
76443
  const value = process.env[key];
76278
- if (value !== void 0) env[key] = value;
76444
+ if (value === void 0) continue;
76445
+ env[key] = regionKeys.has(key) ? canonicalizeRegion(value) : value;
76279
76446
  }
76280
76447
  }
76281
76448
  async function assumeAgentCoreExecutionRole(roleArn, region) {
@@ -76578,6 +76745,10 @@ async function localInvokeCommand(target, options) {
76578
76745
  if (options.verbose) logger.setLevel("debug");
76579
76746
  warnIfDeprecatedRegion(options);
76580
76747
  if (options.region !== void 0) options.region = canonicalizeRegion(options.region);
76748
+ if (options.stackRegion !== void 0) {
76749
+ options.rawStackRegion = options.stackRegion;
76750
+ options.stackRegion = canonicalizeRegion(options.stackRegion);
76751
+ }
76581
76752
  let imagePlan;
76582
76753
  let containerId;
76583
76754
  let stopLogs;
@@ -76731,29 +76902,12 @@ async function localInvokeCommand(target, options) {
76731
76902
  AWS_LAMBDA_LOG_STREAM_NAME: "local",
76732
76903
  ...envResult.resolved
76733
76904
  };
76734
- let assumeSucceeded = false;
76735
- if (resolvedAssumeRoleArn) {
76736
- const stsRegion = options.region ?? process.env["AWS_REGION"] ?? process.env["AWS_DEFAULT_REGION"];
76737
- try {
76738
- const creds = await assumeLambdaExecutionRole(resolvedAssumeRoleArn, stsRegion);
76739
- dockerEnv["AWS_ACCESS_KEY_ID"] = creds.accessKeyId;
76740
- dockerEnv["AWS_SECRET_ACCESS_KEY"] = creds.secretAccessKey;
76741
- dockerEnv["AWS_SESSION_TOKEN"] = creds.sessionToken;
76742
- if (stsRegion) dockerEnv["AWS_REGION"] = stsRegion;
76743
- assumeSucceeded = true;
76744
- } catch (err) {
76745
- const reason = err instanceof Error ? err.message : String(err);
76746
- logger.warn(`--assume-role: STS AssumeRole(${resolvedAssumeRoleArn}) failed: ${reason}. Falling back to the developer's shell credentials.`);
76747
- }
76748
- }
76749
- if (!assumeSucceeded) {
76750
- forwardAwsEnv(dockerEnv);
76751
- applyProfileCredentialsOverlay(dockerEnv, profileCredentials, false);
76752
- if (profileCredsFile) {
76753
- dockerEnv["AWS_SHARED_CREDENTIALS_FILE"] = profileCredsFile.containerPath;
76754
- dockerEnv["AWS_PROFILE"] = profileCredsFile.profileName;
76755
- }
76756
- }
76905
+ await applyLambdaCredentialEnv(dockerEnv, {
76906
+ ...resolvedAssumeRoleArn && { assumeRoleArn: resolvedAssumeRoleArn },
76907
+ ...options.region !== void 0 && { region: options.region },
76908
+ ...profileCredentials && { profileCredentials },
76909
+ ...profileCredsFile && { profileCredsFile }
76910
+ });
76757
76911
  let debugPort;
76758
76912
  if (options.debugPort) {
76759
76913
  debugPort = Number(options.debugPort);
@@ -77153,6 +77307,55 @@ async function readStdin() {
77153
77307
  return Buffer.concat(chunks).toString("utf-8");
77154
77308
  }
77155
77309
  /**
77310
+ * Inject AWS credentials into the container env. Precedence:
77311
+ * 1. `--assume-role` → STS-issued temp creds for the resolved ARN (on
77312
+ * STS failure, warn + fall through to dev creds). Swapping the
77313
+ * developer's credentials for the function's deployed execution role
77314
+ * degrades rather than hard-errors: this is a developer-loop tool, not
77315
+ * a security boundary, and the most common cause is the role's
77316
+ * `AssumeRolePolicy` not trusting the developer's IAM principal (a
77317
+ * config gap, not a cdkd bug).
77318
+ * 2. dev shell creds (`forwardAwsEnv` — which folds the region case for the
77319
+ * same reason arm 1 does) + `--profile` overlay
77320
+ * ({@link applyProfileCredentialsOverlay}) + the bind-mounted
77321
+ * credentials-file env so handler `fromIni({ profile })` resolves.
77322
+ * Applied in that order: the overlay intentionally wins over whatever
77323
+ * `forwardAwsEnv` copied, and the credentials-file env is additive.
77324
+ *
77325
+ * Extracted from `localInvokeCommand`'s body by issue
77326
+ * [#1836](https://github.com/go-to-k/cdkd/issues/1836) and shaped to mirror
77327
+ * `applyAgentCoreCredentialEnv` in `local-invoke-agentcore.ts` (the sibling
77328
+ * issue [#1814](https://github.com/go-to-k/cdkd/issues/1814) fixed), so a unit
77329
+ * test can lock the STS-client region binding with a mocked STS instead of
77330
+ * driving the full synth + docker pipeline.
77331
+ */
77332
+ async function applyLambdaCredentialEnv(dockerEnv, args) {
77333
+ const logger = getLogger();
77334
+ let assumeSucceeded = false;
77335
+ if (args.assumeRoleArn) {
77336
+ const stsRegion = canonicalizeRegion(args.region ?? process.env["AWS_REGION"] ?? process.env["AWS_DEFAULT_REGION"]);
77337
+ try {
77338
+ const creds = await assumeLambdaExecutionRole(args.assumeRoleArn, stsRegion);
77339
+ dockerEnv["AWS_ACCESS_KEY_ID"] = creds.accessKeyId;
77340
+ dockerEnv["AWS_SECRET_ACCESS_KEY"] = creds.secretAccessKey;
77341
+ dockerEnv["AWS_SESSION_TOKEN"] = creds.sessionToken;
77342
+ if (stsRegion) dockerEnv["AWS_REGION"] = stsRegion;
77343
+ assumeSucceeded = true;
77344
+ } catch (err) {
77345
+ const reason = err instanceof Error ? err.message : String(err);
77346
+ logger.warn(`--assume-role: STS AssumeRole(${args.assumeRoleArn}) failed: ${reason}. Falling back to the developer's shell credentials.`);
77347
+ }
77348
+ }
77349
+ if (!assumeSucceeded) {
77350
+ forwardAwsEnv(dockerEnv);
77351
+ applyProfileCredentialsOverlay(dockerEnv, args.profileCredentials, false);
77352
+ if (args.profileCredsFile) {
77353
+ dockerEnv["AWS_SHARED_CREDENTIALS_FILE"] = args.profileCredsFile.containerPath;
77354
+ dockerEnv["AWS_PROFILE"] = args.profileCredsFile.profileName;
77355
+ }
77356
+ }
77357
+ }
77358
+ /**
77156
77359
  * Assume the Lambda execution role and return temporary credentials.
77157
77360
  *
77158
77361
  * Closes the "developer has admin creds, the deployed function has narrow
@@ -77192,17 +77395,30 @@ async function assumeLambdaExecutionRole(roleArn, region) {
77192
77395
  *
77193
77396
  * Region is inherited from `AWS_REGION` / `AWS_DEFAULT_REGION` so
77194
77397
  * `aws.config.region` inside the handler works without extra setup.
77398
+ *
77399
+ * Issue [#1836](https://github.com/go-to-k/cdkd/issues/1836): the two REGION
77400
+ * entries are folded through `canonicalizeRegion` on the way in, the credentials
77401
+ * are copied verbatim. This is the DEFAULT path — no `--assume-role` — and it
77402
+ * carries the same exposure the assume-role branch had: the value lands in the
77403
+ * container's own `AWS_REGION`, i.e. in every SDK client the handler builds, and
77404
+ * SDK endpoint resolution is case-SENSITIVE (`CN-NORTH-1` resolves the
77405
+ * COMMERCIAL partition). Folding only inside `applyLambdaCredentialEnv`'s
77406
+ * assume-role arm made ONE command yield two different container regions for the
77407
+ * same shell — `cn-north-1` with `--assume-role`, `CN-NORTH-1` without.
77195
77408
  */
77196
77409
  function forwardAwsEnv(env) {
77197
- for (const key of [
77410
+ const passThrough = [
77198
77411
  "AWS_ACCESS_KEY_ID",
77199
77412
  "AWS_SECRET_ACCESS_KEY",
77200
77413
  "AWS_SESSION_TOKEN",
77201
77414
  "AWS_REGION",
77202
77415
  "AWS_DEFAULT_REGION"
77203
- ]) {
77416
+ ];
77417
+ const regionKeys = /* @__PURE__ */ new Set(["AWS_REGION", "AWS_DEFAULT_REGION"]);
77418
+ for (const key of passThrough) {
77204
77419
  const value = process.env[key];
77205
- if (value !== void 0) env[key] = value;
77420
+ if (value === void 0) continue;
77421
+ env[key] = regionKeys.has(key) ? canonicalizeRegion(value) : value;
77206
77422
  }
77207
77423
  }
77208
77424
  /**
@@ -78448,7 +78664,7 @@ function createMigrateCommand() {
78448
78664
  */
78449
78665
  function buildProgram() {
78450
78666
  const program = new Command();
78451
- program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.283.13");
78667
+ program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.283.14");
78452
78668
  program.hook("preAction", (_thisCommand, actionCommand) => {
78453
78669
  const { profile } = actionCommand.optsWithGlobals();
78454
78670
  if (profile !== void 0) process.env["AWS_PROFILE"] = profile;