@go-to-k/cdkd 0.296.31 → 0.298.0

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.
Files changed (28) hide show
  1. package/dist/{asg-provider-Ca7cV9tM.js → asg-provider-B1xRE8do.js} +5 -5
  2. package/dist/{asg-provider-Ca7cV9tM.js.map → asg-provider-B1xRE8do.js.map} +1 -1
  3. package/dist/{aws-failure-text-BaWeCWRz.js → aws-failure-text-DtvlUugx.js} +2 -2
  4. package/dist/{aws-failure-text-BaWeCWRz.js.map → aws-failure-text-DtvlUugx.js.map} +1 -1
  5. package/dist/cli.js +2 -2
  6. package/dist/{deploy-engine-5FFXg6ih.js → deploy-engine-CJTG3tft.js} +1944 -469
  7. package/dist/deploy-engine-CJTG3tft.js.map +1 -0
  8. package/dist/{iam-policy-targets-Cz2tPxXk.js → iam-policy-targets-CwKiK9nS.js} +2 -2
  9. package/dist/{iam-policy-targets-Cz2tPxXk.js.map → iam-policy-targets-CwKiK9nS.js.map} +1 -1
  10. package/dist/{iam-role-provider-syetbMnw.js → iam-role-provider-qxfS4sDT.js} +24 -4
  11. package/dist/{iam-role-provider-syetbMnw.js.map → iam-role-provider-qxfS4sDT.js.map} +1 -1
  12. package/dist/index.d.ts +21 -2
  13. package/dist/index.d.ts.map +1 -1
  14. package/dist/index.js +4 -4
  15. package/dist/{neptune-provider-B1D2CFBs.js → neptune-provider-CU_1O2FR.js} +4 -4
  16. package/dist/{neptune-provider-B1D2CFBs.js.map → neptune-provider-CU_1O2FR.js.map} +1 -1
  17. package/dist/{program-Db1eBDeq.js → program-BivXyHmd.js} +584 -83
  18. package/dist/program-BivXyHmd.js.map +1 -0
  19. package/dist/{provider-classes-B7Z4-Zsc.js → provider-classes-1EHrZ847.js} +73 -23
  20. package/dist/provider-classes-1EHrZ847.js.map +1 -0
  21. package/dist/{rds-provider-B4sFJ8YW.js → rds-provider-J40R0ysf.js} +6 -6
  22. package/dist/{rds-provider-B4sFJ8YW.js.map → rds-provider-J40R0ysf.js.map} +1 -1
  23. package/dist/{version-D0cK9V1A.js → version-D96I6uiO.js} +2 -2
  24. package/dist/{version-D0cK9V1A.js.map → version-D96I6uiO.js.map} +1 -1
  25. package/package.json +3 -1
  26. package/dist/deploy-engine-5FFXg6ih.js.map +0 -1
  27. package/dist/program-Db1eBDeq.js.map +0 -1
  28. package/dist/provider-classes-B7Z4-Zsc.js.map +0 -1
@@ -1,9 +1,9 @@
1
1
  import { a as runWithStackAwsClients, i as resetAwsClients, o as setAwsClients, t as AwsClients } from "./aws-clients-CnmtbrxD.js";
2
- import { A as UNRENDERABLE, B as stringifyJsonPayload, D as SAFE_MSG_ALTERED_CHAR, F as displayStackName, I as isPasteableIdent, M as displayAwsMessage, N as displayIdent, O as SECRET_REF_MAX_CODE_POINTS, P as displaySafe, R as plainIdentOr, T as withStackName, V as truncateCodePoints, _ as derivesPatternBName, a as getLogger, f as getLiveRenderer, h as PATTERN_B_RESOURCE_TYPES, j as cutMarker, k as STACK_REF_MAX_CODE_POINTS, l as runStackBuffered, m as PATTERN_B_NAME_PROPERTIES, o as isStdoutReservedForPayload, p as PATTERN_B_NAME_OPTIONS, r as safeStringify, s as reserveStdoutForPayload, t as describeAwsFailure, v as explicitNamePropertyFor, w as withSkipPrefix, y as generateResourceName, z as safeMsg } from "./aws-failure-text-BaWeCWRz.js";
3
- import { $n as isAmbiguousOutcomeError, $t as credentialFingerprint, Bn as withheldTargetClause, D as VALUE_NOT_SHOWN, Dn as TIMEOUT_FLAG_RESOURCE_TYPE, E as assertRegionMatch, En as withErrorHandling, Ft as buildLockContentionMessage, G as runDeleteAttempt, Hn as setPasteableVerbFlags, Ht as CUSTOM_RESOURCE_RESPONSE_PREFIX, In as plainOrDescribed, It as forceQuitRecoveryClause, Jt as setPasteableAwsProfile, Kt as producerCoordinateKey, Ln as quotedOrDescribed, Lt as formatLockExpiry, Nn as isInertUnquoted, Nt as UNREPRODUCIBLE_LOCK_CLAUSE, P as remedyLogicalId, Pn as pasteableCommand, Pt as buildForceUnlockCommand, Qt as clientDefaultsFor, Rn as shellBoundedDisplay, Rt as formatLockOperation, Sn as SynthesisError, T as RESOURCE_NOT_FOUND, Tn as normalizeAwsError, Ut as DEFAULT_STATE_PREFIX, Vn as setPasteableRunFlags, Wt as injectiveKey, Xt as ambientClientDefaults, Yt as withPasteableAwsProfile, Z as isAwsCliLiteral, Zt as ambientCredentialConfig, _n as ResourceTimeoutError, ar as isMarkedNonRetryable, bn as StackTerminationProtectionError, cn as IntrinsicResolutionRefusalError, en as AccountIdUnavailableError, ft as logicalIdShown, gn as ProvisioningError, hn as PartialFailureError, in as CrossAccountSecretRefusalError, it as isInterruptedWaitError, jn as commandHole, kn as SHELL_ACTIVE_WHY, ln as LocalInvokeBuildError$1, mn as NestedStackChildDirectDestroyError, mt as resourceTypeShown, n as RESOLVED_PREVIOUS_PRINCIPAL_KEYS, nn as CdkdError, nr as isTransientServerError, nt as endCommandInterruptScope, on as DeployCancelledError, or as isThrottlingError, pn as MalformedProducerRecordRefusalError, qt as producerRecordKey, rr as retryClassificationText, rt as interruptWatchListenerCount, s as readRecordedPrincipals, sn as DynamicReferenceRegionAmbiguousError, sr as markNonRetryable, t as IAM_PRINCIPAL_NAME, tr as isRetryableTransientError, tt as beginCommandInterruptScope, un as LocalStartServiceError, vn as ResourceUpdateNotSupportedError, yn as StackHasActiveImportsError, zn as shellQuote, zt as formatLockOwner } from "./iam-policy-targets-Cz2tPxXk.js";
2
+ import { A as UNRENDERABLE, B as stringifyJsonPayload, D as SAFE_MSG_ALTERED_CHAR, F as displayStackName, H as truncateCodePoints, I as isPasteableIdent, M as displayAwsMessage, N as displayIdent, O as SECRET_REF_MAX_CODE_POINTS, P as displaySafe, R as plainIdentOr, T as withStackName, _ as derivesPatternBName, a as getLogger, f as getLiveRenderer, h as PATTERN_B_RESOURCE_TYPES, j as cutMarker, k as STACK_REF_MAX_CODE_POINTS, l as runStackBuffered, m as PATTERN_B_NAME_PROPERTIES, o as isStdoutReservedForPayload, p as PATTERN_B_NAME_OPTIONS, r as safeStringify, s as reserveStdoutForPayload, t as describeAwsFailure, v as explicitNamePropertyFor, w as withSkipPrefix, y as generateResourceName, z as safeMsg } from "./aws-failure-text-DtvlUugx.js";
3
+ import { $n as isAmbiguousOutcomeError, $t as credentialFingerprint, Bn as withheldTargetClause, D as VALUE_NOT_SHOWN, Dn as TIMEOUT_FLAG_RESOURCE_TYPE, E as assertRegionMatch, En as withErrorHandling, Ft as buildLockContentionMessage, G as runDeleteAttempt, Hn as setPasteableVerbFlags, Ht as CUSTOM_RESOURCE_RESPONSE_PREFIX, In as plainOrDescribed, It as forceQuitRecoveryClause, Jt as setPasteableAwsProfile, Kt as producerCoordinateKey, Ln as quotedOrDescribed, Lt as formatLockExpiry, Nn as isInertUnquoted, Nt as UNREPRODUCIBLE_LOCK_CLAUSE, P as remedyLogicalId, Pn as pasteableCommand, Pt as buildForceUnlockCommand, Qt as clientDefaultsFor, Rn as shellBoundedDisplay, Rt as formatLockOperation, Sn as SynthesisError, T as RESOURCE_NOT_FOUND, Tn as normalizeAwsError, Ut as DEFAULT_STATE_PREFIX, Vn as setPasteableRunFlags, Wt as injectiveKey, Xt as ambientClientDefaults, Yt as withPasteableAwsProfile, Z as isAwsCliLiteral, Zt as ambientCredentialConfig, _n as ResourceTimeoutError, ar as isMarkedNonRetryable, bn as StackTerminationProtectionError, cn as IntrinsicResolutionRefusalError, en as AccountIdUnavailableError, ft as logicalIdShown, gn as ProvisioningError, hn as PartialFailureError, in as CrossAccountSecretRefusalError, it as isInterruptedWaitError, jn as commandHole, kn as SHELL_ACTIVE_WHY, ln as LocalInvokeBuildError$1, mn as NestedStackChildDirectDestroyError, mt as resourceTypeShown, n as RESOLVED_PREVIOUS_PRINCIPAL_KEYS, nn as CdkdError, nr as isTransientServerError, nt as endCommandInterruptScope, on as DeployCancelledError, or as isThrottlingError, pn as MalformedProducerRecordRefusalError, qt as producerRecordKey, rr as retryClassificationText, rt as interruptWatchListenerCount, s as readRecordedPrincipals, sn as DynamicReferenceRegionAmbiguousError, sr as markNonRetryable, t as IAM_PRINCIPAL_NAME, tr as isRetryableTransientError, tt as beginCommandInterruptScope, un as LocalStartServiceError, vn as ResourceUpdateNotSupportedError, yn as StackHasActiveImportsError, zn as shellQuote, zt as formatLockOwner } from "./iam-policy-targets-CwKiK9nS.js";
4
4
  import { n as awsClientDefaults, r as getAssumedRoleCredentials, u as formatAuthority } from "./aws-client-defaults-D0q0z4dG.js";
5
- import { $c as processStackMessages, $i as malformedScrubResourceEntriesRefusalMessage, $o as inheritNestedStackParameterAssociations, $r as envCredentialsAreAssumedRole, $s as parseBootstrapMarker, $t as orphanDeleteNeedsIdentity, A as noEchoExactEchoLeavesOf, Aa as repairMalformedResourcePropertiesForReadOnly, Ac as resolveApp, Ai as displayLogicalId, An as maskEventTextWithBoundBags, Ao as valueAtCoordinate, As as hasReadableExportSet, At as isHandledOrphan, Ba as NO_CC_HANDLERS_REASON, Bc as CFN_TEMPLATE_BODY_LIMIT, Bi as malformedLocalResourceEntriesWarning, Bn as ccRoutedFinalSnapshotError, Bo as shareLogOnlyValues, Br as poisonRenderedSpellingsCollidingIn, Bs as isSensitiveAttributeName, Bt as withStackRecords, C as recordRetainedForReadoption, Ca as refuseMalformedResourcesForDestroy, Cc as AssetManifestLoader, Ci as JSON_LISTING_HOLE_VALUE, Cn as collectPublishedOutputNames, Co as maskedLeafCoordinatesOf, Cs as crossPrefixDeniedWarning, Ct as revertedNestedRowIds, D as withResourceDeadline, Da as repairMalformedOrphansForReadOnly, Dc as getDefaultStateBucketName, Di as UNREADABLE_RESOURCES_MAP_ROW, Dn as DEFAULT_RESOURCE_WARN_AFTER_MS, Dr as deleteSkipReason, Ds as acceptedCreateOnlyDropsOf, Dt as withNestedStackContext, E as serviceTokenBlockingReason, Ea as repairMalformedOrphanRecordsForReadOnly, Ec as synthesisStatusMessage, Ei as UNREADABLE_ORPHANS_CONTAINER_ROW, En as DEFAULT_RESOURCE_TIMEOUT_MS, Eo as noEchoOutputsComparison, Er as deleteLeftInPlace, Es as splitImportedOps, F as isProtectionValueActive, Fa as withheldAccountClause, Fc as resolveStateBucketWithDefaultAndSource, Fi as isReadableResourceEntry, Fn as storedSecretNameNeedles, Fo as literalSplitDelimitersOf, Fs as isReadableBag, Ft as replayFailedOperations, Ga as isStatefulRecreateTargetForReplace, Gc as CUSTOM_RESOURCE_RESPONSE_OBJECT_DESCRIPTION, Gi as malformedOrphansRefusalMessage, Gn as unsupportedFinalSnapshotError, Go as markSameGenerationBag, Gr as assertFlagRoleArn, Gs as loadPublishableAssetManifest, Gt as isReplacementOrphan, Ha as findReplacedReadersOfRecreateTargets, Hc as MIGRATE_TMP_PREFIX, Hi as malformedOrphanRecordsRefusalMessage, Hn as effectiveDeletionPolicy, Ho as errorCauseChain, Hr as producerRegionsFromState, Hs as WorkGraph, Ht as recordAfterRollbackUpdate, I as perType, Ia as findActionableSilentDrops, Ic as resolveUseCdkBootstrapAssets, Ii as malformedDriftResourcePropertiesWarning, In as extractDeploymentEventError, Io as logOnlyValueCount, Is as orphansAfterRollback, It as replayRollback, Jc as PARTITION_TABLE, Ji as malformedOutputsWarning, Jn as findDestructiveChanges, Jo as DYNAMIC_REFERENCE_TOKEN_SCAN, Jr as isIamRoleArn, Js as BOOTSTRAP_MARKER_PREFIX, Jt as recheckFailedPlan, Ka as isStatefulRecreateTargetSync, Kc as decodeListingKey, Ki as malformedOrphansWarning, Kn as maskingRetryLogger, Ko as redactSecretsForState, Kr as assertSendableRoleArn, Ks as rewriteTemplateAssetReferences, Kt as planFailedOps, L as readProtection, Lc as stateBucketExistenceConfirmed, Li as malformedExportNamesWarning, Ln as ATOMIC_FINAL_SNAPSHOT_TYPES, Lo as printingCorpusOf, Lr as DiffCalculator, Ls as orphansCarriedFrom, M as recordedProtectionEvidence, Ma as unpreviewableOrphanRecords, Mc as resolveCaptureObservedState, Mi as hasReadableOutputs, Mn as secretNamePrintingBag, Mo as carryLogOnlyValues, Ms as hasUnverifiableParameterRefusal, Mt as loadJournaledOrphans, N as recordedProtectionNote, Na as unreadableOrphanRecords, Nc as resolveSkipPrefix, Ni as hasReadableResources, Nn as secretNamedRecordNeedles, No as createUnionSecretMasker, Ns as importableOutputKeys, Nt as makeForeignHolderScan, O as noEchoAttributeNamesOf, Oa as repairMalformedOutputsForReadOnly, Oc as getLegacyStateBucketName, Oi as accountArgs, On as completedReplayEntries, Oo as readbackPathFor, Os as describeRegionValueKind, Ot as deleteJournaledOrphans, P as PROTECTION_PROPERTY_BY_TYPE, Pa as unreadableResourceEntries, Pc as resolveStateBucketWithDefault, Pi as isReadableOrphanRecord, Pn as stateSecretNameNeedles, Po as hasMaskableValues, Ps as importableOutputs, Pt as sameJournaledOrphans, Qc as StageLoadError, Qi as malformedResourcesWarning, Qn as ledgerForStack, Qo as identityKeyFor, Qr as callerEnvCredentials, Qs as isCrossRegionRedirect, Qt as seedsDeleteFirstGuard, Ra as unwrittenCreateOnlyReplacement, Rc as warnDeprecatedNoPrefixCliFlag, Ri as malformedExportSourceWarning, Rn as PRE_DELETE_SNAPSHOT_TYPES, Ro as recordLogOnlyParameterValue, Rr as INTRINSIC_KEYS, Rs as shouldRetainResource, Rt as destroyStackRecordsView, S as keptForReadoption, Sa as refuseMalformedResourcesForDeploy, Sc as stripControlChars, Sn as collectDeclaredOutputNames, So as maskWholeValue, Sr as slowCcOperationTimeoutMs, Ss as applyCrossPrefixScan, T as findServiceTokenRefusals, Ta as refuseMalformedState, Tc as Synthesizer, Ti as STATE_RESOURCES_MALFORMED, Tn as bindingSkippedOutputs, To as noEchoLeavesOf, Tr as deleteIndeterminateGuards, Ts as isNotImplemented, Tt as collectNestedChildUnaddressed, Ua as shownType, Uc as findLargeInlineResources, Ui as malformedOrphanRecordsWarning, Un as isFinalSnapshotError, Uo as maskSecretsInError, Ur as regionLessSecretName, Us as buildAssetRedirectMap, Ut as demoteSupersededOrphans, Va as hasNoCloudControlHandlers, Vc as CFN_TEMPLATE_URL_LIMIT, Vi as malformedLocalResourcesWarning, Vn as createPreDeleteFinalSnapshot, Vo as createSecretMasker, Vr as classifyReplaySecretRegion, Vs as stringifyValue, Vt as removeProtectionTypeList, Wa as MULTI_REGION_RECREATE_BLOCKED_TYPES, Wc as uploadCfnTemplate, Wi as malformedOrphanRowsKeptWarning, Wn as refusesFinalSnapshot, Wo as maskSecretsInText, Wr as applyRoleArnIfSet, Ws as createAssetRedirectResolver, Wt as isJournaledOrphan, Xc as derivePartitionAndUrlSuffix, Xi as malformedResourceEntriesWarning, Xn as RollbackInlinePolicyWriters, Xo as isUncertifiedBaselineMaskPosition, Xr as AWS_CREDENTIAL_ENV_KEYS, Xs as ensureAssetStorage, Xt as replacementNeverSwapped, Yc as canonicalizeRegion, Yi as malformedRenderedContainersWarning, Yn as formatDestructiveChange, Yo as dynamicReferenceTokens, Yr as sendableAssumeRoleCommand, Ys as assertAssetBucketRegion, Yt as recordUnderIdIsNotOwn, Zc as AssemblyReader, Zi as malformedResourcePropertiesWarning, Zo as pathCrossesDottedKey, Zr as applyCallerIdentityCredentials, Zs as getBootstrapMarkerKey, Zt as resolveReplacementOldType, _ as makeCanonicalizePropertiesFn, _a as refuseMalformedResourceEntriesForOrphan, _c as redactedDockerCause, _i as isStalePlaceholderArnAttribute, _n as carriedExportAliasExposure, _o as TemplateParser, _r as readsNoEchoDeleteValues, _s as isLockInfoExpired, _t as displacedPhysicalIdShown, a as findNestedTemplateTreeDefect, aa as refuseMalformedOrphanRecordsForDestroy, ac as resolveDockerContextDirectory, al as absoluteAssemblyPathEscape, as as recordMaskOnlyValuesIn, at as gray, ba as refuseMalformedResourcePropertiesForExport, bc as warnFinchArgvExposure, bi as withSharedDrainBudget, bn as isWholeDynamicReferenceValue, bo as maskAtCoordinates, br as withPrintingSecrets, bs as CrossPrefixReadError, bt as nestedChildStackName$1, c as renderNestedTemplateTreeDefect, ca as refuseMalformedOrphansForOrphan, cc as describeDockerFailure, ci as parameterTypeMayLoseSecretIdentity, cl as renderAssemblyPathEscape, cn as updatePartialReason, cs as recordProvenPublicExpression, ct as yellow, d as conditionInputsFrom, da as refuseMalformedResourceAttributesForOrphan, dc as formatDockerLoginError, di as carriesDynamicReference, dl as resolveBucketRegion, dn as WITHHELD_NAME_DISPLAY, do as describeTypeWithThrottleRetry, dr as CC_BROKEN_REASON, ds as STATE_SOURCED_CROSS_GENERATION_RULES, dt as reachableDivergentParameters, ea as malformedStateRefusalMessage, ec as readBootstrapMarkerBody, el as defineOwnKey, en as findNestedStackTypeChanges, er as withCreateTokenLedger, es as recordNestedStackParameterExpressions, f as parentSuppliedValues, fa as refuseMalformedResourceEntries, fc as getDockerCmd, fi as carriesFetchableDynamicReference, fn as displayTextOrWithheld, fo as withRetry, fr as ProviderRegistry, fs as STATE_SOURCED_READBACK_RULES, ft as resourcesNamingDeclaredParameter, g as mergeNoChangeOutputs, ga as refuseMalformedResourceEntriesForImportSave, gc as redactDockerArgvValues, gi as cfnRefValueFromPhysicalId, gn as secretSafeKeyDisplay, go as DagBuilder, gr as TemplateNoEchoReresolver, gs as LockManager, gt as displacedOpClause, h as keptWholeReasonText, ha as refuseMalformedResourceEntriesForImport, hc as partitionSensitiveEnv, hi as AWS_NO_VALUE, hn as secretBearingStateKeyWarning, hr as wouldReturnToSdkProvider, hs as isSingleDynamicReferenceToken, ht as NESTED_PENDING_PARENT_REASON, i as childTemplateLoader, ia as refuseMalformedOrphanRecords, ic as buildDockerImage, il as ownValue, is as recordMaskOnlyValue, it as cyan, j as noEchoParameterNamesOf, ja as repairMalformedResourcesForReadOnly, jc as resolveAutoAssetStorage, ji as hasReadableOrphans, jn as orphanRecordsPrintingBag, jo as liveMatchesUnresolvedTokenFrame, js as hasReasonlessBaselineRefusal, jt as journaledOrphanLines, k as noEchoComparisonForTemplate, ka as repairMalformedResourceEntriesForReadOnly, kc as loadCdkJson, ki as deployRefusesOrphanRowsReason, kn as journaledOrphanPrintingBag, ko as readsNoEchoSource, kr as isWaitAbandonedError, ks as exportNamesCarriedFrom, kt as dropFailedHint, l as templateIdentity, la as refuseMalformedOutputs, lc as dockerSpawnEnvWithSensitive, li as getAccountInfo, ll as resolveAssemblyPath, ln as DeploymentEventsReader, lo as prefetchCreateOnlyPropertyPaths, ls as wholeStringLeavesOf, lt as computeParameterTaint, m as isSecretBearingReferenceString, ma as refuseMalformedResourceEntriesForDestroy, mc as isMalformedEnvKey, mi as keepsSecretReferenceToken, mn as secretBearing, mr as isCustomResource, ms as WHOLE_DYNAMIC_REFERENCE_PATTERN, mt as planOrphanAdoption, n as IMPLICIT_DELETE_DEPENDENCIES, na as refuseDivergentRecordRegionForDestroy, nc as validateContainerRepoName, nl as hasPlainPrototype, nn as inheritProducerRegions, ns as carriesSecretMask, nt as createReadOnlyAttributeHealerFactory, o as isAbsoluteAssetPath, oa as refuseMalformedOrphans, oc as describeDockerCapturedOutput, oi as coerceParameterTypedValue, ol as describeFileReadFailure, os as recordNoEchoAttributeValues, ot as green, p as readRecordedConditionVerdicts, pa as refuseMalformedResourceEntriesForDeploy, pc as isDockerClientEnvKey, pi as isNamelessDynamicReferenceError, pn as exportAliasCollisionScrubWarning, pr as ccBrokenReason, ps as TEMPLATE_SOURCED_RULES, pt as makeSiblingClaimReader, qa as renderStatefulReason, qc as expectedOwnerParam, qi as malformedOutputsRefusalMessage, qn as constructPathOf, qo as scrubResourceRecord, qr as explicitRoleArnOrThrow, qs as AssetModeResolver, qt as planRollback, r as computeImplicitDeleteEdges, rc as buildDenyExternalAccessPolicy, rl as nullPrototypeRecord, rs as provenPublicValue, rt as bold, s as listNestedTemplateRows, sa as refuseMalformedOrphansForDestroy, sc as describeDockerExecFailure, si as isUnboundTemplateParameter, sl as displayAssemblyPath, sn as updatePartialMessage, ss as recordNoEchoParameterFreshValue, st as red, t as DeployEngine, ta as previewableOrphanRecords, tc as validateAssetBucketName, tl as hasOwnKey, tt as formatResourceLine, u as conditionFingerprint, ua as refuseMalformedOutputsForDestroy, uc as finchSecretArgvRefusal, ui as isImpossibleEmptyStoredAttribute, un as DeploymentEventsStore, uo as templateResourceTypes, us as STATE_SOURCED_BASELINE_RULES, ut as namesAnyDeclaredParameter, va as refuseMalformedResourcePropertiesForDestroy, vc as runDockerForeground, vi as refStateLookupFromResource, vn as exportNameNoEchoParameters, vo as canonicalCoordinates, vr as isCustomResourceType$1, vs as S3StateBackend, vt as dropSettledNestedJournals, w as deferredServiceTokenWarning, wa as refuseMalformedResourcesForExport, wc as getDockerImageBySourceHash, wi as STATE_REGION_DIVERGED, wn as isExportAliasCollision, wo as noEchoCoordinatesOf, wr as UNSPECIFIED_SKIP_REASON, ws as isAccessDenied, wt as withNestedRevertRun, xa as refuseMalformedResourcePropertiesForOrphan, xc as escapeRegExp, xi as parameterInputsFor, xn as noEchoParameterValueSeed, xo as maskReadbackAtCoordinates, xr as CloudControlProvider, xs as CrossPrefixScanCache, xt as recordDisplacedSkips, ya as refuseMalformedResourcePropertiesForDrift, yc as runDockerStreaming, yn as exportNameSecretExposure, yo as isMarkedCoordinate, ys as rebuildClientForBucketRegion, zc as ssmResolvedValueType, zi as malformedLocalOutputsWarning, zn as buildFinalSnapshotIdentifier, zo as recordLogOnlyValue, zr as IntrinsicFunctionResolver, zs as AssetPublisher } from "./deploy-engine-5FFXg6ih.js";
6
- import { t as getCdkdVersion } from "./version-D0cK9V1A.js";
5
+ import { $a as isStatefulRecreateTargetForReplace, $c as CFN_TEMPLATE_URL_LIMIT, $i as malformedOrphansRefusalMessage, $n as ledgerForStack, $o as markSameGenerationBag, $r as assertSendableRoleArn, $s as stringifyValue, $t as seedsDeleteFirstGuard, A as noEchoComparisonForTemplate, Aa as refuseMalformedResourcesForDeploy, Ac as runDockerForeground, Ai as declaresNoEcho, An as journaledOrphanPrintingBag, Ao as maskWholeValue, Ar as deleteIndeterminateGuards, As as S3StateBackend, At as dropFailedHint, Ba as unpreviewableOrphanRecords, Bc as getLegacyStateBucketName, Bi as hasReadableOutputs, Bn as PRE_DELETE_SNAPSHOT_TYPES, Bo as carryLogOnlyValues, Bs as describeRegionValueKind, C as recordRetainedForReadoption, Ca as refuseMalformedResourceEntriesForImport, Cc as formatDockerLoginError, Ci as cfnRefValueFromPhysicalId, Cl as resolveBucketRegion, Cn as collectDeclaredOutputNames, Cr as applyCustomResourceResponseExpiration, Cs as STATE_SOURCED_CROSS_GENERATION_RULES, D as withResourceDeadline, Da as refuseMalformedResourcePropertiesForDrift, Dc as partitionSensitiveEnv, Di as withSharedDrainBudget, Dn as DEFAULT_RESOURCE_TIMEOUT_MS, Do as isMarkedCoordinate, Ds as isSingleDynamicReferenceToken, E as serviceTokenBlockingReason, Ea as refuseMalformedResourcePropertiesForDestroy, Ec as isMalformedEnvKey, En as bindingSkippedOutputs, Eo as canonicalCoordinates, Er as slowCcOperationTimeoutMs, Es as WHOLE_DYNAMIC_REFERENCE_PATTERN, Et as collectNestedChildUnaddressed, F as PROTECTION_PROPERTY_BY_TYPE, Fa as repairMalformedOrphansForReadOnly, Fc as AssetManifestLoader, Fi as UNREADABLE_RESOURCES_MAP_ROW, Fn as secretNamedRecordNeedles, Fs as crossPrefixDeniedWarning, Ft as sameJournaledOrphans, Gc as resolveSkipPrefix, Gi as malformedExportNamesWarning, Gn as isFinalSnapshotError, Go as printingCorpusOf, Gr as poisonRenderedSpellingsCollidingIn, Gs as importableOutputKeys, Gt as isJournaledOrphan, Ha as unreadableResourceEntries, Hc as resolveApp, Hi as isReadableOrphanRecord, Hn as ccRoutedFinalSnapshotError, Ho as hasMaskableValues, Hr as DiffCalculator, Hs as hasReadableExportSet, Ht as removeProtectionTypeList, I as isProtectionValueActive, Ia as repairMalformedOutputsForReadOnly, Ic as getDockerImageBySourceHash, Ii as accountArgs, In as stateSecretNameNeedles, Io as readbackPathFor, Is as isAccessDenied, It as replayFailedOperations, Ja as NO_CC_HANDLERS_REASON, Jc as resolveUseCdkBootstrapAssets, Ji as malformedLocalResourceEntriesWarning, Jn as maskingRetryLogger, Jo as shareLogOnlyValues, Jr as regionLessSecretName, Js as orphansAfterRollback, Jt as planRollback, Ka as unwrittenCreateOnlyReplacement, Kc as resolveStateBucketWithDefault, Ki as malformedExportSourceWarning, Kn as refusesFinalSnapshot, Ko as recordLogOnlyParameterValue, Kr as classifyReplaySecretRegion, Ks as importableOutputs, Kt as isReplacementOrphan, L as perType, La as repairMalformedResourceEntriesForReadOnly, Lc as Synthesizer, Li as deployRefusesOrphanRowsReason, Ln as storedSecretNameNeedles, Lo as readsNoEchoSource, Ls as isNotImplemented, Lt as replayRollback, M as noEchoParameterNamesOf, Ma as refuseMalformedResourcesForExport, Mc as warnFinchArgvExposure, Mi as STATE_REGION_DIVERGED, Mn as orphanRecordsPrintingBag, Mo as noEchoCoordinatesOf, Mr as deleteSkipReason, Ms as CrossPrefixReadError, Mt as journaledOrphanLines, N as recordedProtectionEvidence, Na as refuseMalformedState, Nc as escapeRegExp, Ni as STATE_RESOURCES_MALFORMED, Nn as secretNamePrintingBag, No as noEchoLeavesOf, Ns as CrossPrefixScanCache, Nt as loadJournaledOrphans, O as diffSecurity, Oa as refuseMalformedResourcePropertiesForExport, Oc as redactDockerArgvValues, Oi as parameterInputsFor, On as DEFAULT_RESOURCE_WARN_AFTER_MS, Oo as maskAtCoordinates, Or as RollbackInlinePolicyWriters, Os as LockManager, Ot as withNestedStackContext, P as recordedProtectionNote, Pa as repairMalformedOrphanRecordsForReadOnly, Pc as stripControlChars, Pi as UNREADABLE_ORPHANS_CONTAINER_ROW, Pn as secretNamePrintingBags, Po as noEchoOutputsComparison, Pr as isWaitAbandonedError, Ps as applyCrossPrefixScan, Pt as makeForeignHolderScan, Qa as MULTI_REGION_RECREATE_BLOCKED_TYPES, Qc as CFN_TEMPLATE_BODY_LIMIT, Qi as malformedOrphanRowsKeptWarning, Qo as maskSecretsInText, Qr as assertFlagRoleArn, Qs as isSensitiveAttributeName, Qt as resolveReplacementOldType, R as readProtection, Ra as repairMalformedResourcePropertiesForReadOnly, Rc as synthesisStatusMessage, Ri as displayLogicalId, Rn as extractDeploymentEventError, Ro as valueAtCoordinate, Rs as splitImportedOps, S as keptForReadoption, Sa as refuseMalformedResourceEntriesForDestroy, Sc as finchSecretArgvRefusal, Si as AWS_NO_VALUE, Sn as noEchoParameterValueSeed, Sr as CUSTOM_RESOURCE_RESPONSE_LIFECYCLE_RULE_ID, Ss as STATE_SOURCED_BASELINE_RULES, St as recordDisplacedSkips, T as findServiceTokenRefusals, Ta as refuseMalformedResourceEntriesForOrphan, Tc as isDockerClientEnvKey, Ti as refStateLookupFromResource, Tn as isExportAliasCollision, To as TemplateParser, Tr as CloudControlProvider, Ts as TEMPLATE_SOURCED_RULES, Tt as withNestedRevertRun, Ua as withheldAccountClause, Uc as resolveAutoAssetStorage, Ui as isReadableResourceEntry, Un as createPreDeleteFinalSnapshot, Uo as literalSplitDelimitersOf, Ur as INTRINSIC_KEYS, Us as hasReasonlessBaselineRefusal, Ut as recordAfterRollbackUpdate, Va as unreadableOrphanRecords, Vc as loadCdkJson, Vi as hasReadableResources, Vn as buildFinalSnapshotIdentifier, Vo as createUnionSecretMasker, Vs as exportNamesCarriedFrom, Vt as withStackRecords, Wa as findActionableSilentDrops, Wc as resolveCaptureObservedState, Wi as malformedDriftResourcePropertiesWarning, Wn as effectiveDeletionPolicy, Wo as logOnlyValueCount, Wr as IntrinsicFunctionResolver, Ws as hasUnverifiableParameterRefusal, Wt as demoteSupersededOrphans, Xa as findReplacedReadersOfRecreateTargets, Xc as warnDeprecatedNoPrefixCliFlag, Xi as malformedOrphanRecordsRefusalMessage, Xn as findDestructiveChanges, Xo as errorCauseChain, Xr as refuseUnreadableCdkCrossAccountRefs, Xs as shouldRetainResource, Xt as recordUnderIdIsNotOwn, Ya as hasNoCloudControlHandlers, Yc as stateBucketExistenceConfirmed, Yi as malformedLocalResourcesWarning, Yn as constructPathOf, Yo as createSecretMasker, Yr as producerLookupOver, Ys as orphansCarriedFrom, Yt as recheckFailedPlan, Za as shownType, Zc as ssmResolvedValueType, Zi as malformedOrphanRecordsWarning, Zn as formatDestructiveChange, Zo as maskSecretsInError, Zr as applyRoleArnIfSet, Zs as AssetPublisher, Zt as replacementNeverSwapped, _ as makeCanonicalizePropertiesFn, _a as refuseMalformedOutputs, _c as resolveDockerContextDirectory, _i as isImpossibleEmptyStoredAttribute, _l as absoluteAssemblyPathEscape, _n as secretSafeKeyDisplay, _o as prefetchCreateOnlyPropertyPaths, _r as TemplateNoEchoReresolver, _s as recordMaskOnlyValuesIn, _t as displacedOpClause, a as findNestedTemplateTreeDefect, aa as malformedResourcePropertiesWarning, ac as AssetModeResolver, ai as callerEnvCredentials, al as expectedOwnerParam, as as DYNAMIC_REFERENCE_TOKEN_SCAN, at as cyan, ba as refuseMalformedResourceEntries, bc as describeDockerFailure, bi as isNamelessDynamicReferenceError, bl as renderAssemblyPathEscape, bn as exportNameSecretExposure, bo as withRetry, bs as recordProvenPublicExpression, c as renderNestedTemplateTreeDefect, ca as malformedStateRefusalMessage, cc as ensureAssetStorage, cl as derivePartitionAndUrlSuffix, cn as updatePartialMessage, cs as pathCrossesDottedKey, ct as red, d as conditionInputsFrom, dc as parseBootstrapMarker, dl as processStackMessages, dn as DeploymentEventsStore, ds as recordNestedStackParameterExpressions, dt as namesAnyDeclaredParameter, ea as malformedOrphansWarning, ec as WorkGraph, ei as explicitRoleArnOrThrow, el as MIGRATE_TMP_PREFIX, en as orphanDeleteNeedsIdentity, eo as isStatefulRecreateTargetSync, es as redactSecretsForState, f as parentSuppliedValues, fa as refuseMalformedOrphanRecords, fc as readBootstrapMarkerBody, fl as defineOwnKey, fn as WITHHELD_NAME_DISPLAY, fr as CC_BROKEN_REASON, ft as reachableDivergentParameters, g as mergeNoChangeOutputs, ga as refuseMalformedOrphansForOrphan, gc as buildDockerImage, gi as getAccountInfo, gl as ownValue, gn as secretBearingStateKeyWarning, gr as wouldReturnToSdkProvider, gs as recordMaskOnlyValue, gt as NESTED_PENDING_PARENT_REASON, h as keptWholeReasonText, ha as refuseMalformedOrphansForDestroy, hc as buildDenyExternalAccessPolicy, hi as parameterTypeMayLoseSecretIdentity, hl as nullPrototypeRecord, hn as secretBearing, hr as isCustomResource, hs as provenPublicValue, ht as planOrphanAdoption, i as childTemplateLoader, ia as malformedResourceEntriesWarning, ic as rewriteTemplateAssetReferences, ii as applyCallerIdentityCredentials, il as decodeListingKey, is as recordPhysicalNameCopy, it as bold, j as noEchoExactEchoLeavesOf, ja as refuseMalformedResourcesForDestroy, jc as runDockerStreaming, ji as JSON_LISTING_HOLE_VALUE, jn as maskEventTextWithBoundBags, jo as maskedLeafCoordinatesOf, jr as deleteLeftInPlace, js as rebuildClientForBucketRegion, jt as isHandledOrphan, k as noEchoAttributeNamesOf, ka as refuseMalformedResourcePropertiesForOrphan, kc as redactedDockerCause, kn as completedReplayEntries, ko as maskReadbackAtCoordinates, kr as UNSPECIFIED_SKIP_REASON, ks as isLockInfoExpired, kt as deleteJournaledOrphans, l as templateIdentity, la as previewableOrphanRecords, lc as getBootstrapMarkerKey, ll as AssemblyReader, ln as updatePartialReason, ls as identityKeyFor, lt as yellow, m as isSecretBearingReferenceString, ma as refuseMalformedOrphans, mc as validateContainerRepoName, mi as isUnboundTemplateParameter, ml as hasPlainPrototype, mn as exportAliasCollisionScrubWarning, mr as ccBrokenReason, ms as carriesSecretMask, mt as makeSiblingClaimReader, n as IMPLICIT_DELETE_DEPENDENCIES, na as malformedOutputsWarning, nc as createAssetRedirectResolver, ni as sendableAssumeRoleCommand, nl as uploadCfnTemplate, ns as withPhysicalNameCopies, nt as formatResourceLine, o as isAbsoluteAssetPath, oa as malformedResourcesWarning, oc as BOOTSTRAP_MARKER_PREFIX, oi as envCredentialsAreAssumedRole, ol as PARTITION_TABLE, os as dynamicReferenceTokens, ot as gray, p as readRecordedConditionVerdicts, pa as refuseMalformedOrphanRecordsForDestroy, pc as validateAssetBucketName, pi as coerceParameterTypedValue, pl as hasOwnKey, pn as displayTextOrWithheld, pr as ProviderRegistry, ps as splitGetAttStringForm, pt as resourcesNamingDeclaredParameter, qc as resolveStateBucketWithDefaultAndSource, qi as malformedLocalOutputsWarning, qn as unsupportedFinalSnapshotError, qo as recordLogOnlyValue, qr as producerRegionsFromState, qs as isReadableBag, qt as planFailedOps, r as computeImplicitDeleteEdges, ra as malformedRenderedContainersWarning, rc as loadPublishableAssetManifest, ri as AWS_CREDENTIAL_ENV_KEYS, rl as CUSTOM_RESOURCE_RESPONSE_OBJECT_DESCRIPTION, rn as inheritProducerRegions, rt as createReadOnlyAttributeHealerFactory, s as listNestedTemplateRows, sa as malformedScrubResourceEntriesRefusalMessage, sc as assertAssetBucketRegion, sl as canonicalizeRegion, ss as isUncertifiedBaselineMaskPosition, st as green, t as DeployEngine, ta as malformedOutputsRefusalMessage, tc as buildAssetRedirectMap, ti as isIamRoleArn, tl as findLargeInlineResources, tn as findNestedStackTypeChanges, to as renderStatefulReason, tr as withCreateTokenLedger, ts as scrubResourceRecord, u as conditionFingerprint, ua as refuseDivergentRecordRegionForDestroy, uc as isCrossRegionRedirect, ul as StageLoadError, un as DeploymentEventsReader, us as inheritNestedStackParameterAssociations, ut as computeParameterTaint, va as refuseMalformedOutputsForDestroy, vc as describeDockerCapturedOutput, vi as carriesDynamicReference, vl as describeFileReadFailure, vn as carriedExportAliasExposure, vo as templateResourceTypes, vr as readsNoEchoDeleteValues, vs as recordNoEchoAttributeValues, vt as displacedPhysicalIdShown, w as deferredServiceTokenWarning, wa as refuseMalformedResourceEntriesForImportSave, wc as getDockerCmd, wi as isStalePlaceholderArnAttribute, wn as collectPublishedOutputNames, wo as DagBuilder, wr as customResourceResponseExpirationWarning, ws as STATE_SOURCED_READBACK_RULES, wt as revertedNestedRowIds, xa as refuseMalformedResourceEntriesForDeploy, xc as dockerSpawnEnvWithSensitive, xi as keepsSecretReferenceToken, xl as resolveAssemblyPath, xn as isWholeDynamicReferenceValue, xr as withPrintingSecrets, xs as wholeStringLeavesOf, xt as nestedChildStackName$1, ya as refuseMalformedResourceAttributesForOrphan, yc as describeDockerExecFailure, yi as carriesFetchableDynamicReference, yl as displayAssemblyPath, yn as exportNameNoEchoParameters, yo as describeTypeWithThrottleRetry, yr as isCustomResourceType$1, ys as recordNoEchoParameterFreshValue, yt as dropSettledNestedJournals, za as repairMalformedResourcesForReadOnly, zc as getDefaultStateBucketName, zi as hasReadableOrphans, zn as ATOMIC_FINAL_SNAPSHOT_TYPES, zo as liveMatchesUnresolvedTokenFrame, zs as acceptedCreateOnlyDropsOf, zt as destroyStackRecordsView } from "./deploy-engine-CJTG3tft.js";
6
+ import { t as getCdkdVersion } from "./version-D96I6uiO.js";
7
7
  import { randomBytes, randomUUID } from "node:crypto";
8
8
  import { CopyObjectCommand, CreateBucketCommand, DeleteBucketCommand, DeleteObjectsCommand, GetBucketLocationCommand, GetObjectCommand, HeadBucketCommand, ListObjectVersionsCommand, ListObjectsV2Command, NoSuchBucket, NotFound, PutBucketEncryptionCommand, PutBucketPolicyCommand, PutBucketVersioningCommand, PutObjectCommand, S3Client, S3ServiceException } from "@aws-sdk/client-s3";
9
9
  import { GetRoleCommand, GetUserCommand } from "@aws-sdk/client-iam";
@@ -241,7 +241,7 @@ function parsePinCcApiToken(value, previous) {
241
241
  return [...previous ?? [], token];
242
242
  }
243
243
  const pinCcApiOption = new Option("--pin-cc-api <logicalId>", "Keep the named resource (by CloudFormation logical id) on the Cloud Control route in this deploy, declining the automatic return to the SDK Provider that a type-level sdk-coverage exemption would otherwise apply to a resource recorded as provisionedBy: cc-api. Repeatable — pass the flag once per resource. Has no effect on a resource already on the SDK path, and does not override an exemption admitted because Cloud Control cannot manage the type.").argParser(parsePinCcApiToken);
244
- 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);
244
+ 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 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);
245
245
  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 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. Stateful types are databases, filesystems, KMS keys, table / vector storage, source repositories, and more; https://cdkd.dev/cli-deploy-safety/ carries the full list. S3 buckets and log groups are the conditional cases: at pre-flight, a non-empty bucket, or a log group with retention, with log streams, or whose emptiness the probe could not settle; mid-deploy, ANY bucket or log group, because the emptiness probe cannot run there. The two probes differ on failure — the bucket fails open, the log group closed. 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);
246
246
  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 / any S3 bucket / any Logs log group / etc.) ALSO require --force-stateful-recreation since the replacement is a data-losing DELETE + CREATE — mid-deploy neither emptiness probe can run, so both conditional types count as stateful there.").default(false);
247
247
  const useCdkBootstrapAssetsOption = new Option("--use-cdk-bootstrap-assets", "Publish assets to the CDK bootstrap bucket/repo named by the asset manifest verbatim, skipping the cdkd asset-storage redirection even when this region is opted in via 'cdkd bootstrap'. For apps deployed via both CloudFormation and cdkd during a migration window. Per-app pin: cdk.json context.cdkd.useCdkBootstrapAssets. Also suppresses the legacy-mode 'cdk gc' notice.").default(false);
@@ -867,6 +867,11 @@ async function bootstrapCommand(options) {
867
867
  Policy: JSON.stringify(bucketPolicy)
868
868
  }));
869
869
  logger.info("✓ Set bucket policy (deny external access)");
870
+ logger.debug("Setting custom-resource response lifecycle rule...");
871
+ const lifecycle = await applyCustomResourceResponseExpiration(stateBucketS3, bucketName, { expectedBucketOwner: accountId });
872
+ if (lifecycle.status === "failed" || lifecycle.status === "refused") logger.warn(customResourceResponseExpirationWarning(bucketName, lifecycle));
873
+ else if (lifecycle.status === "added") logger.info(safeMsg`✓ Set lifecycle rule (expire ${CUSTOM_RESOURCE_RESPONSE_PREFIX}/ responses after ${1} day)`);
874
+ else logger.info(safeMsg`✓ Lifecycle rule ${CUSTOM_RESOURCE_RESPONSE_LIFECYCLE_RULE_ID} already present (left unchanged)`);
870
875
  }
871
876
  let assetStorage;
872
877
  if (options.assets) {
@@ -921,7 +926,7 @@ async function bootstrapCommand(options) {
921
926
  }
922
927
  }
923
928
  function createBootstrapCommand() {
924
- const cmd = new Command("bootstrap").description("Bootstrap cdkd by creating the S3 state bucket plus cdkd-owned asset storage (asset bucket + container-asset ECR repo) for the region. With --destroy, tear down the region's asset storage instead (issue #1010).").option("--state-bucket <bucket>", "Name of S3 bucket to create for state storage (default: cdkd-state-{accountId})").option("--force", "Create: force reconfiguration of an existing bucket. Destroy: delete the asset storage even when deployed stacks still reference it", false).option("--no-assets", "Skip cdkd asset storage (asset bucket / ECR repo / marker) — deploys keep publishing assets to the CDK bootstrap bucket/repo").option("--asset-bucket <name>", "Custom name for the cdkd asset bucket (default: cdkd-assets-{accountId}-{region}). Written into the bootstrap marker; re-bootstrapping a region with a different name is refused").option("--container-repo <name>", "Custom name for the container-asset ECR repository (default: cdkd-container-assets-{accountId}-{region}). Written into the bootstrap marker; re-bootstrapping a region with a different name is refused").option("--destroy", "Tear down the region's cdkd asset storage (empty + delete the asset bucket, force-delete the container-asset ECR repo, then delete the bootstrap marker last). The state bucket is kept unless --include-state-bucket is also passed", false).option("--include-state-bucket", "With --destroy: also delete the S3 state bucket. Refused while any stack state exists or any other region is still opted in to asset storage", false).addOption(new Option("--region <region>", "AWS region to bootstrap, or to tear down with --destroy (defaults to AWS_REGION env, else us-east-1)")).action(withErrorHandling(async (options) => {
929
+ const cmd = new Command("bootstrap").description("Bootstrap cdkd by creating the S3 state bucket plus cdkd-owned asset storage (asset bucket + container-asset ECR repo) for the region. With --destroy, tear down the region's asset storage instead.").option("--state-bucket <bucket>", "Name of S3 bucket to create for state storage (default: cdkd-state-{accountId})").option("--force", "Create: force reconfiguration of an existing bucket. Destroy: delete the asset storage even when deployed stacks still reference it", false).option("--no-assets", "Skip cdkd asset storage (asset bucket / ECR repo / marker) — deploys keep publishing assets to the CDK bootstrap bucket/repo").option("--asset-bucket <name>", "Custom name for the cdkd asset bucket (default: cdkd-assets-{accountId}-{region}). Written into the bootstrap marker; re-bootstrapping a region with a different name is refused").option("--container-repo <name>", "Custom name for the container-asset ECR repository (default: cdkd-container-assets-{accountId}-{region}). Written into the bootstrap marker; re-bootstrapping a region with a different name is refused").option("--destroy", "Tear down the region's cdkd asset storage (empty + delete the asset bucket, force-delete the container-asset ECR repo, then delete the bootstrap marker last). The state bucket is kept unless --include-state-bucket is also passed", false).option("--include-state-bucket", "With --destroy: also delete the S3 state bucket. Refused while any stack state exists or any other region is still opted in to asset storage", false).addOption(new Option("--region <region>", "AWS region to bootstrap, or to tear down with --destroy (defaults to AWS_REGION env, else us-east-1)")).action(withErrorHandling(async (options) => {
925
930
  if (options.destroy) {
926
931
  if (options.assetBucket !== void 0 || options.containerRepo !== void 0) throw new CdkdError("--asset-bucket / --container-repo cannot be combined with --destroy. The teardown reads the region's asset storage names from the bootstrap marker.", "INVALID_OPTIONS");
927
932
  if (!options.assets) throw new CdkdError("--no-assets cannot be combined with --destroy. The teardown scope is the asset storage itself; use --include-state-bucket to also delete the state bucket.", "INVALID_OPTIONS");
@@ -1506,7 +1511,7 @@ function renderRecreateTargetsErrors(validation) {
1506
1511
  if (lines.length > 0) lines.push("");
1507
1512
  lines.push(`${FLAG_UMBRELLA} named ${validation.missingFromState.length} logical id(s) the template declares but cdkd state has no record of:`);
1508
1513
  for (const id of validation.missingFromState) lines.push(` - ${id}`);
1509
- lines.push(" These are fresh CREATEs on the next deploy — recreate has nothing to destroy first. Remove the flag for these resources; the auto-route via Cloud Control (#614) handles fresh deploys for silent-drop properties, and SDK Provider is the default for everything else.");
1514
+ lines.push(" These are fresh CREATEs on the next deploy — recreate has nothing to destroy first. Remove the flag for these resources; the auto-route via Cloud Control handles fresh deploys for silent-drop properties, and SDK Provider is the default for everything else.");
1510
1515
  }
1511
1516
  if (validation.ambiguousIntent.length > 0) {
1512
1517
  if (lines.length > 0) lines.push("");
@@ -1916,18 +1921,15 @@ function refuseMalformedNestedTemplateTrees(stacks) {
1916
1921
  const REQUIRE_APPROVAL_VALUES = [
1917
1922
  "never",
1918
1923
  "any-change",
1924
+ "broadening",
1919
1925
  "destructive"
1920
1926
  ];
1921
- const requireApprovalOption = new Option("--require-approval <level>", "What changes require manual approval before they are deployed: \"never\" (default), \"any-change\", or \"destructive\" (a change that replaces, deletes or orphans an existing resource, including in nested stacks). Without a terminal the deploy fails instead of asking, unless --yes is given. Also read from \"requireApproval\" in cdk.json.").choices(REQUIRE_APPROVAL_VALUES);
1922
- function resolveRequireApproval(cliValue, logger, cdkJson = loadCdkJson()) {
1927
+ const requireApprovalOption = new Option("--require-approval <level>", "What changes require manual approval before they are deployed: \"never\" (default), \"any-change\", \"broadening\" (a change that broadens IAM permissions or security-group rules), or \"destructive\" (a change that replaces, deletes or orphans an existing resource). Both \"broadening\" and \"destructive\" check nested stacks too. Without a terminal the deploy fails instead of asking, unless --yes is given. Also read from \"requireApproval\" in cdk.json.").choices(REQUIRE_APPROVAL_VALUES);
1928
+ function resolveRequireApproval(cliValue, cdkJson = loadCdkJson()) {
1923
1929
  if (cliValue !== void 0) return cliValue;
1924
1930
  const configured = cdkJson?.requireApproval;
1925
1931
  if (configured === void 0) return "never";
1926
1932
  if (REQUIRE_APPROVAL_VALUES.includes(configured)) return configured;
1927
- if (configured === "broadening") {
1928
- logger.warn(`cdk.json "requireApproval": "broadening" is ignored: cdkd does not detect security-broadening changes, so no approval is asked. Use "any-change" or "destructive" to have cdkd ask.`);
1929
- return "never";
1930
- }
1931
1933
  throw new CdkdError(`cdk.json "requireApproval" must be one of ${REQUIRE_APPROVAL_VALUES.join(", ")}, got: ${displayIdent(typeof configured === "string" ? configured : JSON.stringify(configured))}`, "INVALID_REQUIRE_APPROVAL");
1932
1934
  }
1933
1935
  function createApprovalPrompter(options) {
@@ -1944,14 +1946,18 @@ function createApprovalPrompter(options) {
1944
1946
  }
1945
1947
  function renderApprovalRequest(request) {
1946
1948
  const lines = [""];
1947
- if (request.destructiveChanges.length > 0) {
1949
+ if (request.securityChanges !== void 0 && request.securityChanges !== "") {
1950
+ lines.push(request.securityChanges.replace(/\n+$/, ""));
1951
+ lines.push("");
1952
+ }
1953
+ if (request.destructiveChanges.length > 0 && request.level !== "broadening") {
1948
1954
  lines.push("Destructive changes:");
1949
1955
  for (const change of request.destructiveChanges) lines.push(` ${formatDestructiveChange(change)}`);
1950
1956
  lines.push("");
1951
1957
  }
1952
1958
  const { create, update, delete: del } = request.counts;
1953
1959
  lines.push(request.outputsOnly ? `Stack ${displayIdent(request.stackName)}: no resource changes; its Outputs change.` : `Stack ${displayIdent(request.stackName)}: ${create} to create, ${update} to update, ${del} to delete.`);
1954
- lines.push(request.level === "destructive" ? `Stack includes destructive updates and "--require-approval" is set to 'destructive'.` : `Stack includes updates and "--require-approval" is set to 'any-change'.`);
1960
+ lines.push(request.level === "destructive" ? `Stack includes destructive updates and "--require-approval" is set to 'destructive'.` : request.level === "broadening" ? `Stack includes security-sensitive updates and "--require-approval" is set to 'broadening'.` : `Stack includes updates and "--require-approval" is set to 'any-change'.`);
1955
1961
  return `${lines.join("\n")}\n`;
1956
1962
  }
1957
1963
 
@@ -2370,12 +2376,13 @@ function inferCrossStackStackDeps(stacks, opts) {
2370
2376
  const result = /* @__PURE__ */ new Map();
2371
2377
  for (const stack of stacks) {
2372
2378
  const producers = /* @__PURE__ */ new Set();
2373
- collectCrossStackRefs(stack.template, (kind, value) => {
2379
+ collectTemplateCrossStackRefs(stack.template, (kind, value, unconditional) => {
2374
2380
  let producer;
2375
2381
  if (kind === "ImportValue") {
2376
2382
  if (!wantImportValue) return;
2377
2383
  if (typeof value === "string") producer = exportOwner.get(value);
2378
2384
  } else {
2385
+ if (opts?.onRoleArnReference && unconditional && value && typeof value === "object" && !Array.isArray(value) && Object.hasOwn(value, "RoleArn")) opts.onRoleArnReference(stack.stackName, value);
2379
2386
  if (!wantGetStackOutput) return;
2380
2387
  if (value && typeof value === "object" && !Array.isArray(value)) {
2381
2388
  const sn = value["StackName"];
@@ -2388,23 +2395,45 @@ function inferCrossStackStackDeps(stacks, opts) {
2388
2395
  }
2389
2396
  return result;
2390
2397
  }
2391
- function collectCrossStackRefs(node, onRef) {
2398
+ function collectTemplateCrossStackRefs(template, onRef) {
2399
+ if (!template || typeof template !== "object" || Array.isArray(template)) {
2400
+ collectCrossStackRefs(template, onRef, false);
2401
+ return;
2402
+ }
2403
+ for (const [section, body] of Object.entries(template)) {
2404
+ if (section !== "Resources" || !body || typeof body !== "object" || Array.isArray(body)) {
2405
+ collectCrossStackRefs(body, onRef, false);
2406
+ continue;
2407
+ }
2408
+ for (const resource of Object.values(body)) {
2409
+ if (!resource || typeof resource !== "object" || Array.isArray(resource)) {
2410
+ collectCrossStackRefs(resource, onRef, false);
2411
+ continue;
2412
+ }
2413
+ const record = resource;
2414
+ const always = !Object.hasOwn(record, "Condition");
2415
+ for (const [key, value] of Object.entries(record)) collectCrossStackRefs(value, onRef, always && key === "Properties");
2416
+ }
2417
+ }
2418
+ }
2419
+ function collectCrossStackRefs(node, onRef, unconditional) {
2392
2420
  if (!node || typeof node !== "object") return;
2393
2421
  if (Array.isArray(node)) {
2394
- for (const item of node) collectCrossStackRefs(item, onRef);
2422
+ for (const item of node) collectCrossStackRefs(item, onRef, unconditional);
2395
2423
  return;
2396
2424
  }
2397
2425
  const obj = node;
2398
- if ("Fn::ImportValue" in obj) onRef("ImportValue", obj["Fn::ImportValue"]);
2399
- if ("Fn::GetStackOutput" in obj) onRef("GetStackOutput", obj["Fn::GetStackOutput"]);
2400
- for (const value of Object.values(obj)) collectCrossStackRefs(value, onRef);
2426
+ if ("Fn::ImportValue" in obj) onRef("ImportValue", obj["Fn::ImportValue"], unconditional);
2427
+ if ("Fn::GetStackOutput" in obj) onRef("GetStackOutput", obj["Fn::GetStackOutput"], unconditional);
2428
+ const below = unconditional && !("Fn::If" in obj);
2429
+ for (const value of Object.values(obj)) collectCrossStackRefs(value, onRef, below);
2401
2430
  }
2402
2431
 
2403
2432
  //#endregion
2404
2433
  //#region src/provisioning/register-providers.ts
2405
2434
  let providerClasses;
2406
2435
  function loadProviderClasses() {
2407
- providerClasses ??= import("./provider-classes-B7Z4-Zsc.js");
2436
+ providerClasses ??= import("./provider-classes-1EHrZ847.js");
2408
2437
  return providerClasses;
2409
2438
  }
2410
2439
  function registerAllProviders(registry, classes) {
@@ -2914,7 +2943,7 @@ async function deployCommand(stacks, options) {
2914
2943
  validateResourceTimeouts(options);
2915
2944
  setResolvedResourceTimeouts(options.resourceTimeout);
2916
2945
  applyWaitFlagEnv(options);
2917
- const requireApproval = resolveRequireApproval(options.requireApproval, logger);
2946
+ const requireApproval = resolveRequireApproval(options.requireApproval);
2918
2947
  const approveDeployment = requireApproval !== "never" && !options.dryRun ? createApprovalPrompter({ yes: options.yes ?? false }) : void 0;
2919
2948
  const rawBaseRegion = rawCliRegion(options.region) ?? "us-east-1";
2920
2949
  foldRegionOption(options);
@@ -3011,7 +3040,12 @@ Force-quit: stack locks may not be released. If the next run reports a lock, run
3011
3040
  const gone = stacksNoLongerSelected(allStacks, stackPatterns, /* @__PURE__ */ new Set());
3012
3041
  throw new Error(renderNoStackMatch(stackPatterns, allStacks) + (gone.stacks.length > 0 ? `\n${renderStacksNoLongerSelected(gone.patterns, gone.stacks)}` : ""));
3013
3042
  }
3014
- const inferredCrossStackDeps = inferCrossStackStackDeps(allStacks);
3043
+ const roleArnReferences = [];
3044
+ const inferredCrossStackDeps = inferCrossStackStackDeps(allStacks, { onRoleArnReference: (consumer, args) => roleArnReferences.push({
3045
+ consumer,
3046
+ args
3047
+ }) });
3048
+ const producerLookup = producerLookupOver(allStacks);
3015
3049
  const effectiveStackDeps = (stackName, deps) => {
3016
3050
  const union = new Set(deps);
3017
3051
  for (const producer of inferredCrossStackDeps.get(stackName) ?? []) union.add(producer);
@@ -3053,6 +3087,13 @@ Force-quit: stack locks may not be released. If the next run reports a lock, run
3053
3087
  ignoreErrors: options.ignoreErrors === true
3054
3088
  });
3055
3089
  refuseMalformedNestedTemplateTrees(targetStacks);
3090
+ if (roleArnReferences.length > 0) {
3091
+ const stackByName = new Map(allStacks.map((s) => [s.stackName, s]));
3092
+ refuseUnreadableCdkCrossAccountRefs(roleArnReferences, new Set(targetStacks.map((s) => s.stackName)), (name) => ({
3093
+ region: stackByName.get(name)?.region ?? namedCliRegion(options.region),
3094
+ account: stackByName.get(name)?.account
3095
+ }), producerLookup);
3096
+ }
3056
3097
  const baseRegion = namedCliRegion(options.region) ?? "us-east-1";
3057
3098
  const crossPrefixGuard = new CrossPrefixGuard(preflightStateBackend, { readOnly: options.dryRun === true });
3058
3099
  await synthesizer.expandMacrosForStacks(targetStacks, synthOptions);
@@ -3278,6 +3319,8 @@ Force-quit: stack locks may not be released. If the next run reports a lock, run
3278
3319
  finalSnapshotClients: stackAwsClients,
3279
3320
  ...options.strictGetatt && { strictGetAtt: true },
3280
3321
  ...options.cfnFallback === false && { cfnFallback: false },
3322
+ producerLookup,
3323
+ ...stackInfo.account && { consumerAccount: stackInfo.account },
3281
3324
  captureObservedState: resolveCaptureObservedState(options.captureObservedState),
3282
3325
  ...options.resourceWarnAfter?.globalMs !== void 0 && { resourceWarnAfterMs: options.resourceWarnAfter.globalMs },
3283
3326
  ...options.resourceTimeout?.globalMs !== void 0 && { resourceTimeoutMs: options.resourceTimeout.globalMs },
@@ -4401,7 +4444,7 @@ function deployRefusesPropertiesReason(logicalIds) {
4401
4444
  return `${logicalIds.length} resource record(s) hold a 'properties' map that cannot be read. This preview repaired them to empty; 'cdkd deploy' refuses the record instead, so the deploy this previews will not start.`;
4402
4445
  }
4403
4446
  function nestedStackTypeChangeReason(tc) {
4404
- return `${displayIdent(tc.logicalId)}: Type changes from ${displayIdent(tc.currentType)} to ${displayIdent(tc.desiredType)}. cdkd does not replace a resource into or out of ${NESTED_STACK_RESOURCE_TYPE} (issue #2668); give the new resource a different logical id, or remove it in one deploy and add its replacement in the next.`;
4447
+ return `${displayIdent(tc.logicalId)}: Type changes from ${displayIdent(tc.currentType)} to ${displayIdent(tc.desiredType)}. cdkd does not replace a resource into or out of ${NESTED_STACK_RESOURCE_TYPE}; give the new resource a different logical id, or remove it in one deploy and add its replacement in the next.`;
4405
4448
  }
4406
4449
  function findUnwrittenCreateOnlyRefusals(changes, stateResources) {
4407
4450
  const reasons = [];
@@ -4657,10 +4700,14 @@ function maskOutputChangeForDisplay(change, mask, nameCorpus) {
4657
4700
  return changed ? shown : change;
4658
4701
  }
4659
4702
  async function computeStackDiff(currentState, template, region, stackName, stateBackend, diffCalculator, options = {}) {
4660
- const { parameters, canonicalizeProperties, cfnFallback, inheritSecretBearingTemplate, inheritedSecrets, inheritedDerivedNames, attributeHealer, parentUnresolvedParameters, inheritedNoEchoParameters, inheritedNoEchoParametersAnyVerdict } = options;
4703
+ const { parameters, canonicalizeProperties, cfnFallback, producerLookup, consumerAccount, inheritSecretBearingTemplate, inheritedSecrets, inheritedDerivedNames, attributeHealer, parentUnresolvedParameters, inheritedNoEchoParameters, inheritedNoEchoParametersAnyVerdict } = options;
4661
4704
  const noEchoParametersOf = (t) => /* @__PURE__ */ new Set([...noEchoParameterNamesOf(t), ...inheritedNoEchoParameters ?? []]);
4662
4705
  const inheritedForResolver = inheritedSecrets !== void 0 && hasMaskableValues(inheritedSecrets) ? inheritedSecrets : void 0;
4663
- const intrinsicResolver = new IntrinsicFunctionResolver(region, { cfnFallback: cfnFallback ?? true });
4706
+ const intrinsicResolver = new IntrinsicFunctionResolver(region, {
4707
+ cfnFallback: cfnFallback ?? true,
4708
+ ...producerLookup && { producerLookup },
4709
+ ...consumerAccount && { consumerAccount }
4710
+ });
4664
4711
  const outputsDigestSource = structuredClone({
4665
4712
  ...template,
4666
4713
  Resources: {}
@@ -4777,8 +4824,8 @@ async function computeStackDiff(currentState, template, region, stackName, state
4777
4824
  const diffSecrets = /* @__PURE__ */ new Map();
4778
4825
  const splitPieces = /* @__PURE__ */ new Map();
4779
4826
  const derivedNames = /* @__PURE__ */ new Map();
4780
- const splitDelimiters = literalSplitDelimitersOf(template, new Set(Object.entries(template.Parameters ?? {}).filter(([, definition]) => definition?.NoEcho === true).map(([name]) => name)));
4781
- for (const [name, definition] of Object.entries(template.Parameters ?? {})) if (definition?.NoEcho === true && mergedParameters !== void 0 && Object.prototype.hasOwnProperty.call(mergedParameters, name) && !isWholeDynamicReferenceValue(mergedParameters[name])) {
4827
+ const splitDelimiters = literalSplitDelimitersOf(template, new Set(Object.entries(template.Parameters ?? {}).filter(([, definition]) => declaresNoEcho(definition)).map(([name]) => name)));
4828
+ for (const [name, definition] of Object.entries(template.Parameters ?? {})) if (declaresNoEcho(definition) && mergedParameters !== void 0 && Object.prototype.hasOwnProperty.call(mergedParameters, name) && !isWholeDynamicReferenceValue(mergedParameters[name])) {
4782
4829
  recordLogOnlyParameterValue(diffSecrets, mergedParameters[name]);
4783
4830
  recordLogOnlyParameterValue(splitPieces, mergedParameters[name], splitDelimiters);
4784
4831
  }
@@ -4992,6 +5039,13 @@ async function computeStackDiff(currentState, template, region, stackName, state
4992
5039
  }
4993
5040
  return {
4994
5041
  changes: shownChanges,
5042
+ securityDiff: () => diffSecurity({
5043
+ stackName,
5044
+ changes: changes.values(),
5045
+ records: stateForDiff.resources,
5046
+ template: effectiveTemplate,
5047
+ secretMask: () => printing.mask
5048
+ }),
4995
5049
  outputChanges: shownOutputChanges,
4996
5050
  adoptedOrphans,
4997
5051
  adoptedRecords,
@@ -5175,10 +5229,14 @@ function noEchoFedChildParameters(parentStackRow, parentNoEchoParameters, parent
5175
5229
  })) fed.add(name);
5176
5230
  return fed;
5177
5231
  }
5178
- async function resolveChildStackParameters(parentStackRow, parentTemplate, parentState, region, parentStackName, stateBackend, parentParameters, parentConditions, cfnFallback, printingSecrets, attributeHealer, unresolvedKeys, trust, derivedNameSink) {
5232
+ async function resolveChildStackParameters(parentStackRow, parentTemplate, parentState, region, parentStackName, stateBackend, parentParameters, parentConditions, cfnFallback, printingSecrets, attributeHealer, unresolvedKeys, trust, derivedNameSink, producerLookup, consumerAccount) {
5179
5233
  const rawParams = parentStackRow.Properties?.["Parameters"];
5180
5234
  if (!rawParams || typeof rawParams !== "object" || Array.isArray(rawParams)) return {};
5181
- const resolver = new IntrinsicFunctionResolver(region, { cfnFallback: cfnFallback ?? true });
5235
+ const resolver = new IntrinsicFunctionResolver(region, {
5236
+ cfnFallback: cfnFallback ?? true,
5237
+ ...producerLookup && { producerLookup },
5238
+ ...consumerAccount && { consumerAccount }
5239
+ });
5182
5240
  const resolved = {};
5183
5241
  for (const [name, value] of Object.entries(rawParams)) {
5184
5242
  if (trust === void 0 || !rowValueTrusted(value, parentState, parentConditions, trust)) unresolvedKeys?.add(name);
@@ -5214,7 +5272,7 @@ function recordedTemplateResources(template, state) {
5214
5272
  return recorded;
5215
5273
  }
5216
5274
  async function buildDiffTree(args) {
5217
- const { stackName, displayName, region, template, nestedTemplates, recursive, stateBackend, diffCalculator, parentHasSecretReference, parameters, parentUnresolvedParameters, canonicalizeProperties, assetRedirect, cfnFallback, previewOrphanAdoption, attributeHealerFor, ancestorTemplatePaths, isNestedChild, inheritedSecrets, inheritedDerivedNames, inheritedNoEchoParameters, inheritedNoEchoParametersAnyVerdict, refusalRecovery } = args;
5275
+ const { stackName, displayName, region, template, nestedTemplates, recursive, stateBackend, diffCalculator, parentHasSecretReference, parameters, parentUnresolvedParameters, canonicalizeProperties, assetRedirect, cfnFallback, producerLookup, consumerAccount, previewOrphanAdoption, attributeHealerFor, ancestorTemplatePaths, isNestedChild, inheritedSecrets, inheritedDerivedNames, inheritedNoEchoParameters, inheritedNoEchoParametersAnyVerdict, refusalRecovery } = args;
5218
5276
  const attributeHealer = attributeHealerFor?.(stackName, region);
5219
5277
  const { state, unreadable, unreadableContainers, deployRefusals } = await loadStateOrEmpty(stackName, region, stateBackend, refusalRecovery);
5220
5278
  const createOnlyPrefetch = prefetchCreateOnlyPropertyPaths(templateResourceTypes(recordedTemplateResources(template, state)));
@@ -5226,6 +5284,8 @@ async function buildDiffTree(args) {
5226
5284
  ...parentUnresolvedParameters && { parentUnresolvedParameters },
5227
5285
  ...canonicalizeProperties && { canonicalizeProperties },
5228
5286
  ...cfnFallback !== void 0 && { cfnFallback },
5287
+ ...producerLookup && { producerLookup },
5288
+ ...consumerAccount && { consumerAccount },
5229
5289
  ...previewOrphanAdoption && { previewOrphanAdoption },
5230
5290
  ...inheritedSecrets && { inheritedSecrets },
5231
5291
  ...inheritedDerivedNames && { inheritedDerivedNames },
@@ -5240,7 +5300,7 @@ async function buildDiffTree(args) {
5240
5300
  } finally {
5241
5301
  createOnlyPrefetch.cancel();
5242
5302
  }
5243
- const { changes, outputChanges, adoptedOrphans, adoptedRecords, blocking, unreadableOrphans, deployRefusals: adoptedDeployRefusals, effectiveTemplate, printingSecrets, derivedNames: nodeDerivedNames, secretNamedRecords: nodeStateNames, resolvedParameters, conditions, knownConditions, trust } = stackDiff;
5303
+ const { changes, securityDiff, outputChanges, adoptedOrphans, adoptedRecords, blocking, unreadableOrphans, deployRefusals: adoptedDeployRefusals, effectiveTemplate, printingSecrets, derivedNames: nodeDerivedNames, secretNamedRecords: nodeStateNames, resolvedParameters, conditions, knownConditions, trust } = stackDiff;
5244
5304
  const stateAfterAdoption = Object.keys(adoptedRecords).length > 0 ? {
5245
5305
  ...state,
5246
5306
  resources: {
@@ -5253,6 +5313,7 @@ async function buildDiffTree(args) {
5253
5313
  displayName,
5254
5314
  region,
5255
5315
  changes,
5316
+ securityDiff,
5256
5317
  ccApiRoutes: collectCcApiRoutes(effectiveTemplate, stateAfterAdoption, changes),
5257
5318
  outputChanges,
5258
5319
  adoptedOrphans,
@@ -5290,7 +5351,7 @@ async function buildDiffTree(args) {
5290
5351
  ...inheritedNoEchoParametersAnyVerdict ?? []
5291
5352
  ]), void 0, stateAfterAdoption.resources);
5292
5353
  const rowDerivedNames = /* @__PURE__ */ new Map();
5293
- const childParameters = await resolveChildStackParameters(resource, effectiveTemplate, stateAfterAdoption, region, stackName, stateBackend, resolvedParameters, conditions, cfnFallback, printingSecrets, attributeHealer, childUnresolvedParameters, trust, rowDerivedNames);
5354
+ const childParameters = await resolveChildStackParameters(resource, effectiveTemplate, stateAfterAdoption, region, stackName, stateBackend, resolvedParameters, conditions, cfnFallback, printingSecrets, attributeHealer, childUnresolvedParameters, trust, rowDerivedNames, producerLookup, consumerAccount);
5294
5355
  const childDerivedNames = diffPrintingSecrets([
5295
5356
  inheritedDerivedNames,
5296
5357
  rowDerivedNames,
@@ -5310,6 +5371,8 @@ async function buildDiffTree(args) {
5310
5371
  ...canonicalizeProperties && { canonicalizeProperties },
5311
5372
  ...assetRedirect && { assetRedirect },
5312
5373
  ...cfnFallback !== void 0 && { cfnFallback },
5374
+ ...producerLookup && { producerLookup },
5375
+ ...consumerAccount && { consumerAccount },
5313
5376
  ...previewOrphanAdoption && { previewOrphanAdoption },
5314
5377
  ...attributeHealerFor && { attributeHealerFor },
5315
5378
  ancestorTemplatePaths: childAncestorTemplatePaths,
@@ -5337,7 +5400,7 @@ async function buildDiffTree(args) {
5337
5400
  async function buildDeletedSubtree(stackName, region, stateBackend, diffCalculator, parentHasSecretReference, inheritedSecrets, refusalRecovery, inheritedDerivedNames) {
5338
5401
  const { state, unreadable, unreadableContainers } = await loadStateOrEmpty(stackName, region, stateBackend, refusalRecovery);
5339
5402
  const derivedNames = diffPrintingSecrets([inheritedDerivedNames, stateDerivedNames(state.resources ?? {})]);
5340
- const { changes, outputChanges } = await computeStackDiff(state, EMPTY_TEMPLATE, region, stackName, stateBackend, diffCalculator, {
5403
+ const { changes, securityDiff, outputChanges } = await computeStackDiff(state, EMPTY_TEMPLATE, region, stackName, stateBackend, diffCalculator, {
5341
5404
  inheritSecretBearingTemplate: parentHasSecretReference,
5342
5405
  inheritedSecrets,
5343
5406
  ...hasMaskableValues(derivedNames) && { inheritedDerivedNames: derivedNames },
@@ -5348,6 +5411,7 @@ async function buildDeletedSubtree(stackName, region, stateBackend, diffCalculat
5348
5411
  displayName: stackName,
5349
5412
  region,
5350
5413
  changes,
5414
+ securityDiff,
5351
5415
  ccApiRoutes: /* @__PURE__ */ new Map(),
5352
5416
  adoptedOrphans: [],
5353
5417
  blocking: [],
@@ -5475,7 +5539,7 @@ function diffTreeToJson(node) {
5475
5539
  children: node.children.map(diffTreeToJson)
5476
5540
  };
5477
5541
  }
5478
- function isIntrinsic(value) {
5542
+ function isIntrinsic$1(value) {
5479
5543
  if (value === null || value === void 0 || typeof value !== "object" || Array.isArray(value)) return false;
5480
5544
  const keys = Object.keys(value);
5481
5545
  return keys.length === 1 && INTRINSIC_KEYS.has(keys[0]);
@@ -5483,12 +5547,12 @@ function isIntrinsic(value) {
5483
5547
  function stripUnchangedValues(value, other) {
5484
5548
  if (value === null || value === void 0 || typeof value !== "object") return value;
5485
5549
  if (Array.isArray(value)) return value;
5486
- if (isIntrinsic(value)) return void 0;
5487
- if (isIntrinsic(other)) return void 0;
5550
+ if (isIntrinsic$1(value)) return void 0;
5551
+ if (isIntrinsic$1(other)) return void 0;
5488
5552
  return value;
5489
5553
  }
5490
5554
  function isPlainNonIntrinsicObject(value) {
5491
- return typeof value === "object" && value !== null && !Array.isArray(value) && !isIntrinsic(value);
5555
+ return typeof value === "object" && value !== null && !Array.isArray(value) && !isIntrinsic$1(value);
5492
5556
  }
5493
5557
  function stripUnchangedValuePair(oldValue, newValue) {
5494
5558
  if (!isPlainNonIntrinsicObject(oldValue) || !isPlainNonIntrinsicObject(newValue)) return [stripUnchangedValues(oldValue, newValue), stripUnchangedValues(newValue, oldValue)];
@@ -5498,7 +5562,7 @@ function stripUnchangedValuePair(oldValue, newValue) {
5498
5562
  for (const key of keys) {
5499
5563
  const o = oldValue[key];
5500
5564
  const n = newValue[key];
5501
- if (isIntrinsic(o) || isIntrinsic(n)) continue;
5565
+ if (isIntrinsic$1(o) || isIntrinsic$1(n)) continue;
5502
5566
  if (JSON.stringify(o) === JSON.stringify(n)) continue;
5503
5567
  if (isPlainNonIntrinsicObject(o) && isPlainNonIntrinsicObject(n)) {
5504
5568
  const [fo, fn] = stripUnchangedValuePair(o, n);
@@ -5513,7 +5577,7 @@ function stripUnchangedValuePair(oldValue, newValue) {
5513
5577
  return [oldResult, newResult];
5514
5578
  }
5515
5579
  function renderDiffValue(own, filtered, indent, isNewSide) {
5516
- if (isIntrinsic(own)) {
5580
+ if (isIntrinsic$1(own)) {
5517
5581
  const suffix = isNewSide ? " (known after deploy)" : "";
5518
5582
  return `${JSON.stringify(own)}${suffix}`;
5519
5583
  }
@@ -5644,9 +5708,20 @@ function renderDiffTree(node, isRoot, logFn) {
5644
5708
 
5645
5709
  //#endregion
5646
5710
  //#region src/cli/commands/diff.ts
5711
+ function treeNodes(node) {
5712
+ return [node, ...node.children.flatMap(treeNodes)];
5713
+ }
5714
+ function securityDiffOf(node) {
5715
+ if (node.securityDiff === void 0) throw new CdkdError(safeMsg`No security diff for stack ${node.displayName}; its security changes cannot be decided.`, "SECURITY_DIFF_FAILED");
5716
+ return node.securityDiff();
5717
+ }
5718
+ function logTable(rendered, log) {
5719
+ for (const row of rendered.replace(/\n+$/, "").split("\n")) log(safeMsg`${row}`);
5720
+ }
5647
5721
  const DIFF_FAIL_ON_VALUES = [
5648
5722
  "never",
5649
5723
  "any-change",
5724
+ "broadening",
5650
5725
  "destructive"
5651
5726
  ];
5652
5727
  function parseFailOn(value, previous) {
@@ -5677,6 +5752,8 @@ var DeployRefusalPreviewError = class DeployRefusalPreviewError extends CdkdErro
5677
5752
  };
5678
5753
  async function diffCommand(stacks, options) {
5679
5754
  const failOn = resolveFailOn(options);
5755
+ const securityOnly = options.securityOnly === true;
5756
+ if (securityOnly && options.json === true) throw new CdkdError("--security-only cannot be used with --json: it prints the security tables only", "INCOMPATIBLE_OPTIONS");
5680
5757
  const providerClasses = await loadProviderClasses();
5681
5758
  const logger = getLogger();
5682
5759
  if (options.json) logger.setLevel("warn");
@@ -5803,7 +5880,7 @@ async function diffCommand(stacks, options) {
5803
5880
  }).provider,
5804
5881
  inRegion: (healRegion, fn) => runWithStackAwsClients(stackRegionScope(healRegion).clients, fn)
5805
5882
  });
5806
- const recursive = (options.recursive ?? false) || failOn === "destructive";
5883
+ const recursive = (options.recursive ?? false) || failOn === "destructive" || failOn === "broadening" || securityOnly;
5807
5884
  const resolveAssetRedirect = createAssetRedirectResolver({
5808
5885
  stateBackend,
5809
5886
  stsRegion: region,
@@ -5812,6 +5889,7 @@ async function diffCommand(stacks, options) {
5812
5889
  suppressLegacyNotice: true
5813
5890
  });
5814
5891
  const trees = [];
5892
+ const producerLookup = producerLookupOver(allStacks);
5815
5893
  for (const stackInfo of targetStacks) {
5816
5894
  logger.info(`\nCalculating diff for stack: ${stackInfo.stackName}`);
5817
5895
  const stackRegion = stackInfo.region || region;
@@ -5832,13 +5910,41 @@ async function diffCommand(stacks, options) {
5832
5910
  canonicalizeProperties,
5833
5911
  ...assetRedirect && { assetRedirect },
5834
5912
  ...options.cfnFallback === false && { cfnFallback: false },
5913
+ producerLookup,
5914
+ ...stackInfo.account && { consumerAccount: stackInfo.account },
5835
5915
  previewOrphanAdoption,
5836
5916
  attributeHealerFor,
5837
5917
  isNestedChild: false,
5838
5918
  refusalRecovery
5839
5919
  }));
5840
5920
  }
5841
- if (options.json) process.stdout.write(`${stringifyJsonPayload(trees.map(diffTreeToJson))}\n`);
5921
+ let securityDifferences = 0;
5922
+ let securityBroadening = false;
5923
+ if (securityOnly) for (const tree of trees) {
5924
+ const nodes = treeNodes(tree);
5925
+ for (const node of nodes) for (const reason of node.blocking) logger.error(safeMsg`Blocking (${node.displayName}): ${reason}`);
5926
+ const results = [];
5927
+ for (const node of nodes) results.push({
5928
+ node,
5929
+ security: await securityDiffOf(node)
5930
+ });
5931
+ if (!results.some(({ security }) => security.broadening)) {
5932
+ logger.info(safeMsg`\n✓ No security-broadening changes for stack ${tree.displayName}`);
5933
+ continue;
5934
+ }
5935
+ securityBroadening = true;
5936
+ securityDifferences += results.filter(({ security }) => security.rendered !== "").length;
5937
+ logger.warn("This deployment will make potentially sensitive changes according to your current security approval level.\nPlease confirm you intend to make the following modifications:\n");
5938
+ for (const { node, security } of results) {
5939
+ logger.info(safeMsg`Stack ${node.displayName}`);
5940
+ if (security.rendered === "") {
5941
+ logger.info("There were no security-related changes (limitations: https://github.com/aws/aws-cdk/issues/1299)");
5942
+ continue;
5943
+ }
5944
+ logTable(security.rendered, (row) => logger.info(row));
5945
+ }
5946
+ }
5947
+ else if (options.json) process.stdout.write(`${stringifyJsonPayload(trees.map(diffTreeToJson))}\n`);
5842
5948
  else for (const tree of trees) {
5843
5949
  if (!treeIsWorthRendering(tree)) {
5844
5950
  logger.info(`\n✓ No changes detected for stack ${tree.stackName}`);
@@ -5848,7 +5954,7 @@ async function diffCommand(stacks, options) {
5848
5954
  }
5849
5955
  const blockingCount = trees.reduce((n, tree) => n + countBlocking(tree), 0);
5850
5956
  if (blockingCount > 0) throw new DeployRefusalPreviewError(blockingCount);
5851
- if (failOn === "any-change" && trees.some(treeHasChanges)) throw new DiffDetectedError();
5957
+ if (failOn === "any-change" && (securityOnly ? securityDifferences > 0 : trees.some(treeHasChanges))) throw new DiffDetectedError();
5852
5958
  if (failOn === "destructive") {
5853
5959
  const destructive = trees.flatMap(treeDestructiveChanges);
5854
5960
  if (destructive.length > 0) {
@@ -5857,13 +5963,26 @@ async function diffCommand(stacks, options) {
5857
5963
  throw new DiffDetectedError();
5858
5964
  }
5859
5965
  }
5966
+ if (failOn === "broadening" && securityOnly) {
5967
+ if (securityBroadening) throw new DiffDetectedError();
5968
+ } else if (failOn === "broadening") {
5969
+ let broadening = 0;
5970
+ for (const node of trees.flatMap(treeNodes)) {
5971
+ const security = await securityDiffOf(node);
5972
+ if (!security.broadening) continue;
5973
+ broadening++;
5974
+ logger.error(safeMsg`\n❌ Stack ${node.displayName} broadens permissions (--fail-on=broadening):`);
5975
+ logTable(security.rendered, (row) => logger.error(row));
5976
+ }
5977
+ if (broadening > 0) throw new DiffDetectedError();
5978
+ }
5860
5979
  } finally {
5861
5980
  for (const { clients } of stackRegionScopes.values()) clients.destroy();
5862
5981
  awsClients.destroy();
5863
5982
  }
5864
5983
  }
5865
5984
  function createDiffCommand() {
5866
- const cmd = new Command("diff").description("Show difference between current state and desired state").argument("[stacks...]", "Stack name(s) to diff. Accepts CDK display paths (e.g. 'MyStage/Api') with wildcards ('MyStage/*', '**'), or exact physical CloudFormation names (e.g. 'MyStage-Api').").option("--all", "Diff every top-level stack (top-level only; Stage stacks: '**')", false).option("--recursive", "Recurse into each AWS::CloudFormation::Stack row and diff every nested-stack child against its own deployed state (DFS order). Default is non-recursive, matching cdk diff.", false).option("--fail", "Exit with code 1 when any change is detected (matches cdk diff --fail). Alias for --fail-on=any-change.").option("--no-fail", "Never exit with code 1 for a change. Alias for --fail-on=never.").addOption(new Option("--fail-on <kind>", "Exit with code 1 when the diff contains the given kind of change: \"any-change\" fails on any difference, \"destructive\" only on changes that replace, delete or orphan a resource, \"never\" does not fail (default). \"destructive\" always checks nested stacks (as --recursive); the others do with --recursive. Cannot be used with --fail / --no-fail.").choices(DIFF_FAIL_ON_VALUES).argParser(parseFailOn)).option("--json", "Output the diff as JSON (nested tree shape when combined with --recursive)", false).addOption(useCdkBootstrapAssetsOption).addOption(noCfnFallbackOption).action(withErrorHandling(diffCommand));
5985
+ const cmd = new Command("diff").description("Show difference between current state and desired state").argument("[stacks...]", "Stack name(s) to diff. Accepts CDK display paths (e.g. 'MyStage/Api') with wildcards ('MyStage/*', '**'), or exact physical CloudFormation names (e.g. 'MyStage-Api').").option("--all", "Diff every top-level stack (top-level only; Stage stacks: '**')", false).option("--recursive", "Recurse into each AWS::CloudFormation::Stack row and diff every nested-stack child against its own deployed state (DFS order). Default is non-recursive, matching cdk diff.", false).option("--fail", "Exit with code 1 when any change is detected (matches cdk diff --fail). Alias for --fail-on=any-change.").option("--no-fail", "Never exit with code 1 for a change. Alias for --fail-on=never.").addOption(new Option("--fail-on <kind>", "Exit with code 1 when the diff contains the given kind of change: \"any-change\" fails on any difference, \"broadening\" only on changes that broaden IAM permissions or security-group rules, \"destructive\" only on changes that replace, delete or orphan a resource, \"never\" does not fail (default). \"broadening\" and \"destructive\" always check nested stacks (as --recursive); \"any-change\" does with --recursive. Cannot be used with --fail / --no-fail.").choices(DIFF_FAIL_ON_VALUES).argParser(parseFailOn)).option("--security-only", "Only print the security changes (IAM and security-group) of each stack whose changes broaden permissions, and count only those stacks as differences for --fail-on (matches cdk diff --security-only). Checks nested stacks too. Cannot be used with --json.").option("--json", "Output the diff as JSON (nested tree shape when combined with --recursive)", false).addOption(useCdkBootstrapAssetsOption).addOption(noCfnFallbackOption).action(withErrorHandling(diffCommand));
5867
5986
  [
5868
5987
  ...commonOptions,
5869
5988
  ...appOptions,
@@ -8782,9 +8901,10 @@ function stripWalk(value) {
8782
8901
 
8783
8902
  //#endregion
8784
8903
  //#region src/cli/commands/refused-baseline-remedy.ts
8904
+ const ACCEPT_REFUSED_REMEDY = "Once the deployed template no longer reads a parameter at this resource, cdkd state refresh-observed '<stack>' --accept-refused '<logicalId>' --template '<file>' records a baseline on your assertion that AWS holds no secret the template does not spell as a dynamic reference.";
8785
8905
  function refusedBaselineRemedy(record) {
8786
- if (hasUnverifiableParameterRefusal(record)) return "Deploying a change does NOT clear this refusal: the resource reads a template parameter whose deployed value cdkd could not prove, so only a deploy that replaces the resource, or a re-import while a CloudFormation stack can prove that value, restores a baseline.";
8787
- if (hasReasonlessBaselineRefusal(record)) return "This refusal was recorded without a reason this cdkd recognizes: a deploy that actually CHANGES this resource restores a baseline unless the resource reads a template parameter, and then only a deploy that replaces the resource, or a re-import while a CloudFormation stack can prove the parameter's deployed value, does.";
8906
+ if (hasUnverifiableParameterRefusal(record)) return "Deploying a change does NOT clear this refusal: the resource reads a template parameter whose deployed value cdkd could not prove, so only a deploy that replaces the resource, or a re-import while a CloudFormation stack can prove that value, restores a baseline. " + ACCEPT_REFUSED_REMEDY;
8907
+ if (hasReasonlessBaselineRefusal(record)) return "This refusal was recorded without a reason this cdkd recognizes: a deploy that actually CHANGES this resource restores a baseline unless the resource reads a template parameter, and then only a deploy that replaces the resource, or a re-import while a CloudFormation stack can prove the parameter's deployed value, does. " + ACCEPT_REFUSED_REMEDY;
8788
8908
  }
8789
8909
 
8790
8910
  //#endregion
@@ -9953,7 +10073,7 @@ async function runAccept(reports, stateBackend, stateConfig, awsClients, options
9953
10073
  setAtPath(newBaseline, change.path, change.awsValue);
9954
10074
  accepted.push(change);
9955
10075
  }
9956
- const redactedBaseline = redactSecretsForState(newBaseline, outcome.secrets, existing.properties ?? {}, hasObserved ? STATE_SOURCED_BASELINE_RULES : STATE_SOURCED_READBACK_RULES);
10076
+ const redactedBaseline = withPhysicalNameCopies(outcome.secrets, otherPhysicalIds(report.state.resources, outcome.logicalId), () => redactSecretsForState(newBaseline, outcome.secrets, existing.properties ?? {}, hasObserved ? STATE_SOURCED_BASELINE_RULES : STATE_SOURCED_READBACK_RULES));
9957
10077
  let recordedChanges = 0;
9958
10078
  for (const change of accepted) {
9959
10079
  if (deepEqualUnordered(getAtPath(redactedBaseline, change.path), change.awsValue)) {
@@ -10446,6 +10566,7 @@ async function runRevert(reports, regionScope, stateBackend, stateConfig, awsCli
10446
10566
  provisionedBy: stateResource.provisionedBy
10447
10567
  }).provider;
10448
10568
  const secrets = /* @__PURE__ */ new Map();
10569
+ for (const id of otherPhysicalIds(report.state.resources, outcome.logicalId)) recordPhysicalNameCopy(secrets, id);
10449
10570
  const revertBaseline = stateResource.observedProperties ?? stateResource.properties ?? {};
10450
10571
  const unresolvedTokens = /* @__PURE__ */ new Set();
10451
10572
  const noteUnresolved = (tokens) => {
@@ -10750,7 +10871,7 @@ function printRevertPlan(reports, out, recovery) {
10750
10871
  ...accountArgs(recovery)
10751
10872
  ]) : void 0;
10752
10873
  const refreshAccountNote = refresh === void 0 ? "" : withheldAccountClause(recovery, "the command below prints").trimEnd();
10753
- out.write(" The template does not declare these, so cdkd cannot tell an AWS-authored value from an out-of-band change and will not reset either (issue #1626). " + (refresh === void 0 ? "Re-deploy if you want them reverted too; this record's identity cannot be named safely in a command, so none is offered.\n" : `Populate observedProperties with the command below, or re-deploy, if you want them reverted too.${refreshAccountNote === "" ? "" : ` ${refreshAccountNote}`}\n`));
10874
+ out.write(" The template does not declare these, so cdkd cannot tell an AWS-authored value from an out-of-band change and will not reset either. " + (refresh === void 0 ? "Re-deploy if you want them reverted too; this record's identity cannot be named safely in a command, so none is offered.\n" : `Populate observedProperties with the command below, or re-deploy, if you want them reverted too.${refreshAccountNote === "" ? "" : ` ${refreshAccountNote}`}\n`));
10754
10875
  if (refresh !== void 0) out.write(` Populate with: ${refresh.command}\n`);
10755
10876
  }
10756
10877
  }
@@ -10992,6 +11113,11 @@ function maskMarkedNoEchoBaselineWithLeaves(baseline, record, observed) {
10992
11113
  ...widened.length > 0 && { noEchoLeaves: canonicalCoordinates([...marked, ...widened]) }
10993
11114
  };
10994
11115
  }
11116
+ function otherPhysicalIds(resources, logicalId) {
11117
+ const ids = [];
11118
+ for (const [id, record] of Object.entries(resources ?? {})) if (id !== logicalId && typeof record?.physicalId === "string") ids.push(record.physicalId);
11119
+ return ids;
11120
+ }
10995
11121
 
10996
11122
  //#endregion
10997
11123
  //#region src/cli/commands/stack-lock-guard.ts
@@ -11150,6 +11276,7 @@ function holdsSecretDerivedPrincipalRecord(resource) {
11150
11276
 
11151
11277
  //#endregion
11152
11278
  //#region src/cli/commands/destroy-runner.ts
11279
+ const NOECHO_PRINTING_BIND_CAP_MS = 1e4;
11153
11280
  const NESTED_STACK_TYPE = "AWS::CloudFormation::Stack";
11154
11281
  function recordDestroyEvent(recorder, event) {
11155
11282
  recorder?.record(maskEventTextWithBoundBags(event));
@@ -11278,15 +11405,37 @@ async function runDestroyForStack(stackName, state, ctx) {
11278
11405
  logger.info(`\nResources to be deleted (${resourceCount}):`);
11279
11406
  for (const [logicalId, resource] of Object.entries(state.resources)) logger.info(` - ${displaySafe(logicalId)} (${displaySafe(resource.resourceType)})`);
11280
11407
  const protectedCount = ctx.removeProtection ? countProtectedResources(state) + countProtectedJournaledOrphans(journaledOrphans, state.region) : 0;
11408
+ let binding;
11409
+ const stackNoEchoValues = () => {
11410
+ if (binding === void 0) {
11411
+ let timer;
11412
+ const capped = new Promise((resolve) => {
11413
+ timer = setTimeout(() => {
11414
+ logger.warn(safeMsg`The stack's NoEcho values were not bound within ${NOECHO_PRINTING_BIND_CAP_MS / 1e3}s, ` + "so a resource name taken from one may print unmasked where state holds it in plaintext (state written before schema v11).");
11415
+ resolve(void 0);
11416
+ }, NOECHO_PRINTING_BIND_CAP_MS);
11417
+ timer.unref?.();
11418
+ });
11419
+ binding = {
11420
+ bound: Promise.resolve().then(() => ctx.noEchoReresolver?.printingNoEchoValues()).catch(() => void 0).finally(() => clearTimeout(timer)),
11421
+ capped
11422
+ };
11423
+ }
11424
+ return Promise.race([binding.bound, binding.capped]);
11425
+ };
11281
11426
  const orphansAtDestroy = state.orphans ?? [];
11282
11427
  if (orphansAtDestroy.length > 0) {
11283
11428
  logger.info(`\n${orphansAtDestroy.length} resource(s) are still in AWS from an earlier failed deploy. cdkd left them there because their DeletionPolicy is Retain (CDK's RemovalPolicy.RETAIN), and has been tracking them so a later deploy of this stack could adopt them back instead of colliding with their names.`);
11284
11429
  logger.info("Destroying this stack deletes that record. The resources stay in AWS, may continue to incur charges, and cdkd will no longer know about them:");
11285
- for (const entry of orphansAtDestroy) logger.info(` - ${displaySafe(entry.logicalId)} (${displaySafe(entry.state.resourceType)}) ${displaySafe(entry.state.physicalId)}`);
11430
+ const orphanListing = orphanRecordsPrintingBag(orphansAtDestroy, await stackNoEchoValues());
11431
+ for (const entry of orphansAtDestroy) withPrintingSecrets(orphanListing, () => logger.info(` - ${displaySafe(entry.logicalId)} (${displaySafe(entry.state.resourceType)}) ${displaySafe(entry.state.physicalId)}`));
11286
11432
  }
11287
11433
  if (journaledOrphans.count > 0) {
11288
11434
  logger.info(`\n${journaledOrphans.count} resource(s) were created by a failed deploy and are recorded only in the rollback journal. Each is deleted first as its DeletionPolicy directs (Retain keeps it in AWS, Snapshot takes the final snapshot), unless state or a later deploy may own it, which is warned about instead:`);
11289
- for (const line of journaledOrphanLines(journaledOrphans, logger)) logger.info(line);
11435
+ const listing = journaledOrphanPrintingBag(journaledOrphans.segments.flatMap(({ ops }) => ops), state.resources, await stackNoEchoValues());
11436
+ withPrintingSecrets(listing, () => {
11437
+ for (const line of journaledOrphanLines(journaledOrphans, logger)) logger.info(line);
11438
+ });
11290
11439
  }
11291
11440
  if (!ctx.skipConfirmation) {
11292
11441
  if (process.stdin.isTTY !== true) throw new CdkdError(`The destroy confirmation prompt for stack ${displayStackName(stackName)} cannot run in a non-interactive environment. Pass --yes / -y to confirm the destroy (cdkd destroy also accepts -f / --force), or run the command from a real terminal.`, "NON_INTERACTIVE_CONFIRM");
@@ -11433,9 +11582,11 @@ async function runDestroyForStack(stackName, state, ctx) {
11433
11582
  secretPrincipalRegions = ctx.resolveSecretDerivedPrincipals !== void 0 && (state.parentStack === void 0 || ctx.resolveSecretDerivedPrincipals.inheritedProducerRegions !== void 0) ? destroyProducerRegions(state, ctx.resolveSecretDerivedPrincipals.inheritedProducerRegions) : void 0;
11434
11583
  if (ctx.resolveSecretDerivedPrincipals !== void 0 && secretPrincipalRegions === void 0 && Object.values(state.resources ?? {}).some((r) => holdsSecretDerivedPrincipalRecord(r))) logger.warn(safeMsg`${stackName}: this nested child's secret-derived principal records resolve when destroyed through its parent stack; destroyed on its own they are skipped.`);
11435
11584
  renderer.start();
11585
+ stackNoEchoValues();
11436
11586
  const journaledUnderLock = await loadJournaledOrphans(ctx.stateBackend, stackName, regionForState, state.orphans, journaledOrphans.unreadable === true ? { warn: () => void 0 } : logger);
11437
11587
  if (journaledUnderLock.unreadable === true && journaledOrphans.unreadable !== true) throw new Error(`The rollback journal of stack ${displayStackName(stackName)} (${displaySafe(regionForState, { asciiOnly: true })}) was readable when this run started and cannot be read now (see the warning above). Nothing was deleted; re-run the destroy once it can be read.`);
11438
11588
  if (!sameJournaledOrphans(journaledUnderLock, journaledOrphans)) throw new Error(`The rollback journal of stack ${displayStackName(stackName)} (${displaySafe(regionForState, { asciiOnly: true })}) recorded ${journaledOrphans.count} resource(s) a failed deploy created when this run started and ${journaledUnderLock.count} now, not the same ones — another cdkd process wrote to it. Nothing was deleted; re-run the destroy to act on the current journal.`);
11589
+ const noEchoValues = await stackNoEchoValues();
11439
11590
  if (journaledUnderLock.count > 0) {
11440
11591
  const orphanOutcome = await deleteJournaledOrphans(journaledUnderLock, { ...state.resources }, stackName, {
11441
11592
  providerRegistry: destroyProviderRegistry,
@@ -11451,7 +11602,10 @@ async function runDestroyForStack(stackName, state, ctx) {
11451
11602
  }),
11452
11603
  importedProducerRegions: producerRegionsFromState(state),
11453
11604
  ...state.parentStack !== void 0 && { producerRegionsIncomplete: true }
11454
- }, { isInterrupted: () => lock.interrupted });
11605
+ }, {
11606
+ isInterrupted: () => lock.interrupted,
11607
+ noEchoValues
11608
+ });
11455
11609
  result.errorCount += orphanOutcome.failures;
11456
11610
  journaledOrphanFailures = orphanOutcome.failures;
11457
11611
  result.skippedCount += orphanOutcome.skipped;
@@ -11512,6 +11666,7 @@ async function runDestroyForStack(stackName, state, ctx) {
11512
11666
  const graph = dagBuilder.buildGraph(template);
11513
11667
  const executionLevels = dagBuilder.getExecutionLevels(graph);
11514
11668
  logger.debug(`Dependency graph: ${executionLevels.length} level(s)`);
11669
+ const printingBagOf = secretNamePrintingBags(state.resources, noEchoValues);
11515
11670
  for (let levelIndex = executionLevels.length - 1; levelIndex >= 0; levelIndex--) {
11516
11671
  if (lock.interrupted) {
11517
11672
  logger.debug("Interrupted (draining) — not scheduling further deletion levels");
@@ -11521,7 +11676,7 @@ async function runDestroyForStack(stackName, state, ctx) {
11521
11676
  if (!level) continue;
11522
11677
  logger.debug(`Deletion level ${executionLevels.length - levelIndex}/${executionLevels.length} (${level.length} resources)`);
11523
11678
  const stackRegion = state.region ?? ctx.baseRegion;
11524
- const deletePromises = level.map((logicalId) => withPrintingSecrets(secretNamePrintingBag(logicalId, state.resources), async () => {
11679
+ const deletePromises = level.map((logicalId) => withPrintingSecrets(printingBagOf(logicalId), async () => {
11525
11680
  if (lock.interrupted) return;
11526
11681
  const resource = state.resources[logicalId];
11527
11682
  if (!resource) {
@@ -12290,7 +12445,7 @@ async function destroyCommand(stackArgs, options) {
12290
12445
  }
12291
12446
  }
12292
12447
  function createDestroyCommand() {
12293
- const cmd = new Command("destroy").description("Destroy all resources in the stack").argument("[stacks...]", "Stack name(s) to destroy. Accepts CDK display paths (e.g. 'MyStage/Api') with wildcards ('MyStage/*', '**'), or exact physical CloudFormation names (e.g. 'MyStage-Api').").option("--all", "Destroy every top-level stack (top-level only; Stage stacks: '**')", false).option("--purge-events", `After a clean destroy, also delete the stack's deployment-event history (issue #808 store), purging every earlier version under the stack's deployments/ prefix on a versioned state bucket too unless a warning says otherwise. By default events survive destroy as post-mortem context. Skipped when the destroy fails or is interrupted (those events aid the retry). Equivalent for an already-destroyed stack: cdkd events prune ${commandHole("stack")} --all.`, false).action(withErrorHandling(destroyCommand));
12448
+ const cmd = new Command("destroy").description("Destroy all resources in the stack").argument("[stacks...]", "Stack name(s) to destroy. Accepts CDK display paths (e.g. 'MyStage/Api') with wildcards ('MyStage/*', '**'), or exact physical CloudFormation names (e.g. 'MyStage-Api').").option("--all", "Destroy every top-level stack (top-level only; Stage stacks: '**')", false).option("--purge-events", `After a clean destroy, also delete the stack's deployment-event history, purging every earlier version under the stack's deployments/ prefix on a versioned state bucket too unless a warning says otherwise. By default events survive destroy as post-mortem context. Skipped when the destroy fails or is interrupted (those events aid the retry). Equivalent for an already-destroyed stack: cdkd events prune ${commandHole("stack")} --all.`, false).action(withErrorHandling(destroyCommand));
12294
12449
  [
12295
12450
  ...commonOptions,
12296
12451
  ...appOptions,
@@ -15270,7 +15425,7 @@ async function cachedTypeSchemaInfo(resourceType, cfnClient, cache) {
15270
15425
  function typeIsImportUnsupported(entry) {
15271
15426
  return entry.importSupport === "unsupported";
15272
15427
  }
15273
- const CFN_IMPORT_REFUSED_DESPITE_REGISTRY = /* @__PURE__ */ new Map([["AWS::AppSync::GraphQLApi", "measured in us-east-1 on 2026-09-18: the registry schema declares a read handler and FULLY_MUTABLE, yet CreateChangeSet --change-set-type IMPORT rejected the type"]]);
15428
+ const CFN_IMPORT_REFUSED_DESPITE_REGISTRY = /* @__PURE__ */ new Map([["AWS::AppSync::GraphQLApi", "measured against AWS: the registry schema declares a read handler and FULLY_MUTABLE, yet CreateChangeSet --change-set-type IMPORT rejected the type"]]);
15274
15429
  function cfnRefusesImportDespiteRegistry(resourceType) {
15275
15430
  return CFN_IMPORT_REFUSED_DESPITE_REGISTRY.get(resourceType);
15276
15431
  }
@@ -16149,7 +16304,7 @@ async function runPerStackImportLoop(args) {
16149
16304
  if (!options.yes) {
16150
16305
  const phase2Note = totalPhase2Creates > 0 ? ` Phase 2 will CREATE ${totalPhase2Creates} non-importable resource(s) across the tree.` : "";
16151
16306
  const recreateNote = totalRecreate > 0 ? ` cdkd will also DELETE ${totalRecreate} AWS resource(s) between phases so CFn can re-CREATE them in phase 2 (brief unavailability window — see the plan above for the affected resources, and for any marked as not confirmed to be re-created).` : "";
16152
- if (!await confirmPrompt$2(`Create ${perStackPlans.length} CloudFormation stack(s) by importing the cdkd nested-stack tree rooted at ${quotedOrNotShown(rootStackName)} (${quotedRegion(rootRegion)}) — leaf-first, per-stack IMPORT loop (#464 design §4.3).` + phase2Note + recreateNote + (totalRecreate > 0 ? ` All other AWS resources are unchanged.` : ` AWS resources are unchanged.`) + ` cdkd state for every adopted stack will be deleted on success.`)) {
16307
+ if (!await confirmPrompt$2(`Create ${perStackPlans.length} CloudFormation stack(s) by importing the cdkd nested-stack tree rooted at ${quotedOrNotShown(rootStackName)} (${quotedRegion(rootRegion)}) — leaf-first, per-stack IMPORT loop.` + phase2Note + recreateNote + (totalRecreate > 0 ? ` All other AWS resources are unchanged.` : ` AWS resources are unchanged.`) + ` cdkd state for every adopted stack will be deleted on success.`)) {
16153
16308
  logger.info("Migration cancelled. cdkd state and CloudFormation are unchanged.");
16154
16309
  return {
16155
16310
  outcome: "cancelled",
@@ -16537,6 +16692,250 @@ function spacedAccountClause(recovery, where) {
16537
16692
  return clause === "" ? "" : ` ${clause}`;
16538
16693
  }
16539
16694
 
16695
+ //#endregion
16696
+ //#region src/cli/commands/state-refresh-observed.ts
16697
+ function collectAcceptRefused(value, previous) {
16698
+ if (value === "") throw new InvalidArgumentError("--accept-refused takes one logical id.");
16699
+ return [...previous ?? [], value];
16700
+ }
16701
+ function readAcceptRefusedTemplate(path) {
16702
+ const shown = displayIdent(path, { maxCodePoints: STACK_REF_MAX_CODE_POINTS });
16703
+ let raw;
16704
+ try {
16705
+ raw = readFileSync(path, "utf-8");
16706
+ } catch (err) {
16707
+ throw new Error(`Failed to read the --template file ${shown}: ${fsReadErrorCause(err)}`);
16708
+ }
16709
+ let parsed;
16710
+ try {
16711
+ parsed = parseCfnTemplateWithFormat(raw).template;
16712
+ } catch {
16713
+ parsed = void 0;
16714
+ }
16715
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error(`The --template file ${shown} is not a valid CloudFormation template (JSON or YAML).`);
16716
+ return parsed;
16717
+ }
16718
+ function judgeAcceptRefused(record, logicalId, template, stack) {
16719
+ const id = displayIdent(logicalId);
16720
+ if (record === void 0) return {
16721
+ ok: false,
16722
+ reason: `The stack's state record has no resource ${id}.`
16723
+ };
16724
+ if (record.observedBaselineRefused !== true) return {
16725
+ ok: false,
16726
+ reason: `${id} carries no observed-baseline refusal, so there is nothing to accept. Run 'cdkd state refresh-observed' without --accept-refused to refresh it.`
16727
+ };
16728
+ const reason = record.observedBaselineRefusalReason;
16729
+ if (reason === "incomplete-resolution") return {
16730
+ ok: false,
16731
+ reason: `The refusal on ${id} is not over a template parameter, and --accept-refused overrides only that class. A deploy that actually CHANGES the resource restores its baseline.`
16732
+ };
16733
+ const legacy = reason !== "unverifiable-parameter";
16734
+ const verdict = resourcesNamingDeclaredParameter(template);
16735
+ if (verdict.failedClosed !== void 0) return {
16736
+ ok: false,
16737
+ reason: `The template's parameter dependence could not be judged (${verdict.failedClosed}), so the refusal on ${id} cannot be accepted.`
16738
+ };
16739
+ const resources = template["Resources"];
16740
+ const definition = Object.hasOwn(resources, logicalId) ? resources[logicalId] : void 0;
16741
+ if (definition === null || typeof definition !== "object" || Array.isArray(definition)) return {
16742
+ ok: false,
16743
+ reason: `The template defines no resource ${id}. Pass the template this stack was last deployed from (for a nested stack, the child's own template).`
16744
+ };
16745
+ if (definition["Type"] !== record.resourceType) return {
16746
+ ok: false,
16747
+ reason: `The template's ${id} is not the resource type the state record holds, so the template does not describe this resource.`
16748
+ };
16749
+ if (verdict(logicalId)) return {
16750
+ ok: false,
16751
+ reason: `In this template ${id} still depends on a template parameter, and cdkd binds a parameter's Default, which is not what AWS may hold. Make the template spell the value as a dynamic reference (or a literal), deploy it, then run this again.`
16752
+ };
16753
+ const shape = matchesDeployedShape(definition["Properties"] ?? {}, record.properties, stack);
16754
+ if (shape === "unprovable") return {
16755
+ ok: false,
16756
+ reason: `The template defines ${id} with a function cdkd cannot work out from the state records (only Ref, Fn::GetAtt, Fn::Join, Fn::Sub and Fn::Select of what the stack records), so cdkd cannot prove the template was deployed to the resource. A deploy does not change that; replacing the resource clears the refusal.`
16757
+ };
16758
+ if (shape === "differs") return {
16759
+ ok: false,
16760
+ reason: `The state record of ${id} does not hold what this template defines for it, so cdkd cannot prove the template was deployed to the resource. Pass the template it was last deployed from, or deploy this one and run this again. If cdkd records this resource with fewer properties than the template sets (one it does not send), a deploy does not change that, and replacing the resource clears the refusal.`
16761
+ };
16762
+ const hit = parameterDefaultInProperties(template, record.properties);
16763
+ if (hit !== void 0) return {
16764
+ ok: false,
16765
+ reason: `A recorded property of ${id} holds the Default of the template parameter ${displayIdent(hit)}, so the record may still hold the placeholder the refusal distrusted. If the template no longer uses the parameter there, deploy it and run this again. If the value only coincides with that Default, change or remove the parameter's Default and deploy, or replace the resource.`
16766
+ };
16767
+ return {
16768
+ ok: true,
16769
+ legacy
16770
+ };
16771
+ }
16772
+ const ACCOUNT = Symbol("account");
16773
+ const MISSING = Symbol("missing");
16774
+ function matchesDeployedShape(node, recorded, stack) {
16775
+ let differs = false;
16776
+ let unprovable = false;
16777
+ const pending = [[node, recorded]];
16778
+ while (pending.length > 0) {
16779
+ const [t, r] = pending.pop();
16780
+ if (t === null || typeof t !== "object") {
16781
+ if (t !== r) differs = true;
16782
+ } else if (Array.isArray(t)) {
16783
+ if (!Array.isArray(r) || r.length !== t.length) differs = true;
16784
+ else t.forEach((item, i) => pending.push([item, r[i]]));
16785
+ } else if (isIntrinsic(t)) {
16786
+ const parts = evaluate(t, stack);
16787
+ if (parts === void 0) {
16788
+ unprovable = true;
16789
+ continue;
16790
+ }
16791
+ if (parts === MISSING) {
16792
+ differs = true;
16793
+ continue;
16794
+ }
16795
+ const text = typeof r === "string" ? r : typeof r === "number" ? String(r) : void 0;
16796
+ if (text === void 0 || !partsPattern(parts).test(text)) differs = true;
16797
+ } else if (r === null || typeof r !== "object" || Array.isArray(r)) differs = true;
16798
+ else {
16799
+ const tKeys = Object.keys(t).sort();
16800
+ const rKeys = Object.keys(r).sort();
16801
+ if (tKeys.length !== rKeys.length || tKeys.some((k, i) => k !== rKeys[i])) differs = true;
16802
+ for (const k of tKeys) {
16803
+ if (!Object.hasOwn(r, k)) continue;
16804
+ pending.push([t[k], r[k]]);
16805
+ }
16806
+ }
16807
+ }
16808
+ return unprovable ? "unprovable" : differs ? "differs" : "match";
16809
+ }
16810
+ function isIntrinsic(node) {
16811
+ const keys = Object.keys(node);
16812
+ return keys.length === 1 && (keys[0] === "Ref" || keys[0].startsWith("Fn::"));
16813
+ }
16814
+ function partsPattern(parts) {
16815
+ const body = parts.map((p) => p === ACCOUNT ? "[0-9]{12}" : p.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("");
16816
+ return new RegExp(`^${body}$`);
16817
+ }
16818
+ function evaluate(node, stack) {
16819
+ if (typeof node === "string") return [node];
16820
+ if (typeof node === "number" && Number.isFinite(node)) return [String(node)];
16821
+ if (node === null || typeof node !== "object" || Array.isArray(node) || !isIntrinsic(node)) return;
16822
+ const [fn, arg] = Object.entries(node)[0];
16823
+ switch (fn) {
16824
+ case "Ref": return typeof arg === "string" ? reference(arg, stack) : void 0;
16825
+ case "Fn::GetAtt": {
16826
+ const pair = typeof arg === "string" ? arg.includes(".") ? [arg.slice(0, arg.indexOf(".")), arg.slice(arg.indexOf(".") + 1)] : void 0 : arg;
16827
+ if (!Array.isArray(pair) || pair.length !== 2) return void 0;
16828
+ const [target, attribute] = pair;
16829
+ return typeof target === "string" && typeof attribute === "string" ? attributeOf(target, attribute, stack) : void 0;
16830
+ }
16831
+ case "Fn::Join": {
16832
+ if (!Array.isArray(arg) || arg.length !== 2) return void 0;
16833
+ const [delimiter, list] = arg;
16834
+ if (typeof delimiter !== "string" || !Array.isArray(list)) return void 0;
16835
+ const out = [];
16836
+ let missing = false;
16837
+ for (let i = 0; i < list.length; i++) {
16838
+ const parts = evaluate(list[i], stack);
16839
+ if (parts === void 0) return void 0;
16840
+ if (i > 0 && delimiter !== "") out.push(delimiter);
16841
+ if (parts === MISSING) missing = true;
16842
+ else out.push(...parts);
16843
+ }
16844
+ return missing ? MISSING : out;
16845
+ }
16846
+ case "Fn::Select": {
16847
+ if (!Array.isArray(arg) || arg.length !== 2) return void 0;
16848
+ const [index, list] = arg;
16849
+ const i = typeof index === "number" ? index : typeof index === "string" ? Number(index) : NaN;
16850
+ if (!Number.isInteger(i) || !Array.isArray(list) || i < 0 || i >= list.length) return;
16851
+ return evaluate(list[i], stack);
16852
+ }
16853
+ case "Fn::Sub": {
16854
+ const [text, vars] = Array.isArray(arg) ? arg : [arg, {}];
16855
+ if (typeof text !== "string" || vars === null || typeof vars !== "object" || Array.isArray(vars)) return;
16856
+ const out = [];
16857
+ let missing = false;
16858
+ let last = 0;
16859
+ for (const m of text.matchAll(/\$\{([^}]*)\}/g)) {
16860
+ out.push(text.slice(last, m.index));
16861
+ last = m.index + m[0].length;
16862
+ const name = m[1];
16863
+ if (name.startsWith("!")) {
16864
+ out.push(`\${${name.slice(1)}}`);
16865
+ continue;
16866
+ }
16867
+ const parts = Object.hasOwn(vars, name) ? evaluate(vars[name], stack) : name.includes(".") ? attributeOf(name.slice(0, name.indexOf(".")), name.slice(name.indexOf(".") + 1), stack) : reference(name, stack);
16868
+ if (parts === void 0) return void 0;
16869
+ if (parts === MISSING) missing = true;
16870
+ else out.push(...parts);
16871
+ }
16872
+ out.push(text.slice(last));
16873
+ return missing ? MISSING : out;
16874
+ }
16875
+ default: return;
16876
+ }
16877
+ }
16878
+ function reference(name, stack) {
16879
+ switch (name) {
16880
+ case "AWS::Region": return [stack.region];
16881
+ case "AWS::Partition": return [derivePartitionAndUrlSuffix(stack.region).partition];
16882
+ case "AWS::URLSuffix": return [derivePartitionAndUrlSuffix(stack.region).urlSuffix];
16883
+ case "AWS::AccountId": return [ACCOUNT];
16884
+ case "AWS::StackName": return [stack.stackName];
16885
+ }
16886
+ if (name.startsWith("AWS::")) return void 0;
16887
+ if (!Object.hasOwn(stack.resources, name)) return MISSING;
16888
+ const physicalId = stack.resources[name]?.physicalId;
16889
+ return typeof physicalId === "string" ? [physicalId] : void 0;
16890
+ }
16891
+ function attributeOf(target, attribute, stack) {
16892
+ if (!Object.hasOwn(stack.resources, target)) return MISSING;
16893
+ const attributes = stack.resources[target]?.attributes;
16894
+ if (attributes === null || typeof attributes !== "object") return MISSING;
16895
+ if (!Object.hasOwn(attributes, attribute)) return MISSING;
16896
+ const value = attributes[attribute];
16897
+ if (typeof value === "string") return [value];
16898
+ if (typeof value === "number" && Number.isFinite(value)) return [String(value)];
16899
+ }
16900
+ const DEFAULT_SUBSTRING_MIN_LENGTH = 4;
16901
+ function parameterDefaultInProperties(template, properties) {
16902
+ const parameters = template["Parameters"];
16903
+ if (parameters === null || typeof parameters !== "object" || Array.isArray(parameters)) return;
16904
+ const needles = /* @__PURE__ */ new Map();
16905
+ for (const [name, decl] of Object.entries(parameters)) {
16906
+ if (decl === null || typeof decl !== "object") continue;
16907
+ const raw = decl["Default"];
16908
+ let text;
16909
+ if (typeof raw === "string") text = raw;
16910
+ else if (typeof raw === "number" && Number.isFinite(raw)) text = String(raw);
16911
+ if (text === void 0 || text === "") continue;
16912
+ if (!needles.has(text)) needles.set(text, name);
16913
+ const type = decl["Type"];
16914
+ if (typeof type === "string" && (type === "CommaDelimitedList" || type.startsWith("List<"))) for (const item of text.split(",")) {
16915
+ const trimmed = item.trim();
16916
+ if (trimmed !== "" && !needles.has(trimmed)) needles.set(trimmed, name);
16917
+ }
16918
+ }
16919
+ if (needles.size === 0) return void 0;
16920
+ const pending = [properties];
16921
+ while (pending.length > 0) {
16922
+ const value = pending.pop();
16923
+ if (typeof value === "string" || typeof value === "number" && Number.isFinite(value)) {
16924
+ const text = String(value);
16925
+ const exact = needles.get(text);
16926
+ if (exact !== void 0) return exact;
16927
+ for (const [needle, name] of needles) if (needle.length >= DEFAULT_SUBSTRING_MIN_LENGTH && text.includes(needle)) return name;
16928
+ } else if (Array.isArray(value)) pending.push(...value);
16929
+ else if (value !== null && typeof value === "object") pending.push(...Object.values(value));
16930
+ }
16931
+ }
16932
+ function acceptRefusedAssertion(logicalId, resourceType, stackRef, legacy) {
16933
+ return `Accepting the refused observed baseline of ${displayIdent(logicalId)} (${displayIdent(resourceType)}) in ${stackRef}. ` + (legacy ? "The refusal was recorded without a reason this cdkd recognizes, and is treated as one over a template parameter whose deployed value cdkd could not prove. " : "cdkd refused it because the resource reads a template parameter whose deployed value it could not prove. ") + "By accepting you assert that this template is deployed, and that AWS holds NO secret at any position of this resource that the template does not spell as a dynamic reference ({{resolve:...}}): whatever AWS holds there now is written to state.json as the drift baseline, in plaintext. A deploy does not resend a value the record already holds, so a literal there need not be what AWS holds.";
16934
+ }
16935
+ async function confirmAcceptRefused(assertion) {
16936
+ return confirmOrRefuse(`${assertion}\nAccept it?`, { refusal: "The cdkd state refresh-observed --accept-refused confirmation prompt cannot run in a non-interactive environment. Pass -y / --yes to make the assertion, or run the command from a real terminal." });
16937
+ }
16938
+
16540
16939
  //#endregion
16541
16940
  //#region src/cli/commands/state.ts
16542
16941
  const STATE_READ_FAILED_REASON = "state record could not be read; run `cdkd state show` for the error";
@@ -17109,7 +17508,7 @@ function treeToShowJson(node) {
17109
17508
  };
17110
17509
  }
17111
17510
  function createStateShowCommand() {
17112
- const cmd = new Command("show").description("Show the full cdkd state record for a stack (metadata, outputs, resources)").argument("<stack>", "Stack name (physical CloudFormation name)").option("--json", "Output the raw state and lock as JSON", false).option("--show-nested", "Recursively show every nested-stack child under the target stack (issue #555 A4)", false).addOption(stackRegionOption$1()).action(withErrorHandling(stateShowCommand));
17511
+ const cmd = new Command("show").description("Show the full cdkd state record for a stack (metadata, outputs, resources)").argument("<stack>", "Stack name (physical CloudFormation name)").option("--json", "Output the raw state and lock as JSON", false).option("--show-nested", "Recursively show every nested-stack child under the target stack", false).addOption(stackRegionOption$1()).action(withErrorHandling(stateShowCommand));
17113
17512
  [...commonOptions, ...stateOptions].forEach((opt) => cmd.addOption(opt));
17114
17513
  cmd.addOption(deprecatedRegionOption);
17115
17514
  return cmd;
@@ -17705,6 +18104,17 @@ async function stateRefreshObservedCommand(stackArgs, options) {
17705
18104
  const providerClasses = await loadProviderClasses();
17706
18105
  const logger = getLogger();
17707
18106
  if (options.verbose) logger.setLevel("debug");
18107
+ let accept;
18108
+ if (options.acceptRefused !== void 0) {
18109
+ if (options.acceptRefused.length !== 1) throw new Error("--accept-refused takes exactly one logical id per run.");
18110
+ if (options.all || stackArgs.length !== 1) throw new Error("--accept-refused applies to one resource of ONE stack: pass a single stack name, not --all.");
18111
+ if (options.template === void 0) throw new Error("--accept-refused requires --template <file>: the synthesized template this stack was last deployed from.");
18112
+ accept = {
18113
+ logicalId: options.acceptRefused[0],
18114
+ template: readAcceptRefusedTemplate(options.template),
18115
+ yes: options.yes ?? false
18116
+ };
18117
+ } else if (options.template !== void 0) throw new Error("--template is used only with --accept-refused.");
17708
18118
  if (!options.all && stackArgs.length === 0) throw new Error(`Stack name is required. Usage: cdkd state refresh-observed ${commandHole("stacks...")} | --all`);
17709
18119
  const setup = await setupStateBackend(options);
17710
18120
  const stackRegionScopes = /* @__PURE__ */ new Map();
@@ -17753,7 +18163,7 @@ async function stateRefreshObservedCommand(stackArgs, options) {
17753
18163
  refuseMalformedState(loaded.state, target.stackName, target.region, lockRecovery);
17754
18164
  refuseMalformedResourceEntries(loaded.state, target.stackName, target.region, lockRecovery);
17755
18165
  }
17756
- if (!options.yes && !options.dryRun) {
18166
+ if (!options.yes && !options.dryRun && accept === void 0) {
17757
18167
  const targetList = targets.map(describedStackRef).join(", ");
17758
18168
  if (targets.some(refIsDescribed)) logger.info(STATE_LIST_POINTER);
17759
18169
  if (!await confirmRefresh(`Refresh observedProperties for ${targets.length} stack(s) (${targetList})?`)) {
@@ -17784,7 +18194,8 @@ async function stateRefreshObservedCommand(stackArgs, options) {
17784
18194
  const counts = await runWithStackAwsClients(scope.clients, () => refreshObservedForStack(target.stackName, targetRegion, setup.stateBackend, setup.lockManager, scope.registry, {
17785
18195
  dryRun: options.dryRun ?? false,
17786
18196
  logger,
17787
- lockRecovery
18197
+ lockRecovery,
18198
+ ...accept && { accept }
17788
18199
  }));
17789
18200
  totalRefreshed += counts.refreshed;
17790
18201
  totalUnsupported += counts.unsupported;
@@ -17827,7 +18238,7 @@ async function warnOnLiveForeignLock(lockManager, stackName, region, logger) {
17827
18238
  } catch {}
17828
18239
  }
17829
18240
  async function refreshObservedForStack(stackName, region, stateBackend, lockManager, providerRegistry, opts) {
17830
- const { logger, lockRecovery } = opts;
18241
+ const { logger, lockRecovery, accept } = opts;
17831
18242
  const ref = formatStackRefSafe({
17832
18243
  stackName,
17833
18244
  region
@@ -17837,7 +18248,33 @@ async function refreshObservedForStack(stackName, region, stateBackend, lockMana
17837
18248
  const { state, etag, migrationPending } = result;
17838
18249
  refuseMalformedState(state, stackName, region, lockRecovery);
17839
18250
  refuseMalformedResourceEntries(state, stackName, region, lockRecovery);
17840
- const entries = Object.entries(state.resources);
18251
+ let entries = Object.entries(state.resources);
18252
+ const acceptId = accept?.logicalId;
18253
+ if (accept !== void 0) {
18254
+ const record = Object.hasOwn(state.resources, accept.logicalId) ? state.resources[accept.logicalId] : void 0;
18255
+ const verdict = judgeAcceptRefused(record, accept.logicalId, accept.template, {
18256
+ stackName,
18257
+ region,
18258
+ resources: state.resources
18259
+ });
18260
+ if (!verdict.ok) throw new Error(safeMsg`${ref}: ${verdict.reason}`);
18261
+ entries = [[accept.logicalId, record]];
18262
+ if (!opts.dryRun) {
18263
+ const assertion = acceptRefusedAssertion(accept.logicalId, record.resourceType, ref, verdict.legacy);
18264
+ if (accept.yes) logger.warn(safeMsg`${assertion} (asserted with --yes)`);
18265
+ else if (!await confirmAcceptRefused(assertion)) {
18266
+ logger.info("Aborted.");
18267
+ return {
18268
+ refreshed: 0,
18269
+ unsupported: 0,
18270
+ failed: 0,
18271
+ notFound: 0,
18272
+ refusedBaseline: 0,
18273
+ refusedSticky: 0
18274
+ };
18275
+ }
18276
+ }
18277
+ }
17841
18278
  if (entries.length === 0) {
17842
18279
  logger.info(safeMsg`✓ ${ref}: no resources in state, skipping`);
17843
18280
  return {
@@ -17854,8 +18291,8 @@ async function refreshObservedForStack(stackName, region, stateBackend, lockMana
17854
18291
  let wouldUnsupported = 0;
17855
18292
  let wouldRefuse = 0;
17856
18293
  let wouldRefuseSticky = 0;
17857
- for (const [, resource] of entries) {
17858
- if (resource.observedBaselineRefused === true) {
18294
+ for (const [logicalId, resource] of entries) {
18295
+ if (resource.observedBaselineRefused === true && logicalId !== acceptId) {
17859
18296
  wouldRefuse++;
17860
18297
  if (refusedBaselineRemedy(resource) !== void 0) wouldRefuseSticky++;
17861
18298
  continue;
@@ -17873,6 +18310,7 @@ async function refreshObservedForStack(stackName, region, stateBackend, lockMana
17873
18310
  if (provider.readCurrentState) wouldRefresh++;
17874
18311
  else wouldUnsupported++;
17875
18312
  }
18313
+ if (accept !== void 0 && wouldRefresh === 0) throw new Error(safeMsg`${ref}: cdkd cannot read ${displayIdent(accept.logicalId)} back from AWS, so no ` + `baseline can be captured and its refusal stands.`);
17876
18314
  logger.info(safeMsg`Plan ${ref}: ${wouldRefresh} resource(s) would be refreshed, ${wouldUnsupported} unsupported` + (wouldRefuse > 0 ? safeMsg`, ${wouldRefuse} refused (import baseline refusal)` : ""));
17877
18315
  return {
17878
18316
  refreshed: wouldRefresh,
@@ -17900,7 +18338,7 @@ async function refreshObservedForStack(stackName, region, stateBackend, lockMana
17900
18338
  const publicSsmProver = new PublicSsmProver(region, () => driftProducerRegionEvidence(state, stackName, region, stateBackend), logger);
17901
18339
  await withStackName(stackName, async () => {
17902
18340
  const tasks = entries.map(async ([logicalId, resource]) => {
17903
- if (resource.observedBaselineRefused === true) {
18341
+ if (resource.observedBaselineRefused === true && logicalId !== acceptId) {
17904
18342
  refusedBaseline++;
17905
18343
  if (refusedBaselineRemedy(resource) !== void 0) refusedSticky++;
17906
18344
  return;
@@ -17935,6 +18373,10 @@ async function refreshObservedForStack(stackName, region, stateBackend, lockMana
17935
18373
  return;
17936
18374
  }
17937
18375
  resource.observedProperties = redactSecretsForState(maskReadbackAtCoordinates(observed, resource.properties ?? {}, [...maskedLeafCoordinatesOf(resource.properties ?? {}), ...noEchoLeavesOf(resource) ?? []]), await publicSsmProver.proofBagFor(resource.properties ?? {}), resource.properties ?? {}, STATE_SOURCED_BASELINE_RULES);
18376
+ if (logicalId === acceptId) {
18377
+ delete resource.observedBaselineRefused;
18378
+ delete resource.observedBaselineRefusalReason;
18379
+ }
17938
18380
  refreshed++;
17939
18381
  } catch (err) {
17940
18382
  failed++;
@@ -17943,12 +18385,24 @@ async function refreshObservedForStack(stackName, region, stateBackend, lockMana
17943
18385
  });
17944
18386
  await Promise.all(tasks);
17945
18387
  });
18388
+ if (accept !== void 0 && refreshed === 0) {
18389
+ if (unsupported > 0) throw new Error(safeMsg`${ref}: cdkd cannot read ${displayIdent(accept.logicalId)} back from AWS, so no ` + `baseline was captured and its refusal stands.`);
18390
+ return {
18391
+ refreshed,
18392
+ unsupported,
18393
+ failed,
18394
+ notFound,
18395
+ refusedBaseline,
18396
+ refusedSticky
18397
+ };
18398
+ }
17946
18399
  state.lastModified = Date.now();
17947
18400
  if (refreshed > 0) delete state.skippedOutputs;
17948
18401
  const saveOptions = { expectedEtag: etag };
17949
18402
  if (migrationPending) saveOptions.migrateLegacy = true;
17950
18403
  await stateBackend.saveState(stackName, region, state, saveOptions);
17951
18404
  logger.info(safeMsg`✓ ${ref}: ` + safeMsg`${refreshed} refreshed, ${unsupported} unsupported, ${failed} failed` + (notFound > 0 ? safeMsg`, ${notFound} not found in AWS` : "") + (refusedBaseline > 0 ? safeMsg`, ${refusedBaseline} refused (import baseline refusal)` : ""));
18405
+ if (accept !== void 0) logger.info(safeMsg`Accepted: the observed-baseline refusal on ${displayIdent(accept.logicalId)} was cleared ` + `and a baseline was captured.`);
17952
18406
  return {
17953
18407
  refreshed,
17954
18408
  unsupported,
@@ -17973,7 +18427,7 @@ async function confirmRefresh(prompt) {
17973
18427
  return confirmOrRefuse(prompt, { refusal: "The cdkd state refresh-observed confirmation prompt cannot run in a non-interactive environment. Pass -y / --yes to confirm the refresh, or run the command from a real terminal." });
17974
18428
  }
17975
18429
  function createStateRefreshObservedCommand() {
17976
- const cmd = new Command("refresh-observed").description("Refresh observedProperties for every resource in a stack by calling provider.readCurrentState — populates the drift baseline for stacks deployed before state schema v3, without redeploying. Run cdkd scrub first on state written by a pre-GHSA binary: the readback is redacted by POSITION against the record, so a record whose own properties still hold plaintext has nothing to redact from.").argument("[stacks...]", "Stack name(s) to refresh (physical CloudFormation names)").option("--all", "Refresh every stack in the state bucket", false).option("--dry-run", "Print the per-stack refresh count without writing state", false).addOption(stackRegionOption$1()).action(withErrorHandling(stateRefreshObservedCommand));
18430
+ const cmd = new Command("refresh-observed").description("Refresh observedProperties for every resource in a stack by calling provider.readCurrentState — populates the drift baseline for stacks deployed before state schema v3, without redeploying. Run cdkd scrub first on state written by a pre-GHSA binary: the readback is redacted by POSITION against the record, so a record whose own properties still hold plaintext has nothing to redact from.").argument("[stacks...]", "Stack name(s) to refresh (physical CloudFormation names)").option("--all", "Refresh every stack in the state bucket", false).option("--dry-run", "Print the per-stack refresh count without writing state", false).option("--accept-refused <logicalId>", "Clear the observed-baseline refusal on ONE resource and capture its baseline, asserting that AWS holds no secret the template does not spell as a dynamic reference. Requires --template and a single stack; prompts unless -y / --yes", collectAcceptRefused).option("--template <file>", "With --accept-refused: the synthesized template (JSON or YAML) this stack was last deployed from").addOption(stackRegionOption$1()).action(withErrorHandling(stateRefreshObservedCommand));
17977
18431
  [...commonOptions, ...stateOptions].forEach((opt) => cmd.addOption(opt));
17978
18432
  cmd.addOption(deprecatedRegionOption);
17979
18433
  return cmd;
@@ -18668,7 +19122,7 @@ async function eventsCommand(stackName, options) {
18668
19122
  async function resolveEventsRegion(reader, stackName, explicitRegion) {
18669
19123
  if (explicitRegion) return canonicalizeRegion(explicitRegion);
18670
19124
  const regions = await reader.listRegions(stackName);
18671
- if (regions.length === 0) throw new CdkdError(`No deployment-event history found for stack ${displayStackName(stackName)}. Events are recorded by cdkd's deploy and destroy commands (issue #808); a stack deployed by an older cdkd version has none.\nIf its history was deleted earlier, the prune subcommand with --all and --stack-region still purges the earlier versions left on a versioned state bucket, unless a warning says otherwise.`, "EVENTS_NOT_FOUND");
19125
+ if (regions.length === 0) throw new CdkdError(`No deployment-event history found for stack ${displayStackName(stackName)}. Events are recorded by cdkd's deploy and destroy commands; a stack deployed by an older cdkd version has none.\nIf its history was deleted earlier, the prune subcommand with --all and --stack-region still purges the earlier versions left on a versioned state bucket, unless a warning says otherwise.`, "EVENTS_NOT_FOUND");
18672
19126
  if (regions.length > 1) {
18673
19127
  const named = regions.filter((r) => isPasteableIdent(r));
18674
19128
  const unnamed = regions.length - named.length;
@@ -18814,7 +19268,7 @@ function colorizeEventType(eventType) {
18814
19268
  return cyan(token);
18815
19269
  }
18816
19270
  function createEventsCommand() {
18817
- 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) => {
19271
+ const cmd = new Command("events").description("Read back structured deployment events (cdkd's DescribeStackEvents equivalent)").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) => {
18818
19272
  return eventsCommand(stack, {
18819
19273
  ...options,
18820
19274
  json: options.json === true || options.format === "json"
@@ -20334,10 +20788,53 @@ function noEchoAttributeNamesCarried(prior, attributes) {
20334
20788
  const kept = prior.filter((name) => typeof name === "string" && hasOwnKey(attributes, name) && carriesSecretMask(attributes[name]));
20335
20789
  return kept.length > 0 ? { noEchoAttributeNames: kept } : {};
20336
20790
  }
20791
+ const READS_EVERY_KEY = Symbol("reads every key");
20792
+ function templateGetAttReads(template) {
20793
+ const reads = /* @__PURE__ */ new Map();
20794
+ const addRead = (logicalId, attribute) => {
20795
+ let attributes = reads.get(logicalId);
20796
+ if (!attributes) {
20797
+ attributes = /* @__PURE__ */ new Set();
20798
+ reads.set(logicalId, attributes);
20799
+ }
20800
+ if (typeof attribute === "string") {
20801
+ const segments = attribute.split(".");
20802
+ for (let i = 1; i <= segments.length; i++) attributes.add(segments.slice(0, i).join("."));
20803
+ } else attributes.add(READS_EVERY_KEY);
20804
+ };
20805
+ const walk = (value) => {
20806
+ if (Array.isArray(value)) {
20807
+ for (const item of value) walk(item);
20808
+ return;
20809
+ }
20810
+ if (value === null || typeof value !== "object") return;
20811
+ for (const [key, inner] of Object.entries(value)) {
20812
+ if (key === "Fn::GetAtt") {
20813
+ if (Array.isArray(inner) && typeof inner[0] === "string") addRead(inner[0], inner[1]);
20814
+ else if (typeof inner === "string") {
20815
+ const split = splitGetAttStringForm(inner);
20816
+ if (split) addRead(split.logicalId, split.attributeName);
20817
+ }
20818
+ } else if (key === "Fn::Sub") {
20819
+ const text = Array.isArray(inner) ? inner[0] : inner;
20820
+ if (typeof text === "string") for (const match of text.matchAll(/\$\{([^}]*)\}/g)) {
20821
+ const ref = match[1] ?? "";
20822
+ if (ref.startsWith("!")) continue;
20823
+ const split = splitGetAttStringForm(ref);
20824
+ if (split) addRead(split.logicalId, split.attributeName);
20825
+ }
20826
+ }
20827
+ walk(inner);
20828
+ }
20829
+ };
20830
+ walk(template);
20831
+ return reads;
20832
+ }
20337
20833
  function buildStackState(stackName, region, rows, templateParser, template, existingState, selectiveMode) {
20338
20834
  const resources = selectiveMode && existingState ? { ...existingState.resources } : {};
20339
20835
  const parameterNames = new Set(Object.keys(template.Parameters ?? {}));
20340
20836
  let namesParameter;
20837
+ const getAttReads = templateGetAttReads(template);
20341
20838
  for (const row of rows) {
20342
20839
  if (row.outcome !== "imported" || !row.physicalId) continue;
20343
20840
  const tmplResource = template.Resources[row.logicalId];
@@ -20349,10 +20846,14 @@ function buildStackState(stackName, region, rows, templateParser, template, exis
20349
20846
  const { stillMaskedKeys, keptRecordedKeys } = reimported;
20350
20847
  const attributes = withDeclaredNoEchoMasked(reimported.attributes, prior && prior.physicalId === row.physicalId ? prior.noEchoAttributeNames : void 0);
20351
20848
  if (keptRecordedKeys.length > 0) getLogger().info(safeMsg`${displayIdent(row.logicalId)}: this import recorded ` + safeMsg`${keptRecordedKeys.map((key) => displayIdent(key)).join(", ")} only as the ` + "redaction mask, so the previously recorded value is kept; a deploy that updates the resource rewrites it.");
20352
- if (stillMaskedKeys.length > 0) {
20353
- const keys = stillMaskedKeys.map((key) => displayIdent(key)).join(", ");
20354
- const pronoun = stillMaskedKeys.length === 1 ? "it" : "them";
20355
- getLogger().warn(safeMsg`${displayIdent(row.logicalId)}: this import produced no value to replace the redaction ` + safeMsg`mask recorded under ${keys}, so the mask is kept and an Fn::GetAtt reading ${pronoun} ` + "is still refused at deploy. It is kept rather than cleared because an attribute missing from state resolves to the physical id instead of failing.");
20849
+ const readAttributes = getAttReads.get(row.logicalId);
20850
+ const refusedKeys = stillMaskedKeys.filter((key) => readAttributes?.has(key) === true || readAttributes?.has(READS_EVERY_KEY) === true);
20851
+ const unreadKeys = stillMaskedKeys.filter((key) => !refusedKeys.includes(key));
20852
+ if (unreadKeys.length > 0) getLogger().debug(safeMsg`${displayIdent(row.logicalId)}: the redaction mask stays recorded under ` + safeMsg`${unreadKeys.map((key) => displayIdent(key)).join(", ")}; this template reads none of them.`);
20853
+ if (refusedKeys.length > 0) {
20854
+ const keys = refusedKeys.map((key) => displayIdent(key)).join(", ");
20855
+ const pronoun = refusedKeys.length === 1 ? "it" : "them";
20856
+ getLogger().warn(safeMsg`${displayIdent(row.logicalId)}: this import produced no value to replace the redaction ` + safeMsg`mask recorded under ${keys}, so the mask is kept and an Fn::GetAtt reading ${pronoun} ` + "in this template is still refused at deploy. It is kept rather than cleared because an attribute missing from state resolves to the physical id instead of failing.");
20356
20857
  }
20357
20858
  resources[row.logicalId] = {
20358
20859
  physicalId: row.physicalId,
@@ -21214,7 +21715,7 @@ function cannotLeaveUnbound(profile) {
21214
21715
  return !(profile || process.env["AWS_PROFILE"]);
21215
21716
  }
21216
21717
  function cfnProviderRefusal(drift) {
21217
- return `--from-cfn-stack cannot run under --role-arn with this cdk-local version: it has ${drift.join(", ")}, so cdkd cannot make it read the stack with your own credentials instead of the role's. Pass --profile <name> (your own profile), or use the cdk-local version this cdkd release ships with (go-to-k/cdkd#3240).`;
21718
+ return `--from-cfn-stack cannot run under --role-arn with this cdk-local version: it has ${drift.join(", ")}, so cdkd cannot make it read the stack with your own credentials instead of the role's. Pass --profile <name> (your own profile), or use the cdk-local version this cdkd release ships with.`;
21218
21719
  }
21219
21720
  const CFN_PROVIDER_CLIENT_GETTERS = [
21220
21721
  "getClient",
@@ -21364,7 +21865,7 @@ function engineRoleExposureWarning(commandName, channels, fromCfnStackChannel, i
21364
21865
  const envProfileSet = !!input.envProfile;
21365
21866
  const open = envProfileSet ? all.filter((c) => c.flagOnly) : all;
21366
21867
  if (open.length === 0) return void 0;
21367
- return `cdkd local ${commandName}: --role-arn ${envProfileSet ? "is set, and the exported AWS_PROFILE does not cover what follows (only the --profile flag does)" : "is set and no profile is selected"}, so the local emulation engine resolves these AS THE ROLE and hands them to your local code: ${open.map((c) => c.what).join("; ")}. Pass --profile <name> with your own profile to keep them on your identity, or do not pass a role whose permissions you would not give the code in the container. Tracked as go-to-k/cdkd#3240 (upstream go-to-k/cdk-local#783).`;
21868
+ return `cdkd local ${commandName}: --role-arn ${envProfileSet ? "is set, and the exported AWS_PROFILE does not cover what follows (only the --profile flag does)" : "is set and no profile is selected"}, so the local emulation engine resolves these AS THE ROLE and hands them to your local code: ${open.map((c) => c.what).join("; ")}. Pass --profile <name> with your own profile to keep them on your identity, or do not pass a role whose permissions you would not give the code in the container.`;
21368
21869
  }
21369
21870
 
21370
21871
  //#endregion
@@ -24956,7 +25457,7 @@ function resolveMtlsConfig(options) {
24956
25457
  });
24957
25458
  }
24958
25459
  function createLocalStartApiCommand() {
24959
- 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://cdkd.dev/local-start-api/#aws-iam-authorization). 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 BOTH event.stageVariables and requestContext.stage, for REST v1 and HTTP API v2 alike: when a route's API has a matching templated Stage, the selected StageName is threaded into requestContext.stage. An HTTP API v2 route reports '$default' only when its API carries no AWS::ApiGatewayV2::Stage in the template — that is v2's auto-deployed stage, not a limit on what this flag can select (AWS's CreateStage accepts named stages on v2 too). An API with no Stage matching the selected name is warned about at start-up and its routes get stageVariables: null.")).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 ListStackResources 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 ListStackResources 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));
25460
+ 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://cdkd.dev/local-start-api/#aws-iam-authorization). 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 BOTH event.stageVariables and requestContext.stage, for REST v1 and HTTP API v2 alike: when a route's API has a matching templated Stage, the selected StageName is threaded into requestContext.stage. An HTTP API v2 route reports '$default' only when its API carries no AWS::ApiGatewayV2::Stage in the template — that is v2's auto-deployed stage, not a limit on what this flag can select (AWS's CreateStage accepts named stages on v2 too). An API with no Stage matching the selected name is warned about at start-up and its routes get stageVariables: null.")).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. 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 ListStackResources 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 ListStackResources 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));
24960
25461
  [
24961
25462
  ...commonOptions,
24962
25463
  ...appOptions,
@@ -26242,7 +26743,7 @@ function buildRunEcsTaskOptions(options, channels, extra = {}) {
26242
26743
  return runOpts;
26243
26744
  }
26244
26745
  function createLocalRunTaskCommand() {
26245
- 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. A same-account pull in ANY region does not need this flag: the ECR client is built for the image URI's own region, so crossing a region costs nothing extra (issue #2536).")).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 ListStackResources 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 ListStackResources 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));
26746
+ 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. 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. A same-account pull in ANY region does not need this flag: the ECR client is built for the image URI's own region, so crossing a region costs nothing extra.")).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 ListStackResources 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 ListStackResources 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));
26246
26747
  [
26247
26748
  ...commonOptions,
26248
26749
  ...appOptions,
@@ -26270,7 +26771,7 @@ function serviceStrategy() {
26270
26771
  };
26271
26772
  }
26272
26773
  function createLocalStartServiceCommand() {
26273
- const cmd = new Command("start-service").description("Run one or more AWS::ECS::Service resources locally as a long-running emulator. Spins up DesiredCount task replicas per service (clamped by --max-tasks) using the same per-task docker network + metadata sidecar pattern as `cdkd local run-task`, then keeps each replica running and restarts it on exit per --restart-policy. ^C tears every replica + sidecar + network down. Each <target> accepts a CDK display path (MyStack/MyService) or stack-qualified logical ID (MyStack:MyServiceXYZ); single-stack apps may omit the stack prefix. When two or more <target>s are supplied, every service is booted into a shared Cloud Map / Service Connect registry so peer services discover each other via docker --add-host overlay (Issue #460). Omit <targets> in an interactive terminal to multi-select the ECS services from a list.").argument("[targets...]", "One or more CDK display paths or stack-qualified logical IDs of the AWS::ECS::Service resources to run (omit to multi-select interactively in a TTY)").addOption(new Option("--from-state", "Read cdkd's S3 state for the target stack and substitute Ref / Fn::GetAtt / Fn::Sub / Fn::ImportValue / Fn::GetStackOutput intrinsics in container images, environment variables, secrets, role ARNs, and volumes. Mutually exclusive with --from-cfn-stack.").default(false)).addOption(new Option("--state-bucket <bucket>", "S3 bucket for --from-state. Falls back to CDKD_STATE_BUCKET env or cdk.json context.cdkd.stateBucket.")).addOption(new Option("--state-prefix <prefix>", "S3 key prefix for --from-state state files.").default("cdkd")).action(withErrorHandling(async (targets, options) => {
26774
+ const cmd = new Command("start-service").description("Run one or more AWS::ECS::Service resources locally as a long-running emulator. Spins up DesiredCount task replicas per service (clamped by --max-tasks) using the same per-task docker network + metadata sidecar pattern as `cdkd local run-task`, then keeps each replica running and restarts it on exit per --restart-policy. ^C tears every replica + sidecar + network down. Each <target> accepts a CDK display path (MyStack/MyService) or stack-qualified logical ID (MyStack:MyServiceXYZ); single-stack apps may omit the stack prefix. When two or more <target>s are supplied, every service is booted into a shared Cloud Map / Service Connect registry so peer services discover each other via docker --add-host overlay. Omit <targets> in an interactive terminal to multi-select the ECS services from a list.").argument("[targets...]", "One or more CDK display paths or stack-qualified logical IDs of the AWS::ECS::Service resources to run (omit to multi-select interactively in a TTY)").addOption(new Option("--from-state", "Read cdkd's S3 state for the target stack and substitute Ref / Fn::GetAtt / Fn::Sub / Fn::ImportValue / Fn::GetStackOutput intrinsics in container images, environment variables, secrets, role ARNs, and volumes. Mutually exclusive with --from-cfn-stack.").default(false)).addOption(new Option("--state-bucket <bucket>", "S3 bucket for --from-state. Falls back to CDKD_STATE_BUCKET env or cdk.json context.cdkd.stateBucket.")).addOption(new Option("--state-prefix <prefix>", "S3 key prefix for --from-state state files.").default("cdkd")).action(withErrorHandling(async (targets, options) => {
26274
26775
  await runEcsServiceEmulator(targets, options, serviceStrategy(), cdkdExtraStateProviders);
26275
26776
  }));
26276
26777
  addStartServiceSpecificOptions(cmd);
@@ -27458,7 +27959,7 @@ function readEnvOverridesFile$1(filePath) {
27458
27959
  return parsed;
27459
27960
  }
27460
27961
  function createLocalInvokeAgentCoreCommand() {
27461
- 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("--no-assume-role", "Explicitly opt out of assuming the runtime's execution role: forward your ambient AWS credentials (or the --profile overlay) unchanged even when --from-state / --from-cfn-stack is set.")).addOption(new Option("--ecr-role-arn <arn>", "Role ARN to assume before authenticating against ECR for cross-account / centralized registries. A same-account pull in ANY region does not need this flag: the ECR client is built for the image URI's own region, so crossing a region costs nothing extra (issue #2536).")).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 ListStackResources 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) => {
27962
+ 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. 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("--no-assume-role", "Explicitly opt out of assuming the runtime's execution role: forward your ambient AWS credentials (or the --profile overlay) unchanged even when --from-state / --from-cfn-stack is set.")).addOption(new Option("--ecr-role-arn <arn>", "Role ARN to assume before authenticating against ECR for cross-account / centralized registries. A same-account pull in ANY region does not need this flag: the ECR client is built for the image URI's own region, so crossing a region costs nothing extra.")).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 ListStackResources 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) => {
27462
27963
  await localInvokeAgentCoreCommand(target, options);
27463
27964
  }));
27464
27965
  [
@@ -27516,7 +28017,7 @@ function warnUnresolvedLambdaTargetEnv(strategy, options) {
27516
28017
  const ecsNote = resolved.boots.length > 0 ? "The ECS service targets behind this ALB DO honor --from-state. " : "";
27517
28018
  return {
27518
28019
  ...resolved,
27519
- warnings: [...resolved.warnings, `--from-state does not reach the container environment of this ALB's Lambda target group(s): ${lambdaIds.map((id) => plainOrDescribed(id, "logical id")).join(", ")}. Their Environment.Variables keep any Ref / Fn::GetAtt / Fn::Sub / Fn::ImportValue intrinsics unresolved, and each is then dropped with its own warning. ` + ecsNote + "The only state source the Lambda path reads is --from-cfn-stack '<name>', which REPLACES --from-state (the two are mutually exclusive) and reaches both target kinds on a CloudFormation-deployed stack; otherwise override the affected variables with --env-vars. Tracked as go-to-k/cdkd#2602 (upstream go-to-k/cdk-local#707)."]
28020
+ warnings: [...resolved.warnings, `--from-state does not reach the container environment of this ALB's Lambda target group(s): ${lambdaIds.map((id) => plainOrDescribed(id, "logical id")).join(", ")}. Their Environment.Variables keep any Ref / Fn::GetAtt / Fn::Sub / Fn::ImportValue intrinsics unresolved, and each is then dropped with its own warning. ` + ecsNote + "The only state source the Lambda path reads is --from-cfn-stack '<name>', which REPLACES --from-state (the two are mutually exclusive) and reaches both target kinds on a CloudFormation-deployed stack; otherwise override the affected variables with --env-vars."]
27520
28021
  };
27521
28022
  }
27522
28023
  };
@@ -27525,7 +28026,7 @@ function buildAlbEmulatorStrategy(options) {
27525
28026
  return warnUnresolvedLambdaTargetEnv(albStrategy(options), options);
27526
28027
  }
27527
28028
  function createLocalStartAlbCommand() {
27528
- const cmd = new Command("start-alb").description("Run an Application Load Balancer locally: name the ALB, and cdk-local boots the ECS service(s) behind its listeners and stands up a local front-door on each listener port that round-robins across the running replicas and routes its listener rules across the backing services — a stable host endpoint, like behind a real load balancer. The symmetric ALB counterpart of `start-api`. Each <target> accepts a CDK display path (MyStack/MyAlb) or stack-qualified logical ID; single-stack apps may omit the stack prefix. Supports HTTP and HTTPS listeners — by default a cloud-HTTPS listener is served over plain HTTP locally (with X-Forwarded-Proto: https preserved). Pass --tls (or --tls-cert / --tls-key) to terminate TLS locally with a self-signed or user-supplied cert. All six ALB rule-condition fields are honored (path-pattern / host-header / http-header / http-request-method / query-string / source-ip); forward (single and weighted), redirect, and fixed-response actions; and ECS or Lambda targets (a Lambda target group is invoked locally via the Lambda RIE). authenticate-cognito / authenticate-oidc actions enforce a local Bearer-JWT check (or AWSELBAuthSessionCookie pass-through) against the same JWKS / OIDC discovery URL the deployed ALB would; use --bearer-token '<jwt>' to inject a default token or --no-verify-auth to disable the guard. Omit <targets> in an interactive terminal to multi-select the load balancers from a list.").argument("[targets...]", "One or more CDK display paths or stack-qualified logical IDs of the AWS::ElasticLoadBalancingV2::LoadBalancer resources to run (omit to multi-select interactively in a TTY)").addOption(new Option("--from-state", "Read cdkd's S3 state for the target stack and substitute Ref / Fn::GetAtt / Fn::Sub / Fn::ImportValue / Fn::GetStackOutput intrinsics in container images, environment variables, secrets, role ARNs, and volumes of the ECS services behind the ALB. Does NOT reach a Lambda target group's container environment (upstream go-to-k/cdk-local#707; a boot warning names the affected functions) — use --from-cfn-stack for those. Mutually exclusive with --from-cfn-stack.").default(false)).addOption(new Option("--state-bucket <bucket>", "S3 bucket for --from-state. Falls back to CDKD_STATE_BUCKET env or cdk.json context.cdkd.stateBucket.")).addOption(new Option("--state-prefix <prefix>", "S3 key prefix for --from-state state files.").default("cdkd")).action(withErrorHandling(async (targets, options) => {
28029
+ const cmd = new Command("start-alb").description("Run an Application Load Balancer locally: name the ALB, and cdk-local boots the ECS service(s) behind its listeners and stands up a local front-door on each listener port that round-robins across the running replicas and routes its listener rules across the backing services — a stable host endpoint, like behind a real load balancer. The symmetric ALB counterpart of `start-api`. Each <target> accepts a CDK display path (MyStack/MyAlb) or stack-qualified logical ID; single-stack apps may omit the stack prefix. Supports HTTP and HTTPS listeners — by default a cloud-HTTPS listener is served over plain HTTP locally (with X-Forwarded-Proto: https preserved). Pass --tls (or --tls-cert / --tls-key) to terminate TLS locally with a self-signed or user-supplied cert. All six ALB rule-condition fields are honored (path-pattern / host-header / http-header / http-request-method / query-string / source-ip); forward (single and weighted), redirect, and fixed-response actions; and ECS or Lambda targets (a Lambda target group is invoked locally via the Lambda RIE). authenticate-cognito / authenticate-oidc actions enforce a local Bearer-JWT check (or AWSELBAuthSessionCookie pass-through) against the same JWKS / OIDC discovery URL the deployed ALB would; use --bearer-token '<jwt>' to inject a default token or --no-verify-auth to disable the guard. Omit <targets> in an interactive terminal to multi-select the load balancers from a list.").argument("[targets...]", "One or more CDK display paths or stack-qualified logical IDs of the AWS::ElasticLoadBalancingV2::LoadBalancer resources to run (omit to multi-select interactively in a TTY)").addOption(new Option("--from-state", "Read cdkd's S3 state for the target stack and substitute Ref / Fn::GetAtt / Fn::Sub / Fn::ImportValue / Fn::GetStackOutput intrinsics in container images, environment variables, secrets, role ARNs, and volumes of the ECS services behind the ALB. Does NOT reach a Lambda target group's container environment (a boot warning names the affected functions) — use --from-cfn-stack for those. Mutually exclusive with --from-cfn-stack.").default(false)).addOption(new Option("--state-bucket <bucket>", "S3 bucket for --from-state. Falls back to CDKD_STATE_BUCKET env or cdk.json context.cdkd.stateBucket.")).addOption(new Option("--state-prefix <prefix>", "S3 key prefix for --from-state state files.").default("cdkd")).action(withErrorHandling(async (targets, options) => {
27529
28030
  await runEcsServiceEmulator(targets, options, buildAlbEmulatorStrategy(options), cdkdExtraStateProviders);
27530
28031
  }));
27531
28032
  addAlbSpecificOptions(cmd);
@@ -27545,7 +28046,7 @@ function createLocalStartCloudFrontCommand$1() {
27545
28046
  embedConfig: getEmbedConfig(),
27546
28047
  extraStateProviders: cdkdExtraStateProviders
27547
28048
  });
27548
- cmd.addOption(new Option("--from-state", "[not supported on start-cloudfront] Read cdkd's S3 state for the target stack. Refused at startup: the CloudFront emulator consults a state source only under --from-cfn-stack (go-to-k/cdkd#2528, upstream go-to-k/cdk-local#699). Use --from-cfn-stack instead.").default(false));
28049
+ cmd.addOption(new Option("--from-state", "[not supported on start-cloudfront] Read cdkd's S3 state for the target stack. Refused at startup: the CloudFront emulator consults a state source only under --from-cfn-stack. Use --from-cfn-stack instead.").default(false));
27549
28050
  cmd.addOption(new Option("--state-bucket <bucket>", "[not supported on start-cloudfront] S3 bucket for --from-state. Refused at startup with --from-state."));
27550
28051
  cmd.addOption(new Option("--state-prefix <prefix>", "[not supported on start-cloudfront] S3 key prefix for --from-state state files. Refused at startup with --from-state.").default("cdkd"));
27551
28052
  cmd.addOption(new Option("--no-assume-role", "Explicitly opt out of assuming the deployed execution role: forward your ambient AWS credentials (or the --profile overlay) unchanged, and suppress the \"re-run with --assume-role\" hint that omitting the flag prints when deployed state is loaded."));
@@ -27561,7 +28062,7 @@ function refuseUnwiredCdkdStateFlags(cmd) {
27561
28062
  if (options.stateBucket !== void 0) passed.push("--state-bucket");
27562
28063
  if (actionCommand.getOptionValueSource("statePrefix") === "cli") passed.push("--state-prefix");
27563
28064
  if (passed.length === 0) return;
27564
- actionCommand.error(`error: ${passed.join(" / ")} ${passed.length === 1 ? "is" : "are"} not supported by \`cdkd local start-cloudfront\`. The CloudFront emulator resolves its deployed-S3 origins, its cf.kvs() bindings and its Function URL / Lambda@Edge containers' environment through cdk-local, which consults a state source only under --from-cfn-stack. DROP the flag(s) above, then use --from-cfn-stack '<name>' for a CloudFormation-deployed stack, or --origin '<originId>'='<dir>' to serve an origin from a local directory. Adding --from-cfn-stack while leaving these on hits this same refusal. Tracked as go-to-k/cdkd#2528 (upstream go-to-k/cdk-local#699).`, { code: "cdkd.startCloudFrontStateFlagUnsupported" });
28065
+ actionCommand.error(`error: ${passed.join(" / ")} ${passed.length === 1 ? "is" : "are"} not supported by \`cdkd local start-cloudfront\`. The CloudFront emulator resolves its deployed-S3 origins, its cf.kvs() bindings and its Function URL / Lambda@Edge containers' environment through cdk-local, which consults a state source only under --from-cfn-stack. DROP the flag(s) above, then use --from-cfn-stack '<name>' for a CloudFormation-deployed stack, or --origin '<originId>'='<dir>' to serve an origin from a local directory. Adding --from-cfn-stack while leaving these on hits this same refusal.`, { code: "cdkd.startCloudFrontStateFlagUnsupported" });
27565
28066
  });
27566
28067
  }
27567
28068
 
@@ -28215,7 +28716,7 @@ function pickReferencedLogicalId(intrinsic) {
28215
28716
  function createLocalCommand() {
28216
28717
  setEmbedConfig(CDKD_EMBED_CONFIG);
28217
28718
  const local = new Command("local").description("Local execution of Lambda functions (RIE) and ECS task definitions (Docker required)");
28218
- 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("--no-assume-role", "Explicitly opt out of assuming the deployed execution role: forward your ambient AWS credentials (or the --profile overlay) even when --from-state is set, and suppress the \"run with --assume-role\" hint. Distinct from omitting the flag, which still prints that hint.")).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. A same-account pull in ANY region does not need this flag: the ECR client is built for the image URI's own region, so crossing a region costs nothing extra (issue #2536).")).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 ListStackResources 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 ListStackResources 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));
28719
+ 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("--no-assume-role", "Explicitly opt out of assuming the deployed execution role: forward your ambient AWS credentials (or the --profile overlay) even when --from-state is set, and suppress the \"run with --assume-role\" hint. Distinct from omitting the flag, which still prints that hint.")).addOption(new Option("--layer-role-arn <arn>", "Role to sts:AssumeRole before calling lambda:GetLayerVersion on every literal-ARN entry in Properties.Layers. 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. 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. A same-account pull in ANY region does not need this flag: the ECR client is built for the image URI's own region, so crossing a region costs nothing extra.")).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 ListStackResources 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 ListStackResources 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));
28219
28720
  [
28220
28721
  ...commonOptions,
28221
28722
  ...appOptions,
@@ -28289,4 +28790,4 @@ function collectVerbFlags(program) {
28289
28790
 
28290
28791
  //#endregion
28291
28792
  export { canonicalizeIpProtocolValue as a, buildProgram, resolveSecretDerivedPrincipals as i, runDestroyForStack as n, resolvedResourceTimeoutMs as o, isResolvableSecretPrincipalList as r, canonicalizeIpv4Cidr as t };
28292
- //# sourceMappingURL=program-Db1eBDeq.js.map
28793
+ //# sourceMappingURL=program-BivXyHmd.js.map