@go-to-k/cdkd 0.285.13 → 0.285.15

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.
@@ -1,8 +1,8 @@
1
- import { $ as DEFAULT_STATE_PREFIX, $n as resolveUseCdkBootstrapAssets, $t as isSingleDynamicReferenceToken, A as bold, An as validateAssetBucketName, Ar as LocalStartServiceError, At as requireConfigObject, B as secretBearingStateKeyWarning, Bn as runDockerStreaming, Bt as DiffCalculator, C as unsupportedFinalSnapshotError, Cn as BOOTSTRAP_MARKER_PREFIX, Ct as assertRegionMatch, D as isStatefulRecreateTargetSync, Dn as isCrossRegionRedirect, Dr as DynamicReferenceRegionAmbiguousError, Dt as readConfigString, E as MULTI_REGION_RECREATE_BLOCKED_TYPES, En as getBootstrapMarkerKey, Er as DeployCancelledError, Et as configStringRefusal, F as yellow, Fn as dockerSpawnEnvWithSensitive, Fr as ProvisioningError, Ft as s3BucketDomainName, G as clearOnUpdateRemoval, Gn as getDefaultStateBucketName, Gr as withErrorHandling, Gt as TemplateParser, H as getCurrentResourceSecrets, Hn as getDockerImageBySourceHash, Ht as describeTypeWithThrottleRetry, I as collectDeclaredOutputNames, In as formatDockerLoginError, Ir as ResourceTimeoutError, It as s3BucketDualStackDomainName, J as findSilentDropProperties, Jn as resolveAutoAssetStorage, Jr as isThrottlingError, Jt as TEMPLATE_SOURCED_RULES, K as ProviderRegistry, Kn as getLegacyStateBucketName, Kr as isMarkedNonRetryable, Kt as STATE_SOURCED_CROSS_GENERATION_RULES, L as collectPublishedOutputNames, Ln as getDockerCmd, Lr as ResourceUpdateNotSupportedError, Lt as s3BucketRegionalDomainName, M as gray, Mn as buildDenyExternalAccessPolicy, Mr as MissingCdkCliError, Mt as classifyReplaySecretRegion, N as green, Nn as describeAwsFailure, Nr as NestedStackChildDirectDestroyError, Nt as producerRegionsFromState, O as renderStatefulReason, On as parseBootstrapMarker, Or as LocalInvokeBuildError$1, Ot as replayWarn, P as red, Pn as buildDockerImage, Pr as PartialFailureError, Pt as s3BucketArn, Q as CUSTOM_RESOURCE_RESPONSE_PREFIX, Qn as resolveStateBucketWithDefaultAndSource, Qr as retryClassificationText, Qt as errorCauseChain, R as exportAliasCollisionScrubWarning, Rn as partitionSensitiveEnv, Rr as StackHasActiveImportsError, Rt as s3BucketWebsiteUrl, S as refusesFinalSnapshot, Sn as AssetModeResolver, Sr as CdkdError, St as resolveExplicitPhysicalId, T as extractDeploymentEventError, Tn as ensureAssetStorage, Tt as configBooleanRefusal, U as IAMRoleProvider, Un as Synthesizer, Ut as withRetry, V as stateKeySecretExposure, Vn as AssetManifestLoader, Vt as INTRINSIC_KEYS, W as collectInlinePolicyNamesManagedBySiblings, Wn as synthesisStatusMessage, Wr as normalizeAwsError, Wt as DagBuilder, X as maskDeep, Xn as resolveSkipPrefix, Xr as markNonRetryable, Xt as createSecretMasker, Y as createMaskedRetryLogger, Yn as resolveCaptureObservedState, Yr as isTransientServerError, Yt as carriesSecretMask, Z as maskerOrIdentity, Zn as resolveStateBucketWithDefault, Zr as markRedactedCause, Zt as dynamicReferenceTokens, _ as PRE_DELETE_SNAPSHOT_TYPES, _n as createAssetRedirectResolver, _r as AwsClients, _t as isUnboundTemplateParameter, a as DeploymentEventsStore, an as scrubResourceRecord, ar as findLargeInlineResources, at as CloudControlProvider, b as createPreDeleteFinalSnapshot, bn as escapeRegExp, br as setAwsClients, bt as WAFv2WebACLProvider, c as replayFailedOperations, cn as rebuildClientForBucketRegion, cr as displaySafe, ct as deleteIndeterminateGuards, d as updatePartialReason, dn as importableOutputs, dr as canonicalizeRegion, dt as isTerminationProtectionPropagationError, en as maskSecretsInError, er as stateBucketExistenceConfirmed, et as beginCommandInterruptScope, f as withResourceDeadline, fn as shouldRetainResource, fr as derivePartitionAndUrlSuffix, ft as IntrinsicFunctionResolver, g as ATOMIC_FINAL_SNAPSHOT_TYPES, gn as buildAssetRedirectMap, gr as resolveBucketRegion, gt as getAccountInfo, h as computeImplicitDeleteEdges, hn as WorkGraph, ht as coerceParameterTypedValue, i as DeploymentEventsReader, in as redactSecretsForState, ir as MIGRATE_TMP_PREFIX, it as startInterruptWatch, j as cyan, jn as validateContainerRepoName, jt as requireConfigString, k as formatResourceLine, kn as readBootstrapMarkerBody, kr as LocalMigrateError, kt as requireConfigArray, l as replayRollback, ln as exportNamesCarriedFrom, lr as expectedOwnerParam, lt as deleteSkipReason, m as IMPLICIT_DELETE_DEPENDENCIES, mn as stringifyValue, mr as processStackMessages, mt as cfnRefValueFromPhysicalId, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nn as recordMaskOnlyValue, nr as CFN_TEMPLATE_BODY_LIMIT, nt as interruptWatchListenerCount, o as planFailedOps, on as LockManager, or as uploadCfnTemplate, ot as slowCcOperationTimeoutMs, p as maskingRetryLogger, pn as AssetPublisher, pr as AssemblyReader, pt as carriesDynamicReference, q as findActionableSilentDrops, qn as resolveApp, qr as isRetryableTransientError, qt as STATE_SOURCED_READBACK_RULES, r as DeployEngine, rn as recoverMaskedOutput, rr as CFN_TEMPLATE_URL_LIMIT, rt as isInterruptedWaitError, s as planRollback, sn as S3StateBackend, sr as CUSTOM_RESOURCE_RESPONSE_OBJECT_DESCRIPTION, st as UNSPECIFIED_SKIP_REASON, t as DEFAULT_RESOURCE_TIMEOUT_MS, tn as maskSecretsInText, tr as warnDeprecatedNoPrefixCliFlag, tt as endCommandInterruptScope, u as updatePartialMessage, un as importableOutputKeys, ur as PARTITION_TABLE, ut as disableInstanceApiTermination, v as buildFinalSnapshotIdentifier, vn as loadPublishableAssetManifest, vr as getAwsClients, vt as parameterTypeMayLoseSecretIdentity, w as makeCanonicalizePropertiesFn, wn as assertAssetBucketRegion, wr as CrossAccountSecretRefusalError, wt as coerceCfnBoolean, x as isFinalSnapshotError, xn as stripControlChars, xt as normalizeAwsTagsToCfn, y as ccRoutedFinalSnapshotError, yn as rewriteTemplateAssetReferences, yr as resetAwsClients, yt as refStateLookupFromResource, z as isExportAliasCollision, zn as runDockerForeground, zr as StackTerminationProtectionError, zt as applyRoleArnIfSet } from "./deploy-engine-DhMm2M33.js";
1
+ import { $ as endCommandInterruptScope, $n as resolveCaptureObservedState, $r as isThrottlingError, $t as maskSecretsInText, A as bold, An as getBootstrapMarkerKey, Ar as DeployCancelledError, At as classifyReplaySecretRegion, B as secretBearingStateKeyWarning, Bn as formatDockerLoginError, Br as ProvisioningError, Bt as describeTypeWithThrottleRetry, C as unsupportedFinalSnapshotError, Cn as rewriteTemplateAssetReferences, Cr as resetAwsClients, Ct as configBooleanRefusal, D as isStatefulRecreateTargetSync, Dn as BOOTSTRAP_MARKER_PREFIX, Dt as requireConfigArray, E as MULTI_REGION_RECREATE_BLOCKED_TYPES, En as AssetModeResolver, Er as CdkdError, Et as replayWarn, F as yellow, Fn as validateContainerRepoName, Fr as LocalStartServiceError, Ft as s3BucketRegionalDomainName, G as clearOnUpdateRemoval, Gn as AssetManifestLoader, Gt as STATE_SOURCED_READBACK_RULES, H as getCurrentResourceSecrets, Hn as partitionSensitiveEnv, Hr as ResourceUpdateNotSupportedError, Ht as DagBuilder, I as collectDeclaredOutputNames, In as buildDenyExternalAccessPolicy, It as s3BucketWebsiteUrl, J as findSilentDropProperties, Jn as synthesisStatusMessage, Jt as createSecretMasker, K as ProviderRegistry, Kn as getDockerImageBySourceHash, Kt as TEMPLATE_SOURCED_RULES, L as collectPublishedOutputNames, Ln as describeAwsFailure, Lr as MissingCdkCliError, Lt as applyRoleArnIfSet, M as gray, Mn as parseBootstrapMarker, Mr as IntrinsicResolutionRefusalError, Mt as s3BucketArn, N as green, Nn as readBootstrapMarkerBody, Nr as LocalInvokeBuildError$1, Nt as s3BucketDomainName, O as renderStatefulReason, On as assertAssetBucketRegion, Or as CrossAccountSecretRefusalError, Ot as requireConfigObject, P as red, Pn as validateAssetBucketName, Pr as LocalMigrateError, Pt as s3BucketDualStackDomainName, Q as beginCommandInterruptScope, Qn as resolveAutoAssetStorage, Qr as isRetryableTransientError, Qt as maskSecretsInError, R as exportAliasCollisionScrubWarning, Rn as buildDockerImage, Rr as NestedStackChildDirectDestroyError, Rt as DiffCalculator, S as refusesFinalSnapshot, Sn as loadPublishableAssetManifest, Sr as getAwsClients, St as coerceCfnBoolean, T as extractDeploymentEventError, Tn as stripControlChars, Tt as readConfigString, U as IAMRoleProvider, Un as runDockerForeground, Ur as StackHasActiveImportsError, Ut as TemplateParser, V as stateKeySecretExposure, Vn as getDockerCmd, Vr as ResourceTimeoutError, Vt as withRetry, W as collectInlinePolicyNamesManagedBySiblings, Wn as runDockerStreaming, Wr as StackTerminationProtectionError, Wt as STATE_SOURCED_CROSS_GENERATION_RULES, X as maskDeep, Xn as getLegacyStateBucketName, Xr as withErrorHandling, Xt as errorCauseChain, Y as createMaskedRetryLogger, Yn as getDefaultStateBucketName, Yr as normalizeAwsError, Yt as dynamicReferenceTokens, Z as maskerOrIdentity, Zn as resolveApp, Zr as isMarkedNonRetryable, Zt as isSingleDynamicReferenceToken, _ as PRE_DELETE_SNAPSHOT_TYPES, _n as AssetPublisher, _r as AssemblyReader, _t as refStateLookupFromResource, a as DeploymentEventsStore, an as S3StateBackend, ar as warnDeprecatedNoPrefixCliFlag, at as UNSPECIFIED_SKIP_REASON, b as createPreDeleteFinalSnapshot, bn as buildAssetRedirectMap, br as resolveBucketRegion, bt as resolveExplicitPhysicalId, c as replayFailedOperations, cn as buildForceUnlockCommand, cr as MIGRATE_TMP_PREFIX, ct as disableInstanceApiTermination, d as updatePartialReason, dn as CUSTOM_RESOURCE_RESPONSE_PREFIX, dr as CUSTOM_RESOURCE_RESPONSE_OBJECT_DESCRIPTION, dt as carriesDynamicReference, ei as isTransientServerError, en as recordMaskOnlyValue, er as resolveSkipPrefix, et as interruptWatchListenerCount, f as withResourceDeadline, fn as DEFAULT_STATE_PREFIX, fr as displaySafe, ft as cfnRefValueFromPhysicalId, g as ATOMIC_FINAL_SNAPSHOT_TYPES, gn as shouldRetainResource, gr as derivePartitionAndUrlSuffix, gt as parameterTypeMayLoseSecretIdentity, h as computeImplicitDeleteEdges, hn as importableOutputs, hr as canonicalizeRegion, ht as isUnboundTemplateParameter, i as DeploymentEventsReader, in as LockManager, ir as stateBucketExistenceConfirmed, it as slowCcOperationTimeoutMs, j as cyan, jn as isCrossRegionRedirect, jr as DynamicReferenceRegionAmbiguousError, jt as producerRegionsFromState, k as formatResourceLine, kn as ensureAssetStorage, kt as requireConfigString, l as replayRollback, ln as buildLockContentionMessage, lr as findLargeInlineResources, lt as isTerminationProtectionPropagationError, m as IMPLICIT_DELETE_DEPENDENCIES, mn as importableOutputKeys, mr as PARTITION_TABLE, mt as getAccountInfo, n as DEFAULT_RESOURCE_WARN_AFTER_MS, ni as markRedactedCause, nn as redactSecretsForState, nr as resolveStateBucketWithDefaultAndSource, nt as startInterruptWatch, o as planFailedOps, on as rebuildClientForBucketRegion, or as CFN_TEMPLATE_BODY_LIMIT, ot as deleteIndeterminateGuards, p as maskingRetryLogger, pn as exportNamesCarriedFrom, pr as expectedOwnerParam, pt as coerceParameterTypedValue, q as findActionableSilentDrops, qn as Synthesizer, qt as carriesSecretMask, r as DeployEngine, ri as retryClassificationText, rn as scrubResourceRecord, rr as resolveUseCdkBootstrapAssets, rt as CloudControlProvider, s as planRollback, sn as UNRENDERABLE, sr as CFN_TEMPLATE_URL_LIMIT, st as deleteSkipReason, t as DEFAULT_RESOURCE_TIMEOUT_MS, ti as markNonRetryable, tn as recoverMaskedOutput, tr as resolveStateBucketWithDefault, tt as isInterruptedWaitError, u as updatePartialMessage, un as forceQuitRecoveryClause, ur as uploadCfnTemplate, ut as IntrinsicFunctionResolver, v as buildFinalSnapshotIdentifier, vn as stringifyValue, vr as processStackMessages, vt as WAFv2WebACLProvider, w as makeCanonicalizePropertiesFn, wn as escapeRegExp, wr as setAwsClients, wt as configStringRefusal, x as isFinalSnapshotError, xn as createAssetRedirectResolver, xr as AwsClients, xt as assertRegionMatch, y as ccRoutedFinalSnapshotError, yn as WorkGraph, yt as normalizeAwsTagsToCfn, z as isExportAliasCollision, zn as dockerSpawnEnvWithSensitive, zr as PartialFailureError, zt as INTRINSIC_KEYS } from "./deploy-engine-DPh-XamZ.js";
2
2
  import { _ as withStackName, c as PATTERN_B_NAME_OPTIONS, f as generateResourceName, g as withSkipPrefix, i as reserveStdoutForPayload, l as PATTERN_B_NAME_PROPERTIES, m as getCurrentStackName, n as getLogger, o as runStackBuffered, p as generateResourceNameWithFallback, s as getLiveRenderer, u as PATTERN_B_RESOURCE_TYPES } from "./logger-Dw-3y48G.js";
3
3
  import { t as awsClientDefaults } from "./aws-client-defaults-D-iYiIJ6.js";
4
- import { t as getCdkdVersion } from "./version-FLW4rE5A.js";
5
- import { t as ASGProvider } from "./asg-provider-DcFgnQQO.js";
4
+ import { t as getCdkdVersion } from "./version-Dcdouqd3.js";
5
+ import { t as ASGProvider } from "./asg-provider-CKEf903K.js";
6
6
  import { AsyncLocalStorage } from "node:async_hooks";
7
7
  import { createHash, randomBytes, randomUUID } from "node:crypto";
8
8
  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";
@@ -38,7 +38,7 @@ import { join as join$1 } from "path";
38
38
  import { BatchDeleteImageCommand, CreateRepositoryCommand, DeleteLifecyclePolicyCommand, DeleteRepositoryCommand, DeleteRepositoryPolicyCommand, DescribeImagesCommand as DescribeImagesCommand$1, DescribeRepositoriesCommand, ECRClient, GetAuthorizationTokenCommand, GetLifecyclePolicyCommand, LifecyclePolicyNotFoundException, ListTagsForResourceCommand as ListTagsForResourceCommand$9, PutImageScanningConfigurationCommand, PutImageTagMutabilityCommand, PutLifecyclePolicyCommand, RepositoryNotEmptyException, RepositoryNotFoundException, SetRepositoryPolicyCommand, TagResourceCommand as TagResourceCommand$12, UntagResourceCommand as UntagResourceCommand$12 } from "@aws-sdk/client-ecr";
39
39
  import graphlib from "graphlib";
40
40
  import { AddTagsToResourceCommand as AddTagsToResourceCommand$2, CreateDBClusterCommand, CreateDBInstanceCommand, CreateDBProxyCommand, CreateDBProxyEndpointCommand, CreateDBSubnetGroupCommand, DBProxyEndpointNotFoundFault, DBProxyNotFoundFault, DBProxyTargetGroupNotFoundFault, DBProxyTargetNotFoundFault, DeleteDBClusterCommand, DeleteDBInstanceCommand, DeleteDBProxyCommand, DeleteDBProxyEndpointCommand, DeleteDBSubnetGroupCommand, DeregisterDBProxyTargetsCommand, DescribeDBClustersCommand, DescribeDBInstancesCommand, DescribeDBProxiesCommand, DescribeDBProxyEndpointsCommand, DescribeDBProxyTargetGroupsCommand, DescribeDBProxyTargetsCommand, DescribeDBSubnetGroupsCommand, ListTagsForResourceCommand as ListTagsForResourceCommand$10, ModifyDBClusterCommand, ModifyDBInstanceCommand, ModifyDBProxyCommand, ModifyDBProxyEndpointCommand, ModifyDBProxyTargetGroupCommand, ModifyDBSubnetGroupCommand, RDSClient, RegisterDBProxyTargetsCommand, RemoveTagsFromResourceCommand as RemoveTagsFromResourceCommand$2 } from "@aws-sdk/client-rds";
41
- import { Command, Option } from "commander";
41
+ import { Command, InvalidArgumentError, Option } from "commander";
42
42
  import * as readline$1 from "node:readline/promises";
43
43
  import readline from "node:readline/promises";
44
44
  import { writeFileSync as writeFileSync$1 } from "fs";
@@ -324,6 +324,50 @@ function parseDuration(value) {
324
324
  return Math.round(num * (unit === "s" ? 1e3 : unit === "m" ? 6e4 : 36e5));
325
325
  }
326
326
  /**
327
+ * Commander `argParser` for `--stack-region` (issue #2556).
328
+ *
329
+ * Refuses a value carrying no region name at all, at PARSE time rather than
330
+ * in each consumer. `--stack-region ''` is falsy, so every consumer that
331
+ * tested the option for truthiness read it as ABSENT — and absent means "all
332
+ * regions" on the commands that take `--all`, so a flag passed to NARROW a
333
+ * destructive operation silently WIDENED it. `drift --all --revert
334
+ * --stack-region ''` wrote cdkd state values into AWS across every stack in
335
+ * every region.
336
+ *
337
+ * Here rather than in the consumers because there are more consumers than
338
+ * declarations, the value is threaded through helpers that cannot see how it
339
+ * was spelled, and the empty value has no legitimate meaning — so it never
340
+ * needs to reach them. `guardStackRegionOptions` extends the same guard to
341
+ * declarations cdkd does not own.
342
+ *
343
+ * The rule is only "this is not a name": empty and whitespace-only are
344
+ * refused, and everything else is passed through UNCHANGED — cdkd keeps no
345
+ * list of region names, so a value that looks wrong is the caller's to own,
346
+ * and trimming one would be the silent reinterpretation this whole class
347
+ * started as.
348
+ */
349
+ function parseStackRegion(value) {
350
+ if (value.trim() === "") throw new InvalidArgumentError(`expected a region name (e.g. us-east-1), got ${value === "" ? "an empty value" : "whitespace only"}. Omit the flag entirely to operate on every region a stack has state in.`);
351
+ return value;
352
+ }
353
+ /**
354
+ * Attach {@link parseStackRegion} to every `--stack-region` the command tree
355
+ * carries, including ones cdkd never declared.
356
+ *
357
+ * `cdkd local start-{service,alb,cloudfront,agentcore}` inherit the option
358
+ * from cdk-local, so there is no declaration in this repo to hang a parser on
359
+ * and a grep for the flag string does not find them. They are the worst place
360
+ * to miss it: cdk-local resolves the CloudFormation client's region with
361
+ * `options.stackRegion ?? options.region ?? AWS_REGION`, and `''` is not
362
+ * nullish — so an empty value WINS over both fallbacks and reaches the client.
363
+ *
364
+ * Idempotent, and it never replaces a parser a declaration already set.
365
+ */
366
+ function guardStackRegionOptions(cmd) {
367
+ for (const option of cmd.options) if (option.long === "--stack-region" && option.parseArg === void 0) option.argParser(parseStackRegion);
368
+ for (const sub of cmd.commands) guardStackRegionOptions(sub);
369
+ }
370
+ /**
327
371
  * Validate that a token's left-hand side looks like a CloudFormation
328
372
  * resource type (e.g. `AWS::S3::Bucket`). The check is intentionally
329
373
  * loose — we don't maintain a closed list of types — but it does reject
@@ -703,21 +747,39 @@ function parseRecreateViaSdkProviderToken(value, previous) {
703
747
  }
704
748
  const recreateViaSdkProviderOption = new Option("--recreate-via-sdk-provider <logicalId>", "Destroy + recreate the named resource (by CloudFormation logical id) via cdkd's SDK Provider in this deploy, so a resource currently sticky on provisionedBy: cc-api flips back to provisionedBy: sdk. Used after a #609 backfill release adds SDK coverage for a type the user originally needed CC for (e.g. Lambda LoggingConfig). Repeatable — pass the flag once per resource. Per-resource opt-in. Stateful resource types refuse unless --force-stateful-recreation is ALSO passed.").argParser(parseRecreateViaSdkProviderToken);
705
749
  /**
706
- * Issue [#615] — `--force-stateful-recreation` (boolean) is the second
707
- * flag required to allow `--recreate-via-cc-api` to operate on a
708
- * stateful resource (RDS / DynamoDB / EFS / S3 with data / etc.). Two
709
- * flags so the user explicitly opts into the data-loss footgun.
750
+ * Issue [#615] — `--force-stateful-recreation` (boolean) confirms a
751
+ * data-losing destroy + recreate of a stateful resource (RDS / DynamoDB /
752
+ * EFS / S3 with data / etc.).
753
+ *
754
+ * It started as the second flag `--recreate-via-cc-api` needs, and that
755
+ * two-flag shape still holds for the `--recreate-via-*` / `--replace`
756
+ * opt-ins. But the guard now runs on the mid-deploy replacement paths
757
+ * too — not only the flag-gated ones, and with its own exemptions (a
758
+ * property-driven replacement under `UpdateReplacePolicy: Retain` creates
759
+ * first, so it is skipped there) (see
760
+ * `docs/cli-deploy-safety.md#stateful-resource-guard`), and some of them
761
+ * reach a replacement with NO flag at all: a property-driven replacement
762
+ * (an immutable / createOnly property changed in the template) and the
763
+ * update-failure fallback's Cloud Control trigger (issue [#2514]). On
764
+ * those, a plain `cdkd deploy` asks for this flag on its own — so the
765
+ * help text must not describe it as scoped to `--recreate-via-cc-api`
766
+ * targets.
710
767
  *
711
768
  * The guard list of "stateful" types lives in
712
769
  * `src/provisioning/stateful-types.ts` so it can be queried from both
713
- * the CLI pre-flight and the deploy engine.
770
+ * the CLI pre-flight and the deploy engine, and it is published — as the
771
+ * only enumeration users read — in `docs/cli-deploy-safety.md`, which a
772
+ * set-equality fence keeps in step with the source. Prose elsewhere
773
+ * (this help string included) names EXAMPLES and points there; it must
774
+ * not read as exhaustive, because a widened guard would silently make it
775
+ * wrong.
714
776
  *
715
777
  * There is no per-resource granularity on the force flag — when set,
716
778
  * EVERY named recreate target bypasses the stateful guard. Per-resource
717
779
  * force would create a false sense of granularity (the user is opting
718
780
  * into a footgun; pretending to scope it per-resource is misleading).
719
781
  */
720
- const forceStatefulRecreationOption = new Option("--force-stateful-recreation", "Bypass the stateful-resource guard for --recreate-via-cc-api targets. Required when ANY named target is a stateful type (RDS / DynamoDB / EFS / FSx / EBS Volume / S3 with data / Logs with retention / Cognito / Secrets / SSM / Glue / ECR / CloudFront / Kinesis / OpenSearch). Destroy + recreate loses ALL data in the resource — no automatic data migration. Triple-opt-in for CI use: --recreate-via-cc-api <id> --force-stateful-recreation --yes.").default(false);
782
+ const forceStatefulRecreationOption = new Option("--force-stateful-recreation", "Confirm a data-losing destroy + recreate of a stateful resource. Required whenever a replacement target is a stateful type databases, filesystems, KMS keys, table / vector storage, source repositories, log groups with retention, and more; docs/cli-deploy-safety.md carries the full list, and S3 is the special case (a non-empty bucket at pre-flight, ANY bucket mid-deploy, where the emptiness probe cannot run) — on the --recreate-via-cc-api / --recreate-via-sdk-provider pre-flight, with --replace, AND on the paths a plain deploy reaches with no flag: a template immutable-property change, and an in-place update the provisioning layer rejects. Destroy + recreate loses ALL data in the resource — no automatic data migration. Full opt-in for CI use: --recreate-via-cc-api <id> --force-stateful-recreation --yes.").default(false);
721
783
  const replaceOption = new Option("--replace", "Replace (DELETE + CREATE) a resource whose in-place update is rejected because an immutable property changed and AWS exposes no update API for it (e.g. Lambda LayerVersion content, EFS AccessPoint, ECS TaskDefinition, ApiGatewayV2 immutable fields). Without this flag such a change fails the deploy. Stateful types (RDS / DynamoDB / EFS / S3 with data / Logs with retention / etc.) ALSO require --force-stateful-recreation since the replacement is a data-losing DELETE + CREATE.").default(false);
722
784
  /**
723
785
  * Issue #1002 PR 2 — per-invocation legacy pin for asset destinations
@@ -2437,7 +2499,7 @@ function isPromptAbortError(error) {
2437
2499
  * bucket with objects, LogGroup with retention) MUST be matched
2438
2500
  * by an explicit `--force-stateful-recreation` flag. The sync
2439
2501
  * first-cut runs from the recorded properties alone; the live
2440
- * `s3:ListObjectsV2` probe (issue [#648]) promotes a `null`
2502
+ * `s3:ListObjectVersions` probe (issue [#648]) promotes a `null`
2441
2503
  * reason to `'has-objects'` when a bucket actually contains data.
2442
2504
  * 4. Multi-region refusal: every named target whose resource type
2443
2505
  * is in {@link MULTI_REGION_RECREATE_BLOCKED_TYPES} (e.g.
@@ -2456,7 +2518,7 @@ function isPromptAbortError(error) {
2456
2518
  *
2457
2519
  * Pure with respect to AWS — does NOT probe S3 bucket emptiness. Wrap
2458
2520
  * the result with {@link probeAndRevalidateStateful} to promote S3
2459
- * targets' `statefulReason` via a live `s3:ListObjectsV2` round-trip
2521
+ * targets' `statefulReason` via a live `s3:ListObjectVersions` round-trip
2460
2522
  * before rendering errors. The deploy command does this; the validator
2461
2523
  * itself stays sync so unit tests don't need an S3 mock.
2462
2524
  *
@@ -2792,7 +2854,7 @@ function renderDownstreamConsumers(producerStack, consumers) {
2792
2854
  *
2793
2855
  * The per-target plan surfaces a **DATA LOSS** prefix for stateful
2794
2856
  * targets (those with a non-null `statefulReason` after the live
2795
- * `s3:ListObjectsV2` probe — see issue [#648]); these reached pre-flight
2857
+ * `s3:ListObjectVersions` probe — see issue [#648]); these reached pre-flight
2796
2858
  * only because the user opted in with `--force-stateful-recreation`,
2797
2859
  * so the prompt's **DATA LOSS** wording is the third "stop and think"
2798
2860
  * moment.
@@ -9590,7 +9652,7 @@ var S3BucketProvider = class S3BucketProvider {
9590
9652
  * `ServerSideEncryptionConfigurationNotFoundError`, `NoSuchTagSet`,
9591
9653
  * `NoSuchPublicAccessBlockConfiguration`, etc.) — those are caught
9592
9654
  * individually and the corresponding key is emitted as a CFn-shape
9593
- * placeholder (per docs/provider-development.md § 3b: always-emit
9655
+ * placeholder (per docs/provider-rules.md#readcurrentstate-for-drift-detection: always-emit
9594
9656
  * user-controllable top-level keys), NOT treated as the bucket being
9595
9657
  * absent.
9596
9658
  *
@@ -32319,7 +32381,7 @@ var CloudFrontOACProvider = class {
32319
32381
  * identical, so the reverse mapping is a straight per-field copy.
32320
32382
  *
32321
32383
  * Every field is emitted unconditionally, with `?? ''` standing in for an
32322
- * AWS response that omits it (docs/provider-development.md § 3b): the
32384
+ * AWS response that omits it (docs/provider-rules.md#readcurrentstate-for-drift-detection): the
32323
32385
  * optional `Description` is user-controllable and mutable, so dropping the
32324
32386
  * key when AWS returns nothing would leave it out of `observedProperties`
32325
32387
  * on a stack that never templated it — and the drift comparator's
@@ -47566,12 +47628,13 @@ var GlueProvider = class {
47566
47628
  * | no | no | AWS-AUTHORED -> preserved from live |
47567
47629
  *
47568
47630
  * That keeps this provider on the repo's established clear-on-removal
47569
- * position (docs/provider-development.md §2a, issue #1155): a removal the
47631
+ * position (docs/provider-rules.md#update-removal-semantics-clear-on-removal, issue #1155): a removal the
47570
47632
  * user expressed in the template still reaches AWS. Glue's update APIs are
47571
47633
  * full-replace, so removal needs no explicit reset sentinel — omitting the
47572
47634
  * key IS the reset, and the only thing this helper adds back is the set of
47573
47635
  * keys the user never authored in the first place. The generalized rule for
47574
- * any full-replace update API lives in docs/provider-development.md §2b.
47636
+ * any full-replace update API lives in
47637
+ * docs/provider-rules.md#full-replace-update-apis-erase-aws-authored-values.
47575
47638
  *
47576
47639
  * Audited siblings that do NOT get this treatment: `JobUpdate`'s
47577
47640
  * `DefaultArguments` / `NonOverridableArguments`, `ConnectionInput`'s
@@ -53664,7 +53727,7 @@ var FSxFileSystemProvider = class {
53664
53727
  * by their respective `read*Configuration` helpers; the few inputs AWS
53665
53728
  * never returns stay listed in {@link getDriftUnknownPaths}.
53666
53729
  *
53667
- * Emission follows `docs/provider-development.md` §3b: every top-level
53730
+ * Emission follows `docs/provider-rules.md#readcurrentstate-for-drift-detection`: every top-level
53668
53731
  * property `update()` can mutate is emitted unconditionally with a
53669
53732
  * placeholder, registry-createOnly properties keep their guard, and the
53670
53733
  * `<Variant>Configuration` blocks take the Class 1 type-discriminator
@@ -57806,7 +57869,7 @@ var CodeCommitRepositoryProvider = class {
57806
57869
  *
57807
57870
  * Every user-controllable top-level key `update()` can mutate is emitted
57808
57871
  * ALWAYS — with a `?? ''` / `?? []` placeholder when AWS returns the field
57809
- * as undefined / empty (docs/provider-development.md §3b). Omitting the key
57872
+ * as undefined / empty (docs/provider-rules.md#readcurrentstate-for-drift-detection). Omitting the key
57810
57873
  * on the empty path would let a resource deployed WITHOUT a description
57811
57874
  * never carry `RepositoryDescription` in `observedProperties`, making a
57812
57875
  * console-side ADD of a description invisible to drift forever. `Tags` are
@@ -60122,104 +60185,6 @@ var ECRProvider = class {
60122
60185
  }
60123
60186
  };
60124
60187
 
60125
- //#endregion
60126
- //#region src/state/lock-contention-message.ts
60127
- /**
60128
- * The message every fail-fast lock-contention site raises.
60129
- *
60130
- * Issue #2161 made six commands throw on `acquireLock` returning `false`
60131
- * instead of running under a foreign lock. Issue #2170 is the follow-up: the
60132
- * message those sites raise asked the user to decide "is another process
60133
- * active?" while printing none of the evidence that would answer it, and the
60134
- * recovery command it suggested could resolve against the wrong AWS account.
60135
- *
60136
- * Both matter more than they look, because of WHICH locks reach this message.
60137
- * `LockManager.acquireLock` reaps an EXPIRED foreign lock and retries, so a
60138
- * `false` return means the lock is LIVE — in practice a running `cdkd deploy`.
60139
- * The user who follows a bare `cdkd force-unlock <stack>` suggestion therefore
60140
- * deletes a live owner's lock and reproduces issue #2161's harm by hand, which
60141
- * is the outcome #2161 exists to prevent.
60142
- *
60143
- * Centralising the text here also settles the third finding: the nine sites
60144
- * had drifted to three spellings (`for stack` / `for nested stack` /
60145
- * `for nested-stack child`), so a user grepping CI logs for one of them found
60146
- * two of three. `subject` now varies only the noun.
60147
- */
60148
- /**
60149
- * Stand-in for a value with nothing renderable left after sanitization. Named
60150
- * rather than inlined so the message and the command-suppression branch cannot
60151
- * disagree about what "unrenderable" looks like.
60152
- */
60153
- const UNRENDERABLE = "<unrenderable>";
60154
- /** Render `expiresIn` without implying more precision than a clock skew allows. */
60155
- function formatRemaining(ms) {
60156
- if (!Number.isFinite(ms)) return "at an unknown time";
60157
- if (ms <= 0) return "already expired";
60158
- const minutes = Math.round(ms / 6e4);
60159
- if (minutes < 1) return "in under a minute";
60160
- return `in ~${minutes}m`;
60161
- }
60162
- function shellQuote(value) {
60163
- return /^[A-Za-z0-9._/@:+-]+$/.test(value) ? value : `'${value.replace(/'/g, `'\\''`)}'`;
60164
- }
60165
- /**
60166
- * The `cdkd force-unlock ...` line, carrying every flag that decides which
60167
- * lock object it resolves to.
60168
- */
60169
- function buildForceUnlockCommand(stackName, region, recovery) {
60170
- const safeStack = displaySafe(stackName, { asciiOnly: true });
60171
- const stackIsExact = safeStack === stackName;
60172
- const safeRegion = region === void 0 ? void 0 : displaySafe(region, { asciiOnly: true });
60173
- if (!safeStack || safeRegion === "" || !stackIsExact || !(region === void 0 || safeRegion === region)) return "";
60174
- const parts = [safeRegion === void 0 ? `cdkd force-unlock ${shellQuote(safeStack)}` : `cdkd force-unlock ${shellQuote(safeStack)} --stack-region ${shellQuote(safeRegion)}`];
60175
- if (recovery?.profile) parts.push(`--profile ${shellQuote(recovery.profile)}`);
60176
- if (recovery?.stateBucket) parts.push(`--state-bucket ${shellQuote(recovery.stateBucket)}`);
60177
- if (recovery?.statePrefix && recovery.statePrefix !== "cdkd") parts.push(`--state-prefix ${shellQuote(recovery.statePrefix)}`);
60178
- return parts.join(" ");
60179
- }
60180
- /**
60181
- * The force-quit banner's recovery sentence.
60182
- *
60183
- * Exported so the two `destroy-runner.ts` banners do not each decide what to
60184
- * say when {@link buildForceUnlockCommand} suppresses — a banner ending in a
60185
- * bare `run: ` is the shape the review found, and two copies of the branch is
60186
- * how the next one drifts. Returns a leading-space clause so the caller can
60187
- * concatenate it unconditionally.
60188
- */
60189
- function forceQuitRecoveryClause(stackName, region, recovery) {
60190
- const command = buildForceUnlockCommand(stackName, region, recovery);
60191
- return command ? ` If the next run reports a lock, run: ${command}` : " Inspect the lock object directly: the name or region recorded for this stack cannot be reproduced safely on a command line, so any command shown here would address a different lock.";
60192
- }
60193
- /**
60194
- * Build the contention message, reading the holder's identity best-effort.
60195
- *
60196
- * The `getLockInfo` read is one GetObject and is deliberately NOT allowed to
60197
- * fail the command: the caller is already on its way to throwing, and turning
60198
- * a contention refusal into an S3 error would lose the reason. A failed or
60199
- * absent read degrades to the evidence-free wording rather than to a crash.
60200
- */
60201
- async function buildLockContentionMessage(args) {
60202
- const { lockManager, stackName, region, subject = "stack", recovery, heldClause, suffix } = args;
60203
- let held = heldClause ?? "another cdkd process holds it";
60204
- let sawHolder = false;
60205
- try {
60206
- const info = await lockManager.getLockInfo(stackName, region);
60207
- if (info) {
60208
- const operation = info.operation ? `, operation: ${displaySafe(info.operation)}` : "";
60209
- const expires = formatRemaining(info.expiresAt - Date.now());
60210
- const owner = displaySafe(info.owner);
60211
- const holder = owner ? `held by ${owner}${operation}` : `held by an unnamed holder`;
60212
- held = `${heldClause ? `${heldClause} — ` : ""}${holder}, expires ${expires}`;
60213
- if (owner) sawHolder = true;
60214
- }
60215
- } catch {}
60216
- const advice = sawHolder ? `That process is still running — wait for it to finish. Only if you are certain it is gone` : `Wait for it to finish, or if you are certain no other process is active`;
60217
- const recoveryCommand = buildForceUnlockCommand(stackName, region, recovery);
60218
- const head = `Could not acquire lock for ${subject} '${displaySafe(stackName, { asciiOnly: true }) || "<unrenderable>"}' (${displaySafe(region, { asciiOnly: true }) || "<unrenderable>"}) — ${held}.` + (suffix ? ` ${suffix}` : "");
60219
- if (!recoveryCommand) return `${head} ${advice}. No recovery command can be shown: the name or region recorded for this lock contains characters that cannot be reproduced safely on a command line, so any command shown here would address a different lock — inspect the lock object directly.`;
60220
- return `${head} ${advice}, run: ${recoveryCommand}`;
60221
- }
60222
-
60223
60188
  //#endregion
60224
60189
  //#region src/cli/commands/destroy-runner.ts
60225
60190
  /**
@@ -65391,15 +65356,14 @@ const DYNAMIC_REFERENCE_OPENING = "{{resolve:";
65391
65356
  * unactionable for prose. Requiring the service makes the count a count of
65392
65357
  * SECRET references rather than of the characters `{{resolve:`.
65393
65358
  *
65394
- * `secretsmanager` / `ssm` mirror `REPLAY_SECRET_SERVICES` in
65395
- * `rollback-executor.ts`, whose {@link classifyReplaySecretRegion} is what
65396
- * classifies the tokens below — the same set on both sides, so the count and
65397
- * the classification never disagree about what is in scope. `ssm-secure` is
65398
- * added on top: it IS a CloudFormation secret spelling, and although cdkd does
65399
- * not resolve it today (the resolver's unsupported-service arm leaves it
65400
- * verbatim, so it can never become a wrong-region needle), counting it costs
65401
- * only an assembled `ssm-secure` reference in a cross-region stack and keeps
65402
- * the guard correct the day that arm changes.
65359
+ * All three mirror `REPLAY_SECRET_SERVICES` in
65360
+ * `secret-region-classification.ts`, whose {@link classifyReplaySecretRegion}
65361
+ * is what classifies the tokens below — the same set on both sides, so the
65362
+ * count and the classification never disagree about what is in scope. Until
65363
+ * issue #2482 `ssm-secure` was counted here AHEAD of that set (cdkd did not
65364
+ * resolve it, so it could never become a wrong-region needle, and counting it
65365
+ * kept the guard correct for the day the resolver's arm changed); that day
65366
+ * came, and the two lists agree again.
65403
65367
  *
65404
65368
  * THE TRADE, stated rather than hidden: an `Fn::Join` that splits BEFORE the
65405
65369
  * service name (`['{{resolve:', 'secretsmanager:db:SecretString:pw}}']`) has no
@@ -67751,9 +67715,9 @@ var DriftDetectedError = class DriftDetectedError extends CdkdError {
67751
67715
  *
67752
67716
  * SCOPED TO THE CLEARABLE CAUSES, not to everything the report calls
67753
67717
  * `notCompared`. The roll-up also contains resources whose only problem is a
67754
- * surviving `{{resolve:ssm-secure:...}}` token — a pre-existing population cdkd resolves
67755
- * for nobody, which can never clear on a re-run, and which this change did not
67756
- * create. Exiting non-zero for them would break `cdkd drift` in CI forever over
67718
+ * surviving `{{resolve:...}}` token cdkd resolves for nobody — a look-alike
67719
+ * spelling, since issue #2482 moved `ssm-secure` onto the `ssm` path which
67720
+ * can never clear on a re-run, and which this change did not create. Exiting non-zero for them would break `cdkd drift` in CI forever over
67757
67721
  * an unrelated defect, so the exit reads `refused` and `readFailed` while the
67758
67722
  * report covers all three. See `NotComparedCause` and `outcomeExitSignal`.
67759
67723
  *
@@ -67787,12 +67751,15 @@ var DriftDetectedError = class DriftDetectedError extends CdkdError {
67787
67751
  * honest SENTENCE, since their no-drift line claimed a clean bill of health for
67788
67752
  * a stack they had not read. See {@link incompleteRemediationMessage}.
67789
67753
  *
67790
- * WHAT DOES NOT TRIGGER IT: a stack whose properties reference
67791
- * `{{resolve:ssm-secure:...}}` still exits 0. Those properties genuinely are not
67792
- * compared and the report says so, but nothing REFUSED them — cdkd has never
67793
- * resolved that spelling — so the condition is permanent, unclearable by any
67794
- * action the user can take, and predates this change. It is reported as
67795
- * information and kept out of the exit code.
67754
+ * WHAT DOES NOT TRIGGER IT: a stack whose properties carry a `{{resolve:...}}`
67755
+ * spelling cdkd resolves for nobody still exits 0. Those properties genuinely
67756
+ * are not compared and the report says so, but nothing REFUSED them — cdkd has
67757
+ * never resolved that spelling — so the condition is permanent, unclearable by
67758
+ * any action the user can take, and predates this change. It is reported as
67759
+ * information and kept out of the exit code. (`ssm-secure` was that spelling
67760
+ * when this was written; since issue #2482 it resolves like `ssm`, and a
67761
+ * lookup that fails for it lands with the other resolution failures, not
67762
+ * here.)
67796
67763
  *
67797
67764
  * Carries no message of its own for the same reason {@link DriftDetectedError}
67798
67765
  * does -- the report was already printed.
@@ -67863,8 +67830,7 @@ function notComparedOutcomes(report) {
67863
67830
  * code covers only the CLEARABLE ones — `refused` (the population #2108 created)
67864
67831
  * and `readFailed` (issues #2151 / #1945). Exiting non-zero on
67865
67832
  * `unresolvedToken` would break `cdkd drift` in CI forever for every stack
67866
- * holding an `{{resolve:ssm-secure:...}}` reference — permanent, unclearable,
67867
- * and unrelated. See {@link NotComparedCause}.
67833
+ * holding such a spelling — permanent, unclearable, and unrelated. See {@link NotComparedCause}.
67868
67834
  *
67869
67835
  * A `drifted` outcome reports `drifted` even when its own comparison was
67870
67836
  * refused: drift is the stronger, actionable signal and the caller ranks it
@@ -68000,7 +67966,7 @@ function uncomparedTally(reports) {
68000
67966
  *
68001
67967
  * - The TRIGGER is `anyIncomplete`, i.e. {@link outcomeExitSignal}'s
68002
67968
  * `incomplete`. It is narrow on purpose: a stack whose ONLY uncompared
68003
- * resource holds a `{{resolve:ssm-secure:...}}` token must not start
67969
+ * resource holds a `{{resolve:...}}` token cdkd resolves for nobody must not start
68004
67970
  * shouting on every run about a comparison no action of the user's can ever
68005
67971
  * complete -- the same CI-forever hazard that cause is kept out of the exit
68006
67972
  * code on.
@@ -68285,25 +68251,6 @@ function carriesRecordedSecret(value, secrets) {
68285
68251
  return false;
68286
68252
  }
68287
68253
  /**
68288
- * Is this unresolvable token one whose value is a SECRET by definition?
68289
- *
68290
- * `ssm-secure` is the only such spelling, and it is decidable with no lookup:
68291
- * CloudFormation defines exactly three dynamic-reference services (`ssm`,
68292
- * `ssm-secure`, `secretsmanager`), cdkd resolves the other two, and
68293
- * `ssm-secure` is the encrypted one.
68294
- *
68295
- * Anything else reaching the survivor arm is not a CloudFormation dynamic
68296
- * reference at all — it is text that merely looks like one — and treating it as
68297
- * secret is not the safe direction it appears to be: the path would be masked
68298
- * to `***`, refused by `--accept`, and pinned to the live value by
68299
- * {@link preserveLiveValuesAtUnresolvedTokens}, which is permanently stuck
68300
- * drift with no remedy the user can apply. Such a token is still REPORTED; it
68301
- * just does not claim to be a secret.
68302
- */
68303
- function isSecretBySpelling(token) {
68304
- return token.startsWith("{{resolve:ssm-secure:");
68305
- }
68306
- /**
68307
68254
  * Every `{{resolve:...}}` token still present in `value`.
68308
68255
  *
68309
68256
  * Used to NAME an unresolvable reference without quoting the string it sits in.
@@ -68456,10 +68403,13 @@ var DriftSecretRefusalError = class DriftSecretRefusalError extends CdkdError {
68456
68403
  /**
68457
68404
  * Whether an error thrown out of `resolveStateSecretExpressions` is a
68458
68405
  * deliberate REFUSAL rather than a failed read. See
68459
- * {@link DriftSecretRefusalError}.
68406
+ * {@link DriftSecretRefusalError} — drift's own region refusal — and
68407
+ * `IntrinsicResolutionRefusalError`, the resolver's: since issue #2482 the
68408
+ * `ssm-secure` arm refuses a definitive `String` / `StringList` parameter with
68409
+ * the latter, and that is a decision, not an IAM problem to go hunting for.
68460
68410
  */
68461
68411
  function isDriftSecretRefusal(err) {
68462
- return err instanceof DriftSecretRefusalError;
68412
+ return err instanceof DriftSecretRefusalError || err instanceof IntrinsicResolutionRefusalError;
68463
68413
  }
68464
68414
  /**
68465
68415
  * One-line human phrasing for a {@link NotComparedCause}, used per entry in the
@@ -68638,17 +68588,19 @@ async function resolveDriftLeafByRegion(leaf, propertyPath, logicalId, consumerR
68638
68588
  *
68639
68589
  * A `{{resolve:...}}` token that SURVIVES the pass is reported through
68640
68590
  * `onUnresolved` and left in place — it is not an error. The resolver's
68641
- * unsupported-service arm (`ssm-secure:` is the live example) warns and returns
68642
- * the literal, and `cdkd deploy` resolves through the very same code, so AWS is
68643
- * ALREADY holding that literal string and state records it. Replaying it is
68644
- * therefore a correct no-op, and failing the resource over it would take every
68645
- * other drifted property on that resource down with it (plus exit 2). The
68646
- * report exists so the user learns cdkd is shipping a token it cannot resolve,
68647
- * which is a real thing to know and not a reason to stop.
68591
+ * unsupported-service arm warns and returns the literal (`ssm-secure:` was the
68592
+ * live example until issue #2482 gave it an arm; what reaches it now is text
68593
+ * that merely looks like a reference), and `cdkd deploy` resolves through the
68594
+ * very same code, so AWS is ALREADY holding that literal string and state
68595
+ * records it. Replaying it is therefore a correct no-op, and failing the
68596
+ * resource over it would take every other drifted property on that resource
68597
+ * down with it (plus exit 2). The report exists so the user learns cdkd is
68598
+ * shipping a token it cannot resolve, which is a real thing to know and not a
68599
+ * reason to stop.
68648
68600
  *
68649
68601
  * Only the TOKENS are reported, never the leaf that held them: the leaf is
68650
68602
  * partially substituted by this point, so a mixed `secretsmanager` +
68651
- * `ssm-secure` string comes back carrying real plaintext.
68603
+ * look-alike string comes back carrying real plaintext.
68652
68604
  *
68653
68605
  * Returns the input by identity when it holds no dynamic reference, so the
68654
68606
  * non-secret path is unchanged down to object identity.
@@ -68671,10 +68623,7 @@ async function resolveStateSecretExpressions(props, resolvers, secrets, options)
68671
68623
  if (!v.includes("{{resolve:")) return v;
68672
68624
  const resolved = await resolveDriftLeafByRegion(v, path, logicalId, consumerRegion, producerRegions, resolvers, ctx);
68673
68625
  const survivors = survivingDynamicReferences(resolved).filter((t) => v.includes(t));
68674
- if (survivors.length > 0) {
68675
- if (survivors.some(isSecretBySpelling)) secretPaths?.add(path);
68676
- onUnresolved?.(survivors, path);
68677
- }
68626
+ if (survivors.length > 0) onUnresolved?.(survivors, path);
68678
68627
  if (carriesRecordedSecret(resolved, secrets)) secretPaths?.add(path);
68679
68628
  return resolved;
68680
68629
  }
@@ -68934,7 +68883,7 @@ async function runDriftForStack(stackName, region, stateBackend, providerRegistr
68934
68883
  logger.warn(`${logicalId} (${resource.resourceType}): ` + (refused ? "refused to resolve a dynamic reference this resource's state records, so its secret-bearing properties are NOT compared — " : "could not resolve the dynamic reference(s) this resource's state records, so its secret-bearing properties are NOT compared — ") + `${maskSecretsInText(err instanceof Error ? err.message : String(err), secrets)}`);
68935
68884
  secrets.clear();
68936
68885
  }
68937
- if (unresolvedTokens.size > 0) logger.warn(`${logicalId} (${resource.resourceType}): cdkd cannot resolve ${maskSecretsInText([...unresolvedTokens].join(", "), secrets)} — those properties are NOT compared. A revert leaves a property whose WHOLE value is one of these tokens untouched; where a token is EMBEDDED in a longer string, the revert writes that string with the token literal, exactly as 'cdkd deploy' does — so a resolved value AWS holds there WOULD be overwritten. cdkd resolves 'secretsmanager' and 'ssm' references only.`);
68886
+ if (unresolvedTokens.size > 0) logger.warn(`${logicalId} (${resource.resourceType}): cdkd cannot resolve ${maskSecretsInText([...unresolvedTokens].join(", "), secrets)} — those properties are NOT compared. A revert leaves a property whose WHOLE value is one of these tokens untouched; where a token is EMBEDDED in a longer string, the revert writes that string with the token literal, exactly as 'cdkd deploy' does — so a resolved value AWS holds there WOULD be overwritten. cdkd resolves 'secretsmanager', 'ssm' and 'ssm-secure' references; anything else is left as written.`);
68938
68887
  const observedIgnorePaths = useObserved ? undeclaredEmptyObservedKeys(resource.observedProperties, resource.properties ?? {}) : [];
68939
68888
  const normalized = await canonicalizePrincipalUniqueIds(comparisonBaseline, aws, resolvePrincipalUniqueId);
68940
68889
  const protocolNormalized = canonicalizeIpProtocols(normalized.baseline, normalized.aws, resource.resourceType);
@@ -69505,19 +69454,24 @@ function mergeUntemplatedValue(awsValue, desiredValue) {
69505
69454
  * warning, on the premise that `cdkd deploy` resolves through the
69506
69455
  * resolver's same unsupported-service arm — so AWS already holds the literal
69507
69456
  * and replaying it is a no-op. **That premise holds only for records cdkd
69508
- * deployed.** CloudFormation resolves `ssm-secure` SERVER-side, so a record
69509
- * adopted by `cdkd import --migrate-from-cloudformation` has the literal token
69510
- * in state while the live resource holds the PLAINTEXT. `diffAt` skips such a
69511
- * leaf, so it never drifts — but `buildRevertNewProperties` overlays the whole
69512
- * top-level subtree when any SIBLING key drifts, which would push the literal
69513
- * token over a resolved value. That is defect 1 of this issue surviving in a
69514
- * narrower case, on a live AWS write.
69457
+ * deployed.** A record adopted by `cdkd import --migrate-from-cloudformation`,
69458
+ * or one whose position was edited out of band, holds something ELSE there.
69459
+ * `diffAt` skips such a leaf, so it never drifts but
69460
+ * `buildRevertNewProperties` overlays the whole top-level subtree when any
69461
+ * SIBLING key drifts, which would push the literal token over that value. That
69462
+ * is defect 1 of this issue surviving in a narrower case, on a live AWS write.
69463
+ *
69464
+ * (When this was written the population was `ssm-secure`, which CloudFormation
69465
+ * resolves SERVER-side, so a migrated record held the PLAINTEXT on AWS. Since
69466
+ * issue #2482 cdkd resolves that spelling itself and the token never reaches
69467
+ * this pass; what survives is a spelling nobody resolves, so the live value
69468
+ * here is whatever the user or another tool put at that position.)
69515
69469
  *
69516
69470
  * Deciding by PROVENANCE would need a flag state does not carry. Deciding by
69517
69471
  * what AWS actually holds needs nothing and is exact in both directions: for a
69518
69472
  * cdkd-deployed record AWS holds the token, so copying it back is the same
69519
- * no-op the premise described; for a migrated record AWS holds the resolved
69520
- * value, which is left untouched.
69473
+ * no-op the premise described; for any other record AWS holds a value cdkd
69474
+ * cannot compare, which is left untouched.
69521
69475
  *
69522
69476
  * Falls back to KEEPING the token wherever AWS has nothing at that position —
69523
69477
  * that is what `cdkd deploy` sends, so it is the safe residual rather than
@@ -69525,12 +69479,11 @@ function mergeUntemplatedValue(awsValue, desiredValue) {
69525
69479
  * and bails to that same residual on any length mismatch, since a reordered
69526
69480
  * readback cannot be positioned against.
69527
69481
  */
69528
- function preserveLiveValuesAtUnresolvedTokens(send, awsProperties, secrets) {
69482
+ function preserveLiveValuesAtUnresolvedTokens(send, awsProperties) {
69529
69483
  const walk = (value, live) => {
69530
69484
  if (typeof value === "string" && value.includes("{{resolve:")) {
69531
69485
  if (!isSingleDynamicReferenceToken(value)) return value;
69532
69486
  if (live === void 0) return value;
69533
- if (typeof live === "string" && live.length >= MIN_SECRET_NEEDLE_LENGTH && isSecretBySpelling(value)) secrets.set(live, value);
69534
69487
  return live;
69535
69488
  }
69536
69489
  if (Array.isArray(value)) {
@@ -69884,7 +69837,7 @@ async function runRevert(reports, providerRegistry, stateBackend, stateConfig, a
69884
69837
  let newProperties;
69885
69838
  try {
69886
69839
  const overlaid = buildRevertNewProperties(outcome.changes, desiredProperties, outcome.awsProperties, { preserveUntemplated: stateResource.observedProperties === void 0 });
69887
- const maskPreserved = preserveLiveValuesAtMaskedLeaves(unresolvedTokens.size > 0 ? preserveLiveValuesAtUnresolvedTokens(overlaid, outcome.awsProperties, secrets) : overlaid, outcome.awsProperties, secrets);
69840
+ const maskPreserved = preserveLiveValuesAtMaskedLeaves(unresolvedTokens.size > 0 ? preserveLiveValuesAtUnresolvedTokens(overlaid, outcome.awsProperties) : overlaid, outcome.awsProperties, secrets);
69888
69841
  if (maskPreserved.unpreservablePaths.length > 0) {
69889
69842
  totalUnresolvable++;
69890
69843
  logger.error(` ✗ ${report.stackName}/${outcome.logicalId} (${outcome.resourceType}): refused to revert ${maskPreserved.unpreservablePaths.join(", ")} — the recorded baseline holds only the redaction mask there (a NoEcho custom-resource value), and AWS reports nothing to preserve, so cdkd has no value it may write. Force that custom resource to update (change one of its properties, e.g. a nonce) and re-deploy, so its handler runs again and supplies the value — an ordinary re-deploy leaves it unchanged and the mask stays.`);
@@ -70240,7 +70193,7 @@ function formatScalar(value) {
70240
70193
  * Reusable `--stack-region <region>` option (mirrors `state show`).
70241
70194
  */
70242
70195
  function stackRegionOption$2() {
70243
- return new Option("--stack-region <region>", "Region of the stack record to inspect. Required when the same stack name has state in multiple regions.");
70196
+ return new Option("--stack-region <region>", "Region of the stack record to inspect. Required when the same stack name has state in multiple regions.").argParser(parseStackRegion);
70244
70197
  }
70245
70198
  /**
70246
70199
  * Create the `drift` command.
@@ -70679,6 +70632,11 @@ async function stateMigrateCommand(options) {
70679
70632
  const sourceObjects = await listAllObjects(legacyS3, legacyBucket);
70680
70633
  logger.info(` source object count: ${sourceObjects.length}`);
70681
70634
  if (sourceObjects.length === 0) logger.info("Source bucket is empty — no objects to copy.");
70635
+ if (options.dryRun) {
70636
+ if (options.removeLegacy) logger.info(`--remove-legacy: '${legacyBucket}' would then be emptied (every object version and delete marker) and deleted.`);
70637
+ logger.info("--dry-run: no changes will be made. Stopping here.");
70638
+ return;
70639
+ }
70682
70640
  if (!options.yes) {
70683
70641
  const action = options.removeLegacy ? "and DELETE the source bucket" : "(source bucket will be kept)";
70684
70642
  if (!await confirmPrompt$4(`Copy ${sourceObjects.length} object(s) from ${legacyBucket} -> ${newBucket} ${action}?`)) {
@@ -70686,10 +70644,6 @@ async function stateMigrateCommand(options) {
70686
70644
  return;
70687
70645
  }
70688
70646
  }
70689
- if (options.dryRun) {
70690
- logger.info("--dry-run: no changes will be made. Stopping here.");
70691
- return;
70692
- }
70693
70647
  const newS3 = await ensureDestinationBucket(newBucket, legacyRegion, accountId, logger);
70694
70648
  try {
70695
70649
  let copied = 0;
@@ -70864,7 +70818,11 @@ async function emptyBucketAllVersions(s3, bucket) {
70864
70818
  /**
70865
70819
  * `cdkd state migrate`'s confirmation prompt. Its only call site is inside the
70866
70820
  * `if (!options.yes)` block above, which is what keeps `confirmOrRefuse`'s
70867
- * non-interactive refusal (issue #2275) from firing on a `--yes` run.
70821
+ * non-interactive refusal (issue #2275) from firing on a `--yes` run — and it
70822
+ * sits BELOW the `--dry-run` bail (issue #2538), so a preview never reaches it
70823
+ * either. Together those are the two ways not to be asked, matching
70824
+ * `state refresh-observed`, whose prompt condition spells both:
70825
+ * `!options.yes && !options.dryRun`.
70868
70826
  *
70869
70827
  * Exported for unit testing — internal to the command flow otherwise.
70870
70828
  */
@@ -74503,7 +74461,7 @@ async function confirmPrompt$3(prompt) {
74503
74461
  return confirmOrRefuse(prompt, { refusal: "The cdkd export confirmation prompt cannot run in a non-interactive environment. Pass -y / --yes to confirm, or run the command from a real terminal." });
74504
74462
  }
74505
74463
  function createExportCommand() {
74506
- const cmd = new Command("export").description("Hand a cdkd-managed stack over to CloudFormation via CFn IMPORT (changeset). AWS resources are unchanged; cdkd state for the stack is deleted on success. Mirror of `cdkd import` (AWS → cdkd) in the reverse direction (cdkd → CFn). Accepts JSON and YAML templates (YAML via a CFn-aware codec that preserves !Ref / !GetAtt / !Sub shorthand). Aborts if any resource is not CFn-importable.").argument("[stack]", "Stack name to export (auto-detected for single-stack apps)").option("--cfn-stack-name <name>", "Name of the destination CloudFormation stack for the root stack. Defaults to the cdkd stack name (or the cdkd2cfnStackName mapping when the name contains '~'). For per-nested-child overrides, use --cfn-child-stack-name.").option("--cfn-child-stack-name <pair...>", "Per-nested-child CFn stack-name override. Repeatable. Format: '<cdkdName>=<cfnName>' (e.g. --cfn-child-stack-name 'MyApp~Database=my-app-db'). The cdkd stack name for a nested child is '<parent>~<childLogicalId>' (v6 state-key form). Without an override the CFn name is derived by replacing '~' with '-'. Only consulted when the exported stack tree contains nested children; ignored for flat stacks.").option("--template <path>", "Path to a pre-rendered CloudFormation template (JSON or YAML — format auto-detected). Skips synth.").option("--stack-region <region>", "Region of the cdkd state record to operate on. Required when the same stack name has state in multiple regions.").option("--dry-run", "Print the import plan without creating a changeset.", false).option("--accept-transient-context", "Allow CLI -c key=value overrides at export time even though they are not persisted to cdk.json / cdk.context.json (default: refuse). When set, the user is responsible for passing the same -c flags to every future cdk deploy.", false).option("--include-non-importable", "Run a 2-phase migration when the stack contains non-importable resources (Custom::*). Phase 1 imports the importable resources; phase 2 CFn-CREATEs the non-importable ones, which re-invokes each Custom Resource's onCreate handler. Make sure onCreate is idempotent before enabling.", false).option("--parameter <key=value...>", "CFn template Parameter override, repeatable. Required when the synthesized template has Parameters without Default values; otherwise overrides the template's default value. Format: --parameter Key=Value.").option("--strict-cross-stack", "Refuse to export when sibling cdkd stacks in the same CDK app reference the exporting stack via Fn::GetStackOutput. Without the flag, cdkd warns and proceeds — the consumers keep resolving via the CloudFormation fallback (weak reference) unless they deploy with --no-cfn-fallback.", false).option("--skip-import-support-preflight", "Skip the pre-flight that refuses resource types whose CloudFormation registry schema says CFn cannot IMPORT them (no read handler AND NON_PROVISIONABLE), and let CreateChangeSet answer instead. The pre-flight is a registry HEURISTIC, not AWS's supported-for-import list, so this is the escape hatch for a type AWS has since made importable. Expect the changeset to be rejected if the heuristic was right.", false).option("--no-recreate-import-unsupported", "Block instead of auto-handling resource types AWS does NOT support in IMPORT changesets (currently only AWS::ApiGatewayV2::Stage, emitted by CDK HttpApi). Default behavior: cdkd skips these from phase 1, deletes the AWS-side resource between phases, and lets CFn re-CREATE in phase 2 (brief unavailability window). With this flag, the export aborts with a clear error instead.").action(withErrorHandling(exportCommand));
74464
+ const cmd = new Command("export").description("Hand a cdkd-managed stack over to CloudFormation via CFn IMPORT (changeset). AWS resources are unchanged; cdkd state for the stack is deleted on success. Mirror of `cdkd import` (AWS → cdkd) in the reverse direction (cdkd → CFn). Accepts JSON and YAML templates (YAML via a CFn-aware codec that preserves !Ref / !GetAtt / !Sub shorthand). Aborts if any resource is not CFn-importable.").argument("[stack]", "Stack name to export (auto-detected for single-stack apps)").option("--cfn-stack-name <name>", "Name of the destination CloudFormation stack for the root stack. Defaults to the cdkd stack name (or the cdkd2cfnStackName mapping when the name contains '~'). For per-nested-child overrides, use --cfn-child-stack-name.").option("--cfn-child-stack-name <pair...>", "Per-nested-child CFn stack-name override. Repeatable. Format: '<cdkdName>=<cfnName>' (e.g. --cfn-child-stack-name 'MyApp~Database=my-app-db'). The cdkd stack name for a nested child is '<parent>~<childLogicalId>' (v6 state-key form). Without an override the CFn name is derived by replacing '~' with '-'. Only consulted when the exported stack tree contains nested children; ignored for flat stacks.").option("--template <path>", "Path to a pre-rendered CloudFormation template (JSON or YAML — format auto-detected). Skips synth.").option("--stack-region <region>", "Region of the cdkd state record to operate on. Required when the same stack name has state in multiple regions.", parseStackRegion).option("--dry-run", "Print the import plan without creating a changeset.", false).option("--accept-transient-context", "Allow CLI -c key=value overrides at export time even though they are not persisted to cdk.json / cdk.context.json (default: refuse). When set, the user is responsible for passing the same -c flags to every future cdk deploy.", false).option("--include-non-importable", "Run a 2-phase migration when the stack contains non-importable resources (Custom::*). Phase 1 imports the importable resources; phase 2 CFn-CREATEs the non-importable ones, which re-invokes each Custom Resource's onCreate handler. Make sure onCreate is idempotent before enabling.", false).option("--parameter <key=value...>", "CFn template Parameter override, repeatable. Required when the synthesized template has Parameters without Default values; otherwise overrides the template's default value. Format: --parameter Key=Value.").option("--strict-cross-stack", "Refuse to export when sibling cdkd stacks in the same CDK app reference the exporting stack via Fn::GetStackOutput. Without the flag, cdkd warns and proceeds — the consumers keep resolving via the CloudFormation fallback (weak reference) unless they deploy with --no-cfn-fallback.", false).option("--skip-import-support-preflight", "Skip the pre-flight that refuses resource types whose CloudFormation registry schema says CFn cannot IMPORT them (no read handler AND NON_PROVISIONABLE), and let CreateChangeSet answer instead. The pre-flight is a registry HEURISTIC, not AWS's supported-for-import list, so this is the escape hatch for a type AWS has since made importable. Expect the changeset to be rejected if the heuristic was right.", false).option("--no-recreate-import-unsupported", "Block instead of auto-handling resource types AWS does NOT support in IMPORT changesets (AWS::ApiGatewayV2::Stage and AWS::IAM::Policy). Default behavior: cdkd skips these from phase 1, deletes the AWS-side resource between phases, and lets CFn re-CREATE in phase 2 (brief unavailability window). With this flag, the export aborts with a clear error instead.").action(withErrorHandling(exportCommand));
74507
74465
  [
74508
74466
  ...commonOptions,
74509
74467
  ...appOptions,
@@ -75100,14 +75058,14 @@ async function stateOrphanCommand(stackArgs, options) {
75100
75058
  logger.info(`No state found for stack: ${stackName}, skipping`);
75101
75059
  continue;
75102
75060
  }
75103
- const targets = options.stackRegion ? stackRefs.filter((r) => r.region === options.stackRegion) : stackRefs;
75061
+ const targets = options.stackRegion !== void 0 ? stackRefs.filter((r) => r.region === options.stackRegion) : stackRefs;
75104
75062
  if (targets.length === 0) {
75105
- const seen = stackRefs.map((r) => r.region ?? "(legacy)").join(", ");
75106
- throw new Error(`No state found for stack '${stackName}' in region '${options.stackRegion}'. Available regions: ${seen}.`);
75063
+ const seen = stackRefs.map((r) => r.region ? displaySafe(r.region, { asciiOnly: true }) || "<unrenderable>" : "(legacy)").join(", ");
75064
+ throw new Error(`No state found for stack '${displaySafe(stackName, { asciiOnly: true }) || "<unrenderable>"}' in region '${displaySafe(options.stackRegion ?? "", { asciiOnly: true })}'. Available regions: ${seen}.`);
75107
75065
  }
75108
75066
  if (!options.force) {
75109
75067
  for (const target of targets) if (await setup.lockManager.isLocked(stackName, target.region)) {
75110
- const where = target.region ? displaySafe(target.region, { asciiOnly: true }) || "<unrenderable>" : "(legacy)";
75068
+ const where = target.region ? displaySafe(target.region, { asciiOnly: true }) || "<unrenderable>" : "legacy";
75111
75069
  throw new Error(`Stack '${displaySafe(stackName, { asciiOnly: true })}' (${where}) is locked. Run: ${buildForceUnlockCommand(stackName, target.region, {
75112
75070
  profile: options.profile,
75113
75071
  stateBucket: setup.bucket,
@@ -75117,7 +75075,7 @@ async function stateOrphanCommand(stackArgs, options) {
75117
75075
  }
75118
75076
  if (!options.yes && !options.force) {
75119
75077
  const targetList = targets.map((t) => formatStackRefSafe(t)).join(", ");
75120
- process.stdout.write(`\nWARNING: This removes cdkd's state record for [${targetList}] only. AWS resources will NOT be deleted.\nUse 'cdkd destroy ${stackName}' if you want to delete the actual resources.\n\n`);
75078
+ process.stdout.write(`\nWARNING: This removes cdkd's state record for [${targetList}] only. AWS resources will NOT be deleted.\nUse 'cdkd destroy ${displaySafe(stackName, { asciiOnly: true }) || "<unrenderable>"}' if you want to delete the actual resources.\n\n`);
75121
75079
  if (!await confirmStateOrphanRemoval(`Remove state for ${targetList} from s3://${setup.bucket}/${setup.prefix}/?`)) {
75122
75080
  logger.info(`Cancelled removal of state for stack: ${stackName}`);
75123
75081
  continue;
@@ -75130,9 +75088,10 @@ async function stateOrphanCommand(stackArgs, options) {
75130
75088
  await setup.lockManager.forceReleaseLock(stackName, target.region);
75131
75089
  } else {
75132
75090
  await warnOnLiveForeignLock(setup.lockManager, stackName, void 0, logger);
75091
+ await setup.stateBackend.deleteLegacyState(stackName);
75133
75092
  await setup.lockManager.forceReleaseLock(stackName, void 0);
75134
75093
  }
75135
- logger.info(`✓ Removed state for stack: ${formatStackRef(target)}`);
75094
+ logger.info(`✓ Removed state for stack: ${formatStackRefSafe(target)}`);
75136
75095
  }
75137
75096
  }
75138
75097
  } finally {
@@ -75145,7 +75104,7 @@ async function stateOrphanCommand(stackArgs, options) {
75145
75104
  * `--region` (AWS profile region) defined in `commonOptions`.
75146
75105
  */
75147
75106
  function stackRegionOption$1() {
75148
- return new Option("--stack-region <region>", "Region of the stack record to operate on. Required when the same stack name has state in multiple regions.");
75107
+ return new Option("--stack-region <region>", "Region of the stack record to operate on. Required when the same stack name has state in multiple regions.").argParser(parseStackRegion);
75149
75108
  }
75150
75109
  /**
75151
75110
  * Create the `state orphan` subcommand.
@@ -75939,7 +75898,7 @@ function createStateCommand() {
75939
75898
  * in multiple regions (same pattern + messages as the `state` subcommands).
75940
75899
  */
75941
75900
  function stackRegionOption() {
75942
- return new Option("--stack-region <region>", "Region of the target stack when the same name has state in multiple regions");
75901
+ return new Option("--stack-region <region>", "Region of the target stack when the same name has state in multiple regions").argParser(parseStackRegion);
75943
75902
  }
75944
75903
  /**
75945
75904
  * Discover every stack that currently has a rollback journal. One raw key
@@ -76553,7 +76512,7 @@ function colorizeEventType(eventType) {
76553
76512
  * Create the `cdkd events` command.
76554
76513
  */
76555
76514
  function createEventsCommand() {
76556
- const cmd = new Command("events").description("Read back structured deployment events (cdkd's DescribeStackEvents equivalent, issue #808)").argument("<stack>", "Stack name (physical CloudFormation name)").option("--run <runId>", "Read a single run's full event stream instead of the run listing").option("--stack-region <region>", "Disambiguate a stack with event history in multiple regions").option("--json", "Output as JSON", false).option("--format <format>", "Output format ('json' is equivalent to --json)").action(withErrorHandling((stack, options) => {
76515
+ const cmd = new Command("events").description("Read back structured deployment events (cdkd's DescribeStackEvents equivalent, issue #808)").argument("<stack>", "Stack name (physical CloudFormation name)").option("--run <runId>", "Read a single run's full event stream instead of the run listing").option("--stack-region <region>", "Disambiguate a stack with event history in multiple regions", parseStackRegion).option("--json", "Output as JSON", false).option("--format <format>", "Output format ('json' is equivalent to --json)").action(withErrorHandling((stack, options) => {
76557
76516
  return eventsCommand(stack, {
76558
76517
  ...options,
76559
76518
  json: options.json === true || options.format === "json"
@@ -78520,7 +78479,7 @@ async function confirmPrompt$1(prompt) {
78520
78479
  * Create the top-level `cdkd orphan` command.
78521
78480
  */
78522
78481
  function createOrphanCommand() {
78523
- const cmd = new Command("orphan").description("Remove one or more resources from cdkd state by construct path (does NOT delete AWS resources). Mirrors aws-cdk-cli's 'cdk orphan --unstable=orphan'. Synth-driven; for the previous whole-stack-orphan behavior, use 'cdkd state orphan <stack>'.").argument("<paths...>", "Construct paths to orphan, e.g. 'MyStack/MyTable'. Multiple paths must reference the same stack.").option("--stack-region <region>", "Region of the stack record to operate on. Required when the same stack name has state in multiple regions.").option("--dry-run", "Compute and print the rewrite audit table without acquiring a lock or saving state.", false).action(withErrorHandling(orphanCommand));
78482
+ const cmd = new Command("orphan").description("Remove one or more resources from cdkd state by construct path (does NOT delete AWS resources). Mirrors aws-cdk-cli's 'cdk orphan --unstable=orphan'. Synth-driven; for the previous whole-stack-orphan behavior, use 'cdkd state orphan <stack>'.").argument("<paths...>", "Construct paths to orphan, e.g. 'MyStack/MyTable'. Multiple paths must reference the same stack.").option("--stack-region <region>", "Region of the stack record to operate on. Required when the same stack name has state in multiple regions.", parseStackRegion).option("--dry-run", "Compute and print the rewrite audit table without acquiring a lock or saving state.", false).action(withErrorHandling(orphanCommand));
78524
78483
  [
78525
78484
  ...commonOptions,
78526
78485
  ...appOptions,
@@ -78809,7 +78768,7 @@ async function forceUnlockCommand(stackArgs, options) {
78809
78768
  * Create force-unlock command
78810
78769
  */
78811
78770
  function createForceUnlockCommand() {
78812
- const cmd = new Command("force-unlock").description("Force-release a stale lock on a stack").argument("[stacks...]", "Stack name(s) to unlock").addOption(new Option("--stack-region <region>", "Stack region whose lock to release (use when the same stack name has locks in multiple regions). Defaults to all regions where the stack has state.")).action(withErrorHandling(forceUnlockCommand));
78771
+ const cmd = new Command("force-unlock").description("Force-release a stale lock on a stack").argument("[stacks...]", "Stack name(s) to unlock").addOption(new Option("--stack-region <region>", "Stack region whose lock to release (use when the same stack name has locks in multiple regions). Defaults to all regions where the stack has state.").argParser(parseStackRegion)).action(withErrorHandling(forceUnlockCommand));
78813
78772
  [
78814
78773
  ...commonOptions,
78815
78774
  ...stateOptions,
@@ -85095,7 +85054,7 @@ function resolveMtlsConfig(options) {
85095
85054
  * Builder for the `start-api` subcommand. Wired up by `local.ts`.
85096
85055
  */
85097
85056
  function createLocalStartApiCommand() {
85098
- const startApi = new Command("start-api").description("Run a long-running local HTTP server that maps API Gateway routes (REST v1, HTTP API, Function URL) to Lambda invocations against the AWS Lambda Runtime Interface Emulator (Docker required). Supports Lambda TOKEN/REQUEST authorizers, Cognito User Pool / HTTP v2 JWT authorizers, and AWS_IAM auth (REST v1 `AuthorizationType: AWS_IAM` and Function URL `AuthType: AWS_IAM` — SigV4 signature verification only; IAM policy evaluation is NOT emulated; see https://github.com/go-to-k/cdkd/blob/main/docs/local-emulation.md). When JWKS is unreachable, JWT authorizers fall back to pass-through (every token accepted) with a warn line — local dev fallback. VPC-config Lambdas run locally and surface a warn line at startup; their containers do NOT get attached to the deployed VPC subnets, so calls to private RDS / ElastiCache will fail.").argument("[target]", "Optional API filter. Accepts the bare CDK logical id ('MyHttpApi'; single-stack apps only), stack-qualified logical id ('MyStack:MyHttpApi'), full CDK Construct path ('MyStack/MyHttpApi/Resource'), or an ancestor Construct path that prefix-matches ('MyStack/MyHttpApi'). When omitted, every discovered API gets its own server. Mirrors `cdkd local invoke` / `cdkd local run-task` target syntax.").addOption(new Option("--port <port>", "HTTP server port (default: auto-allocate)").default("0")).addOption(new Option("--host <host>", "Bind address").default("127.0.0.1")).addOption(new Option("--stack <name>", "Stack to start (single-stack apps auto-detect)")).addOption(new Option("--warm", "Pre-start one container per Lambda at server boot").default(false)).addOption(new Option("--per-lambda-concurrency <n>", "Pool size cap per Lambda (default 2, max 4)").default("2")).addOption(new Option("--no-pull", "Skip docker pull (cached image)")).addOption(new Option("--container-host <host>", "IP the host uses to bind/probe the RIE port (must be a numeric IP — `docker run -p <ip>:<port>:8080` rejects hostnames). Defaults to 127.0.0.1.").default("127.0.0.1")).addOption(new Option("--debug-port-base <port>", "Reserve a contiguous --debug-port range (one per Lambda)")).addOption(new Option("--env-vars <file>", "JSON env-var overrides (SAM-compatible: {\"LogicalId\":{\"KEY\":\"VALUE\"}, \"Parameters\": {...}})")).addOption(new Option("--assume-role <arn-or-pair>", "Assume the Lambda's execution role and forward STS-issued temp creds. Two forms: (1) `--assume-role <arn>` sets a single global default ARN used for every routed Lambda; (2) `--assume-role <LogicalId>=<arn>` (repeatable) is a per-Lambda override (wins over the global default and over --assume-role-auto for the named Lambda). Pair with --assume-role-auto to auto-resolve every other Lambda. Precedence: per-Lambda > (--assume-role-auto OR global default) > unset (developer creds passed through).").argParser((raw, prev) => parseAssumeRoleToken(raw, prev))).addOption(new Option("--assume-role-auto", "Auto-resolve EACH routed Lambda's own execution role per-Lambda (literal ARN / Fn::GetAtt against template / --from-cfn-stack state). Slower boot — one STS call per Lambda — but the right shape when each Lambda's deployed role differs. Mutually exclusive with `--assume-role <arn>` (global default); errors at boot. Compatible with `--assume-role <LogicalId>=<arn>` per-Lambda overrides — the map wins for named Lambdas, auto-resolve handles the rest.").default(false)).addOption(new Option("--watch", "Hot-reload: watch the CDK app source tree and re-synth + re-discover routes on a source edit (cdk.out / node_modules / .git excluded; honors cdk.json watch.include / watch.exclude). Off by default; the server keeps the previous version serving when synth fails mid-reload.").default(false)).addOption(new Option("--stage <name>", "Select an API Gateway Stage by its 'StageName'. Default: the first Stage attached to each API. Drives event.stageVariables for both REST v1 and HTTP API v2. NOTE: For HTTP API v2 routes, requestContext.stage is always '$default' regardless of this flag (AWS-side limitation — HTTP API only exposes one stage to the integration event); only event.stageVariables is affected for v2 routes. For REST v1 routes the selected StageName is also threaded into requestContext.stage.")).addOption(new Option("--api <id>", "DEPRECATED — use the positional <target> argument instead. Same accepted forms (bare logical id, stack-qualified, Construct path, ancestor prefix). Will be removed in a future major release.")).addOption(new Option("--layer-role-arn <arn>", "Role to sts:AssumeRole before calling lambda:GetLayerVersion on every literal-ARN entry in Properties.Layers (issue #448). Use only when the dev credentials cannot read the layer — typically cross-account layers. AWS-published public layers (e.g. Lambda Powertools) are readable from every account and need no role.")).addOption(new Option("--from-state", "Read cdkd S3 state for every routed stack and substitute Ref / Fn::GetAtt / Fn::Sub / Fn::Join (and AWS pseudo parameters) in Lambda env vars with the deployed physical IDs / attributes. Off by default — pre-PR warn-and-drop semantics are preserved. Turn on for stacks already deployed via cdkd deploy. Mirrors `cdkd local invoke --from-state` / `cdkd local run-task --from-state`. Re-runs against fresh state on every hot-reload firing (--watch).").default(false)).addOption(new Option("--from-cfn-stack [cfn-stack-name]", "Read a deployed CloudFormation stack via DescribeStackResources and substitute Ref / Fn::ImportValue in Lambda env vars with the deployed physical IDs / exports. Use for CDK apps deployed via the upstream CDK CLI (`cdk deploy`). Bare form uses the cdkd stack name per routed stack; pass an explicit value when a single CFn stack should serve every routed stack. Mutually exclusive with --from-state. Fn::GetAtt is warn-and-dropped in v1 (CFn DescribeStackResources does not return per-attribute values).")).addOption(new Option("--stack-region <region>", "Region of the state record to read. Used with --from-state when the same stack name has state in multiple regions, and with --from-cfn-stack as the CFn client region (cdkd does not have a separate --cfn-stack-region flag).")).addOption(new Option("--mtls-truststore <path>", "PEM-encoded CA bundle for client-certificate verification (mutual TLS). When set, the local server switches from HTTP to HTTPS and the TLS handshake rejects clients whose certificate doesn't chain to one of these CAs. Verified certs are surfaced on the Lambda event under requestContext.identity.clientCert (REST v1) / requestContext.authentication.clientCert (HTTP API v2). Must be set together with --mtls-cert + --mtls-key; partial flag sets are rejected. Generate a CA + server + client cert for local dev: openssl req -x509 -newkey rsa:2048 -nodes -keyout ca-key.pem -out ca.pem -subj \"/CN=cdkd-local-ca\" -days 365; openssl req -newkey rsa:2048 -nodes -keyout server-key.pem -out server-csr.pem -subj \"/CN=localhost\"; openssl x509 -req -in server-csr.pem -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out server-cert.pem -days 365; openssl req -newkey rsa:2048 -nodes -keyout client-key.pem -out client-csr.pem -subj \"/CN=client\"; openssl x509 -req -in client-csr.pem -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out client-cert.pem -days 365; curl --cacert ca.pem --cert client-cert.pem --key client-key.pem https://localhost:<port>/...")).addOption(new Option("--mtls-cert <path>", "PEM-encoded server certificate for mutual TLS. Self-signed is fine for local dev. Must be set together with --mtls-truststore + --mtls-key.")).addOption(new Option("--mtls-key <path>", "PEM-encoded server private key matching --mtls-cert. Must be set together with --mtls-truststore + --mtls-cert.")).addOption(new Option("--strict-sigv4", "Opt-in: enforce strict AWS_IAM SigV4 verification. When set, requests whose signature cannot be cryptographically verified (foreign access-key-id, OR no local AWS credentials configured) are denied. DEFAULT off — warn-and-pass with a placeholder principalId, matching cdk-local's `cdkl start-api`. Enable this when you want local parity with the deployed API Gateway's signature enforcement.").default(false)).action(withErrorHandling(localStartApiCommand));
85057
+ const startApi = new Command("start-api").description("Run a long-running local HTTP server that maps API Gateway routes (REST v1, HTTP API, Function URL) to Lambda invocations against the AWS Lambda Runtime Interface Emulator (Docker required). Supports Lambda TOKEN/REQUEST authorizers, Cognito User Pool / HTTP v2 JWT authorizers, and AWS_IAM auth (REST v1 `AuthorizationType: AWS_IAM` and Function URL `AuthType: AWS_IAM` — SigV4 signature verification only; IAM policy evaluation is NOT emulated; see https://github.com/go-to-k/cdkd/blob/main/docs/local-emulation.md). When JWKS is unreachable, JWT authorizers fall back to pass-through (every token accepted) with a warn line — local dev fallback. VPC-config Lambdas run locally and surface a warn line at startup; their containers do NOT get attached to the deployed VPC subnets, so calls to private RDS / ElastiCache will fail.").argument("[target]", "Optional API filter. Accepts the bare CDK logical id ('MyHttpApi'; single-stack apps only), stack-qualified logical id ('MyStack:MyHttpApi'), full CDK Construct path ('MyStack/MyHttpApi/Resource'), or an ancestor Construct path that prefix-matches ('MyStack/MyHttpApi'). When omitted, every discovered API gets its own server. Mirrors `cdkd local invoke` / `cdkd local run-task` target syntax.").addOption(new Option("--port <port>", "HTTP server port (default: auto-allocate)").default("0")).addOption(new Option("--host <host>", "Bind address").default("127.0.0.1")).addOption(new Option("--stack <name>", "Stack to start (single-stack apps auto-detect)")).addOption(new Option("--warm", "Pre-start one container per Lambda at server boot").default(false)).addOption(new Option("--per-lambda-concurrency <n>", "Pool size cap per Lambda (default 2, max 4)").default("2")).addOption(new Option("--no-pull", "Skip docker pull (cached image)")).addOption(new Option("--container-host <host>", "IP the host uses to bind/probe the RIE port (must be a numeric IP — `docker run -p <ip>:<port>:8080` rejects hostnames). Defaults to 127.0.0.1.").default("127.0.0.1")).addOption(new Option("--debug-port-base <port>", "Reserve a contiguous --debug-port range (one per Lambda)")).addOption(new Option("--env-vars <file>", "JSON env-var overrides (SAM-compatible: {\"LogicalId\":{\"KEY\":\"VALUE\"}, \"Parameters\": {...}})")).addOption(new Option("--assume-role <arn-or-pair>", "Assume the Lambda's execution role and forward STS-issued temp creds. Two forms: (1) `--assume-role <arn>` sets a single global default ARN used for every routed Lambda; (2) `--assume-role <LogicalId>=<arn>` (repeatable) is a per-Lambda override (wins over the global default and over --assume-role-auto for the named Lambda). Pair with --assume-role-auto to auto-resolve every other Lambda. Precedence: per-Lambda > (--assume-role-auto OR global default) > unset (developer creds passed through).").argParser((raw, prev) => parseAssumeRoleToken(raw, prev))).addOption(new Option("--assume-role-auto", "Auto-resolve EACH routed Lambda's own execution role per-Lambda (literal ARN / Fn::GetAtt against template / --from-cfn-stack state). Slower boot — one STS call per Lambda — but the right shape when each Lambda's deployed role differs. Mutually exclusive with `--assume-role <arn>` (global default); errors at boot. Compatible with `--assume-role <LogicalId>=<arn>` per-Lambda overrides — the map wins for named Lambdas, auto-resolve handles the rest.").default(false)).addOption(new Option("--watch", "Hot-reload: watch the CDK app source tree and re-synth + re-discover routes on a source edit (cdk.out / node_modules / .git excluded; honors cdk.json watch.include / watch.exclude). Off by default; the server keeps the previous version serving when synth fails mid-reload.").default(false)).addOption(new Option("--stage <name>", "Select an API Gateway Stage by its 'StageName'. Default: the first Stage attached to each API. Drives event.stageVariables for both REST v1 and HTTP API v2. NOTE: For HTTP API v2 routes, requestContext.stage is always '$default' regardless of this flag (AWS-side limitation — HTTP API only exposes one stage to the integration event); only event.stageVariables is affected for v2 routes. For REST v1 routes the selected StageName is also threaded into requestContext.stage.")).addOption(new Option("--api <id>", "DEPRECATED — use the positional <target> argument instead. Same accepted forms (bare logical id, stack-qualified, Construct path, ancestor prefix). Will be removed in a future major release.")).addOption(new Option("--layer-role-arn <arn>", "Role to sts:AssumeRole before calling lambda:GetLayerVersion on every literal-ARN entry in Properties.Layers (issue #448). Use only when the dev credentials cannot read the layer — typically cross-account layers. AWS-published public layers (e.g. Lambda Powertools) are readable from every account and need no role.")).addOption(new Option("--from-state", "Read cdkd S3 state for every routed stack and substitute Ref / Fn::GetAtt / Fn::Sub / Fn::Join (and AWS pseudo parameters) in Lambda env vars with the deployed physical IDs / attributes. Off by default — pre-PR warn-and-drop semantics are preserved. Turn on for stacks already deployed via cdkd deploy. Mirrors `cdkd local invoke --from-state` / `cdkd local run-task --from-state`. Re-runs against fresh state on every hot-reload firing (--watch).").default(false)).addOption(new Option("--from-cfn-stack [cfn-stack-name]", "Read a deployed CloudFormation stack via DescribeStackResources and substitute Ref / Fn::ImportValue in Lambda env vars with the deployed physical IDs / exports. Use for CDK apps deployed via the upstream CDK CLI (`cdk deploy`). Bare form uses the cdkd stack name per routed stack; pass an explicit value when a single CFn stack should serve every routed stack. Mutually exclusive with --from-state. Fn::GetAtt is warn-and-dropped in v1 (CFn DescribeStackResources does not return per-attribute values).")).addOption(new Option("--stack-region <region>", "Region of the state record to read. Used with --from-state when the same stack name has state in multiple regions, and with --from-cfn-stack as the CFn client region (cdkd does not have a separate --cfn-stack-region flag).").argParser(parseStackRegion)).addOption(new Option("--mtls-truststore <path>", "PEM-encoded CA bundle for client-certificate verification (mutual TLS). When set, the local server switches from HTTP to HTTPS and the TLS handshake rejects clients whose certificate doesn't chain to one of these CAs. Verified certs are surfaced on the Lambda event under requestContext.identity.clientCert (REST v1) / requestContext.authentication.clientCert (HTTP API v2). Must be set together with --mtls-cert + --mtls-key; partial flag sets are rejected. Generate a CA + server + client cert for local dev: openssl req -x509 -newkey rsa:2048 -nodes -keyout ca-key.pem -out ca.pem -subj \"/CN=cdkd-local-ca\" -days 365; openssl req -newkey rsa:2048 -nodes -keyout server-key.pem -out server-csr.pem -subj \"/CN=localhost\"; openssl x509 -req -in server-csr.pem -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out server-cert.pem -days 365; openssl req -newkey rsa:2048 -nodes -keyout client-key.pem -out client-csr.pem -subj \"/CN=client\"; openssl x509 -req -in client-csr.pem -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out client-cert.pem -days 365; curl --cacert ca.pem --cert client-cert.pem --key client-key.pem https://localhost:<port>/...")).addOption(new Option("--mtls-cert <path>", "PEM-encoded server certificate for mutual TLS. Self-signed is fine for local dev. Must be set together with --mtls-truststore + --mtls-key.")).addOption(new Option("--mtls-key <path>", "PEM-encoded server private key matching --mtls-cert. Must be set together with --mtls-truststore + --mtls-cert.")).addOption(new Option("--strict-sigv4", "Opt-in: enforce strict AWS_IAM SigV4 verification. When set, requests whose signature cannot be cryptographically verified (foreign access-key-id, OR no local AWS credentials configured) are denied. DEFAULT off — warn-and-pass with a placeholder principalId, matching cdk-local's `cdkl start-api`. Enable this when you want local parity with the deployed API Gateway's signature enforcement.").default(false)).action(withErrorHandling(localStartApiCommand));
85099
85058
  [
85100
85059
  ...commonOptions,
85101
85060
  ...appOptions,
@@ -86331,7 +86290,7 @@ async function resolveSidecarCredentials(options, assumedCredentials) {
86331
86290
  if (options.profile) return resolveProfileCredentials(options.profile);
86332
86291
  }
86333
86292
  function createLocalRunTaskCommand() {
86334
- const cmd = new Command("run-task").description("Run an AWS::ECS::TaskDefinition locally — pulls/builds images, sets up a per-task docker network with the AWS-published metadata-endpoints sidecar, and starts every container in dependsOn order. Target accepts a CDK display path (MyStack/MyService/TaskDef) or stack-qualified logical ID (MyStack:MyServiceTaskDefXYZ1234). Single-stack apps may omit the stack prefix.").argument("<target>", "CDK display path or stack-qualified logical ID of the AWS::ECS::TaskDefinition to run").addOption(new Option("--cluster <name>", "Cluster name surfaced to ECS_CONTAINER_METADATA_URI_V4 and used as the docker network prefix").default("cdkd-local")).addOption(new Option("--env-vars <file>", "JSON env-var overrides (SAM-compatible: {\"ContainerName\":{\"KEY\":\"VALUE\"}, \"Parameters\":{}})")).addOption(new Option("--container-host <ip>", "Host IP to bind published container ports to. Must be a numeric IP (Docker rejects hostnames here)").default("127.0.0.1")).addOption(new Option("--assume-task-role [arn]", "Assume the task definition's TaskRoleArn (or the supplied ARN) and forward STS-issued temp credentials via the metadata sidecar so containers run with the deployed function role. Bare flag uses the template's TaskRoleArn; pass an explicit ARN to override.")).addOption(new Option("--no-pull", "Skip docker pull for every container image and the metadata sidecar")).addOption(new Option("--ecr-role-arn <arn>", "Role ARN to assume before authenticating against ECR for cross-account / centralized registries (#455). Issues sts:AssumeRole via the default credential chain and uses the temporary credentials for ecr:GetAuthorizationToken + docker pull. Required when the caller does not have direct cross-account access to the target repository. Same-account / same-region pulls do not need this flag.")).addOption(new Option("--platform <platform>", "Force docker --platform (linux/amd64 or linux/arm64). Default: inferred from task RuntimePlatform.CpuArchitecture")).addOption(new Option("--keep-running", "Don't docker rm -f the user containers on task exit (network + sidecar are still torn down). Use when you want to docker exec into a stopped container for post-mortems.").default(false)).addOption(new Option("--detach", "Start the containers in the background and exit (skip log streaming + auto teardown). Useful in CI smoke tests; caller manages container lifecycle.").default(false)).addOption(new Option("--from-state", "Read cdkd S3 state for the target stack and substitute Fn::Sub / Fn::GetAtt references to same-stack AWS::ECR::Repository resources with the deployed URI. Off by default — only the AWS pseudo-parameter tier (${AWS::AccountId} / ${AWS::Region}) is resolved without this flag.").default(false)).addOption(new Option("--from-cfn-stack [cfn-stack-name]", "Read a deployed CloudFormation stack via DescribeStackResources and substitute Ref / Fn::ImportValue in container env vars / secrets / image URIs with the deployed physical IDs / exports. Use for CDK apps deployed via the upstream CDK CLI (`cdk deploy`). Bare form uses the cdkd stack name; pass an explicit value when the CFn stack name differs. Mutually exclusive with --from-state. Fn::GetAtt is warn-and-dropped in v1 (CFn DescribeStackResources does not return per-attribute values).")).addOption(new Option("--stack-region <region>", "Region of the state record to read. Used with --from-state when the same stack name has state in multiple regions, and with --from-cfn-stack as the CFn client region (cdkd does not have a separate --cfn-stack-region flag).")).action(withErrorHandling(localRunTaskCommand));
86293
+ const cmd = new Command("run-task").description("Run an AWS::ECS::TaskDefinition locally — pulls/builds images, sets up a per-task docker network with the AWS-published metadata-endpoints sidecar, and starts every container in dependsOn order. Target accepts a CDK display path (MyStack/MyService/TaskDef) or stack-qualified logical ID (MyStack:MyServiceTaskDefXYZ1234). Single-stack apps may omit the stack prefix.").argument("<target>", "CDK display path or stack-qualified logical ID of the AWS::ECS::TaskDefinition to run").addOption(new Option("--cluster <name>", "Cluster name surfaced to ECS_CONTAINER_METADATA_URI_V4 and used as the docker network prefix").default("cdkd-local")).addOption(new Option("--env-vars <file>", "JSON env-var overrides (SAM-compatible: {\"ContainerName\":{\"KEY\":\"VALUE\"}, \"Parameters\":{}})")).addOption(new Option("--container-host <ip>", "Host IP to bind published container ports to. Must be a numeric IP (Docker rejects hostnames here)").default("127.0.0.1")).addOption(new Option("--assume-task-role [arn]", "Assume the task definition's TaskRoleArn (or the supplied ARN) and forward STS-issued temp credentials via the metadata sidecar so containers run with the deployed function role. Bare flag uses the template's TaskRoleArn; pass an explicit ARN to override.")).addOption(new Option("--no-pull", "Skip docker pull for every container image and the metadata sidecar")).addOption(new Option("--ecr-role-arn <arn>", "Role ARN to assume before authenticating against ECR for cross-account / centralized registries (#455). Issues sts:AssumeRole via the default credential chain and uses the temporary credentials for ecr:GetAuthorizationToken + docker pull. Required when the caller does not have direct cross-account access to the target repository. Same-account / same-region pulls do not need this flag.")).addOption(new Option("--platform <platform>", "Force docker --platform (linux/amd64 or linux/arm64). Default: inferred from task RuntimePlatform.CpuArchitecture")).addOption(new Option("--keep-running", "Don't docker rm -f the user containers on task exit (network + sidecar are still torn down). Use when you want to docker exec into a stopped container for post-mortems.").default(false)).addOption(new Option("--detach", "Start the containers in the background and exit (skip log streaming + auto teardown). Useful in CI smoke tests; caller manages container lifecycle.").default(false)).addOption(new Option("--from-state", "Read cdkd S3 state for the target stack and substitute Fn::Sub / Fn::GetAtt references to same-stack AWS::ECR::Repository resources with the deployed URI. Off by default — only the AWS pseudo-parameter tier (${AWS::AccountId} / ${AWS::Region}) is resolved without this flag.").default(false)).addOption(new Option("--from-cfn-stack [cfn-stack-name]", "Read a deployed CloudFormation stack via DescribeStackResources and substitute Ref / Fn::ImportValue in container env vars / secrets / image URIs with the deployed physical IDs / exports. Use for CDK apps deployed via the upstream CDK CLI (`cdk deploy`). Bare form uses the cdkd stack name; pass an explicit value when the CFn stack name differs. Mutually exclusive with --from-state. Fn::GetAtt is warn-and-dropped in v1 (CFn DescribeStackResources does not return per-attribute values).")).addOption(new Option("--stack-region <region>", "Region of the state record to read. Used with --from-state when the same stack name has state in multiple regions, and with --from-cfn-stack as the CFn client region (cdkd does not have a separate --cfn-stack-region flag).").argParser(parseStackRegion)).action(withErrorHandling(localRunTaskCommand));
86335
86294
  [
86336
86295
  ...commonOptions,
86337
86296
  ...appOptions,
@@ -87755,7 +87714,7 @@ function readEnvOverridesFile$1(filePath) {
87755
87714
  return parsed;
87756
87715
  }
87757
87716
  function createLocalInvokeAgentCoreCommand() {
87758
- const cmd = new Command("invoke-agentcore").description("Run a Bedrock AgentCore Runtime container locally and invoke it once over its protocol contract: HTTP (POST /invocations + GET /ping on 8080) or MCP (POST /mcp Streamable HTTP on 8000). Resolves the AWS::BedrockAgentCore::Runtime, pulls/builds its container, injects env vars + AWS credentials, and prints the response. For an MCP runtime, runs the session handshake then sends one JSON-RPC request (tools/list by default, or the method/params from --event). Target accepts a CDK display path (MyStack/MyAgent) or stack-qualified logical ID (MyStack:MyAgentRuntime1234). Single-stack apps may omit the stack prefix. Omit <target> in an interactive terminal to pick from a list. Supports the container artifact and the CodeConfiguration managed-runtime artifact (fromCodeAsset, built from source) on the HTTP + MCP protocols; the agent calls real AWS for managed services.").argument("[target]", "CDK display path or stack-qualified logical ID of the AgentCore Runtime to invoke (omit to pick interactively in a TTY)").addOption(new Option("-e, --event <file>", "JSON event payload file (default: {})")).addOption(new Option("--event-stdin", "Read event JSON from stdin").default(false)).addOption(new Option("--env-vars <file>", "JSON env-var overrides (SAM-compatible: {\"LogicalId\":{\"KEY\":\"VALUE\"}})")).addOption(new Option("--session-id <id>", "AgentCore runtime session id header value (default: a random UUID)")).addOption(new Option("--ws", "Stream over the HTTP-protocol agent's bidirectional /ws WebSocket endpoint (on 8080) instead of POST /invocations: send --event as the first frame and print every received frame to stdout until the agent closes. When stdin is a TTY, auto-enters a REPL — each typed line is sent as a follow-up text frame until Ctrl-D / agent close; pipe from /dev/null to force one-shot in a TTY. Ignored for an MCP runtime.").default(false)).addOption(new Option("--watch", "Re-synth and reload the agent container on CDK source changes (follows cdk-local #270). Supported on the HTTP / AGUI protocols, both the --ws session AND the default one-shot POST /invocations (the reload re-runs the single shot). An interpreted-language source edit inside a CodeConfiguration source tree takes a soft-reload fast path (docker cp + docker restart, no rebuild); Dockerfile / compiled-source / asset-hash-changed / ambiguous edits force a full rebuild. For MCP / A2A runtimes --watch is a no-op WARN and the single shot proceeds.").default(false)).addOption(new Option("--bearer-token <jwt>", "Bearer JWT to present when the runtime declares a customJwtAuthorizer. Verified against the runtime OIDC discovery URL (signature / issuer / expiry / audience) before the container starts, then forwarded to /invocations as Authorization: Bearer <jwt>.")).addOption(new Option("--no-verify-auth", "Skip inbound JWT verification even when the runtime declares a customJwtAuthorizer (local-dev escape hatch). A --bearer-token, if given, is still forwarded.")).addOption(new Option("--sigv4", "Sign the /invocations POST with AWS SigV4 (service bedrock-agentcore) using the resolved credentials, matching the cloud default when the runtime declares no customJwtAuthorizer. Opt-in: default unsigned. Mutually exclusive with --bearer-token; ignored on a JWT-protected runtime.").default(false)).addOption(new Option("--platform <platform>", "docker --platform for the agent container (linux/amd64 or linux/arm64)").choices(["linux/amd64", "linux/arm64"]).default("linux/arm64")).addOption(new Option("--no-pull", "Skip docker pull (use cached image) — no-op for the local-build path")).addOption(new Option("--no-build", "Skip docker build on the local-asset path (use the previously-built tag). No-op for the ECR / registry pull paths.")).addOption(new Option("--container-host <host>", "Host to bind the agent port to").default("127.0.0.1")).addOption(new Option("--timeout <ms>", "Per-request timeout in milliseconds. Applied to POST /invocations, POST /mcp, and the /ws open-to-close window. Raise this for long-running agent calls that exceed the default.").default(12e4).argParser(parseTimeoutMs)).addOption(new Option("--assume-role [arn]", "Assume the runtime's execution role and forward STS-issued temp credentials to the container so the agent runs with the deployed role. Three forms: (1) `--assume-role <arn>` assumes the explicit ARN; (2) `--assume-role` (bare) uses the runtime's RoleArn when it is a literal ARN; (3) `--no-assume-role` opts out. Off by default — the developer's shell credentials are forwarded unchanged.")).addOption(new Option("--ecr-role-arn <arn>", "Role ARN to assume before authenticating against ECR for cross-account / centralized registries. Same-account / same-region pulls do not need this flag.")).addOption(new Option("--from-state", "Load cdkd's S3 state for the target stack and substitute Ref / Fn::GetAtt / Fn::Sub / Fn::ImportValue in env vars with the deployed physical IDs / cross-stack exports. Mutually exclusive with --from-cfn-stack.").default(false)).addOption(new Option("--from-cfn-stack [cfn-stack-name]", "Read a deployed CloudFormation stack via DescribeStackResources and substitute Ref / Fn::ImportValue in env vars with the deployed physical IDs / exports. For CDK apps deployed via the upstream CDK CLI. Bare form uses the resolved stack name; pass an explicit value when the CFn stack name differs. Mutually exclusive with --from-state.")).addOption(new Option("--stack-region <region>", "Region of the state record to read. Used with --from-cfn-stack as the CFn client region.")).action(withErrorHandling(async (target, options) => {
87717
+ const cmd = new Command("invoke-agentcore").description("Run a Bedrock AgentCore Runtime container locally and invoke it once over its protocol contract: HTTP (POST /invocations + GET /ping on 8080) or MCP (POST /mcp Streamable HTTP on 8000). Resolves the AWS::BedrockAgentCore::Runtime, pulls/builds its container, injects env vars + AWS credentials, and prints the response. For an MCP runtime, runs the session handshake then sends one JSON-RPC request (tools/list by default, or the method/params from --event). Target accepts a CDK display path (MyStack/MyAgent) or stack-qualified logical ID (MyStack:MyAgentRuntime1234). Single-stack apps may omit the stack prefix. Omit <target> in an interactive terminal to pick from a list. Supports the container artifact and the CodeConfiguration managed-runtime artifact (fromCodeAsset, built from source) on the HTTP + MCP protocols; the agent calls real AWS for managed services.").argument("[target]", "CDK display path or stack-qualified logical ID of the AgentCore Runtime to invoke (omit to pick interactively in a TTY)").addOption(new Option("-e, --event <file>", "JSON event payload file (default: {})")).addOption(new Option("--event-stdin", "Read event JSON from stdin").default(false)).addOption(new Option("--env-vars <file>", "JSON env-var overrides (SAM-compatible: {\"LogicalId\":{\"KEY\":\"VALUE\"}})")).addOption(new Option("--session-id <id>", "AgentCore runtime session id header value (default: a random UUID)")).addOption(new Option("--ws", "Stream over the HTTP-protocol agent's bidirectional /ws WebSocket endpoint (on 8080) instead of POST /invocations: send --event as the first frame and print every received frame to stdout until the agent closes. When stdin is a TTY, auto-enters a REPL — each typed line is sent as a follow-up text frame until Ctrl-D / agent close; pipe from /dev/null to force one-shot in a TTY. Ignored for an MCP runtime.").default(false)).addOption(new Option("--watch", "Re-synth and reload the agent container on CDK source changes (follows cdk-local #270). Supported on the HTTP / AGUI protocols, both the --ws session AND the default one-shot POST /invocations (the reload re-runs the single shot). An interpreted-language source edit inside a CodeConfiguration source tree takes a soft-reload fast path (docker cp + docker restart, no rebuild); Dockerfile / compiled-source / asset-hash-changed / ambiguous edits force a full rebuild. For MCP / A2A runtimes --watch is a no-op WARN and the single shot proceeds.").default(false)).addOption(new Option("--bearer-token <jwt>", "Bearer JWT to present when the runtime declares a customJwtAuthorizer. Verified against the runtime OIDC discovery URL (signature / issuer / expiry / audience) before the container starts, then forwarded to /invocations as Authorization: Bearer <jwt>.")).addOption(new Option("--no-verify-auth", "Skip inbound JWT verification even when the runtime declares a customJwtAuthorizer (local-dev escape hatch). A --bearer-token, if given, is still forwarded.")).addOption(new Option("--sigv4", "Sign the /invocations POST with AWS SigV4 (service bedrock-agentcore) using the resolved credentials, matching the cloud default when the runtime declares no customJwtAuthorizer. Opt-in: default unsigned. Mutually exclusive with --bearer-token; ignored on a JWT-protected runtime.").default(false)).addOption(new Option("--platform <platform>", "docker --platform for the agent container (linux/amd64 or linux/arm64)").choices(["linux/amd64", "linux/arm64"]).default("linux/arm64")).addOption(new Option("--no-pull", "Skip docker pull (use cached image) — no-op for the local-build path")).addOption(new Option("--no-build", "Skip docker build on the local-asset path (use the previously-built tag). No-op for the ECR / registry pull paths.")).addOption(new Option("--container-host <host>", "Host to bind the agent port to").default("127.0.0.1")).addOption(new Option("--timeout <ms>", "Per-request timeout in milliseconds. Applied to POST /invocations, POST /mcp, and the /ws open-to-close window. Raise this for long-running agent calls that exceed the default.").default(12e4).argParser(parseTimeoutMs)).addOption(new Option("--assume-role [arn]", "Assume the runtime's execution role and forward STS-issued temp credentials to the container so the agent runs with the deployed role. Three forms: (1) `--assume-role <arn>` assumes the explicit ARN; (2) `--assume-role` (bare) uses the runtime's RoleArn when it is a literal ARN; (3) `--no-assume-role` opts out. Off by default — the developer's shell credentials are forwarded unchanged.")).addOption(new Option("--ecr-role-arn <arn>", "Role ARN to assume before authenticating against ECR for cross-account / centralized registries. Same-account / same-region pulls do not need this flag.")).addOption(new Option("--from-state", "Load cdkd's S3 state for the target stack and substitute Ref / Fn::GetAtt / Fn::Sub / Fn::ImportValue in env vars with the deployed physical IDs / cross-stack exports. Mutually exclusive with --from-cfn-stack.").default(false)).addOption(new Option("--from-cfn-stack [cfn-stack-name]", "Read a deployed CloudFormation stack via DescribeStackResources and substitute Ref / Fn::ImportValue in env vars with the deployed physical IDs / exports. For CDK apps deployed via the upstream CDK CLI. Bare form uses the resolved stack name; pass an explicit value when the CFn stack name differs. Mutually exclusive with --from-state.")).addOption(new Option("--stack-region <region>", "Region of the state record to read. Used with --from-cfn-stack as the CFn client region.").argParser(parseStackRegion)).action(withErrorHandling(async (target, options) => {
87759
87718
  await localInvokeAgentCoreCommand(target, options);
87760
87719
  }));
87761
87720
  [
@@ -88755,7 +88714,7 @@ function pickReferencedLogicalId(intrinsic) {
88755
88714
  function createLocalCommand() {
88756
88715
  setEmbedConfig(CDKD_EMBED_CONFIG);
88757
88716
  const local = new Command("local").description("Local execution of Lambda functions (RIE) and ECS task definitions (Docker required)");
88758
- const invoke = new Command("invoke").description("Run a Lambda function locally in a Docker container (RIE-backed). Target accepts a CDK display path (MyStack/MyApi/Handler) or stack-qualified logical ID (MyStack:MyApiHandler1234ABCD). Single-stack apps may omit the stack prefix.").argument("<target>", "CDK display path or stack-qualified logical ID of the Lambda to invoke").addOption(new Option("-e, --event <file>", "JSON event payload file (default: {})")).addOption(new Option("--event-stdin", "Read event JSON from stdin").default(false)).addOption(new Option("--env-vars <file>", "JSON env-var overrides (SAM-compatible: {\"LogicalId\":{\"KEY\":\"VALUE\"}})")).addOption(new Option("--no-pull", "Skip docker pull (use cached image) — no-op for IMAGE local-build path; `docker build` does not pull base layers by default")).addOption(new Option("--no-build", "Skip docker build on the IMAGE local-build path (use the previously-built tag). Requires the deterministic tag to already be in the local registry; errors with an actionable message when missing. No-op for ZIP Lambdas and the IMAGE ECR-pull path. Compatible with --no-pull.")).addOption(new Option("--debug-port <port>", "Node --inspect-brk port (default: off)")).addOption(new Option("--container-host <host>", "Host to bind the RIE port to").default("127.0.0.1")).addOption(new Option("--assume-role [arn]", "Assume the Lambda's deployed execution role and forward STS-issued temp credentials to the container so the handler runs with the deployed function's narrow permissions (closes the \"developer admin / function narrow\" skew). Three forms: (1) `--assume-role <arn>` assumes the explicit ARN; (2) `--assume-role` (bare) auto-resolves the function's execution role ARN from cdkd state (requires --from-state); (3) `--no-assume-role` explicitly opts out (forces dev creds even with --from-state). Off by default — when omitted, the developer's shell credentials are forwarded unchanged (SAM-compatible default). STS failures degrade to a warn + dev-creds fallback.")).addOption(new Option("--layer-role-arn <arn>", "Role to sts:AssumeRole before calling lambda:GetLayerVersion on every literal-ARN entry in Properties.Layers (issue #448). Use only when the dev credentials cannot read the layer — typically cross-account layers. AWS-published public layers (e.g. Lambda Powertools) are readable from every account and need no role.")).addOption(new Option("--ecr-role-arn <arn>", "Role ARN to assume before authenticating against ECR for cross-account / centralized registries (#455). Issues sts:AssumeRole via the default credential chain and uses the temporary credentials for ecr:GetAuthorizationToken + docker pull. Required when the caller does not have direct cross-account access to the target repository. Same-account / same-region pulls do not need this flag.")).addOption(new Option("--from-state", "Read cdkd S3 state for the target stack and substitute Ref / Fn::GetAtt / Fn::Sub in env vars with the deployed physical IDs / attributes. Off by default — keep PR 1 warn-and-drop semantics; turn on for stacks already deployed via cdkd deploy.").default(false)).addOption(new Option("--from-cfn-stack [cfn-stack-name]", "Read a deployed CloudFormation stack via DescribeStackResources and substitute Ref / Fn::ImportValue in env vars with the deployed physical IDs / exports. Use for CDK apps deployed via the upstream CDK CLI (`cdk deploy`). Bare form uses the cdkd stack name; pass an explicit value when CFn stack name differs. Mutually exclusive with --from-state. Fn::GetAtt is warn-and-dropped in v1 (CFn DescribeStackResources does not return per-attribute values).")).addOption(new Option("--stack-region <region>", "Region of the state record to read. Used with --from-state when the same stack name has state in multiple regions, and with --from-cfn-stack as the CFn client region (cdkd does not have a separate --cfn-stack-region flag).")).action(withErrorHandling(localInvokeCommand));
88717
+ const invoke = new Command("invoke").description("Run a Lambda function locally in a Docker container (RIE-backed). Target accepts a CDK display path (MyStack/MyApi/Handler) or stack-qualified logical ID (MyStack:MyApiHandler1234ABCD). Single-stack apps may omit the stack prefix.").argument("<target>", "CDK display path or stack-qualified logical ID of the Lambda to invoke").addOption(new Option("-e, --event <file>", "JSON event payload file (default: {})")).addOption(new Option("--event-stdin", "Read event JSON from stdin").default(false)).addOption(new Option("--env-vars <file>", "JSON env-var overrides (SAM-compatible: {\"LogicalId\":{\"KEY\":\"VALUE\"}})")).addOption(new Option("--no-pull", "Skip docker pull (use cached image) — no-op for IMAGE local-build path; `docker build` does not pull base layers by default")).addOption(new Option("--no-build", "Skip docker build on the IMAGE local-build path (use the previously-built tag). Requires the deterministic tag to already be in the local registry; errors with an actionable message when missing. No-op for ZIP Lambdas and the IMAGE ECR-pull path. Compatible with --no-pull.")).addOption(new Option("--debug-port <port>", "Node --inspect-brk port (default: off)")).addOption(new Option("--container-host <host>", "Host to bind the RIE port to").default("127.0.0.1")).addOption(new Option("--assume-role [arn]", "Assume the Lambda's deployed execution role and forward STS-issued temp credentials to the container so the handler runs with the deployed function's narrow permissions (closes the \"developer admin / function narrow\" skew). Three forms: (1) `--assume-role <arn>` assumes the explicit ARN; (2) `--assume-role` (bare) auto-resolves the function's execution role ARN from cdkd state (requires --from-state); (3) `--no-assume-role` explicitly opts out (forces dev creds even with --from-state). Off by default — when omitted, the developer's shell credentials are forwarded unchanged (SAM-compatible default). STS failures degrade to a warn + dev-creds fallback.")).addOption(new Option("--layer-role-arn <arn>", "Role to sts:AssumeRole before calling lambda:GetLayerVersion on every literal-ARN entry in Properties.Layers (issue #448). Use only when the dev credentials cannot read the layer — typically cross-account layers. AWS-published public layers (e.g. Lambda Powertools) are readable from every account and need no role.")).addOption(new Option("--ecr-role-arn <arn>", "Role ARN to assume before authenticating against ECR for cross-account / centralized registries (#455). Issues sts:AssumeRole via the default credential chain and uses the temporary credentials for ecr:GetAuthorizationToken + docker pull. Required when the caller does not have direct cross-account access to the target repository. Same-account / same-region pulls do not need this flag.")).addOption(new Option("--from-state", "Read cdkd S3 state for the target stack and substitute Ref / Fn::GetAtt / Fn::Sub in env vars with the deployed physical IDs / attributes. Off by default — keep PR 1 warn-and-drop semantics; turn on for stacks already deployed via cdkd deploy.").default(false)).addOption(new Option("--from-cfn-stack [cfn-stack-name]", "Read a deployed CloudFormation stack via DescribeStackResources and substitute Ref / Fn::ImportValue in env vars with the deployed physical IDs / exports. Use for CDK apps deployed via the upstream CDK CLI (`cdk deploy`). Bare form uses the cdkd stack name; pass an explicit value when CFn stack name differs. Mutually exclusive with --from-state. Fn::GetAtt is warn-and-dropped in v1 (CFn DescribeStackResources does not return per-attribute values).")).addOption(new Option("--stack-region <region>", "Region of the state record to read. Used with --from-state when the same stack name has state in multiple regions, and with --from-cfn-stack as the CFn client region (cdkd does not have a separate --cfn-stack-region flag).").argParser(parseStackRegion)).action(withErrorHandling(localInvokeCommand));
88759
88718
  [
88760
88719
  ...commonOptions,
88761
88720
  ...appOptions,
@@ -89888,9 +89847,10 @@ function buildProgram() {
89888
89847
  program.addCommand(createLocalCommand());
89889
89848
  program.addCommand(createExportCommand());
89890
89849
  program.addCommand(createMigrateCommand());
89850
+ guardStackRegionOptions(program);
89891
89851
  return program;
89892
89852
  }
89893
89853
 
89894
89854
  //#endregion
89895
89855
  export { buildProgram };
89896
- //# sourceMappingURL=program-DHJ4G_ZZ.js.map
89856
+ //# sourceMappingURL=program-D8-mNNcf.js.map