cdk-local 0.59.0 → 0.60.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.
@@ -1 +0,0 @@
1
- {"version":3,"file":"local-list-ChGfKSkV.js","names":["readEnvOverridesFile","readEvent","resolveZipImagePlan","resolveContainerImagePlan","materializeInlineCode","parseEvent","readStdin","forwardAwsEnv","resolveProfileCredentials","readEnvOverridesFile","resolveCallerAccountId","forwardAwsEnv","execFileAsync","execFileAsync","sleep","pickCandidateStack","buildEcsImageResolutionContext","resolvePlaceholderAccount","assumeTaskRole","readEnvOverridesFile","resolveCallerAccountId","resolveProfileCredentials","pickStack","obj","createHttpsServer","httpRequest","resolveProfileCredentials"],"sources":["../src/cli/commands/local-invoke.ts","../src/cli/commands/local-invoke-agentcore.ts","../src/local/ecs-network.ts","../src/local/ecs-secrets-resolver.ts","../src/local/ecs-task-runner.ts","../src/cli/commands/local-run-task.ts","../src/local/ecs-service-resolver.ts","../src/local/ecs-service-runner.ts","../src/local/front-door-pool.ts","../src/local/alb-lambda-event.ts","../src/local/front-door-server.ts","../src/local/alb-path-matcher.ts","../src/local/front-door-tls.ts","../src/local/front-door-lambda-runner.ts","../src/local/front-door-auth.ts","../src/cli/commands/ecs-service-emulator.ts","../src/cli/commands/local-start-service.ts","../src/local/elb-front-door-resolver.ts","../src/cli/commands/local-start-alb.ts","../src/cli/commands/local-list.ts"],"sourcesContent":["import { cpSync, mkdtempSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';\nimport { tmpdir } from 'node:os';\nimport { dirname } from 'node:path';\nimport * as path from 'node:path';\nimport { Command, Option } from 'commander';\nimport {\n appOptions,\n commonOptions,\n contextOptions,\n deprecatedRegionOption,\n parseContextOptions,\n warnIfDeprecatedRegion,\n} from '../options.js';\nimport { getLogger } from '../../utils/logger.js';\nimport { applyRoleArnIfSet } from '../../utils/role-arn.js';\nimport { CdkLocalError, withErrorHandling } from '../../utils/error-handler.js';\nimport { listTargets } from '../../local/target-lister.js';\nimport { resolveSingleTarget } from '../../local/target-picker.js';\nimport { Synthesizer, type SynthesisOptions } from '../../synthesis/synthesizer.js';\nimport { resolveApp } from '../config-loader.js';\nimport { readCdkPathOrUndefined } from '../cdk-path.js';\nimport {\n createLocalStateProvider,\n resolveCfnFallbackRegion,\n type ExtraStateProviders,\n} from './local-state-source.js';\nimport {\n getEmbedConfig,\n setEmbedConfig,\n type CdkLocalEmbedConfig,\n} from '../../local/embed-config.js';\nimport {\n resolveLambdaTarget,\n type ResolvedImageLambda,\n type ResolvedLambda,\n type ResolvedLambdaLayer,\n type ResolvedZipLambda,\n} from '../../local/lambda-resolver.js';\nimport { materializeLayerFromArn } from '../../local/layer-arn-materializer.js';\nimport { resolveEnvVars, type EnvOverrideFile } from '../../local/env-resolver.js';\nimport {\n applyDeployedEnvFallback,\n substituteEnvVarsFromStateAsync,\n type StateEnvSubstitutionAudit,\n type SubstitutionContext,\n} from '../../local/state-resolver.js';\nimport { derivePartitionAndUrlSuffix } from '../../local/ecs-task-resolver.js';\nimport {\n resolveRuntimeCodeMountPath,\n resolveRuntimeFileExtension,\n resolveRuntimeImage,\n} from '../../local/runtime-image.js';\nimport {\n ensureDockerAvailable,\n pickFreePort,\n pullImage,\n removeContainer,\n runDetached,\n streamLogs,\n} from '../../local/docker-runner.js';\nimport { architectureToPlatform, buildContainerImage } from '../../local/docker-image-builder.js';\nimport { pullEcrImage, parseEcrUri } from '../../local/ecr-puller.js';\nimport { invokeRie, waitForRieReady } from '../../local/rie-client.js';\nimport {\n AssetManifestLoader,\n getDockerImageBySourceHash,\n} from '../../assets/asset-manifest-loader.js';\nimport { singleFlight } from '../../utils/single-flight.js';\nimport {\n writeProfileCredentialsFile,\n type ProfileCredentialsFile,\n} from './local-profile-credentials-file.js';\nimport type { StackState } from '../../types/state.js';\n\ninterface LocalInvokeOptions {\n app?: string;\n output: string;\n verbose: boolean;\n region?: string;\n profile?: string;\n roleArn?: string;\n context?: string[];\n event?: string;\n eventStdin?: boolean;\n envVars?: string;\n /**\n * Commander maps `--no-pull` to `pull: boolean` (default `true`). When\n * the user passes `--no-pull` the value flips to `false` and we skip\n * `docker pull`.\n */\n pull: boolean;\n /**\n * Commander maps `--no-build` to `build: boolean` (default `true`).\n * When the user passes `--no-build` the value flips to `false` and we\n * skip `docker build` on the IMAGE local-build path.\n */\n build: boolean;\n debugPort?: string;\n containerHost: string;\n /**\n * Optional Lambda execution role to assume before invoking. Commander's\n * `[arn]` syntax maps to `string | boolean` here:\n * - flag absent → `undefined` (pass dev creds through; SAM-compatible default)\n * - `--assume-role` (bare) → `true` (auto-resolve from state if a host extension is active)\n * - `--assume-role <arn>` → `'<arn>'` (explicit ARN; precedence wins)\n * - `--no-assume-role` → `false` (explicit opt-out)\n */\n assumeRole?: string | boolean;\n /**\n * Issue #448: explicit role to `sts:AssumeRole` into before calling\n * `lambda:GetLayerVersion` for every literal-ARN entry in a Lambda's\n * `Properties.Layers`.\n */\n layerRoleArn?: string;\n /**\n * Optional role ARN passed to `pullEcrImage` when the IMAGE ECR-pull\n * path fires.\n */\n ecrRoleArn?: string;\n /**\n * Issue #606: alternative state source for CDK apps deployed via the\n * upstream CDK CLI (`cdk deploy` → CloudFormation). Reads the named\n * CFn stack via `ListStackResources` to populate physical IDs.\n * Commander maps:\n * - flag absent → `undefined`\n * - `--from-cfn-stack` (bare) → `true` (use the resolved stack name)\n * - `--from-cfn-stack <name>` → `'<name>'`\n */\n fromCfnStack?: string | boolean;\n /**\n * Region of the state record to read. Drives the CFn client's region\n * when `--from-cfn-stack` is set.\n */\n stackRegion?: string;\n /** Host-injected extra state-source flag fields. */\n [key: string]: unknown;\n}\n\n/**\n * Factory options for {@link createLocalInvokeCommand}.\n */\nexport interface CreateLocalInvokeCommandOptions {\n extraStateProviders?: ExtraStateProviders;\n /** Embed-time branding overrides for a host wrapping this factory. */\n embedConfig?: CdkLocalEmbedConfig;\n}\n\n/**\n * `cdkl invoke <target>` — run a Lambda function locally inside a\n * Docker container that bundles the AWS Lambda Runtime Interface\n * Emulator (RIE). Modeled on `sam local invoke` but reusing cdk-local's\n * synthesis / asset / construct-path plumbing.\n */\nasync function localInvokeCommand(\n target: string | undefined,\n options: LocalInvokeOptions,\n extraStateProviders: ExtraStateProviders | undefined\n): Promise<void> {\n const logger = getLogger();\n if (options.verbose) {\n logger.setLevel('debug');\n }\n\n warnIfDeprecatedRegion(options);\n\n let imagePlan: ImagePlan | undefined;\n let containerId: string | undefined;\n let stopLogs: (() => void) | undefined;\n let sigintHandler: (() => void) | undefined;\n // Synthesized AWS shared credentials file (one INI section) bind-mounted\n // into the container so handlers using `fromIni({ profile })` explicitly\n // resolve to the same creds. Disposed in the shared `cleanup` single-flight.\n let profileCredsFile: ProfileCredentialsFile | undefined;\n\n /**\n * Unified cleanup for both the success / failure unwind path AND the\n * SIGINT handler.\n */\n const cleanup = singleFlight(\n async (): Promise<void> => {\n if (stopLogs) {\n try {\n stopLogs();\n } catch (err) {\n getLogger().debug(\n `streamLogs stop failed: ${err instanceof Error ? err.message : String(err)}`\n );\n }\n }\n if (containerId) {\n try {\n await removeContainer(containerId);\n } catch (err) {\n getLogger().debug(\n `removeContainer(${containerId}) failed: ${err instanceof Error ? err.message : String(err)}`\n );\n }\n }\n if (imagePlan?.inlineTmpDir) {\n try {\n rmSync(imagePlan.inlineTmpDir, { recursive: true, force: true });\n } catch (err) {\n getLogger().debug(\n `Failed to remove inline-code tmpdir ${imagePlan.inlineTmpDir}: ${\n err instanceof Error ? err.message : String(err)\n }`\n );\n }\n }\n if (imagePlan?.layersTmpDir) {\n try {\n rmSync(imagePlan.layersTmpDir, { recursive: true, force: true });\n } catch (err) {\n getLogger().debug(\n `Failed to remove merged-layers tmpdir ${imagePlan.layersTmpDir}: ${\n err instanceof Error ? err.message : String(err)\n }`\n );\n }\n }\n if (imagePlan?.layerArnTmpDirs) {\n for (const dir of imagePlan.layerArnTmpDirs) {\n try {\n rmSync(dir, { recursive: true, force: true });\n } catch (err) {\n getLogger().debug(\n `Failed to remove ARN-layer tmpdir ${dir}: ${\n err instanceof Error ? err.message : String(err)\n }`\n );\n }\n }\n }\n if (profileCredsFile) {\n try {\n await profileCredsFile.dispose();\n } catch (err) {\n getLogger().debug(\n `Failed to remove profile credentials tmpdir ${profileCredsFile.hostPath}: ${\n err instanceof Error ? err.message : String(err)\n }`\n );\n }\n }\n },\n (err) => {\n getLogger().debug(`cleanup failed: ${err instanceof Error ? err.message : String(err)}`);\n }\n );\n\n try {\n await applyRoleArnIfSet({ roleArn: options.roleArn, region: options.region });\n\n await ensureDockerAvailable();\n\n // When `--profile <p>` is set, resolve the profile to a concrete\n // credential set ONCE up-front so it can be overlaid onto the Lambda\n // container's env after `forwardAwsEnv`.\n const profileCredentials = options.profile\n ? await resolveProfileCredentials(options.profile)\n : undefined;\n\n // Synthesize an AWS shared credentials file with the resolved creds\n // under `[<options.profile>]` so handler code that uses\n // `fromIni({ profile: '<options.profile>' })` explicitly (instead of\n // the default credential chain) resolves to the same creds locally.\n // Mounted read-only into the container at the path pointed to by\n // `AWS_SHARED_CREDENTIALS_FILE` (set below). The default-chain path\n // (most handlers) keeps working through the existing env-var\n // injection; this file is the additive layer for the explicit-\n // profile case. Disposed in the shared `cleanup` single-flight above.\n if (options.profile && profileCredentials) {\n profileCredsFile = await writeProfileCredentialsFile(options.profile, profileCredentials);\n }\n\n const appCmd = resolveApp(options.app);\n if (!appCmd) {\n throw new Error(\n `No CDK app specified. Pass --app, set ${getEmbedConfig().envPrefix}_APP, or add \"app\" to cdk.json.`\n );\n }\n\n logger.info('Synthesizing CDK app...');\n const synthesizer = new Synthesizer();\n const context = parseContextOptions(options.context);\n const synthOpts: SynthesisOptions = {\n app: appCmd,\n output: options.output,\n ...(options.region && { region: options.region }),\n ...(options.profile && { profile: options.profile }),\n ...(Object.keys(context).length > 0 && { context }),\n };\n const { stacks } = await synthesizer.synthesize(synthOpts);\n\n const resolvedTarget = await resolveSingleTarget(target, {\n entries: listTargets(stacks).lambdas,\n message: 'Select a Lambda function to invoke',\n noun: 'Lambda functions',\n onMissing: () =>\n new CdkLocalError(\n `${getEmbedConfig().cliName} invoke requires a <target> (a Lambda display path or logical ID). ` +\n `Run \\`${getEmbedConfig().cliName} list\\` to see them, or run it in a TTY to pick interactively.`,\n 'LOCAL_INVOKE_TARGET_REQUIRED'\n ),\n });\n\n const lambda = resolveLambdaTarget(resolvedTarget, stacks);\n const targetLabel = lambda.kind === 'zip' ? lambda.runtime : 'container image';\n logger.info(`Target: ${lambda.stack.stackName}/${lambda.logicalId} (${targetLabel})`);\n\n imagePlan = await resolveImagePlan(lambda, options);\n\n let stateAudit: StateEnvSubstitutionAudit | undefined;\n let templateEnv = getTemplateEnv(lambda.resource);\n let stateForRoleHint: StackState | undefined;\n // Pick the right LocalStateProvider for the supplied flags. Returns\n // `undefined` when no state-source flag is set.\n const stateProvider = createLocalStateProvider(\n options,\n lambda.stack.stackName,\n await resolveCfnFallbackRegion(options, lambda.stack.region),\n extraStateProviders\n );\n if (stateProvider) {\n try {\n const loaded = await stateProvider.load(lambda.stack.stackName, lambda.stack.region);\n if (loaded) {\n // Synthetic StackState shape consumed by the legacy\n // `--assume-role` hint path. Sufficient for\n // `resolveExecutionRoleArnFromState`, which only touches\n // `state.resources[...].properties.Role` /\n // `attributes.Arn`.\n stateForRoleHint = {\n version: 1,\n stackName: lambda.stack.stackName,\n resources: loaded.resources,\n outputs: loaded.outputs,\n lastModified: 0,\n };\n const subContext: SubstitutionContext = {\n resources: loaded.resources,\n consumerRegion: loaded.region,\n };\n if (envHasIntrinsicValue(templateEnv)) {\n const pseudo = await resolvePseudoParametersForInvoke(lambda.stack.region, options);\n if (pseudo) subContext.pseudoParameters = pseudo;\n }\n // Resolve SSM-backed template parameters\n // (`AWS::SSM::Parameter::Value<String>`) so a `Ref` to such a\n // parameter in an env var resolves to its SSM value instead of\n // being warn-and-dropped (issue #94). Only the CFn provider\n // implements this; the resolved map feeds the substitution\n // context's `parameters` field consulted by `resolveRef`.\n if (envHasIntrinsicValue(templateEnv) && stateProvider.resolveTemplateSsmParameters) {\n const ssmParams = await stateProvider.resolveTemplateSsmParameters(\n lambda.stack.template\n );\n if (Object.keys(ssmParams.values).length > 0) subContext.parameters = ssmParams.values;\n // Flag decrypted SecureString parameters so the consuming env\n // keys are kept off the `docker run` argv (issue #99).\n if (ssmParams.secureStringLogicalIds.length > 0) {\n subContext.sensitiveParameters = new Set(ssmParams.secureStringLogicalIds);\n }\n }\n if (envHasCrossStackIntrinsic(templateEnv)) {\n const resolver = await stateProvider.buildCrossStackResolver(loaded.region);\n if (resolver) {\n subContext.crossStackResolver = resolver;\n }\n }\n const { env, audit } = await substituteEnvVarsFromStateAsync(templateEnv, subContext);\n templateEnv = env;\n const label = stateProvider.label;\n for (const key of audit.resolvedKeys) {\n logger.debug(`${label}: substituted env var ${key}`);\n }\n // Deployed-env fallback: keys whose intrinsic value the static\n // substituter could not resolve (e.g. `Fn::GetAtt <Sibling>.Arn`)\n // are filled from the consumer function's deploy-time-resolved\n // `Environment.Variables`. Only the CFn provider implements\n // `resolveDeployedFunctionEnv`; the S3 provider's state already\n // carries deploy-time attributes so its GetAtt resolves above.\n let unresolved = audit.unresolved;\n const resolvedKeys = [...audit.resolvedKeys];\n if (unresolved.length > 0 && stateProvider.resolveDeployedFunctionEnv) {\n const physicalId = loaded.resources[lambda.logicalId]?.physicalId;\n if (physicalId) {\n const deployedEnv = await stateProvider.resolveDeployedFunctionEnv(physicalId);\n const fb = applyDeployedEnvFallback(templateEnv, unresolved, deployedEnv);\n templateEnv = fb.env;\n unresolved = fb.stillUnresolved;\n for (const key of fb.filled) {\n resolvedKeys.push(key);\n logger.debug(`${label}: filled env var ${key} from deployed function config`);\n }\n }\n }\n stateAudit = { resolvedKeys, unresolved, sensitiveKeys: audit.sensitiveKeys };\n for (const { key, reason } of unresolved) {\n logger.warn(\n `${label}: could not substitute env var ${key} (${reason}). ` +\n `Override it via --env-vars or it will be dropped.`\n );\n }\n }\n } finally {\n stateProvider.dispose();\n }\n }\n\n // Resolve env vars. Intrinsic-valued template entries are warned about\n // and dropped; the user can override them via --env-vars (SAM-shape).\n const overrides = readEnvOverridesFile(options.envVars);\n const lambdaCdkPath = readCdkPathOrUndefined(lambda.resource);\n const envResult = resolveEnvVars(lambda.logicalId, lambdaCdkPath, templateEnv, overrides);\n for (const key of envResult.unresolved) {\n if (stateAudit && stateAudit.unresolved.some((u) => u.key === key)) continue;\n // Prefer the L2 form (`MyStack/MyFn`) in the suggestion since that\n // matches the README guidance and `cdkl invoke` target shape; the\n // resolver's prefix rule accepts either form.\n const overrideKeyExample = lambdaCdkPath?.replace(/\\/Resource$/, '') ?? lambda.logicalId;\n logger.warn(\n `Environment variable ${key} contains a CloudFormation intrinsic and was dropped. ` +\n `Override it with --env-vars (e.g. {\"${overrideKeyExample}\":{\"${key}\":\"<literal>\"}}), or pass a state-source flag (e.g. --from-cfn-stack or a host-provided extension) to recover deployed values.`\n );\n }\n\n // Auto-resolve the execution-role ARN from state when the user passed\n // bare `--assume-role` together with a state source.\n let resolvedAssumeRoleArn: string | undefined;\n if (typeof options.assumeRole === 'string') {\n resolvedAssumeRoleArn = options.assumeRole;\n } else if (options.assumeRole === true) {\n if (!stateForRoleHint) {\n logger.warn(\n '--assume-role passed without an ARN, but no state was loaded. ' +\n 'Pair it with a state-source flag, or pass the ARN explicitly: --assume-role <arn>. ' +\n \"Falling back to the developer's shell credentials.\"\n );\n } else {\n const arn = resolveExecutionRoleArnFromState(stateForRoleHint, lambda.logicalId);\n if (arn) {\n resolvedAssumeRoleArn = arn;\n logger.info(`--assume-role: auto-resolved execution role from state: ${arn}`);\n } else {\n logger.warn(\n `--assume-role: could not resolve the execution role ARN from state for '${lambda.logicalId}'. ` +\n \"Pass the ARN explicitly: --assume-role <arn>. Falling back to the developer's shell credentials.\"\n );\n }\n }\n } else if (options.assumeRole === undefined && stateForRoleHint) {\n // Legacy hint path: surface the deployed role ARN so they can re-run\n // with `--assume-role`.\n suggestAssumeRoleFromState(stateForRoleHint, lambda.logicalId);\n }\n\n // Read the event payload. Default to {} (matches SAM).\n const event = await readEvent(options);\n\n const dockerEnv: Record<string, string> = {\n AWS_LAMBDA_FUNCTION_NAME: lambda.logicalId,\n AWS_LAMBDA_FUNCTION_MEMORY_SIZE: String(lambda.memoryMb),\n AWS_LAMBDA_FUNCTION_TIMEOUT: String(lambda.timeoutSec),\n AWS_LAMBDA_FUNCTION_VERSION: '$LATEST',\n AWS_LAMBDA_LOG_GROUP_NAME: `/aws/lambda/${lambda.logicalId}`,\n AWS_LAMBDA_LOG_STREAM_NAME: 'local',\n ...envResult.resolved,\n };\n let assumeSucceeded = false;\n if (resolvedAssumeRoleArn) {\n const stsRegion =\n options.region ?? process.env['AWS_REGION'] ?? process.env['AWS_DEFAULT_REGION'];\n try {\n const creds = await assumeLambdaExecutionRole(resolvedAssumeRoleArn, stsRegion);\n dockerEnv['AWS_ACCESS_KEY_ID'] = creds.accessKeyId;\n dockerEnv['AWS_SECRET_ACCESS_KEY'] = creds.secretAccessKey;\n dockerEnv['AWS_SESSION_TOKEN'] = creds.sessionToken;\n if (stsRegion) dockerEnv['AWS_REGION'] = stsRegion;\n assumeSucceeded = true;\n } catch (err) {\n const reason = err instanceof Error ? err.message : String(err);\n logger.warn(\n `--assume-role: STS AssumeRole(${resolvedAssumeRoleArn}) failed: ${reason}. ` +\n \"Falling back to the developer's shell credentials.\"\n );\n }\n }\n if (!assumeSucceeded) {\n forwardAwsEnv(dockerEnv);\n applyProfileCredentialsOverlay(dockerEnv, profileCredentials, false);\n // Point the container's SDK chain at the bind-mounted credentials\n // file so\n // `fromIni({ profile })` calls inside the handler resolve to the\n // same creds. `AWS_PROFILE` makes `fromIni()` (no explicit arg)\n // ALSO use this profile.\n if (profileCredsFile) {\n dockerEnv['AWS_SHARED_CREDENTIALS_FILE'] = profileCredsFile.containerPath;\n dockerEnv['AWS_PROFILE'] = profileCredsFile.profileName;\n }\n }\n\n let debugPort: number | undefined;\n if (options.debugPort) {\n debugPort = Number(options.debugPort);\n if (!Number.isInteger(debugPort) || debugPort <= 0 || debugPort > 65535) {\n throw new Error(`--debug-port must be an integer in 1..65535, got '${options.debugPort}'`);\n }\n dockerEnv['NODE_OPTIONS'] = `--inspect-brk=0.0.0.0:${debugPort}`;\n if (lambda.kind === 'image') {\n logger.warn(\n '--debug-port sets NODE_OPTIONS unconditionally on container Lambdas. ' +\n \"If the image's runtime is not Node.js, this flag is a no-op.\"\n );\n }\n }\n\n const hostPort = await pickFreePort();\n const containerHost = options.containerHost;\n\n if (lambda.layers.length > 0) {\n logger.info(\n `Mounting ${lambda.layers.length} Lambda layer${lambda.layers.length === 1 ? '' : 's'} at /opt`\n );\n }\n logger.info(`Starting container (image=${imagePlan.image}, port=${hostPort})...`);\n // Append the profile credentials file bind-mount to the existing\n // extraMounts (which\n // already carry the /opt layer mount when present). Read-only — the\n // container has no business writing to its credentials file; a\n // writable mount would let a compromised handler tamper with the\n // host-side temp file.\n const extraMountsWithProfile = profileCredsFile\n ? [\n ...(imagePlan.extraMounts ?? []),\n {\n hostPath: profileCredsFile.hostPath,\n containerPath: profileCredsFile.containerPath,\n readOnly: true,\n },\n ]\n : imagePlan.extraMounts;\n containerId = await runDetached({\n image: imagePlan.image,\n mounts: imagePlan.mounts,\n extraMounts: extraMountsWithProfile,\n env: dockerEnv,\n ...(stateAudit &&\n stateAudit.sensitiveKeys.length > 0 && {\n sensitiveEnvKeys: new Set(stateAudit.sensitiveKeys),\n }),\n cmd: imagePlan.cmd,\n hostPort,\n host: containerHost,\n ...(debugPort !== undefined && { debugPort }),\n ...(imagePlan.platform !== undefined && { platform: imagePlan.platform }),\n ...(imagePlan.entryPoint !== undefined && { entryPoint: imagePlan.entryPoint }),\n ...(imagePlan.workingDir !== undefined && { workingDir: imagePlan.workingDir }),\n ...(imagePlan.tmpfs !== undefined && { tmpfs: imagePlan.tmpfs }),\n });\n\n stopLogs = streamLogs(containerId);\n\n sigintHandler = (): void => {\n void cleanup().then(() => {\n process.exit(130);\n });\n };\n process.on('SIGINT', sigintHandler);\n\n await waitForRieReady(containerHost, hostPort, 5000);\n\n const invokeTimeoutMs = Math.max(30_000, lambda.timeoutSec * 2 * 1000);\n const result = await invokeRie(containerHost, hostPort, event, invokeTimeoutMs);\n\n // Settle a few hundred ms so logs fully flush before we tear down.\n await new Promise((resolveDelay) => setTimeout(resolveDelay, 250));\n process.stdout.write(`${result.raw}\\n`);\n } finally {\n if (sigintHandler) process.off('SIGINT', sigintHandler);\n await cleanup();\n }\n}\n\ninterface ImagePlan {\n image: string;\n mounts: { hostPath: string; containerPath: string; readOnly?: boolean }[];\n extraMounts: { hostPath: string; containerPath: string; readOnly?: boolean }[];\n cmd: string[];\n platform?: string;\n entryPoint?: string[];\n workingDir?: string;\n inlineTmpDir?: string;\n layersTmpDir?: string;\n layerArnTmpDirs?: string[];\n tmpfs?: { target: string; sizeMb: number };\n}\n\nasync function resolveImagePlan(\n lambda: ResolvedLambda,\n options: LocalInvokeOptions\n): Promise<ImagePlan> {\n if (lambda.kind === 'zip') {\n return resolveZipImagePlan(lambda, options);\n }\n return resolveContainerImagePlan(lambda, options);\n}\n\nasync function resolveZipImagePlan(\n lambda: ResolvedZipLambda,\n options: LocalInvokeOptions\n): Promise<ImagePlan> {\n let inlineTmpDir: string | undefined;\n let codeDir = lambda.codePath;\n if (codeDir === null) {\n inlineTmpDir = materializeInlineCode(\n lambda.handler,\n lambda.inlineCode ?? '',\n resolveRuntimeFileExtension(lambda.runtime)\n );\n codeDir = inlineTmpDir;\n }\n\n const image = resolveRuntimeImage(lambda.runtime);\n\n await pullImage(image, options.pull === false);\n\n const layerPlan = await materializeLambdaLayersIncludingArns(lambda.layers, options);\n\n // provided.al2 / provided.al2023 require the deployment package at\n // /var/runtime; every other runtime expects /var/task.\n const containerCodePath = resolveRuntimeCodeMountPath(lambda.runtime);\n\n const tmpfs = resolveTmpfsForLambda(lambda);\n\n return {\n image,\n mounts: [{ hostPath: codeDir, containerPath: containerCodePath, readOnly: true }],\n extraMounts: layerPlan.mount ? [layerPlan.mount] : [],\n cmd: [lambda.handler],\n ...(inlineTmpDir !== undefined && { inlineTmpDir }),\n ...(layerPlan.tmpDir !== undefined && { layersTmpDir: layerPlan.tmpDir }),\n ...(layerPlan.extraTmpDirs.length > 0 && { layerArnTmpDirs: layerPlan.extraTmpDirs }),\n ...(tmpfs !== undefined && { tmpfs }),\n };\n}\n\nexport async function materializeLambdaLayersIncludingArns(\n layers: ResolvedLambdaLayer[],\n options: LocalInvokeOptions\n): Promise<{\n mount?: { hostPath: string; containerPath: string; readOnly: boolean };\n tmpDir?: string;\n extraTmpDirs: string[];\n}> {\n const extraTmpDirs: string[] = [];\n const flat: { logicalId: string; assetPath: string }[] = [];\n for (const layer of layers) {\n if (layer.kind === 'asset') {\n flat.push({ logicalId: layer.logicalId, assetPath: layer.assetPath });\n continue;\n }\n const dir = await materializeLayerFromArn(layer, {\n ...(options.layerRoleArn !== undefined && { roleArn: options.layerRoleArn }),\n });\n extraTmpDirs.push(dir);\n flat.push({ logicalId: layer.arn, assetPath: dir });\n }\n const plan = materializeLambdaLayers(flat);\n return { ...plan, extraTmpDirs };\n}\n\nexport function resolveTmpfsForLambda(\n lambda: ResolvedLambda\n): { target: string; sizeMb: number } | undefined {\n if (lambda.ephemeralStorageMb === undefined) return undefined;\n const logger = getLogger();\n if (lambda.kind === 'image') {\n logger.info(\n `Lambda ${lambda.logicalId}: capping /tmp at ${lambda.ephemeralStorageMb} MiB via --tmpfs (overlays any base-image /tmp content)`\n );\n } else {\n logger.debug(\n `Lambda ${lambda.logicalId}: applying EphemeralStorage cap via --tmpfs /tmp:size=${lambda.ephemeralStorageMb}m`\n );\n }\n return { target: '/tmp', sizeMb: lambda.ephemeralStorageMb };\n}\n\nexport function materializeLambdaLayers(layers: { logicalId: string; assetPath: string }[]): {\n mount?: { hostPath: string; containerPath: string; readOnly: boolean };\n tmpDir?: string;\n} {\n if (layers.length === 0) return {};\n if (layers.length === 1) {\n return {\n mount: { hostPath: layers[0]!.assetPath, containerPath: '/opt', readOnly: true },\n };\n }\n const tmpDir = mkdtempSync(\n path.join(tmpdir(), `${getEmbedConfig().resourceNamePrefix}-invoke-layers-`)\n );\n for (const layer of layers) {\n cpSync(layer.assetPath, tmpDir, { recursive: true, force: true });\n }\n return {\n mount: { hostPath: tmpDir, containerPath: '/opt', readOnly: true },\n tmpDir,\n };\n}\n\nexport async function resolveContainerImagePlan(\n lambda: ResolvedImageLambda,\n options: LocalInvokeOptions\n): Promise<ImagePlan> {\n const logger = getLogger();\n const platform = architectureToPlatform(lambda.architecture);\n\n const localBuild = await resolveLocalBuildPlan(lambda);\n let imageRef: string;\n if (localBuild) {\n imageRef = await buildContainerImage(localBuild.asset, localBuild.cdkOutDir, {\n architecture: lambda.architecture,\n noBuild: options.build === false,\n });\n } else {\n if (!parseEcrUri(lambda.imageUri)) {\n throw new Error(\n `Container Lambda '${lambda.logicalId}' has no matching asset in cdk.out, and Code.ImageUri ` +\n `'${lambda.imageUri}' is not an ECR URI ${getEmbedConfig().binaryName} can authenticate against. ` +\n 'Re-synthesize the CDK app (so cdk.out includes the build context) or deploy the image to ECR first.'\n );\n }\n logger.info(\n `No matching cdk.out asset for ${lambda.imageUri}; falling back to ECR pull (same-acct/region only)...`\n );\n imageRef = await pullEcrImage(lambda.imageUri, {\n skipPull: options.pull === false,\n ...(options.region !== undefined && { region: options.region }),\n ...(options.ecrRoleArn !== undefined && { ecrRoleArn: options.ecrRoleArn }),\n ...(options.profile !== undefined && { profile: options.profile }),\n });\n }\n\n const tmpfs = resolveTmpfsForLambda(lambda);\n\n return {\n image: imageRef,\n mounts: [],\n extraMounts: [],\n cmd: lambda.imageConfig.command ?? [],\n platform,\n ...(lambda.imageConfig.entryPoint &&\n lambda.imageConfig.entryPoint.length > 0 && {\n entryPoint: lambda.imageConfig.entryPoint,\n }),\n ...(lambda.imageConfig.workingDirectory !== undefined && {\n workingDir: lambda.imageConfig.workingDirectory,\n }),\n ...(tmpfs !== undefined && { tmpfs }),\n };\n}\n\nasync function resolveLocalBuildPlan(\n lambda: ResolvedImageLambda\n): Promise<\n | { asset: { source: import('../../types/assets.js').DockerImageAssetSource }; cdkOutDir: string }\n | undefined\n> {\n const manifestPath = lambda.stack.assetManifestPath;\n if (!manifestPath) return undefined;\n const cdkOutDir = dirname(manifestPath);\n\n const loader = new AssetManifestLoader();\n const manifest = await loader.loadManifest(cdkOutDir, lambda.stack.stackName);\n if (!manifest) return undefined;\n\n const entry = getDockerImageBySourceHash(manifest, lambda.imageUri);\n if (!entry) return undefined;\n return { asset: entry.asset, cdkOutDir };\n}\n\nexport function envHasIntrinsicValue(templateEnv: Record<string, unknown> | undefined): boolean {\n if (!templateEnv) return false;\n for (const v of Object.values(templateEnv)) {\n if (v === undefined || v === null) continue;\n if (typeof v === 'string' || typeof v === 'number' || typeof v === 'boolean') continue;\n return true;\n }\n return false;\n}\n\nexport function envHasCrossStackIntrinsic(\n templateEnv: Record<string, unknown> | undefined\n): boolean {\n if (!templateEnv) return false;\n for (const v of Object.values(templateEnv)) {\n if (!v || typeof v !== 'object') continue;\n const obj = v as Record<string, unknown>;\n if ('Fn::ImportValue' in obj || 'Fn::GetStackOutput' in obj) return true;\n }\n return false;\n}\n\nasync function resolvePseudoParametersForInvoke(\n stackRegion: string | undefined,\n options: LocalInvokeOptions\n): Promise<\n { accountId?: string; region?: string; partition?: string; urlSuffix?: string } | undefined\n> {\n const logger = getLogger();\n const region =\n options.region ?? process.env['AWS_REGION'] ?? process.env['AWS_DEFAULT_REGION'] ?? stackRegion;\n if (!region) {\n logger.warn(\n `Resolver references \\${AWS::Region} but ${getEmbedConfig().binaryName} could not determine the target region. ` +\n 'Pass --region, set AWS_REGION, or declare env.region on the CDK stack.'\n );\n }\n let accountId: string | undefined;\n try {\n const { STSClient, GetCallerIdentityCommand } = await import('@aws-sdk/client-sts');\n const sts = new STSClient({ ...(region && { region }) });\n try {\n const identity = await sts.send(new GetCallerIdentityCommand({}));\n accountId = identity.Account;\n } finally {\n sts.destroy();\n }\n } catch (err) {\n logger.warn(\n `Resolver needs \\${AWS::AccountId} but STS GetCallerIdentity failed: ${err instanceof Error ? err.message : String(err)}. ` +\n 'Substitution will be skipped; affected env entries will be dropped with per-key warnings.'\n );\n }\n const partitionAndSuffix = region ? derivePartitionAndUrlSuffix(region) : undefined;\n const bag: {\n accountId?: string;\n region?: string;\n partition?: string;\n urlSuffix?: string;\n } = {\n ...(accountId !== undefined && { accountId }),\n ...(region !== undefined && { region }),\n ...(partitionAndSuffix && {\n partition: partitionAndSuffix.partition,\n urlSuffix: partitionAndSuffix.urlSuffix,\n }),\n };\n return Object.keys(bag).length === 0 ? undefined : bag;\n}\n\nfunction getTemplateEnv(resource: {\n Properties?: Record<string, unknown>;\n}): Record<string, unknown> | undefined {\n const props = resource.Properties ?? {};\n const env = props['Environment'];\n if (!env || typeof env !== 'object') return undefined;\n const vars = (env as Record<string, unknown>)['Variables'];\n if (!vars || typeof vars !== 'object') return undefined;\n return vars as Record<string, unknown>;\n}\n\nfunction readEnvOverridesFile(filePath: string | undefined): EnvOverrideFile | undefined {\n if (!filePath) return undefined;\n let raw: string;\n try {\n raw = readFileSync(filePath, 'utf-8');\n } catch (err) {\n throw new Error(\n `Failed to read --env-vars file '${filePath}': ${\n err instanceof Error ? err.message : String(err)\n }`\n );\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (err) {\n throw new Error(\n `Failed to parse --env-vars file '${filePath}' as JSON: ${\n err instanceof Error ? err.message : String(err)\n }`\n );\n }\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {\n throw new Error(`--env-vars file '${filePath}' must contain a JSON object at the top level.`);\n }\n return parsed as EnvOverrideFile;\n}\n\nasync function readEvent(options: LocalInvokeOptions): Promise<unknown> {\n if (options.event && options.eventStdin) {\n throw new Error('--event and --event-stdin are mutually exclusive.');\n }\n if (options.eventStdin) {\n const raw = await readStdin();\n return parseEvent(raw, '<stdin>');\n }\n if (options.event) {\n const raw = readFileSync(options.event, 'utf-8');\n return parseEvent(raw, options.event);\n }\n return {};\n}\n\nfunction parseEvent(raw: string, source: string): unknown {\n try {\n return JSON.parse(raw);\n } catch (err) {\n throw new Error(\n `Failed to parse event payload from ${source} as JSON: ${\n err instanceof Error ? err.message : String(err)\n }`\n );\n }\n}\n\nasync function readStdin(): Promise<string> {\n const chunks: Buffer[] = [];\n for await (const chunk of process.stdin as AsyncIterable<Buffer | string>) {\n chunks.push(typeof chunk === 'string' ? Buffer.from(chunk) : chunk);\n }\n return Buffer.concat(chunks).toString('utf-8');\n}\n\nasync function assumeLambdaExecutionRole(\n roleArn: string,\n region: string | undefined\n): Promise<{ accessKeyId: string; secretAccessKey: string; sessionToken: string }> {\n const { STSClient, AssumeRoleCommand } = await import('@aws-sdk/client-sts');\n const sts = new STSClient({ ...(region && { region }) });\n try {\n const response = await sts.send(\n new AssumeRoleCommand({\n RoleArn: roleArn,\n RoleSessionName: `${getEmbedConfig().resourceNamePrefix}-invoke-${Date.now()}`,\n DurationSeconds: 3600,\n })\n );\n const creds = response.Credentials;\n if (!creds?.AccessKeyId || !creds.SecretAccessKey || !creds.SessionToken) {\n throw new Error(`AssumeRole(${roleArn}) returned no usable credentials.`);\n }\n return {\n accessKeyId: creds.AccessKeyId,\n secretAccessKey: creds.SecretAccessKey,\n sessionToken: creds.SessionToken,\n };\n } finally {\n sts.destroy();\n }\n}\n\nfunction forwardAwsEnv(env: Record<string, string>): void {\n const passThrough = [\n 'AWS_ACCESS_KEY_ID',\n 'AWS_SECRET_ACCESS_KEY',\n 'AWS_SESSION_TOKEN',\n 'AWS_REGION',\n 'AWS_DEFAULT_REGION',\n ] as const;\n for (const key of passThrough) {\n const value = process.env[key];\n if (value !== undefined) env[key] = value;\n }\n}\n\n/**\n * Resolve `--profile <p>` to a concrete credential set. Mirrors the\n * helper in `local-start-api.ts`.\n */\nasync function resolveProfileCredentials(\n profile: string\n): Promise<{ accessKeyId: string; secretAccessKey: string; sessionToken?: string }> {\n const { STSClient } = await import('@aws-sdk/client-sts');\n const sts = new STSClient({ profile });\n try {\n const credsProvider = sts.config.credentials;\n const creds = typeof credsProvider === 'function' ? await credsProvider() : credsProvider;\n if (!creds || !creds.accessKeyId || !creds.secretAccessKey) {\n throw new Error(\n `--profile '${profile}': credential provider chain resolved without usable credentials. ` +\n 'Check `aws sso login --profile ' +\n profile +\n '` for SSO profiles, or `~/.aws/credentials` / `~/.aws/config` for regular profiles.'\n );\n }\n return {\n accessKeyId: creds.accessKeyId,\n secretAccessKey: creds.secretAccessKey,\n ...(creds.sessionToken && { sessionToken: creds.sessionToken }),\n };\n } finally {\n sts.destroy();\n }\n}\n\nexport function applyProfileCredentialsOverlay(\n env: Record<string, string>,\n profileCreds: { accessKeyId: string; secretAccessKey: string; sessionToken?: string } | undefined,\n assumeRoleActive: boolean\n): void {\n if (!profileCreds) return;\n if (assumeRoleActive) return;\n env['AWS_ACCESS_KEY_ID'] = profileCreds.accessKeyId;\n env['AWS_SECRET_ACCESS_KEY'] = profileCreds.secretAccessKey;\n if (profileCreds.sessionToken) {\n env['AWS_SESSION_TOKEN'] = profileCreds.sessionToken;\n } else {\n delete env['AWS_SESSION_TOKEN'];\n }\n}\n\nfunction materializeInlineCode(handler: string, source: string, fileExtension: string): string {\n const lastDot = handler.lastIndexOf('.');\n if (lastDot <= 0) {\n throw new Error(`Handler '${handler}' is malformed: expected '<modulePath>.<exportName>'.`);\n }\n const modulePath = handler.substring(0, lastDot);\n const dir = mkdtempSync(path.join(tmpdir(), `${getEmbedConfig().resourceNamePrefix}-invoke-`));\n const filePath = path.join(dir, `${modulePath}${fileExtension}`);\n mkdirSync(path.dirname(filePath), { recursive: true });\n writeFileSync(filePath, source, 'utf-8');\n return dir;\n}\n\nfunction suggestAssumeRoleFromState(state: StackState, logicalId: string): void {\n const logger = getLogger();\n const roleArn = resolveExecutionRoleArnFromState(state, logicalId);\n if (roleArn) {\n logger.info(\n `Hint: the deployed function uses execution role ${roleArn}. ` +\n `Re-run with --assume-role to invoke under the deployed function's narrow permissions.`\n );\n }\n}\n\nexport function resolveExecutionRoleArnFromState(\n state: Pick<StackState, 'resources'>,\n logicalId: string,\n roleProperty = 'Role'\n): string | undefined {\n const lambda = state.resources[logicalId];\n if (!lambda) return undefined;\n\n const roleRef = lambda.properties?.[roleProperty] ?? lambda.observedProperties?.[roleProperty];\n if (typeof roleRef === 'string' && roleRef.startsWith('arn:')) {\n return roleRef;\n }\n if (typeof roleRef === 'object' && roleRef !== null) {\n const refLogicalId = pickReferencedLogicalId(roleRef as Record<string, unknown>);\n if (refLogicalId) {\n const roleResource = state.resources[refLogicalId];\n const cached = roleResource?.attributes?.['Arn'];\n if (typeof cached === 'string' && cached.startsWith('arn:')) {\n return cached;\n }\n }\n }\n return undefined;\n}\n\nfunction pickReferencedLogicalId(intrinsic: Record<string, unknown>): string | undefined {\n if ('Ref' in intrinsic && typeof intrinsic['Ref'] === 'string') return intrinsic['Ref'];\n if ('Fn::GetAtt' in intrinsic) {\n const arg = intrinsic['Fn::GetAtt'];\n if (Array.isArray(arg) && typeof arg[0] === 'string') return arg[0];\n if (typeof arg === 'string') return arg.split('.')[0];\n }\n return undefined;\n}\n\nexport function createLocalInvokeCommand(opts: CreateLocalInvokeCommandOptions = {}): Command {\n setEmbedConfig(opts.embedConfig);\n const invoke = new Command('invoke')\n .description(\n 'Run a Lambda function locally in a Docker container (RIE-backed). ' +\n 'Target accepts a CDK display path (MyStack/MyApi/Handler) or stack-qualified logical ID ' +\n '(MyStack:MyApiHandler1234ABCD). Single-stack apps may omit the stack prefix. ' +\n 'Omit <target> in an interactive terminal to pick the Lambda from a list.'\n )\n .argument(\n '[target]',\n 'CDK display path or stack-qualified logical ID of the Lambda to invoke (omit to pick interactively in a TTY)'\n )\n .addOption(new Option('-e, --event <file>', 'JSON event payload file (default: {})'))\n .addOption(new Option('--event-stdin', 'Read event JSON from stdin').default(false))\n .addOption(\n new Option(\n '--env-vars <file>',\n 'JSON env-var overrides (SAM-compatible: {\"LogicalId\":{\"KEY\":\"VALUE\"}})'\n )\n )\n .addOption(\n new Option(\n '--no-pull',\n 'Skip docker pull (use cached image) — no-op for IMAGE local-build path; ' +\n '`docker build` does not pull base layers by default'\n )\n )\n .addOption(\n new Option(\n '--no-build',\n 'Skip docker build on the IMAGE local-build path (use the previously-built tag). ' +\n 'Requires the deterministic tag to already be in the local registry; errors with ' +\n 'an actionable message when missing. No-op for ZIP Lambdas and the IMAGE ECR-pull path. ' +\n 'Compatible with --no-pull.'\n )\n )\n .addOption(new Option('--debug-port <port>', 'Node --inspect-brk port (default: off)'))\n .addOption(\n new Option('--container-host <host>', 'Host to bind the RIE port to').default('127.0.0.1')\n )\n .addOption(\n new Option(\n '--assume-role [arn]',\n \"Assume the Lambda's deployed execution role and forward STS-issued temp credentials \" +\n \"to the container so the handler runs with the deployed function's narrow permissions. \" +\n 'Three forms: ' +\n '(1) `--assume-role <arn>` assumes the explicit ARN; ' +\n \"(2) `--assume-role` (bare) auto-resolves the function's execution role ARN from state \" +\n '(requires an active state source); ' +\n '(3) `--no-assume-role` explicitly opts out. ' +\n \"Off by default — when omitted, the developer's shell credentials are forwarded \" +\n 'unchanged (SAM-compatible default). STS failures degrade to a warn + dev-creds fallback.'\n )\n )\n .addOption(\n new Option(\n '--layer-role-arn <arn>',\n 'Role to sts:AssumeRole before calling lambda:GetLayerVersion on every literal-ARN ' +\n 'entry in Properties.Layers. Use only when the dev credentials cannot ' +\n 'read the layer — typically cross-account layers. AWS-published public layers (e.g. ' +\n 'Lambda Powertools) are readable from every account and need no role.'\n )\n )\n .addOption(\n new Option(\n '--ecr-role-arn <arn>',\n 'Role ARN to assume before authenticating against ECR for cross-account / centralized ' +\n 'registries. Issues sts:AssumeRole via the default credential chain and uses the ' +\n 'temporary credentials for ecr:GetAuthorizationToken + docker pull. Required when the ' +\n 'caller does not have direct cross-account access to the target repository. ' +\n 'Same-account / same-region pulls do not need this flag.'\n )\n )\n .addOption(\n new Option(\n '--from-cfn-stack [cfn-stack-name]',\n 'Read a deployed CloudFormation stack via ListStackResources and substitute Ref / Fn::ImportValue ' +\n 'in env vars with the deployed physical IDs / exports. Use for CDK apps deployed via the upstream ' +\n 'CDK CLI (`cdk deploy`). Bare form uses the resolved stack name; pass an explicit value when CFn stack name differs. ' +\n 'Fn::GetAtt is warn-and-dropped in v1 (CFn ListStackResources does not return per-attribute values).'\n )\n )\n .addOption(\n new Option(\n '--stack-region <region>',\n 'Region of the state record to read. Used with --from-cfn-stack as the CFn client region.'\n )\n )\n .action(\n withErrorHandling(async (target: string | undefined, options: LocalInvokeOptions) => {\n await localInvokeCommand(target, options, opts.extraStateProviders);\n })\n );\n\n [...commonOptions(), ...appOptions(), ...contextOptions].forEach((option) =>\n invoke.addOption(option)\n );\n invoke.addOption(deprecatedRegionOption);\n\n return invoke;\n}\n","import { randomUUID } from 'node:crypto';\nimport { existsSync, readFileSync, statSync } from 'node:fs';\nimport { dirname } from 'node:path';\nimport { Command, Option } from 'commander';\nimport {\n appOptions,\n commonOptions,\n contextOptions,\n deprecatedRegionOption,\n parseContextOptions,\n warnIfDeprecatedRegion,\n} from '../options.js';\nimport { getLogger } from '../../utils/logger.js';\nimport { applyRoleArnIfSet } from '../../utils/role-arn.js';\nimport { CdkLocalError, withErrorHandling } from '../../utils/error-handler.js';\nimport { listTargets } from '../../local/target-lister.js';\nimport { resolveSingleTarget } from '../../local/target-picker.js';\nimport { Synthesizer, type SynthesisOptions } from '../../synthesis/synthesizer.js';\nimport { resolveApp } from '../config-loader.js';\nimport { readCdkPathOrUndefined } from '../cdk-path.js';\nimport {\n createLocalStateProvider,\n resolveCfnFallbackRegion,\n type ExtraStateProviders,\n} from './local-state-source.js';\nimport type { LocalStateProvider, LocalStateRecord } from '../../local/local-state-provider.js';\nimport type { StackInfo } from '../../synthesis/assembly-reader.js';\nimport {\n getEmbedConfig,\n setEmbedConfig,\n type CdkLocalEmbedConfig,\n} from '../../local/embed-config.js';\nimport {\n AGENTCORE_A2A_PROTOCOL,\n AGENTCORE_AGUI_PROTOCOL,\n AGENTCORE_MCP_PROTOCOL,\n pickAgentCoreCandidateStack,\n resolveAgentCoreTarget,\n type AgentCoreCodeArtifact,\n type ResolvedAgentCoreRuntime,\n} from '../../local/agentcore-resolver.js';\nimport { buildAgentCoreCodeImage } from '../../local/agentcore-code-build.js';\nimport { downloadAndExtractS3Bundle } from '../../local/agentcore-s3-bundle.js';\nimport {\n signAgentCoreInvocation,\n type SigV4Credentials,\n} from '../../local/agentcore-sigv4-sign.js';\nimport {\n invokeAgentCore,\n waitForAgentCorePing,\n type AgentCoreInvokeResult,\n} from '../../local/agentcore-client.js';\nimport {\n mcpInvokeOnce,\n MCP_CONTAINER_PORT,\n MCP_PATH,\n type McpInvokeResult,\n type McpJsonRpcRequest,\n} from '../../local/agentcore-mcp-client.js';\nimport {\n a2aInvokeOnce,\n A2A_CONTAINER_PORT,\n A2A_PATH,\n type A2aInvokeResult,\n type A2aJsonRpcRequest,\n} from '../../local/agentcore-a2a-client.js';\nimport { invokeAgentCoreWs, type AgentCoreWsResult } from '../../local/agentcore-ws-client.js';\nimport { createJwksCache, verifyJwtViaDiscovery } from '../../local/cognito-jwt.js';\nimport { resolveEnvVars, type EnvOverrideFile } from '../../local/env-resolver.js';\nimport {\n substituteAgainstStateAsync,\n substituteEnvVarsFromStateAsync,\n type SubstitutionContext,\n} from '../../local/state-resolver.js';\nimport {\n derivePseudoParametersFromRegion,\n type ImageResolutionContext,\n} from '../../local/intrinsic-image.js';\nimport {\n ensureDockerAvailable,\n pickFreePort,\n pullImage,\n removeContainer,\n runDetached,\n streamLogs,\n} from '../../local/docker-runner.js';\nimport { buildContainerImage } from '../../local/docker-image-builder.js';\nimport { parseEcrUri, pullEcrImage } from '../../local/ecr-puller.js';\nimport {\n AssetManifestLoader,\n getDockerImageBySourceHash,\n} from '../../assets/asset-manifest-loader.js';\nimport type { FileAsset } from '../../types/assets.js';\nimport { singleFlight } from '../../utils/single-flight.js';\nimport { resolveProfileCredentials } from './local-start-api.js';\nimport {\n applyProfileCredentialsOverlay,\n resolveExecutionRoleArnFromState,\n} from './local-invoke.js';\nimport {\n writeProfileCredentialsFile,\n type ProfileCredentialsFile,\n} from './local-profile-credentials-file.js';\n\ninterface LocalInvokeAgentCoreOptions {\n app?: string;\n output: string;\n verbose: boolean;\n region?: string;\n profile?: string;\n roleArn?: string;\n context?: string[];\n event?: string;\n eventStdin?: boolean;\n envVars?: string;\n pull: boolean;\n build: boolean;\n containerHost: string;\n /** `--platform <linux/amd64|linux/arm64>`. Defaults to AgentCore's required arm64. */\n platform: string;\n /**\n * `--ws`: use the HTTP-protocol agent's bidirectional `/ws` WebSocket\n * endpoint (on 8080) instead of `POST /invocations` — send `--event` as the\n * first frame and stream received frames to stdout until the agent closes.\n */\n ws?: boolean;\n /**\n * `--ws-interactive`: after the initial `--event` frame, read additional\n * frames from stdin (one frame per line, trailing newline stripped) and\n * send each as a text frame to the agent until stdin EOFs (Ctrl-D) or the\n * agent closes the connection. Only meaningful with `--ws`.\n */\n wsInteractive?: boolean;\n /** Session id forwarded via the AgentCore session-id header (auto-generated when omitted). */\n sessionId?: string;\n /**\n * Bearer JWT to present when the runtime declares a `customJwtAuthorizer`.\n * Verified against the runtime's OIDC discovery URL before the container\n * starts, then forwarded to `/invocations` as `Authorization: Bearer <jwt>`.\n */\n bearerToken?: string;\n /**\n * Commander maps `--no-verify-auth` to `verifyAuth: boolean` (default\n * `true`). When `false`, skip inbound JWT verification entirely (local-dev\n * escape hatch) — the token, if any, is still forwarded.\n */\n verifyAuth: boolean;\n /**\n * `--sigv4`: sign the `/invocations` POST with AWS SigV4 (service\n * `bedrock-agentcore`) using the resolved credentials — matching the\n * cloud's default IAM-auth behavior when the runtime declares no\n * `customJwtAuthorizer`. The local agent receives the same `Authorization:\n * AWS4-HMAC-SHA256 ...` + `X-Amz-Date` / `X-Amz-Content-Sha256` /\n * `X-Amz-Security-Token` headers it would in the cloud. Opt-in: default\n * unsigned (preserves existing behavior). Mutually exclusive with\n * `--bearer-token` and ignored on a JWT-protected runtime.\n */\n sigv4?: boolean;\n /**\n * Optional execution role to assume before invoking. Commander's `[arn]`\n * maps to `string | boolean`:\n * - absent → `undefined` (dev creds pass through; SAM-compatible default)\n * - `--assume-role` (bare) → `true` (use the runtime's literal RoleArn)\n * - `--assume-role <arn>` → `'<arn>'`\n * - `--no-assume-role` → `false`\n */\n assumeRole?: string | boolean;\n /** Role ARN to assume before authenticating against ECR for the container image pull. */\n ecrRoleArn?: string;\n fromCfnStack?: string | boolean;\n stackRegion?: string;\n /**\n * Per-request timeout in milliseconds, applied to the HTTP `/invocations`\n * POST, the MCP `POST /mcp` request, and the `/ws` open-to-close window.\n * Default 120000 (120s). Raise this for long-running agent calls that\n * exceed the default — the cloud's AgentCore quota goes well above 120s.\n */\n timeout: number;\n /** Host-injected extra state-source flag fields. */\n [key: string]: unknown;\n}\n\n/**\n * Parser for `--timeout <ms>`. Accepts a positive integer; rejects 0,\n * negatives, fractions, and non-numeric input.\n */\nexport function parseTimeoutMs(raw: string): number {\n const parsed = Number(raw);\n if (!Number.isInteger(parsed) || parsed <= 0) {\n throw new CdkLocalError(\n `--timeout must be a positive integer number of milliseconds (got '${raw}').`,\n 'LOCAL_INVOKE_AGENTCORE_TIMEOUT_INVALID'\n );\n }\n return parsed;\n}\n\n/**\n * Factory options for {@link createLocalInvokeAgentCoreCommand}.\n */\nexport interface CreateLocalInvokeAgentCoreCommandOptions {\n extraStateProviders?: ExtraStateProviders;\n /** Embed-time branding overrides for a host wrapping this factory. */\n embedConfig?: CdkLocalEmbedConfig;\n}\n\n/**\n * `cdkl invoke-agentcore <target>` — run a Bedrock AgentCore Runtime container\n * locally and invoke it once over the AgentCore HTTP contract. Resolves\n * the `AWS::BedrockAgentCore::Runtime`, pulls / builds its container,\n * starts it on port 8080, waits for `GET /ping`, POSTs the event to\n * `POST /invocations`, prints the response, and tears down. Covers the\n * container artifact and the CodeConfiguration managed-runtime artifact\n * (fromCodeAsset, built from source) on the HTTP + MCP protocols; the agent's\n * calls to real AWS go to real AWS (credentials injected like `cdkl invoke`).\n */\nasync function localInvokeAgentCoreCommand(\n target: string | undefined,\n options: LocalInvokeAgentCoreOptions,\n extraStateProviders: ExtraStateProviders | undefined\n): Promise<void> {\n const logger = getLogger();\n if (options.verbose) logger.setLevel('debug');\n\n warnIfDeprecatedRegion(options);\n\n let containerId: string | undefined;\n let stopLogs: (() => void) | undefined;\n let sigintHandler: (() => void) | undefined;\n let profileCredsFile: ProfileCredentialsFile | undefined;\n let stateProvider: LocalStateProvider | undefined;\n\n const cleanup = singleFlight(\n async (): Promise<void> => {\n if (stateProvider) {\n try {\n stateProvider.dispose();\n } catch (err) {\n getLogger().debug(\n `state provider dispose failed: ${err instanceof Error ? err.message : String(err)}`\n );\n }\n }\n if (stopLogs) {\n try {\n stopLogs();\n } catch (err) {\n getLogger().debug(\n `streamLogs stop failed: ${err instanceof Error ? err.message : String(err)}`\n );\n }\n }\n if (containerId) {\n try {\n await removeContainer(containerId);\n } catch (err) {\n getLogger().debug(\n `removeContainer(${containerId}) failed: ${err instanceof Error ? err.message : String(err)}`\n );\n }\n }\n if (profileCredsFile) {\n try {\n await profileCredsFile.dispose();\n } catch (err) {\n getLogger().debug(\n `Failed to remove profile credentials tmpdir ${profileCredsFile.hostPath}: ${\n err instanceof Error ? err.message : String(err)\n }`\n );\n }\n }\n },\n (err) => {\n getLogger().debug(`cleanup failed: ${err instanceof Error ? err.message : String(err)}`);\n }\n );\n\n try {\n await applyRoleArnIfSet({ roleArn: options.roleArn, region: options.region });\n await ensureDockerAvailable();\n\n const profileCredentials = options.profile\n ? await resolveProfileCredentials(options.profile)\n : undefined;\n if (options.profile && profileCredentials) {\n profileCredsFile = await writeProfileCredentialsFile(options.profile, profileCredentials);\n }\n\n const appCmd = resolveApp(options.app);\n if (!appCmd) {\n throw new Error(\n `No CDK app specified. Pass --app, set ${getEmbedConfig().envPrefix}_APP, or add \"app\" to cdk.json.`\n );\n }\n\n logger.info('Synthesizing CDK app...');\n const synthesizer = new Synthesizer();\n const context = parseContextOptions(options.context);\n const synthOpts: SynthesisOptions = {\n app: appCmd,\n output: options.output,\n ...(options.region && { region: options.region }),\n ...(options.profile && { profile: options.profile }),\n ...(Object.keys(context).length > 0 && { context }),\n };\n const { stacks } = await synthesizer.synthesize(synthOpts);\n\n const resolvedTarget = await resolveSingleTarget(target, {\n entries: listTargets(stacks).agentCoreRuntimes,\n message: 'Select an AgentCore Runtime to invoke',\n noun: 'AgentCore Runtimes',\n onMissing: () =>\n new CdkLocalError(\n `${getEmbedConfig().cliName} invoke-agentcore requires a <target> (an AgentCore Runtime display path or logical ID). ` +\n `Run \\`${getEmbedConfig().cliName} list\\` to see them, or run it in a TTY to pick interactively.`,\n 'LOCAL_INVOKE_AGENTCORE_TARGET_REQUIRED'\n ),\n });\n\n // Build a `--from-cfn-stack` image-resolution context BEFORE resolving the\n // target, so a same-stack AWS::ECR::Repository Fn::Join ContainerUri (or an\n // Fn::Sub asset URI) reduces to the deployed image URI. The state load is\n // shared with the env-substitution + role-from-state steps below.\n const candidate = pickAgentCoreCandidateStack(resolvedTarget, stacks);\n stateProvider = createLocalStateProvider(\n options,\n candidate?.stackName ?? '',\n await resolveCfnFallbackRegion(options, candidate?.region),\n extraStateProviders\n );\n const { context: imageContext, loaded: loadedState } =\n stateProvider && candidate\n ? await buildAgentCoreImageContext(candidate, stateProvider, options)\n : { context: undefined, loaded: undefined };\n\n const resolved = resolveAgentCoreTarget(resolvedTarget, stacks, imageContext);\n logger.info(`Target: ${resolved.stack.stackName}/${resolved.logicalId} (${resolved.protocol})`);\n const isMcp = resolved.protocol === AGENTCORE_MCP_PROTOCOL;\n const isA2a = resolved.protocol === AGENTCORE_A2A_PROTOCOL;\n const isAgui = resolved.protocol === AGENTCORE_AGUI_PROTOCOL;\n if (isAgui) {\n // AG-UI's wire shape is HTTP-compatible: SSE on `POST /invocations` and a\n // bidirectional WebSocket on `/ws`, both on port 8080. So an AG-UI\n // runtime routes through the same client path as HTTP — the existing\n // SSE / WS handlers stream bytes transparently. Surface this in the\n // log so users aren't surprised the AGUI runtime \"becomes\" HTTP.\n logger.info(\n 'AGUI runtime: routing through the HTTP /invocations + /ws path (AG-UI wire is SSE / WebSocket on port 8080).'\n );\n }\n if ((isMcp || isA2a) && options.ws) {\n logger.warn(\n `--ws applies only to the HTTP / AGUI protocols; ignoring it for this ${resolved.protocol} runtime.`\n );\n }\n if (options.wsInteractive && !options.ws) {\n logger.warn('--ws-interactive is meaningful only with --ws; ignoring.');\n }\n if (options.sigv4 && (isMcp || isA2a || options.ws)) {\n logger.warn(\n '--sigv4 signs the HTTP /invocations request only; ignoring it for the ' +\n (isMcp ? 'MCP' : isA2a ? 'A2A' : '/ws WebSocket') +\n ' path.'\n );\n }\n\n // Read + validate the event (and resolve the session id) BEFORE any\n // Docker work, so a bad --event / --event-stdin fails fast instead of\n // after paying for an image build + container boot.\n const sessionId = options.sessionId ?? randomUUID();\n const event = await readEvent(options);\n // For MCP, parse the event into a JSON-RPC request up front so a malformed\n // one fails fast too (default: tools/list).\n const mcpRequest = isMcp ? buildMcpRequest(event) : undefined;\n // A2A is JSON-RPC 2.0 too: parse the event up front into a method/params\n // (default: agent/getCard — the agent's discovery card).\n const a2aRequest = isA2a ? buildA2aRequest(event) : undefined;\n\n // Inbound JWT auth: when the runtime declares a customJwtAuthorizer,\n // verify the supplied bearer token against its OIDC discovery URL BEFORE\n // any Docker work — rejecting a missing / invalid token the way AgentCore\n // does. Returns the `Authorization` header to forward to the container.\n // MCP / A2A talk vanilla JSON-RPC directly to the container; their\n // bearer / session is an AgentCore managed-plane concern the front door\n // layers on top, so it is not applied to a direct local POST.\n let authorization: string | undefined;\n if (isMcp || isA2a) {\n if (resolved.jwtAuthorizer || options.bearerToken) {\n const pathLabel = isMcp ? MCP_PATH : A2A_PATH;\n logger.info(\n `${resolved.protocol} runtime: invoking the local container's ${pathLabel} directly (vanilla ${resolved.protocol}). ` +\n `An inbound JWT / --bearer-token is an AgentCore managed-plane concern and is not applied locally.`\n );\n }\n } else {\n authorization = await resolveInboundAuthorization(resolved, options);\n }\n\n // If the fromS3 bundle's Code.S3.Bucket is an intrinsic (Ref /\n // Fn::ImportValue / Fn::GetStackOutput), resolve it against --from-cfn-stack\n // state BEFORE the image step (which needs a literal bucket for the S3\n // download). Reuses the same substitution machinery env vars use, so\n // every cross-stack intrinsic the env path supports is supported here too.\n await resolveFromS3BucketIntrinsic(resolved, stateProvider, loadedState, imageContext);\n\n const image = await resolveAgentCoreImage(resolved, options, loadedState);\n\n const { env: dockerEnv, sensitiveEnvKeys } = await buildContainerEnv(\n resolved,\n options,\n profileCredentials,\n profileCredsFile,\n stateProvider,\n loadedState,\n imageContext\n );\n\n const hostPort = await pickFreePort();\n const containerHost = options.containerHost;\n // Stable `cdkl-`-prefixed name so the orphan sweep (`docker ps --filter\n // name=cdkl-`) used by `/cleanup` + `/run-integ` can find this container\n // if the process is killed before teardown — unlike a one-shot Lambda\n // invoke, the agent container runs a long-lived HTTP server.\n const containerName = `${getEmbedConfig().resourceNamePrefix}-agentcore-${process.pid}-${Math.random().toString(36).slice(2, 8)}`;\n const containerPort = isMcp ? MCP_CONTAINER_PORT : isA2a ? A2A_CONTAINER_PORT : undefined;\n const containerPortLabel = isMcp\n ? `${MCP_CONTAINER_PORT}${MCP_PATH}`\n : isA2a\n ? `${A2A_CONTAINER_PORT}${A2A_PATH}`\n : '8080';\n logger.info(\n `Starting agent container (image=${image}, port=${hostPort} -> ${containerPortLabel})...`\n );\n containerId = await runDetached({\n image,\n mounts: [],\n env: dockerEnv,\n cmd: [],\n hostPort,\n host: containerHost,\n platform: options.platform,\n name: containerName,\n ...(containerPort !== undefined && { containerPort }),\n // Keep decrypted SecureString SSM env values off the `docker run` argv.\n ...(sensitiveEnvKeys.size > 0 && { sensitiveEnvKeys }),\n });\n\n stopLogs = streamLogs(containerId);\n\n sigintHandler = (): void => {\n void cleanup().then(() => process.exit(130));\n };\n process.on('SIGINT', sigintHandler);\n\n if (isMcp && mcpRequest) {\n // MCP has no /ping: mcpInvokeOnce folds the boot-wait into a retried\n // `initialize`, then runs the session handshake + the one request.\n logger.info(`MCP request: ${mcpRequest.method}`);\n const mcp = await mcpInvokeOnce(containerHost, hostPort, mcpRequest, {\n requestTimeoutMs: options.timeout,\n });\n // Settle so container logs flush before teardown.\n await new Promise((r) => setTimeout(r, 250));\n emitMcpResult(mcp);\n } else if (isA2a && a2aRequest) {\n // A2A has no /ping either: a2aInvokeOnce folds the boot-wait into a\n // retried POST. One JSON-RPC round-trip — vanilla A2A.\n logger.info(`A2A request: ${a2aRequest.method}`);\n const a2a = await a2aInvokeOnce(containerHost, hostPort, a2aRequest, {\n requestTimeoutMs: options.timeout,\n });\n await new Promise((r) => setTimeout(r, 250));\n emitA2aResult(a2a);\n } else if (options.ws) {\n // Bidirectional `/ws` (same 8080 container as /invocations): send the\n // event as the first frame, stream every received frame to stdout, then\n // resolve when the agent closes the stream. With `--ws-interactive` we\n // additionally wire `process.stdin` (line-buffered) as a frame source,\n // so each typed line becomes a follow-up text frame until EOF / agent\n // close — a REPL on top of the same connection.\n await waitForAgentCorePing(containerHost, hostPort);\n const frameSource = options.wsInteractive ? readStdinLines() : undefined;\n logger.info(\n options.wsInteractive\n ? 'Opening the agent /ws WebSocket (interactive — stdin lines = follow-up frames; Ctrl-D to end)...'\n : 'Opening the agent /ws WebSocket and streaming frames...'\n );\n const wsResult = await invokeAgentCoreWs(containerHost, hostPort, event, {\n sessionId,\n timeoutMs: options.timeout,\n onMessage: (text) => process.stdout.write(text),\n ...(authorization && { authorization }),\n ...(frameSource && { frameSource }),\n });\n // Settle so container logs flush before teardown.\n await new Promise((r) => setTimeout(r, 250));\n emitWsResult(wsResult);\n } else {\n await waitForAgentCorePing(containerHost, hostPort);\n\n // `--sigv4` opt-in: sign the request with the resolved host credentials\n // (service `bedrock-agentcore`) so the agent receives the same\n // Authorization + X-Amz-* headers it would in the cloud. Mutually\n // exclusive with `--bearer-token`; ignored when a customJwtAuthorizer is\n // declared (the JWT path takes precedence).\n const additionalHeaders = await buildSigV4HeadersIfRequested(\n options,\n resolved,\n loadedState,\n containerHost,\n hostPort,\n event,\n sessionId\n );\n\n const result = await invokeAgentCore(containerHost, hostPort, event, {\n sessionId,\n timeoutMs: options.timeout,\n // Stream a text/event-stream response to stdout as it arrives, so a\n // token-streaming agent shows incrementally rather than all at once.\n onChunk: (text) => process.stdout.write(text),\n ...(authorization && { authorization }),\n ...(additionalHeaders && { additionalHeaders }),\n });\n\n // Settle so container logs flush before teardown.\n await new Promise((r) => setTimeout(r, 250));\n emitResult(result);\n }\n } finally {\n if (sigintHandler) process.off('SIGINT', sigintHandler);\n await cleanup();\n }\n}\n\n/**\n * Enforce the runtime's inbound JWT authorizer (when declared) and return\n * the `Authorization` header to forward to `/invocations`.\n *\n * - No authorizer → forward the token verbatim if one was given (no-op\n * otherwise).\n * - `--no-verify-auth` → warn + forward without verifying (local-dev escape).\n * - Authorizer + no token → reject (AgentCore returns 401).\n * - Authorizer + token → verify against the OIDC discovery URL; reject on\n * failure (AgentCore returns 403); forward on success. An unreachable\n * discovery URL falls back to pass-through accept (offline-dev fallback in\n * {@link verifyJwtViaDiscovery}).\n *\n * Exported so a unit test can drive the gate without the full Docker pipeline.\n */\nexport async function resolveInboundAuthorization(\n resolved: ResolvedAgentCoreRuntime,\n options: { bearerToken?: string; verifyAuth: boolean }\n): Promise<string | undefined> {\n const logger = getLogger();\n const authorizer = resolved.jwtAuthorizer;\n const header = options.bearerToken ? `Bearer ${options.bearerToken}` : undefined;\n\n if (!authorizer) return header;\n\n if (options.verifyAuth === false) {\n logger.warn(\n `Runtime '${resolved.logicalId}' declares a customJwtAuthorizer, but --no-verify-auth was set — ` +\n `skipping inbound JWT verification (local-dev escape hatch).`\n );\n return header;\n }\n\n if (!header) {\n throw new CdkLocalError(\n `Runtime '${resolved.logicalId}' requires an inbound JWT (customJwtAuthorizer). ` +\n `Pass --bearer-token <jwt>, or --no-verify-auth to skip verification for local dev.`,\n 'LOCAL_INVOKE_AGENTCORE_AUTH_REQUIRED'\n );\n }\n\n const result = await verifyJwtViaDiscovery(\n {\n discoveryUrl: authorizer.discoveryUrl,\n ...(authorizer.allowedAudience && { allowedAudience: authorizer.allowedAudience }),\n ...(authorizer.allowedClients && { allowedClients: authorizer.allowedClients }),\n ...(authorizer.allowedScopes && { allowedScopes: authorizer.allowedScopes }),\n ...(authorizer.customClaims && { customClaims: authorizer.customClaims }),\n },\n header,\n createJwksCache(),\n { warned: new Set() }\n );\n if (!result.allow) {\n throw new CdkLocalError(\n `Inbound JWT rejected by the runtime's customJwtAuthorizer ` +\n `(signature / issuer / expiry / audience check failed against ${authorizer.discoveryUrl}).`,\n 'LOCAL_INVOKE_AGENTCORE_AUTH_DENIED'\n );\n }\n logger.info(`Inbound JWT verified against ${authorizer.discoveryUrl}.`);\n return header;\n}\n\n/**\n * Compute the SigV4 headers for the `/invocations` POST when `--sigv4` is\n * requested. Returns `undefined` (no header overlay) when:\n *\n * - `--sigv4` is not set,\n * - the runtime declares a `customJwtAuthorizer` (the JWT path wins; warns),\n *\n * Throws a {@link CdkLocalError} when `--sigv4` conflicts with\n * `--bearer-token`, or when no AWS credentials are resolvable for signing.\n *\n * Exported so a unit test can drive the gate without the full Docker pipeline.\n */\nexport async function buildSigV4HeadersIfRequested(\n options: LocalInvokeAgentCoreOptions,\n resolved: ResolvedAgentCoreRuntime,\n loaded: LocalStateRecord | undefined,\n host: string,\n port: number,\n event: unknown,\n sessionId: string\n): Promise<Record<string, string> | undefined> {\n if (!options.sigv4) return undefined;\n if (options.bearerToken) {\n throw new CdkLocalError(\n `--sigv4 and --bearer-token are mutually exclusive: pick one inbound auth.`,\n 'LOCAL_INVOKE_AGENTCORE_AUTH_CONFLICT'\n );\n }\n if (resolved.jwtAuthorizer) {\n getLogger().warn(\n `Runtime '${resolved.logicalId}' declares a customJwtAuthorizer; --sigv4 ignored (JWT path takes precedence).`\n );\n return undefined;\n }\n const region =\n options.region ??\n options.stackRegion ??\n process.env['AWS_REGION'] ??\n process.env['AWS_DEFAULT_REGION'] ??\n resolved.stack.region;\n if (!region) {\n throw new CdkLocalError(\n `--sigv4: no region resolved for the AgentCore signing scope. ` +\n `Pass --region <region>, set AWS_REGION, or use --from-cfn-stack with a region-bound stack.`,\n 'LOCAL_INVOKE_AGENTCORE_SIGV4_NO_REGION'\n );\n }\n const credentials = await resolveHostCredentialsForSigV4(options, resolved, loaded, region);\n const signed = await signAgentCoreInvocation({\n credentials,\n region,\n host,\n port,\n path: '/invocations',\n body: JSON.stringify(event ?? {}),\n sessionId,\n });\n const headers: Record<string, string> = {\n Authorization: signed.authorization,\n 'X-Amz-Date': signed.amzDate,\n 'X-Amz-Content-Sha256': signed.amzContentSha256,\n };\n if (signed.amzSecurityToken) headers['X-Amz-Security-Token'] = signed.amzSecurityToken;\n getLogger().info(`Signed /invocations with SigV4 (region=${region}).`);\n return headers;\n}\n\n/**\n * Resolve credentials for host-side SigV4 signing. Precedence:\n * 1. `--assume-role` → STS temp creds (warn + fall through on STS failure);\n * 2. `--profile` → profile creds (sessionToken when the profile carries one);\n * 3. shell env (`AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY` / optional\n * `AWS_SESSION_TOKEN`).\n *\n * Throws a {@link CdkLocalError} when none are available — `--sigv4` cannot\n * proceed without credentials, unlike the unsigned path.\n */\nasync function resolveHostCredentialsForSigV4(\n options: LocalInvokeAgentCoreOptions,\n resolved: ResolvedAgentCoreRuntime,\n loaded: LocalStateRecord | undefined,\n region: string\n): Promise<SigV4Credentials> {\n const logger = getLogger();\n const assumeRoleArn = resolveAssumeRoleArn(options, resolved, loaded);\n if (assumeRoleArn) {\n try {\n return await assumeAgentCoreExecutionRole(assumeRoleArn, region);\n } catch (err) {\n logger.warn(\n `--assume-role: STS AssumeRole(${assumeRoleArn}) failed for --sigv4 signing: ` +\n `${err instanceof Error ? err.message : String(err)}. ` +\n `Falling back to ${options.profile ? `--profile ${options.profile}` : 'shell credentials'}.`\n );\n }\n }\n if (options.profile) {\n const creds = await resolveProfileCredentials(options.profile);\n if (creds?.accessKeyId && creds.secretAccessKey) {\n return {\n accessKeyId: creds.accessKeyId,\n secretAccessKey: creds.secretAccessKey,\n ...(creds.sessionToken && { sessionToken: creds.sessionToken }),\n };\n }\n }\n const accessKeyId = process.env['AWS_ACCESS_KEY_ID'];\n const secretAccessKey = process.env['AWS_SECRET_ACCESS_KEY'];\n if (accessKeyId && secretAccessKey) {\n const sessionToken = process.env['AWS_SESSION_TOKEN'];\n return {\n accessKeyId,\n secretAccessKey,\n ...(sessionToken && { sessionToken }),\n };\n }\n throw new CdkLocalError(\n `--sigv4: no AWS credentials available to sign the request. ` +\n `Set AWS_ACCESS_KEY_ID + AWS_SECRET_ACCESS_KEY, pass --profile <name>, or pass --assume-role <arn>.`,\n 'LOCAL_INVOKE_AGENTCORE_SIGV4_NO_CREDENTIALS'\n );\n}\n\n/**\n * Acquire the agent image. A CODE artifact (managed runtime) is built from\n * source — a fromCodeAsset bundle from its cdk.out asset, a fromS3 bundle\n * downloaded + extracted from S3. A CONTAINER artifact mirrors the\n * container-Lambda path: build from a local cdk.out asset when the URI matches\n * one, else pull from ECR, else pull a plain registry image.\n *\n * `loaded` is the `--from-cfn-stack` state record (when available) — threaded\n * through so a bare `--assume-role` can resolve the execution-role ARN from\n * state for the fromS3 download.\n */\nexport async function resolveAgentCoreImage(\n resolved: ResolvedAgentCoreRuntime,\n options: LocalInvokeAgentCoreOptions,\n loaded?: LocalStateRecord\n): Promise<string> {\n const logger = getLogger();\n const architecture = platformToArchitecture(options.platform);\n\n if (resolved.codeArtifact) {\n return resolveAgentCoreCodeImage(\n resolved,\n resolved.codeArtifact,\n options,\n architecture,\n loaded\n );\n }\n\n const containerUri = resolved.containerUri;\n if (containerUri === undefined) {\n throw new CdkLocalError(\n `AgentCore Runtime '${resolved.logicalId}' has neither a container image nor a code artifact to run.`,\n 'LOCAL_INVOKE_AGENTCORE_NO_ARTIFACT'\n );\n }\n\n const manifestPath = resolved.stack.assetManifestPath;\n if (manifestPath) {\n const cdkOutDir = dirname(manifestPath);\n const loader = new AssetManifestLoader();\n const manifest = await loader.loadManifest(cdkOutDir, resolved.stack.stackName);\n if (manifest) {\n const entry = getDockerImageBySourceHash(manifest, containerUri);\n if (entry) {\n return buildContainerImage(entry.asset, cdkOutDir, {\n architecture,\n noBuild: options.build === false,\n });\n }\n }\n }\n\n if (parseEcrUri(containerUri)) {\n logger.info(`Pulling agent image from ECR: ${containerUri}`);\n return pullEcrImage(containerUri, {\n skipPull: options.pull === false,\n ...(options.region !== undefined && { region: options.region }),\n ...(options.ecrRoleArn !== undefined && { ecrRoleArn: options.ecrRoleArn }),\n ...(options.profile !== undefined && { profile: options.profile }),\n });\n }\n\n await pullImage(containerUri, options.pull === false);\n return containerUri;\n}\n\n/**\n * Build a local image from a `CodeConfiguration` (managed-runtime) bundle.\n *\n * - fromS3 (`code.s3Source` set, a literal S3 object): download + extract the\n * bundle, then run the from-source build over the extracted dir.\n * - fromCodeAsset: locate the source dir in cdk.out via its asset hash, then\n * run the same from-source build (generated Dockerfile → install deps → run\n * EntryPoint).\n */\nasync function resolveAgentCoreCodeImage(\n resolved: ResolvedAgentCoreRuntime,\n code: AgentCoreCodeArtifact,\n options: LocalInvokeAgentCoreOptions,\n architecture: 'x86_64' | 'arm64',\n loaded?: LocalStateRecord\n): Promise<string> {\n if (code.s3Source) {\n return resolveAgentCoreCodeImageFromS3(\n resolved,\n code,\n code.s3Source,\n options,\n architecture,\n loaded\n );\n }\n\n const manifestPath = resolved.stack.assetManifestPath;\n if (!manifestPath) {\n throw new CdkLocalError(\n `AgentCore Runtime '${resolved.logicalId}' uses a code artifact, but its stack has no asset ` +\n `manifest in cdk.out to read the bundle source from.`,\n 'LOCAL_INVOKE_AGENTCORE_CODE_NO_MANIFEST'\n );\n }\n const cdkOutDir = dirname(manifestPath);\n const loader = new AssetManifestLoader();\n const manifest = await loader.loadManifest(cdkOutDir, resolved.stack.stackName);\n const fileAssets = manifest ? loader.getFileAssets(manifest) : undefined;\n // The manifest's `files` are keyed by SOURCE hash; for the default\n // synthesizer that equals the destination objectKey hash (`<hash>.zip`), so a\n // direct key lookup hits. Fall back to matching the destination objectKey so\n // a synthesizer that emits a prefixed / differing objectKey still resolves.\n const asset = fileAssets\n ? (fileAssets.get(code.codeAssetHash) ??\n findFileAssetByObjectKey(fileAssets, code.codeAssetHash))\n : undefined;\n if (!asset) {\n throw new CdkLocalError(\n `AgentCore Runtime '${resolved.logicalId}' code bundle (asset ${code.codeAssetHash}) was not found ` +\n `in the cdk.out asset manifest. ${getEmbedConfig().cliName} invoke-agentcore runs a local from-source ` +\n `build of a fromCodeAsset bundle — re-synthesize the app so the asset is staged in cdk.out and retry. ` +\n `(A fromS3 bundle is downloaded from S3 instead; this runtime has no literal Code.S3.Bucket.)`,\n 'LOCAL_INVOKE_AGENTCORE_CODE_ASSET_NOT_FOUND'\n );\n }\n const sourceDir = loader.getAssetSourcePath(cdkOutDir, asset);\n if (!existsSync(sourceDir) || !statSync(sourceDir).isDirectory()) {\n throw new CdkLocalError(\n `AgentCore Runtime '${resolved.logicalId}' code bundle source '${sourceDir}' does not exist or is not a ` +\n `directory. Re-synthesize the app and retry.`,\n 'LOCAL_INVOKE_AGENTCORE_CODE_SOURCE_MISSING'\n );\n }\n return buildAgentCoreCodeImage({\n sourceDir,\n runtime: code.runtime,\n entryPoint: code.entryPoint,\n architecture,\n noBuild: options.build === false,\n });\n}\n\n/**\n * Build a local image from a fromS3 CodeConfiguration bundle: download +\n * extract the S3 object, run the from-source build over the extracted dir, then\n * clean up the temp dir.\n *\n * Credentials mirror the rest of the command: an `--assume-role` ARN (explicit,\n * or resolved from `--from-cfn-stack` state for the bare form) yields STS temp\n * creds for the download; otherwise `--profile` / the default chain is used.\n * The region is `--region` / `--stack-region` / env / the stack's region.\n */\nasync function resolveAgentCoreCodeImageFromS3(\n resolved: ResolvedAgentCoreRuntime,\n code: AgentCoreCodeArtifact,\n s3Source: NonNullable<AgentCoreCodeArtifact['s3Source']>,\n options: LocalInvokeAgentCoreOptions,\n architecture: 'x86_64' | 'arm64',\n loaded: LocalStateRecord | undefined\n): Promise<string> {\n const logger = getLogger();\n // The bucket should be a literal string by this point — a template-literal\n // bucket falls through from the resolver, an intrinsic bucket was resolved by\n // `resolveFromS3BucketIntrinsic` in the outer command before we got here.\n if (typeof s3Source.bucket !== 'string' || s3Source.bucket.length === 0) {\n throw new CdkLocalError(\n `AgentCore Runtime '${resolved.logicalId}' fromS3 bundle reached the image step with no literal bucket. ` +\n `This is a cdk-local bug — please report it.`,\n 'LOCAL_INVOKE_AGENTCORE_FROMS3_BUCKET_UNRESOLVED'\n );\n }\n const location = {\n bucket: s3Source.bucket,\n key: s3Source.key,\n ...(s3Source.versionId !== undefined && { versionId: s3Source.versionId }),\n };\n const region =\n options.region ??\n options.stackRegion ??\n process.env['AWS_REGION'] ??\n process.env['AWS_DEFAULT_REGION'] ??\n resolved.stack.region;\n\n const assumeRoleArn = resolveAssumeRoleArn(options, resolved, loaded);\n let credentials:\n | { accessKeyId: string; secretAccessKey: string; sessionToken: string }\n | undefined;\n if (assumeRoleArn) {\n try {\n credentials = await assumeAgentCoreExecutionRole(assumeRoleArn, region);\n } catch (err) {\n logger.warn(\n `--assume-role: STS AssumeRole(${assumeRoleArn}) failed for the fromS3 bundle download: ` +\n `${err instanceof Error ? err.message : String(err)}. ` +\n `Falling back to ${options.profile ? `--profile ${options.profile}` : 'the default credentials'}.`\n );\n }\n }\n\n const bundle = await downloadAndExtractS3Bundle(location, {\n ...(region !== undefined && { region }),\n ...(options.profile !== undefined && { profile: options.profile }),\n ...(credentials !== undefined && { credentials }),\n });\n try {\n return await buildAgentCoreCodeImage({\n sourceDir: bundle.dir,\n runtime: code.runtime,\n entryPoint: code.entryPoint,\n architecture,\n noBuild: options.build === false,\n });\n } finally {\n await bundle.cleanup();\n }\n}\n\n/**\n * Find the file asset whose destination objectKey is `<hash>.zip` (matching the\n * `Code.S3.Prefix`'s hash) when the source-hash-keyed lookup misses — covers a\n * synthesizer whose source hash differs from the destination objectKey.\n */\nfunction findFileAssetByObjectKey(\n fileAssets: Map<string, FileAsset>,\n hash: string\n): FileAsset | undefined {\n const zip = `${hash}.zip`;\n for (const asset of fileAssets.values()) {\n const hit = Object.values(asset.destinations).some(\n (d) => d.objectKey === zip || d.objectKey.endsWith(`/${zip}`)\n );\n if (hit) return asset;\n }\n return undefined;\n}\n\n/**\n * Build the container env + the set of env keys to keep off the `docker run`\n * argv. Substitutes `--from-cfn-stack` state into the template env (reusing the\n * shared state load + image-resolution context — Ref / Fn::Sub / Fn::Join +\n * SSM parameters, with decrypted SecureString values flagged sensitive),\n * applies `--env-vars` overrides, then injects AWS credentials (`--assume-role`\n * STS temp creds — resolving an intrinsic RoleArn from state for bare\n * `--assume-role` — else `--profile` / dev creds).\n *\n * The state provider + loaded record + image context are built once by the\n * caller and shared here, so this does not re-load state.\n */\nexport async function buildContainerEnv(\n resolved: ResolvedAgentCoreRuntime,\n options: LocalInvokeAgentCoreOptions,\n profileCredentials:\n | { accessKeyId: string; secretAccessKey: string; sessionToken?: string }\n | undefined,\n profileCredsFile: ProfileCredentialsFile | undefined,\n stateProvider: LocalStateProvider | undefined,\n loaded: LocalStateRecord | undefined,\n imageContext: ImageResolutionContext | undefined\n): Promise<{ env: Record<string, string>; sensitiveEnvKeys: Set<string> }> {\n const logger = getLogger();\n let templateEnv: Record<string, unknown> = resolved.environmentVariables;\n const sensitiveEnvKeys = new Set<string>();\n\n if (stateProvider && loaded) {\n const subContext: SubstitutionContext = {\n resources: imageContext?.stateResources ?? loaded.resources,\n consumerRegion: loaded.region,\n };\n const pseudo =\n imageContext?.pseudoParameters ?? derivePseudoParametersFromRegion(loaded.region);\n if (pseudo) subContext.pseudoParameters = pseudo;\n if (imageContext?.stateParameters) subContext.parameters = imageContext.stateParameters;\n if (imageContext?.stateSensitiveParameters?.length) {\n subContext.sensitiveParameters = new Set(imageContext.stateSensitiveParameters);\n }\n const resolver = await stateProvider.buildCrossStackResolver(loaded.region);\n if (resolver) subContext.crossStackResolver = resolver;\n const { env, audit } = await substituteEnvVarsFromStateAsync(templateEnv, subContext);\n templateEnv = env;\n for (const key of audit.resolvedKeys) {\n logger.debug(`${stateProvider.label}: substituted env var ${key}`);\n }\n // Decrypted SecureString SSM values: keep them off the `docker run` argv.\n for (const key of audit.sensitiveKeys) sensitiveEnvKeys.add(key);\n for (const { key, reason } of audit.unresolved) {\n logger.warn(\n `${stateProvider.label}: could not substitute env var ${key} (${reason}). ` +\n `Override it via --env-vars or it will be dropped.`\n );\n }\n }\n\n const overrides = readEnvOverridesFile(options.envVars);\n const cdkPath = readCdkPathOrUndefined(resolved.resource);\n const envResult = resolveEnvVars(resolved.logicalId, cdkPath, templateEnv, overrides);\n for (const key of envResult.unresolved) {\n const overrideKeyExample = cdkPath?.replace(/\\/Resource$/, '') ?? resolved.logicalId;\n logger.warn(\n `Environment variable ${key} contains a CloudFormation intrinsic and was dropped. ` +\n `Override it with --env-vars (e.g. {\"${overrideKeyExample}\":{\"${key}\":\"<literal>\"}}), ` +\n `or pass a state-source flag (e.g. --from-cfn-stack) to recover deployed values.`\n );\n }\n\n const dockerEnv: Record<string, string> = { ...envResult.resolved };\n const assumeRoleArn = resolveAssumeRoleArn(options, resolved, loaded);\n await applyAgentCoreCredentialEnv(dockerEnv, {\n ...(assumeRoleArn !== undefined && { assumeRoleArn }),\n ...(options.region !== undefined && { region: options.region }),\n ...(profileCredentials !== undefined && { profileCredentials }),\n ...(profileCredsFile !== undefined && {\n profileCredsFile: {\n containerPath: profileCredsFile.containerPath,\n profileName: profileCredsFile.profileName,\n },\n }),\n });\n return { env: dockerEnv, sensitiveEnvKeys };\n}\n\n/**\n * Resolve a fromS3 bundle's intrinsic `Code.S3.Bucket` to a literal bucket\n * name in place on `resolved.codeArtifact.s3Source.bucket`. Uses the SAME\n * state-substitution machinery env vars use under `--from-cfn-stack`, so\n * every cross-stack intrinsic that path supports (`Ref` / `Fn::ImportValue` /\n * `Fn::GetStackOutput`) is supported transparently here.\n *\n * No-op when there is no intrinsic to resolve. Errors when no state is\n * available, or when the substitution returns a non-string / unresolved value.\n *\n * Exported so a unit test can drive the gate without the full Docker pipeline.\n */\nexport async function resolveFromS3BucketIntrinsic(\n resolved: ResolvedAgentCoreRuntime,\n stateProvider: LocalStateProvider | undefined,\n loaded: LocalStateRecord | undefined,\n imageContext: ImageResolutionContext | undefined\n): Promise<void> {\n const s3Source = resolved.codeArtifact?.s3Source;\n if (!s3Source || s3Source.bucketIntrinsic === undefined) return;\n if (s3Source.bucket !== undefined) return; // already resolved\n\n if (!stateProvider || !loaded) {\n throw new CdkLocalError(\n `AgentCore Runtime '${resolved.logicalId}' fromS3 bundle's Code.S3.Bucket is an unresolved intrinsic ` +\n `(${describeIntrinsic(s3Source.bucketIntrinsic)}). ` +\n `Pass --from-cfn-stack so its physical bucket name can be resolved against the deployed stack state.`,\n 'LOCAL_INVOKE_AGENTCORE_FROMS3_BUCKET_INTRINSIC_NO_STATE'\n );\n }\n\n const subContext: SubstitutionContext = {\n resources: imageContext?.stateResources ?? loaded.resources,\n consumerRegion: loaded.region,\n };\n const pseudo = imageContext?.pseudoParameters ?? derivePseudoParametersFromRegion(loaded.region);\n if (pseudo) subContext.pseudoParameters = pseudo;\n const crossStackResolver = await stateProvider.buildCrossStackResolver(loaded.region);\n if (crossStackResolver) subContext.crossStackResolver = crossStackResolver;\n\n const result = await substituteAgainstStateAsync(s3Source.bucketIntrinsic, subContext);\n if (result.kind !== 'literal') {\n throw new CdkLocalError(\n `Could not resolve AgentCore Runtime '${resolved.logicalId}' fromS3 Code.S3.Bucket intrinsic ` +\n `(${describeIntrinsic(s3Source.bucketIntrinsic)}) against the --from-cfn-stack state: ${result.reason}. ` +\n `Confirm the referenced resource / export exists in the deployed stack.`,\n 'LOCAL_INVOKE_AGENTCORE_FROMS3_BUCKET_INTRINSIC_UNRESOLVED'\n );\n }\n if (typeof result.value !== 'string' || result.value.length === 0) {\n throw new CdkLocalError(\n `AgentCore Runtime '${resolved.logicalId}' fromS3 Code.S3.Bucket intrinsic resolved to a ` +\n `${typeof result.value} value, not a bucket name string. ` +\n `(${describeIntrinsic(s3Source.bucketIntrinsic)})`,\n 'LOCAL_INVOKE_AGENTCORE_FROMS3_BUCKET_INTRINSIC_NOT_STRING'\n );\n }\n s3Source.bucket = result.value;\n getLogger().info(\n `Resolved fromS3 Code.S3.Bucket from state: ${describeIntrinsic(s3Source.bucketIntrinsic)} -> ${result.value}`\n );\n}\n\n/** Render the intrinsic key for an error / log message (e.g. `Ref:Bucket1`). */\nfunction describeIntrinsic(value: unknown): string {\n if (!value || typeof value !== 'object') return String(value);\n const obj = value as Record<string, unknown>;\n const key = Object.keys(obj)[0] ?? '?';\n const arg = obj[key];\n if (typeof arg === 'string') return `${key}:${arg}`;\n return key;\n}\n\n/**\n * Build the `--from-cfn-stack` image-resolution context + return the loaded\n * state record (loaded once, reused by env substitution + role resolution).\n * Mirrors `run-task`'s `buildEcsImageResolutionContext`: pseudo parameters\n * (region + STS account id), the deployed resources, and SSM template\n * parameters (decrypted SecureString logical ids flagged sensitive).\n */\nexport async function buildAgentCoreImageContext(\n candidate: StackInfo,\n stateProvider: LocalStateProvider,\n options: LocalInvokeAgentCoreOptions\n): Promise<{ context: ImageResolutionContext | undefined; loaded: LocalStateRecord | undefined }> {\n const logger = getLogger();\n const region =\n options.region ??\n process.env['AWS_REGION'] ??\n process.env['AWS_DEFAULT_REGION'] ??\n candidate.region;\n\n let accountId: string | undefined;\n try {\n accountId = await resolveCallerAccountId(region, options.profile);\n } catch (err) {\n logger.warn(\n `--from-cfn-stack: STS GetCallerIdentity failed: ${err instanceof Error ? err.message : String(err)}. ` +\n 'A same-stack ECR image URI referencing ${AWS::AccountId} may not resolve.'\n );\n }\n\n const context: ImageResolutionContext = {};\n const pseudo = derivePseudoParametersFromRegion(region, accountId);\n if (pseudo) context.pseudoParameters = pseudo;\n\n const loaded = await stateProvider.load(candidate.stackName, candidate.region);\n if (loaded) {\n context.stateResources = loaded.resources;\n if (stateProvider.resolveTemplateSsmParameters) {\n const ssm = await stateProvider.resolveTemplateSsmParameters(candidate.template);\n if (Object.keys(ssm.values).length > 0) context.stateParameters = ssm.values;\n if (ssm.secureStringLogicalIds.length > 0) {\n context.stateSensitiveParameters = ssm.secureStringLogicalIds;\n }\n }\n }\n return { context, loaded: loaded ?? undefined };\n}\n\n/** STS `GetCallerIdentity` for the `${AWS::AccountId}` pseudo parameter (threads `--profile`). */\nasync function resolveCallerAccountId(\n region: string | undefined,\n profile: string | undefined\n): Promise<string | undefined> {\n const { STSClient, GetCallerIdentityCommand } = await import('@aws-sdk/client-sts');\n const sts = new STSClient({ ...(region && { region }), ...(profile && { profile }) });\n try {\n const identity = await sts.send(new GetCallerIdentityCommand({}));\n return identity.Account;\n } finally {\n sts.destroy();\n }\n}\n\n/**\n * Inject AWS credentials into the container env. Precedence:\n * 1. `--assume-role` → STS-issued temp creds for the resolved ARN (on\n * STS failure, warn + fall through to dev creds).\n * 2. dev shell creds (`forwardAwsEnv`) + `--profile` overlay\n * ({@link applyProfileCredentialsOverlay}) + the bind-mounted\n * credentials-file env so handler `fromIni({ profile })` resolves.\n *\n * Exported so a unit test can lock the binding (mock STS) without driving\n * the full synth + docker pipeline.\n */\nexport async function applyAgentCoreCredentialEnv(\n dockerEnv: Record<string, string>,\n args: {\n assumeRoleArn?: string;\n region?: string;\n profileCredentials?: { accessKeyId: string; secretAccessKey: string; sessionToken?: string };\n profileCredsFile?: { containerPath: string; profileName: string };\n }\n): Promise<void> {\n const logger = getLogger();\n let assumeSucceeded = false;\n if (args.assumeRoleArn) {\n const stsRegion = args.region ?? process.env['AWS_REGION'] ?? process.env['AWS_DEFAULT_REGION'];\n try {\n const creds = await assumeAgentCoreExecutionRole(args.assumeRoleArn, stsRegion);\n dockerEnv['AWS_ACCESS_KEY_ID'] = creds.accessKeyId;\n dockerEnv['AWS_SECRET_ACCESS_KEY'] = creds.secretAccessKey;\n dockerEnv['AWS_SESSION_TOKEN'] = creds.sessionToken;\n if (stsRegion) dockerEnv['AWS_REGION'] = stsRegion;\n assumeSucceeded = true;\n } catch (err) {\n logger.warn(\n `--assume-role: STS AssumeRole(${args.assumeRoleArn}) failed: ${err instanceof Error ? err.message : String(err)}. ` +\n \"Falling back to the developer's shell credentials.\"\n );\n }\n }\n if (!assumeSucceeded) {\n forwardAwsEnv(dockerEnv);\n applyProfileCredentialsOverlay(dockerEnv, args.profileCredentials, false);\n if (args.profileCredsFile) {\n dockerEnv['AWS_SHARED_CREDENTIALS_FILE'] = args.profileCredsFile.containerPath;\n dockerEnv['AWS_PROFILE'] = args.profileCredsFile.profileName;\n }\n }\n}\n\n/**\n * Resolve the role ARN to assume, honoring the three `--assume-role` forms.\n * Bare `--assume-role` uses the runtime's literal `RoleArn`; when that is an\n * intrinsic (the common L2 case — `Fn::GetAtt` to an auto-created role) it\n * resolves the execution-role ARN from `--from-cfn-stack` state, and only\n * warns + falls back to dev creds when neither is available.\n */\nexport function resolveAssumeRoleArn(\n options: LocalInvokeAgentCoreOptions,\n resolved: ResolvedAgentCoreRuntime,\n loaded: LocalStateRecord | undefined\n): string | undefined {\n if (typeof options.assumeRole === 'string') return options.assumeRole;\n if (options.assumeRole === true) {\n if (resolved.roleArn) return resolved.roleArn;\n if (loaded) {\n const fromState = resolveExecutionRoleArnFromState(loaded, resolved.logicalId, 'RoleArn');\n if (fromState) {\n getLogger().debug(`--assume-role: resolved RoleArn from state: ${fromState}`);\n return fromState;\n }\n }\n getLogger().warn(\n \"--assume-role passed without an ARN, but the runtime's RoleArn is not a literal ARN in the template \" +\n (loaded\n ? 'and could not be resolved from the deployed stack state. '\n : 'and no --from-cfn-stack state is available to resolve it. ') +\n 'Pass the ARN explicitly: --assume-role <arn>. ' +\n \"Falling back to the developer's shell credentials.\"\n );\n }\n return undefined;\n}\n\nexport function emitResult(result: AgentCoreInvokeResult): void {\n const logger = getLogger();\n if (result.status >= 400) {\n logger.warn(`Agent /invocations returned HTTP ${result.status}.`);\n process.exitCode = 1;\n }\n if (result.streamed) {\n // The SSE body was already written chunk-by-chunk via the onChunk sink;\n // just terminate with a newline so the shell prompt resumes cleanly.\n process.stdout.write('\\n');\n return;\n }\n process.stdout.write(`${result.raw}\\n`);\n}\n\n/**\n * Finish a `/ws` exchange: the frames were already streamed to stdout via the\n * onMessage sink, so just terminate with a newline (so the shell prompt resumes\n * cleanly) and note the frame count at debug level.\n */\nexport function emitWsResult(result: AgentCoreWsResult): void {\n process.stdout.write('\\n');\n getLogger().debug(`Agent /ws closed after ${result.frames} frame(s).`);\n}\n\n/**\n * Build the JSON-RPC request to send to an MCP runtime from `--event`:\n * - no `--event` (empty object) → `tools/list` (discover the server's tools),\n * - an object with a string `method` → that method + its `params`,\n * - anything else → a fail-fast error.\n *\n * Exported for unit testing.\n */\nexport function buildMcpRequest(event: unknown): McpJsonRpcRequest {\n if (event === undefined || event === null) return { method: 'tools/list', params: {} };\n if (typeof event !== 'object' || Array.isArray(event)) {\n throw new CdkLocalError(\n 'MCP --event must be a JSON object describing a JSON-RPC request ' +\n '(e.g. {\"method\":\"tools/call\",\"params\":{\"name\":\"...\",\"arguments\":{...}}}).',\n 'LOCAL_INVOKE_AGENTCORE_MCP_EVENT_INVALID'\n );\n }\n const obj = event as Record<string, unknown>;\n if (Object.keys(obj).length === 0) return { method: 'tools/list', params: {} };\n if (typeof obj['method'] !== 'string') {\n throw new CdkLocalError(\n 'MCP --event must include a string \"method\" (a JSON-RPC method such as ' +\n `\"tools/list\" or \"tools/call\"). Got keys: ${Object.keys(obj).join(', ')}.`,\n 'LOCAL_INVOKE_AGENTCORE_MCP_EVENT_INVALID'\n );\n }\n return {\n method: obj['method'],\n ...(obj['params'] !== undefined && { params: obj['params'] }),\n };\n}\n\n/** Print the MCP JSON-RPC response; exit 1 when it carried a JSON-RPC error. */\nexport function emitMcpResult(result: McpInvokeResult): void {\n if (!result.ok) {\n getLogger().warn('MCP server returned a JSON-RPC error.');\n process.exitCode = 1;\n }\n process.stdout.write(`${result.raw}\\n`);\n}\n\n/**\n * Build the JSON-RPC request to send to an A2A runtime from `--event`:\n * - no `--event` (empty object) → `agent/getCard` (discover the agent's card),\n * - an object with a string `method` → that method + its `params`,\n * - anything else → a fail-fast error.\n *\n * Exported for unit testing.\n */\nexport function buildA2aRequest(event: unknown): A2aJsonRpcRequest {\n if (event === undefined || event === null) return { method: 'agent/getCard', params: {} };\n if (typeof event !== 'object' || Array.isArray(event)) {\n throw new CdkLocalError(\n 'A2A --event must be a JSON object describing a JSON-RPC request ' +\n '(e.g. {\"method\":\"tasks/send\",\"params\":{\"id\":\"...\",\"message\":{...}}}).',\n 'LOCAL_INVOKE_AGENTCORE_A2A_EVENT_INVALID'\n );\n }\n const obj = event as Record<string, unknown>;\n if (Object.keys(obj).length === 0) return { method: 'agent/getCard', params: {} };\n if (typeof obj['method'] !== 'string') {\n throw new CdkLocalError(\n 'A2A --event must include a string \"method\" (a JSON-RPC method such as ' +\n `\"agent/getCard\" or \"tasks/send\"). Got keys: ${Object.keys(obj).join(', ')}.`,\n 'LOCAL_INVOKE_AGENTCORE_A2A_EVENT_INVALID'\n );\n }\n return {\n method: obj['method'],\n ...(obj['params'] !== undefined && { params: obj['params'] }),\n };\n}\n\n/** Print the A2A JSON-RPC response; exit 1 when it carried a JSON-RPC error. */\nexport function emitA2aResult(result: A2aInvokeResult): void {\n if (!result.ok) {\n getLogger().warn('A2A server returned a JSON-RPC error.');\n process.exitCode = 1;\n }\n process.stdout.write(`${result.raw}\\n`);\n}\n\n/** Map a `--platform` value to the architecture `buildContainerImage` expects. */\nexport function platformToArchitecture(platform: string): 'x86_64' | 'arm64' {\n return platform === 'linux/amd64' ? 'x86_64' : 'arm64';\n}\n\nfunction forwardAwsEnv(env: Record<string, string>): void {\n const passThrough = [\n 'AWS_ACCESS_KEY_ID',\n 'AWS_SECRET_ACCESS_KEY',\n 'AWS_SESSION_TOKEN',\n 'AWS_REGION',\n 'AWS_DEFAULT_REGION',\n ] as const;\n for (const key of passThrough) {\n const value = process.env[key];\n if (value !== undefined) env[key] = value;\n }\n}\n\nasync function assumeAgentCoreExecutionRole(\n roleArn: string,\n region: string | undefined\n): Promise<{ accessKeyId: string; secretAccessKey: string; sessionToken: string }> {\n const { STSClient, AssumeRoleCommand } = await import('@aws-sdk/client-sts');\n const sts = new STSClient({ ...(region && { region }) });\n try {\n const response = await sts.send(\n new AssumeRoleCommand({\n RoleArn: roleArn,\n RoleSessionName: `${getEmbedConfig().resourceNamePrefix}-invoke-agentcore-${Date.now()}`,\n DurationSeconds: 3600,\n })\n );\n const creds = response.Credentials;\n if (!creds?.AccessKeyId || !creds.SecretAccessKey || !creds.SessionToken) {\n throw new Error(`AssumeRole(${roleArn}) returned no usable credentials.`);\n }\n return {\n accessKeyId: creds.AccessKeyId,\n secretAccessKey: creds.SecretAccessKey,\n sessionToken: creds.SessionToken,\n };\n } finally {\n sts.destroy();\n }\n}\n\nexport async function readEvent(options: LocalInvokeAgentCoreOptions): Promise<unknown> {\n if (options.event && options.eventStdin) {\n throw new Error('--event and --event-stdin are mutually exclusive.');\n }\n if (options.eventStdin) {\n return parseEvent(await readStdin(), '<stdin>');\n }\n if (options.event) {\n let raw: string;\n try {\n raw = readFileSync(options.event, 'utf-8');\n } catch (err) {\n throw new Error(\n `Failed to read --event file '${options.event}': ${err instanceof Error ? err.message : String(err)}`\n );\n }\n return parseEvent(raw, options.event);\n }\n return {};\n}\n\nfunction parseEvent(raw: string, source: string): unknown {\n try {\n return JSON.parse(raw);\n } catch (err) {\n throw new Error(\n `Failed to parse event payload from ${source} as JSON: ${err instanceof Error ? err.message : String(err)}`\n );\n }\n}\n\nasync function readStdin(): Promise<string> {\n const chunks: Buffer[] = [];\n for await (const chunk of process.stdin as AsyncIterable<Buffer | string>) {\n chunks.push(typeof chunk === 'string' ? Buffer.from(chunk) : chunk);\n }\n return Buffer.concat(chunks).toString('utf-8');\n}\n\n/**\n * Read `process.stdin` line-buffered and yield each line as a string (trailing\n * `\\r?\\n` stripped). The async iterable completes when stdin EOFs (Ctrl-D /\n * end-of-pipe), and surrenders the underlying stream when its `return()` is\n * called — so the WS client can close down the source when the server closes\n * first without leaving stdin held open.\n *\n * Exported so a unit test can drive the iterable shape directly.\n */\nexport async function* readStdinLines(): AsyncIterable<string> {\n const { createInterface } = await import('node:readline');\n const rl = createInterface({ input: process.stdin, crlfDelay: Infinity });\n try {\n for await (const line of rl) {\n yield line;\n }\n } finally {\n rl.close();\n }\n}\n\nexport function readEnvOverridesFile(filePath: string | undefined): EnvOverrideFile | undefined {\n if (!filePath) return undefined;\n let raw: string;\n try {\n raw = readFileSync(filePath, 'utf-8');\n } catch (err) {\n throw new Error(\n `Failed to read --env-vars file '${filePath}': ${err instanceof Error ? err.message : String(err)}`\n );\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (err) {\n throw new Error(\n `Failed to parse --env-vars file '${filePath}' as JSON: ${err instanceof Error ? err.message : String(err)}`\n );\n }\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {\n throw new Error(`--env-vars file '${filePath}' must contain a JSON object at the top level.`);\n }\n return parsed as EnvOverrideFile;\n}\n\nexport function createLocalInvokeAgentCoreCommand(\n opts: CreateLocalInvokeAgentCoreCommandOptions = {}\n): Command {\n setEmbedConfig(opts.embedConfig);\n const cmd = new Command('invoke-agentcore')\n .description(\n 'Run a Bedrock AgentCore Runtime container locally and invoke it once over its protocol ' +\n 'contract: HTTP (POST /invocations + GET /ping on 8080) or MCP (POST /mcp Streamable HTTP ' +\n 'on 8000). Resolves the AWS::BedrockAgentCore::Runtime, pulls/builds its container, injects ' +\n 'env vars + AWS credentials, and prints the response. For an MCP runtime, runs the session ' +\n 'handshake then sends one JSON-RPC request (tools/list by default, or the method/params from ' +\n '--event). Target accepts a CDK display path (MyStack/MyAgent) or stack-qualified logical ID ' +\n '(MyStack:MyAgentRuntime1234). Single-stack apps may omit the stack prefix. ' +\n 'Omit <target> in an interactive terminal to pick from a list. ' +\n 'Supports the container artifact and the CodeConfiguration managed-runtime artifact ' +\n '(fromCodeAsset, built from source) on the HTTP + MCP protocols; the agent calls real AWS for managed services.'\n )\n .argument(\n '[target]',\n 'CDK display path or stack-qualified logical ID of the AgentCore Runtime to invoke (omit to pick interactively in a TTY)'\n )\n .addOption(new Option('-e, --event <file>', 'JSON event payload file (default: {})'))\n .addOption(new Option('--event-stdin', 'Read event JSON from stdin').default(false))\n .addOption(\n new Option(\n '--env-vars <file>',\n 'JSON env-var overrides (SAM-compatible: {\"LogicalId\":{\"KEY\":\"VALUE\"}})'\n )\n )\n .addOption(\n new Option(\n '--session-id <id>',\n 'AgentCore runtime session id header value (default: a random UUID)'\n )\n )\n .addOption(\n new Option(\n '--ws',\n \"Stream over the HTTP-protocol agent's bidirectional /ws WebSocket endpoint (on 8080) \" +\n 'instead of POST /invocations: send --event as the first frame and print every received ' +\n 'frame to stdout until the agent closes. Ignored for an MCP runtime.'\n ).default(false)\n )\n .addOption(\n new Option(\n '--ws-interactive',\n 'REPL mode for --ws: after the initial --event frame, read additional frames from stdin ' +\n '(one frame per line, trailing newline stripped) and send each as a text frame until ' +\n 'stdin EOFs (Ctrl-D) or the agent closes. Only meaningful with --ws.'\n ).default(false)\n )\n .addOption(\n new Option(\n '--bearer-token <jwt>',\n 'Bearer JWT to present when the runtime declares a customJwtAuthorizer. ' +\n 'Verified against the runtime OIDC discovery URL (signature / issuer / expiry / ' +\n 'audience) before the container starts, then forwarded to /invocations as ' +\n 'Authorization: Bearer <jwt>.'\n )\n )\n .addOption(\n new Option(\n '--no-verify-auth',\n 'Skip inbound JWT verification even when the runtime declares a customJwtAuthorizer ' +\n '(local-dev escape hatch). A --bearer-token, if given, is still forwarded.'\n )\n )\n .addOption(\n new Option(\n '--sigv4',\n 'Sign the /invocations POST with AWS SigV4 (service bedrock-agentcore) using the resolved ' +\n 'credentials, matching the cloud default when the runtime declares no customJwtAuthorizer. ' +\n 'Opt-in: default unsigned. Mutually exclusive with --bearer-token; ignored on a JWT-protected runtime.'\n ).default(false)\n )\n .addOption(\n new Option(\n '--platform <platform>',\n 'docker --platform for the agent container (linux/amd64 or linux/arm64)'\n )\n .choices(['linux/amd64', 'linux/arm64'])\n .default('linux/arm64')\n )\n .addOption(\n new Option(\n '--no-pull',\n 'Skip docker pull (use cached image) — no-op for the local-build path'\n )\n )\n .addOption(\n new Option(\n '--no-build',\n 'Skip docker build on the local-asset path (use the previously-built tag). No-op for the ECR / registry pull paths.'\n )\n )\n .addOption(\n new Option('--container-host <host>', 'Host to bind the agent port to').default('127.0.0.1')\n )\n .addOption(\n new Option(\n '--timeout <ms>',\n 'Per-request timeout in milliseconds. Applied to POST /invocations, POST /mcp, and the ' +\n '/ws open-to-close window. Raise this for long-running agent calls that exceed the default.'\n )\n .default(120000)\n .argParser(parseTimeoutMs)\n )\n .addOption(\n new Option(\n '--assume-role [arn]',\n \"Assume the runtime's execution role and forward STS-issued temp credentials to the container \" +\n 'so the agent runs with the deployed role. Three forms: ' +\n '(1) `--assume-role <arn>` assumes the explicit ARN; ' +\n \"(2) `--assume-role` (bare) uses the runtime's RoleArn when it is a literal ARN; \" +\n '(3) `--no-assume-role` opts out. ' +\n \"Off by default — the developer's shell credentials are forwarded unchanged.\"\n )\n )\n .addOption(\n new Option(\n '--ecr-role-arn <arn>',\n 'Role ARN to assume before authenticating against ECR for cross-account / centralized registries. ' +\n 'Same-account / same-region pulls do not need this flag.'\n )\n )\n .addOption(\n new Option(\n '--from-cfn-stack [cfn-stack-name]',\n 'Read a deployed CloudFormation stack via ListStackResources and substitute Ref / Fn::ImportValue ' +\n 'in env vars with the deployed physical IDs / exports. Bare form uses the resolved stack name; ' +\n 'pass an explicit value when the CFn stack name differs.'\n )\n )\n .addOption(\n new Option(\n '--stack-region <region>',\n 'Region of the state record to read. Used with --from-cfn-stack as the CFn client region.'\n )\n )\n .action(\n withErrorHandling(\n async (target: string | undefined, options: LocalInvokeAgentCoreOptions) => {\n await localInvokeAgentCoreCommand(target, options, opts.extraStateProviders);\n }\n )\n );\n\n [...commonOptions(), ...appOptions(), ...contextOptions].forEach((opt) => cmd.addOption(opt));\n cmd.addOption(deprecatedRegionOption);\n return cmd;\n}\n","import { execFile } from 'node:child_process';\nimport { randomBytes } from 'node:crypto';\nimport { promisify } from 'node:util';\nimport { getDockerCmd } from '../utils/docker-cmd.js';\nimport { getLogger } from '../utils/logger.js';\nimport {\n DockerRunnerError,\n pullImage,\n removeContainer,\n appendEnvFlags,\n execEnvForSecrets,\n SENSITIVE_ENV_KEYS,\n} from './docker-runner.js';\nimport { getEmbedConfig } from './embed-config.js';\n\nconst execFileAsync = promisify(execFile);\n\n/**\n * Docker network + AWS-published metadata-endpoints sidecar lifecycle for\n * `cdkl run-task`. The sidecar (a small Go binary maintained by\n * awslabs) is started at `169.254.170.2` on the per-task docker network so\n * containers can hit `http://169.254.170.2/v4/<container-id>` for task\n * metadata AND `AWS_CONTAINER_CREDENTIALS_RELATIVE_URI=/role/<role-arn>`\n * for IAM task-role credentials. cdk-local does NOT re-implement the sidecar\n * — pulling the AWS-published image keeps cdk-local in lock-step with whatever\n * ECS-Agent fidelity AWS chooses to provide.\n */\n\n/** AWS-published sidecar image (latest tag). amd64 is the only image AWS ships. */\nexport const METADATA_ENDPOINT_IMAGE = 'amazon/amazon-ecs-local-container-endpoints:latest-amd64';\n\n/**\n * Default well-known IP for the ECS local-container-endpoints sidecar —\n * matches the documented AWS task-metadata endpoint address. Containers\n * inject `ECS_CONTAINER_METADATA_URI_V4=http://169.254.170.2/v4/<id>`\n * to reach it. `cdkl run-task` keeps this verbatim; `cdk-local\n * start-service` creates ONE shared network at CLI startup (design\n * § 5 Option A) — the shared sidecar lives at `169.254.171.2` (see\n * `SHARED_SVC_SUBNET_OCTET` below), one octet up so the two CLI\n * variants can run on the same host without bridge-pool collision.\n */\nexport const METADATA_ENDPOINT_IP = '169.254.170.2';\n\n/** Default subnet — used when no `subnetOctet` override is supplied. */\nconst DEFAULT_METADATA_ENDPOINT_SUBNET = '169.254.170.0/24';\n\n/**\n * Pure-functional subnet allocator. `cdkl run-task` uses the\n * default subnet (octet 170); `cdkl start-service` uses a SINGLE\n * shared network at the fixed octet `SHARED_SVC_SUBNET_OCTET = 171`\n * (one octet up from run-task so the two CLI variants can coexist on\n * the same host). The link-local 169.254.0.0/16 space is reserved\n * AWS-wide for cloud metadata so collisions with user workloads are\n * unlikely, but the fixed /24 still keeps docker's `--subnet`\n * allocator from rejecting \"Pool overlaps\".\n *\n * `subnetOctet` is the second-from-last byte of the network: 170 →\n * 169.254.170.0/24 (default), 171 → 169.254.171.0/24, etc. Valid\n * range is 1..254 — exported here so callers keep the CIDR /\n * sidecar-IP derivation in one place.\n */\nexport function buildEndpointSubnet(subnetOctet: number): {\n cidr: string;\n sidecarIp: string;\n} {\n if (subnetOctet < 1 || subnetOctet > 254 || !Number.isInteger(subnetOctet)) {\n throw new Error(\n `buildEndpointSubnet: subnetOctet must be an integer in 1..254 (got ${subnetOctet}).`\n );\n }\n return {\n cidr: `169.254.${subnetOctet}.0/24`,\n sidecarIp: `169.254.${subnetOctet}.2`,\n };\n}\n\nexport interface TaskNetwork {\n /** Generated docker network name (`<prefix>-task-<rand>` or `<prefix>-svc-<rand>` for shared). */\n networkName: string;\n /** Container id of the metadata-endpoints sidecar. Cleaned up at teardown. */\n sidecarContainerId: string;\n /**\n * Resolved sidecar IP for THIS network instance. `cdkl run-task`\n * sees 169.254.170.2 (the default); `cdkl start-service` shares\n * a single network across every service in the run at\n * `169.254.171.2`. Containers' `ECS_CONTAINER_METADATA_URI_V4` is\n * derived from this so each replica's containers hit the right\n * sidecar regardless of which network they joined.\n */\n sidecarIp: string;\n /**\n * When true, the network + sidecar are owned by the caller (the CLI\n * created them once and reuses across every task / replica boot in\n * the run) and `cleanupEcsRun()` MUST NOT teardown — only the caller\n * tears down at the end of the CLI lifecycle. When false / undefined,\n * the task runner owns the lifecycle (the pre-existing\n * `cdkl run-task` shape: one network per task, torn down\n * with the task).\n */\n ownedByCaller?: boolean;\n}\n\nexport interface CreateTaskNetworkOptions {\n /**\n * Docker network name prefix. Default `cdkl`; the runner injects\n * the CLI's `--cluster <name>`. The full name is `<prefix>-task-<rand>`.\n */\n prefix?: string;\n /**\n * When set, the sidecar receives `AWS_ACCESS_KEY_ID` /\n * `AWS_SECRET_ACCESS_KEY` / `AWS_SESSION_TOKEN` env vars so its\n * `/role/<role-arn>` endpoint serves these creds to the user\n * containers. When unset, the sidecar falls back to its default\n * credential chain (typically empty — the user containers will get\n * 4xx from the credentials endpoint, mimicking IAM-misconfigured prod).\n */\n credentials?: {\n accessKeyId: string;\n secretAccessKey: string;\n sessionToken?: string;\n };\n /** `--cluster <name>` value. Forwarded to the sidecar's `CLUSTER` env. */\n cluster?: string;\n /** Skip `docker pull <sidecar>`. */\n skipPull?: boolean;\n /**\n * Optional second-from-last octet of the link-local /24 subnet\n * (1..254). Default 170 (the AWS-documented metadata-endpoint subnet).\n * `cdkl start-service` walks this value per replica so\n * concurrent docker networks don't collide on the same /24 range.\n */\n subnetOctet?: number;\n}\n\n/**\n * Subnet octet for the shared-service docker network used by\n * `cdkl start-service`. One octet up from `cdkl run-task`'s\n * default (170 → 171) so the two CLI variants can run on the same host\n * without docker rejecting the second `--subnet`. The shared-service\n * network reuses the same `createTaskNetwork` machinery; the sidecar at\n * `169.254.171.2` serves the same metadata-endpoint API to every\n * container that joins this one network.\n */\nexport const SHARED_SVC_SUBNET_OCTET = 171;\n\n/**\n * Create the one shared docker network + metadata-endpoints sidecar\n * used by every service-replica boot in a single\n * `cdkl start-service` invocation. This is design doc § 5\n * Option A — one network per CLI invocation instead of one network\n * per task — so peer services can reach each other by IP / network\n * alias without docker `--network connect` choreography (Option B,\n * rejected in design § 5 as \"unwieldy and racy\"). The returned\n * `TaskNetwork` carries `ownedByCaller: true` so `cleanupEcsRun()`\n * (called per replica by the service runner) does NOT teardown — the\n * CLI tears down ONCE at the end of the run.\n */\nexport async function createSharedSvcNetwork(\n options: Omit<CreateTaskNetworkOptions, 'subnetOctet'> = {}\n): Promise<TaskNetwork> {\n const prefix = options.prefix ?? getEmbedConfig().resourceNamePrefix;\n // Reclaim any `<prefix>-svc-*` network left behind by a previous\n // `start-service` run that was interrupted (Ctrl-C / crash) before its\n // teardown. Because start-service pins a SINGLE fixed subnet\n // (SHARED_SVC_SUBNET_OCTET), a leaked network blocks every later run with\n // a \"Pool overlaps\" error that never self-heals — so sweep BEFORE the\n // fixed-subnet create rather than only surfacing the error afterwards.\n await sweepOrphanedSvcNetworks(prefix);\n const suffix = randomBytes(4).toString('hex');\n const networkName = `${prefix}-svc-${suffix}`;\n const { cidr, sidecarIp } = buildEndpointSubnet(SHARED_SVC_SUBNET_OCTET);\n const sidecarContainerId = await createNetworkAndSidecar({\n networkName,\n cidr,\n sidecarIp,\n skipPull: options.skipPull ?? false,\n ...(options.credentials !== undefined ? { credentials: options.credentials } : {}),\n ...(options.cluster !== undefined ? { cluster: options.cluster } : {}),\n });\n return { networkName, sidecarContainerId, sidecarIp, ownedByCaller: true };\n}\n\n/**\n * Startup orphan sweep for the shared `cdkl start-service` network\n * (Issue #93, design Option 1). When a `start-service` run is interrupted\n * before its end-of-run teardown, the shared `<prefix>-svc-<rand>` network\n * and its `<prefix>-svc-<rand>-metadata` sidecar LEAK. Because\n * `start-service` pins a single fixed subnet (`SHARED_SVC_SUBNET_OCTET`),\n * the next run's `docker network create` always fails with\n * \"Pool overlaps with other one on this address space\" — a state that,\n * unlike a port conflict, never self-heals. This sweep detects and removes\n * leaked `<prefix>-svc-*` networks that have NO live owner before the next\n * run re-creates the network.\n *\n * Classification heuristic: a `<prefix>-svc-*` network is ORPHANED when its\n * only attached container is its own `<name>-metadata` sidecar (or it has\n * zero attached containers). A network that still has a non-metadata\n * (user replica) container attached is a live concurrent run and is LEFT\n * untouched. Caveat: \"only the metadata sidecar attached ⇒ orphan\" can\n * misclassify a network in the sub-second window between sidecar start and\n * the first replica attach — so a second `start-service` launched in that\n * window could reclaim a concurrently-starting run's network out from under\n * it. This is an accepted limitation, not a benign one: start-service pins a\n * single fixed subnet, so two concurrent same-prefix runs were never\n * supported (the second run's `docker network create` already fails with\n * \"Pool overlaps\"). The sweep therefore cannot regress a\n * previously-working scenario.\n *\n * Resilient by design: a `docker network ls` / `inspect` failure must not\n * abort the run — it logs at debug and skips, matching the idempotent\n * teardown style in this file. Returns the list of swept network names.\n */\nexport async function sweepOrphanedSvcNetworks(prefix: string): Promise<string[]> {\n const logger = getLogger().child('ecs-network');\n const filter = `${prefix}-svc-`;\n let names: string[];\n try {\n const { stdout } = await execFileAsync(getDockerCmd(), [\n 'network',\n 'ls',\n '--filter',\n `name=${filter}`,\n '--format',\n '{{.Name}}',\n ]);\n names = stdout\n .split('\\n')\n .map((n) => n.trim())\n .filter((n) => n.length > 0);\n } catch (err) {\n const e = err as { stderr?: string; message?: string };\n logger.debug(`docker network ls (sweep) failed: ${e.stderr || e.message || String(err)}`);\n return [];\n }\n\n const swept: string[] = [];\n for (const name of names) {\n let attached: string[];\n try {\n const { stdout } = await execFileAsync(getDockerCmd(), [\n 'network',\n 'inspect',\n name,\n '--format',\n '{{range .Containers}}{{.Name}} {{end}}',\n ]);\n attached = stdout\n .split(/\\s+/)\n .map((c) => c.trim())\n .filter((c) => c.length > 0);\n } catch (err) {\n const e = err as { stderr?: string; message?: string };\n logger.debug(\n `docker network inspect ${name} (sweep) failed: ${e.stderr || e.message || String(err)}`\n );\n continue;\n }\n\n const sidecarName = `${name}-metadata`;\n const hasLiveOwner = attached.some((c) => c !== sidecarName);\n if (hasLiveOwner) {\n // A user replica is still attached — this is a live concurrent run.\n continue;\n }\n\n logger.info(`Reclaiming orphaned shared network ${name} (no live owner)...`);\n await removeContainer(sidecarName);\n await destroyNetworkOnly(name);\n swept.push(name);\n }\n return swept;\n}\n\n/**\n * Internal helper shared by `createTaskNetwork` (per-task) and\n * `createSharedSvcNetwork` (per-CLI-run). Creates the docker network,\n * pulls the sidecar image, and starts the sidecar at the documented\n * IP. Throws `DockerRunnerError` with a hint when the network already\n * exists (the typical \"leftover from previous run\" path).\n */\nasync function createNetworkAndSidecar(args: {\n networkName: string;\n cidr: string;\n sidecarIp: string;\n credentials?: CreateTaskNetworkOptions['credentials'];\n cluster?: string;\n skipPull: boolean;\n}): Promise<string> {\n const logger = getLogger().child('ecs-network');\n const { networkName, cidr, sidecarIp, credentials, cluster, skipPull } = args;\n\n await pullImage(METADATA_ENDPOINT_IMAGE, skipPull);\n\n logger.info(`Creating docker network ${networkName} (subnet ${cidr})...`);\n try {\n await execFileAsync(getDockerCmd(), [\n 'network',\n 'create',\n '--driver',\n 'bridge',\n '--subnet',\n cidr,\n networkName,\n ]);\n } catch (err) {\n const e = err as { stderr?: string; message?: string };\n throw new DockerRunnerError(\n `docker network create failed: ${e.stderr?.trim() || e.message || String(err)}. ` +\n `Hint: another ${getEmbedConfig().productName} run may already own subnet ${cidr}; wait for it to ` +\n 'finish, or remove the leftover network with `docker network ls` + ' +\n `\\`docker network rm\\`. \\`${getEmbedConfig().cliName} start-service\\` shares one network ` +\n `across every service in the run; bare \\`${getEmbedConfig().cliName} run-task\\` uses a ` +\n 'per-task network so only one run can be active at a time.'\n );\n }\n\n const sidecarArgs: string[] = [\n 'run',\n '-d',\n '--rm',\n '--name',\n `${networkName}-metadata`,\n '--network',\n networkName,\n '--ip',\n sidecarIp,\n ];\n const sidecarEnv: Record<string, string> = {};\n if (credentials) {\n sidecarEnv['AWS_ACCESS_KEY_ID'] = credentials.accessKeyId;\n sidecarEnv['AWS_SECRET_ACCESS_KEY'] = credentials.secretAccessKey;\n if (credentials.sessionToken) {\n sidecarEnv['AWS_SESSION_TOKEN'] = credentials.sessionToken;\n }\n }\n if (cluster) sidecarEnv['CLUSTER'] = cluster;\n // AWS credentials served by the metadata sidecar route through docker's\n // value-from-process-env form (`-e KEY`) so they never appear in argv;\n // CLUSTER (non-secret) keeps the inline `-e KEY=VALUE` form.\n const sidecarPassthroughEnv = appendEnvFlags(sidecarArgs, sidecarEnv, SENSITIVE_ENV_KEYS);\n sidecarArgs.push(METADATA_ENDPOINT_IMAGE);\n\n logger.info(`Starting ECS local-container-endpoints sidecar at ${sidecarIp}...`);\n try {\n const { stdout } = await execFileAsync(getDockerCmd(), sidecarArgs, {\n maxBuffer: 10 * 1024 * 1024,\n ...execEnvForSecrets(sidecarPassthroughEnv),\n });\n return stdout.trim();\n } catch (err) {\n await destroyNetworkOnly(networkName);\n const e = err as { stderr?: string; message?: string };\n throw new DockerRunnerError(\n `Failed to start metadata-endpoints sidecar: ${e.stderr?.trim() || e.message || String(err)}`\n );\n }\n}\n\n/**\n * Create the per-task docker network + start the metadata-endpoints\n * sidecar. The sidecar must come up at the well-known address BEFORE any\n * user container starts so the `AWS_CONTAINER_CREDENTIALS_RELATIVE_URI`\n * lookup at container start doesn't race.\n */\nexport async function createTaskNetwork(\n options: CreateTaskNetworkOptions = {}\n): Promise<TaskNetwork> {\n const prefix = options.prefix ?? getEmbedConfig().resourceNamePrefix;\n const suffix = randomBytes(4).toString('hex');\n const networkName = `${prefix}-task-${suffix}`;\n const { cidr, sidecarIp } =\n options.subnetOctet === undefined\n ? { cidr: DEFAULT_METADATA_ENDPOINT_SUBNET, sidecarIp: METADATA_ENDPOINT_IP }\n : buildEndpointSubnet(options.subnetOctet);\n const sidecarContainerId = await createNetworkAndSidecar({\n networkName,\n cidr,\n sidecarIp,\n skipPull: options.skipPull ?? false,\n ...(options.credentials !== undefined ? { credentials: options.credentials } : {}),\n ...(options.cluster !== undefined ? { cluster: options.cluster } : {}),\n });\n return { networkName, sidecarContainerId, sidecarIp };\n}\n\n/**\n * Build the env var entries every user container needs so its AWS SDK\n * picks up the sidecar. `<container-id>` is replaced by the actual docker\n * id post-`run` — at this point we use the container name as a stable\n * proxy since the metadata endpoint accepts a name lookup.\n *\n * `roleArn` is the optional task role ARN. When set, the credentials\n * endpoint path bakes it in so AWS SDK clients pull the assumed creds\n * automatically; when unset, the path is omitted (containers fall back\n * to whichever credentials AWS SDK chains find).\n */\nexport function buildMetadataEnv(opts: {\n containerName: string;\n roleArn?: string;\n region?: string;\n /**\n * Sidecar IP for this task's network. Defaults to the AWS-documented\n * `169.254.170.2` so `cdkl run-task` keeps the canonical shape.\n * `cdkl start-service` passes the per-replica IP allocated by\n * `buildEndpointSubnet(subnetOctet)` so each replica's containers\n * resolve their own sidecar (not a sibling replica's).\n */\n sidecarIp?: string;\n}): Record<string, string> {\n const ip = opts.sidecarIp ?? METADATA_ENDPOINT_IP;\n const env: Record<string, string> = {\n ECS_CONTAINER_METADATA_URI_V4: `http://${ip}/v4/${opts.containerName}`,\n ECS_CONTAINER_METADATA_URI: `http://${ip}/v3/${opts.containerName}`,\n };\n if (opts.roleArn) {\n env['AWS_CONTAINER_CREDENTIALS_RELATIVE_URI'] = `/role/${encodeURIComponent(opts.roleArn)}`;\n }\n if (opts.region) env['AWS_REGION'] = opts.region;\n return env;\n}\n\n/**\n * Tear down the metadata-endpoints sidecar + the docker network. Idempotent\n * — `docker rm -f` and `docker network rm` both swallow not-found errors\n * by design, and the function logs at debug instead of throwing.\n */\nexport async function destroyTaskNetwork(net: TaskNetwork | undefined): Promise<void> {\n if (!net) return;\n await removeContainer(net.sidecarContainerId);\n await destroyNetworkOnly(net.networkName);\n}\n\nasync function destroyNetworkOnly(networkName: string): Promise<void> {\n if (!networkName) return;\n const logger = getLogger().child('ecs-network');\n try {\n await execFileAsync(getDockerCmd(), ['network', 'rm', networkName]);\n logger.debug(`Removed docker network ${networkName}`);\n } catch (err) {\n const e = err as { stderr?: string; message?: string };\n logger.debug(\n `docker network rm ${networkName} failed: ${e.stderr || e.message || String(err)}`\n );\n }\n}\n","import { SecretsManagerClient, GetSecretValueCommand } from '@aws-sdk/client-secrets-manager';\nimport { SSMClient, GetParameterCommand } from '@aws-sdk/client-ssm';\nimport { getLogger } from '../utils/logger.js';\n\n/**\n * Resolve `ContainerDefinitions[].Secrets[].ValueFrom` references to real\n * values via the AWS SDK so containers can be started with the secret\n * material injected as plain env vars.\n *\n * Same resolution rules as the ECS Agent: `valueFrom` is either a\n * Secrets Manager secret ARN (optionally with a `:<json-key>::` suffix for\n * key extraction from a JSON blob) or an SSM Parameter ARN. Resolution\n * happens once at startup, before any container boots — partial resolution\n * is meaningless because a \"missing\" secret would otherwise look like a\n * literal empty string and break the container silently.\n *\n * Failure mode is hard-fail. Mirrors `cdkl invoke --from-state`'s\n * philosophy: explicit failure beats silently-empty. The user fixes their\n * AWS creds / IAM policy / parameter name and re-runs.\n */\n\nexport class EcsSecretsResolutionError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'EcsSecretsResolutionError';\n Object.setPrototypeOf(this, EcsSecretsResolutionError.prototype);\n }\n}\n\nexport interface SecretEntry {\n /** Container name (only used to thread richer error messages). */\n containerName: string;\n /** Env var name the resolved value lands at. */\n name: string;\n /** Raw ValueFrom (Secrets Manager ARN or SSM Parameter ARN). */\n valueFrom: string;\n}\n\n/**\n * Result of secret resolution: each entry maps to the container that\n * requested it plus the resolved value. Returned as a flat list (not\n * grouped) so the caller can populate per-container env maps in one pass.\n */\nexport interface ResolvedSecret extends SecretEntry {\n value: string;\n}\n\nexport interface ResolveEcsSecretsOptions {\n /** Region for the AWS SDK clients. Falls back to env defaults when unset. */\n region?: string;\n /**\n * The CLI's `--profile`. Threaded into the Secrets Manager / SSM\n * clients so `secretsmanager:GetSecretValue` / `ssm:GetParameter`\n * authenticate as the profile's account — for `--from-cfn-stack` the\n * secret lives in the deployed (profile) account, and without this the\n * default credential chain authenticates as the wrong account and AWS\n * rejects with AccessDenied. Undefined when `--profile` is unset.\n */\n profile?: string;\n /**\n * Hook for tests: supply pre-built SDK clients. Production callers\n * leave both unset and let the resolver construct + destroy its own\n * clients.\n */\n secretsManagerClient?: SecretsManagerClient;\n ssmClient?: SSMClient;\n}\n\n/**\n * Resolve every secret entry in parallel. Returns successfully only when\n * every entry resolved — a single failure aborts the whole batch with the\n * offending container + secret name in the error message.\n */\nexport async function resolveEcsSecrets(\n entries: SecretEntry[],\n options: ResolveEcsSecretsOptions = {}\n): Promise<ResolvedSecret[]> {\n if (entries.length === 0) return [];\n const logger = getLogger().child('ecs-secrets');\n\n const secretsClient =\n options.secretsManagerClient ??\n new SecretsManagerClient({\n ...(options.region && { region: options.region }),\n ...(options.profile && { profile: options.profile }),\n });\n const ssmClient =\n options.ssmClient ??\n new SSMClient({\n ...(options.region && { region: options.region }),\n ...(options.profile && { profile: options.profile }),\n });\n const ownsSecretsClient = options.secretsManagerClient === undefined;\n const ownsSsmClient = options.ssmClient === undefined;\n\n try {\n const results = await Promise.all(\n entries.map(async (entry) => {\n const value = await resolveOne(entry, secretsClient, ssmClient);\n logger.debug(`Resolved secret ${entry.containerName}.${entry.name} (${entry.valueFrom})`);\n return { ...entry, value };\n })\n );\n return results;\n } finally {\n if (ownsSecretsClient) secretsClient.destroy();\n if (ownsSsmClient) ssmClient.destroy();\n }\n}\n\nasync function resolveOne(\n entry: SecretEntry,\n secretsClient: SecretsManagerClient,\n ssmClient: SSMClient\n): Promise<string> {\n const arn = entry.valueFrom;\n const shape = classifySecretArn(arn);\n switch (shape.kind) {\n case 'secrets-manager':\n return resolveSecretsManager(entry, shape, secretsClient);\n case 'ssm':\n return resolveSsm(entry, shape, ssmClient);\n case 'unknown':\n throw new EcsSecretsResolutionError(\n `Container '${entry.containerName}' secret '${entry.name}' references an unsupported ValueFrom shape '${arn}'. ` +\n 'Expected Secrets Manager ARN (optionally with :<json-key>::) or SSM Parameter ARN.'\n );\n }\n}\n\ninterface SecretsManagerShape {\n kind: 'secrets-manager';\n /** Full ARN minus the optional `:<json-key>::` suffix. */\n baseArn: string;\n /** When set, extract this top-level key from the JSON-decoded SecretString. */\n jsonKey?: string;\n}\n\ninterface SsmShape {\n kind: 'ssm';\n /** Parameter name (with leading `/`). */\n name: string;\n}\n\ninterface UnknownShape {\n kind: 'unknown';\n}\n\n/**\n * Classify the `ValueFrom` string per the AWS ECS Agent rules. Three\n * accepted shapes:\n * - `arn:aws:secretsmanager:<region>:<account>:secret:<name>`\n * - `arn:aws:secretsmanager:<region>:<account>:secret:<name>:<json-key>::`\n * - `arn:aws:ssm:<region>:<account>:parameter/<name>`\n */\nexport function classifySecretArn(arn: string): SecretsManagerShape | SsmShape | UnknownShape {\n if (!arn.startsWith('arn:')) return { kind: 'unknown' };\n\n // Secrets Manager: 7 colon-delimited segments minimum\n // (arn:aws:secretsmanager:region:account:secret:name). The optional\n // json-key suffix appends `:<json-key>::` — exactly two trailing colons\n // because the SecretsManager ARN convention reserves `version-stage`\n // and `version-id` slots that follow.\n const smMatch = /^(arn:[^:]+:secretsmanager:[^:]+:\\d+:secret:[^:]+)(?::([^:]+)::?)?$/.exec(arn);\n if (smMatch) {\n const out: SecretsManagerShape = { kind: 'secrets-manager', baseArn: smMatch[1]! };\n if (smMatch[2]) out.jsonKey = smMatch[2];\n return out;\n }\n\n const ssmMatch = /^arn:[^:]+:ssm:[^:]+:\\d+:parameter(\\/.+)$/.exec(arn);\n if (ssmMatch) {\n return { kind: 'ssm', name: ssmMatch[1]! };\n }\n\n return { kind: 'unknown' };\n}\n\nasync function resolveSecretsManager(\n entry: SecretEntry,\n shape: SecretsManagerShape,\n client: SecretsManagerClient\n): Promise<string> {\n let secretString: string | undefined;\n try {\n const resp = await client.send(new GetSecretValueCommand({ SecretId: shape.baseArn }));\n secretString = resp.SecretString;\n } catch (err) {\n throw new EcsSecretsResolutionError(\n `Failed to resolve Secrets Manager secret for container '${entry.containerName}' / env '${entry.name}' (${shape.baseArn}): ${\n err instanceof Error ? err.message : String(err)\n }`\n );\n }\n if (secretString === undefined) {\n throw new EcsSecretsResolutionError(\n `Secrets Manager returned no SecretString for container '${entry.containerName}' / env '${entry.name}' (${shape.baseArn}). ` +\n 'Binary secrets are not supported.'\n );\n }\n if (shape.jsonKey === undefined) return secretString;\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(secretString);\n } catch (err) {\n throw new EcsSecretsResolutionError(\n `Container '${entry.containerName}' secret '${entry.name}' specified json-key '${shape.jsonKey}' but the secret value is not valid JSON: ${\n err instanceof Error ? err.message : String(err)\n }`\n );\n }\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {\n throw new EcsSecretsResolutionError(\n `Container '${entry.containerName}' secret '${entry.name}' specified json-key '${shape.jsonKey}' but the secret root is not a JSON object.`\n );\n }\n const value = (parsed as Record<string, unknown>)[shape.jsonKey];\n if (value === undefined) {\n throw new EcsSecretsResolutionError(\n `Container '${entry.containerName}' secret '${entry.name}' specified json-key '${shape.jsonKey}' but no such key exists in the secret JSON.`\n );\n }\n return typeof value === 'string' ? value : JSON.stringify(value);\n}\n\nasync function resolveSsm(entry: SecretEntry, shape: SsmShape, client: SSMClient): Promise<string> {\n try {\n const resp = await client.send(\n new GetParameterCommand({ Name: shape.name, WithDecryption: true })\n );\n const value = resp.Parameter?.Value;\n if (value === undefined) {\n throw new EcsSecretsResolutionError(\n `SSM parameter '${shape.name}' returned no Value for container '${entry.containerName}' / env '${entry.name}'.`\n );\n }\n return value;\n } catch (err) {\n if (err instanceof EcsSecretsResolutionError) throw err;\n throw new EcsSecretsResolutionError(\n `Failed to resolve SSM parameter for container '${entry.containerName}' / env '${entry.name}' (${shape.name}): ${\n err instanceof Error ? err.message : String(err)\n }`\n );\n }\n}\n","import { execFile, spawn } from 'node:child_process';\nimport { randomBytes } from 'node:crypto';\nimport { dirname } from 'node:path';\nimport { promisify } from 'node:util';\nimport graphlib from 'graphlib';\nimport { getDockerCmd, runDockerStreaming } from '../utils/docker-cmd.js';\nimport { getLogger } from '../utils/logger.js';\nimport {\n DockerRunnerError,\n pullImage,\n removeContainer,\n appendEnvFlags,\n execEnvForSecrets,\n SENSITIVE_ENV_KEYS,\n} from './docker-runner.js';\nimport { buildDockerImage } from '../assets/docker-build.js';\nimport { pullEcrImage } from './ecr-puller.js';\nimport { LocalInvokeBuildError } from '../utils/error-handler.js';\nimport { AssetManifestLoader } from '../assets/asset-manifest-loader.js';\nimport {\n buildMetadataEnv,\n createTaskNetwork,\n destroyTaskNetwork,\n type TaskNetwork,\n} from './ecs-network.js';\nimport { resolveEcsSecrets, type ResolvedSecret } from './ecs-secrets-resolver.js';\nimport {\n checkVolumeHostPath,\n type ResolvedEcsContainer,\n type ResolvedEcsImage,\n type ResolvedEcsTask,\n type ResolvedEcsVolume,\n} from './ecs-task-resolver.js';\nimport { getEmbedConfig } from './embed-config.js';\n\nconst execFileAsync = promisify(execFile);\n\n/**\n * Top-level orchestrator for `cdkl run-task`. Coordinates image\n * preparation, secret resolution, docker-network bring-up, container\n * boot in `dependsOn` order, log streaming, exit propagation, and\n * teardown. Designed to be called from the CLI with an idempotent\n * `cleanup()` hook hoisted in the caller so SIGINT and the outer finally\n * share teardown semantics with `cdkl invoke`.\n */\n\nexport class EcsTaskRunnerError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'EcsTaskRunnerError';\n Object.setPrototypeOf(this, EcsTaskRunnerError.prototype);\n }\n}\n\nexport interface RunEcsTaskOptions {\n /** `--cluster <name>`. Surfaced to metadata sidecar and the network prefix. */\n cluster: string;\n /** Override container env vars (SAM-style top-level keys are container names; `Parameters` is global). */\n envOverrides?: Record<string, Record<string, string | null> | undefined>;\n /** Host IP to bind published container ports to. Default `127.0.0.1`. */\n containerHost: string;\n /**\n * When true, the runner omits EVERY `-p <hostPort>:<containerPort>`\n * flag from `docker run` (Issue #585). Set by `cdk-local\n * start-service` for multi-replica services: N replicas of one\n * service all map the same container port, so publishing a fixed host\n * port makes the 2nd+ replica fail to boot with `Bind for\n * 127.0.0.1:<port> failed: port is already allocated` — true whether\n * the TaskDefinition declares an explicit `hostPort` or omits it (cdk-local\n * defaults the omitted host port to `containerPort`). Peer comms still\n * works via container IP / network alias on the shared docker network\n * (the production-like path — real ECS Service Connect / awsvpc tasks\n * have per-task ENIs and never share a host port), so dropping the\n * host-port publish is the correct local analogue. Single-replica\n * services leave this unset so `curl localhost:<port>` from the host\n * still works.\n */\n skipHostPortPublish?: boolean;\n /**\n * `--host-port <containerPort>=<hostPort>` overrides (`containerPort ->\n * hostPort`). When a published container port has an entry here, it is\n * bound on that host port instead of the declared one. Lets the user\n * map a privileged container port (e.g. 80) to a non-privileged host\n * port (e.g. 8080) so the run avoids macOS Docker Desktop's privileged\n * helper. Empty / unset = bind the declared host port (`host ==\n * container`).\n */\n hostPortOverrides?: Record<number, number>;\n /**\n * Issue #86 v1 — container ports to publish on docker-assigned EPHEMERAL\n * host ports (`-p <containerHost>::<port>/tcp`), independent of\n * {@link skipHostPortPublish}. Set by `cdkl start-alb` for the ALB-fronted\n * service: each replica publishes its target container port on a unique\n * ephemeral host port (so N replicas never collide) and the local front-door\n * round-robins to `127.0.0.1:<ephemeralPort>`. The caller discovers the\n * assigned host port post-boot via `getPublishedHostPort`. A port listed here\n * is NOT also fixed-published (the declared `-p host:port:port` is suppressed\n * for it) so the ephemeral binding is unambiguous. Empty / unset → no extra\n * publish flags emitted.\n */\n ephemeralPublishContainerPorts?: number[];\n /** Optional STS-issued temp credentials to expose via the metadata sidecar (`--assume-task-role`). */\n taskCredentials?: {\n accessKeyId: string;\n secretAccessKey: string;\n sessionToken?: string;\n };\n /** ARN of the task role being assumed (forwarded to AWS_CONTAINER_CREDENTIALS_RELATIVE_URI). */\n taskRoleArn?: string;\n /** Force a `--platform` (default: inferred from task `RuntimePlatform.CpuArchitecture`). */\n platformOverride?: string;\n /** Skip `docker pull` on every image (sidecar + each container's image). */\n skipPull: boolean;\n /**\n * Optional role ARN to assume before authenticating against ECR for\n * cross-account / centralized registry pulls (#455). Forwarded to\n * `pullEcrImage`'s `ecrRoleArn` option. Same-account / same-region\n * pulls do not need this.\n */\n ecrRoleArn?: string;\n /**\n * The CLI's `--profile`, forwarded to `pullEcrImage` so the ECR auth\n * authenticates as the profile's account. For `--from-cfn-stack` the\n * image lives in the deployed (profile) account; without this the\n * default credential chain authenticates as the wrong account and the\n * pull is denied. Distinct from `ecrRoleArn` (cross-account via\n * AssumeRole) — both can be set, in which case the assumed role wins.\n */\n profile?: string;\n /** Don't `docker rm -f` containers on task exit; useful for `docker exec` post-mortems. */\n keepRunning: boolean;\n /** Start the containers and return without streaming logs. */\n detach: boolean;\n /** AWS region for secret resolution + metadata sidecar. */\n region?: string;\n /**\n * Optional pre-resolved `ImagePlan` map — only used by tests. Production\n * callers leave undefined and let the runner walk every container's\n * Image / docker build / ECR pull path.\n */\n imagePlanByContainer?: Map<string, string>;\n /**\n * Optional second-from-last octet of the link-local /24 subnet for this\n * task's docker network (1..254). Default 170 (AWS-documented). `cdkl start-service` walks this per replica so concurrent replicas\n * don't collide on the same /24. See `buildEndpointSubnet` in\n * `ecs-network.ts`.\n */\n subnetOctet?: number;\n /**\n * Phase 3 of #262 (Issue #460) — extra `--add-host name:ip` flag\n * pairs the docker-runner injects into EVERY user container's\n * `docker run` invocation. Used by the Cloud Map / Service Connect\n * overlay to map `<discoveryName>.<namespace>` (and bare\n * `<discoveryName>` ClientAlias short forms) to the IP of a peer\n * replica's container on the host's docker bridge.\n *\n * **Shape**: flat array of `['--add-host', 'name:ip', '--add-host', 'name2:ip2', ...]`.\n * The runner appends these to `docker run` verbatim — caller is\n * responsible for filtering out self-host (no point adding a\n * replica's own service to its own resolver) and for building the\n * flag pairs in the order they should be evaluated (docker's\n * resolver hits each entry in order; first match wins). Empty /\n * undefined → no extra flags emitted.\n */\n addHostFlags?: ReadonlyArray<string>;\n /**\n * Pre-existing docker network + sidecar to reuse instead of letting\n * the runner create a fresh per-task one. Set by the\n * `cdkl start-service` CLI which creates ONE shared network at\n * the start of the run (per design doc § 5 Option A — peer services\n * can reach each other by IP without docker `network connect`\n * choreography). When this option is supplied, `runEcsTask`:\n * 1. Skips `createTaskNetwork()`.\n * 2. Uses `existingNetwork.networkName` / `sidecarIp` for every\n * container's `--network` and `ECS_CONTAINER_METADATA_URI_V4`\n * env injection.\n * 3. Marks `state.network.ownedByCaller = true` so\n * `cleanupEcsRun()` does NOT teardown the shared lifecycle —\n * only the caller (CLI) tears down once at the end of the run.\n *\n * When undefined, the pre-existing per-task lifecycle applies (one\n * network per task, created + destroyed with the task).\n */\n existingNetwork?: TaskNetwork;\n /**\n * Extra docker `--network-alias <alias>` values to register against\n * specific containers in the task. Keyed by container name so the\n * runner only attaches the aliases to the matching container's\n * `docker run` invocation — Service Connect aliases belong to the\n * container that declared the matching `PortMappings[].Name`, not\n * to every container in the task.\n *\n * Used by `cdkl start-service` Option A (shared docker\n * network) so peers can reach this service by its Service Connect\n * `<discoveryName>` short-form / ClientAlias / fqdn via docker's\n * built-in DNS server — without depending on a separate Cloud Map\n * DNS sidecar. Empty / undefined → no extra aliases emitted\n * (network-aliases default to the container's `--name` only).\n */\n networkAliasesByContainer?: ReadonlyMap<string, ReadonlyArray<string>>;\n /**\n * ECS analogue of the Lambda-container credential fix:\n * synthesized AWS shared credentials file (one INI section under\n * `[<profileName>]`) bind-mounted read-only into EVERY user\n * container, plus `AWS_SHARED_CREDENTIALS_FILE` +\n * `AWS_PROFILE` env-var injection. Lets handlers that call\n * `fromIni({ profile: '<name>' })` explicitly resolve to the same\n * `--profile`-resolved creds the metadata sidecar serves, instead\n * of failing with `CredentialsProviderError: Profile <name> could\n * not be found` inside the container.\n *\n * Set by the CLI ONLY when `--profile` is effective AND\n * `--assume-task-role` is NOT (precedence: assume-task-role >\n * profile-file > sidecar). Caller is responsible for the\n * `writeProfileCredentialsFile()` allocation + `dispose()` cleanup\n * in its single-flight chain — the runner just plumbs the mount\n * and env-vars through `buildDockerRunArgs`.\n */\n profileCredentialsFile?: { hostPath: string; containerPath: string; profileName: string };\n}\n\n/**\n * Single struct that carries everything the orchestrator must tear down,\n * regardless of which step failed. Designed so the caller can hoist a\n * single `cleanup(state)` call in both the outer finally and the SIGINT\n * handler.\n */\nexport interface EcsRunState {\n network: TaskNetwork | undefined;\n /** Resolved docker volume names (`docker volume rm` on teardown). */\n dockerVolumeNames: string[];\n /** Container name → docker id, in start order. */\n startedContainers: { name: string; id: string }[];\n /** Active log streams (stop functions). Drained on teardown. */\n logStoppers: (() => void)[];\n}\n\nexport interface RunEcsTaskResult {\n /** Exit code of the essential container (0 by default when `--keep-running` and no exit awaited). */\n exitCode: number;\n /** Name of the essential container whose exit drove the result. */\n essentialContainerName?: string;\n state: EcsRunState;\n}\n\n/**\n * Build a fresh, empty `EcsRunState`. Surfaces a single allocation point\n * so the CLI's `cleanup()` closure doesn't have to reach into runner\n * internals.\n */\nexport function createEcsRunState(): EcsRunState {\n return { network: undefined, dockerVolumeNames: [], startedContainers: [], logStoppers: [] };\n}\n\n/**\n * Cleanup the resources tracked in `state`. Idempotent and safe to call\n * from both the outer `finally` AND the SIGINT handler. Errors per-step\n * are logged at debug so cleanup never masks a real handler error.\n */\nexport async function cleanupEcsRun(\n state: EcsRunState,\n options: { keepRunning: boolean }\n): Promise<void> {\n const logger = getLogger().child('ecs-runner');\n for (const stop of state.logStoppers) {\n try {\n stop();\n } catch (err) {\n logger.debug(`log stream stop failed: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n state.logStoppers = [];\n\n if (!options.keepRunning) {\n for (const c of state.startedContainers) {\n try {\n await stopContainer(c.id, 10);\n } catch (err) {\n logger.debug(\n `docker stop ${c.id} failed: ${err instanceof Error ? err.message : String(err)}`\n );\n }\n try {\n await removeContainer(c.id);\n } catch (err) {\n logger.debug(\n `docker rm -f ${c.id} failed: ${err instanceof Error ? err.message : String(err)}`\n );\n }\n }\n state.startedContainers = [];\n }\n\n // Sidecar + network teardown runs unconditionally for runner-owned\n // networks (the docs spell out that `--keep-running` only spares\n // user containers — the network + sidecar would otherwise leak\n // across runs). Caller-owned (shared) networks survive per-task\n // cleanup — `cdkl start-service` tears down ONCE at the end\n // of the run after every replica has been cleaned up.\n if (state.network && !state.network.ownedByCaller) {\n await destroyTaskNetwork(state.network);\n }\n state.network = undefined;\n\n for (const v of state.dockerVolumeNames) {\n try {\n await execFileAsync(getDockerCmd(), ['volume', 'rm', v]);\n logger.debug(`Removed docker volume ${v}`);\n } catch (err) {\n logger.debug(\n `docker volume rm ${v} failed: ${err instanceof Error ? err.message : String(err)}`\n );\n }\n }\n state.dockerVolumeNames = [];\n}\n\n/**\n * Top-level entry point. Mutates `state` as it makes progress so the\n * caller's `cleanup(state)` can roll back partial side effects on any\n * thrown error.\n */\nexport async function runEcsTask(\n task: ResolvedEcsTask,\n options: RunEcsTaskOptions,\n state: EcsRunState\n): Promise<RunEcsTaskResult> {\n const logger = getLogger();\n\n if (task.containers.length === 0) {\n throw new EcsTaskRunnerError(\n `Task '${task.taskDefinitionLogicalId}' has no containers — nothing to run.`\n );\n }\n\n for (const w of task.warnings) logger.warn(w);\n\n // Build the dependency DAG up front so cyclic configs fail before we\n // touch docker.\n const dag = buildDependencyGraph(task.containers);\n const startOrder = topoSort(dag, task.containers);\n\n // Resolve every container's image. Production callers leave\n // `imagePlanByContainer` undefined — the resolver below walks the asset\n // manifest / ECR / public-image path per image.\n const imagePlan = options.imagePlanByContainer ?? new Map<string, string>();\n if (!options.imagePlanByContainer) {\n await prepareImages(task, imagePlan, options);\n }\n\n // Resolve every container's secrets in parallel BEFORE network /\n // container boot — any failure short-circuits the whole task. Mirrors\n // the ECS Agent's \"fail-fast on missing secret\" UX.\n const allSecrets: { containerName: string; name: string; valueFrom: string }[] = [];\n for (const c of task.containers) {\n for (const s of c.secrets) {\n allSecrets.push({ containerName: c.name, name: s.name, valueFrom: s.valueFrom });\n }\n }\n const resolvedSecrets = await resolveEcsSecrets(allSecrets, {\n ...(options.region !== undefined && { region: options.region }),\n ...(options.profile !== undefined && { profile: options.profile }),\n });\n const secretsByContainer = groupSecretsByContainer(resolvedSecrets);\n\n // Bring the network + sidecar up. From this point on the cleanup\n // path is non-trivial — any failure must `destroyTaskNetwork(state.network)`\n // for runner-owned networks, but caller-owned (shared) networks survive\n // per-task cleanup.\n if (options.existingNetwork) {\n // Option A (design § 5) — `cdkl start-service` creates one\n // shared network at the start of the run; every replica reuses it.\n // The runner marks the state's network as caller-owned so\n // `cleanupEcsRun()` skips teardown (only the CLI tears down once).\n state.network = { ...options.existingNetwork, ownedByCaller: true };\n } else {\n const netCreateOpts: Parameters<typeof createTaskNetwork>[0] = {\n prefix: options.cluster,\n skipPull: options.skipPull,\n };\n if (options.taskCredentials) netCreateOpts.credentials = options.taskCredentials;\n if (options.cluster) netCreateOpts.cluster = options.cluster;\n if (options.subnetOctet !== undefined) netCreateOpts.subnetOctet = options.subnetOctet;\n state.network = await createTaskNetwork(netCreateOpts);\n }\n\n // Realize docker volumes (per-task `Scope: 'task'` are torn down at\n // cleanup; `Scope: 'shared'` would survive but the docs explicitly\n // pin v1 to per-task semantics).\n const volumeByName = await realizeDockerVolumes(task.volumes, state);\n\n // Pre-compute every container's CMD args so the start loop only does\n // docker calls.\n const dockerCmds = new Map<string, { args: string[]; sensitiveEnv: Record<string, string> }>();\n for (const container of task.containers) {\n const image = imagePlan.get(container.name);\n if (!image) {\n throw new EcsTaskRunnerError(\n `Internal: no resolved image for container '${container.name}'.`\n );\n }\n dockerCmds.set(\n container.name,\n buildDockerRunArgs({\n task,\n container,\n image,\n network: state.network.networkName,\n volumeByName,\n secrets: secretsByContainer.get(container.name) ?? [],\n envOverrides: options.envOverrides,\n containerHost: options.containerHost,\n roleArn: options.taskRoleArn,\n platformOverride: options.platformOverride,\n region: options.region,\n sidecarIp: state.network.sidecarIp,\n ...(options.skipHostPortPublish ? { skipHostPortPublish: true } : {}),\n ...(options.hostPortOverrides ? { hostPortOverrides: options.hostPortOverrides } : {}),\n ...(options.ephemeralPublishContainerPorts &&\n options.ephemeralPublishContainerPorts.length > 0\n ? { ephemeralPublishContainerPorts: options.ephemeralPublishContainerPorts }\n : {}),\n ...(options.addHostFlags && options.addHostFlags.length > 0\n ? { addHostFlags: options.addHostFlags }\n : {}),\n ...((options.networkAliasesByContainer?.get(container.name)?.length ?? 0) > 0\n ? { networkAliases: options.networkAliasesByContainer!.get(container.name)! }\n : {}),\n ...(options.profileCredentialsFile && {\n profileCredentialsFile: options.profileCredentialsFile,\n }),\n })\n );\n }\n\n // Boot containers in dependency order. Each container's `dependsOn`\n // gates its start: START condition needs `docker run` to have\n // returned; COMPLETE / SUCCESS / HEALTHY each wait for the dependency\n // container's lifecycle to reach the matching state. The DAG's\n // `startOrder` is the dependency-respecting topological order; any\n // remaining condition gating fires inside the per-container\n // `awaitDependencies` step.\n const startedByName = new Map<string, { id: string; container: ResolvedEcsContainer }>();\n for (const containerName of startOrder) {\n const container = task.containers.find((c) => c.name === containerName)!;\n await awaitDependencies(container, startedByName);\n\n const { args, sensitiveEnv } = dockerCmds.get(container.name)!;\n logger.info(`Starting container '${container.name}' (image=${imagePlan.get(container.name)})`);\n let id: string;\n try {\n const { stdout } = await execFileAsync(getDockerCmd(), args, {\n maxBuffer: 10 * 1024 * 1024,\n ...execEnvForSecrets(sensitiveEnv),\n });\n id = stdout.trim();\n } catch (err) {\n const e = err as { stderr?: string; message?: string };\n throw new DockerRunnerError(\n `docker run failed for container '${container.name}': ${e.stderr?.trim() || e.message || String(err)}`\n );\n }\n state.startedContainers.push({ name: container.name, id });\n startedByName.set(container.name, { id, container });\n\n if (!options.detach) {\n state.logStoppers.push(streamContainerLogs(container.name, id));\n }\n }\n\n if (options.detach) {\n return { exitCode: 0, state };\n }\n\n // Wait for the essential container to exit. AWS-side ECS treats the\n // first `essential: true` container as the task-driving one; cdk-local\n // mirrors that. When no container declares `essential: false`, every\n // container is essential — we use `containers[0]` as the\n // task-driving one.\n const essential = task.containers.find((c) => c.essential) ?? task.containers[0]!;\n const essentialId = startedByName.get(essential.name)?.id;\n if (!essentialId) {\n throw new EcsTaskRunnerError(`Essential container '${essential.name}' did not start.`);\n }\n const exitCode = await waitForContainerExit(essentialId);\n return { exitCode, essentialContainerName: essential.name, state };\n}\n\n/**\n * Build the directed graph for `dependsOn` ordering. Each container is\n * a node; an edge `A -> B` means A must start AFTER B. graphlib's\n * topological sort returns B before A so the start loop walks the array\n * in correct order. Cyclic graphs are rejected up front with the\n * offending cycle named.\n */\nexport function buildDependencyGraph(containers: ResolvedEcsContainer[]): graphlib.Graph {\n const g = new graphlib.Graph({ directed: true });\n for (const c of containers) g.setNode(c.name);\n for (const c of containers) {\n for (const d of c.dependsOn) {\n g.setEdge(c.name, d.containerName);\n }\n }\n const cycles = graphlib.alg.findCycles(g);\n if (cycles.length > 0) {\n throw new EcsTaskRunnerError(\n `Cyclic DependsOn detected: ${cycles.map((c) => c.join(' -> ')).join('; ')}`\n );\n }\n return g;\n}\n\nexport function topoSort(g: graphlib.Graph, containers: ResolvedEcsContainer[]): string[] {\n // Layered topological sort with template-order tiebreak. Each node's\n // depth = max(depth of nodes it depends on) + 1; nodes with no\n // dependencies are at depth 0. Sorting by (depth, templateIndex)\n // guarantees dependencies are listed before dependents (valid topo\n // order) AND that siblings at the same depth follow the user's\n // template order. The pre-fix double-sort (`topsort.reverse().sort()`)\n // re-ranked globally by template index and could violate topo order\n // when an adversarial template listed a dependent BEFORE its\n // dependency.\n //\n // Edges point dependent -> dependency; a node's depth is one more\n // than the max depth of any node it points to. Cycles are rejected up\n // front in `buildDependencyGraph`, so memoized recursion terminates.\n const depth = new Map<string, number>();\n const computeDepth = (name: string): number => {\n const cached = depth.get(name);\n if (cached !== undefined) return cached;\n let max = -1;\n const successors = g.successors(name) ?? [];\n for (const s of successors) {\n const d = computeDepth(s);\n if (d > max) max = d;\n }\n const result = max + 1;\n depth.set(name, result);\n return result;\n };\n for (const node of g.nodes()) computeDepth(node);\n\n const byPosition = new Map<string, number>();\n containers.forEach((c, idx) => byPosition.set(c.name, idx));\n\n return containers\n .map((c) => c.name)\n .filter((n) => depth.has(n))\n .sort((a, b) => {\n const da = depth.get(a)!;\n const db = depth.get(b)!;\n if (da !== db) return da - db;\n return (byPosition.get(a) ?? 0) - (byPosition.get(b) ?? 0);\n });\n}\n\n/**\n * Await the dependency conditions for one container. Walks the\n * container's `dependsOn` list in order, blocking on each according to\n * its condition. START is a no-op when the dependency is already in\n * `startedByName` (graphlib has already ordered dependencies before\n * dependents).\n */\nasync function awaitDependencies(\n container: ResolvedEcsContainer,\n started: Map<string, { id: string; container: ResolvedEcsContainer }>\n): Promise<void> {\n for (const dep of container.dependsOn) {\n const entry = started.get(dep.containerName);\n if (!entry) {\n throw new EcsTaskRunnerError(\n `Container '${container.name}' depends on '${dep.containerName}' but the latter never started.`\n );\n }\n switch (dep.condition) {\n case 'START':\n // already started — the topological order guarantees this.\n break;\n case 'COMPLETE':\n await waitForContainerExit(entry.id);\n break;\n case 'SUCCESS': {\n const code = await waitForContainerExit(entry.id);\n if (code !== 0) {\n throw new EcsTaskRunnerError(\n `Container '${container.name}' requires dependency '${dep.containerName}' to exit 0, but it exited ${code}.`\n );\n }\n break;\n }\n case 'HEALTHY':\n await waitForContainerHealthy(entry.id, dep.containerName);\n break;\n }\n }\n}\n\n/**\n * Poll `docker inspect --format '{{.State.Health.Status}}'` until the\n * container reports `healthy`, capped at 5 minutes (AWS-side ECS uses\n * the user-declared interval × retries × startPeriod budget but we keep\n * a fixed local cap so a hung healthcheck doesn't block teardown\n * indefinitely).\n */\nasync function waitForContainerHealthy(containerId: string, displayName: string): Promise<void> {\n const logger = getLogger().child('ecs-runner');\n const deadline = Date.now() + 5 * 60 * 1000;\n let lastStatus = '';\n while (Date.now() < deadline) {\n try {\n const { stdout } = await execFileAsync(getDockerCmd(), [\n 'inspect',\n '--format',\n '{{.State.Health.Status}}',\n containerId,\n ]);\n const status = stdout.trim();\n if (status !== lastStatus) {\n logger.debug(`Container '${displayName}' health status: ${status}`);\n lastStatus = status;\n }\n if (status === 'healthy') return;\n if (status === 'unhealthy') {\n throw new EcsTaskRunnerError(\n `Container '${displayName}' health status is 'unhealthy'; aborting before dependents start.`\n );\n }\n } catch (err) {\n if (err instanceof EcsTaskRunnerError) throw err;\n // `docker inspect` may transiently fail right after start; log and retry.\n logger.debug(\n `docker inspect on '${displayName}' failed: ${err instanceof Error ? err.message : String(err)}`\n );\n }\n await sleep(1000);\n }\n throw new EcsTaskRunnerError(\n `Container '${displayName}' did not become healthy within 5 minutes.`\n );\n}\n\nasync function waitForContainerExit(containerId: string): Promise<number> {\n try {\n const { stdout } = await execFileAsync(getDockerCmd(), ['wait', containerId], {\n maxBuffer: 1024 * 1024,\n });\n const code = Number.parseInt(stdout.trim(), 10);\n return Number.isFinite(code) ? code : 1;\n } catch (err) {\n const e = err as { stderr?: string; message?: string };\n throw new DockerRunnerError(\n `docker wait failed: ${e.stderr?.trim() || e.message || String(err)}`\n );\n }\n}\n\nasync function stopContainer(containerId: string, graceSeconds: number): Promise<void> {\n try {\n await execFileAsync(getDockerCmd(), ['stop', '-t', String(graceSeconds), containerId]);\n } catch {\n // Ignore — the subsequent `docker rm -f` covers stuck containers.\n }\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((res) => setTimeout(res, ms));\n}\n\n/**\n * Stream `docker logs -f <id>` with `[<container-name>]` prefixes on\n * every line. Returns a stop function for the caller's `finally`.\n */\nfunction streamContainerLogs(containerName: string, containerId: string): () => void {\n const proc = spawn(getDockerCmd(), ['logs', '-f', containerId], {\n stdio: ['ignore', 'pipe', 'pipe'],\n });\n const prefix = `[${containerName}] `;\n let stdoutBuf = '';\n let stderrBuf = '';\n proc.stdout?.on('data', (chunk: Buffer) => {\n stdoutBuf = writePrefixed(prefix, stdoutBuf + chunk.toString('utf-8'), process.stdout);\n });\n proc.stderr?.on('data', (chunk: Buffer) => {\n stderrBuf = writePrefixed(prefix, stderrBuf + chunk.toString('utf-8'), process.stderr);\n });\n proc.on('error', () => {\n /* surfaced through the parent's docker-wait result */\n });\n return () => {\n if (stdoutBuf) process.stdout.write(prefix + stdoutBuf + '\\n');\n if (stderrBuf) process.stderr.write(prefix + stderrBuf + '\\n');\n if (!proc.killed) proc.kill('SIGTERM');\n };\n}\n\nfunction writePrefixed(prefix: string, buffer: string, out: NodeJS.WritableStream): string {\n const lines = buffer.split('\\n');\n const remainder = lines.pop() ?? '';\n for (const line of lines) {\n out.write(prefix + line + '\\n');\n }\n return remainder;\n}\n\n/**\n * Resolve every container's `Image` to a tag the runner can pass to\n * `docker run`. The map is keyed by container name; entries are\n * populated in parallel up to the asset-manifest bound (single\n * `docker build` for shared assets is left to docker's own cache).\n */\nasync function prepareImages(\n task: ResolvedEcsTask,\n out: Map<string, string>,\n options: RunEcsTaskOptions\n): Promise<void> {\n const logger = getLogger().child('ecs-runner');\n // Sequential is fine — most tasks have 1–3 containers and each\n // `docker build` / pull would saturate IO anyway.\n for (const container of task.containers) {\n const image = await prepareOneImage(task, container, options);\n out.set(container.name, image);\n logger.debug(`Container '${container.name}' image=${image}`);\n }\n}\n\nasync function prepareOneImage(\n task: ResolvedEcsTask,\n container: ResolvedEcsContainer,\n options: RunEcsTaskOptions\n): Promise<string> {\n const image: ResolvedEcsImage = container.image;\n switch (image.kind) {\n case 'public': {\n await pullImage(image.uri, options.skipPull);\n return image.uri;\n }\n case 'ecr': {\n return pullEcrImage(image.uri, {\n skipPull: options.skipPull,\n ...(options.region !== undefined && { region: options.region }),\n ...(options.ecrRoleArn !== undefined && { ecrRoleArn: options.ecrRoleArn }),\n ...(options.profile !== undefined && { profile: options.profile }),\n });\n }\n case 'cdk-asset': {\n const cdkOutDir = task.stack.assetManifestPath\n ? dirname(task.stack.assetManifestPath)\n : undefined;\n if (!cdkOutDir) {\n throw new EcsTaskRunnerError(\n `Container '${container.name}' uses a CDK asset image but the stack has no asset manifest. ` +\n 'Re-synthesize the app (without `--output <stale-dir>`) and retry.'\n );\n }\n const loader = new AssetManifestLoader();\n const manifest = await loader.loadManifest(cdkOutDir, task.stack.stackName);\n if (!manifest) {\n throw new EcsTaskRunnerError(\n `No asset manifest at ${cdkOutDir} for stack ${task.stack.stackName}.`\n );\n }\n const dockerImages = manifest.dockerImages ?? {};\n const entries = Object.entries(dockerImages);\n let asset: { source: import('../types/assets.js').DockerImageAssetSource } | undefined;\n if (image.assetHash && dockerImages[image.assetHash]) {\n asset = dockerImages[image.assetHash];\n } else if (entries.length === 1) {\n asset = entries[0]![1];\n }\n if (!asset) {\n throw new EcsTaskRunnerError(\n `Container '${container.name}' references a CDK asset image but no matching entry was found in cdk.out. ` +\n 'Re-synthesize the CDK app and retry.'\n );\n }\n const tag = `${getEmbedConfig().resourceNamePrefix}-run-task-${(image.assetHash ?? 'single').slice(0, 16)}`;\n const actualTag = await buildDockerImage(asset, cdkOutDir, {\n tag,\n ...(options.platformOverride !== undefined && { platform: options.platformOverride }),\n wrapError: (stderr: string) =>\n new LocalInvokeBuildError(\n `docker build failed for ECS container '${container.name}' (${asset.source.directory ?? asset.source.executable?.join(' ')}): ${stderr}`\n ),\n });\n if (actualTag !== tag) {\n // `executable` source mode returns the script's own tag — re-tag\n // to the deterministic `tag` so the downstream `docker run` finds\n // the image under the expected name. Routed through the shared\n // `runDockerStreaming` helper for consistency with publisher /\n // local-invoke.\n try {\n await runDockerStreaming(['tag', actualTag, tag]);\n } catch (err) {\n const e = err as { stderr?: string; message?: string };\n throw new LocalInvokeBuildError(\n `docker tag failed re-tagging '${actualTag}' → '${tag}' for ECS container '${container.name}': ${e.stderr?.trim() || e.message || String(err)}`\n );\n }\n }\n return tag;\n }\n }\n}\n\n/**\n * `docker volume create` for every `DockerVolumeConfiguration` entry.\n * Anonymous + host-path volumes need no create call — they're realized\n * at `docker run` time via `-v <hostPath>:<containerPath>`.\n */\nasync function realizeDockerVolumes(\n volumes: ResolvedEcsVolume[],\n state: EcsRunState\n): Promise<Map<string, ResolvedEcsVolume & { dockerVolumeName?: string }>> {\n const logger = getLogger().child('ecs-runner');\n const out = new Map<string, ResolvedEcsVolume & { dockerVolumeName?: string }>();\n for (const v of volumes) {\n if (v.kind === 'host') {\n if (v.hostPath && !checkVolumeHostPath(v.hostPath)) {\n logger.warn(\n `Volume '${v.name}': host path '${v.hostPath}' does not exist or is not a directory. ` +\n 'Docker will create an anonymous bind mount; create the host path before run-task if you expected to bind-mount it.'\n );\n }\n out.set(v.name, v);\n continue;\n }\n const cfg = v.dockerVolumeConfig;\n const args: string[] = ['volume', 'create'];\n if (cfg?.driver) args.push('--driver', cfg.driver);\n if (cfg?.driverOpts) {\n for (const [k, val] of Object.entries(cfg.driverOpts)) args.push('--opt', `${k}=${val}`);\n }\n if (cfg?.labels) {\n for (const [k, val] of Object.entries(cfg.labels)) args.push('--label', `${k}=${val}`);\n }\n const dockerVolumeName = `${getEmbedConfig().resourceNamePrefix}-${v.name}-${randHex(4)}`;\n args.push(dockerVolumeName);\n try {\n await execFileAsync(getDockerCmd(), args);\n state.dockerVolumeNames.push(dockerVolumeName);\n logger.debug(`Created docker volume ${dockerVolumeName} for task volume '${v.name}'`);\n } catch (err) {\n const e = err as { stderr?: string; message?: string };\n throw new DockerRunnerError(\n `docker volume create failed for '${v.name}': ${e.stderr?.trim() || e.message || String(err)}`\n );\n }\n out.set(v.name, { ...v, dockerVolumeName });\n }\n return out;\n}\n\nfunction randHex(bytes: number): string {\n return randomBytes(bytes).toString('hex');\n}\n\nfunction groupSecretsByContainer(\n resolved: ResolvedSecret[]\n): Map<string, { name: string; value: string }[]> {\n const out = new Map<string, { name: string; value: string }[]>();\n for (const r of resolved) {\n const arr = out.get(r.containerName) ?? [];\n arr.push({ name: r.name, value: r.value });\n out.set(r.containerName, arr);\n }\n return out;\n}\n\ninterface BuildDockerRunArgs {\n task: ResolvedEcsTask;\n container: ResolvedEcsContainer;\n image: string;\n network: string;\n volumeByName: Map<string, ResolvedEcsVolume & { dockerVolumeName?: string }>;\n secrets: { name: string; value: string }[];\n envOverrides: Record<string, Record<string, string | null> | undefined> | undefined;\n containerHost: string;\n roleArn: string | undefined;\n platformOverride: string | undefined;\n region: string | undefined;\n /**\n * Optional sidecar IP for the metadata-endpoints sidecar on this\n * task's docker network. Defaults to `169.254.170.2`; `cdk-local\n * start-service` overrides per replica so each replica's containers\n * point at their own sidecar instance.\n */\n sidecarIp?: string;\n /**\n * Issue #585 — when true, omit EVERY `-p <hostPort>:<containerPort>`\n * flag. Set by `cdkl start-service` for multi-replica services\n * so the 2nd+ replica does not collide on a shared host port (see the\n * matching field on {@link RunEcsTaskOptions} for the full rationale).\n */\n skipHostPortPublish?: boolean;\n /**\n * `--host-port` overrides (`containerPort -> hostPort`); see the\n * matching field on {@link RunEcsTaskOptions}.\n */\n hostPortOverrides?: Record<number, number>;\n /**\n * Issue #86 v1 — container ports to publish on EPHEMERAL host ports, used by\n * the local ALB front-door. A port is published (as\n * `-p <containerHost>::<port>/<proto>`) only when THIS container declares a\n * matching `portMappings` entry, so the flag lands on the target container\n * even when {@link skipHostPortPublish} dropped the declared mappings. See\n * the matching field on {@link RunEcsTaskOptions}.\n */\n ephemeralPublishContainerPorts?: number[];\n /**\n * Issue #460 — extra `--add-host name:ip` flag pairs forwarded\n * verbatim to `docker run`. Used by the Cloud Map overlay so\n * `<discoveryName>.<namespace>` (and bare ClientAlias short forms)\n * resolve inside this container. Caller is responsible for the\n * flag-pair shape (`['--add-host', 'name:ip', ...]`).\n */\n addHostFlags?: ReadonlyArray<string>;\n /**\n * Issue #460 / design § 5 Option A — extra `--network-alias <alias>`\n * values the runner adds to the `docker run` invocation. Used by\n * `cdkl start-service` shared-network mode so peers can reach\n * this container by its Service Connect discoveryName / ClientAlias\n * / fqdn via docker's built-in DNS server. The container's\n * `--name`-derived alias is ALWAYS added (line 813); these are\n * additional aliases registered alongside it.\n */\n networkAliases?: ReadonlyArray<string>;\n /**\n * ECS analogue of the Lambda-container credential fix — bind-mounts\n * a host-side AWS shared credentials file (one `[<profileName>]`\n * INI section) read-only into the container and sets\n * `AWS_SHARED_CREDENTIALS_FILE` + `AWS_PROFILE` env vars so handler\n * code calling `fromIni({ profile })` resolves to the same creds\n * the metadata sidecar serves. When undefined (the default), no\n * mount / env vars are emitted — `--profile` is forwarded to the\n * sidecar only.\n */\n profileCredentialsFile?: { hostPath: string; containerPath: string; profileName: string };\n}\n\n/**\n * Parse `--host-port <containerPort>=<hostPort>` overrides into a\n * `containerPort -> hostPort` map.\n *\n * By default cdk-local publishes a container port on the SAME host port\n * (`host == container`), which is predictable but fails on macOS for\n * privileged ports (< 1024): Docker Desktop binds those through the\n * `com.docker.vmnetd` privileged helper, which prompts for an admin\n * password and fails when cancelled. Rather than silently changing the\n * host port, the user opts in explicitly — e.g. `--host-port 80=8080`\n * publishes the container's port 80 on host port 8080.\n *\n * Repeatable; each value is `<containerPort>=<hostPort>` with both in\n * 1-65535. Throws on a malformed value so the CLI surfaces a clear error.\n */\nexport function parseHostPortOverrides(values: string[] | undefined): Record<number, number> {\n const out: Record<number, number> = {};\n for (const raw of values ?? []) {\n const m = /^(\\d+)=(\\d+)$/.exec(raw.trim());\n if (!m) {\n throw new Error(\n `Invalid --host-port '${raw}'. Expected <containerPort>=<hostPort> (e.g. 80=8080).`\n );\n }\n const containerPort = Number(m[1]);\n const hostPort = Number(m[2]);\n for (const [label, p] of [\n ['container', containerPort],\n ['host', hostPort],\n ] as const) {\n if (p < 1 || p > 65535) {\n throw new Error(`Invalid --host-port '${raw}': ${label} port must be 1-65535.`);\n }\n }\n out[containerPort] = hostPort;\n }\n return out;\n}\n\n/**\n * Build the full `docker run -d` argument list for one container.\n * Exported (no-leading-underscore) so the unit tests can assert against\n * the shape directly without spawning a process.\n */\nexport function buildDockerRunArgs(opts: BuildDockerRunArgs): {\n args: string[];\n sensitiveEnv: Record<string, string>;\n} {\n const { task, container, image, network, volumeByName, secrets, containerHost, roleArn } = opts;\n const args: string[] = ['run', '-d'];\n\n // Stable name so siblings can reach this container via DNS.\n args.push(\n '--name',\n `${getEmbedConfig().resourceNamePrefix}-${task.family}-${container.name}-${randHex(3)}`\n );\n args.push('--network', network);\n args.push('--network-alias', container.name);\n\n // Issue #460 / design § 5 Option A — extra `--network-alias` for\n // every Service Connect discoveryName / ClientAlias short-form /\n // fqdn this container should be reachable as. With a shared docker\n // network across all services in the CLI run, peers resolve any\n // alias via docker's built-in DNS server — no extra DNS sidecar\n // needed. De-duplicated against the `container.name` alias above.\n if (opts.networkAliases && opts.networkAliases.length > 0) {\n const seen = new Set<string>([container.name]);\n for (const a of opts.networkAliases) {\n if (!seen.has(a)) {\n args.push('--network-alias', a);\n seen.add(a);\n }\n }\n }\n\n // Issue #460 — Cloud Map / Service Connect overlay. The\n // `--add-host` flags here come from the in-process CloudMapRegistry\n // populated by the service runner after each peer replica boots.\n // Multi-replica routing is approximated as \"first registered\n // endpoint per fqdn\" — full multi-instance DNS rotation requires\n // the deferred DNS-sidecar option (§6 of the design).\n if (opts.addHostFlags && opts.addHostFlags.length > 0) {\n for (const f of opts.addHostFlags) args.push(f);\n }\n\n if (opts.platformOverride) {\n args.push('--platform', opts.platformOverride);\n } else if (task.runtimePlatform) {\n args.push(\n '--platform',\n task.runtimePlatform.cpuArchitecture === 'ARM64' ? 'linux/arm64' : 'linux/amd64'\n );\n }\n\n // Issue #585 — multi-replica services skip the host-port publish.\n // N replicas of one service all map the same container port; binding\n // a fixed host port makes the 2nd+ replica fail with `port is already\n // allocated`. Peer comms still works via container IP / network alias\n // on the shared docker network (production-like — real ECS Service\n // Connect tasks have per-task ENIs and never share a host port).\n // Ports that are published ephemerally for the front-door (below) must NOT\n // also get the fixed declared publish — otherwise a single-replica ALB\n // service would bind the same container port TWICE (`-p host:80:80` AND\n // `-p host::80`), and `getPublishedHostPort` could read the fixed binding\n // instead of the ephemeral one. The ephemeral publish is the front-door's\n // sole binding for these ports.\n const ephemeralPorts = new Set(opts.ephemeralPublishContainerPorts ?? []);\n if (!opts.skipHostPortPublish) {\n for (const pm of container.portMappings) {\n if (ephemeralPorts.has(pm.containerPort)) continue;\n const declaredHostPort = pm.hostPort ?? pm.containerPort;\n const hostPort = opts.hostPortOverrides?.[pm.containerPort] ?? declaredHostPort;\n const overrideNote = hostPort !== declaredHostPort ? ' (--host-port override)' : '';\n getLogger()\n .child('ecs')\n .info(\n `Container '${container.name}' container port ${pm.containerPort} published on ` +\n `${containerHost}:${hostPort}${overrideNote}. Reach it at ${containerHost}:${hostPort}.`\n );\n args.push('-p', `${containerHost}:${hostPort}:${pm.containerPort}/${pm.protocol}`);\n }\n }\n\n // Issue #86 v1 — ALB front-door ephemeral publish. For each requested\n // target container port that THIS container actually declares, publish it on\n // a docker-assigned ephemeral host port (`-p <host>::<port>/<proto>`),\n // independent of skipHostPortPublish. N replicas can each publish the same\n // container port without colliding because the host port is unallocated; the\n // service runner discovers the assigned port via `getPublishedHostPort` and\n // round-robins the front-door to it.\n if (ephemeralPorts.size > 0) {\n const alreadyEphemeral = new Set<number>();\n for (const pm of container.portMappings) {\n if (!ephemeralPorts.has(pm.containerPort) || alreadyEphemeral.has(pm.containerPort)) continue;\n alreadyEphemeral.add(pm.containerPort);\n args.push('-p', `${containerHost}::${pm.containerPort}/${pm.protocol}`);\n }\n }\n\n // Bind-mount the host-side profile credentials file read-only at the\n // fixed in-container path so `fromIni({\n // profile })` handlers resolve to the same creds the sidecar\n // serves. The `:ro` flag is load-bearing: a compromised handler\n // must not be able to tamper with the host-side temp file. Set\n // BEFORE the user's own `MountPoints` so a (malformed) user mount\n // that targets `/cdk-local-aws/credentials` doesn't shadow the\n // creds file — docker honors mount-order by container path\n // uniqueness; a later mount at the same target would fail or shadow.\n if (opts.profileCredentialsFile) {\n args.push(\n '-v',\n `${opts.profileCredentialsFile.hostPath}:${opts.profileCredentialsFile.containerPath}:ro`\n );\n }\n\n // Mounts: walk the container's `MountPoints` and look up the matching\n // volume to decide bind-mount vs docker volume.\n for (const mp of container.mountPoints) {\n const v = volumeByName.get(mp.sourceVolume);\n if (!v) continue;\n if (v.kind === 'host') {\n if (v.hostPath) {\n const ro = mp.readOnly ? ':ro' : '';\n args.push('-v', `${v.hostPath}:${mp.containerPath}${ro}`);\n } else {\n // Anonymous: only the container path, docker manages the volume.\n args.push('-v', mp.containerPath);\n }\n } else {\n const name = v.dockerVolumeName ?? v.name;\n const ro = mp.readOnly ? ':ro' : '';\n args.push('-v', `${name}:${mp.containerPath}${ro}`);\n }\n }\n\n // Env precedence (highest wins):\n // 1. function-specific `--env-vars` entry\n // 2. global `Parameters` `--env-vars` entry\n // 3. resolved secrets\n // 4. template literal env\n // 5. profile credentials file env (AWS_SHARED_CREDENTIALS_FILE / AWS_PROFILE)\n // 6. metadata sidecar env (sidecar URL / role URL)\n const finalEnv: Record<string, string> = {};\n const metaEnv = buildMetadataEnv({\n containerName: container.name,\n ...(roleArn !== undefined && { roleArn }),\n ...(opts.region !== undefined && { region: opts.region }),\n ...(opts.sidecarIp !== undefined && { sidecarIp: opts.sidecarIp }),\n });\n Object.assign(finalEnv, metaEnv);\n // Point the container's SDK chain at the bind-mounted credentials file\n // so `fromIni({ profile })` calls inside\n // the handler resolve to the same creds. `AWS_PROFILE` makes\n // `fromIni()` (no explicit arg) ALSO use this profile. Sits ABOVE\n // template / secret / --env-vars overrides so a user template that\n // sets its own `AWS_PROFILE` (e.g. for a different in-container\n // chain) still wins.\n if (opts.profileCredentialsFile) {\n finalEnv['AWS_SHARED_CREDENTIALS_FILE'] = opts.profileCredentialsFile.containerPath;\n finalEnv['AWS_PROFILE'] = opts.profileCredentialsFile.profileName;\n }\n Object.assign(finalEnv, container.environment);\n for (const s of secrets) finalEnv[s.name] = s.value;\n\n const overrides = opts.envOverrides;\n if (overrides) {\n applyOverrideMap(finalEnv, overrides['Parameters']);\n applyOverrideMap(finalEnv, overrides[container.name]);\n }\n\n // Resolved secret values (and any AWS credentials that landed in the\n // env) route through docker's value-from-process-env form (`-e KEY`,\n // value supplied via the spawned docker process's env) so they never\n // appear in `docker run`'s argv. Non-secret config keeps `-e KEY=VALUE`.\n const sensitiveEnvKeys = new Set<string>(SENSITIVE_ENV_KEYS);\n for (const s of secrets) sensitiveEnvKeys.add(s.name);\n // Env keys that resolved to a decrypted SecureString SSM parameter\n // (issue #99) — same off-argv treatment as secrets.\n for (const k of container.sensitiveEnvKeys) sensitiveEnvKeys.add(k);\n const sensitiveEnv = appendEnvFlags(args, finalEnv, sensitiveEnvKeys);\n\n if (container.user) args.push('--user', container.user);\n if (container.privileged) args.push('--privileged');\n if (container.readonlyRootFilesystem) args.push('--read-only');\n if (container.workingDirectory) args.push('--workdir', container.workingDirectory);\n for (const u of container.ulimits) {\n args.push('--ulimit', `${u.name}=${u.softLimit}:${u.hardLimit}`);\n }\n for (const link of container.links) args.push('--link', link);\n\n if (container.healthCheck) {\n args.push('--health-cmd', shellJoin(container.healthCheck.command));\n if (container.healthCheck.interval !== undefined) {\n args.push('--health-interval', `${container.healthCheck.interval}s`);\n }\n if (container.healthCheck.timeout !== undefined) {\n args.push('--health-timeout', `${container.healthCheck.timeout}s`);\n }\n if (container.healthCheck.retries !== undefined) {\n args.push('--health-retries', String(container.healthCheck.retries));\n }\n if (container.healthCheck.startPeriod !== undefined) {\n args.push('--health-start-period', `${container.healthCheck.startPeriod}s`);\n }\n }\n\n // EntryPoint maps the same way as docker — first item to --entrypoint,\n // the rest become positional args before CMD.\n let entryPointTail: string[] = [];\n if (container.entryPoint && container.entryPoint.length > 0) {\n args.push('--entrypoint', container.entryPoint[0]!);\n entryPointTail = container.entryPoint.slice(1);\n }\n\n args.push(image, ...entryPointTail, ...(container.command ?? []));\n return { args, sensitiveEnv };\n}\n\nfunction applyOverrideMap(\n acc: Record<string, string>,\n map: Record<string, string | null> | undefined\n): void {\n if (!map) return;\n for (const [k, v] of Object.entries(map)) {\n if (v === null) delete acc[k];\n else if (typeof v === 'string' || typeof v === 'number' || typeof v === 'boolean') {\n acc[k] = String(v);\n }\n }\n}\n\n/**\n * Quote each arg for `docker --health-cmd`. Docker's healthcheck takes\n * a single string which is passed to `/bin/sh -c`, so multi-word commands\n * need to be space-joined. We escape single quotes / `$` characters to\n * avoid shell injection from CFn-supplied values.\n */\nfunction shellJoin(parts: string[]): string {\n return parts\n .map((p) => {\n if (/^[A-Za-z0-9_\\-./=:]+$/.test(p)) return p;\n return `'${p.replace(/'/g, \"'\\\\''\")}'`;\n })\n .join(' ');\n}\n","import { readFileSync } from 'node:fs';\nimport { Command, Option } from 'commander';\nimport {\n appOptions,\n commonOptions,\n contextOptions,\n deprecatedRegionOption,\n parseContextOptions,\n warnIfDeprecatedRegion,\n} from '../options.js';\nimport { getLogger } from '../../utils/logger.js';\nimport { applyRoleArnIfSet } from '../../utils/role-arn.js';\nimport { CdkLocalError, withErrorHandling } from '../../utils/error-handler.js';\nimport { listTargets } from '../../local/target-lister.js';\nimport { resolveSingleTarget } from '../../local/target-picker.js';\nimport { Synthesizer, type SynthesisOptions } from '../../synthesis/synthesizer.js';\nimport { resolveApp } from '../config-loader.js';\nimport { ensureDockerAvailable } from '../../local/docker-runner.js';\nimport { resolveProfileCredentials } from './local-start-api.js';\nimport {\n writeProfileCredentialsFile,\n type ProfileCredentialsFile,\n} from './local-profile-credentials-file.js';\nimport {\n applyCrossStackResolverToTask,\n derivePartitionAndUrlSuffix,\n detectEcsImageResolutionNeeds,\n parseEcsTarget,\n resolveEcsTaskTarget,\n TASK_ROLE_ACCOUNT_PLACEHOLDER,\n type EcsImageResolutionContext,\n} from '../../local/ecs-task-resolver.js';\nimport type { StackInfo } from '../../synthesis/assembly-reader.js';\nimport {\n cleanupEcsRun,\n createEcsRunState,\n parseHostPortOverrides,\n runEcsTask,\n type EcsRunState,\n type RunEcsTaskOptions,\n} from '../../local/ecs-task-runner.js';\nimport { matchStacks } from '../stack-matcher.js';\nimport {\n createLocalStateProvider,\n resolveCfnFallbackRegion,\n type ExtraStateProviders,\n} from './local-state-source.js';\nimport {\n getEmbedConfig,\n setEmbedConfig,\n type CdkLocalEmbedConfig,\n} from '../../local/embed-config.js';\nimport type { LocalStateProvider } from '../../local/local-state-provider.js';\nimport type { SubstitutionContext } from '../../local/state-resolver.js';\n\ninterface LocalRunTaskOptions {\n app?: string;\n output: string;\n verbose: boolean;\n region?: string;\n profile?: string;\n roleArn?: string;\n context?: string[];\n cluster: string;\n envVars?: string;\n containerHost: string;\n /**\n * Commander's `[<arg>]` syntax maps to `string | boolean` here:\n * - flag absent → `undefined`\n * - `--assume-task-role` (bare) → `true`\n * - `--assume-task-role <arn>` → `'<arn>'`\n * The runner branches on `typeof options.assumeTaskRole`.\n */\n assumeTaskRole?: string | boolean;\n pull: boolean;\n /**\n * Optional role ARN passed to `pullEcrImage` for cross-account /\n * centralized registry pulls. Issues `sts:AssumeRole` via the\n * default credential chain and uses the resulting temp credentials to\n * authenticate against the target ECR repository.\n */\n ecrRoleArn?: string;\n /** `--host-port <containerPort=hostPort>` overrides (repeatable; variadic array). */\n hostPort?: string[];\n platform?: string;\n keepRunning: boolean;\n detach: boolean;\n /**\n * Issue #606: alternative state source. Reads physical IDs from a\n * deployed CloudFormation stack via `ListStackResources`.\n */\n fromCfnStack?: string | boolean;\n /**\n * Region of the state record to read. Used as the CFn client region\n * for `--from-cfn-stack`.\n */\n stackRegion?: string;\n /** Host-injected extra state-source flag fields. */\n [key: string]: unknown;\n}\n\n/**\n * Factory options for {@link createLocalRunTaskCommand}. Hosts embedding\n * cdk-local can supply additional state-source factories via\n * `extraStateProviders` (forwarded to every `createLocalStateProvider`\n * call inside the command).\n */\nexport interface CreateLocalRunTaskCommandOptions {\n extraStateProviders?: ExtraStateProviders;\n /** Embed-time branding overrides for a host wrapping this factory. */\n embedConfig?: CdkLocalEmbedConfig;\n}\n\n/**\n * `cdkl run-task <target>` — Phase 1 of the ECS local-execution\n * trilogy. Synthesizes the CDK app, locates the target\n * `AWS::ECS::TaskDefinition`, stands up a per-task docker network with\n * the AWS-published `amazon-ecs-local-container-endpoints` sidecar, and\n * starts every container in `dependsOn` order. The essential\n * container's exit code drives the CLI's exit.\n */\nasync function localRunTaskCommand(\n target: string | undefined,\n options: LocalRunTaskOptions,\n extraStateProviders: ExtraStateProviders | undefined\n): Promise<void> {\n const logger = getLogger();\n if (options.verbose) logger.setLevel('debug');\n\n warnIfDeprecatedRegion(options);\n\n const state: EcsRunState = createEcsRunState();\n let sigintHandler: (() => void) | undefined;\n let sigintCount = 0;\n // The active state provider (--from-cfn-stack or a host-injected\n // extra). Hoisted so the outer `finally` can dispose it even if the\n // body throws between provider creation and the normal exit path.\n let stateProvider: LocalStateProvider | undefined;\n // ECS analogue of the Lambda-container credential fix: synthesized AWS\n // shared credentials file (one INI section) bind-mounted into every\n // user container so\n // handlers using `fromIni({ profile })` resolve to the same creds.\n // Disposed in the cleanup chain below.\n let profileCredsFile: ProfileCredentialsFile | undefined;\n\n // Single-flight cleanup: the SIGINT handler AND the outer `finally` both\n // call this, so we await the first invocation's promise on every later\n // call rather than running concurrently against the shared mutable\n // `state` arrays (which would otherwise double-`docker rm -f` containers\n // and corrupt the entries map mid-iteration).\n let cleanupPromise: Promise<void> | undefined;\n const cleanup = async (): Promise<void> => {\n if (!cleanupPromise) {\n cleanupPromise = (async () => {\n try {\n await cleanupEcsRun(state, { keepRunning: options.keepRunning });\n } catch (err) {\n getLogger().debug(`cleanup failed: ${err instanceof Error ? err.message : String(err)}`);\n }\n if (profileCredsFile) {\n try {\n await profileCredsFile.dispose();\n } catch (err) {\n getLogger().debug(\n `Failed to remove profile credentials tmpdir ${profileCredsFile.hostPath}: ${\n err instanceof Error ? err.message : String(err)\n }`\n );\n }\n }\n })();\n }\n await cleanupPromise;\n };\n\n try {\n await applyRoleArnIfSet({ roleArn: options.roleArn, region: options.region });\n await ensureDockerAvailable();\n\n const appCmd = resolveApp(options.app);\n if (!appCmd) {\n throw new Error(\n `No CDK app specified. Pass --app, set ${getEmbedConfig().envPrefix}_APP, or add \"app\" to cdk.json.`\n );\n }\n\n logger.info('Synthesizing CDK app...');\n const synthesizer = new Synthesizer();\n const context = parseContextOptions(options.context);\n const synthOpts: SynthesisOptions = {\n app: appCmd,\n output: options.output,\n ...(options.region && { region: options.region }),\n ...(options.profile && { profile: options.profile }),\n ...(Object.keys(context).length > 0 && { context }),\n };\n const { stacks } = await synthesizer.synthesize(synthOpts);\n\n const resolvedTarget = await resolveSingleTarget(target, {\n entries: listTargets(stacks).ecsTaskDefinitions,\n message: 'Select an ECS task definition to run',\n noun: 'ECS task definitions',\n onMissing: () =>\n new CdkLocalError(\n `${getEmbedConfig().cliName} run-task requires a <target> (an ECS task definition display path or logical ID). ` +\n `Run \\`${getEmbedConfig().cliName} list\\` to see them, or run it in a TTY to pick interactively.`,\n 'LOCAL_RUN_TASK_TARGET_REQUIRED'\n ),\n });\n\n // Pick a LocalStateProvider for whichever flag the user passed\n // (--from-cfn-stack OR a host-injected extra). Constructed BEFORE the\n // candidate-stack picker so the same provider drives both the\n // image-context state-load AND the post-pass cross-stack resolver.\n const parsed = parseEcsTarget(resolvedTarget);\n const candidate = pickCandidateStack(parsed.stackPattern, stacks);\n stateProvider = createLocalStateProvider(\n options,\n candidate?.stackName ?? '',\n await resolveCfnFallbackRegion(options, candidate?.region),\n extraStateProviders\n );\n\n // Build the optional substitution context BEFORE resolving the\n // target, so `Fn::Sub`-shaped ECR image URIs (pseudo parameters +\n // same-stack ECR Repository refs) get rewritten in-place during\n // `parseContainerImage`. STS / state-load are lazy — we only fire\n // them when at least one stack's template references the\n // placeholders.\n const imageContext = await buildEcsImageResolutionContext(candidate, stateProvider, options);\n const task = resolveEcsTaskTarget(resolvedTarget, stacks, imageContext);\n logger.info(\n `Target: ${task.stack.stackName}/${task.taskDefinitionLogicalId} (family=${task.family}, containers=${task.containers.length})`\n );\n\n // Cross-stack `Fn::ImportValue` / `Fn::GetStackOutput` resolution in\n // env vars / secrets. The sync `parseContainerDefinition` pass\n // dropped these with a warn-and-drop entry; the async post-pass\n // re-attempts them via the active state provider when the template\n // actually references a cross-stack output.\n const taskStack = stacks.find((s) => s.stackName === task.stack.stackName) ?? task.stack;\n const taskNeeds = detectEcsImageResolutionNeeds(taskStack);\n if (stateProvider && taskNeeds.needsCrossStackResolver) {\n const consumerRegion =\n options.region ??\n process.env['AWS_REGION'] ??\n process.env['AWS_DEFAULT_REGION'] ??\n task.stack.region ??\n 'us-east-1';\n const resolver = await stateProvider.buildCrossStackResolver(consumerRegion);\n if (resolver) {\n const subContext: SubstitutionContext = {\n resources: imageContext?.stateResources ?? {},\n ...(imageContext?.pseudoParameters && {\n pseudoParameters: imageContext.pseudoParameters,\n }),\n ...(imageContext?.stateParameters && {\n parameters: imageContext.stateParameters,\n }),\n ...(imageContext?.stateSensitiveParameters?.length && {\n sensitiveParameters: new Set(imageContext.stateSensitiveParameters),\n }),\n consumerRegion,\n crossStackResolver: resolver,\n };\n await applyCrossStackResolverToTask(task, subContext);\n }\n } else if (!stateProvider && taskNeeds.needsCrossStackResolver) {\n logger.warn(\n 'Container Environment / Secrets entries contain Fn::ImportValue / Fn::GetStackOutput intrinsics. ' +\n 'Pass a state-source flag (e.g. --from-cfn-stack or a host-provided extension) to substitute them against deployed state.'\n );\n }\n\n // Double-^C exits 130 immediately.\n sigintHandler = (): void => {\n sigintCount += 1;\n if (sigintCount >= 2) {\n process.stderr.write('Force-exit on second ^C; container cleanup skipped.\\n');\n process.exit(130);\n }\n logger.info('Stopping task...');\n void cleanup().then(() => process.exit(130));\n };\n process.on('SIGINT', sigintHandler);\n\n // `--assume-task-role` branches: bare flag (boolean `true`) uses the\n // task definition's resolved `TaskRoleArn`; otherwise the\n // user-supplied ARN is used. The resolver emits a synth-time\n // placeholder ARN (`arn:aws:iam::${AWS::AccountId}:role/<LogicalId>`)\n // when TaskRoleArn references an inline same-stack IAM Role; we fill\n // in the account segment lazily via STS only when bare\n // `--assume-task-role` is set, so the STS round-trip does not fire\n // on the common pass-through path.\n let assumedCredentials: RunEcsTaskOptions['taskCredentials'];\n let resolvedRoleArn: string | undefined;\n if (options.assumeTaskRole === true) {\n if (!task.taskRoleArn) {\n throw new Error(\n `--assume-task-role passed without an ARN but the task definition has no resolvable TaskRoleArn. ` +\n `Either the task definition does not set TaskRoleArn, or it points at a resource ${getEmbedConfig().binaryName} cannot resolve to an IAM Role at synth time. ` +\n `Pass the ARN explicitly: --assume-task-role <arn>`\n );\n }\n resolvedRoleArn = await resolvePlaceholderAccount(task.taskRoleArn, options.region);\n assumedCredentials = await assumeTaskRole(resolvedRoleArn, options.region);\n } else if (typeof options.assumeTaskRole === 'string') {\n resolvedRoleArn = options.assumeTaskRole;\n assumedCredentials = await assumeTaskRole(resolvedRoleArn, options.region);\n }\n\n // When `--assume-task-role` is NOT effective but `--profile <p>` IS\n // set, resolve the profile via the SDK's default\n // credential provider chain (SSO / IAM Identity Center / fromIni /\n // role-assumption) and forward the resulting `{AKID, SAK,\n // sessionToken?}` to the metadata-endpoints sidecar. Without this,\n // the sidecar starts inside a fresh container with no SSO config and\n // no `~/.aws/credentials`, so every user container that hits\n // `169.254.170.2/role/<role>` gets a credential-provider failure.\n // Same gap class as the equivalent forward for `cdkl start-api`'s\n // Lambda container path.\n const sidecarCredentials = await resolveSidecarCredentials(options, assumedCredentials);\n\n // ECS analogue of the Lambda-container credential fix-back:\n // when `--profile <p>` is set AND `--assume-task-role` did NOT\n // produce credentials for this task, synthesize a host-side AWS\n // shared credentials file under `[<options.profile>]` and bind-\n // mount it read-only into every user container. Handler code\n // calling `fromIni({ profile: '<options.profile>' })` then\n // resolves to the same creds the metadata sidecar serves —\n // without this, the SDK looks for `[<options.profile>]` in\n // `~/.aws/credentials` inside the container and fails.\n //\n // Gating `!assumedCredentials` preserves the documented\n // precedence (assume-task-role > profile-file > sidecar): when\n // `--assume-task-role` won, the sidecar's `/role/<arn>` endpoint\n // already serves the assumed creds and the file env vars must\n // NOT override them.\n if (options.profile && sidecarCredentials && !assumedCredentials) {\n profileCredsFile = await writeProfileCredentialsFile(options.profile, sidecarCredentials);\n }\n\n const envOverrides = readEnvOverridesFile(options.envVars);\n\n const runOpts: RunEcsTaskOptions = {\n cluster: options.cluster,\n containerHost: options.containerHost,\n skipPull: options.pull === false,\n keepRunning: options.keepRunning,\n detach: options.detach,\n };\n if (envOverrides) runOpts.envOverrides = envOverrides;\n if (sidecarCredentials) runOpts.taskCredentials = sidecarCredentials;\n if (resolvedRoleArn) runOpts.taskRoleArn = resolvedRoleArn;\n if (options.platform) runOpts.platformOverride = options.platform;\n if (options.region) runOpts.region = options.region;\n if (options.ecrRoleArn) runOpts.ecrRoleArn = options.ecrRoleArn;\n if (options.profile) runOpts.profile = options.profile;\n const hostPortOverrides = parseHostPortOverrides(options.hostPort);\n if (Object.keys(hostPortOverrides).length > 0) runOpts.hostPortOverrides = hostPortOverrides;\n if (profileCredsFile) {\n runOpts.profileCredentialsFile = {\n hostPath: profileCredsFile.hostPath,\n containerPath: profileCredsFile.containerPath,\n profileName: profileCredsFile.profileName,\n };\n }\n\n const result = await runEcsTask(task, runOpts, state);\n\n if (options.detach) {\n logger.info(\n `Task containers started in detached mode; ${getEmbedConfig().binaryName} is exiting.`\n );\n logger.info(\n `Use 'docker ps --filter network=${result.state.network?.networkName ?? '<network>'}' to inspect; ` +\n `tear down with 'docker rm -f' and 'docker network rm'.`\n );\n // Detach mode skips cleanup — the caller manages container lifecycle.\n sigintCount = 99;\n return;\n }\n\n if (result.essentialContainerName) {\n logger.info(\n `Essential container '${result.essentialContainerName}' exited with code ${result.exitCode}.`\n );\n }\n if (result.exitCode !== 0) {\n process.exitCode = result.exitCode;\n }\n } finally {\n if (sigintHandler) process.off('SIGINT', sigintHandler);\n if (stateProvider) stateProvider.dispose();\n if (!options.detach) await cleanup();\n }\n}\n\n/**\n * If `arn` contains the `${AWS::AccountId}` placeholder emitted by the\n * resolver for inline same-stack IAM Roles, substitute the live caller\n * account via STS `GetCallerIdentity`. Otherwise pass through unchanged.\n */\nasync function resolvePlaceholderAccount(arn: string, region: string | undefined): Promise<string> {\n if (!arn.includes(TASK_ROLE_ACCOUNT_PLACEHOLDER)) return arn;\n const { STSClient, GetCallerIdentityCommand } = await import('@aws-sdk/client-sts');\n const sts = new STSClient({ ...(region && { region }) });\n try {\n const identity = await sts.send(new GetCallerIdentityCommand({}));\n const account = identity.Account;\n if (!account) {\n throw new Error(\n `--assume-task-role: GetCallerIdentity returned no Account; cannot resolve placeholder ARN '${arn}'. ` +\n `Pass the ARN explicitly: --assume-task-role <arn>`\n );\n }\n return arn.split(TASK_ROLE_ACCOUNT_PLACEHOLDER).join(account);\n } finally {\n sts.destroy();\n }\n}\n\n/**\n * Assume `roleArn` and return temp credentials.\n */\nasync function assumeTaskRole(\n roleArn: string,\n region: string | undefined\n): Promise<{ accessKeyId: string; secretAccessKey: string; sessionToken: string }> {\n const { STSClient, AssumeRoleCommand } = await import('@aws-sdk/client-sts');\n const sts = new STSClient({ ...(region && { region }) });\n try {\n const response = await sts.send(\n new AssumeRoleCommand({\n RoleArn: roleArn,\n RoleSessionName: `${getEmbedConfig().resourceNamePrefix}-run-task-${Date.now()}`,\n DurationSeconds: 3600,\n })\n );\n const creds = response.Credentials;\n if (!creds?.AccessKeyId || !creds.SecretAccessKey || !creds.SessionToken) {\n throw new Error(`AssumeRole(${roleArn}) returned no usable credentials.`);\n }\n return {\n accessKeyId: creds.AccessKeyId,\n secretAccessKey: creds.SecretAccessKey,\n sessionToken: creds.SessionToken,\n };\n } finally {\n sts.destroy();\n }\n}\n\n/**\n * Build the substitution context the ECS task resolver consumes.\n * Returns `undefined` when no container's `Image` field needs\n * substitution — the resolver behaves as before in that case.\n */\nexport async function buildEcsImageResolutionContext(\n candidate: StackInfo | undefined,\n stateProvider: LocalStateProvider | undefined,\n options: LocalRunTaskOptions\n): Promise<EcsImageResolutionContext | undefined> {\n const logger = getLogger();\n if (!candidate) return undefined;\n\n const needs = detectEcsImageResolutionNeeds(candidate);\n if (\n !needs.needsPseudoParameters &&\n !needs.needsStateResources &&\n !needs.needsEnvOrSecretSubstitution\n ) {\n return undefined;\n }\n\n const ctx: EcsImageResolutionContext = {};\n\n const wantsPseudoForEnvOrSecret = !!stateProvider && needs.needsEnvOrSecretSubstitution;\n if (needs.needsPseudoParameters || wantsPseudoForEnvOrSecret) {\n const region =\n options.region ??\n process.env['AWS_REGION'] ??\n process.env['AWS_DEFAULT_REGION'] ??\n candidate.region;\n if (!region) {\n logger.warn(\n `Resolver references \\${AWS::Region} but ${getEmbedConfig().binaryName} could not determine the target region. ` +\n 'Pass --region, set AWS_REGION, or declare env.region on the CDK stack.'\n );\n }\n let accountId: string | undefined;\n try {\n accountId = await resolveCallerAccountId(region, options.profile);\n } catch (err) {\n logger.warn(\n `Resolver needs \\${AWS::AccountId} but STS GetCallerIdentity failed: ${err instanceof Error ? err.message : String(err)}. ` +\n 'Substitution will be skipped; affected env / secret entries will be dropped with per-key warnings.'\n );\n }\n const partitionAndSuffix = region ? derivePartitionAndUrlSuffix(region) : undefined;\n ctx.pseudoParameters = {\n ...(accountId !== undefined && { accountId }),\n ...(region !== undefined && { region }),\n ...(partitionAndSuffix && {\n partition: partitionAndSuffix.partition,\n urlSuffix: partitionAndSuffix.urlSuffix,\n }),\n };\n }\n\n const wantsState = needs.needsStateResources || needs.needsEnvOrSecretSubstitution;\n if (stateProvider && wantsState) {\n const loaded = await stateProvider.load(candidate.stackName, candidate.region);\n if (loaded) {\n ctx.stateResources = loaded.resources;\n }\n // Resolve SSM-backed template parameters\n // (`AWS::SSM::Parameter::Value<String>`) so a `Ref` to such a\n // parameter in a container Environment / Secrets entry resolves to\n // its SSM value instead of being warn-and-dropped (issue #94). Only\n // the CFn provider implements this; gated on env/secret substitution\n // being needed so image-only resolutions skip the SSM round-trip.\n if (needs.needsEnvOrSecretSubstitution && stateProvider.resolveTemplateSsmParameters) {\n const ssmParameters = await stateProvider.resolveTemplateSsmParameters(candidate.template);\n if (Object.keys(ssmParameters.values).length > 0) ctx.stateParameters = ssmParameters.values;\n // Flag decrypted SecureString parameters so the consuming container\n // env keys are kept off the `docker run` argv (issue #99).\n if (ssmParameters.secureStringLogicalIds.length > 0) {\n ctx.stateSensitiveParameters = ssmParameters.secureStringLogicalIds;\n }\n }\n } else if (!stateProvider && needs.needsStateResources) {\n logger.warn(\n 'Container Image references a same-stack AWS::ECR::Repository. Pass a state-source flag ' +\n '(e.g. --from-cfn-stack or a host-provided extension) to substitute the deployed repository URI. ' +\n 'Otherwise the resolver will surface its existing error.'\n );\n } else if (!stateProvider && needs.needsEnvOrSecretSubstitution) {\n logger.warn(\n 'Container Environment / Secrets entries contain CloudFormation intrinsics (Ref / Fn::GetAtt / Fn::Sub / Fn::Join). ' +\n 'Pass a state-source flag (e.g. --from-cfn-stack or a host-provided extension) to substitute them against deployed state. ' +\n 'Without a state source these entries are dropped (per-key warnings will follow).'\n );\n }\n\n return ctx;\n}\n\nfunction pickCandidateStack(\n stackPattern: string | null,\n stacks: StackInfo[]\n): StackInfo | undefined {\n if (stackPattern === null) {\n if (stacks.length === 1) return stacks[0];\n return undefined;\n }\n const matched = matchStacks(stacks, [stackPattern]);\n if (matched.length === 1) return matched[0];\n return undefined;\n}\n\nasync function resolveCallerAccountId(\n region: string | undefined,\n profile: string | undefined\n): Promise<string | undefined> {\n const { STSClient, GetCallerIdentityCommand } = await import('@aws-sdk/client-sts');\n // Thread `--profile` so the resolved account is the profile's account\n // (e.g. the deployed account behind `--from-cfn-stack`), not whatever\n // the default credential chain points at. Without this, the\n // `${AWS::AccountId}` substitution that builds same-stack ECR image\n // URIs picks the wrong account and the subsequent `docker pull` 404s.\n const sts = new STSClient({ ...(region && { region }), ...(profile && { profile }) });\n try {\n const identity = await sts.send(new GetCallerIdentityCommand({}));\n return identity.Account;\n } finally {\n sts.destroy();\n }\n}\n\n/**\n * Read the `--env-vars` JSON file using the same SAM-style shape as\n * `cdkl invoke --env-vars`: top-level keys are container names, with\n * `Parameters` reserved for global entries.\n */\nfunction readEnvOverridesFile(\n filePath: string | undefined\n): Record<string, Record<string, string | null> | undefined> | undefined {\n if (!filePath) return undefined;\n let raw: string;\n try {\n raw = readFileSync(filePath, 'utf-8');\n } catch (err) {\n throw new Error(\n `Failed to read --env-vars file '${filePath}': ${err instanceof Error ? err.message : String(err)}`\n );\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (err) {\n throw new Error(\n `Failed to parse --env-vars file '${filePath}' as JSON: ${err instanceof Error ? err.message : String(err)}`\n );\n }\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {\n throw new Error(`--env-vars file '${filePath}' must contain a JSON object at the top level.`);\n }\n return parsed as Record<string, Record<string, string | null> | undefined>;\n}\n\n/**\n * Pick the credentials forwarded to the AWS-published\n * `amazon-ecs-local-container-endpoints` sidecar. Precedence:\n * 1. `--assume-task-role <arn>` (or bare `--assume-task-role` against\n * a resolvable `TaskRoleArn`) → STS-assumed temp creds. Highest\n * priority — when the user opted in to IAM emulation, those creds\n * drive the sidecar regardless of `--profile`.\n * 2. `--profile <p>` → resolved via {@link resolveProfileCredentials}\n * (the SDK's default credential provider chain — SSO / IAM\n * Identity Center / fromIni / role-assumption). NEW in this PR.\n * 3. Neither set → `undefined`; the sidecar runs with its own\n * default credential chain (typically empty inside a fresh\n * container — user containers will get 4xx from the credentials\n * endpoint, mimicking IAM-misconfigured prod).\n *\n * Extracted as an exported helper so a unit test can exercise every\n * branch without having to mock the full Synth + Docker + AWS pipeline\n * (the strategy used for the Lambda container path).\n */\nexport async function resolveSidecarCredentials(\n options: { profile?: string },\n assumedCredentials:\n | { accessKeyId: string; secretAccessKey: string; sessionToken?: string }\n | undefined\n): Promise<{ accessKeyId: string; secretAccessKey: string; sessionToken?: string } | undefined> {\n if (assumedCredentials) return assumedCredentials;\n if (options.profile) return resolveProfileCredentials(options.profile);\n return undefined;\n}\n\nexport function createLocalRunTaskCommand(opts: CreateLocalRunTaskCommandOptions = {}): Command {\n setEmbedConfig(opts.embedConfig);\n const cmd = new Command('run-task')\n .description(\n 'Run an AWS::ECS::TaskDefinition locally — pulls/builds images, sets up a per-task docker network ' +\n 'with the AWS-published metadata-endpoints sidecar, and starts every container in dependsOn order. ' +\n 'Target accepts a CDK display path (MyStack/MyService/TaskDef) or stack-qualified logical ID ' +\n '(MyStack:MyServiceTaskDefXYZ1234). Single-stack apps may omit the stack prefix. ' +\n 'Omit <target> in an interactive terminal to pick the task definition from a list.'\n )\n .argument(\n '[target]',\n 'CDK display path or stack-qualified logical ID of the AWS::ECS::TaskDefinition to run (omit to pick interactively in a TTY)'\n )\n .addOption(\n new Option(\n '--cluster <name>',\n 'Cluster name surfaced to ECS_CONTAINER_METADATA_URI_V4 and used as the docker network prefix'\n ).default(getEmbedConfig().resourceNamePrefix)\n )\n .addOption(\n new Option(\n '--env-vars <file>',\n 'JSON env-var overrides (SAM-compatible: {\"ContainerName\":{\"KEY\":\"VALUE\"}, \"Parameters\":{}})'\n )\n )\n .addOption(\n new Option(\n '--container-host <ip>',\n 'Host IP to bind published container ports to. Must be a numeric IP (Docker rejects hostnames here)'\n ).default('127.0.0.1')\n )\n .addOption(\n new Option(\n '--host-port <containerPort=hostPort...>',\n 'Publish a container port on a specific host port (e.g. 80=8080); repeatable. ' +\n 'Default: host port == container port. Use this on macOS to map a privileged ' +\n 'container port (< 1024) to a non-privileged host port and avoid the Docker ' +\n 'Desktop admin-password prompt.'\n )\n )\n .addOption(\n new Option(\n '--assume-task-role [arn]',\n \"Assume the task definition's TaskRoleArn (or the supplied ARN) and forward STS-issued temp \" +\n 'credentials via the metadata sidecar so containers run with the deployed function role. ' +\n \"Bare flag uses the template's TaskRoleArn; pass an explicit ARN to override.\"\n )\n )\n .addOption(\n new Option('--no-pull', 'Skip docker pull for every container image and the metadata sidecar')\n )\n .addOption(\n new Option(\n '--ecr-role-arn <arn>',\n 'Role ARN to assume before authenticating against ECR for cross-account / centralized ' +\n 'registries. Issues sts:AssumeRole via the default credential chain and uses the ' +\n 'temporary credentials for ecr:GetAuthorizationToken + docker pull. Required when the ' +\n 'caller does not have direct cross-account access to the target repository. ' +\n 'Same-account / same-region pulls do not need this flag.'\n )\n )\n .addOption(\n new Option(\n '--platform <platform>',\n 'Force docker --platform (linux/amd64 or linux/arm64). Default: inferred from task RuntimePlatform.CpuArchitecture'\n )\n )\n .addOption(\n new Option(\n '--keep-running',\n \"Don't docker rm -f the user containers on task exit (network + sidecar are still torn down). \" +\n 'Use when you want to docker exec into a stopped container for post-mortems.'\n ).default(false)\n )\n .addOption(\n new Option(\n '--detach',\n 'Start the containers in the background and exit (skip log streaming + auto teardown). ' +\n 'Useful in CI smoke tests; caller manages container lifecycle.'\n ).default(false)\n )\n .addOption(\n new Option(\n '--from-cfn-stack [cfn-stack-name]',\n 'Read a deployed CloudFormation stack via ListStackResources and substitute Ref / Fn::ImportValue ' +\n 'in container env vars / secrets / image URIs with the deployed physical IDs / exports. ' +\n 'Use for CDK apps deployed via the upstream CDK CLI (`cdk deploy`). ' +\n `Bare form uses the ${getEmbedConfig().binaryName} stack name; pass an explicit value when the CFn stack name differs. ` +\n 'Fn::GetAtt is warn-and-dropped in v1 (CFn ListStackResources does not return per-attribute values).'\n )\n )\n .addOption(\n new Option(\n '--stack-region <region>',\n 'Region of the state record to read. Used with --from-cfn-stack as the CFn client region.'\n )\n )\n .action(\n withErrorHandling(async (target: string | undefined, options: LocalRunTaskOptions) => {\n await localRunTaskCommand(target, options, opts.extraStateProviders);\n })\n );\n\n [...commonOptions(), ...appOptions(), ...contextOptions].forEach((opt) => cmd.addOption(opt));\n cmd.addOption(deprecatedRegionOption);\n return cmd;\n}\n","import type { StackInfo } from '../synthesis/assembly-reader.js';\nimport type { TemplateResource } from '../types/resource.js';\nimport { buildCdkPathIndex, resolveCdkPathToLogicalIds } from '../cli/cdk-path.js';\nimport { matchStacks } from '../cli/stack-matcher.js';\nimport {\n EcsTaskResolutionError,\n parseEcsTarget,\n resolveEcsTaskTarget,\n type EcsImageResolutionContext,\n type ParsedEcsTarget,\n type ResolvedEcsTask,\n} from './ecs-task-resolver.js';\nimport { getEmbedConfig } from './embed-config.js';\n\n/**\n * Phase 2 of #262 — synthesized `AWS::ECS::Service` resolved against the\n * cloud assembly. Wraps an already-resolved `ResolvedEcsTask` (Phase 1's\n * descriptor) plus the service-specific knobs `cdkl start-service`\n * needs: `DesiredCount`, `HealthCheckGracePeriodSeconds`, the physical\n * task-definition logical ID (so the runner can name its docker networks\n * after the service rather than only after the task definition), AND\n * (Phase 3 / Issue #460) the Service Connect + ServiceRegistry surfaces\n * so the Cloud Map registry can publish each replica's endpoint for\n * peer discovery via the docker `--add-host` DNS overlay.\n *\n * `LoadBalancers[]` is intentionally NOT surfaced in v1 — local\n * load-balancer emulation is deferred to a follow-up PR per the issue's\n * own PR-split recommendation (see CLAUDE.md \"cdkl start-service\"\n * bullet for the deferral list).\n */\n/**\n * Resolved `AWS::ECS::Service.ServiceConnectConfiguration` (Phase 3 of #262).\n * Pre-PR cdk-local warned and skipped this block; post-PR each entry's\n * `PortName` is matched against the producer TaskDef's\n * `ContainerDefinitions[].PortMappings[].Name` (verified empirically\n * via real `cdk synth` on 2026-05-22 — the CFn field is `PortName`,\n * NOT the design doc's `PortMappingName`) and the resolver surfaces\n * the resulting `(discoveryName, port, clientAliases)` tuples so the\n * Cloud Map registry can publish each replica's endpoint under the\n * configured DNS names.\n */\nexport interface ResolvedServiceConnect {\n /**\n * Namespace name (e.g. `cdkl.local`). CDK 2.x synthesizes\n * `ServiceConnectConfiguration.Namespace` as a literal string (not\n * a Ref / ARN) — verified empirically on 2026-05-22. The literal is\n * matched against the resolved `CloudMapIndex.namespacesByName` at\n * registry-publish time; an unmatched name surfaces as an error\n * before any container starts.\n */\n namespaceName: string;\n /**\n * Per-entry mapping: which port (from the task def) is exposed under\n * which DNS name (the Service Connect ClientAlias), and what the\n * canonical Cloud Map service-discovery name is.\n */\n services: ReadonlyArray<{\n /** The producer container's `PortMappings[].Name`. */\n portName: string;\n /**\n * Resolved containerPort from the matching `PortMappings[]` entry.\n * Falls back to the `ClientAliases[0].Port` when the producer side\n * cannot be resolved (rare — only when the resolver is invoked\n * against a service whose TaskDef hasn't been parsed yet).\n */\n containerPort: number;\n /**\n * The canonical Cloud Map discovery name. cdk-local derives this from\n * `ClientAliases[0].DnsName` when the user supplied one; otherwise\n * defaults to the `PortName` so the registry publishes under a\n * meaningful key.\n */\n discoveryName: string;\n /** All ClientAlias entries (each becomes its own `--add-host` alias). */\n clientAliases: ReadonlyArray<{ dnsName?: string; port: number }>;\n }>;\n}\n\n/**\n * Resolved `AWS::ECS::Service.ServiceRegistries[]` (Phase 3 of #262).\n * Each entry binds an ECS Service to one `AWS::ServiceDiscovery::Service`\n * — the AWS-side ECS Agent calls `RegisterInstance` on each task launch.\n * Locally, the runner mirrors that behavior into the Cloud Map registry\n * after each replica's main container boots.\n */\nexport interface ResolvedServiceRegistry {\n /** Logical id of the `AWS::ServiceDiscovery::Service` this binds to. */\n cloudMapServiceLogicalId: string;\n /** Optional container name override (CFn `ContainerName`). */\n containerName?: string;\n /** Optional container port override (CFn `ContainerPort`). */\n containerPort?: number;\n}\n\nexport interface ResolvedEcsService {\n /** Stack the service belongs to. */\n stack: StackInfo;\n /** Logical id of the AWS::ECS::Service resource. */\n serviceLogicalId: string;\n /** Raw template entry — kept for future feature additions (LB / Service Connect). */\n resource: TemplateResource;\n /**\n * Service name. Falls back to the logical id when the template does\n * not declare `ServiceName` (the AWS-deployed name would be\n * auto-generated; local execution does not need it for anything\n * load-bearing — used only in log lines).\n */\n serviceName: string;\n /** DesiredCount from the template; defaults to 1 when absent. */\n desiredCount: number;\n /**\n * HealthCheckGracePeriodSeconds from the template. AWS defaults to 0\n * when the service has no load balancer, 30s with an ALB attached.\n *\n * **Currently not consumed by the runner.** cdk-local's\n * `ecs-service-runner.ts` is exit-code-driven (`docker wait` →\n * `shouldRestart`); there is no health-check polling in v1 because\n * health-check-driven restarts are only meaningful once the local\n * load-balancer emulator lands (see [docs/design/461-awsvpc-decision.md]\n * and the deferred LB scope in CLAUDE.md). The field is parsed,\n * surfaced on `ResolvedEcsService`, and intentionally retained so the\n * follow-up LB emulator PR can use it as the time-from-start before\n * an unhealthy target-group health check counts toward a restart. The\n * value defaults to 30s locally to match the AWS-with-ALB default.\n */\n healthCheckGracePeriodSeconds: number;\n /**\n * The resolved task descriptor (every container / volume / network mode /\n * runtime platform / task-role detail). cdk-local reuses this verbatim per\n * replica instance.\n */\n task: ResolvedEcsTask;\n /**\n * Phase 3 of #262 / Issue #460 — `ServiceConnectConfiguration` resolved\n * against the producer TaskDef's PortMappings. `undefined` when the\n * service has no Service Connect block OR has it but `Enabled: false`.\n */\n serviceConnect?: ResolvedServiceConnect;\n /**\n * Phase 3 of #262 / Issue #460 — `ServiceRegistries[]` resolved. Empty\n * array when the service has no Cloud Map registration (the common\n * case for Service-Connect-only or no-discovery services). Each entry\n * names the `AWS::ServiceDiscovery::Service` logical id the runner\n * publishes replica endpoints under.\n */\n serviceRegistries: ReadonlyArray<ResolvedServiceRegistry>;\n /**\n * Resolution warnings (e.g. `awsvpc` → bridge map from the task\n * resolver, or load-balancer fields not honored locally). Non-fatal —\n * the runner still proceeds.\n */\n warnings: string[];\n}\n\n/**\n * Walk the synth template to locate an `AWS::ECS::Service` by display\n * path or stack-qualified logical id, resolve its `TaskDefinition`\n * reference, and chain into the existing `resolveEcsTaskTarget` machinery\n * to produce a `ResolvedEcsService` carrying both the service knobs and\n * the underlying task descriptor.\n *\n * Target shape mirrors `cdkl run-task`: `<Stack>/<DisplayPath>` or\n * `<Stack>:<LogicalId>`; single-stack apps may omit the stack prefix.\n *\n * Optional `context` (same as the task resolver) carries the ECR image\n * substitution data — pseudo parameters (Tier 1) + state-recorded\n * resources (Tier 2). The CLI builds it lazily when the candidate\n * service's task definition actually needs substitution.\n */\nexport function resolveEcsServiceTarget(\n target: string,\n stacks: StackInfo[],\n context?: EcsImageResolutionContext\n): ResolvedEcsService {\n if (stacks.length === 0) {\n throw new EcsTaskResolutionError('No stacks found in the synthesized assembly.');\n }\n const parsed = parseEcsTarget(target);\n const stack = pickStack(parsed, stacks);\n const resources = stack.template.Resources ?? {};\n\n let serviceLogicalId: string | undefined;\n let serviceResource: TemplateResource | undefined;\n\n if (parsed.isPath) {\n const index = buildCdkPathIndex(stack.template);\n const resolved = resolveCdkPathToLogicalIds(parsed.pathOrId, index);\n const services = resolved.filter(\n ({ logicalId: l }) => resources[l]?.Type === 'AWS::ECS::Service'\n );\n if (services.length === 0) {\n throw notFoundError(target, stack, resources, parsed);\n }\n if (services.length > 1) {\n throw new EcsTaskResolutionError(\n `Target '${target}' matches ${services.length} ECS services in ${stack.stackName}: ` +\n services.map((s) => s.logicalId).join(', ') +\n '. Refine the path or use the stack:LogicalId form.'\n );\n }\n serviceLogicalId = services[0]!.logicalId;\n serviceResource = resources[serviceLogicalId];\n } else {\n serviceResource = resources[parsed.pathOrId];\n if (!serviceResource) throw notFoundError(target, stack, resources, parsed);\n serviceLogicalId = parsed.pathOrId;\n }\n\n if (!serviceLogicalId || !serviceResource) throw notFoundError(target, stack, resources, parsed);\n\n if (serviceResource.Type === 'AWS::ECS::TaskDefinition') {\n throw new EcsTaskResolutionError(\n `Resource '${serviceLogicalId}' in ${stack.stackName} is an ECS TaskDefinition, not a Service. ` +\n `Use \\`${getEmbedConfig().cliName} run-task\\` for one-shot tasks; \\`${getEmbedConfig().cliName} start-service\\` is Service-only.`\n );\n }\n if (serviceResource.Type !== 'AWS::ECS::Service') {\n throw new EcsTaskResolutionError(\n `Resource '${serviceLogicalId}' in ${stack.stackName} is ${serviceResource.Type}, not an AWS::ECS::Service.`\n );\n }\n\n return extractServiceProperties(stack, serviceLogicalId, serviceResource, stacks, context);\n}\n\n/**\n * Pure-functional extraction from the synth resource. Exposed for unit\n * testing the per-field resolution rules (DesiredCount default, missing\n * TaskDefinition, intrinsic shapes).\n */\nexport function extractServiceProperties(\n stack: StackInfo,\n serviceLogicalId: string,\n resource: TemplateResource,\n stacks: StackInfo[],\n context?: EcsImageResolutionContext\n): ResolvedEcsService {\n const props = (resource.Properties ?? {}) as Record<string, unknown>;\n const warnings: string[] = [];\n\n const taskDefRef = props['TaskDefinition'];\n if (taskDefRef === undefined || taskDefRef === null) {\n throw new EcsTaskResolutionError(\n `ECS Service '${serviceLogicalId}' in ${stack.stackName} has no TaskDefinition property.`\n );\n }\n const taskDefLogicalId = resolveTaskDefinitionReference(taskDefRef, stack, serviceLogicalId);\n\n // Chain into the existing task resolver. Reuses every per-container /\n // per-volume / network-mode resolution rule (incl. the `awsvpc` →\n // bridge map warn from #461).\n const task = resolveEcsTaskTarget(`${stack.stackName}:${taskDefLogicalId}`, stacks, context);\n\n const desiredCount = parseDesiredCount(props['DesiredCount'], serviceLogicalId);\n const healthCheckGracePeriodSeconds = parseHealthCheckGrace(\n props['HealthCheckGracePeriodSeconds'],\n serviceLogicalId\n );\n const serviceName = parseServiceName(props['ServiceName'], serviceLogicalId);\n\n // Surface deferred-feature warnings so users learn what's NOT\n // emulated locally without reading source.\n if (Array.isArray(props['LoadBalancers']) && (props['LoadBalancers'] as unknown[]).length > 0) {\n warnings.push(\n `ECS Service '${serviceLogicalId}' declares LoadBalancers, but local load-balancer ` +\n 'emulation is deferred to a follow-up PR. Containers are NOT registered to a local ' +\n 'listener; reach them via their published ports.'\n );\n }\n\n // Phase 3 of #262 / Issue #460 — Service Connect + ServiceRegistries\n // are now first-class. Parse + surface; the registry/runner layer\n // handles cross-service publish + DNS overlay.\n const serviceConnect = extractServiceConnect(props['ServiceConnectConfiguration'], task);\n const serviceRegistries = extractServiceRegistries(\n props['ServiceRegistries'],\n serviceLogicalId,\n warnings\n );\n\n const out: ResolvedEcsService = {\n stack,\n serviceLogicalId,\n resource,\n serviceName,\n desiredCount,\n healthCheckGracePeriodSeconds,\n task,\n serviceRegistries,\n warnings,\n };\n if (serviceConnect) out.serviceConnect = serviceConnect;\n return out;\n}\n\n/**\n * Parse `ServiceConnectConfiguration` against the producer TaskDef.\n * Returns `undefined` when the block is missing OR `Enabled: false`.\n *\n * Reject conditions (surface as resolver-time errors so the user sees\n * them BEFORE the docker network is created):\n * - `Namespace` is not a literal string. CDK 2.x always emits a\n * literal string here (verified 2026-05-22); cross-stack /\n * intrinsic shapes are out of scope.\n * - `Services[].PortName` doesn't match any of the TaskDef's\n * `ContainerDefinitions[].PortMappings[].Name` entries.\n *\n * Note on `clientAliases[]` shape: each ClientAlias can declare a\n * `DnsName` (the bare short-name peers connect to, e.g. `orders`) AND\n * a `Port` (the listening port the alias maps to inside the consumer).\n * cdk-local surfaces both verbatim; the registry / `--add-host` overlay\n * publishes each `DnsName` as a bare alias pointing at the same IP as\n * the canonical fqdn.\n */\nfunction extractServiceConnect(\n raw: unknown,\n task: ResolvedEcsTask\n): ResolvedServiceConnect | undefined {\n if (!raw || typeof raw !== 'object') return undefined;\n const cfg = raw as Record<string, unknown>;\n // CFn default for `Enabled` is `true` when ServiceConnectConfiguration\n // is present (CDK always emits `Enabled: true`); honor an explicit\n // `false` as opt-out.\n if (cfg['Enabled'] === false) return undefined;\n\n const namespaceName = pickServiceConnectNamespace(cfg['Namespace']);\n if (!namespaceName) {\n throw new EcsTaskResolutionError(\n `ServiceConnectConfiguration.Namespace must be a literal string (the Cloud Map ` +\n `namespace name like '${getEmbedConfig().resourceNamePrefix}.local'); got ${JSON.stringify(cfg['Namespace'])}. ` +\n 'Intrinsic / cross-stack namespace references are not supported in v1.'\n );\n }\n\n const rawServices = cfg['Services'];\n if (!Array.isArray(rawServices) || rawServices.length === 0) {\n // No `Services[]` is valid in AWS — the task still gets the local\n // Cloud Map DNS resolver but doesn't expose anything itself. cdk-local's\n // local emulation treats it the same way (registry publishes\n // nothing, consumer-side `--add-host` still works).\n return { namespaceName, services: [] };\n }\n\n // Build a `Name → containerPort` index from the producer TaskDef.\n // The lookup is across ALL containers because CDK emits port mapping\n // names without container-scoping them.\n const portByName = new Map<string, number>();\n for (const c of task.containers) {\n for (const pm of c.portMappings) {\n if (pm.name) portByName.set(pm.name, pm.containerPort);\n }\n }\n\n const services: Array<{\n portName: string;\n containerPort: number;\n discoveryName: string;\n clientAliases: Array<{ dnsName?: string; port: number }>;\n }> = [];\n for (const entry of rawServices) {\n if (!entry || typeof entry !== 'object') continue;\n const e = entry as Record<string, unknown>;\n const portName = typeof e['PortName'] === 'string' ? e['PortName'] : undefined;\n if (!portName) {\n throw new EcsTaskResolutionError(\n `ServiceConnectConfiguration.Services[] entry has no PortName: ${JSON.stringify(entry)}. ` +\n 'Every Service entry must reference a producer-side PortMappings[].Name.'\n );\n }\n const containerPort = portByName.get(portName);\n if (containerPort === undefined) {\n const available = [...portByName.keys()].join(', ') || '(none)';\n throw new EcsTaskResolutionError(\n `ServiceConnectConfiguration.Services[].PortName='${portName}' does not match any ` +\n `PortMappings[].Name on the producer TaskDef (available: ${available}).`\n );\n }\n const clientAliases: { dnsName?: string; port: number }[] = [];\n if (Array.isArray(e['ClientAliases'])) {\n for (const ca of e['ClientAliases'] as unknown[]) {\n if (!ca || typeof ca !== 'object') continue;\n const caObj = ca as Record<string, unknown>;\n const dnsName = typeof caObj['DnsName'] === 'string' ? caObj['DnsName'] : undefined;\n const port = typeof caObj['Port'] === 'number' ? caObj['Port'] : containerPort;\n const aliasEntry: { dnsName?: string; port: number } = { port };\n if (dnsName !== undefined) aliasEntry.dnsName = dnsName;\n clientAliases.push(aliasEntry);\n }\n }\n // `discoveryName` precedence: first ClientAlias with a DnsName, else\n // the PortName. Mirrors how AWS-side Service Connect publishes the\n // service in Cloud Map.\n const aliasWithName = clientAliases.find((c) => c.dnsName !== undefined);\n const discoveryName = aliasWithName?.dnsName ?? portName;\n services.push({ portName, containerPort, discoveryName, clientAliases });\n }\n\n return { namespaceName, services };\n}\n\n/**\n * Parse `ServiceRegistries[]`. Each entry's `RegistryArn` is the\n * canonical `Fn::GetAtt: [<CloudMapServiceLogicalId>, 'Arn']` shape;\n * cdk-local surfaces the logical id (the AWS-side ARN is irrelevant\n * locally — the registry is in-process).\n *\n * Issue #544 — entries with a literal-string `RegistryArn` (rare\n * locally — would imply the user bound to an existing Cloud Map\n * service deployed out-of-band) are skipped with a warning, since the\n * in-process registry cannot resolve an external Cloud Map service\n * back to its `(namespace, name)` pair. Pre-fix this was a silent\n * `continue` and the user got no feedback about why the registration\n * didn't show up.\n */\nfunction extractServiceRegistries(\n raw: unknown,\n serviceLogicalId: string,\n warnings: string[]\n): ReadonlyArray<ResolvedServiceRegistry> {\n if (!Array.isArray(raw)) return [];\n const out: ResolvedServiceRegistry[] = [];\n for (const entry of raw) {\n if (!entry || typeof entry !== 'object') continue;\n const e = entry as Record<string, unknown>;\n const registryArn = e['RegistryArn'];\n let cloudMapServiceLogicalId: string | undefined;\n if (typeof registryArn === 'string') {\n // Already a literal ARN (rare locally — would imply the user\n // bound to an existing Cloud Map service deployed out-of-band).\n // We can't resolve it via the in-process registry; warn + skip.\n warnings.push(\n `ECS Service '${serviceLogicalId}' ServiceRegistries[] entry has a literal-string ` +\n `RegistryArn ('${registryArn}'); ${getEmbedConfig().productName} cannot resolve external Cloud Map services ` +\n 'locally. Skipping this registration; peer services will not discover this endpoint ' +\n 'through the in-process registry. Use Fn::GetAtt: [<CloudMapServiceLogicalId>, \"Arn\"] ' +\n `instead so ${getEmbedConfig().productName} can resolve the namespace + service name from the synthesized template.`\n );\n continue;\n }\n if (registryArn && typeof registryArn === 'object' && !Array.isArray(registryArn)) {\n const obj = registryArn as Record<string, unknown>;\n const getAtt = obj['Fn::GetAtt'];\n if (Array.isArray(getAtt) && typeof getAtt[0] === 'string') {\n cloudMapServiceLogicalId = getAtt[0];\n }\n }\n if (!cloudMapServiceLogicalId) continue;\n const reg: ResolvedServiceRegistry = { cloudMapServiceLogicalId };\n if (typeof e['ContainerName'] === 'string') reg.containerName = e['ContainerName'];\n if (typeof e['ContainerPort'] === 'number') reg.containerPort = e['ContainerPort'];\n out.push(reg);\n }\n return out;\n}\n\nfunction pickServiceConnectNamespace(raw: unknown): string | undefined {\n // CDK 2.x synthesizes `ServiceConnectConfiguration.Namespace` as a\n // literal string (verified via real `cdk synth` 2026-05-22).\n // Defensive: accept a `Ref` that points to a literal-only context\n // (rare; would land here only when the user hand-rolled a CfnService).\n if (typeof raw === 'string' && raw.length > 0) return raw;\n return undefined;\n}\n\n/**\n * Resolve `Properties.TaskDefinition` to a logical id in the same stack.\n * Accepted shapes — verified against real CDK 2.x `cdk synth` output on\n * 2026-05-22 (per `feedback_verify_cdk_synth_shape_before_resolver.md`):\n * - `{Ref: '<TaskDefLogicalId>'}` — the CDK-canonical shape emitted by\n * `new ecs.FargateService({ taskDefinition })`.\n * - flat string `'<TaskDefLogicalId>'` — accepted defensively but CDK\n * rarely emits this for cross-resource refs.\n * Other intrinsic shapes (`Fn::ImportValue` / `Fn::GetAtt` / etc.) are\n * rejected — cross-stack task definitions and `Fn::GetAtt` shapes have\n * no clean local resolution and would land here only as user errors.\n */\nfunction resolveTaskDefinitionReference(\n taskDefRef: unknown,\n stack: StackInfo,\n serviceLogicalId: string\n): string {\n if (typeof taskDefRef === 'string') {\n return taskDefRef;\n }\n if (taskDefRef && typeof taskDefRef === 'object' && !Array.isArray(taskDefRef)) {\n const obj = taskDefRef as Record<string, unknown>;\n const refValue = obj['Ref'];\n if (typeof refValue === 'string') {\n const resources = stack.template.Resources ?? {};\n const target = resources[refValue];\n if (!target) {\n throw new EcsTaskResolutionError(\n `ECS Service '${serviceLogicalId}' references TaskDefinition '${refValue}' but no ` +\n `such resource exists in ${stack.stackName}.`\n );\n }\n if (target.Type !== 'AWS::ECS::TaskDefinition') {\n throw new EcsTaskResolutionError(\n `ECS Service '${serviceLogicalId}' references '${refValue}' as TaskDefinition but it ` +\n `is of type ${target.Type}, not AWS::ECS::TaskDefinition.`\n );\n }\n return refValue;\n }\n }\n throw new EcsTaskResolutionError(\n `ECS Service '${serviceLogicalId}' has an unsupported TaskDefinition reference shape: ` +\n `${JSON.stringify(taskDefRef)}. ${getEmbedConfig().cliName} start-service v1 supports only Ref to a ` +\n 'same-stack AWS::ECS::TaskDefinition; cross-stack TaskDefinitions are deferred.'\n );\n}\n\nfunction parseDesiredCount(raw: unknown, serviceLogicalId: string): number {\n if (raw === undefined || raw === null) return 1;\n if (typeof raw === 'number' && Number.isFinite(raw) && raw >= 0) {\n return Math.floor(raw);\n }\n if (typeof raw === 'string' && /^\\d+$/.test(raw)) {\n return parseInt(raw, 10);\n }\n throw new EcsTaskResolutionError(\n `ECS Service '${serviceLogicalId}' has an unsupported DesiredCount value: ` +\n `${JSON.stringify(raw)}. Must be a non-negative integer.`\n );\n}\n\nfunction parseHealthCheckGrace(raw: unknown, _serviceLogicalId: string): number {\n if (raw === undefined || raw === null) return 30;\n if (typeof raw === 'number' && Number.isFinite(raw) && raw >= 0) {\n return Math.floor(raw);\n }\n if (typeof raw === 'string' && /^\\d+$/.test(raw)) {\n return parseInt(raw, 10);\n }\n // Intrinsic shapes silently default to 30s — fail-soft because the\n // grace period is local-only behavior tuning, not a correctness\n // boundary.\n return 30;\n}\n\nfunction parseServiceName(raw: unknown, serviceLogicalId: string): string {\n if (typeof raw === 'string' && raw.length > 0) return raw;\n return serviceLogicalId;\n}\n\n/**\n * Local copy of the same `pickStack` helper used by the task resolver.\n * Kept in-file rather than exported from `ecs-task-resolver.ts` so future\n * service-specific extensions (e.g. cross-stack service-to-task refs)\n * can diverge without breaking the run-task code path.\n */\nfunction pickStack(\n parsed: { stackPattern: string | null; pathOrId: string },\n stacks: StackInfo[]\n): StackInfo {\n if (parsed.stackPattern === null) {\n if (stacks.length === 1) return stacks[0]!;\n throw new EcsTaskResolutionError(\n `Target has no stack prefix, and the assembly contains ${stacks.length} stacks: ` +\n `${stacks.map((s) => s.stackName).join(', ')}. Pass the target as 'Stack/Path' or 'Stack:LogicalId'.`\n );\n }\n const matched = matchStacks(stacks, [parsed.stackPattern]);\n if (matched.length === 0) {\n throw new EcsTaskResolutionError(\n `No stack matches '${parsed.stackPattern}'. Available stacks: ${stacks\n .map((s) => s.stackName)\n .join(', ')}.`\n );\n }\n if (matched.length > 1) {\n throw new EcsTaskResolutionError(\n `Multiple stacks match '${parsed.stackPattern}': ${matched.map((s) => s.stackName).join(', ')}. ` +\n 'Refine the pattern.'\n );\n }\n return matched[0]!;\n}\n\nfunction notFoundError(\n target: string,\n stack: StackInfo,\n resources: Record<string, TemplateResource>,\n parsed: ParsedEcsTarget\n): EcsTaskResolutionError {\n const services: { displayPath: string; logicalId: string }[] = [];\n for (const [logicalId, r] of Object.entries(resources)) {\n if (r.Type !== 'AWS::ECS::Service') continue;\n const meta = r.Metadata;\n const cdkPath = typeof meta?.['aws:cdk:path'] === 'string' ? meta['aws:cdk:path'] : '';\n services.push({ displayPath: cdkPath || logicalId, logicalId });\n }\n if (services.length === 0) {\n return new EcsTaskResolutionError(\n `Target '${target}' did not match any resource in ${stack.stackName}, and the stack ` +\n 'declares no AWS::ECS::Service resources at all.'\n );\n }\n // Show BOTH forms per service so the message itself documents how to\n // address each: the 'Stack/Path' form matches the CDK construct path\n // (left column), the 'Stack:LogicalId' form matches the logical ID\n // (right column).\n const width = Math.max(...services.map((s) => s.displayPath.length));\n let msg = `Target '${target}' did not match any ECS Service in ${stack.stackName}.\\n\\n`;\n msg += `Available services in ${stack.stackName} (CDK path and logical ID):\\n`;\n for (const s of services) {\n msg += ` ${s.displayPath.padEnd(width)} (${s.logicalId})\\n`;\n }\n // When a 'Stack/Path'-form target's trailing segment is exactly a\n // service logical ID, the user almost certainly meant the colon form —\n // the slash form resolves CDK construct paths, not logical IDs, so it\n // dead-ends with an \"Available services\" list that appears to contain\n // the very name they typed. Call out the fix explicitly.\n if (parsed.isPath) {\n const tail = parsed.pathOrId.includes('/')\n ? parsed.pathOrId.slice(parsed.pathOrId.lastIndexOf('/') + 1)\n : parsed.pathOrId;\n if (services.some((s) => s.logicalId === tail)) {\n msg +=\n `\\n'${tail}' is a logical ID, but the 'Stack/Path' form matches CDK construct paths. ` +\n `To target it by logical ID, use the colon form: '${stack.stackName}:${tail}'.`;\n }\n }\n return new EcsTaskResolutionError(msg.trimEnd());\n}\n","import { getLogger } from '../utils/logger.js';\nimport { singleFlight } from '../utils/single-flight.js';\nimport {\n cleanupEcsRun,\n createEcsRunState,\n runEcsTask,\n type EcsRunState,\n type RunEcsTaskOptions,\n} from './ecs-task-runner.js';\nimport type { ResolvedEcsService } from './ecs-service-resolver.js';\nimport type { CloudMapRegistry, RegistrationHandle } from './cloud-map-registry.js';\nimport type { CloudMapIndex } from './cloud-map-resolver.js';\nimport type { FrontDoorEndpointPool } from './front-door-pool.js';\nimport { getContainerNetworkIp, getPublishedHostPort } from './docker-inspect.js';\nimport { SHARED_SVC_SUBNET_OCTET, type TaskNetwork } from './ecs-network.js';\nimport { getEmbedConfig } from './embed-config.js';\n\n/**\n * Phase 2 of #262 — long-running ECS Service emulator. Wraps the existing\n * `ecs-task-runner` machinery in a replica pool: N concurrent task\n * instances per `DesiredCount`, each with its own docker network +\n * metadata sidecar + container set. Tasks that exit non-zero AFTER the\n * health-check grace period are restarted with exponential backoff so a\n * crash-looping container does not hammer docker.\n *\n * v1 scope (per the issue's PR-split recommendation):\n * - Replica pool sizing via `DesiredCount` clamped by `--max-tasks`.\n * - Restart-on-exit with exponential backoff (1s → 30s, capped) +\n * a per-instance retry counter so a permanently-broken container\n * stops compounding cleanup work.\n * - Long-running lifecycle (returns only on shutdown).\n *\n * Phase 3 of #262 (Issue #460) — Cloud Map / Service Connect peer\n * discovery is wired through `ServiceRunnerOptions.discovery`. When\n * supplied, every booted replica discovers its docker IP, registers\n * itself into the shared in-process `CloudMapRegistry`, and emits\n * `--add-host` flags so consumer containers reach peer services via\n * the canonical `<discoveryName>.<namespace>` fqdn. Envoy L7 sidecar\n * emulation (design Layer B) is deferred to a follow-up PR per the\n * design's §O5 \"--no-envoy by default\" recommendation.\n *\n * Deferred to follow-up PRs:\n * - Local load-balancer emulation (LB listener + target-group health\n * check + round-robin) — separate PR per the issue's PR-split.\n * - Envoy sidecar for Service Connect L7 routing / retries / circuit\n * breaking (Cloud Map DNS-only mode ships now).\n * - Rolling deployment (`--reload` / `--watch`).\n */\n\nexport class EcsServiceRunnerError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'EcsServiceRunnerError';\n Object.setPrototypeOf(this, EcsServiceRunnerError.prototype);\n }\n}\n\nexport interface ServiceRunnerOptions {\n /**\n * Hard cap on local replica count. Even when the service's\n * `DesiredCount` is high (e.g. production-shape 10+), local dev\n * machines should not have to run that many containers. Default 3 in\n * the CLI; the runner clamps to this regardless of `DesiredCount`.\n */\n maxTasks: number;\n /**\n * Restart policy on exit. Default `on-failure`: restart only when the\n * essential container exits non-zero. `always` restarts on every exit\n * (mirroring ECS Service deployment behavior more closely but produces\n * more cleanup churn). `none` skips restart entirely; the runner\n * shuts the affected replica down and the service runs degraded.\n */\n restartPolicy: 'on-failure' | 'always' | 'none';\n /**\n * Underlying per-task options. Forwarded verbatim per replica to the\n * task runner.\n */\n taskOptions: RunEcsTaskOptions;\n /**\n * Issue #460 — Cloud Map / Service Connect shared registry. When\n * provided, every booted replica:\n * 1. Has its main-container IP resolved via `docker inspect`.\n * 2. Registers `(namespace, discoveryName) → ip:port` into the\n * registry for every Service Connect entry AND every\n * ServiceRegistry (Cloud Map service) referenced by this\n * service.\n * 3. Re-builds its own `addHostFlags` from the registry's current\n * snapshot so the consumer can reach previously-booted peer\n * services via DNS overlay.\n * Pass `undefined` (single-service runs that don't need cross-\n * service discovery) to short-circuit registry interaction\n * entirely.\n */\n discovery?: ServiceDiscoveryContext;\n /**\n * Issue #86 v1 — local ALB front-door. When set, every replica publishes\n * each pool's target container port on an ephemeral host port and registers\n * the resulting `127.0.0.1:<port>` endpoint into the pool so the host-side\n * front-door server can round-robin to it. Registrations are dropped on\n * replica restart / shutdown, mirroring the Cloud Map handle lifecycle.\n * Undefined for services with no resolvable load-balancer listener.\n */\n frontDoor?: FrontDoorRunnerContext;\n}\n\n/**\n * Per-service front-door wiring threaded from the CLI. One pool per resolved\n * listener `forward` target; each carries the container name + port the\n * listener targets so the runner can publish + discover the right ephemeral\n * host port per replica.\n */\nexport interface FrontDoorRunnerContext {\n pools: ReadonlyArray<{\n pool: FrontDoorEndpointPool;\n targetContainerName: string;\n targetContainerPort: number;\n }>;\n}\n\n/**\n * Shared Cloud Map state across all services run in one\n * `cdkl start-service` invocation. The CLI builds this once and\n * threads the same object into every `startEcsService` call so peer\n * services discover each other through the shared `registry`.\n */\nexport interface ServiceDiscoveryContext {\n /** The in-process registry shared across every service in this CLI run. */\n registry: CloudMapRegistry;\n /**\n * Combined `CloudMapIndex` across every CDK stack we know about,\n * keyed by stack name so the runner can resolve a service's\n * `ServiceRegistries[].cloudMapServiceLogicalId` against the right\n * stack's index.\n */\n cloudMapIndexByStack: ReadonlyMap<string, CloudMapIndex>;\n /**\n * Single docker network shared across every replica boot in this\n * CLI invocation (design doc § 5 Option A). The CLI creates one\n * `cdkl-svc-<rand>` network at startup via\n * `createSharedSvcNetwork()` and tears it down at the end of the\n * run. Per-replica `runEcsTask()` calls receive this as\n * `existingNetwork` so every container joins the shared bridge —\n * peer services then reach each other by IP / network alias\n * without docker `network connect` choreography (design rejected\n * Option B for being \"unwieldy and racy\"). Undefined for callers\n * that opt out of shared mode (single-service runs that do not\n * need cross-service discovery).\n */\n sharedNetwork?: TaskNetwork;\n}\n\n/**\n * One running replica instance. The runner keeps the `EcsRunState`\n * around so the shutdown path can fan out cleanup across every\n * instance. `restartCount` lets the runner backoff before re-spinning a\n * crash-looping replica.\n */\nexport interface ServiceReplicaInstance {\n /** Replica index 0..desiredCount-1; load-bearing for per-instance docker network names. */\n index: number;\n state: EcsRunState;\n /** Number of restarts since service boot. Drives the backoff schedule. */\n restartCount: number;\n /** Set when the replica is being torn down so the watcher skips it. */\n shuttingDown: boolean;\n /**\n * Cloud Map registry handles published for this replica. Cleared on\n * cleanup so the service's discovery footprint shrinks atomically\n * with the docker network teardown. Empty when the service has no\n * Service Connect / ServiceRegistries OR when `discovery` was not\n * supplied at startEcsService time.\n */\n cloudMapHandles: RegistrationHandle[];\n /**\n * Issue #86 v1 — owner key this replica's endpoint is registered under in\n * every front-door pool (`<serviceLogicalId>:r<index>`). Set after the\n * replica publishes its ephemeral host port; used by the restart / shutdown\n * paths to `pool.unregister` symmetrically. `undefined` when the service has\n * no front-door OR the replica hasn't published yet.\n */\n frontDoorOwnerKey: string | undefined;\n /**\n * In-flight `bootReplica()` promise when the watcher loop is mid-\n * restart (between the old state's cleanup and the new state being\n * fully populated). `ServiceController.shutdown()` awaits this BEFORE\n * iterating `instance.state.replicas` for cleanup — otherwise a\n * SIGTERM that lands between `instance.state = createEcsRunState()`\n * and `bootReplica()` finishing would call `cleanupEcsRun()` against\n * a freshly-allocated empty state while the in-flight boot was still\n * populating `instance.state.network` / `startedContainers`,\n * leaking the just-created docker network + sidecar.\n *\n * `undefined` when the replica is not currently restarting (steady\n * state — watching the running container). Declared as\n * `Promise<void> | undefined` (not `?:`) so the runner's\n * `instance.inFlightBoot = undefined` reset compiles under\n * `exactOptionalPropertyTypes`.\n */\n inFlightBoot: Promise<void> | undefined;\n /**\n * Last error from a failed run, if any. Surfaced in the shutdown\n * summary so users know why a degraded service ended up degraded.\n */\n lastError?: Error;\n}\n\nexport interface ServiceRunState {\n /** All currently-tracked replicas (active OR shutting down). */\n replicas: ServiceReplicaInstance[];\n /** When true the watcher loop stops triggering restarts. */\n shuttingDown: boolean;\n}\n\nexport function createServiceRunState(): ServiceRunState {\n return { replicas: [], shuttingDown: false };\n}\n\n/**\n * Compute the effective replica count for a service: the smaller of\n * `service.desiredCount` and `--max-tasks`, floored at 1. Pure-\n * functional so the CLI can show the user what cdk-local is about to do\n * before any docker calls fire.\n */\nexport function computeReplicaCount(desiredCount: number, maxTasks: number): number {\n if (maxTasks < 1) {\n throw new EcsServiceRunnerError(\n `--max-tasks must be >= 1 (got ${maxTasks}); local dev needs at least one running replica.`\n );\n }\n if (desiredCount <= 0) return 1;\n return Math.min(desiredCount, maxTasks);\n}\n\n/**\n * Exponential backoff schedule: 1s, 2s, 4s, 8s, 16s, 30s, 30s, ... Used\n * between restarts of a crash-looping replica so docker is not hammered\n * by the watcher loop. Exposed for unit testing.\n */\nexport function backoffDelayMs(restartCount: number): number {\n const base = 1000;\n const cap = 30_000;\n const factor = Math.pow(2, Math.min(restartCount, 10));\n return Math.min(base * factor, cap);\n}\n\n/**\n * Maximum number of replica indices the per-replica subnet allocator\n * can serve without modulo-wrap collision. The allocator below walks\n * the link-local /24 range `169.254.170.0..169.254.253.0` (84 octets)\n * and **skips 171** because that octet is owned by the shared-service\n * network in design § 5 Option A (see `SHARED_SVC_SUBNET_OCTET`), so\n * the usable count is 83. The CLI's `--max-tasks` parser enforces this\n * cap before any boot work fires.\n */\nexport const SUBNET_ALLOCATOR_RANGE = 83;\n\n/**\n * Defensive per-replica subnet octet allocator (Issue #544). Only used\n * when callers bypass the CLI's `sharedNetwork` construction — i.e.\n * test paths that hand-build `ServiceRunnerOptions.discovery` without\n * `sharedNetwork`, or the bare `cdkl run-task`-shaped path that\n * runs one network per task. Production `cdkl start-service`\n * runs always go through the shared network (design § 5 Option A) so\n * this allocator is unreachable in the standard path.\n *\n * Returns the second-from-last octet of the per-replica /24 (170 →\n * `169.254.170.0/24`). Walks the 83-slot output range\n * `[170, 172, 173, ..., 253]` — 171 is intentionally **skipped**\n * because it's reserved for the shared-service network sidecar\n * (`SHARED_SVC_SUBNET_OCTET`), and assigning a per-replica network\n * the same /24 would have docker reject the duplicate-subnet\n * `network create` with the cryptic \"Pool overlaps with other one on\n * this address space\" error.\n */\nexport function pickSubnetOctet(index: number): number {\n const slot = ((index % SUBNET_ALLOCATOR_RANGE) + SUBNET_ALLOCATOR_RANGE) % SUBNET_ALLOCATOR_RANGE;\n // Output sequence: index 0 -> 170, index 1 -> 172, index 2 -> 173, ...\n // The range [170..253] has 84 entries; we drop one (SHARED_SVC_SUBNET_OCTET)\n // to keep every replica's subnet disjoint from the shared-service network.\n const base = 170;\n const candidate = base + slot;\n // Skip SHARED_SVC_SUBNET_OCTET by shifting every slot >= its offset\n // up by one. With base=170 and SHARED_SVC_SUBNET_OCTET=171, this\n // collapses to \"slot 0 -> 170; slot 1+ -> 172..253\" but stays\n // defensive if either constant moves in the future.\n return candidate < SHARED_SVC_SUBNET_OCTET ? candidate : candidate + 1;\n}\n\n/**\n * Decide whether a replica that just exited should restart. Pure-\n * functional so the watcher loop's policy is easy to unit-test.\n */\nexport function shouldRestart(\n exitCode: number,\n policy: ServiceRunnerOptions['restartPolicy']\n): boolean {\n if (policy === 'none') return false;\n if (policy === 'always') return true;\n return exitCode !== 0;\n}\n\n/**\n * Long-running entry point. Boots `replicaCount` instances of the\n * service's task descriptor, returns a controller object the CLI uses\n * to (1) wait for the first failure that gives up restarting and (2)\n * shut every replica down on SIGINT / SIGTERM.\n *\n * The returned `shutdown()` is idempotent and safe to call from\n * multiple SIGINT handlers (CLI's single-flight pattern wraps it\n * anyway).\n */\nexport async function startEcsService(\n service: ResolvedEcsService,\n options: ServiceRunnerOptions,\n runState: ServiceRunState\n): Promise<ServiceController> {\n const logger = getLogger().child('ecs-service');\n for (const w of service.warnings) logger.warn(w);\n\n const replicaCount = computeReplicaCount(service.desiredCount, options.maxTasks);\n if (replicaCount < service.desiredCount) {\n logger.warn(\n `Service '${service.serviceName}' template DesiredCount=${service.desiredCount} exceeds ` +\n `--max-tasks=${options.maxTasks}; running ${replicaCount} replica(s) locally. ` +\n 'Raise --max-tasks to lift the cap, or accept the reduced concurrency for local dev.'\n );\n }\n logger.info(\n `Starting ECS service '${service.serviceName}' with ${replicaCount} replica(s) ` +\n `(restartPolicy=${options.restartPolicy})`\n );\n\n // Boot each replica sequentially so a first-replica failure surfaces\n // before we spend `docker run` budget on the rest. Once all are up\n // the watcher loop monitors them concurrently.\n for (let i = 0; i < replicaCount; i++) {\n const instance: ServiceReplicaInstance = {\n index: i,\n state: createEcsRunState(),\n restartCount: 0,\n shuttingDown: false,\n inFlightBoot: undefined,\n cloudMapHandles: [],\n frontDoorOwnerKey: undefined,\n };\n runState.replicas.push(instance);\n // Track the in-flight boot so a concurrent shutdown awaits it\n // before iterating `instance.state` for cleanup (same contract\n // as the watcher's restart branch — see `watchReplica` below).\n const bootPromise = bootReplica(service, options, instance);\n instance.inFlightBoot = bootPromise;\n try {\n await bootPromise;\n } catch (err) {\n // Boot failure of the FIRST replica is fatal — there is no\n // healthy replica to fall back to, and the runner contract is\n // \"every replica is running before startEcsService returns\".\n instance.lastError = err instanceof Error ? err : new Error(String(err));\n throw new EcsServiceRunnerError(\n `Failed to boot replica ${i} of service '${service.serviceName}': ` +\n `${instance.lastError.message}`\n );\n } finally {\n instance.inFlightBoot = undefined;\n }\n }\n\n // Wire each replica's exit-handler ONCE the boot is complete. The\n // watcher fires on essential-container exit and decides whether to\n // restart per `restartPolicy`.\n for (const instance of runState.replicas) {\n void watchReplica(service, options, instance, runState);\n }\n\n // Return the controller. The CLI keeps this alive until SIGINT.\n return new ServiceController(service, runState, options);\n}\n\n/**\n * Public controller surface. The CLI awaits `controller.waitForShutdown()`\n * to block until the user ^Cs. `controller.shutdown()` is wired into the\n * SIGINT / SIGTERM handlers.\n */\nexport class ServiceController {\n // Note: declared as plain fields (not parameter properties) because\n // `erasableSyntaxOnly` rejects `public readonly` constructor parameter\n // shorthand. The CLI reads `service` / `runState` / `options` so they\n // stay public-readable; runtime immutability is not enforced (TS-only\n // discipline).\n readonly service: ResolvedEcsService;\n readonly runState: ServiceRunState;\n readonly options: ServiceRunnerOptions;\n private shutdownResolve: (() => void) | undefined;\n private shutdownPromise: Promise<void>;\n /**\n * Single-flight wrapper for `shutdown()` so the fan-out cleanup runs\n * exactly once even when SIGINT and the CLI's outer `finally` both\n * fire (the canonical pattern documented in\n * `feedback_sigint_finally_cleanup_singleflight.md`). Built in the\n * constructor so every call to `shutdown()` resolves against the same\n * underlying promise.\n */\n private readonly runShutdown: () => Promise<void>;\n\n constructor(\n service: ResolvedEcsService,\n runState: ServiceRunState,\n options: ServiceRunnerOptions\n ) {\n this.service = service;\n this.runState = runState;\n this.options = options;\n this.shutdownPromise = new Promise<void>((resolve) => {\n this.shutdownResolve = resolve;\n });\n this.runShutdown = singleFlight(() => this.doShutdown());\n }\n\n /**\n * Returns the count of currently-active (non-shutting-down) replicas.\n * Exposed so the CLI can surface a one-line \"service is degraded\"\n * banner when restarts stop firing.\n */\n activeReplicaCount(): number {\n return this.runState.replicas.filter((r) => !r.shuttingDown).length;\n }\n\n /**\n * Block until `shutdown()` is called. Used by the CLI as the\n * long-running blocking point — the SIGINT handler resolves it.\n */\n waitForShutdown(): Promise<void> {\n return this.shutdownPromise;\n }\n\n /**\n * Idempotent fan-out shutdown across every active replica. Wired into\n * both SIGINT and the outer `finally` of the CLI command; the\n * `singleFlight`-wrapped `runShutdown` collapses concurrent / repeated\n * callers to one underlying invocation.\n */\n async shutdown(): Promise<void> {\n await this.runShutdown();\n return this.shutdownPromise;\n }\n\n private async doShutdown(): Promise<void> {\n this.runState.shuttingDown = true;\n const logger = getLogger().child('ecs-service');\n logger.info(`Shutting down service '${this.service.serviceName}'...`);\n\n // Mark every replica as shutting-down BEFORE awaiting cleanup so\n // an in-flight watcher restart cannot resurrect it mid-cleanup.\n for (const r of this.runState.replicas) r.shuttingDown = true;\n\n // CRITICAL: await every in-flight `bootReplica()` BEFORE iterating\n // `instance.state` for cleanup. The watcher loop's restart branch\n // assigns `instance.state = createEcsRunState()` and then awaits\n // `bootReplica()` — if SIGTERM lands between those two lines, the\n // cleanup loop would call `cleanupEcsRun()` against the freshly-\n // allocated empty state while `bootReplica()` is still populating\n // it (creating a docker network + sidecar that nobody tracks).\n // Settle every in-flight boot first so cleanup sees the populated\n // state. `Promise.allSettled` because we don't care whether the\n // boot succeeded — the goal is to wait until the state is no\n // longer being mutated.\n const inFlightBoots = this.runState.replicas\n .map((r) => r.inFlightBoot)\n .filter((p): p is Promise<void> => p !== undefined);\n if (inFlightBoots.length > 0) {\n logger.debug(\n `Awaiting ${inFlightBoots.length} in-flight bootReplica() call(s) before cleanup...`\n );\n await Promise.allSettled(inFlightBoots);\n }\n\n await Promise.allSettled(\n this.runState.replicas.map(async (instance) => {\n // Issue #460 — drop every Cloud Map registration for this\n // replica BEFORE tearing the network down so a peer service\n // observing the registry during shutdown doesn't briefly see\n // an unreachable endpoint.\n if (this.options.discovery) {\n for (const handle of instance.cloudMapHandles) {\n try {\n this.options.discovery.registry.unregister(handle);\n } catch {\n /* registry op is sync + best-effort */\n }\n }\n instance.cloudMapHandles = [];\n }\n // Issue #86 v1 — drop this replica from every front-door pool.\n unregisterReplicaFromFrontDoor(instance, this.options.frontDoor);\n try {\n await cleanupEcsRun(instance.state, {\n keepRunning: this.options.taskOptions.keepRunning,\n });\n } catch (err) {\n logger.debug(\n `Replica ${instance.index} cleanup failed: ` +\n `${err instanceof Error ? err.message : String(err)}`\n );\n }\n })\n );\n this.shutdownResolve?.();\n }\n}\n\n/**\n * Build the `--network-alias` map for one service's containers (design\n * doc § 5 Option A). For every Service Connect entry, attach the\n * fqdn (`<discoveryName>.<namespaceName>`), the bare discoveryName,\n * AND every ClientAlias DnsName to the container that owns the\n * matching PortName. Other containers in the task get NO extra\n * aliases (only their default `--name`-derived alias from\n * `buildDockerRunArgs`).\n *\n * Aliases per container are de-duplicated so docker doesn't reject\n * a `--network-alias X` repeated against the same container.\n *\n * Returns an empty map when the service has no Service Connect — the\n * runner's `... .size > 0 ? { networkAliasesByContainer } : {}` guard\n * short-circuits in that case so backward-compat callers pay no cost.\n */\nexport function buildNetworkAliasesByContainer(\n service: ResolvedEcsService\n): Map<string, ReadonlyArray<string>> {\n const out = new Map<string, string[]>();\n const sc = service.serviceConnect;\n if (!sc) return out as Map<string, ReadonlyArray<string>>;\n\n // PortName → container that declared it. AWS Service Connect uses\n // the first matching PortMappings[].Name to bind a service to a\n // container; cdk-local mirrors that. The resolver already throws\n // `EcsTaskResolutionError` on PortName mismatch BEFORE this runs\n // (`ecs-service-resolver.ts` `extractServiceConnect`), so `owner`\n // is always defined here in production. The defensive `continue`\n // below keeps the helper testable in isolation (callers that hand\n // in a service with a deliberately mismatched PortName, which the\n // unit tests do) without throwing twice from two layers.\n for (const entry of sc.services) {\n const owner = service.task.containers.find((c) =>\n c.portMappings.some((pm) => pm.name === entry.portName)\n );\n if (!owner) continue;\n const aliases: string[] = [];\n aliases.push(entry.discoveryName);\n aliases.push(`${entry.discoveryName}.${sc.namespaceName}`);\n for (const ca of entry.clientAliases) {\n if (ca.dnsName) aliases.push(ca.dnsName);\n }\n const existing = out.get(owner.name) ?? [];\n for (const a of aliases) {\n if (!existing.includes(a)) existing.push(a);\n }\n out.set(owner.name, existing);\n }\n return out as Map<string, ReadonlyArray<string>>;\n}\n\n/**\n * Boot a single replica. Mutates the supplied `instance.state` so the\n * shutdown path's `cleanupEcsRun(instance.state)` covers every partial\n * side effect. Network names are suffixed with the replica index so\n * docker doesn't collide on shared per-task network names when N > 1.\n */\nasync function bootReplica(\n service: ResolvedEcsService,\n options: ServiceRunnerOptions,\n instance: ServiceReplicaInstance\n): Promise<void> {\n const logger = getLogger().child('ecs-service');\n // Per-replica cluster suffix: docker uses the network name as a key,\n // and the existing `createTaskNetwork` already appends a 6-char\n // random suffix, but using a stable replica index in the cluster\n // prefix makes per-replica logs easier to scan and prevents\n // accidental collisions if two replicas start on the same ms.\n const perReplicaCluster = `${options.taskOptions.cluster}-svc-${service.serviceLogicalId.toLowerCase()}-r${instance.index}`;\n const ownerKeyPrefix = `${service.serviceLogicalId}:r${instance.index}`;\n // Build per-boot `--add-host` flags from the registry's current\n // snapshot — every peer service that booted BEFORE this replica is\n // resolvable as `<discoveryName>.<namespace>` and via any bare\n // ClientAlias short-form. Exclude self entries so a service that\n // registers under, say, `frontend.cdkl.local` does not\n // resolve to its own previous replica.\n const addHostFlags = options.discovery?.registry\n ? options.discovery.registry.buildAddHostFlags(ownerKeyPrefix)\n : [];\n // Network strategy:\n // - With a shared discovery network (design § 5 Option A — the\n // CLI-built `cdkl-svc-<rand>` network), every replica\n // joins the SAME docker bridge; peer services are reachable by\n // IP / network alias without cross-network bridging. The\n // per-replica subnet allocator is unused in this mode.\n // - Without a shared network (defensive fallback for callers\n // that bypass the CLI's shared-context construction), the\n // pre-Option-A formula applies: each replica gets a per-replica\n // subnet octet from `pickSubnetOctet()` so concurrent replicas\n // don't collide on a single /24 — but design § 5 Option B\n // already rejected this for cross-service routing reasons.\n // The allocator skips SHARED_SVC_SUBNET_OCTET (Issue #544) so\n // a hand-built ServiceRunnerOptions.discovery that DOES\n // pre-create the shared network doesn't collide on the same\n // /24 here.\n const sharedNetwork = options.discovery?.sharedNetwork;\n const networkAliasesByContainer = buildNetworkAliasesByContainer(service);\n // Issue #585 — when this service boots more than one replica, every\n // replica maps the same container port, so a fixed host-port publish\n // makes the 2nd+ replica fail with `port is already allocated`. Drop\n // the `-p` flag for multi-replica services; peers still reach this\n // service by IP / network alias on the shared docker network. Gated\n // on the EFFECTIVE replica count (clamped by `--max-tasks`), not the\n // raw template DesiredCount — a DesiredCount: 2 service capped to 1\n // replica boots a single container and keeps its host-port publish.\n const replicaCount = computeReplicaCount(service.desiredCount, options.maxTasks);\n const skipHostPortPublish = replicaCount > 1;\n // Issue #86 v1 — ALB front-door. Publish each pool's target container port on\n // an ephemeral host port so the host-side front-door can round-robin to this\n // replica. Distinct ports only (two pools may target the same container port).\n const ephemeralPublishContainerPorts = options.frontDoor\n ? [...new Set(options.frontDoor.pools.map((p) => p.targetContainerPort))]\n : [];\n const perReplicaTaskOptions: RunEcsTaskOptions = {\n ...options.taskOptions,\n cluster: perReplicaCluster,\n // Detach is FORCED true at the runner layer — the service runner\n // takes over essential-container monitoring (so it can restart on\n // exit) rather than letting the task runner block on\n // `waitForContainerExit`. The CLI's `--detach` flag still controls\n // whether the SERVICE runs in the background; the per-replica\n // detach is internal plumbing.\n detach: true,\n ...(skipHostPortPublish ? { skipHostPortPublish: true } : {}),\n ...(sharedNetwork\n ? { existingNetwork: sharedNetwork }\n : { subnetOctet: pickSubnetOctet(instance.index) }),\n ...(addHostFlags.length > 0 ? { addHostFlags } : {}),\n ...(networkAliasesByContainer.size > 0 ? { networkAliasesByContainer } : {}),\n ...(ephemeralPublishContainerPorts.length > 0 ? { ephemeralPublishContainerPorts } : {}),\n };\n logger.info(`Booting replica ${instance.index} (${perReplicaCluster})`);\n await runEcsTask(service.task, perReplicaTaskOptions, instance.state);\n\n // Cloud Map / Service Connect publish (Issue #460). Runs AFTER the\n // task boot so we know docker has assigned an IP. Best-effort: a\n // failed publish logs warn but does NOT abort the replica — the\n // replica is still alive, peer discovery just degrades.\n if (options.discovery) {\n await publishReplicaToCloudMap(service, instance, options.discovery, ownerKeyPrefix);\n }\n\n // Issue #86 v1 — ALB front-door publish. Discover the ephemeral host port\n // each target container port was published on and register it into the pool\n // so the host-side server can round-robin to this replica. Best-effort: a\n // failed lookup logs warn but does NOT abort the replica.\n if (options.frontDoor) {\n await publishReplicaToFrontDoor(\n service,\n instance,\n options.frontDoor,\n options.taskOptions.containerHost,\n ownerKeyPrefix\n );\n }\n}\n\n/**\n * After the replica's main container is up, discover its docker\n * network IP and publish the configured Service Connect + Cloud Map\n * endpoints into the shared registry. The handles are tracked on the\n * instance so the shutdown / restart path can unregister symmetrically.\n *\n * Errors here are best-effort: docker inspect can fail right after run\n * (container vanished, network not fully wired), and the registry is\n * advisory — losing one replica's registration means peer services\n * can't reach it via the overlay, but it doesn't break that replica's\n * own work or AWS SDK calls.\n */\nasync function publishReplicaToCloudMap(\n service: ResolvedEcsService,\n instance: ServiceReplicaInstance,\n discovery: ServiceDiscoveryContext,\n ownerKeyPrefix: string\n): Promise<void> {\n const logger = getLogger().child('ecs-service');\n const networkName = instance.state.network?.networkName;\n if (!networkName) return; // boot didn't get far enough to have a network\n\n // Pick the canonical container — Service Connect uses the producer\n // TaskDef's first essential container, mirroring AWS's ECS Agent.\n // The container's docker name is recorded in startedContainers.\n const essential = service.task.containers.find((c) => c.essential) ?? service.task.containers[0];\n if (!essential) return;\n const started = instance.state.startedContainers.find((c) => c.name === essential.name);\n if (!started) return;\n\n let ip: string | undefined;\n try {\n ip = await getContainerNetworkIp(started.id, networkName);\n } catch (err) {\n logger.warn(\n `Replica ${instance.index}: docker inspect failed before Cloud Map publish: ` +\n `${err instanceof Error ? err.message : String(err)}`\n );\n return;\n }\n if (!ip) {\n logger.warn(\n `Replica ${instance.index}: no docker IP discovered on network ${networkName}; ` +\n 'skipping Cloud Map publish for this replica.'\n );\n return;\n }\n\n // Publish Service Connect entries. Each one carries:\n // - canonical fqdn `<discoveryName>.<namespace>` (always)\n // - bare alias `<dnsName>` for every ClientAlias with a DnsName\n if (service.serviceConnect) {\n const ns = service.serviceConnect.namespaceName;\n // Validate against the cloud-map index. The CLI passes the index\n // for the stack the service belongs to; an unmatched namespace\n // surfaces as a warn — registration still proceeds against the\n // literal name (so a CFn-but-not-CDK consumer that hand-rolled a\n // namespace can still discover the producer).\n const index = discovery.cloudMapIndexByStack.get(service.stack.stackName);\n if (index && !index.namespacesByName.has(ns)) {\n logger.warn(\n `ECS Service '${service.serviceLogicalId}' ServiceConnectConfiguration.Namespace='${ns}' ` +\n 'does not match any AWS::ServiceDiscovery::PrivateDnsNamespace declared in stack ' +\n `${service.stack.stackName}. Publishing under the literal name anyway; peer services ` +\n 'using the same literal will still discover this endpoint.'\n );\n }\n let i = 0;\n for (const entry of service.serviceConnect.services) {\n const ownerKey = `${ownerKeyPrefix}:sc:${i}`;\n const handle = discovery.registry.register(ns, entry.discoveryName, {\n ip,\n port: entry.containerPort,\n ownerKey,\n });\n instance.cloudMapHandles.push(handle);\n // Each ClientAlias with a DnsName becomes a bare-name alias\n // pointing at this fqdn.\n for (const alias of entry.clientAliases) {\n if (alias.dnsName) {\n discovery.registry.registerAlias(alias.dnsName, handle.fqdn);\n }\n }\n i++;\n }\n }\n\n // Publish ServiceRegistries[] entries. Each one references a\n // same-stack AWS::ServiceDiscovery::Service whose namespace +\n // discovery name we resolved at index-build time.\n if (service.serviceRegistries.length > 0) {\n const index = discovery.cloudMapIndexByStack.get(service.stack.stackName);\n if (!index) {\n logger.warn(\n `ECS Service '${service.serviceLogicalId}' declares ServiceRegistries[] but ${getEmbedConfig().productName} has ` +\n `no Cloud Map index for stack ${service.stack.stackName}. Skipping registration.`\n );\n return;\n }\n let j = 0;\n for (const reg of service.serviceRegistries) {\n const cm = index.servicesByLogicalId.get(reg.cloudMapServiceLogicalId);\n if (!cm) {\n logger.warn(\n `ECS Service '${service.serviceLogicalId}' ServiceRegistries[].cloudMapServiceLogicalId=` +\n `'${reg.cloudMapServiceLogicalId}' did not resolve to an AWS::ServiceDiscovery::Service ` +\n `in stack ${service.stack.stackName}. Skipping this registration.`\n );\n continue;\n }\n // Resolve port: explicit `ContainerPort` override > the\n // essential container's first port mapping. AWS-side\n // `ServiceRegistries[].ContainerName` (the sibling override\n // that says \"register THIS container's IP rather than the\n // essential one\") is intentionally IGNORED in v1 — every\n // container in the task shares the same docker network IP\n // (shared-network mode, design § 5 Option A), so picking a\n // different container would resolve to the same address.\n // Multi-IP-per-task is the `awsvpc` mode case which is itself\n // deferred to [#461]. If a sibling container exposes a\n // different port-mapping that the user wants registered, file\n // a follow-up — the in-process registry's `register()` API can\n // take the port verbatim once the resolver surfaces it.\n let port = reg.containerPort;\n if (port === undefined && essential.portMappings.length > 0) {\n port = essential.portMappings[0]!.containerPort;\n }\n if (port === undefined) {\n logger.warn(\n `ECS Service '${service.serviceLogicalId}' ServiceRegistries[] entry for Cloud Map ` +\n `service '${cm.logicalId}' has no resolvable container port; skipping.`\n );\n continue;\n }\n const ownerKey = `${ownerKeyPrefix}:sr:${j}`;\n const handle = discovery.registry.register(cm.namespaceName, cm.name, {\n ip,\n port,\n ownerKey,\n });\n instance.cloudMapHandles.push(handle);\n j++;\n }\n }\n}\n\n/**\n * Issue #86 v1 — register this replica's host-reachable endpoint into every\n * front-door pool. For each pool, find the target container's docker id, read\n * back the ephemeral host port docker assigned to its target container port\n * (`-p <host>::<port>`), and register `<containerHost>:<hostPort>` under the\n * per-replica owner key.\n *\n * Best-effort, mirroring `publishReplicaToCloudMap`: a missing container / port\n * logs a warn and skips that pool rather than aborting the replica (the\n * replica is alive; the front-door just can't route to it until it re-boots).\n * The owner key is stamped on the instance so the restart / shutdown paths can\n * `pool.unregister` symmetrically.\n */\nasync function publishReplicaToFrontDoor(\n service: ResolvedEcsService,\n instance: ServiceReplicaInstance,\n frontDoor: FrontDoorRunnerContext,\n containerHost: string,\n ownerKeyPrefix: string\n): Promise<void> {\n const logger = getLogger().child('ecs-service');\n instance.frontDoorOwnerKey = ownerKeyPrefix;\n for (const target of frontDoor.pools) {\n const started = instance.state.startedContainers.find(\n (c) => c.name === target.targetContainerName\n );\n if (!started) {\n logger.warn(\n `ECS Service '${service.serviceLogicalId}' front-door: container ` +\n `'${target.targetContainerName}' did not start for replica ${instance.index}; ` +\n 'the front-door cannot route to it.'\n );\n continue;\n }\n let hostPort: number | undefined;\n try {\n hostPort = await getPublishedHostPort(started.id, target.targetContainerPort);\n } catch (err) {\n logger.warn(\n `Replica ${instance.index}: docker inspect failed before front-door publish: ` +\n `${err instanceof Error ? err.message : String(err)}`\n );\n continue;\n }\n if (hostPort === undefined) {\n logger.warn(\n `Replica ${instance.index}: container port ${target.targetContainerPort} on ` +\n `'${target.targetContainerName}' was not published on a host port; ` +\n 'skipping front-door registration for this replica.'\n );\n continue;\n }\n target.pool.register(ownerKeyPrefix, { host: containerHost, port: hostPort });\n logger.debug(\n `Front-door: replica ${instance.index} registered at ${containerHost}:${hostPort} ` +\n `(container ${target.targetContainerName}:${target.targetContainerPort}).`\n );\n }\n}\n\n/**\n * Drop this replica's endpoint from every front-door pool. Idempotent; called\n * from the watcher restart branch and the controller shutdown path so a\n * dying / restarting replica is removed from round-robin before its container\n * is torn down.\n */\nfunction unregisterReplicaFromFrontDoor(\n instance: ServiceReplicaInstance,\n frontDoor: FrontDoorRunnerContext | undefined\n): void {\n if (!frontDoor || !instance.frontDoorOwnerKey) return;\n for (const target of frontDoor.pools) {\n target.pool.unregister(instance.frontDoorOwnerKey);\n }\n instance.frontDoorOwnerKey = undefined;\n}\n\n/**\n * Long-running watcher loop for one replica. Polls the essential\n * container's exit code via `docker wait`; on exit, decides whether to\n * restart per `restartPolicy` + applies exponential backoff. The loop\n * exits only when the replica's `shuttingDown` flag is set.\n */\nasync function watchReplica(\n service: ResolvedEcsService,\n options: ServiceRunnerOptions,\n instance: ServiceReplicaInstance,\n runState: ServiceRunState\n): Promise<void> {\n const logger = getLogger().child('ecs-service');\n while (!instance.shuttingDown && !runState.shuttingDown) {\n const essentialId = pickEssentialContainerId(instance, service);\n if (!essentialId) {\n // The container exited and was cleaned up between iterations of\n // the loop; the previous restart branch will have been the cause.\n // Break and let the outer loop's restart branch re-enter.\n await sleep(500);\n continue;\n }\n let exitCode: number;\n try {\n exitCode = await waitForExitImpl(essentialId);\n } catch (err) {\n // `docker wait` failures (e.g. container already removed) are\n // surfaced as \"exited with -1\" — same shape as the runner's\n // wait helper so the restart branch's decision is consistent.\n logger.debug(\n `docker wait failed for replica ${instance.index}: ` +\n `${err instanceof Error ? err.message : String(err)}`\n );\n exitCode = -1;\n }\n if (instance.shuttingDown || runState.shuttingDown) return;\n\n logger.warn(\n `Replica ${instance.index} essential container exited with code ${exitCode} ` +\n `(restartCount=${instance.restartCount}).`\n );\n // Surface the container's log tail so the user sees WHY it exited\n // (run-task streams logs in the foreground, but detached service\n // replicas otherwise leave the exit unexplained). Dump on the first\n // failure and on the terminal degraded exit, but NOT on every\n // restart cycle of a crash loop.\n const willRestart = shouldRestart(exitCode, options.restartPolicy);\n if (!willRestart || instance.restartCount === 0) {\n await printExitedContainerLogs(instance.index, essentialId, logger);\n }\n if (!willRestart) {\n logger.warn(\n `Replica ${instance.index} not restarting (policy=${options.restartPolicy}, ` +\n `exit=${exitCode}). Service running in degraded mode.`\n );\n // Mark this replica as shutting-down so the controller's\n // `activeReplicaCount` reflects the degradation but DO NOT call\n // cleanupEcsRun here — the controller's shutdown path is the\n // single owner of teardown, and racing it from the watcher\n // corrupts the shared run-state via the same SIGINT-during-\n // cleanup pattern that `feedback_sigint_finally_cleanup_singleflight.md`\n // documents.\n instance.shuttingDown = true;\n return;\n }\n\n // Backoff before restarting.\n const delay = backoffDelayMs(instance.restartCount);\n logger.info(`Restarting replica ${instance.index} in ${delay}ms...`);\n await sleep(delay);\n if (instance.shuttingDown || runState.shuttingDown) return;\n\n // Drop Cloud Map registrations from the dying replica before its\n // network teardown — peers should not route to the about-to-be-\n // killed container.\n if (options.discovery) {\n for (const handle of instance.cloudMapHandles) {\n try {\n options.discovery.registry.unregister(handle);\n } catch {\n /* sync + best-effort */\n }\n }\n instance.cloudMapHandles = [];\n }\n // Issue #86 v1 — drop this replica from the front-door round-robin before\n // its container is torn down so in-flight requests aren't routed to a\n // dying replica.\n unregisterReplicaFromFrontDoor(instance, options.frontDoor);\n\n // Tear down the old per-replica run-state before re-booting (else\n // the new boot collides on the docker network name).\n try {\n await cleanupEcsRun(instance.state, {\n keepRunning: false, // restart MUST clean the dead containers regardless of --keep-running\n });\n } catch (err) {\n logger.debug(\n `Replica ${instance.index} pre-restart cleanup failed: ` +\n `${err instanceof Error ? err.message : String(err)}`\n );\n }\n instance.state = createEcsRunState();\n instance.restartCount += 1;\n\n // Race-safety: `instance.state = createEcsRunState()` above + the\n // upcoming `bootReplica()` populating it is the SIGTERM-mid-restart\n // hazard. Track the in-flight boot so the controller's `shutdown()`\n // can `Promise.allSettled` against it BEFORE iterating the\n // replica's state for cleanup — otherwise the cleanup loop would\n // race the boot and orphan the freshly-created docker network +\n // sidecar.\n const bootPromise = bootReplica(service, options, instance);\n instance.inFlightBoot = bootPromise;\n try {\n await bootPromise;\n } catch (err) {\n instance.lastError = err instanceof Error ? err : new Error(String(err));\n logger.error(\n `Replica ${instance.index} restart failed: ` +\n `${instance.lastError.message}. Service running in degraded mode.`\n );\n // Same single-owner rule as above — mark and exit, don't\n // cleanup from the watcher.\n instance.shuttingDown = true;\n return;\n } finally {\n instance.inFlightBoot = undefined;\n }\n }\n}\n\nfunction pickEssentialContainerId(\n instance: ServiceReplicaInstance,\n service?: ResolvedEcsService\n): string | undefined {\n // Mirror the task runner's essential-container selection: first\n // container marked `essential: true`, else first container in\n // template order. The task runner records started containers in\n // start order (dependency-resolved), so we walk the service's task\n // descriptor (in template order) to find the first essential one\n // and look it up by name in `startedContainers`.\n if (service) {\n const essential =\n service.task.containers.find((c) => c.essential) ?? service.task.containers[0];\n if (essential) {\n const started = instance.state.startedContainers.find((c) => c.name === essential.name);\n if (started) return started.id;\n }\n }\n // Fallback: first started container. Used when the service handle\n // isn't threaded through (test-only paths).\n return instance.state.startedContainers[0]?.id;\n}\n\n/**\n * Production `docker wait <id>` implementation. Captured once so the\n * test override can restore it without duplicating the body.\n */\nconst defaultWaitForExitImpl = async (containerId: string): Promise<number> => {\n const { execFile } = await import('node:child_process');\n const { promisify } = await import('node:util');\n const { getDockerCmd } = await import('../utils/docker-cmd.js');\n const execFileAsync = promisify(execFile);\n const { stdout } = await execFileAsync(getDockerCmd(), ['wait', containerId], {\n maxBuffer: 1024 * 1024,\n });\n const code = parseInt(stdout.trim(), 10);\n return Number.isFinite(code) ? code : -1;\n};\n\n/**\n * `docker wait <id>` returns the exit code on stdout. Extracted as a\n * test-overridable function so unit tests do not need a real container.\n */\nlet waitForExitImpl: (containerId: string) => Promise<number> = defaultWaitForExitImpl;\n\n/**\n * Test-only hook to inject a synthetic exit-code stream without docker.\n * Restores the production implementation when called with `undefined`.\n */\nexport function __setWaitForExitImpl(\n impl: ((containerId: string) => Promise<number>) | undefined\n): void {\n if (impl === undefined) {\n waitForExitImpl = defaultWaitForExitImpl;\n return;\n }\n waitForExitImpl = impl;\n}\n\n/** How many trailing lines of a crashed container's logs to surface. */\nconst EXIT_LOG_TAIL_LINES = 50;\n\n/**\n * Production `docker logs --tail <N> <id>` reader. Captures BOTH streams\n * (apps log to stdout and stderr) so the surfaced tail shows whatever the\n * container printed before exiting.\n */\nconst defaultReadContainerLogsImpl = async (containerId: string): Promise<string> => {\n const { execFile } = await import('node:child_process');\n const { promisify } = await import('node:util');\n const { getDockerCmd } = await import('../utils/docker-cmd.js');\n const execFileAsync = promisify(execFile);\n const { stdout, stderr } = await execFileAsync(\n getDockerCmd(),\n ['logs', '--tail', String(EXIT_LOG_TAIL_LINES), containerId],\n { maxBuffer: 4 * 1024 * 1024 }\n );\n return [stdout, stderr].filter((s) => s.length > 0).join('\\n');\n};\n\n/**\n * Surface the tail of a just-exited essential container's logs so the\n * user sees WHY it stopped (e.g. an app's startup DB-connection error)\n * without manually running `docker logs`. Best-effort: a read failure or\n * empty output is swallowed (debug-logged) rather than masking the\n * primary exit message.\n *\n * `read` is injectable so the unit test can assert the formatting without\n * a real container; production callers use the default `docker logs`\n * reader.\n */\nexport async function printExitedContainerLogs(\n replicaIndex: number,\n containerId: string,\n logger: { warn: (m: string) => void; debug: (m: string) => void },\n read: (id: string) => Promise<string> = defaultReadContainerLogsImpl\n): Promise<void> {\n let raw: string;\n try {\n raw = await read(containerId);\n } catch (err) {\n logger.debug(\n `Replica ${replicaIndex}: could not read container logs: ${err instanceof Error ? err.message : String(err)}`\n );\n return;\n }\n const tail = raw.trimEnd();\n if (tail.length === 0) return;\n logger.warn(\n `Replica ${replicaIndex} essential container logs (last ${EXIT_LOG_TAIL_LINES} lines):\\n${tail}`\n );\n}\n\nconst defaultSleepImpl = (ms: number): Promise<void> =>\n new Promise((resolve) => setTimeout(resolve, ms));\n\nlet sleepImpl: (ms: number) => Promise<void> = defaultSleepImpl;\n\n/**\n * Test-only hook to short-circuit the restart-backoff sleep in the\n * watcher loop. Production code uses real-time `setTimeout`; the\n * canonical 1s `backoffDelayMs(0)` is too slow for a unit test poll\n * loop that wants to assert `bootCount >= 2` in <100ms.\n *\n * Restores the production `setTimeout` impl when called with `undefined`.\n */\nexport function __setSleepImpl(impl: ((ms: number) => Promise<void>) | undefined): void {\n if (impl === undefined) {\n sleepImpl = defaultSleepImpl;\n return;\n }\n sleepImpl = impl;\n}\n\nfunction sleep(ms: number): Promise<void> {\n return sleepImpl(ms);\n}\n","/**\n * Issue #86 v1 — in-process pool of host-reachable replica endpoints behind a\n * local ALB front-door. The service runner registers one entry per replica\n * (the `127.0.0.1:<ephemeralHostPort>` the replica's target container port was\n * published on) as replicas boot, and unregisters on restart / shutdown. The\n * front-door server reads `next()` per request to round-robin across the live\n * set.\n *\n * Modeled on `CloudMapRegistry`: synchronous Map mutations so a `next()` read\n * concurrent with a `register()` / `unregister()` returns a consistent\n * snapshot — never a partially-mutated one. No async / mutex needed.\n *\n * Entries are keyed by `ownerKey` (typically `<serviceLogicalId>:r<index>`) so\n * a replica restart re-registers idempotently and shutdown can drop a single\n * replica without disturbing its peers.\n */\n\nexport interface FrontDoorEndpoint {\n /** Host the replica's target container port was published on (e.g. `127.0.0.1`). */\n host: string;\n /** Docker-assigned ephemeral host port forwarding to the replica container. */\n port: number;\n}\n\ninterface PoolEntry extends FrontDoorEndpoint {\n ownerKey: string;\n}\n\nexport class FrontDoorEndpointPool {\n private entries: PoolEntry[] = [];\n /** Monotonic counter; `next()` rotates over the current entries by index. */\n private cursor = 0;\n\n /**\n * Register (or idempotently replace) the endpoint for `ownerKey`. A replica\n * restart calls this again with the same key and the new ephemeral port; the\n * prior entry for that key is replaced rather than duplicated.\n */\n register(ownerKey: string, endpoint: FrontDoorEndpoint): void {\n if (!ownerKey) throw new Error('FrontDoorEndpointPool.register: ownerKey must be non-empty.');\n const next = this.entries.filter((e) => e.ownerKey !== ownerKey);\n next.push({ ownerKey, host: endpoint.host, port: endpoint.port });\n this.entries = next;\n }\n\n /** Drop the endpoint for `ownerKey`. Idempotent; returns whether one was removed. */\n unregister(ownerKey: string): boolean {\n const next = this.entries.filter((e) => e.ownerKey !== ownerKey);\n const removed = next.length !== this.entries.length;\n this.entries = next;\n return removed;\n }\n\n /**\n * Round-robin the next live endpoint, or `undefined` when the pool is empty\n * (the front-door server replies 503 in that case). The cursor advances per\n * call; modulo by the current length tolerates entries being added / removed\n * between calls.\n */\n next(): FrontDoorEndpoint | undefined {\n if (this.entries.length === 0) return undefined;\n const entry = this.entries[this.cursor % this.entries.length]!;\n this.cursor = (this.cursor + 1) % Number.MAX_SAFE_INTEGER;\n return { host: entry.host, port: entry.port };\n }\n\n /** Snapshot of the current endpoints (for diagnostics / tests). */\n list(): ReadonlyArray<FrontDoorEndpoint> {\n return this.entries.map((e) => ({ host: e.host, port: e.port }));\n }\n\n /** Number of live endpoints. */\n size(): number {\n return this.entries.length;\n }\n}\n","import type { IncomingMessage } from 'node:http';\n\n/**\n * Issue #123 (Lambda-target slice) — translate an inbound HTTP request into the\n * Application Load Balancer Lambda-target invocation event, and translate the\n * handler's response back into HTTP components.\n *\n * This is the **ALB** Lambda event shape, NOT the API Gateway one (`start-api`\n * owns that via `api-gateway-event.ts`). The shapes are deliberately distinct:\n *\n * Request event (confirmed against the AWS docs\n * https://docs.aws.amazon.com/elasticloadbalancing/latest/application/lambda-functions.html):\n *\n * ```\n * {\n * \"requestContext\": { \"elb\": { \"targetGroupArn\": \"<tg-arn>\" } },\n * \"httpMethod\": \"GET\",\n * \"path\": \"/\",\n * // single-value form (default):\n * \"queryStringParameters\": { \"k\": \"v2\" }, // last value wins on dup keys\n * \"headers\": { \"k\": \"v2\" }, // last value wins on dup keys\n * // multi-value form (target-group attribute lambda.multi_value_headers.enabled=true):\n * \"multiValueQueryStringParameters\": { \"k\": [\"v1\", \"v2\"] },\n * \"multiValueHeaders\": { \"k\": [\"v1\", \"v2\"] },\n * \"body\": \"<string>\",\n * \"isBase64Encoded\": false\n * }\n * ```\n *\n * Exactly ONE of the single-value / multi-value variants is present, chosen by\n * the target group's `lambda.multi_value_headers.enabled` attribute. ALB also\n * stamps `x-amzn-trace-id`, `x-forwarded-for`, `x-forwarded-port`, and\n * `x-forwarded-proto` on every request — the front-door server already appends\n * the `x-forwarded-*` set, so the request snapshot reaching this builder carries\n * them.\n *\n * Body base64: when the `content-encoding` header is present, OR the\n * content-type is not one of `text/*`, `application/json`,\n * `application/javascript`, `application/xml`, the body is base64-encoded and\n * `isBase64Encoded` is `true` (mirrors ALB).\n *\n * Response (the handler must return): `{ statusCode, statusDescription?,\n * headers | multiValueHeaders, body?, isBase64Encoded? }`. A malformed response\n * (no numeric `statusCode`, or a Lambda runtime error envelope) maps to HTTP\n * 502 — mirroring how a real ALB answers when a Lambda target returns an\n * invalid response.\n */\n\n/**\n * The HTTP request shape the ALB event-builder consumes. Decoupled from\n * `node:http` so the builder stays pure / unit-testable. Header names are\n * passed in their on-wire case (lowercased by the builder).\n */\nexport interface AlbHttpRequestSnapshot {\n /** HTTP method, uppercased (`GET` / `POST` / ...). */\n method: string;\n /** Full URL path including query string, NOT decoded. e.g. `/items?a=1&a=2`. */\n rawUrl: string;\n /** Headers as a key -> array map (multiple values per name preserved). */\n headers: Record<string, string[]>;\n /** Request body as a Buffer. Empty body -> zero-length Buffer. */\n body: Buffer;\n}\n\n/** Content types ALB treats as text (body sent verbatim, `isBase64Encoded: false`). */\nfunction isTextualContentType(contentType: string): boolean {\n const ct = contentType.toLowerCase();\n return (\n ct.startsWith('text/') ||\n ct.startsWith('application/json') ||\n ct.startsWith('application/javascript') ||\n ct.startsWith('application/xml')\n );\n}\n\n/**\n * Build a `AlbHttpRequestSnapshot` from a live `node:http` request plus the\n * already-buffered body. Header values arrive as `string | string[]`; this\n * normalizes them to `string[]` (preserving multi-value) the builder expects.\n */\nexport function snapshotFromIncoming(req: IncomingMessage, body: Buffer): AlbHttpRequestSnapshot {\n const headers: Record<string, string[]> = {};\n for (const [name, value] of Object.entries(req.headers)) {\n if (value === undefined) continue;\n headers[name] = Array.isArray(value) ? value : [value];\n }\n return {\n method: (req.method ?? 'GET').toUpperCase(),\n rawUrl: req.url ?? '/',\n headers,\n body,\n };\n}\n\n/** Split a raw URL into its path (query-stripped, not decoded) and raw query string. */\nfunction splitRawUrl(rawUrl: string): { path: string; rawQuery: string } {\n const hashIdx = rawUrl.indexOf('#');\n const noHash = hashIdx === -1 ? rawUrl : rawUrl.slice(0, hashIdx);\n const qIdx = noHash.indexOf('?');\n if (qIdx === -1) return { path: noHash, rawQuery: '' };\n return { path: noHash.slice(0, qIdx), rawQuery: noHash.slice(qIdx + 1) };\n}\n\n/**\n * Parse a raw query string into single + multi-value maps. ALB does NOT decode\n * query parameters (the docs explicitly say \"If the query parameters are\n * URL-encoded, the load balancer does not decode them\"), so values are kept\n * verbatim. A bare `?flag` key yields the empty string value.\n */\nfunction parseQuery(rawQuery: string): {\n single: Record<string, string>;\n multi: Record<string, string[]>;\n} {\n const single: Record<string, string> = {};\n const multi: Record<string, string[]> = {};\n if (rawQuery.length === 0) return { single, multi };\n for (const pair of rawQuery.split('&')) {\n if (pair.length === 0) continue;\n const eq = pair.indexOf('=');\n const key = eq === -1 ? pair : pair.slice(0, eq);\n const value = eq === -1 ? '' : pair.slice(eq + 1);\n // single: last value wins (ALB default-format semantics).\n single[key] = value;\n (multi[key] ??= []).push(value);\n }\n return { single, multi };\n}\n\n/**\n * Build the single + multi-value header maps. Names are lowercased; the single\n * map keeps the LAST value (ALB default-format), the multi map keeps every\n * value in arrival order.\n */\nfunction buildHeaderMaps(headers: Record<string, string[]>): {\n single: Record<string, string>;\n multi: Record<string, string[]>;\n} {\n const single: Record<string, string> = {};\n const multi: Record<string, string[]> = {};\n for (const [name, values] of Object.entries(headers)) {\n const lower = name.toLowerCase();\n const list = values.slice();\n multi[lower] = list;\n if (list.length > 0) single[lower] = list[list.length - 1]!;\n }\n return { single, multi };\n}\n\n/** Header lookup that tolerates the case-folded multi-value request map. */\nfunction firstHeader(headers: Record<string, string[]>, name: string): string | undefined {\n const lower = name.toLowerCase();\n for (const [k, v] of Object.entries(headers)) {\n if (k.toLowerCase() === lower) return v[0];\n }\n return undefined;\n}\n\nexport interface BuildAlbLambdaEventOptions {\n /** The resolved target-group ARN-or-logical-id surfaced under `requestContext.elb`. */\n targetGroupArn: string;\n /**\n * Whether the target group has `lambda.multi_value_headers.enabled=true`.\n * `true` -> emit `multiValueHeaders` / `multiValueQueryStringParameters`;\n * `false` (default) -> emit `headers` / `queryStringParameters` (last-wins).\n */\n multiValueHeaders: boolean;\n}\n\n/**\n * Build the ALB Lambda-target invocation event from an HTTP request snapshot.\n * Emits exactly the single-value OR multi-value variant per\n * `opts.multiValueHeaders`.\n */\nexport function buildAlbLambdaEvent(\n req: AlbHttpRequestSnapshot,\n opts: BuildAlbLambdaEventOptions\n): Record<string, unknown> {\n const { path, rawQuery } = splitRawUrl(req.rawUrl);\n const query = parseQuery(rawQuery);\n const headerMaps = buildHeaderMaps(req.headers);\n\n const contentEncoding = firstHeader(req.headers, 'content-encoding');\n const contentType = firstHeader(req.headers, 'content-type') ?? '';\n const isBase64Encoded =\n req.body.length > 0 &&\n (contentEncoding !== undefined ? true : !isTextualContentType(contentType));\n const body = isBase64Encoded ? req.body.toString('base64') : req.body.toString('utf-8');\n\n const event: Record<string, unknown> = {\n requestContext: { elb: { targetGroupArn: opts.targetGroupArn } },\n httpMethod: req.method,\n path,\n isBase64Encoded,\n body,\n };\n\n if (opts.multiValueHeaders) {\n event['multiValueHeaders'] = headerMaps.multi;\n event['multiValueQueryStringParameters'] = query.multi;\n } else {\n event['headers'] = headerMaps.single;\n event['queryStringParameters'] = query.single;\n }\n\n return event;\n}\n\n/** HTTP response components translated from a Lambda ALB-target response. */\nexport interface TranslatedAlbResponse {\n statusCode: number;\n /** Optional human status line (ALB `statusDescription`); undefined -> Node default. */\n statusDescription?: string;\n /**\n * Headers as a key -> array map so the server can emit one wire header per\n * value (e.g. multiple `set-cookie` lines). Names are lowercased.\n */\n headers: Record<string, string[]>;\n /** Body bytes ready to write to the socket. */\n body: Buffer;\n}\n\n/** A Lambda RIE error envelope (`{errorMessage, errorType, ...}`) with no statusCode. */\nfunction isErrorEnvelope(payload: unknown): boolean {\n if (!payload || typeof payload !== 'object' || Array.isArray(payload)) return false;\n const obj = payload as Record<string, unknown>;\n if ('statusCode' in obj) return false;\n return typeof obj['errorMessage'] === 'string';\n}\n\n/**\n * The canonical 502 a real ALB returns when a Lambda target's response is\n * malformed (missing/invalid `statusCode`, non-object payload, or a runtime\n * error envelope). Plain-text body, mirroring ALB's own 502 page shape closely\n * enough for local dev.\n */\nfunction badGatewayResponse(): TranslatedAlbResponse {\n const body = Buffer.from('<html><body><h1>502 Bad Gateway</h1></body></html>', 'utf-8');\n return {\n statusCode: 502,\n statusDescription: '502 Bad Gateway',\n headers: {\n 'content-type': ['text/html'],\n 'content-length': [String(body.length)],\n },\n body,\n };\n}\n\nfunction stringifyHeaderValue(value: unknown): string {\n if (value === null || value === undefined) return '';\n if (typeof value === 'string') return value;\n if (typeof value === 'number' || typeof value === 'boolean' || typeof value === 'bigint') {\n return String(value);\n }\n return JSON.stringify(value) ?? '';\n}\n\n/**\n * Translate a Lambda ALB-target response payload into HTTP components.\n *\n * A well-formed response is an object with a numeric `statusCode`. Headers come\n * from `headers` (single-value) and/or `multiValueHeaders` (array-valued); ALB\n * accepts either regardless of the request-side multi-value setting, so both\n * are honored here (multiValueHeaders extend / append to the single map).\n * `body` is optional; `isBase64Encoded: true` means the body is base64 and is\n * decoded to raw bytes.\n *\n * Anything else -> 502 (matching a real ALB), incl. a runtime error envelope.\n */\nexport function translateAlbLambdaResponse(payload: unknown): TranslatedAlbResponse {\n if (isErrorEnvelope(payload)) return badGatewayResponse();\n if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {\n return badGatewayResponse();\n }\n const obj = payload as Record<string, unknown>;\n const statusRaw = obj['statusCode'];\n if (typeof statusRaw !== 'number' || !Number.isFinite(statusRaw)) {\n return badGatewayResponse();\n }\n const statusCode = Math.trunc(statusRaw);\n\n const isBase64 = obj['isBase64Encoded'] === true;\n const rawBody = obj['body'];\n let body: Buffer;\n if (rawBody === undefined || rawBody === null) {\n body = Buffer.alloc(0);\n } else if (typeof rawBody === 'string') {\n body = isBase64 ? Buffer.from(rawBody, 'base64') : Buffer.from(rawBody, 'utf-8');\n } else {\n body = Buffer.from(JSON.stringify(rawBody), 'utf-8');\n }\n\n const headers: Record<string, string[]> = {};\n const addHeader = (name: string, value: string): void => {\n const lower = name.toLowerCase();\n (headers[lower] ??= []).push(value);\n };\n\n const singleHeaders = obj['headers'];\n if (singleHeaders && typeof singleHeaders === 'object' && !Array.isArray(singleHeaders)) {\n for (const [name, value] of Object.entries(singleHeaders as Record<string, unknown>)) {\n addHeader(name, stringifyHeaderValue(value));\n }\n }\n\n const multiHeaders = obj['multiValueHeaders'];\n if (multiHeaders && typeof multiHeaders === 'object' && !Array.isArray(multiHeaders)) {\n for (const [name, values] of Object.entries(multiHeaders as Record<string, unknown>)) {\n if (!Array.isArray(values)) continue;\n for (const v of values) addHeader(name, stringifyHeaderValue(v));\n }\n }\n\n // content-length is informational; emit a correct one from the actual body\n // bytes so a partial / mismatched length from the handler doesn't lie on the\n // wire (ALB recomputes it too).\n headers['content-length'] = [String(body.length)];\n\n const result: TranslatedAlbResponse = { statusCode, headers, body };\n const statusDescription = obj['statusDescription'];\n if (typeof statusDescription === 'string' && statusDescription.length > 0) {\n result.statusDescription = statusDescription;\n }\n return result;\n}\n","import {\n createServer,\n request as httpRequest,\n type IncomingMessage,\n type Server,\n type ServerResponse,\n} from 'node:http';\nimport { createServer as createHttpsServer } from 'node:https';\nimport { getLogger } from '../utils/logger.js';\nimport type { FrontDoorEndpointPool } from './front-door-pool.js';\nimport {\n buildAlbLambdaEvent,\n snapshotFromIncoming,\n translateAlbLambdaResponse,\n type TranslatedAlbResponse,\n} from './alb-lambda-event.js';\n\n/**\n * Host-side ALB front-door. A plain HTTP reverse proxy bound to the ALB's\n * declared listener port that, per request, resolves a {@link RouteAction} and\n * serves it: a `forward` action round-robins a (weighted) replica pool set, a\n * `redirect` answers a 301 / 302 with a synthesized `Location`, and a\n * `fixed-response` answers a synthesized status / body — mirroring what a real\n * ALB does for the matched rule / default action. Lifecycle mirrors\n * `startApiServer` in `http-server.ts`.\n *\n * Route selection is delegated to {@link StartFrontDoorServerOptions.route},\n * which receives the request path AND its `Host` header (host-header rules need\n * the latter):\n * - single default-forward listener -> a constant `forward` action;\n * - a listener with rules -> the matched rule's action, falling back to the\n * default action (`undefined` when neither matches, which the proxy answers\n * with 404 — like an ALB rule miss with no default).\n *\n * A weighted `forward` action's targets are EITHER an ECS replica pool\n * ({@link FrontDoorEndpointPool}) OR a Lambda invoker (#123 Lambda-target slice),\n * and a single forward may mix both. The weighted pick selects one target per\n * request. For an ECS pool, the proxy round-robins a live replica and\n * reverse-proxies the raw HTTP request to `127.0.0.1:<ephemeralPort>` (the\n * daemon-in-a-VM reality on macOS means the host can't reach container IPs\n * directly, so replicas publish their target port on ephemeral host ports). For\n * a Lambda target, the proxy translates the request into the ALB Lambda-target\n * event, invokes the function locally, and translates the response back — a\n * malformed handler response yields 502, mirroring a real ALB.\n *\n * Callers that only ever route to a single ECS-or-Lambda target per path may use\n * the simpler {@link StartFrontDoorServerOptions.selectTarget} /\n * {@link StartFrontDoorServerOptions.selectPool} selectors instead of `route`.\n *\n * Scope: per-request round-robin + weighted forward (ECS pools and/or Lambda\n * invokers), redirect / fixed-response synthesis, every ALB rule-condition\n * field. HTTP and HTTPS listeners are both served — HTTPS termination uses\n * the {@link StartFrontDoorServerOptions.tls} materials (a user-supplied or\n * auto-generated self-signed cert/key pair) and switches `X-Forwarded-Proto`\n * + redirect `#{protocol}` to `https`. No health-check-gated draining, no\n * sticky sessions, no websocket `Upgrade` proxying.\n */\n\n/**\n * A Lambda forward target the front-door invokes locally per request. The\n * `invoke` callback boots-lazily / reuses a warm RIE container under the hood\n * (see `front-door-lambda-runner.ts`); this interface keeps the server decoupled\n * from the container machinery for testability.\n */\nexport interface FrontDoorLambdaTarget {\n /** The resolved target-group ARN-or-id surfaced under `requestContext.elb`. */\n targetGroupArn: string;\n /** Whether the TG has `lambda.multi_value_headers.enabled=true`. */\n multiValueHeaders: boolean;\n /** Invoke the backing Lambda with the ALB event; resolves the parsed payload. */\n invoke: (event: Record<string, unknown>) => Promise<unknown>;\n /** Human label for log lines (e.g. the Lambda logical id). */\n label: string;\n}\n\n/** A resolved front-door dispatch target: an ECS replica pool or a Lambda invoker. */\nexport type FrontDoorDispatchTarget =\n | { kind: 'pool'; pool: FrontDoorEndpointPool }\n | { kind: 'lambda'; lambda: FrontDoorLambdaTarget };\n\n/** One weighted member of a forward action backed by an ECS replica pool. */\nexport interface WeightedPool {\n /** The live-replica pool for one (service, container, port) target group. */\n pool: FrontDoorEndpointPool;\n /** Forward weight (>= 0; weight 0 is never selected, per ALB semantics). */\n weight: number;\n}\n\n/** One weighted member of a forward action backed by a Lambda invoker (#123). */\nexport interface WeightedLambda {\n /** The Lambda invoker for one `TargetType: lambda` target group. */\n lambda: FrontDoorLambdaTarget;\n /** Forward weight (>= 0; weight 0 is never selected, per ALB semantics). */\n weight: number;\n}\n\n/** One weighted forward target: an ECS pool OR a Lambda invoker. */\nexport type WeightedForwardTarget = WeightedPool | WeightedLambda;\n\n/**\n * Optional auth guard the front-door enforces before serving a route action.\n * An ALB `authenticate-cognito` / `authenticate-oidc` action is resolved into\n * one of these by `front-door-auth`. Failing the check answers 401 with a\n * `WWW-Authenticate: Bearer realm=\"...\"` header instead of forwarding.\n */\nexport interface AuthCheck {\n /** Realm for the `WWW-Authenticate: Bearer realm=\"...\"` header on 401. */\n realm: string;\n /**\n * Verify the request. Resolves `{ allow: true }` to serve the action, or\n * `{ allow: false, reason? }` to answer 401 (reason becomes the body when\n * present; otherwise the body is `Unauthorized`).\n */\n check: (headers: NodeJS.Dict<string | string[]>) => Promise<{ allow: boolean; reason?: string }>;\n}\n\n/**\n * A resolved forward action: pick a target by weight, then dispatch it (an ECS\n * pool round-robins a replica; a Lambda invoker is invoked locally). A single\n * forward may mix ECS and Lambda targets.\n */\nexport interface ForwardRouteAction {\n kind: 'forward';\n /** Weighted targets (length >= 1; a single-target forward is one entry, weight 1). */\n pools: WeightedForwardTarget[];\n /** Optional auth guard enforced before serving (set when the rule wrapped an authenticate-* action). */\n auth?: AuthCheck;\n}\n\n/** A resolved redirect action: synthesize a 301 / 302 with a `Location` header. */\nexport interface RedirectRouteAction {\n kind: 'redirect';\n statusCode: 301 | 302;\n protocol?: string;\n host?: string;\n port?: string;\n path?: string;\n query?: string;\n /** Optional auth guard enforced before serving (see {@link ForwardRouteAction.auth}). */\n auth?: AuthCheck;\n}\n\n/** A resolved fixed-response action: synthesize the whole response. */\nexport interface FixedResponseRouteAction {\n kind: 'fixed-response';\n statusCode: number;\n contentType?: string;\n messageBody?: string;\n /** Optional auth guard enforced before serving (see {@link ForwardRouteAction.auth}). */\n auth?: AuthCheck;\n}\n\n/** What the front-door does for a request: forward / redirect / fixed-response. */\nexport type RouteAction = ForwardRouteAction | RedirectRouteAction | FixedResponseRouteAction;\n\n/**\n * The request facts the route resolver is handed (path + Host header + all\n * other ALB-condition-relevant fields). The matcher strips the query for\n * path-pattern matching but uses it for query-string conditions.\n */\nexport interface FrontDoorRouteRequest {\n /** Request URL (path + query); the matcher strips the query for path-pattern. */\n path: string;\n /** Request `Host` header (for host-header rule matching). */\n host?: string;\n /** Raw incoming request headers (for http-header rule matching). */\n headers?: NodeJS.Dict<string | string[]>;\n /** Request method (e.g. `GET`) for http-request-method rule matching. */\n method?: string;\n /** Connection source IP for source-ip rule matching. */\n sourceIp?: string;\n}\n\nexport interface StartFrontDoorServerOptions {\n /**\n * Resolve the action to serve a request, given its path + Host header.\n * Returns `undefined` when no rule matched and there is no default action\n * (the proxy then replies 404). This is the full router: a `forward` action's\n * weighted targets may mix ECS pools and Lambda invokers, plus `redirect` /\n * `fixed-response` actions and host-header matching. Takes precedence over the\n * single-target {@link selectTarget} / {@link selectPool} selectors.\n */\n route?: (req: FrontDoorRouteRequest) => RouteAction | undefined;\n /**\n * Single-target selector (ECS pool only) for a request path. Returns\n * `undefined` -> 404. A convenience for callers that never weight / redirect.\n */\n selectPool?: (requestPath: string) => FrontDoorEndpointPool | undefined;\n /**\n * Generalized single-target selection (#123): choose either an ECS pool or a\n * Lambda invoker for a request path. Returns `undefined` -> 404. When several\n * selectors are provided, `route` wins, then `selectTarget`, then `selectPool`.\n */\n selectTarget?: (requestPath: string) => FrontDoorDispatchTarget | undefined;\n /** Host port to bind (the listener port, or its `--lb-port` override). */\n port: number;\n /** Host interface to bind. Defaults to `127.0.0.1`. */\n host?: string;\n /** ALB listener port (for the `X-Forwarded-Port` header / logs). */\n listenerPort: number;\n /** Human label for log / error lines (e.g. `listener port 80`). */\n label: string;\n /**\n * When set, the front-door listens over HTTPS using these PEM materials\n * (server cert + private key). `X-Forwarded-Proto` switches to `https`\n * and redirect `#{protocol}` placeholders resolve to `https`. Absent =\n * plain HTTP listener (the default).\n */\n tls?: { certPem: Buffer; keyPem: Buffer };\n /**\n * Per-request upstream timeout (ms). A replica that accepts the connection\n * but never responds (deadlocked app, half-open socket) must not hang the\n * request forever; on timeout the upstream socket is destroyed and the\n * client gets a 504. Defaults to {@link DEFAULT_UPSTREAM_TIMEOUT_MS}.\n */\n upstreamTimeoutMs?: number;\n}\n\n/** Default per-request upstream timeout — a hung replica yields a 504, not a hang. */\nexport const DEFAULT_UPSTREAM_TIMEOUT_MS = 30_000;\n\nexport interface StartedFrontDoorServer {\n /** Actual bound port (equals `opts.port`; surfaced for symmetry / tests). */\n port: number;\n /** Actual bound host. */\n host: string;\n /** `'https'` when started with `tls`, otherwise `'http'`. Used by log banners. */\n scheme: 'http' | 'https';\n /** Underlying server (for diagnostics). */\n server: Server;\n /** Drain + close. Idempotent. */\n close: () => Promise<void>;\n}\n\nexport async function startFrontDoorServer(\n opts: StartFrontDoorServerOptions\n): Promise<StartedFrontDoorServer> {\n const logger = getLogger().child('front-door');\n const host = opts.host ?? '127.0.0.1';\n\n const requestHandler = (req: IncomingMessage, res: ServerResponse): void => {\n handleProxyRequest(req, res, opts).catch((err) => {\n logger.debug(`front-door request error: ${err instanceof Error ? err.message : String(err)}`);\n if (!res.headersSent) writeError(res, 502, 'Bad Gateway');\n });\n };\n // HTTPS branch: `https.createServer` with the supplied PEM materials. The\n // request handler is the same — TLS only adds the handshake at the socket\n // layer. `X-Forwarded-Proto` is stamped based on `tls` presence so a\n // downstream app reads the right scheme.\n const server: Server = opts.tls\n ? (createHttpsServer(\n { cert: opts.tls.certPem, key: opts.tls.keyPem },\n requestHandler\n ) as unknown as Server)\n : createServer(requestHandler);\n const scheme: 'http' | 'https' = opts.tls ? 'https' : 'http';\n server.on('connection', (socket) => socket.setNoDelay(true));\n\n const boundPort = await new Promise<number>((resolve, reject) => {\n server.once('error', reject);\n server.listen(opts.port, host, () => {\n const addr = server.address();\n if (addr === null || typeof addr === 'string') {\n reject(new Error('Could not determine front-door listening address'));\n return;\n }\n resolve(addr.port);\n });\n });\n\n let closed = false;\n return {\n port: boundPort,\n host,\n scheme,\n server,\n close: async (): Promise<void> => {\n if (closed) return;\n closed = true;\n await new Promise<void>((resolve) => {\n server.close(() => resolve());\n server.closeAllConnections?.();\n });\n },\n };\n}\n\n/**\n * Resolve the dispatch target for a request path from whichever selector the\n * caller supplied. `selectTarget` (#123, ECS-or-Lambda) wins over the\n * pool-only `selectPool`; a `selectPool` hit is adapted to a `kind: 'pool'`\n * target so the request handler has a single code path.\n */\nfunction resolveDispatchTarget(\n opts: StartFrontDoorServerOptions,\n requestPath: string\n): FrontDoorDispatchTarget | undefined {\n if (opts.selectTarget) return opts.selectTarget(requestPath);\n if (opts.selectPool) {\n const pool = opts.selectPool(requestPath);\n return pool ? { kind: 'pool', pool } : undefined;\n }\n return undefined;\n}\n\nfunction handleProxyRequest(\n req: IncomingMessage,\n res: ServerResponse,\n opts: StartFrontDoorServerOptions\n): Promise<void> {\n const url = req.url ?? '/';\n\n // The full router path (host-header rules, weighted forward, redirect /\n // fixed-response) takes precedence. The single-target selectors are a\n // convenience for callers that never weight / redirect.\n if (opts.route) {\n const action = opts.route({\n path: url,\n ...hostHeader(req),\n headers: req.headers,\n ...(req.method !== undefined && { method: req.method }),\n ...(req.socket.remoteAddress !== undefined && { sourceIp: req.socket.remoteAddress }),\n });\n if (!action) return reply404(req, res, opts);\n\n // Auth gate: when the action wraps an authenticate-* guard, evaluate it\n // before serving. Failure -> 401 + `WWW-Authenticate: Bearer realm`.\n // The gate runs BEFORE redirect / fixed-response so a deny-by-default\n // listener stays locked even for synthesized responses.\n if (action.auth) {\n const auth = action.auth;\n return auth\n .check(req.headers)\n .then((result) => {\n if (!result.allow) {\n req.resume();\n writeUnauthorized(res, auth.realm, result.reason);\n return;\n }\n return serveAction(req, res, action, opts);\n })\n .catch((err) => {\n getLogger()\n .child('front-door')\n .debug(`auth gate error: ${err instanceof Error ? err.message : String(err)}`);\n req.resume();\n writeUnauthorized(res, auth.realm, 'auth check failed');\n });\n }\n return serveAction(req, res, action, opts);\n }\n\n const target = resolveDispatchTarget(opts, url);\n if (!target) return reply404(req, res, opts);\n if (target.kind === 'lambda') {\n return handleLambdaRequest(req, res, target.lambda, opts);\n }\n return handlePoolRequest(req, res, target.pool, opts);\n}\n\n/**\n * Dispatch a resolved {@link RouteAction} — the path the route resolver\n * returned (after any auth gate). Splits forward (ECS pool or Lambda invoker)\n * from redirect / fixed-response, mirroring what the inline logic used to do.\n */\nfunction serveAction(\n req: IncomingMessage,\n res: ServerResponse,\n action: RouteAction,\n opts: StartFrontDoorServerOptions\n): Promise<void> {\n if (action.kind === 'redirect' || action.kind === 'fixed-response') {\n // Drain any request body (ALB serves redirect / fixed-response for every\n // method, incl. POST) so an unconsumed body doesn't stall HTTP/1.1\n // keep-alive socket reuse, then synthesize the response with no backend.\n req.resume();\n if (action.kind === 'redirect') {\n writeRedirect(res, action, req, opts.listenerPort, opts.tls ? 'https' : 'http');\n } else {\n writeFixedResponse(res, action);\n }\n return Promise.resolve();\n }\n\n const picked = pickWeightedTarget(action.pools);\n if (!picked) {\n // Every target has weight 0 (or none) — mirror an ALB whose rule forwards\n // nowhere usable (502, like a misconfigured forward).\n writeError(\n res,\n 502,\n `No forward target selected behind ${opts.label} (every weighted target has weight 0).`\n );\n return Promise.resolve();\n }\n if ('lambda' in picked) return handleLambdaRequest(req, res, picked.lambda, opts);\n return handlePoolRequest(req, res, picked.pool, opts);\n}\n\n/**\n * Write a 401 with `WWW-Authenticate: Bearer realm=\"...\"`. ALB itself would\n * redirect to the IdP's authorize endpoint; for local dev parity we deny\n * loudly so the user notices and either supplies `--bearer-token` / passes a\n * fresh Authorization header / disables the guard with `--no-verify-auth`.\n */\nfunction writeUnauthorized(res: ServerResponse, realm: string, reason?: string): void {\n const body = reason && reason !== '' ? reason : 'Unauthorized';\n res.writeHead(401, {\n 'content-type': 'text/plain; charset=utf-8',\n 'content-length': String(Buffer.byteLength(`${body}\\n`)),\n 'www-authenticate': `Bearer realm=\"${escapeRealmQuotes(realm)}\"`,\n });\n res.end(`${body}\\n`);\n}\n\n/** Escape `\"` in the realm so the `WWW-Authenticate` header parses cleanly. */\nfunction escapeRealmQuotes(realm: string): string {\n return realm.replace(/\"/g, '\\\\\"');\n}\n\n/** Reply 404 — an ALB listener with no matching rule and no default action. */\nfunction reply404(\n req: IncomingMessage,\n res: ServerResponse,\n opts: StartFrontDoorServerOptions\n): Promise<void> {\n writeError(\n res,\n 404,\n `No listener rule matched '${req.url ?? '/'}' on ${opts.label}, and the listener has no ` +\n 'default action forwarding to a local target.'\n );\n return Promise.resolve();\n}\n\nfunction handlePoolRequest(\n req: IncomingMessage,\n res: ServerResponse,\n pool: FrontDoorEndpointPool,\n opts: StartFrontDoorServerOptions\n): Promise<void> {\n return new Promise<void>((resolve) => {\n const endpoint = pool.next();\n if (!endpoint) {\n // No live replica — mirror an ALB with no healthy targets (503).\n writeError(\n res,\n 503,\n `No running replicas behind ${opts.label} for the matched target. The front-door has no ` +\n 'healthy target to forward to.'\n );\n resolve();\n return;\n }\n\n // `settled` makes the resolve idempotent — the timeout, upstream error,\n // client-disconnect, and normal-end paths can race, and only the first\n // should settle the request.\n let settled = false;\n const done = (): void => {\n if (settled) return;\n settled = true;\n resolve();\n };\n\n const headers = { ...req.headers };\n stripHopByHopHeaders(headers);\n appendForwardedHeaders(headers, req, opts.listenerPort, opts.tls ? 'https' : 'http');\n\n const proxyReq = httpRequest(\n {\n host: endpoint.host,\n port: endpoint.port,\n method: req.method,\n path: req.url,\n headers,\n },\n (proxyRes) => {\n const resHeaders = { ...proxyRes.headers };\n stripHopByHopHeaders(resHeaders);\n res.writeHead(proxyRes.statusCode ?? 502, resHeaders);\n proxyRes.pipe(res);\n proxyRes.on('end', done);\n proxyRes.on('error', () => {\n // Upstream reset mid-body (headers already sent): destroy rather than\n // cleanly end so the client sees a broken transfer, not a truncated 200.\n if (!res.writableEnded) res.destroy();\n done();\n });\n }\n );\n\n // A replica that accepts the connection but never responds must not hang\n // the request forever — destroy the upstream and surface a 504.\n proxyReq.setTimeout(opts.upstreamTimeoutMs ?? DEFAULT_UPSTREAM_TIMEOUT_MS, () => {\n if (!res.headersSent) {\n writeError(\n res,\n 504,\n `Replica ${endpoint.host}:${endpoint.port} behind ${opts.label} did not respond in time.`\n );\n } else if (!res.writableEnded) {\n res.destroy();\n }\n proxyReq.destroy();\n done();\n });\n\n proxyReq.on('error', () => {\n if (!res.headersSent) {\n writeError(\n res,\n 502,\n `Failed to reach replica ${endpoint.host}:${endpoint.port} behind ${opts.label}.`\n );\n } else if (!res.writableEnded) {\n res.destroy();\n }\n done();\n });\n\n // Client disconnected before the upstream finished — tear the upstream\n // request down so its socket doesn't leak against the replica.\n res.on('close', () => {\n if (!res.writableEnded) proxyReq.destroy();\n });\n\n req.pipe(proxyReq);\n });\n}\n\n/** Extract the request `Host` header (string) for host-header rule matching. */\nfunction hostHeader(req: IncomingMessage): { host?: string } {\n const raw = req.headers.host;\n const host = Array.isArray(raw) ? raw[0] : raw;\n return host ? { host } : {};\n}\n\n/**\n * Pick one weighted target from a forward set: weighted random over the\n * non-zero weights. A single-entry set short-circuits to that entry. Returns\n * `undefined` when every weight is 0 (an ALB-valid but un-routable forward).\n * Used for forwards that may mix ECS pools and Lambda invokers.\n */\nexport function pickWeightedTarget(\n targets: readonly WeightedForwardTarget[]\n): WeightedForwardTarget | undefined {\n if (targets.length === 0) return undefined;\n if (targets.length === 1) return targets[0]!.weight > 0 ? targets[0]! : undefined;\n const total = targets.reduce((sum, t) => sum + Math.max(0, t.weight), 0);\n if (total <= 0) return undefined;\n let roll = Math.random() * total;\n for (const t of targets) {\n const w = Math.max(0, t.weight);\n if (w === 0) continue;\n roll -= w;\n if (roll < 0) return t;\n }\n // Floating-point edge: roll landed exactly at the total. Return the last\n // non-zero-weight target.\n for (let i = targets.length - 1; i >= 0; i--) {\n if (Math.max(0, targets[i]!.weight) > 0) return targets[i]!;\n }\n return undefined;\n}\n\n/**\n * Pick one pool from a weighted pool set (ECS-only forwards): a convenience\n * over {@link pickWeightedTarget} that returns just the pool. Returns\n * `undefined` when every weight is 0.\n */\nexport function pickWeightedPool(\n pools: readonly WeightedPool[]\n): FrontDoorEndpointPool | undefined {\n const picked = pickWeightedTarget(pools);\n return picked && 'pool' in picked ? picked.pool : undefined;\n}\n\n/**\n * Synthesize an ALB-style redirect (301 / 302). ALB builds the `Location` from\n * the action fields with `#{protocol}` / `#{host}` / `#{port}` / `#{path}` /\n * `#{query}` placeholders filled from the incoming request. We resolve those\n * placeholders against the request the front-door received.\n */\nfunction writeRedirect(\n res: ServerResponse,\n action: RedirectRouteAction,\n req: IncomingMessage,\n listenerPort: number,\n scheme: 'http' | 'https'\n): void {\n const location = buildRedirectLocation(action, req, listenerPort, scheme);\n res.writeHead(action.statusCode, {\n location,\n 'content-type': 'text/plain; charset=utf-8',\n 'content-length': '0',\n });\n res.end();\n}\n\n/**\n * Build the `Location` URL for a redirect action, resolving ALB `#{...}`\n * placeholders. `scheme` is the receiving listener's protocol — it sets the\n * default for `#{protocol}` so an HTTPS listener redirects to `https://...`\n * by default, matching what a real ALB does.\n */\nexport function buildRedirectLocation(\n action: RedirectRouteAction,\n req: { url?: string | undefined; headers: NodeJS.Dict<string | string[]> },\n listenerPort: number,\n scheme: 'http' | 'https' = 'http'\n): string {\n const url = req.url ?? '/';\n const qIndex = url.indexOf('?');\n const reqPath = qIndex === -1 ? url : url.slice(0, qIndex);\n const reqQuery = qIndex === -1 ? '' : url.slice(qIndex + 1);\n const rawHost = req.headers['host'];\n const hostHeaderValue = Array.isArray(rawHost) ? rawHost[0] : rawHost;\n const reqHostName = (hostHeaderValue ?? '').split(':')[0] ?? '';\n\n const placeholders: Record<string, string> = {\n protocol: scheme,\n host: reqHostName,\n port: String(listenerPort),\n path: reqPath.replace(/^\\//, ''), // ALB's #{path} excludes the leading slash\n query: reqQuery,\n };\n const fill = (template: string): string =>\n template.replace(\n /#\\{(protocol|host|port|path|query)\\}/g,\n (_m, key: string) => placeholders[key] ?? ''\n );\n\n const protocol = (\n action.protocol ? fill(action.protocol) : placeholders['protocol']!\n ).toLowerCase();\n const host = action.host ? fill(action.host) : placeholders['host']!;\n const port = action.port ? fill(action.port) : placeholders['port']!;\n // ALB Path defaults to `/#{path}`; it always starts with a `/`.\n const pathTemplate = action.path ?? '/#{path}';\n const path = fill(pathTemplate);\n const queryTemplate = action.query ?? '#{query}';\n const query = fill(queryTemplate);\n\n // Omit the port when it is the protocol default (80/http, 443/https), matching\n // how an ALB-built Location reads.\n const isDefaultPort =\n (protocol === 'http' && port === '80') || (protocol === 'https' && port === '443');\n const authority = isDefaultPort || port === '' ? host : `${host}:${port}`;\n const normalizedPath = path.startsWith('/') ? path : `/${path}`;\n const queryPart = query ? `?${query}` : '';\n return `${protocol}://${authority}${normalizedPath}${queryPart}`;\n}\n\n/** Synthesize an ALB-style fixed-response. */\nfunction writeFixedResponse(res: ServerResponse, action: FixedResponseRouteAction): void {\n const body = action.messageBody ?? '';\n res.writeHead(action.statusCode, {\n 'content-type': action.contentType ?? 'text/plain; charset=utf-8',\n 'content-length': String(Buffer.byteLength(body)),\n });\n res.end(body);\n}\n\n/** Maximum request body the ALB Lambda-target path buffers (ALB's own limit is 1 MB). */\nconst ALB_LAMBDA_MAX_BODY_BYTES = 1024 * 1024;\n\n/**\n * Serve a request that resolved to a Lambda forward target (#123). Buffers the\n * request body (ALB caps the Lambda-target request body at 1 MB), translates\n * the request into the ALB Lambda-target event, invokes the function locally,\n * and writes the translated response. A malformed handler response or an\n * invoke failure surfaces as 502 — mirroring a real ALB.\n */\nfunction handleLambdaRequest(\n req: IncomingMessage,\n res: ServerResponse,\n lambda: FrontDoorLambdaTarget,\n opts: StartFrontDoorServerOptions\n): Promise<void> {\n const logger = getLogger().child('front-door');\n return new Promise<void>((resolve) => {\n let settled = false;\n const done = (): void => {\n if (settled) return;\n settled = true;\n resolve();\n };\n\n const chunks: Buffer[] = [];\n let total = 0;\n let aborted = false;\n req.on('data', (chunk: Buffer) => {\n if (aborted) return;\n total += chunk.length;\n if (total > ALB_LAMBDA_MAX_BODY_BYTES) {\n aborted = true;\n // ALB returns 413 when the request body exceeds the 1 MB Lambda-target\n // limit; mirror that locally.\n writeError(\n res,\n 413,\n `Request body exceeds the ${ALB_LAMBDA_MAX_BODY_BYTES}-byte Lambda-target limit on ${opts.label}.`\n );\n req.destroy();\n done();\n return;\n }\n chunks.push(chunk);\n });\n\n req.on('error', () => {\n if (!res.headersSent) writeError(res, 400, `Failed to read request body on ${opts.label}.`);\n done();\n });\n\n req.on('end', () => {\n if (aborted) return;\n // Self-contained async task; every failure path (invoke rejection,\n // unexpected synchronous throw) lands on a 502 + `done()` INSIDE the\n // function via the surrounding try/catch, so it never rejects — invoked\n // with `void`, no promise callback the linter would flag.\n const serveLambda = async (): Promise<void> => {\n try {\n const body = Buffer.concat(chunks);\n // ALB stamps the x-forwarded-* set onto the Lambda event; reuse the\n // same header injection as the ECS proxy path so the event a handler\n // sees locally matches production.\n const forwardHeaders: NodeJS.Dict<string | string[]> = { ...req.headers };\n // Strip hop-by-hop headers (as the ECS proxy path does) so the Lambda\n // event's headers match what a real ALB forwards — no connection /\n // transfer-encoding / keep-alive leaking into the handler.\n stripHopByHopHeaders(forwardHeaders);\n appendForwardedHeaders(\n forwardHeaders,\n req,\n opts.listenerPort,\n opts.tls ? 'https' : 'http'\n );\n const snapshot = snapshotFromIncoming(req, body);\n for (const [name, value] of Object.entries(forwardHeaders)) {\n if (value === undefined) continue;\n snapshot.headers[name] = Array.isArray(value) ? value : [value];\n }\n const event = buildAlbLambdaEvent(snapshot, {\n targetGroupArn: lambda.targetGroupArn,\n multiValueHeaders: lambda.multiValueHeaders,\n });\n\n const payload = await lambda.invoke(event);\n const translated: TranslatedAlbResponse = translateAlbLambdaResponse(payload);\n\n if (res.headersSent || res.writableEnded) {\n done();\n return;\n }\n const outHeaders: Record<string, string | string[]> = {};\n for (const [name, values] of Object.entries(translated.headers)) {\n outHeaders[name] = values.length === 1 ? values[0]! : values;\n }\n if (translated.statusDescription) {\n res.writeHead(translated.statusCode, translated.statusDescription, outHeaders);\n } else {\n res.writeHead(translated.statusCode, outHeaders);\n }\n res.end(translated.body);\n done();\n } catch (err) {\n logger.debug(\n `Lambda target '${lambda.label}' request failed: ${err instanceof Error ? err.message : String(err)}`\n );\n if (!res.headersSent) {\n writeError(res, 502, `Lambda target '${lambda.label}' behind ${opts.label} failed.`);\n } else if (!res.writableEnded) {\n res.destroy();\n }\n done();\n }\n };\n void serveLambda();\n });\n });\n}\n\n/** Standard hop-by-hop headers (RFC 7230 §6.1) — a proxy must not forward these. */\nconst HOP_BY_HOP_HEADERS = [\n 'connection',\n 'keep-alive',\n 'proxy-authenticate',\n 'proxy-authorization',\n 'te',\n 'trailer',\n 'transfer-encoding',\n 'upgrade',\n];\n\n/**\n * Strip hop-by-hop headers before relaying a request to / a response from the\n * upstream, mirroring what a real ALB does. Forwarding the upstream's\n * `Transfer-Encoding` / `Connection` verbatim while Node re-frames the body can\n * produce a malformed response; the headers named in a `Connection` token list\n * are also hop-by-hop and removed. Mutates `headers` in place.\n */\nfunction stripHopByHopHeaders(headers: NodeJS.Dict<string | string[]>): void {\n const connection = headers['connection'];\n const connectionValue = Array.isArray(connection) ? connection.join(',') : connection;\n if (connectionValue) {\n for (const token of connectionValue.split(',')) {\n const name = token.trim().toLowerCase();\n if (name) delete headers[name];\n }\n }\n for (const name of HOP_BY_HOP_HEADERS) delete headers[name];\n}\n\n/**\n * Inject the ALB-style forwarding headers a downstream app may read. Appends\n * the client IP to any existing `X-Forwarded-For` chain (ALB appends rather\n * than replaces) and stamps the scheme / listener port. `scheme` follows the\n * listener's protocol so an HTTPS listener stamps `x-forwarded-proto: https`.\n */\nfunction appendForwardedHeaders(\n headers: NodeJS.Dict<string | string[]>,\n req: IncomingMessage,\n listenerPort: number,\n scheme: 'http' | 'https'\n): void {\n const clientIp = req.socket.remoteAddress ?? '';\n const existing = headers['x-forwarded-for'];\n const chain = Array.isArray(existing) ? existing.join(', ') : existing;\n headers['x-forwarded-for'] = chain ? `${chain}, ${clientIp}` : clientIp;\n headers['x-forwarded-proto'] = scheme;\n headers['x-forwarded-port'] = String(listenerPort);\n}\n\nfunction writeError(res: ServerResponse, statusCode: number, message: string): void {\n res.writeHead(statusCode, { 'content-type': 'text/plain; charset=utf-8' });\n res.end(`${message}\\n`);\n}\n","/**\n * Issue #123 — match a request against an ALB listener's rules, in priority\n * order. Covers all six ALB rule-condition fields: `path-pattern` (path glob),\n * `host-header` (Host glob), `http-header` (named-header glob), `http-request-method`\n * (exact method), `query-string` (key / value glob pairs), and `source-ip`\n * (CIDR). A rule may carry up to one of each and all present conditions must\n * match (ALB ANDs conditions of different fields).\n *\n * ALB path-pattern semantics (NOT the same as API Gateway route matching, so\n * this is a dedicated matcher rather than a reuse of `route-matcher.ts`):\n *\n * - Matched against the **path only** — the query string is excluded.\n * - **Case-sensitive**.\n * - Two wildcards: `*` matches 0 or more characters (including `/`), and `?`\n * matches exactly 1 character. Every other character is literal.\n * - The pattern matches the **entire** path (anchored both ends), so\n * `/api/*` matches `/api/` and `/api/v1/x` but not `/api` (no trailing\n * character for `*` to begin after the slash) and not `/apix`.\n *\n * ALB host-header semantics:\n *\n * - Matched against the request `Host` header, **case-insensitive** (DNS\n * hostnames are case-insensitive). The port suffix (`:8080`) is stripped\n * before matching, mirroring ALB's host comparison.\n * - Same `*` / `?` glob alphabet as path-pattern, anchored both ends.\n *\n * ALB http-header semantics:\n *\n * - A condition names one HTTP header (e.g. `User-Agent`) and one or more\n * value globs (`*` / `?`, case-insensitive). The condition matches when\n * ANY value of that header (a multi-valued header is treated as the list\n * of its values) matches ANY glob.\n * - Header NAME lookup is case-insensitive (HTTP).\n * - A rule may carry multiple http-header conditions (different names);\n * they AND.\n *\n * ALB http-request-method semantics:\n *\n * - One condition with a list of method names (OR-matched). **Exact match,\n * no wildcards**, case-sensitive uppercase (ALB requires uppercase).\n *\n * ALB query-string semantics:\n *\n * - One condition with a list of `{ Key?, Value }` pairs (OR-matched). Both\n * `Key` (optional) and `Value` accept the `*` / `?` glob alphabet;\n * matching is case-insensitive. When `Key` is omitted, ANY key with a\n * value matching the `Value` glob satisfies the entry.\n * - Repeated query-string parameters (`?a=1&a=2`) match if ANY value\n * satisfies the entry.\n *\n * ALB source-ip semantics:\n *\n * - One condition with a list of CIDR ranges (OR-matched). Each value is an\n * IPv4 or IPv6 CIDR. The request matches when the connection's source IP\n * falls inside ANY listed range. IPv4-mapped IPv6 source addresses (the\n * `::ffff:a.b.c.d` form Node reports for IPv4 on a dual-stack listener)\n * are unmapped before matching.\n * - Local-front-door caveat: a request that comes in on `127.0.0.1` is what\n * the rule sees; CIDR rules narrower than `127.0.0.0/8` will not match\n * locally without `X-Forwarded-For` plumbing (deferred).\n *\n * Rule precedence: ALB evaluates listener rules in ascending `Priority`\n * (lower number = higher priority); the first rule whose condition(s) match\n * wins. A condition with multiple values OR-matches; conditions of different\n * fields AND. When no rule matches, the caller falls back to the listener's\n * default action.\n */\n\n/** One http-header condition: a header name + its OR-matched value globs. */\nexport interface AlbHttpHeaderCondition {\n /** Header name (case-insensitive lookup). */\n name: string;\n /** OR-matched value globs (`*` / `?`, case-insensitive). */\n values: readonly string[];\n}\n\n/** One query-string condition value: an optional key glob + a required value glob. */\nexport interface AlbQueryStringCondition {\n /** Optional key glob; absent = any key. */\n key?: string;\n /** Required value glob. */\n value: string;\n}\n\n/** One routing rule, generic over the target it selects. */\nexport interface AlbPathRule<T> {\n /** ALB rule priority (lower = evaluated first). */\n priority: number;\n /** The rule's `path-pattern` condition values (OR-matched). Empty = no path constraint. */\n pathPatterns: string[];\n /** The rule's `host-header` condition values (OR-matched). Empty = no host constraint. */\n hostPatterns?: string[];\n /** The rule's `http-header` conditions (each AND'd; values within OR). Empty = no header constraint. */\n httpHeaderConditions?: readonly AlbHttpHeaderCondition[];\n /** The rule's `http-request-method` values (OR-matched, exact). Empty = no method constraint. */\n httpRequestMethods?: readonly string[];\n /** The rule's `query-string` `{ Key?, Value }` pairs (OR-matched). Empty = no query-string constraint. */\n queryStringConditions?: readonly AlbQueryStringCondition[];\n /** The rule's `source-ip` CIDR values (OR-matched). Empty = no source-IP constraint. */\n sourceIpCidrs?: readonly string[];\n /** What this rule routes to (e.g. a pool, a resolved target). */\n target: T;\n}\n\n/** The request facts a rule is evaluated against. */\nexport interface AlbRequestMatch {\n /** Request URL path (query string is split out before matching path-pattern). */\n path: string;\n /** Request `Host` header (port suffix is stripped before host matching). */\n host?: string;\n /** Raw incoming request headers (multi-value supported); names are looked up case-insensitively. */\n headers?: NodeJS.Dict<string | string[]>;\n /** HTTP request method (e.g. `GET`); compared case-sensitively to the rule's uppercase values. */\n method?: string;\n /** Connection source IP for source-ip rule matching. */\n sourceIp?: string;\n}\n\n/**\n * Return the target of the highest-priority rule whose conditions all match\n * `req`, or `undefined` when none match (caller uses the default). Rules are\n * evaluated in ascending priority; the input order is irrelevant.\n *\n * Accepts either a request facts object or a bare path string (the path-only\n * form keeps the original signature working for callers that have no Host /\n * headers / method / source IP).\n */\nexport function matchAlbPathRule<T>(\n req: AlbRequestMatch | string,\n rules: readonly AlbPathRule<T>[]\n): T | undefined {\n const facts: AlbRequestMatch = typeof req === 'string' ? { path: req } : req;\n const requestPath = pathOf(facts.path);\n const requestQuery = queryOf(facts.path);\n const requestHost = facts.host !== undefined ? hostOf(facts.host) : undefined;\n const ordered = [...rules].sort((a, b) => a.priority - b.priority);\n for (const rule of ordered) {\n if (ruleMatches(rule, requestPath, requestQuery, requestHost, facts)) return rule.target;\n }\n return undefined;\n}\n\n/**\n * Whether a single rule's conditions all match. Each present condition field\n * is OR-matched within (multiple values) and AND'd across fields. An empty /\n * absent condition list means \"no constraint on that field\" (the condition was\n * absent on the rule).\n */\nfunction ruleMatches<T>(\n rule: AlbPathRule<T>,\n requestPath: string,\n requestQuery: string,\n requestHost: string | undefined,\n facts: AlbRequestMatch\n): boolean {\n if (rule.pathPatterns.length > 0) {\n if (!rule.pathPatterns.some((pattern) => globToRegExp(pattern, false).test(requestPath))) {\n return false;\n }\n }\n const hostPatterns = rule.hostPatterns ?? [];\n if (hostPatterns.length > 0) {\n if (requestHost === undefined) return false;\n if (!hostPatterns.some((pattern) => globToRegExp(pattern, true).test(requestHost))) {\n return false;\n }\n }\n const headerConditions = rule.httpHeaderConditions ?? [];\n if (headerConditions.length > 0) {\n if (!headerConditions.every((cond) => httpHeaderConditionMatches(cond, facts.headers))) {\n return false;\n }\n }\n const methods = rule.httpRequestMethods ?? [];\n if (methods.length > 0) {\n if (facts.method === undefined) return false;\n if (!methods.includes(facts.method)) return false;\n }\n const queryConditions = rule.queryStringConditions ?? [];\n if (queryConditions.length > 0) {\n const params = parseQueryParams(requestQuery);\n if (!queryConditions.some((cond) => queryStringConditionMatches(cond, params))) {\n return false;\n }\n }\n const cidrs = rule.sourceIpCidrs ?? [];\n if (cidrs.length > 0) {\n if (facts.sourceIp === undefined) return false;\n const ip = unmapV4MappedV6(facts.sourceIp);\n if (!cidrs.some((cidr) => albCidrMatches(cidr, ip))) return false;\n }\n return true;\n}\n\n/**\n * Whether a single ALB `path-pattern` value matches a request path. The path\n * must already be query-stripped, or pass a raw URL and it is stripped here.\n */\nexport function albPathPatternMatches(pattern: string, requestPath: string): boolean {\n return globToRegExp(pattern, false).test(pathOf(requestPath));\n}\n\n/**\n * Whether a single ALB `host-header` value matches a request Host. Both the\n * pattern and the host are lower-cased (host comparison is case-insensitive),\n * and the host's port suffix is stripped.\n */\nexport function albHostPatternMatches(pattern: string, requestHost: string): boolean {\n return globToRegExp(pattern, true).test(hostOf(requestHost));\n}\n\n/**\n * Whether an `http-header` condition matches the request's headers. The\n * header name is looked up case-insensitively; each listed value glob is\n * matched case-insensitively against every value of that header (multi-valued\n * headers count each value separately).\n */\nexport function httpHeaderConditionMatches(\n cond: AlbHttpHeaderCondition,\n headers: NodeJS.Dict<string | string[]> | undefined\n): boolean {\n if (!headers) return false;\n const targetName = cond.name.toLowerCase();\n const rawValue = headerLookup(headers, targetName);\n if (rawValue === undefined) return false;\n const headerValues = Array.isArray(rawValue) ? rawValue : [rawValue];\n return cond.values.some((glob) => {\n const re = globToRegExp(glob, true);\n return headerValues.some((v) => re.test(v.toLowerCase()));\n });\n}\n\n/**\n * Whether a single `query-string` condition `{ Key?, Value }` matches the\n * request's parsed query parameters. Both Key and Value are case-insensitive\n * `*` / `?` globs; when `Key` is absent, ANY parameter whose value matches the\n * Value glob satisfies the condition.\n */\nexport function queryStringConditionMatches(\n cond: AlbQueryStringCondition,\n params: ReadonlyArray<{ key: string; value: string }>\n): boolean {\n const valueRe = globToRegExp(cond.value, true);\n const keyRe = cond.key !== undefined ? globToRegExp(cond.key, true) : undefined;\n return params.some((p) => {\n if (keyRe !== undefined && !keyRe.test(p.key.toLowerCase())) return false;\n return valueRe.test(p.value.toLowerCase());\n });\n}\n\n/**\n * Whether an IPv4 or IPv6 address falls inside an IPv4 or IPv6 CIDR. Returns\n * `false` for mismatched families (an IPv4 address tested against an IPv6\n * CIDR, or vice versa) and for unparseable input.\n */\nexport function albCidrMatches(cidr: string, ip: string): boolean {\n const parsed = parseCidr(cidr);\n if (!parsed) return false;\n const addr = parseIpAddress(ip);\n if (!addr) return false;\n if (parsed.family !== addr.family) return false;\n return matchBitPrefix(parsed.bytes, addr.bytes, parsed.prefixLength);\n}\n\n/** Strip the query string / fragment so only the URL path is matched. */\nfunction pathOf(url: string): string {\n let end = url.length;\n const q = url.indexOf('?');\n if (q !== -1) end = q;\n const h = url.indexOf('#');\n if (h !== -1 && h < end) end = h;\n return url.slice(0, end);\n}\n\n/**\n * Pull the raw query string (`a=1&b=2`) out of a URL. Returns the empty string\n * when the URL has no `?`. The fragment is dropped.\n */\nfunction queryOf(url: string): string {\n const q = url.indexOf('?');\n if (q === -1) return '';\n const rest = url.slice(q + 1);\n const h = rest.indexOf('#');\n return h === -1 ? rest : rest.slice(0, h);\n}\n\n/**\n * Decode a raw query string into a flat list of `{ key, value }` entries\n * (preserving repeats), URI-decoding both sides and treating `+` as a space\n * (the `application/x-www-form-urlencoded` convention browsers use). Pairs\n * with no `=` are treated as `{ key, value: '' }`.\n */\nfunction parseQueryParams(query: string): Array<{ key: string; value: string }> {\n if (!query) return [];\n const out: Array<{ key: string; value: string }> = [];\n for (const pair of query.split('&')) {\n if (!pair) continue;\n const eq = pair.indexOf('=');\n const rawKey = eq === -1 ? pair : pair.slice(0, eq);\n const rawValue = eq === -1 ? '' : pair.slice(eq + 1);\n out.push({ key: decodeQueryPart(rawKey), value: decodeQueryPart(rawValue) });\n }\n return out;\n}\n\nfunction decodeQueryPart(s: string): string {\n try {\n return decodeURIComponent(s.replace(/\\+/g, ' '));\n } catch {\n return s;\n }\n}\n\n/**\n * Normalize a `Host` header for matching: drop any `:port` suffix and lower-case\n * it (DNS hostnames are case-insensitive). IPv6 literals (`[::1]:8080`) keep the\n * bracketed address and only the trailing port is removed.\n */\nfunction hostOf(host: string): string {\n const trimmed = host.trim();\n if (trimmed.startsWith('[')) {\n // IPv6 literal: `[addr]` or `[addr]:port`.\n const close = trimmed.indexOf(']');\n if (close !== -1) return trimmed.slice(0, close + 1).toLowerCase();\n return trimmed.toLowerCase();\n }\n const colon = trimmed.indexOf(':');\n const bare = colon === -1 ? trimmed : trimmed.slice(0, colon);\n return bare.toLowerCase();\n}\n\n/** Case-insensitive header lookup against a Node-style headers dict. */\nfunction headerLookup(\n headers: NodeJS.Dict<string | string[]>,\n lowerCaseName: string\n): string | string[] | undefined {\n // Most callers already pass lower-cased keys (Node's IncomingMessage does),\n // so a direct lookup is the fast path; fall back to a scan otherwise.\n const direct = headers[lowerCaseName];\n if (direct !== undefined) return direct;\n for (const key of Object.keys(headers)) {\n if (key.toLowerCase() === lowerCaseName) return headers[key];\n }\n return undefined;\n}\n\nconst REGEXP_META = /[.+^${}()|[\\]\\\\]/;\n\n/**\n * Translate an ALB `*` / `?` glob into an anchored RegExp: `*` -> `.*`,\n * `?` -> `.`, every other character is escaped and matched literally. The\n * `caseInsensitive` form lower-cases the pattern (callers pair it with a\n * lower-cased input); path patterns are case-sensitive.\n */\nfunction globToRegExp(pattern: string, caseInsensitive: boolean): RegExp {\n const source = caseInsensitive ? pattern.toLowerCase() : pattern;\n let body = '';\n for (const ch of source) {\n if (ch === '*') body += '.*';\n else if (ch === '?') body += '.';\n else if (REGEXP_META.test(ch)) body += `\\\\${ch}`;\n else body += ch;\n }\n return new RegExp(`^${body}$`);\n}\n\ninterface CidrParsed {\n family: 'v4' | 'v6';\n bytes: Uint8Array;\n prefixLength: number;\n}\n\ninterface IpParsed {\n family: 'v4' | 'v6';\n bytes: Uint8Array;\n}\n\n/** Parse a CIDR (`ip/prefix`) into its address bytes + prefix length. */\nfunction parseCidr(cidr: string): CidrParsed | undefined {\n const slash = cidr.indexOf('/');\n if (slash === -1) return undefined;\n const addrPart = cidr.slice(0, slash);\n const prefixPart = cidr.slice(slash + 1);\n if (!/^\\d+$/.test(prefixPart)) return undefined;\n const prefixLength = parseInt(prefixPart, 10);\n const addr = parseIpAddress(addrPart);\n if (!addr) return undefined;\n const maxPrefix = addr.family === 'v4' ? 32 : 128;\n if (prefixLength < 0 || prefixLength > maxPrefix) return undefined;\n return { family: addr.family, bytes: addr.bytes, prefixLength };\n}\n\n/** Parse an IPv4 (`1.2.3.4`) or IPv6 address into its byte representation. */\nfunction parseIpAddress(ip: string): IpParsed | undefined {\n if (ip.includes('.') && !ip.includes(':')) {\n return parseIpv4(ip);\n }\n if (ip.includes(':')) {\n return parseIpv6(ip);\n }\n return undefined;\n}\n\nfunction parseIpv4(ip: string): IpParsed | undefined {\n const parts = ip.split('.');\n if (parts.length !== 4) return undefined;\n const bytes = new Uint8Array(4);\n for (let i = 0; i < 4; i++) {\n const part = parts[i]!;\n if (!/^\\d+$/.test(part)) return undefined;\n const n = parseInt(part, 10);\n if (n < 0 || n > 255) return undefined;\n bytes[i] = n;\n }\n return { family: 'v4', bytes };\n}\n\n/**\n * Parse a textual IPv6 address (incl. `::` shorthand and IPv4-suffix form like\n * `::ffff:1.2.3.4`) into its 16-byte representation.\n */\nfunction parseIpv6(ip: string): IpParsed | undefined {\n // Strip bracketed form (`[::1]` -> `::1`).\n let s = ip;\n if (s.startsWith('[') && s.endsWith(']')) s = s.slice(1, -1);\n\n // Split off an embedded IPv4 suffix (`::ffff:1.2.3.4`).\n let v4Suffix: Uint8Array | undefined;\n const lastColon = s.lastIndexOf(':');\n if (lastColon !== -1 && s.slice(lastColon + 1).includes('.')) {\n const v4Part = s.slice(lastColon + 1);\n const parsed = parseIpv4(v4Part);\n if (!parsed) return undefined;\n v4Suffix = parsed.bytes;\n s = s.slice(0, lastColon) + ':0:0';\n }\n\n const doubleColon = s.indexOf('::');\n let head: string[];\n let tail: string[];\n if (doubleColon === -1) {\n head = s.split(':');\n tail = [];\n } else {\n head = s.slice(0, doubleColon) === '' ? [] : s.slice(0, doubleColon).split(':');\n tail = s.slice(doubleColon + 2) === '' ? [] : s.slice(doubleColon + 2).split(':');\n }\n if (head.length + tail.length > 8) return undefined;\n if (doubleColon === -1 && head.length !== 8) return undefined;\n\n const groups: number[] = new Array(8).fill(0);\n for (let i = 0; i < head.length; i++) {\n const g = parseHexGroup(head[i]!);\n if (g === undefined) return undefined;\n groups[i] = g;\n }\n for (let i = 0; i < tail.length; i++) {\n const g = parseHexGroup(tail[tail.length - 1 - i]!);\n if (g === undefined) return undefined;\n groups[7 - i] = g;\n }\n\n const bytes = new Uint8Array(16);\n for (let i = 0; i < 8; i++) {\n bytes[i * 2] = (groups[i]! >> 8) & 0xff;\n bytes[i * 2 + 1] = groups[i]! & 0xff;\n }\n if (v4Suffix) {\n bytes[12] = v4Suffix[0]!;\n bytes[13] = v4Suffix[1]!;\n bytes[14] = v4Suffix[2]!;\n bytes[15] = v4Suffix[3]!;\n }\n return { family: 'v6', bytes };\n}\n\nfunction parseHexGroup(g: string): number | undefined {\n if (g.length === 0 || g.length > 4) return undefined;\n if (!/^[0-9a-fA-F]+$/.test(g)) return undefined;\n return parseInt(g, 16);\n}\n\n/**\n * Unmap an IPv4-mapped IPv6 source IP (`::ffff:a.b.c.d` or `::ffff:NNNN:NNNN`)\n * to its bare IPv4 form. Node reports `::ffff:127.0.0.1` for an IPv4 client on\n * a dual-stack listener; rules that name a v4 CIDR should still match.\n */\nfunction unmapV4MappedV6(ip: string): string {\n const m = /^::ffff:(.+)$/i.exec(ip);\n if (!m) return ip;\n const suffix = m[1]!;\n if (suffix.includes('.')) return suffix;\n // `::ffff:7f00:1` form -> reconstruct dotted quad.\n const parts = suffix.split(':');\n if (parts.length !== 2) return ip;\n const high = parseInt(parts[0]!, 16);\n const low = parseInt(parts[1]!, 16);\n if (!Number.isFinite(high) || !Number.isFinite(low)) return ip;\n return `${(high >> 8) & 0xff}.${high & 0xff}.${(low >> 8) & 0xff}.${low & 0xff}`;\n}\n\n/** Whether the first `prefixLength` bits of `a` equal those of `b`. */\nfunction matchBitPrefix(a: Uint8Array, b: Uint8Array, prefixLength: number): boolean {\n if (a.length !== b.length) return false;\n const fullBytes = Math.floor(prefixLength / 8);\n for (let i = 0; i < fullBytes; i++) {\n if (a[i] !== b[i]) return false;\n }\n const remainingBits = prefixLength % 8;\n if (remainingBits === 0) return true;\n const mask = 0xff << (8 - remainingBits);\n return (a[fullBytes]! & mask) === (b[fullBytes]! & mask);\n}\n","import { execFile } from 'node:child_process';\nimport { X509Certificate } from 'node:crypto';\nimport { mkdirSync, readFileSync, statSync, unlinkSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport { promisify } from 'node:util';\nimport { getLogger } from '../utils/logger.js';\n\nconst execFileAsync = promisify(execFile);\n\n/**\n * PEM materials for an HTTPS front-door listener: a server cert + its private\n * key. Buffers so they pass straight to `https.createServer({ cert, key })`.\n */\nexport interface FrontDoorTlsMaterials {\n certPem: Buffer;\n keyPem: Buffer;\n}\n\n/**\n * Resolve a single global cert/key pair for HTTPS front-door listeners. When\n * BOTH `certPath` and `keyPath` are supplied, those PEM files are read from\n * disk. When neither is supplied, a long-lived self-signed cert is cached\n * under `$XDG_CACHE_HOME/cdk-local/alb-https/` (defaulting to\n * `~/.cache/cdk-local/alb-https/`) and reused across boots; it is regenerated\n * when missing or within `regenerateWithinDays` of expiry. Pairing is enforced\n * — supplying exactly one of `--tls-cert` / `--tls-key` is rejected with a\n * clear error before the server starts.\n *\n * The self-signed cert path subprocesses `openssl req -x509 ...`. `openssl`\n * is on macOS / Linux dev boxes and Docker base images by default; absence\n * surfaces as an actionable error pointing at the BYO recipe.\n */\nexport async function resolveFrontDoorTlsMaterials(opts: {\n certPath: string | undefined;\n keyPath: string | undefined;\n cacheDir?: string;\n /** Days before expiry at which the cached cert is regenerated. Default 30. */\n regenerateWithinDays?: number;\n /** Validity period for a freshly generated cert, in days. Default 825. */\n validityDays?: number;\n}): Promise<FrontDoorTlsMaterials> {\n const hasCert = opts.certPath !== undefined && opts.certPath !== '';\n const hasKey = opts.keyPath !== undefined && opts.keyPath !== '';\n if (hasCert !== hasKey) {\n const set = hasCert ? '--tls-cert' : '--tls-key';\n const missing = hasCert ? '--tls-key' : '--tls-cert';\n throw new Error(\n `${set} is set but ${missing} is missing. ` +\n 'Both --tls-cert and --tls-key must be set together, or both left unset to use an ' +\n 'auto-generated self-signed cert.'\n );\n }\n if (hasCert && hasKey) {\n return {\n certPem: readPemOrThrow(opts.certPath!, '--tls-cert'),\n keyPem: readPemOrThrow(opts.keyPath!, '--tls-key'),\n };\n }\n return ensureSelfSignedCert({\n cacheDir: opts.cacheDir ?? defaultCacheDir(),\n regenerateWithinDays: opts.regenerateWithinDays ?? 30,\n validityDays: opts.validityDays ?? 825,\n });\n}\n\n/** Default cache dir for the auto-generated self-signed cert. */\nexport function defaultCacheDir(): string {\n const xdg = process.env['XDG_CACHE_HOME'];\n const base = xdg && xdg !== '' ? xdg : join(homedir(), '.cache');\n return join(base, 'cdk-local', 'alb-https');\n}\n\nconst CERT_FILENAME = 'cert.pem';\nconst KEY_FILENAME = 'key.pem';\n\ninterface SelfSignedOptions {\n cacheDir: string;\n regenerateWithinDays: number;\n validityDays: number;\n}\n\nasync function ensureSelfSignedCert(opts: SelfSignedOptions): Promise<FrontDoorTlsMaterials> {\n const logger = getLogger().child('front-door-tls');\n const certPath = join(opts.cacheDir, CERT_FILENAME);\n const keyPath = join(opts.cacheDir, KEY_FILENAME);\n\n // Reuse the cached pair when both files exist AND the cert is not expiring\n // soon. A near-expiry regeneration keeps long-running shells healthy\n // without forcing a manual cleanup.\n if (cachedPairIsFresh(certPath, keyPath, opts.regenerateWithinDays)) {\n return {\n certPem: readFileSync(certPath),\n keyPem: readFileSync(keyPath),\n };\n }\n\n mkdirSync(opts.cacheDir, { recursive: true });\n // Defensive: if a previous run left a half-written cert / key (e.g. openssl\n // crashed mid-write), `openssl req -x509 ...` will overwrite them, but a\n // residual non-cert file could keep failing the X509Certificate parse on\n // the next cachedPairIsFresh check and trigger a regen loop. Remove them\n // up front so a failed regen does not poison subsequent reads.\n try {\n unlinkSync(certPath);\n } catch {\n /* missing is fine */\n }\n try {\n unlinkSync(keyPath);\n } catch {\n /* missing is fine */\n }\n try {\n await runOpenssl([\n 'req',\n '-x509',\n '-newkey',\n 'rsa:2048',\n '-nodes',\n '-keyout',\n keyPath,\n '-out',\n certPath,\n '-subj',\n '/CN=localhost',\n '-days',\n String(opts.validityDays),\n '-addext',\n 'subjectAltName=DNS:localhost,IP:127.0.0.1',\n ]);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n throw new Error(\n `Failed to auto-generate a self-signed cert via openssl: ${msg}. ` +\n 'Install openssl, or supply --tls-cert <path> + --tls-key <path> with your own PEM files. ' +\n 'Recipe: openssl req -x509 -newkey rsa:2048 -nodes -keyout key.pem -out cert.pem ' +\n '-subj \"/CN=localhost\" -days 365.'\n );\n }\n logger.info(\n `ALB front-door: generated self-signed cert at ${certPath} (valid ${opts.validityDays} days)`\n );\n return {\n certPem: readFileSync(certPath),\n keyPem: readFileSync(keyPath),\n };\n}\n\nfunction cachedPairIsFresh(certPath: string, keyPath: string, regenWithinDays: number): boolean {\n try {\n statSync(certPath);\n statSync(keyPath);\n } catch {\n return false;\n }\n try {\n const notAfter = readCertNotAfter(certPath);\n if (notAfter === undefined) return false;\n const renewAt = notAfter.getTime() - regenWithinDays * 86_400_000;\n return Date.now() < renewAt;\n } catch {\n return false;\n }\n}\n\n/**\n * Read a cert's `notAfter` expiry timestamp. Used to decide whether to\n * regenerate the cached self-signed cert proactively. Returns `undefined`\n * when the cert is unreadable (caller then regenerates).\n */\nfunction readCertNotAfter(certPath: string): Date | undefined {\n try {\n // `crypto.X509Certificate` parses a PEM/DER cert and exposes the `validTo`\n // string. Available since Node 15.6 — well within the >=20 engine floor.\n const cert = new X509Certificate(readFileSync(certPath));\n const parsed = new Date(cert.validTo);\n return Number.isNaN(parsed.getTime()) ? undefined : parsed;\n } catch {\n return undefined;\n }\n}\n\nasync function runOpenssl(args: string[]): Promise<void> {\n await execFileAsync('openssl', args, { timeout: 30_000 });\n}\n\nfunction readPemOrThrow(path: string, flagName: string): Buffer {\n try {\n return readFileSync(path);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n throw new Error(`${flagName}: cannot read PEM file at '${path}': ${msg}`);\n }\n}\n","import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from 'node:fs';\nimport { tmpdir } from 'node:os';\nimport * as path from 'node:path';\nimport { getLogger } from '../utils/logger.js';\nimport {\n pickFreePort,\n pullImage,\n removeContainer,\n runDetached,\n streamLogs,\n} from './docker-runner.js';\nimport { architectureToPlatform, buildContainerImage } from './docker-image-builder.js';\nimport { pullEcrImage, parseEcrUri } from './ecr-puller.js';\nimport { invokeRie, waitForRieReady } from './rie-client.js';\nimport {\n resolveRuntimeCodeMountPath,\n resolveRuntimeFileExtension,\n resolveRuntimeImage,\n} from './runtime-image.js';\nimport {\n AssetManifestLoader,\n getDockerImageBySourceHash,\n} from '../assets/asset-manifest-loader.js';\nimport type { ResolvedImageLambda, ResolvedLambda, ResolvedZipLambda } from './lambda-resolver.js';\nimport { getEmbedConfig } from './embed-config.js';\n\n/**\n * Issue #123 (Lambda-target slice) — boot a single Lambda function in a\n * long-lived RIE container behind a local ALB front-door, and invoke it per\n * request. This is the Lambda counterpart of the ECS `FrontDoorEndpointPool`:\n * where an ECS forward target round-robins live replica ports, a Lambda forward\n * target keeps ONE warm RIE container and invokes it via the same machinery\n * `cdkl invoke` / `start-api` use (`runDetached` -> `waitForRieReady` ->\n * `invokeRie`).\n *\n * Lifecycle:\n * - `start()` resolves the image plan (ZIP base image + bind mount, or a\n * built/pulled container image), `docker run`s it detached, and waits for\n * RIE to come up. Idempotent — a second `start()` is a no-op.\n * - `invoke(event)` POSTs the event to RIE and returns the parsed payload.\n * The container serializes invokes on its own (one warm container, like a\n * concurrency-1 Lambda locally); the front-door's per-request handling is\n * what concurrency the dev loop needs.\n * - `stop()` tears the container down + removes any materialized tmpdirs.\n * Idempotent.\n *\n * Scope: the common ZIP (asset / inline) and IMAGE (local-build / ECR-pull)\n * cases. Lambda Layers are intentionally NOT mounted here in v1 (the front-door\n * Lambda-target path targets simple request handlers; layered functions remain\n * reachable via `cdkl invoke` / `start-api`). Env-var / state substitution and\n * `--assume-role` credential injection are also out of scope for the v1\n * front-door Lambda target — the handler runs with the dev shell's forwarded\n * AWS env (same default as `cdkl invoke` without `--assume-role`).\n */\n\nexport interface FrontDoorLambdaRunnerOptions {\n /** Host interface to bind the RIE port to (the `--container-host` value). */\n containerHost: string;\n /** Skip `docker pull` for the base image (the `--no-pull` flag). */\n skipPull?: boolean;\n /** Force `docker run --platform` for the IMAGE path. */\n platformOverride?: string;\n /** Role ARN to assume before authenticating against ECR (IMAGE ECR-pull path). */\n ecrRoleArn?: string;\n /** Region passed to the ECR-pull fallback. */\n region?: string;\n /** Whether to attach `docker logs -f`. Default true. */\n streamLogs?: boolean;\n}\n\ninterface ImagePlan {\n image: string;\n mounts: { hostPath: string; containerPath: string; readOnly?: boolean }[];\n cmd: string[];\n platform?: string;\n entryPoint?: string[];\n workingDir?: string;\n /** Temp dir to remove on stop (materialized inline ZIP code). */\n inlineTmpDir?: string;\n}\n\n/** Forward the dev shell's AWS credential / region env into the Lambda container. */\nfunction forwardAwsEnv(): Record<string, string> {\n const env: Record<string, string> = {};\n const passThrough = [\n 'AWS_ACCESS_KEY_ID',\n 'AWS_SECRET_ACCESS_KEY',\n 'AWS_SESSION_TOKEN',\n 'AWS_REGION',\n 'AWS_DEFAULT_REGION',\n ] as const;\n for (const key of passThrough) {\n const value = process.env[key];\n if (value !== undefined) env[key] = value;\n }\n return env;\n}\n\n/**\n * Materialize an inline (`Code.ZipFile`) ZIP Lambda's source into a temp dir at\n * the path implied by `handler`, returning the dir to bind-mount. Mirrors\n * `local-invoke.ts:materializeInlineCode`.\n */\nfunction materializeInlineCode(handler: string, source: string, fileExtension: string): string {\n const lastDot = handler.lastIndexOf('.');\n if (lastDot <= 0) {\n throw new Error(`Handler '${handler}' is malformed: expected '<modulePath>.<exportName>'.`);\n }\n const modulePath = handler.substring(0, lastDot);\n const dir = mkdtempSync(\n path.join(tmpdir(), `${getEmbedConfig().resourceNamePrefix}-alb-lambda-`)\n );\n const filePath = path.join(dir, `${modulePath}${fileExtension}`);\n mkdirSync(path.dirname(filePath), { recursive: true });\n writeFileSync(filePath, source, 'utf-8');\n return dir;\n}\n\nasync function resolveZipImagePlan(\n lambda: ResolvedZipLambda,\n opts: FrontDoorLambdaRunnerOptions\n): Promise<ImagePlan> {\n let inlineTmpDir: string | undefined;\n let codeDir = lambda.codePath;\n if (codeDir === null) {\n inlineTmpDir = materializeInlineCode(\n lambda.handler,\n lambda.inlineCode ?? '',\n resolveRuntimeFileExtension(lambda.runtime)\n );\n codeDir = inlineTmpDir;\n }\n const image = resolveRuntimeImage(lambda.runtime);\n await pullImage(image, opts.skipPull === true);\n const containerCodePath = resolveRuntimeCodeMountPath(lambda.runtime);\n return {\n image,\n mounts: [{ hostPath: codeDir, containerPath: containerCodePath, readOnly: true }],\n cmd: [lambda.handler],\n ...(inlineTmpDir !== undefined && { inlineTmpDir }),\n };\n}\n\nasync function resolveContainerImagePlan(\n lambda: ResolvedImageLambda,\n opts: FrontDoorLambdaRunnerOptions\n): Promise<ImagePlan> {\n const logger = getLogger().child('front-door-lambda');\n const platform = opts.platformOverride ?? architectureToPlatform(lambda.architecture);\n\n const manifestPath = lambda.stack.assetManifestPath;\n let imageRef: string;\n let localBuilt = false;\n if (manifestPath) {\n const cdkOutDir = path.dirname(manifestPath);\n const loader = new AssetManifestLoader();\n const manifest = await loader.loadManifest(cdkOutDir, lambda.stack.stackName);\n const entry = manifest ? getDockerImageBySourceHash(manifest, lambda.imageUri) : undefined;\n if (entry) {\n imageRef = await buildContainerImage(entry.asset, cdkOutDir, {\n architecture: lambda.architecture,\n });\n localBuilt = true;\n }\n }\n if (!localBuilt) {\n if (!parseEcrUri(lambda.imageUri)) {\n throw new Error(\n `Container Lambda '${lambda.logicalId}' has no matching asset in cdk.out, and Code.ImageUri ` +\n `'${lambda.imageUri}' is not an ECR URI ${getEmbedConfig().binaryName} can authenticate against. ` +\n 'Re-synthesize the CDK app or deploy the image to ECR first.'\n );\n }\n logger.info(`No matching cdk.out asset for ${lambda.imageUri}; falling back to ECR pull...`);\n imageRef = await pullEcrImage(lambda.imageUri, {\n skipPull: opts.skipPull === true,\n ...(opts.region !== undefined && { region: opts.region }),\n ...(opts.ecrRoleArn !== undefined && { ecrRoleArn: opts.ecrRoleArn }),\n });\n }\n\n return {\n image: imageRef!,\n mounts: [],\n cmd: lambda.imageConfig.command ?? [],\n platform,\n ...(lambda.imageConfig.entryPoint &&\n lambda.imageConfig.entryPoint.length > 0 && {\n entryPoint: lambda.imageConfig.entryPoint,\n }),\n ...(lambda.imageConfig.workingDirectory !== undefined && {\n workingDir: lambda.imageConfig.workingDirectory,\n }),\n };\n}\n\n/**\n * A booted, invokable Lambda behind the front-door. Reuses the same RIE\n * container machinery as `cdkl invoke`. Construct via {@link createFrontDoorLambdaRunner}.\n */\nexport interface FrontDoorLambdaRunner {\n /** Logical id of the backing `AWS::Lambda::Function` (diagnostics). */\n readonly logicalId: string;\n /** Boot the RIE container (idempotent). Throws if the container never becomes ready. */\n start(): Promise<void>;\n /** Invoke the warm container with the ALB event; returns the parsed RIE payload. */\n invoke(event: unknown, timeoutMs?: number): Promise<unknown>;\n /** Tear the container down + remove any materialized tmpdirs (idempotent). */\n stop(): Promise<void>;\n}\n\n/**\n * Build a {@link FrontDoorLambdaRunner} for a resolved Lambda. Construction is\n * pure (no docker work); `start()` does the boot. The invoke timeout defaults\n * to `max(30s, timeoutSec * 2 * 1000)` — same formula as `cdkl invoke`.\n */\nexport function createFrontDoorLambdaRunner(\n lambda: ResolvedLambda,\n opts: FrontDoorLambdaRunnerOptions\n): FrontDoorLambdaRunner {\n const logger = getLogger().child('front-door-lambda');\n const defaultTimeoutMs = Math.max(30_000, lambda.timeoutSec * 2 * 1000);\n\n let plan: ImagePlan | undefined;\n let containerId: string | undefined;\n let hostPort: number | undefined;\n let stopLogStream: (() => void) | undefined;\n let starting: Promise<void> | undefined;\n let stopped = false;\n\n async function doStart(): Promise<void> {\n plan =\n lambda.kind === 'zip'\n ? await resolveZipImagePlan(lambda, opts)\n : await resolveContainerImagePlan(lambda, opts);\n const port = await pickFreePort();\n hostPort = port;\n const name = `${getEmbedConfig().resourceNamePrefix}-alblambda-${lambda.logicalId}-${process.pid}-${Math.floor(\n Math.random() * 1_000_000\n )}`;\n const env: Record<string, string> = {\n AWS_LAMBDA_FUNCTION_NAME: lambda.logicalId,\n AWS_LAMBDA_FUNCTION_MEMORY_SIZE: String(lambda.memoryMb),\n AWS_LAMBDA_FUNCTION_TIMEOUT: String(lambda.timeoutSec),\n AWS_LAMBDA_FUNCTION_VERSION: '$LATEST',\n AWS_LAMBDA_LOG_GROUP_NAME: `/aws/lambda/${lambda.logicalId}`,\n AWS_LAMBDA_LOG_STREAM_NAME: 'local',\n ...forwardAwsEnv(),\n };\n logger.info(\n `Starting Lambda target container for ${lambda.logicalId} (image=${plan.image}, port=${port})...`\n );\n const id = await runDetached({\n image: plan.image,\n mounts: plan.mounts,\n env,\n cmd: plan.cmd,\n hostPort: port,\n host: opts.containerHost,\n name,\n ...(plan.platform !== undefined && { platform: plan.platform }),\n ...(plan.entryPoint !== undefined && { entryPoint: plan.entryPoint }),\n ...(plan.workingDir !== undefined && { workingDir: plan.workingDir }),\n });\n containerId = id;\n stopLogStream = opts.streamLogs === false ? undefined : streamLogs(id);\n try {\n await waitForRieReady(opts.containerHost, port, 30_000);\n } catch (err) {\n // RIE never came up — clean up before propagating so we don't leak.\n try {\n stopLogStream?.();\n } catch {\n /* swallow */\n }\n await removeContainer(id).catch(() => undefined);\n containerId = undefined;\n throw err;\n }\n }\n\n return {\n logicalId: lambda.logicalId,\n async start(): Promise<void> {\n if (stopped) throw new Error('FrontDoorLambdaRunner.start called after stop');\n if (containerId) return;\n if (!starting) starting = doStart();\n await starting;\n },\n async invoke(event: unknown, timeoutMs?: number): Promise<unknown> {\n if (!containerId || hostPort === undefined) {\n throw new Error(\n `FrontDoorLambdaRunner('${lambda.logicalId}').invoke called before start() completed.`\n );\n }\n const result = await invokeRie(\n opts.containerHost,\n hostPort,\n event,\n timeoutMs ?? defaultTimeoutMs\n );\n return result.payload;\n },\n async stop(): Promise<void> {\n if (stopped) return;\n stopped = true;\n try {\n stopLogStream?.();\n } catch (err) {\n logger.debug(\n `stopLogStream(${lambda.logicalId}) failed: ${err instanceof Error ? err.message : String(err)}`\n );\n }\n if (containerId) {\n try {\n await removeContainer(containerId);\n } catch (err) {\n logger.warn(\n `Failed to remove Lambda target container for ${lambda.logicalId}: ${err instanceof Error ? err.message : String(err)}. Continuing cleanup.`\n );\n }\n containerId = undefined;\n }\n if (plan?.inlineTmpDir) {\n try {\n rmSync(plan.inlineTmpDir, { recursive: true, force: true });\n } catch (err) {\n logger.debug(\n `Failed to remove inline-code tmpdir ${plan.inlineTmpDir}: ${err instanceof Error ? err.message : String(err)}`\n );\n }\n }\n },\n };\n}\n","import { verifyJwtAuthorizer, type JwksCache } from './cognito-jwt.js';\nimport type { AuthCheck } from './front-door-server.js';\nimport type { FrontDoorAuthGuard } from './elb-front-door-resolver.js';\nimport { getLogger } from '../utils/logger.js';\n\n/**\n * Build the front-door's auth-check callback for an\n * `authenticate-cognito` / `authenticate-oidc` guard.\n *\n * Local-dev parity model: the cloud-side ALB authenticates the user via a\n * full OAuth roundtrip (redirect to the IdP's authorize endpoint, callback,\n * AWSELBAuthSessionCookie issuance). The local front-door does NOT reproduce\n * the roundtrip — it accepts EITHER a Bearer JWT (verified against the\n * Cognito JWKS / OIDC discovery URL just like API Gateway's JWT authorizer)\n * OR an `AWSELBAuthSessionCookie-*` cookie pass-through (the user is acting\n * as if already signed in via the deployed ALB — `--bearer-token` makes the\n * Bearer-JWT path the headline path; the cookie pass-through is convenience\n * for hitting the local front-door from a browser session that already\n * authenticated through the deployed ALB).\n *\n * `--no-verify-auth` (the `noVerifyAuth` flag) short-circuits everything to\n * `allow: true` — explicitly off-switching the guard for local dev where\n * you do not want to mint a Bearer token at all.\n *\n * `--bearer-token <jwt>` (the `bearerToken` arg) makes the supplied token\n * the default `Authorization` value when the inbound request has none.\n */\nexport function buildAuthCheck(\n guard: FrontDoorAuthGuard,\n jwksCache: JwksCache,\n opts: {\n /** When true, the check resolves `{ allow: true }` for every request. */\n noVerifyAuth?: boolean;\n /** Injected as the default `Authorization: Bearer <jwt>` when missing. */\n bearerToken?: string;\n /**\n * Shared \"JWKS warn-once\" Set. Lifted to caller scope so two rules\n * pointing at the same Cognito JWKS URL share the warn-on-first-request\n * de-dupe instead of each warning independently.\n */\n warned?: Set<string>;\n } = {}\n): AuthCheck {\n const realm = guard.label;\n\n if (opts.noVerifyAuth === true) {\n return {\n realm,\n check: async () => ({ allow: true }),\n };\n }\n\n // De-dupe \"JWKS unreachable -> pass-through\" warn lines across requests for\n // the same authorizer URL (matches how start-api's JWT authorizers behave).\n // Falls back to a per-AuthCheck Set when the caller did not supply one.\n const warned = opts.warned ?? new Set<string>();\n const sessionCookiePrefix = guard.sessionCookieName;\n const injectedBearer = opts.bearerToken;\n\n return {\n realm,\n check: async (headers) => {\n // 1) Cookie pass-through. The deployed ALB issues\n // `AWSELBAuthSessionCookie-N` cookies after a successful sign-in;\n // when one is present on the request we accept it locally so the\n // browser session that already authenticated through the cloud\n // ALB keeps working against the local front-door.\n const cookieHeader = headerValue(headers['cookie']);\n if (cookieHeader && cookieHasSessionPrefix(cookieHeader, sessionCookiePrefix)) {\n return { allow: true };\n }\n\n // 2) Bearer JWT — inbound `Authorization` header wins; otherwise fall\n // back to `--bearer-token` when supplied. A non-Bearer scheme\n // (e.g. `Basic`) is rejected with a scheme-specific reason so the\n // user does not assume the JWKS / iss / aud check rejected them.\n let authorization = headerValue(headers['authorization']);\n if ((!authorization || authorization === '') && injectedBearer !== undefined) {\n authorization = `Bearer ${injectedBearer}`;\n }\n if (!authorization || authorization === '') {\n return {\n allow: false,\n reason:\n 'No Bearer token presented. Supply Authorization: Bearer <jwt> or pass --bearer-token <jwt>.',\n };\n }\n if (!authorization.toLowerCase().startsWith('bearer ')) {\n return {\n allow: false,\n reason:\n 'Authorization scheme is not Bearer; the ALB authenticate-* guard only accepts Bearer JWTs.',\n };\n }\n\n // The existing `verifyJwtAuthorizer` handles both shapes — Cognito's\n // direct JWKS URL (when `region` + `userPoolId` are present) and the\n // generic OIDC-issuer discovery URL.\n try {\n const result = await verifyJwtAuthorizer(\n {\n kind: 'jwt',\n logicalId: guard.label,\n declaredAt: 'start-alb authenticate-* action',\n issuer: guard.issuer,\n audience: [guard.audience],\n ...(guard.region !== undefined && { region: guard.region }),\n ...(guard.userPoolId !== undefined && { userPoolId: guard.userPoolId }),\n },\n authorization,\n jwksCache,\n { warned }\n );\n if (result.allow) return { allow: true };\n return {\n allow: false,\n reason: 'Bearer token rejected (signature / iss / aud / exp check failed).',\n };\n } catch (err) {\n getLogger()\n .child('front-door-auth')\n .debug(`auth check threw: ${err instanceof Error ? err.message : String(err)}`);\n return { allow: false, reason: 'Auth check failed.' };\n }\n },\n };\n}\n\nfunction headerValue(raw: string | string[] | undefined): string | undefined {\n if (raw === undefined) return undefined;\n if (Array.isArray(raw)) return raw[0];\n return raw;\n}\n\n/**\n * True iff the `Cookie` header contains a cookie whose name starts with\n * `<sessionCookiePrefix>` (ALB suffixes the cookie with `-0` / `-1` / ...\n * when the auth session payload exceeds the per-cookie size limit, so we\n * match the prefix, not an exact name).\n */\nfunction cookieHasSessionPrefix(cookieHeader: string, sessionCookiePrefix: string): boolean {\n for (const pair of cookieHeader.split(';')) {\n const eq = pair.indexOf('=');\n const name = (eq === -1 ? pair : pair.slice(0, eq)).trim();\n if (name === sessionCookiePrefix || name.startsWith(`${sessionCookiePrefix}-`)) {\n return true;\n }\n }\n return false;\n}\n","import { readFileSync } from 'node:fs';\nimport { Command, Option } from 'commander';\nimport {\n appOptions,\n commonOptions,\n contextOptions,\n deprecatedRegionOption,\n parseContextOptions,\n warnIfDeprecatedRegion,\n} from '../options.js';\nimport { getLogger } from '../../utils/logger.js';\nimport { applyRoleArnIfSet } from '../../utils/role-arn.js';\nimport { CdkLocalError, LocalStartServiceError } from '../../utils/error-handler.js';\nimport { resolveMultiTarget } from '../../local/target-picker.js';\nimport type { TargetEntry } from '../../local/target-lister.js';\nimport { singleFlight } from '../../utils/single-flight.js';\nimport { Synthesizer, type SynthesisOptions } from '../../synthesis/synthesizer.js';\nimport { resolveApp } from '../config-loader.js';\nimport { ensureDockerAvailable } from '../../local/docker-runner.js';\nimport { resolveProfileCredentials } from './local-start-api.js';\nimport {\n writeProfileCredentialsFile,\n type ProfileCredentialsFile,\n} from './local-profile-credentials-file.js';\nimport {\n applyCrossStackResolverToTask,\n derivePartitionAndUrlSuffix,\n detectEcsImageResolutionNeeds,\n parseEcsTarget,\n TASK_ROLE_ACCOUNT_PLACEHOLDER,\n type EcsImageResolutionContext,\n} from '../../local/ecs-task-resolver.js';\nimport { resolveEcsServiceTarget } from '../../local/ecs-service-resolver.js';\nimport {\n createServiceRunState,\n startEcsService,\n type ServiceController,\n type ServiceDiscoveryContext,\n type ServiceRunnerOptions,\n type ServiceRunState,\n} from '../../local/ecs-service-runner.js';\nimport type { StackInfo } from '../../synthesis/assembly-reader.js';\nimport {\n cleanupEcsRun,\n parseHostPortOverrides,\n type RunEcsTaskOptions,\n} from '../../local/ecs-task-runner.js';\nimport { matchStacks } from '../stack-matcher.js';\nimport {\n createLocalStateProvider,\n rejectExplicitCfnStackWithMultipleStacks,\n resolveCfnFallbackRegion,\n type ExtraStateProviders,\n} from './local-state-source.js';\nimport { getEmbedConfig } from '../../local/embed-config.js';\nimport type { LocalStateProvider } from '../../local/local-state-provider.js';\nimport type { SubstitutionContext } from '../../local/state-resolver.js';\nimport { CloudMapRegistry } from '../../local/cloud-map-registry.js';\nimport { buildCloudMapIndex, type CloudMapIndex } from '../../local/cloud-map-resolver.js';\nimport {\n createSharedSvcNetwork,\n destroyTaskNetwork,\n type TaskNetwork,\n} from '../../local/ecs-network.js';\nimport { FrontDoorEndpointPool } from '../../local/front-door-pool.js';\nimport {\n startFrontDoorServer,\n type FrontDoorDispatchTarget,\n type StartedFrontDoorServer,\n type RouteAction,\n type WeightedForwardTarget,\n} from '../../local/front-door-server.js';\nimport {\n matchAlbPathRule,\n type AlbHttpHeaderCondition,\n type AlbPathRule,\n type AlbQueryStringCondition,\n} from '../../local/alb-path-matcher.js';\nimport {\n resolveFrontDoorTlsMaterials,\n type FrontDoorTlsMaterials,\n} from '../../local/front-door-tls.js';\nimport type { ResolvedLambda } from '../../local/lambda-resolver.js';\nimport {\n createFrontDoorLambdaRunner,\n type FrontDoorLambdaRunner,\n} from '../../local/front-door-lambda-runner.js';\nimport type { FrontDoorAuthGuard } from '../../local/elb-front-door-resolver.js';\nimport { buildAuthCheck } from '../../local/front-door-auth.js';\nimport { createJwksCache } from '../../local/cognito-jwt.js';\n\n/**\n * Neutral ECS-service emulator orchestration shared by `cdkl start-service`\n * (pure replica runner) and `cdkl start-alb` (ALB front-door entry). It synths,\n * lets a {@link EmulatorStrategy} pick targets and turn them into the concrete\n * set of {@link ServiceBoot}s (plus an optional {@link FrontDoorPlan}), then\n * boots every service replica pool (shared docker network + Cloud Map registry\n * + restart watcher) and, when a front-door plan is present, stands up ONE\n * host-side reverse proxy per listener port that path-routes across the\n * services it fronts.\n *\n * The front-door MECHANISM (generic \"expose services' replicas on host ports\n * and path-route between them\") lives here; the ALB-specific resolution (which\n * listener fronts which service on which path) lives entirely in the\n * `start-alb` command. `start-service` returns no front-door plan, so it never\n * touches the front-door path.\n */\n\n/** Shared CLI option shape for both ECS-service commands. */\nexport interface EcsServiceEmulatorOptions {\n app?: string;\n output: string;\n verbose: boolean;\n region?: string;\n profile?: string;\n roleArn?: string;\n context?: string[];\n cluster: string;\n envVars?: string;\n containerHost: string;\n /** See `local-run-task.ts` for the same flag's three-state grammar. */\n assumeTaskRole?: string | boolean;\n pull: boolean;\n ecrRoleArn?: string;\n /** `--host-port <containerPort=hostPort>` overrides (start-service; repeatable). */\n hostPort?: string[];\n /** `--lb-port <listenerPort=hostPort>` front-door overrides (start-alb; repeatable). */\n lbPort?: string[];\n /**\n * Path to a PEM-encoded server cert for HTTPS front-door listeners\n * (start-alb). Must be set together with `--tls-key`. Absent =\n * auto-generated self-signed cert (cached under\n * `$XDG_CACHE_HOME/cdk-local/alb-https/`).\n */\n tlsCert?: string;\n /**\n * Path to a PEM-encoded server private key for HTTPS front-door listeners\n * (start-alb). Must be set together with `--tls-cert`.\n */\n tlsKey?: string;\n /**\n * Local enforcement of authenticate-* guards (start-alb only). Defaults to\n * `true`; `--no-verify-auth` flips it to `false` (Commander convention)\n * which makes every request pass the guard. Useful for local dev that does\n * not want to mint a Bearer token.\n */\n verifyAuth?: boolean;\n /**\n * Default Bearer JWT injected as `Authorization: Bearer <jwt>` when the\n * inbound request has none (start-alb only). Verified against the same\n * JWKS / OIDC discovery URL the deployed ALB would.\n */\n bearerToken?: string;\n platform?: string;\n /** Cap on local replica count regardless of template `DesiredCount`. */\n maxTasks: number;\n /** Restart-on-exit policy: 'on-failure' (default), 'always', or 'none'. */\n restartPolicy: 'on-failure' | 'always' | 'none';\n /**\n * Issue #606: alternative state source. Reads physical IDs from a\n * deployed CloudFormation stack via `ListStackResources`.\n */\n fromCfnStack?: string | boolean;\n stackRegion?: string;\n /** Host-injected extra state-source flag fields. */\n [key: string]: unknown;\n}\n\n/** One ECS service to boot. Front-door wiring lives in the {@link FrontDoorPlan}. */\nexport interface ServiceBoot {\n /** Service target string (`Stack:LogicalId` or `Stack/Path`). */\n target: string;\n}\n\n/**\n * The backing target one weighted forward target routes to: either an ECS\n * service (round-robin a replica pool) or a Lambda function (invoke locally per\n * request, #123). A single weighted forward may mix both.\n */\nexport type PlannedForwardTarget = PlannedEcsForwardTarget | PlannedLambdaForwardTarget;\n\n/** The backing (service target, container) an ECS weighted forward target routes to. */\nexport interface PlannedEcsForwardTarget {\n kind: 'ecs';\n /** Service target string (`Stack:LogicalId`) whose replica pool serves this. */\n serviceTarget: string;\n /** Container the listener forwards to. */\n targetContainerName: string;\n /** Container port the target group targets. */\n targetContainerPort: number;\n /** Forward weight for weighted routing (single-target forward = 1). */\n weight: number;\n}\n\n/** A Lambda weighted forward target (#123): a resolved function invoked locally per request. */\nexport interface PlannedLambdaForwardTarget {\n kind: 'lambda';\n /** The resolved Lambda the front-door boots + invokes. */\n lambda: ResolvedLambda;\n /** Target-group ARN-or-id surfaced under the event's `requestContext.elb`. */\n targetGroupArn: string;\n /** Whether the TG has `lambda.multi_value_headers.enabled=true`. */\n multiValueHeaders: boolean;\n /** Forward weight for weighted routing (single-target forward = 1). */\n weight: number;\n}\n\n/** A planned forward action: one or more weighted backing targets (ECS and/or Lambda). */\nexport interface PlannedForwardAction {\n kind: 'forward';\n targets: PlannedForwardTarget[];\n}\n\n/** A planned redirect action (no backing pool). */\nexport interface PlannedRedirectAction {\n kind: 'redirect';\n statusCode: 301 | 302;\n protocol?: string;\n host?: string;\n port?: string;\n path?: string;\n query?: string;\n}\n\n/** A planned fixed-response action (no backing pool). */\nexport interface PlannedFixedResponseAction {\n kind: 'fixed-response';\n statusCode: number;\n contentType?: string;\n messageBody?: string;\n}\n\n/** Any planned listener / rule action (the strategy-side mirror of the front-door's RouteAction). */\nexport type PlannedAction =\n | PlannedForwardAction\n | PlannedRedirectAction\n | PlannedFixedResponseAction;\n\n/** One host front-door listener: a bound host port + its routing table. */\nexport interface PlannedFrontDoorListener {\n /** ALB listener port (for the `X-Forwarded-Port` header / logs). */\n listenerPort: number;\n /** Host port to bind (the listener port, or its `--lb-port` override). */\n hostPort: number;\n /** Listener protocol (`HTTP` or `HTTPS`); drives TLS termination + X-Forwarded-Proto. */\n protocol: 'HTTP' | 'HTTPS';\n /** Default action (absent for a rules-only listener -> 404 on miss). */\n defaultAction?: PlannedAction;\n /** Default action's authenticate-* guard (set when DefaultActions[] wrapped one). */\n defaultAuthGuard?: FrontDoorAuthGuard;\n /** Rules, evaluated by priority (lower first); each carries up to all six ALB condition fields. */\n rules: Array<{\n priority: number;\n pathPatterns: string[];\n hostPatterns: string[];\n httpHeaderConditions: AlbHttpHeaderCondition[];\n httpRequestMethods: string[];\n queryStringConditions: AlbQueryStringCondition[];\n sourceIpCidrs: string[];\n action: PlannedAction;\n /** authenticate-* guard wrapping the action (set when Actions[] declared one). */\n authGuard?: FrontDoorAuthGuard;\n }>;\n}\n\n/** The full set of host front-doors to stand up for one emulator invocation. */\nexport interface FrontDoorPlan {\n listeners: PlannedFrontDoorListener[];\n}\n\n/** Mutable front-door pool list for a single service's runner (one entry per (container, port)). */\ntype FrontDoorServicePools = Array<{\n pool: FrontDoorEndpointPool;\n targetContainerName: string;\n targetContainerPort: number;\n}>;\n\n/**\n * Per-command behavior the neutral orchestration delegates to: how to pick\n * targets when none are passed, how to turn chosen targets into concrete\n * service boots (+ an optional front-door plan + warnings), and the `--lb-port`\n * host-port remap.\n */\nexport interface EmulatorStrategy {\n pickEntries(stacks: StackInfo[]): TargetEntry[];\n pickerMessage: string;\n pickerNoun: string;\n onMissing(): CdkLocalError;\n resolveBoots(\n stacks: StackInfo[],\n chosenTargets: string[]\n ): { boots: ServiceBoot[]; frontDoor?: FrontDoorPlan; warnings: string[] };\n lbPortOverrides: Record<number, number>;\n}\n\n/**\n * Long-running ECS-service emulator. Synths the app, resolves the strategy's\n * targets into service boots, boots every replica pool (with optional\n * front-door), and blocks until `^C`. Idempotent single-flight cleanup tears\n * down every replica + front-door server + the shared network + sidecar.\n */\nexport async function runEcsServiceEmulator(\n targets: string[],\n options: EcsServiceEmulatorOptions,\n strategy: EmulatorStrategy,\n extraStateProviders: ExtraStateProviders | undefined\n): Promise<void> {\n const logger = getLogger();\n if (options.verbose) logger.setLevel('debug');\n\n warnIfDeprecatedRegion(options);\n\n // Commander resolves `--no-pull` to `options.pull = false` (the default is\n // true). Compute the \"should we skip docker pull?\" flag once here.\n const skipPull = options.pull === false;\n\n type PerTarget = {\n boot: ServiceBoot;\n runState: ServiceRunState;\n controller?: ServiceController;\n };\n let perTarget: PerTarget[] = [];\n\n let sigintHandler: (() => void) | undefined;\n let sigintCount = 0;\n let sharedNetwork: TaskNetwork | undefined;\n let profileCredsFile: ProfileCredentialsFile | undefined;\n // Host-side ALB front-door servers (one per listener port), shared across the\n // services they front. Created once before the boot loop; torn down after all\n // replicas are down so no request is forwarded to a vanished container.\n let frontDoorServers: StartedFrontDoorServer[] = [];\n // Per-service-target front-door pools to thread into each runner.\n let frontDoorByService = new Map<string, FrontDoorServicePools>();\n // Long-lived Lambda-target containers behind the front-door (#123). Torn\n // down alongside the front-door servers so no request is dispatched to a\n // vanished container.\n let frontDoorLambdaRunners: FrontDoorLambdaRunner[] = [];\n\n const cleanup = singleFlight(\n async (): Promise<void> => {\n await Promise.allSettled(\n perTarget.map(async (pt) => {\n if (pt.controller) {\n await pt.controller.shutdown();\n } else {\n // SIGINT-during-bootOneTarget early-failure path.\n await Promise.allSettled(\n pt.runState.replicas\n .map((r) => r.inFlightBoot)\n .filter((p): p is Promise<void> => p !== undefined)\n );\n await Promise.allSettled(\n pt.runState.replicas.map((r) =>\n cleanupEcsRun(r.state, { keepRunning: false }).catch(() => undefined)\n )\n );\n }\n })\n );\n // Close the front-door servers AFTER every replica is down so no in-flight\n // request is forwarded to a torn-down container. Idempotent.\n await Promise.allSettled(\n frontDoorServers.map((s) =>\n s\n .close()\n .catch((err) =>\n getLogger().warn(\n `front-door server teardown failed: ${err instanceof Error ? err.message : String(err)}`\n )\n )\n )\n );\n frontDoorServers = [];\n // Stop the Lambda-target containers AFTER the front-door servers are\n // closed so no in-flight request lands on a torn-down RIE container.\n await Promise.allSettled(\n frontDoorLambdaRunners.map((r) =>\n r\n .stop()\n .catch((err) =>\n getLogger().warn(\n `front-door Lambda target teardown failed: ${err instanceof Error ? err.message : String(err)}`\n )\n )\n )\n );\n frontDoorLambdaRunners = [];\n if (profileCredsFile) {\n try {\n await profileCredsFile.dispose();\n } catch (err) {\n getLogger().warn(\n `Failed to remove profile credentials tmpdir ${profileCredsFile.hostPath}: ${err instanceof Error ? err.message : String(err)}`\n );\n }\n profileCredsFile = undefined;\n }\n if (sharedNetwork) {\n try {\n await destroyTaskNetwork(sharedNetwork);\n } catch (err) {\n getLogger().warn(\n `shared service network teardown failed: ${err instanceof Error ? err.message : String(err)}`\n );\n }\n sharedNetwork = undefined;\n }\n },\n (err) =>\n getLogger().warn(\n `service cleanup failed: ${err instanceof Error ? err.message : String(err)}`\n )\n );\n\n try {\n await applyRoleArnIfSet({ roleArn: options.roleArn, region: options.region });\n await ensureDockerAvailable();\n\n const appCmd = resolveApp(options.app);\n if (!appCmd) {\n throw new Error(\n `No CDK app specified. Pass --app, set ${getEmbedConfig().envPrefix}_APP, or add \"app\" to cdk.json.`\n );\n }\n\n logger.info('Synthesizing CDK app...');\n const synthesizer = new Synthesizer();\n const context = parseContextOptions(options.context);\n const synthOpts: SynthesisOptions = {\n app: appCmd,\n output: options.output,\n ...(options.region && { region: options.region }),\n ...(options.profile && { profile: options.profile }),\n ...(Object.keys(context).length > 0 && { context }),\n };\n const { stacks } = await synthesizer.synthesize(synthOpts);\n\n const resolvedTargets = await resolveMultiTarget(targets, {\n entries: strategy.pickEntries(stacks),\n message: strategy.pickerMessage,\n noun: strategy.pickerNoun,\n onMissing: () => strategy.onMissing(),\n });\n\n const { boots, frontDoor, warnings } = strategy.resolveBoots(stacks, resolvedTargets);\n for (const w of warnings) logger.warn(w);\n // A front-door whose listeners forward ONLY to Lambda targets (#123) has no\n // ECS service to boot; it is still runnable (the Lambda containers live\n // behind the front-door). Only error when there is nothing to run at all.\n const hasFrontDoorListeners = !!frontDoor && frontDoor.listeners.length > 0;\n if (boots.length === 0 && !hasFrontDoorListeners) {\n throw new LocalStartServiceError(\n `No runnable target resolved from ${resolvedTargets.join(', ')}.`\n );\n }\n\n // Issue #606: reject explicit `--from-cfn-stack <name>` when multiple\n // services are booted in one invocation.\n rejectExplicitCfnStackWithMultipleStacks(options, boots.length);\n perTarget = boots.map((boot) => ({ boot, runState: createServiceRunState() }));\n\n const cloudMapIndexByStack = new Map<string, CloudMapIndex>();\n for (const stack of stacks) {\n const index = buildCloudMapIndex(stack);\n cloudMapIndexByStack.set(stack.stackName, index);\n for (const w of index.warnings) logger.warn(w);\n }\n\n const registry = new CloudMapRegistry();\n const sidecarCredentials = await resolveSharedSidecarCredentials(options);\n try {\n sharedNetwork = await createSharedSvcNetwork({\n prefix: options.cluster,\n skipPull,\n cluster: options.cluster,\n ...(sidecarCredentials !== undefined && { credentials: sidecarCredentials }),\n });\n } catch (err) {\n throw new LocalStartServiceError(\n `Failed to create shared service network: ${err instanceof Error ? err.message : String(err)}`\n );\n }\n if (options.profile && sidecarCredentials) {\n profileCredsFile = await writeProfileCredentialsFile(options.profile, sidecarCredentials);\n }\n const discovery: ServiceDiscoveryContext = {\n registry,\n cloudMapIndexByStack,\n sharedNetwork,\n };\n\n // Stand up the host front-door(s) BEFORE booting replicas: the pools start\n // empty (so the proxy answers 503 until replicas register) and a host-port\n // bind failure should surface before any docker budget is spent. No-op when\n // the strategy returned no plan (start-service / pure compute).\n if (frontDoor && frontDoor.listeners.length > 0) {\n const built = await buildFrontDoor(frontDoor, options, logger);\n frontDoorServers = built.servers;\n frontDoorByService = built.frontDoorByService;\n frontDoorLambdaRunners = built.lambdaRunners;\n }\n\n sigintHandler = (): void => {\n sigintCount += 1;\n if (sigintCount >= 2) {\n process.stderr.write('Force-exit on second ^C; container cleanup skipped.\\n');\n process.exit(130);\n }\n logger.info('Stopping service(s)...');\n void cleanup().then(() => process.exit(130));\n };\n process.on('SIGINT', sigintHandler);\n process.on('SIGTERM', sigintHandler);\n\n // Boot every target SEQUENTIALLY so a first-target failure surfaces before\n // we burn docker budget on the rest.\n for (const pt of perTarget) {\n pt.controller = await bootOneTarget(\n pt.boot,\n pt.runState,\n stacks,\n options,\n discovery,\n skipPull,\n extraStateProviders,\n profileCredsFile,\n frontDoorByService.get(pt.boot.target)\n );\n }\n\n if (perTarget.length > 0) {\n const summary = perTarget\n .map(\n (pt) =>\n `${pt.controller!.service.serviceName} (${pt.controller!.activeReplicaCount()} replica(s))`\n )\n .join(', ');\n logger.info(`Service(s) running: ${summary}.`);\n } else {\n // Lambda-target-only front-door (#123): no ECS replicas, just the\n // Lambda container(s) behind the front-door.\n logger.info(\n `Service(s) running: ${frontDoorLambdaRunners.length} Lambda target(s) behind the ALB front-door.`\n );\n }\n logger.info('Press ^C to shut down.');\n\n if (perTarget.length > 0) {\n await Promise.all(perTarget.map((pt) => pt.controller!.waitForShutdown()));\n } else {\n // Block on a SIGINT/SIGTERM that resolves via the cleanup -> process.exit\n // path. The front-door + Lambda containers keep serving until then.\n await new Promise<void>(() => {\n /* resolved only by the SIGINT/SIGTERM handler's process.exit */\n });\n }\n } finally {\n if (sigintHandler) {\n process.off('SIGINT', sigintHandler);\n process.off('SIGTERM', sigintHandler);\n }\n await cleanup();\n }\n}\n\nasync function bootOneTarget(\n boot: ServiceBoot,\n runState: ServiceRunState,\n stacks: StackInfo[],\n options: EcsServiceEmulatorOptions,\n discovery: ServiceDiscoveryContext,\n skipPull: boolean,\n extraStateProviders: ExtraStateProviders | undefined,\n profileCredsFile: ProfileCredentialsFile | undefined,\n frontDoorPools: FrontDoorServicePools | undefined\n): Promise<ServiceController> {\n const parsed = parseEcsTarget(boot.target);\n const candidate = pickCandidateStack(parsed.stackPattern, stacks);\n const stateProvider = createLocalStateProvider(\n options,\n candidate?.stackName ?? '',\n await resolveCfnFallbackRegion(options, candidate?.region),\n extraStateProviders\n );\n\n try {\n return await runOneTarget(\n boot,\n runState,\n stacks,\n options,\n discovery,\n skipPull,\n stateProvider,\n profileCredsFile,\n frontDoorPools\n );\n } finally {\n if (stateProvider) stateProvider.dispose();\n }\n}\n\nasync function runOneTarget(\n boot: ServiceBoot,\n runState: ServiceRunState,\n stacks: StackInfo[],\n options: EcsServiceEmulatorOptions,\n discovery: ServiceDiscoveryContext,\n skipPull: boolean,\n stateProvider: LocalStateProvider | undefined,\n profileCredsFile: ProfileCredentialsFile | undefined,\n frontDoorPools: FrontDoorServicePools | undefined\n): Promise<ServiceController> {\n const logger = getLogger();\n const target = boot.target;\n\n const imageContext = await buildEcsImageResolutionContext(target, stacks, options, stateProvider);\n const service = resolveEcsServiceTarget(target, stacks, imageContext);\n logger.info(\n `Target: ${service.stack.stackName}/${service.serviceLogicalId} ` +\n `(service=${service.serviceName}, desiredCount=${service.desiredCount}, ` +\n `task=${service.task.taskDefinitionLogicalId})`\n );\n if (service.serviceConnect) {\n logger.info(\n `Service Connect: namespace='${service.serviceConnect.namespaceName}', ` +\n `${service.serviceConnect.services.length} service(s) registered for peer discovery.`\n );\n }\n if (service.serviceRegistries.length > 0) {\n logger.info(`Cloud Map: ${service.serviceRegistries.length} ServiceRegistry binding(s).`);\n }\n\n // Cross-stack env / secret resolution post-pass.\n const taskStack = stacks.find((s) => s.stackName === service.stack.stackName) ?? service.stack;\n const taskNeeds = detectEcsImageResolutionNeeds(taskStack);\n if (stateProvider && taskNeeds.needsCrossStackResolver) {\n const consumerRegion =\n options.region ??\n process.env['AWS_REGION'] ??\n process.env['AWS_DEFAULT_REGION'] ??\n service.stack.region ??\n 'us-east-1';\n const resolver = await stateProvider.buildCrossStackResolver(consumerRegion);\n if (resolver) {\n const subContext: SubstitutionContext = {\n resources: imageContext?.stateResources ?? {},\n ...(imageContext?.pseudoParameters && {\n pseudoParameters: imageContext.pseudoParameters,\n }),\n ...(imageContext?.stateParameters && {\n parameters: imageContext.stateParameters,\n }),\n ...(imageContext?.stateSensitiveParameters?.length && {\n sensitiveParameters: new Set(imageContext.stateSensitiveParameters),\n }),\n consumerRegion,\n crossStackResolver: resolver,\n };\n await applyCrossStackResolverToTask(service.task, subContext);\n }\n } else if (!stateProvider && taskNeeds.needsCrossStackResolver) {\n logger.warn(\n 'Container Environment / Secrets entries contain Fn::ImportValue / Fn::GetStackOutput intrinsics. ' +\n 'Pass a state-source flag (e.g. --from-cfn-stack or a host-provided extension) to substitute them against deployed state.'\n );\n }\n\n // Per-service task-role credentials.\n let assumedCredentials: RunEcsTaskOptions['taskCredentials'];\n let resolvedRoleArn: string | undefined;\n if (options.assumeTaskRole === true) {\n if (!service.task.taskRoleArn) {\n throw new LocalStartServiceError(\n `--assume-task-role passed without an ARN but service '${service.serviceLogicalId}' ` +\n `has no resolvable TaskRoleArn. Pass the ARN explicitly: --assume-task-role <arn>`\n );\n }\n resolvedRoleArn = await resolvePlaceholderAccount(service.task.taskRoleArn, options.region);\n assumedCredentials = await assumeTaskRole(resolvedRoleArn, options.region);\n } else if (typeof options.assumeTaskRole === 'string') {\n resolvedRoleArn = options.assumeTaskRole;\n assumedCredentials = await assumeTaskRole(resolvedRoleArn, options.region);\n }\n\n const envOverrides = readEnvOverridesFile(options.envVars);\n\n const taskOpts: RunEcsTaskOptions = {\n cluster: options.cluster,\n containerHost: options.containerHost,\n skipPull,\n keepRunning: false,\n detach: true,\n };\n if (envOverrides) taskOpts.envOverrides = envOverrides;\n if (assumedCredentials) taskOpts.taskCredentials = assumedCredentials;\n if (resolvedRoleArn) taskOpts.taskRoleArn = resolvedRoleArn;\n if (options.platform) taskOpts.platformOverride = options.platform;\n if (options.region) taskOpts.region = options.region;\n if (options.ecrRoleArn) taskOpts.ecrRoleArn = options.ecrRoleArn;\n if (options.profile) taskOpts.profile = options.profile;\n const hostPortOverrides = parseHostPortOverrides(options.hostPort);\n if (Object.keys(hostPortOverrides).length > 0) taskOpts.hostPortOverrides = hostPortOverrides;\n if (profileCredsFile && !assumedCredentials) {\n taskOpts.profileCredentialsFile = {\n hostPath: profileCredsFile.hostPath,\n containerPath: profileCredsFile.containerPath,\n profileName: profileCredsFile.profileName,\n };\n }\n\n // Front-door pools for THIS service (built once at the emulator level and\n // shared with the listener servers). Each replica publishes + registers its\n // ephemeral endpoint into these pools as it boots. Undefined / empty for a\n // pure-compute boot (start-service) or a service no listener forwards to.\n const runnerOpts: ServiceRunnerOptions = {\n maxTasks: options.maxTasks,\n restartPolicy: options.restartPolicy,\n taskOptions: taskOpts,\n discovery,\n ...(frontDoorPools && frontDoorPools.length > 0\n ? { frontDoor: { pools: frontDoorPools } }\n : {}),\n };\n\n return startEcsService(service, runnerOpts, runState);\n}\n\n/**\n * Stand up one host-side reverse-proxy server PER LISTENER PORT from the\n * resolved {@link FrontDoorPlan}, path-routing each request across the services\n * the listener fronts, and return the started servers (for teardown) plus a\n * per-service-target pool list to thread into each service's runner (so every\n * replica publishes + registers its ephemeral endpoint into the right pool).\n *\n * One `FrontDoorEndpointPool` is created per distinct (service, container,\n * port) forward target and SHARED between the listener's routing table and the\n * owning service's runner context — same object on both sides, so a replica\n * registering itself is immediately reachable through the front-door.\n *\n * On a bind failure (e.g. EACCES on a privileged listener port, or the port is\n * already in use) every server started so far is closed and the error is\n * re-thrown with a `--lb-port` hint.\n */\nexport async function buildFrontDoor(\n plan: FrontDoorPlan,\n options: EcsServiceEmulatorOptions,\n logger: ReturnType<typeof getLogger>\n): Promise<{\n servers: StartedFrontDoorServer[];\n frontDoorByService: Map<string, FrontDoorServicePools>;\n lambdaRunners: FrontDoorLambdaRunner[];\n}> {\n const containerHost = options.containerHost;\n const servers: StartedFrontDoorServer[] = [];\n // ECS poolKey -> { pool, target }. Built lazily so the same (service,\n // container, port) reuses one pool across listeners / rules.\n const poolRegistry = new Map<\n string,\n { pool: FrontDoorEndpointPool; target: PlannedEcsForwardTarget }\n >();\n // Lambda logicalId -> one warm runner, reused across listeners / rules.\n const lambdaRegistry = new Map<string, FrontDoorLambdaRunner>();\n\n const dispatchFor = (t: PlannedForwardTarget): FrontDoorDispatchTarget => {\n if (t.kind === 'lambda') {\n let runner = lambdaRegistry.get(t.lambda.logicalId);\n if (!runner) {\n runner = createFrontDoorLambdaRunner(t.lambda, {\n containerHost,\n skipPull: options.pull === false,\n ...(options.platform !== undefined && { platformOverride: options.platform }),\n ...(options.ecrRoleArn !== undefined && { ecrRoleArn: options.ecrRoleArn }),\n ...(options.region !== undefined && { region: options.region }),\n });\n lambdaRegistry.set(t.lambda.logicalId, runner);\n }\n const boundRunner = runner;\n return {\n kind: 'lambda',\n lambda: {\n targetGroupArn: t.targetGroupArn,\n multiValueHeaders: t.multiValueHeaders,\n label: t.lambda.logicalId,\n invoke: (event) => boundRunner.invoke(event),\n },\n };\n }\n const key = `${t.serviceTarget} ${t.targetContainerName} ${t.targetContainerPort}`;\n let entry = poolRegistry.get(key);\n if (!entry) {\n entry = { pool: new FrontDoorEndpointPool(), target: t };\n poolRegistry.set(key, entry);\n }\n return { kind: 'pool', pool: entry.pool };\n };\n // Build / reuse the weighted dispatch entry for one planned forward target:\n // an ECS pool or a Lambda invoker, carrying the target's forward weight.\n const weightedTargetFor = (t: PlannedForwardTarget): WeightedForwardTarget => {\n const dispatch = dispatchFor(t);\n return dispatch.kind === 'lambda'\n ? { lambda: dispatch.lambda, weight: t.weight }\n : { pool: dispatch.pool, weight: t.weight };\n };\n // Convert a planned action into the front-door's RouteAction, building /\n // reusing a pool or Lambda runner per weighted forward target.\n const toRouteAction = (action: PlannedAction): RouteAction => {\n if (action.kind === 'forward') {\n const pools: WeightedForwardTarget[] = action.targets.map(weightedTargetFor);\n return { kind: 'forward', pools };\n }\n if (action.kind === 'redirect') {\n return {\n kind: 'redirect',\n statusCode: action.statusCode,\n ...(action.protocol !== undefined && { protocol: action.protocol }),\n ...(action.host !== undefined && { host: action.host }),\n ...(action.port !== undefined && { port: action.port }),\n ...(action.path !== undefined && { path: action.path }),\n ...(action.query !== undefined && { query: action.query }),\n };\n }\n return {\n kind: 'fixed-response',\n statusCode: action.statusCode,\n ...(action.contentType !== undefined && { contentType: action.contentType }),\n ...(action.messageBody !== undefined && { messageBody: action.messageBody }),\n };\n };\n\n // Resolve TLS materials once, up front, when any HTTPS listener is in the\n // plan. Kept OUTSIDE the surrounding try/catch so a TLS resolution failure\n // (e.g. openssl missing, BYO PEM unreadable) surfaces its own actionable\n // error instead of being re-wrapped in the generic `--lb-port` port-bind\n // envelope below. A single resolve is shared across every HTTPS listener.\n const hasHttpsListener = plan.listeners.some((l) => l.protocol === 'HTTPS');\n const tlsMaterials: FrontDoorTlsMaterials | undefined = hasHttpsListener\n ? await resolveFrontDoorTlsMaterials({\n certPath: options.tlsCert,\n keyPath: options.tlsKey,\n })\n : undefined;\n\n // Shared JWKS cache + warn-once Set, both at buildFrontDoor scope so two\n // rules pointing at the same Cognito JWKS URL de-dupe the \"JWKS\n // unreachable -> pass-through\" warn line instead of each warning\n // independently.\n const jwksCache = createJwksCache();\n const sharedWarned = new Set<string>();\n const authForGuard = (guard: FrontDoorAuthGuard): ReturnType<typeof buildAuthCheck> =>\n buildAuthCheck(guard, jwksCache, {\n ...(options.verifyAuth === false && { noVerifyAuth: true }),\n ...(options.bearerToken !== undefined && { bearerToken: options.bearerToken }),\n warned: sharedWarned,\n });\n const attachAuth = (action: RouteAction, guard: FrontDoorAuthGuard | undefined): RouteAction =>\n guard ? { ...action, auth: authForGuard(guard) } : action;\n\n try {\n for (const listener of plan.listeners) {\n const defaultRoute = listener.defaultAction\n ? attachAuth(toRouteAction(listener.defaultAction), listener.defaultAuthGuard)\n : undefined;\n const ruleRoutes: AlbPathRule<RouteAction>[] = listener.rules.map((r) => ({\n priority: r.priority,\n pathPatterns: r.pathPatterns,\n hostPatterns: r.hostPatterns,\n httpHeaderConditions: r.httpHeaderConditions,\n httpRequestMethods: r.httpRequestMethods,\n queryStringConditions: r.queryStringConditions,\n sourceIpCidrs: r.sourceIpCidrs,\n target: attachAuth(toRouteAction(r.action), r.authGuard),\n }));\n const route = (req: {\n path: string;\n host?: string;\n headers?: NodeJS.Dict<string | string[]>;\n method?: string;\n sourceIp?: string;\n }): RouteAction | undefined => matchAlbPathRule(req, ruleRoutes) ?? defaultRoute;\n\n const tls = listener.protocol === 'HTTPS' ? tlsMaterials : undefined;\n const server = await startFrontDoorServer({\n route,\n port: listener.hostPort,\n host: containerHost,\n listenerPort: listener.listenerPort,\n label: `listener port ${listener.listenerPort}`,\n ...(tls ? { tls } : {}),\n });\n servers.push(server);\n\n logger.info(\n `ALB front-door: ${server.scheme}://${server.host}:${server.port} (listener port ${listener.listenerPort})`\n );\n if (listener.defaultAction) {\n logger.info(` default -> ${describeAction(listener.defaultAction)}`);\n }\n for (const r of [...listener.rules].sort((a, b) => a.priority - b.priority)) {\n logger.info(\n ` ${describeConditions(r)} (priority ${r.priority}) -> ${describeAction(r.action)}`\n );\n }\n if (!listener.defaultAction) {\n logger.info(' (no default action: unmatched requests return 404)');\n }\n }\n\n // Boot every distinct Lambda-target container before returning so the\n // front-door is invokable as soon as it accepts connections. A boot\n // failure tears down everything started so far (servers + earlier\n // runners) and propagates with the same `--lb-port` hint envelope.\n for (const runner of lambdaRegistry.values()) {\n logger.info(`Booting Lambda target '${runner.logicalId}' behind the ALB front-door...`);\n await runner.start();\n }\n } catch (err) {\n await Promise.allSettled(servers.map((s) => s.close()));\n await Promise.allSettled([...lambdaRegistry.values()].map((r) => r.stop()));\n throw new LocalStartServiceError(\n `Failed to start ALB front-door: ${err instanceof Error ? err.message : String(err)}. If a ` +\n 'listener port is privileged (< 1024), remap it to a non-privileged host port with ' +\n '--lb-port <listenerPort>=<hostPort> (e.g. --lb-port 80=8080).'\n );\n }\n\n const frontDoorByService = new Map<string, FrontDoorServicePools>();\n for (const { pool, target } of poolRegistry.values()) {\n const list = frontDoorByService.get(target.serviceTarget) ?? [];\n list.push({\n pool,\n targetContainerName: target.targetContainerName,\n targetContainerPort: target.targetContainerPort,\n });\n frontDoorByService.set(target.serviceTarget, list);\n }\n return { servers, frontDoorByService, lambdaRunners: [...lambdaRegistry.values()] };\n}\n\n/** Human-readable summary of a planned rule's six ALB condition fields (for the boot banner). */\nfunction describeConditions(rule: {\n pathPatterns: string[];\n hostPatterns: string[];\n httpHeaderConditions: AlbHttpHeaderCondition[];\n httpRequestMethods: string[];\n queryStringConditions: AlbQueryStringCondition[];\n sourceIpCidrs: string[];\n}): string {\n const parts: string[] = [];\n if (rule.pathPatterns.length > 0) parts.push(`path ${rule.pathPatterns.join(', ')}`);\n if (rule.hostPatterns.length > 0) parts.push(`host ${rule.hostPatterns.join(', ')}`);\n for (const h of rule.httpHeaderConditions) {\n parts.push(`header ${h.name}: ${h.values.join(', ')}`);\n }\n if (rule.httpRequestMethods.length > 0) {\n parts.push(`method ${rule.httpRequestMethods.join(', ')}`);\n }\n if (rule.queryStringConditions.length > 0) {\n parts.push(`query ${rule.queryStringConditions.map(describeQueryStringCondition).join(', ')}`);\n }\n if (rule.sourceIpCidrs.length > 0) parts.push(`source-ip ${rule.sourceIpCidrs.join(', ')}`);\n return parts.join(' AND ') || '(no condition)';\n}\n\nfunction describeQueryStringCondition(c: AlbQueryStringCondition): string {\n return c.key !== undefined ? `${c.key}=${c.value}` : c.value;\n}\n\n/** Human-readable summary of a planned action (for the boot banner). */\nfunction describeAction(action: PlannedAction): string {\n if (action.kind === 'redirect') {\n return `redirect ${action.statusCode}`;\n }\n if (action.kind === 'fixed-response') {\n return `fixed-response ${action.statusCode}`;\n }\n if (action.targets.length === 1) {\n return describeTarget(action.targets[0]!);\n }\n const weights = action.targets.map((t) => `${describeTargetShort(t)}@${t.weight}`).join(', ');\n return `weighted forward [${weights}]`;\n}\n\n/** One forward target, described in full (for a single-target forward banner). */\nfunction describeTarget(t: PlannedForwardTarget): string {\n if (t.kind === 'lambda') {\n return `Lambda ${t.lambda.logicalId} (invoke)`;\n }\n return `${t.serviceTarget} (container ${t.targetContainerName}:${t.targetContainerPort}) (round-robin)`;\n}\n\n/** One forward target, described compactly (for a weighted-forward banner). */\nfunction describeTargetShort(t: PlannedForwardTarget): string {\n return t.kind === 'lambda' ? `Lambda ${t.lambda.logicalId}` : t.serviceTarget;\n}\n\nasync function resolvePlaceholderAccount(arn: string, region: string | undefined): Promise<string> {\n if (!arn.includes(TASK_ROLE_ACCOUNT_PLACEHOLDER)) return arn;\n const { STSClient, GetCallerIdentityCommand } = await import('@aws-sdk/client-sts');\n const sts = new STSClient({ ...(region && { region }) });\n try {\n const identity = await sts.send(new GetCallerIdentityCommand({}));\n const account = identity.Account;\n if (!account) {\n throw new LocalStartServiceError(\n `--assume-task-role: GetCallerIdentity returned no Account; cannot resolve placeholder ARN '${arn}'.`\n );\n }\n return arn.split(TASK_ROLE_ACCOUNT_PLACEHOLDER).join(account);\n } finally {\n sts.destroy();\n }\n}\n\nasync function assumeTaskRole(\n roleArn: string,\n region: string | undefined\n): Promise<{ accessKeyId: string; secretAccessKey: string; sessionToken: string }> {\n const { STSClient, AssumeRoleCommand } = await import('@aws-sdk/client-sts');\n const sts = new STSClient({ ...(region && { region }) });\n try {\n const response = await sts.send(\n new AssumeRoleCommand({\n RoleArn: roleArn,\n RoleSessionName: `${getEmbedConfig().resourceNamePrefix}-start-service-${Date.now()}`,\n DurationSeconds: 3600,\n })\n );\n const creds = response.Credentials;\n if (!creds?.AccessKeyId || !creds.SecretAccessKey || !creds.SessionToken) {\n throw new LocalStartServiceError(`AssumeRole(${roleArn}) returned no usable credentials.`);\n }\n return {\n accessKeyId: creds.AccessKeyId,\n secretAccessKey: creds.SecretAccessKey,\n sessionToken: creds.SessionToken,\n };\n } finally {\n sts.destroy();\n }\n}\n\n/**\n * Build the substitution context the ECS resolver consumes. Exported for the\n * site-level binding test that locks the `--from-cfn-stack` SSM-parameter\n * resolution call (issue #94).\n */\nexport async function buildEcsImageResolutionContext(\n target: string,\n stacks: StackInfo[],\n options: EcsServiceEmulatorOptions,\n stateProvider: LocalStateProvider | undefined\n): Promise<EcsImageResolutionContext | undefined> {\n const logger = getLogger();\n const parsed = parseEcsTarget(target);\n const candidate = pickCandidateStack(parsed.stackPattern, stacks);\n if (!candidate) return undefined;\n\n const needs = detectEcsImageResolutionNeeds(candidate);\n if (\n !needs.needsPseudoParameters &&\n !needs.needsStateResources &&\n !needs.needsEnvOrSecretSubstitution\n ) {\n return undefined;\n }\n\n const ctx: EcsImageResolutionContext = {};\n\n const wantsPseudoForEnvOrSecret = !!stateProvider && needs.needsEnvOrSecretSubstitution;\n if (needs.needsPseudoParameters || wantsPseudoForEnvOrSecret) {\n const region =\n options.region ??\n process.env['AWS_REGION'] ??\n process.env['AWS_DEFAULT_REGION'] ??\n candidate.region;\n if (!region) {\n logger.warn(\n `Resolver references \\${AWS::Region} but ${getEmbedConfig().binaryName} could not determine the target region. ` +\n 'Pass --region, set AWS_REGION, or declare env.region on the CDK stack.'\n );\n }\n let accountId: string | undefined;\n try {\n accountId = await resolveCallerAccountId(region, options.profile);\n } catch (err) {\n logger.warn(\n `Resolver needs \\${AWS::AccountId} but STS GetCallerIdentity failed: ${err instanceof Error ? err.message : String(err)}. ` +\n 'Substitution will be skipped; affected env / secret entries will be dropped with per-key warnings.'\n );\n }\n const partitionAndSuffix = region ? derivePartitionAndUrlSuffix(region) : undefined;\n ctx.pseudoParameters = {\n ...(accountId !== undefined && { accountId }),\n ...(region !== undefined && { region }),\n ...(partitionAndSuffix && {\n partition: partitionAndSuffix.partition,\n urlSuffix: partitionAndSuffix.urlSuffix,\n }),\n };\n }\n\n const wantsState = needs.needsStateResources || needs.needsEnvOrSecretSubstitution;\n if (stateProvider && wantsState) {\n const loaded = await stateProvider.load(candidate.stackName, candidate.region);\n if (loaded) {\n ctx.stateResources = loaded.resources;\n }\n if (needs.needsEnvOrSecretSubstitution && stateProvider.resolveTemplateSsmParameters) {\n const ssmParameters = await stateProvider.resolveTemplateSsmParameters(candidate.template);\n if (Object.keys(ssmParameters.values).length > 0) ctx.stateParameters = ssmParameters.values;\n if (ssmParameters.secureStringLogicalIds.length > 0) {\n ctx.stateSensitiveParameters = ssmParameters.secureStringLogicalIds;\n }\n }\n } else if (!stateProvider && needs.needsStateResources) {\n logger.warn(\n 'Container Image references a same-stack AWS::ECR::Repository. Pass a state-source flag ' +\n '(e.g. --from-cfn-stack or a host-provided extension) to substitute the deployed repository URI.'\n );\n } else if (!stateProvider && needs.needsEnvOrSecretSubstitution) {\n logger.warn(\n 'Container Environment / Secrets entries contain CloudFormation intrinsics. ' +\n 'Pass a state-source flag (e.g. --from-cfn-stack or a host-provided extension) to substitute them against the deployed state.'\n );\n }\n\n return ctx;\n}\n\nfunction pickCandidateStack(\n stackPattern: string | null,\n stacks: StackInfo[]\n): StackInfo | undefined {\n if (stackPattern === null) {\n if (stacks.length === 1) return stacks[0];\n return undefined;\n }\n const matched = matchStacks(stacks, [stackPattern]);\n if (matched.length === 1) return matched[0];\n return undefined;\n}\n\nasync function resolveCallerAccountId(\n region: string | undefined,\n profile: string | undefined\n): Promise<string | undefined> {\n const { STSClient, GetCallerIdentityCommand } = await import('@aws-sdk/client-sts');\n const sts = new STSClient({ ...(region && { region }), ...(profile && { profile }) });\n try {\n const identity = await sts.send(new GetCallerIdentityCommand({}));\n return identity.Account;\n } finally {\n sts.destroy();\n }\n}\n\nfunction readEnvOverridesFile(\n filePath: string | undefined\n): Record<string, Record<string, string | null> | undefined> | undefined {\n if (!filePath) return undefined;\n let raw: string;\n try {\n raw = readFileSync(filePath, 'utf-8');\n } catch (err) {\n throw new LocalStartServiceError(\n `Failed to read --env-vars file '${filePath}': ${err instanceof Error ? err.message : String(err)}`\n );\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (err) {\n throw new LocalStartServiceError(\n `Failed to parse --env-vars file '${filePath}' as JSON: ${err instanceof Error ? err.message : String(err)}`\n );\n }\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {\n throw new LocalStartServiceError(\n `--env-vars file '${filePath}' must contain a JSON object at the top level.`\n );\n }\n return parsed as Record<string, Record<string, string | null> | undefined>;\n}\n\nfunction parsePositiveInt(raw: string, flagName: string): number {\n const parsed = parseInt(raw, 10);\n if (!Number.isFinite(parsed) || parsed < 1) {\n throw new LocalStartServiceError(`${flagName} must be a positive integer (got '${raw}').`);\n }\n return parsed;\n}\n\n/**\n * Hard cap on `--max-tasks` driven by the per-replica subnet allocator in\n * `ecs-service-runner.ts:pickSubnetOctet`.\n */\nexport const MAX_TASKS_SUBNET_RANGE_CAP = 83;\n\nexport function parseMaxTasks(raw: string): number {\n const parsed = parsePositiveInt(raw, '--max-tasks');\n if (parsed > MAX_TASKS_SUBNET_RANGE_CAP) {\n throw new LocalStartServiceError(\n `--max-tasks ${parsed} exceeds the per-replica link-local /24 subnet allocator's range ` +\n `(${MAX_TASKS_SUBNET_RANGE_CAP}). Lower --max-tasks to <= ${MAX_TASKS_SUBNET_RANGE_CAP}.`\n );\n }\n return parsed;\n}\n\nexport function parseRestartPolicy(raw: string): 'on-failure' | 'always' | 'none' {\n if (raw === 'on-failure' || raw === 'always' || raw === 'none') return raw;\n throw new LocalStartServiceError(\n `--restart-policy must be one of 'on-failure', 'always', or 'none' (got '${raw}').`\n );\n}\n\n/**\n * Resolve the credentials forwarded to the AWS-published metadata-endpoints\n * sidecar (shared across every replica boot in one CLI invocation). `--profile`\n * resolves via the SDK default chain; unset yields `undefined`. Per-service\n * `--assume-task-role` overrides are intentionally NOT consulted here. Exported\n * for a unit test that exercises both branches.\n */\nexport async function resolveSharedSidecarCredentials(options: {\n profile?: string;\n}): Promise<{ accessKeyId: string; secretAccessKey: string; sessionToken?: string } | undefined> {\n if (options.profile) return resolveProfileCredentials(options.profile);\n return undefined;\n}\n\n/**\n * Add the CLI options shared by both ECS-service commands (`start-service` and\n * `start-alb`) to a command. The command-specific argument / description and\n * the one unique option (`--host-port` vs `--lb-port`) are added by each\n * factory.\n */\nexport function addCommonEcsServiceOptions(cmd: Command): Command {\n cmd\n .addOption(\n new Option(\n '--cluster <name>',\n 'Cluster name surfaced to ECS_CONTAINER_METADATA_URI_V4 and used as the docker network prefix'\n ).default(getEmbedConfig().resourceNamePrefix)\n )\n .addOption(\n new Option(\n '--env-vars <file>',\n 'JSON env-var overrides (SAM-compatible: {\"ContainerName\":{\"KEY\":\"VALUE\"}, \"Parameters\":{}})'\n )\n )\n .addOption(\n new Option(\n '--container-host <ip>',\n 'Host IP to bind published container ports to. Must be a numeric IP (Docker rejects hostnames here)'\n ).default('127.0.0.1')\n )\n .addOption(\n new Option(\n '--assume-task-role [arn]',\n \"Assume the task definition's TaskRoleArn (or the supplied ARN) and forward STS-issued temp \" +\n 'credentials via the metadata sidecar so containers run with the deployed task role. ' +\n \"Bare flag uses the template's TaskRoleArn; pass an explicit ARN to override.\"\n )\n )\n .addOption(\n new Option('--no-pull', 'Skip docker pull for every container image and the metadata sidecar')\n )\n .addOption(\n new Option(\n '--ecr-role-arn <arn>',\n 'Role ARN to assume before authenticating against ECR for cross-account / centralized registries.'\n )\n )\n .addOption(\n new Option(\n '--platform <platform>',\n 'Force docker --platform (linux/amd64 or linux/arm64). Default: inferred from task RuntimePlatform.CpuArchitecture'\n )\n )\n .addOption(\n new Option(\n '--max-tasks <n>',\n 'Hard cap on local replica count. Caps the template DesiredCount so local dev machines ' +\n \"don't run an unbounded number of containers. Cannot exceed \" +\n `${MAX_TASKS_SUBNET_RANGE_CAP} due to the per-replica link-local /24 subnet allocator's range.`\n )\n .default(3)\n .argParser(parseMaxTasks)\n )\n .addOption(\n new Option(\n '--restart-policy <policy>',\n \"How to react when an essential container exits. 'on-failure' (default) restarts only \" +\n \"on non-zero exit; 'always' restarts on every exit; 'none' shuts the replica down \" +\n 'and runs the service degraded.'\n )\n .default('on-failure')\n .argParser(parseRestartPolicy)\n )\n .addOption(\n new Option(\n '--from-cfn-stack [cfn-stack-name]',\n 'Read a deployed CloudFormation stack via ListStackResources and substitute Ref / Fn::ImportValue ' +\n 'in container env vars / secrets / image URIs with the deployed physical IDs / exports. ' +\n 'Use for CDK apps deployed via the upstream CDK CLI (`cdk deploy`). ' +\n `Bare form uses the ${getEmbedConfig().binaryName} stack name; pass an explicit value when the CFn stack name differs. ` +\n 'Fn::GetAtt is warn-and-dropped in v1 (CFn ListStackResources does not return per-attribute values).'\n )\n )\n .addOption(\n new Option(\n '--stack-region <region>',\n 'Region of the state record to read. Used with --from-cfn-stack as the CFn client region.'\n )\n );\n\n [...commonOptions(), ...appOptions(), ...contextOptions].forEach((opt) => cmd.addOption(opt));\n cmd.addOption(deprecatedRegionOption);\n return cmd;\n}\n","import { Command, Option } from 'commander';\nimport { withErrorHandling, LocalStartServiceError } from '../../utils/error-handler.js';\nimport { listTargets } from '../../local/target-lister.js';\nimport {\n getEmbedConfig,\n setEmbedConfig,\n type CdkLocalEmbedConfig,\n} from '../../local/embed-config.js';\nimport type { ExtraStateProviders } from './local-state-source.js';\nimport {\n addCommonEcsServiceOptions,\n runEcsServiceEmulator,\n type EcsServiceEmulatorOptions,\n type EmulatorStrategy,\n} from './ecs-service-emulator.js';\n\n// Re-exported for existing unit tests that import these from this module.\nexport {\n resolveSharedSidecarCredentials,\n buildEcsImageResolutionContext,\n MAX_TASKS_SUBNET_RANGE_CAP,\n} from './ecs-service-emulator.js';\n\n/**\n * Factory options for {@link createLocalStartServiceCommand}.\n */\nexport interface CreateLocalStartServiceCommandOptions {\n extraStateProviders?: ExtraStateProviders;\n /** Embed-time branding overrides for a host wrapping this factory. */\n embedConfig?: CdkLocalEmbedConfig;\n}\n\n/**\n * `cdkl start-service` strategy — a pure ECS replica runner. It picks\n * `AWS::ECS::Service` targets and boots each with NO front-door (the ALB\n * front-door is its own command, `cdkl start-alb`). This keeps `start-service`\n * a leaf compute runner, symmetric with `invoke` / `run-task`.\n */\nexport function serviceStrategy(): EmulatorStrategy {\n return {\n pickEntries: (stacks) => listTargets(stacks).ecsServices,\n pickerMessage: 'Select one or more ECS services to run',\n pickerNoun: 'ECS services',\n onMissing: () =>\n new LocalStartServiceError(\n `${getEmbedConfig().cliName} start-service requires at least one <target>. ` +\n \"Pass one or more service paths like 'Stack/Orders' 'Stack/Frontend', \" +\n 'or run it in a TTY to pick interactively.'\n ),\n resolveBoots: (_stacks, chosenTargets) => ({\n boots: chosenTargets.map((target) => ({ target })),\n warnings: [],\n }),\n lbPortOverrides: {},\n };\n}\n\n/**\n * `cdkl start-service <Stack/Service>` — Phase 2 of #262. Spins up\n * `DesiredCount` task replicas locally (clamped by `--max-tasks`) using the\n * existing `ecs-task-runner` per replica. Long-running; ^C cleans every replica\n * + sidecar + shared network. Pure compute: to put a local ALB front-door in\n * front of an ALB-fronted service, use `cdkl start-alb`.\n */\nexport function createLocalStartServiceCommand(\n opts: CreateLocalStartServiceCommandOptions = {}\n): Command {\n setEmbedConfig(opts.embedConfig);\n const cmd = new Command('start-service')\n .description(\n 'Run one or more AWS::ECS::Service resources locally as a long-running emulator. Spins up ' +\n 'DesiredCount task replicas per service (clamped by --max-tasks) using the same per-task ' +\n `docker network + metadata sidecar pattern as \\`${getEmbedConfig().cliName} run-task\\`, then keeps each ` +\n 'replica running and restarts it on exit per --restart-policy. ^C tears every replica + ' +\n 'sidecar + network down. Each <target> accepts a CDK display path (MyStack/MyService) ' +\n 'or stack-qualified logical ID (MyStack:MyServiceXYZ); single-stack apps may omit the ' +\n 'stack prefix. When two or more <target>s are supplied, every service is booted into a ' +\n 'shared Cloud Map / Service Connect registry so peer services discover each other via ' +\n 'docker --add-host overlay. Omit <targets> in an interactive terminal to ' +\n `multi-select the services from a list. To put a local ALB front-door in front of an ` +\n `ALB-fronted service, use \\`${getEmbedConfig().cliName} start-alb\\` instead.`\n )\n .argument(\n '[targets...]',\n '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)'\n )\n .addOption(\n new Option(\n '--host-port <containerPort=hostPort...>',\n 'Publish a container port on a specific host port (e.g. 80=8080); repeatable. ' +\n 'Default: host port == container port. Use this on macOS to map a privileged ' +\n 'container port (< 1024) to a non-privileged host port and avoid the Docker ' +\n 'Desktop admin-password prompt. (Single-replica services only — multi-replica ' +\n 'services do not publish host ports.)'\n )\n )\n .action(\n withErrorHandling(async (targets: string[], options: EcsServiceEmulatorOptions) => {\n await runEcsServiceEmulator(targets, options, serviceStrategy(), opts.extraStateProviders);\n })\n );\n\n return addCommonEcsServiceOptions(cmd);\n}\n","import type { StackInfo } from '../synthesis/assembly-reader.js';\nimport type { TemplateResource } from '../types/resource.js';\nimport type { AlbHttpHeaderCondition, AlbQueryStringCondition } from './alb-path-matcher.js';\n\n/**\n * Resolve an `AWS::ElasticLoadBalancingV2::LoadBalancer` (an ALB) into the\n * backing ECS service(s) and the host listener port(s) a local front-door\n * should expose. This is the `cdkl start-alb` entry: you name the ALB, and\n * cdk-local discovers the services behind it (mirroring how `start-api` names\n * the API and discovers the backing Lambdas).\n *\n * The synthesized linkage (confirmed against real `cdk synth` of an\n * `ApplicationLoadBalancer` + `addAction` rules):\n *\n * ```\n * ElasticLoadBalancingV2::LoadBalancer (the ALB you name)\n * ElasticLoadBalancingV2::Listener : { LoadBalancerArn:{Ref:<ALB>}, Port, Protocol,\n * DefaultActions:[ <action> ] }\n * ElasticLoadBalancingV2::ListenerRule : { ListenerArn:{Ref:<Listener>}, Priority,\n * Conditions:[{ Field:\"path-pattern\", PathPatternConfig:{ Values:[\"/api/*\"] } },\n * { Field:\"host-header\", HostHeaderConfig:{ Values:[\"api.example.com\"] } },\n * { Field:\"http-header\", HttpHeaderConfig:{ HttpHeaderName:\"X-API\", Values:[\"*\"] } },\n * { Field:\"http-request-method\", HttpRequestMethodConfig:{ Values:[\"POST\"] } },\n * { Field:\"query-string\", QueryStringConfig:{ Values:[{ Key:\"v\", Value:\"1\" }] } },\n * { Field:\"source-ip\", SourceIpConfig:{ Values:[\"10.0.0.0/8\"] } }],\n * Actions:[ <action> ] }\n * ElasticLoadBalancingV2::TargetGroup : { Port, Protocol, TargetType:\"ip\" }\n * ECS::Service.LoadBalancers[] -> { ContainerName, ContainerPort, TargetGroupArn:{Ref:<TG>} }\n * ```\n *\n * Each `<action>` is one of:\n * - `forward` — `{ Type:\"forward\", TargetGroupArn:{Ref} }` (single target) OR\n * `{ Type:\"forward\", ForwardConfig:{ TargetGroups:[{ TargetGroupArn:{Ref}, Weight }] } }`\n * (one or more weighted targets);\n * - `redirect` — `{ Type:\"redirect\", RedirectConfig:{ Protocol/Host/Port/Path/Query/StatusCode } }`;\n * - `fixed-response` — `{ Type:\"fixed-response\", FixedResponseConfig:{ StatusCode/ContentType/MessageBody } }`.\n *\n * Resolution walks ALB -> listeners (by `LoadBalancerArn` Ref) -> their default\n * action AND any ListenerRules -> for each `forward`, the ECS Service whose\n * `LoadBalancers[]` references each target group (a reverse scan; there is no\n * direct TG -> service pointer). Output is a per-listener routing table: a\n * default action plus the ordered rules, each carrying its resolved action and\n * its full set of routing conditions.\n *\n * Scope: HTTP listeners; all six ALB rule-condition fields (`path-pattern`,\n * `host-header`, `http-header`, `http-request-method`, `query-string`,\n * `source-ip`); `forward` (single or weighted) to ECS services AND/OR Lambda\n * functions (`TargetType:\"lambda\"` target groups — #123: the TG -> backing\n * `AWS::Lambda::Function` is resolved and the front-door invokes it locally per\n * request); `redirect` / `fixed-response` actions. A single weighted forward may\n * mix ECS and Lambda targets. HTTPS listeners are served — local TLS\n * termination uses a user-supplied or auto-generated self-signed cert/key\n * pair (the deployed `Listener.Certificates[]` ACM ARNs are not fetched,\n * because ACM private keys are not retrievable by design).\n *\n * `authenticate-cognito` / `authenticate-oidc` actions ARE served — they are\n * lifted from the `Actions[]` array into an `authGuard` attached to the\n * terminal action they wrap. The front-door enforces a Bearer-JWT check\n * locally (signature + `iss` + `exp` + `aud`) using the existing JWKS-cached\n * verifier; missing / invalid token -> 401 with `WWW-Authenticate: Bearer`.\n * Out of scope: the full OAuth roundtrip (authorize-endpoint redirect +\n * callback). The local-dev parity is \"I have a JWT, let me through\" — the\n * `--bearer-token <jwt>` flag is the convenience escape hatch; the\n * `--no-verify-auth` flag disables the guard entirely.\n *\n * Skipped with a warning: TLS listeners (NLB-style, not ALB).\n */\n\nconst ALB_TYPE = 'AWS::ElasticLoadBalancingV2::LoadBalancer';\nconst LISTENER_TYPE = 'AWS::ElasticLoadBalancingV2::Listener';\nconst LISTENER_RULE_TYPE = 'AWS::ElasticLoadBalancingV2::ListenerRule';\nconst TARGET_GROUP_TYPE = 'AWS::ElasticLoadBalancingV2::TargetGroup';\nconst SERVICE_TYPE = 'AWS::ECS::Service';\nconst LAMBDA_FUNCTION_TYPE = 'AWS::Lambda::Function';\n\n/**\n * The backing target one forward target group routes to, plus its weight. A\n * discriminated union: either an ECS service (the original `start-alb` path) or\n * a Lambda function (`TargetType: lambda` target groups, #123). A single\n * weighted forward may mix both. The front-door dispatches an ECS target to a\n * replica pool and a Lambda target to a locally-invoked RIE container.\n */\nexport type FrontDoorForwardTarget = FrontDoorEcsTarget | FrontDoorLambdaTarget;\n\n/** A weighted forward target backed by an ECS service behind the target group. */\nexport interface FrontDoorEcsTarget {\n kind: 'ecs';\n /** Logical id of the `AWS::ECS::Service` behind the target group. */\n serviceLogicalId: string;\n /** Container the action forwards to (`LoadBalancers[].ContainerName`). */\n targetContainerName: string;\n /** Container port the target group targets (`LoadBalancers[].ContainerPort`). */\n targetContainerPort: number;\n /** Logical id of the resolved target group (diagnostics). */\n targetGroupLogicalId: string;\n /**\n * Forward weight for weighted routing. A single-target forward defaults to 1;\n * a `ForwardConfig.TargetGroups[]` entry carries its declared `Weight`\n * (weight 0 means \"never routed\", per ALB semantics).\n */\n weight: number;\n}\n\n/**\n * A weighted forward target backed by a Lambda function (a `TargetType: lambda`\n * target group, #123). The synthesized linkage:\n *\n * ```\n * ElasticLoadBalancingV2::TargetGroup : { TargetType:\"lambda\",\n * Targets:[{ Id:{ \"Fn::GetAtt\":[<FnLogicalId>, \"Arn\"] } }],\n * TargetGroupAttributes?:[{ Key:\"lambda.multi_value_headers.enabled\", Value:\"true\" }] }\n * Lambda::Permission : grants elasticloadbalancing principal invoke\n * ```\n *\n * The backing function is read from `Targets[0].Id.Fn::GetAtt[0]`.\n */\nexport interface FrontDoorLambdaTarget {\n kind: 'lambda';\n /** Logical id of the backing `AWS::Lambda::Function`. */\n lambdaLogicalId: string;\n /** Logical id of the resolved target group (diagnostics + `requestContext.elb`). */\n targetGroupLogicalId: string;\n /**\n * `lambda.multi_value_headers.enabled` target-group attribute. `true` ->\n * the ALB event uses `multiValueHeaders` / `multiValueQueryStringParameters`;\n * `false` (default) -> single-value `headers` / `queryStringParameters`.\n */\n multiValueHeaders: boolean;\n /**\n * Forward weight for weighted routing. A single-target forward defaults to 1;\n * a `ForwardConfig.TargetGroups[]` entry carries its declared `Weight`\n * (weight 0 means \"never routed\", per ALB semantics).\n */\n weight: number;\n}\n\n/** A resolved `redirect` action (ALB builds a `Location` from these + request placeholders). */\nexport interface FrontDoorRedirectAction {\n kind: 'redirect';\n /** HTTP status (301 permanent / 302 found). ALB emits `HTTP_301` / `HTTP_302`. */\n statusCode: 301 | 302;\n /** `#{protocol}` (default) / `HTTP` / `HTTPS`. */\n protocol?: string;\n /** `#{host}` (default) or a literal host. */\n host?: string;\n /** `#{port}` (default) or a literal port. */\n port?: string;\n /** `/#{path}` (default) or a literal path; ALB requires a leading `/`. */\n path?: string;\n /** `#{query}` (default) or a literal query (without the leading `?`). */\n query?: string;\n}\n\n/** A resolved `fixed-response` action (ALB synthesizes the whole response). */\nexport interface FrontDoorFixedResponseAction {\n kind: 'fixed-response';\n /** HTTP status code (ALB stores it as a numeric string, e.g. `\"404\"`). */\n statusCode: number;\n /** Response `Content-Type` (defaults to `text/plain` when absent). */\n contentType?: string;\n /** Response body (empty when absent). */\n messageBody?: string;\n}\n\n/** A resolved `forward` action: one or more weighted ECS-service targets. */\nexport interface FrontDoorForwardAction {\n kind: 'forward';\n /** The weighted forward targets (length >= 1; single-target forward = one entry, weight 1). */\n targets: FrontDoorForwardTarget[];\n}\n\n/**\n * The local-enforced ALB authenticate-* guard. An `authenticate-cognito` /\n * `authenticate-oidc` action in `Actions[]` is parsed into one of these and\n * attached to the terminal action it wraps. The front-door evaluates the\n * guard before serving the action; the cloud-side OAuth roundtrip is NOT\n * reproduced (see module jsdoc).\n *\n * Cognito vs OIDC are mostly the same shape — both end up checking a Bearer\n * JWT's `iss` + `aud` + signature. The `kind` is retained for logs / docs and\n * for the Cognito-direct JWKS optimization (`region` + `userPoolId`).\n */\nexport interface FrontDoorAuthGuard {\n kind: 'authenticate-cognito' | 'authenticate-oidc';\n /** Expected JWT `iss` (Cognito issuer URL or the OIDC `Issuer`). */\n issuer: string;\n /** Allowed JWT `aud` value (Cognito `UserPoolClientId` or OIDC `ClientId`). */\n audience: string;\n /** Cognito-only: enables the direct JWKS URL fast path. */\n region?: string;\n /** Cognito-only: enables the direct JWKS URL fast path. */\n userPoolId?: string;\n /**\n * Cookie name prefix that ALB issues on a successful sign-in. The presence\n * of any cookie matching `<prefix>-N` short-circuits the guard (the user is\n * acting as if already signed in via the deployed ALB; local-dev parity).\n * Defaults to `AWSELBAuthSessionCookie` per ALB.\n */\n sessionCookieName: string;\n /** Diagnostic label for log lines + the 401 `WWW-Authenticate` realm. */\n label: string;\n}\n\n/** Any resolved listener / rule action the local front-door can serve. */\nexport type ResolvedListenerAction =\n | FrontDoorForwardAction\n | FrontDoorRedirectAction\n | FrontDoorFixedResponseAction;\n\n/** One resolved routing rule on a listener — all six ALB condition fields + an action. */\nexport interface ResolvedListenerRule {\n /** ALB rule priority (lower = evaluated first). */\n priority: number;\n /** The rule's `path-pattern` condition values (OR-matched; empty = no path constraint). */\n pathPatterns: string[];\n /** The rule's `host-header` condition values (OR-matched; empty = no host constraint). */\n hostPatterns: string[];\n /** The rule's `http-header` conditions (each AND'd; values within OR; empty = no header constraint). */\n httpHeaderConditions: AlbHttpHeaderCondition[];\n /** The rule's `http-request-method` values (OR-matched, exact; empty = no method constraint). */\n httpRequestMethods: string[];\n /** The rule's `query-string` `{ Key?, Value }` pairs (OR-matched; empty = no query-string constraint). */\n queryStringConditions: AlbQueryStringCondition[];\n /** The rule's `source-ip` CIDR values (OR-matched; empty = no source-IP constraint). */\n sourceIpCidrs: string[];\n /** The action this rule performs when its conditions match. */\n action: ResolvedListenerAction;\n /**\n * When the rule's `Actions[]` declared an `authenticate-cognito` /\n * `authenticate-oidc` action before the terminal action, this carries the\n * resolved guard. The front-door enforces it before serving `action`.\n */\n authGuard?: FrontDoorAuthGuard;\n}\n\n/** A resolved listener front-door: one host port, an optional default action + rules. */\nexport interface ResolvedListenerFrontDoor {\n /** Listener port declared on the ALB (the stable host endpoint port). */\n listenerPort: number;\n /** Listener protocol — `HTTP` or `HTTPS`. */\n listenerProtocol: 'HTTP' | 'HTTPS';\n /** Logical id of the listener (diagnostics). */\n listenerLogicalId: string;\n /**\n * Default action. Present when the listener's `DefaultActions` is a resolvable\n * forward / redirect / fixed-response. Absent only when the default action\n * was a bare authenticate-* with no terminal action — unmatched requests then\n * get a 404 from the front-door.\n */\n defaultAction?: ResolvedListenerAction;\n /**\n * Auth guard for the default action (when `DefaultActions[]` started with\n * an `authenticate-cognito` / `authenticate-oidc` entry).\n */\n defaultAuthGuard?: FrontDoorAuthGuard;\n /** Rules (unordered here; the matcher evaluates by priority). */\n rules: ResolvedListenerRule[];\n}\n\nexport interface AlbFrontDoorResolution {\n /** Front-door listeners discovered on the ALB, each with its routing table. */\n listeners: ResolvedListenerFrontDoor[];\n /** Non-fatal warnings (the CLI surfaces these and proceeds). */\n warnings: string[];\n}\n\n/**\n * Resolve an ALB into its front-door listeners + routing tables. Pure — reads\n * only the supplied stack template. Returns an empty `listeners` array (with\n * warnings) when the ALB fronts nothing cdk-local can serve locally.\n */\nexport function resolveAlbFrontDoor(\n stack: StackInfo,\n albLogicalId: string\n): AlbFrontDoorResolution {\n const warnings: string[] = [];\n const resources = stack.template.Resources ?? {};\n const stackName = stack.stackName;\n\n // TG logical id -> backing ECS service (reverse of Service.LoadBalancers[]).\n const tgToService = indexTargetGroupToService(resources);\n // Listener logical id -> its ListenerRules.\n const rulesByListener = indexRulesByListener(resources);\n\n const listeners: ResolvedListenerFrontDoor[] = [];\n\n for (const [listenerLogicalId, resource] of Object.entries(resources)) {\n if (resource.Type !== LISTENER_TYPE) continue;\n const props = (resource.Properties ?? {}) as Record<string, unknown>;\n if (refOf(props['LoadBalancerArn']) !== albLogicalId) continue;\n\n const port = parsePort(props['Port']);\n if (port === undefined) continue;\n const protocol = typeof props['Protocol'] === 'string' ? props['Protocol'] : 'HTTP';\n if (protocol !== 'HTTP' && protocol !== 'HTTPS') {\n warnings.push(\n `Listener '${listenerLogicalId}' on port ${port} uses protocol ${protocol}; the local ` +\n 'ALB front-door supports HTTP and HTTPS listeners only (TLS / NLB-style listeners are ' +\n 'not served). Skipping it.'\n );\n continue;\n }\n if (\n protocol === 'HTTPS' &&\n Array.isArray(props['Certificates']) &&\n props['Certificates'].length > 0\n ) {\n // The deployed Listener's ACM-backed Certificates[] is not fetched: ACM\n // private keys are not retrievable, by design. The local front-door\n // terminates TLS with a user-supplied or auto-generated self-signed\n // cert instead.\n warnings.push(\n `Listener '${listenerLogicalId}' on port ${port} declares ACM Certificates which are not ` +\n 'retrievable locally. The front-door terminates TLS with --tls-cert/--tls-key or an ' +\n 'auto-generated self-signed cert.'\n );\n }\n\n const defaultResolved = resolveAction(\n props['DefaultActions'],\n resources,\n tgToService,\n stackName,\n `Listener '${listenerLogicalId}' (port ${port}) default action`,\n warnings\n );\n\n const rules: ResolvedListenerRule[] = [];\n for (const { ruleLogicalId, ruleProps } of rulesByListener.get(listenerLogicalId) ?? []) {\n const priority = parsePriority(ruleProps['Priority']);\n const ruleLabel = `Listener rule '${ruleLogicalId}' (priority ${priority})`;\n const parsed = parseRuleConditions(ruleProps['Conditions'], ruleLabel, warnings);\n if (!parsed) {\n // parseRuleConditions warned about an unsupported / malformed condition.\n continue;\n }\n const {\n pathPatterns,\n hostPatterns,\n httpHeaderConditions,\n httpRequestMethods,\n queryStringConditions,\n sourceIpCidrs,\n } = parsed;\n const hasAnyCondition =\n pathPatterns.length > 0 ||\n hostPatterns.length > 0 ||\n httpHeaderConditions.length > 0 ||\n httpRequestMethods.length > 0 ||\n queryStringConditions.length > 0 ||\n sourceIpCidrs.length > 0;\n if (!hasAnyCondition) {\n // No supported condition to route on (an empty / catch-all rule).\n continue;\n }\n const ruleResolved = resolveAction(\n ruleProps['Actions'],\n resources,\n tgToService,\n stackName,\n `${ruleLabel} action`,\n warnings\n );\n if (!ruleResolved) continue; // resolveAction already warned (or it was a bare authenticate-*)\n rules.push({\n priority,\n pathPatterns,\n hostPatterns,\n httpHeaderConditions,\n httpRequestMethods,\n queryStringConditions,\n sourceIpCidrs,\n action: ruleResolved.action,\n ...(ruleResolved.authGuard ? { authGuard: ruleResolved.authGuard } : {}),\n });\n }\n\n if (!defaultResolved && rules.length === 0) {\n // The listener serves nothing cdk-local can route (e.g. a bare authenticate-*\n // default and every rule skipped above).\n continue;\n }\n\n listeners.push({\n listenerPort: port,\n listenerProtocol: protocol,\n listenerLogicalId,\n ...(defaultResolved ? { defaultAction: defaultResolved.action } : {}),\n ...(defaultResolved?.authGuard ? { defaultAuthGuard: defaultResolved.authGuard } : {}),\n rules,\n });\n }\n\n return { listeners, warnings };\n}\n\n/** True when the resource is an application Load Balancer (the `start-alb` target type). */\nexport function isApplicationLoadBalancer(resource: TemplateResource): boolean {\n if (resource.Type !== ALB_TYPE) return false;\n const type = (resource.Properties as Record<string, unknown> | undefined)?.['Type'];\n // CDK / CFn default for ELBv2 LoadBalancer.Type is `application`.\n return type === undefined || type === 'application';\n}\n\ninterface BackingServiceRef {\n serviceLogicalId: string;\n containerName: string;\n containerPort: number;\n}\n\n/**\n * Resolve a listener / rule `Actions` (or `DefaultActions`) array. ALB allows\n * any number of `authenticate-cognito` / `authenticate-oidc` entries followed\n * by exactly one terminal action (`forward` / `redirect` / `fixed-response`).\n * The first parseable authenticate-* (last wins if multiple) becomes the\n * returned `authGuard`; the terminal action becomes `action`. Returns\n * `undefined` when no terminal action is resolvable.\n */\nfunction resolveAction(\n actions: unknown,\n resources: Record<string, TemplateResource>,\n tgToService: Map<string, BackingServiceRef>,\n stackName: string,\n label: string,\n warnings: string[]\n): { action: ResolvedListenerAction; authGuard?: FrontDoorAuthGuard } | undefined {\n if (!Array.isArray(actions)) return undefined;\n\n let authGuard: FrontDoorAuthGuard | undefined;\n let sawAuthenticate = false;\n for (const action of actions) {\n if (!action || typeof action !== 'object') continue;\n const a = action as Record<string, unknown>;\n const type = a['Type'];\n\n if (type === 'authenticate-cognito' || type === 'authenticate-oidc') {\n sawAuthenticate = true;\n const parsed = parseAuthenticateAction(a, type, label, warnings);\n if (parsed) authGuard = parsed;\n continue;\n }\n let terminal: ResolvedListenerAction | undefined;\n if (type === 'forward') {\n terminal = resolveForwardAction(a, resources, tgToService, stackName, label, warnings);\n } else if (type === 'redirect') {\n terminal = resolveRedirectAction(a, label, warnings);\n } else if (type === 'fixed-response') {\n terminal = resolveFixedResponseAction(a);\n } else if (typeof type === 'string') {\n warnings.push(\n `${label} uses an unsupported action type '${type}'. The local ALB front-door supports ` +\n 'forward / redirect / fixed-response actions only. Skipping it.'\n );\n }\n if (terminal) {\n return authGuard ? { action: terminal, authGuard } : { action: terminal };\n }\n }\n\n if (sawAuthenticate) {\n warnings.push(\n `${label} is an authenticate-* action with no local-servable terminal action; skipping it.`\n );\n }\n return undefined;\n}\n\n/**\n * Resolve a `forward` action into one or more weighted targets. Each target\n * group is either an ECS service (the original `start-alb` path) or a\n * `TargetType: lambda` group backed by an in-stack Lambda (#123); a single\n * weighted forward may mix both.\n */\nfunction resolveForwardAction(\n action: Record<string, unknown>,\n resources: Record<string, TemplateResource>,\n tgToService: Map<string, BackingServiceRef>,\n stackName: string,\n label: string,\n warnings: string[]\n): FrontDoorForwardAction | undefined {\n const refs = collectForwardTargetGroupRefs(action);\n if (refs.length === 0) {\n if (hasUnresolvableForward(action)) {\n warnings.push(\n `${label} forwards to a non-Ref TargetGroupArn (literal / cross-stack / imported); the ` +\n 'local front-door only supports in-stack target groups. Skipping it.'\n );\n }\n return undefined;\n }\n\n const targets: FrontDoorForwardTarget[] = [];\n for (const { tgRef, weight } of refs) {\n const tg = resources[tgRef];\n if (!tg || tg.Type !== TARGET_GROUP_TYPE) {\n warnings.push(\n `${label} forwards to target group '${tgRef}', but no ${TARGET_GROUP_TYPE} with that ` +\n `logical id exists in ${stackName}. Skipping that target group.`\n );\n continue;\n }\n const tgProps = (tg.Properties as Record<string, unknown> | undefined) ?? {};\n const tgType = tgProps['TargetType'];\n if (tgType === 'lambda') {\n // #123 Lambda-target slice: resolve the TG -> backing Lambda function.\n const lambdaTarget = resolveLambdaForwardTarget(\n tgProps,\n tgRef,\n resources,\n stackName,\n label,\n warnings\n );\n if (lambdaTarget) targets.push({ ...lambdaTarget, weight });\n continue;\n }\n const backing = tgToService.get(tgRef);\n if (!backing) {\n warnings.push(\n `${label} forwards to target group '${tgRef}', which is not referenced by any ` +\n `${SERVICE_TYPE}.LoadBalancers[] in ${stackName}; cdk-local has no ECS service to front ` +\n 'behind it. Skipping that target group.'\n );\n continue;\n }\n targets.push({\n kind: 'ecs',\n serviceLogicalId: backing.serviceLogicalId,\n targetContainerName: backing.containerName,\n targetContainerPort: backing.containerPort,\n targetGroupLogicalId: tgRef,\n weight,\n });\n }\n\n if (targets.length === 0) return undefined; // every target group was skipped (already warned)\n return { kind: 'forward', targets };\n}\n\n/**\n * Resolve a `TargetType: lambda` target group into its `FrontDoorLambdaTarget`\n * (weight applied by the caller), or `undefined` (with a warning) when the\n * backing function is not an in-stack `AWS::Lambda::Function` reference.\n */\nfunction resolveLambdaForwardTarget(\n tgProps: Record<string, unknown>,\n tgRef: string,\n resources: Record<string, TemplateResource>,\n stackName: string,\n label: string,\n warnings: string[]\n): Omit<FrontDoorLambdaTarget, 'weight'> | undefined {\n const lambdaLogicalId = resolveLambdaTargetLogicalId(tgProps['Targets']);\n if (!lambdaLogicalId) {\n warnings.push(\n `${label} forwards to a Lambda target group '${tgRef}', but its Targets[].Id is not an ` +\n 'in-stack { \"Fn::GetAtt\": [<FnLogicalId>, \"Arn\"] } reference; the local ALB front-door ' +\n 'supports an in-stack Lambda target only (literal / imported ARNs deferred). Skipping that target group.'\n );\n return undefined;\n }\n const lambda = resources[lambdaLogicalId];\n if (!lambda || lambda.Type !== LAMBDA_FUNCTION_TYPE) {\n warnings.push(\n `${label} forwards to Lambda target group '${tgRef}', whose target resolves to ` +\n `'${lambdaLogicalId}', but no ${LAMBDA_FUNCTION_TYPE} with that logical id exists in ` +\n `${stackName}. Skipping that target group.`\n );\n return undefined;\n }\n return {\n kind: 'lambda',\n lambdaLogicalId,\n targetGroupLogicalId: tgRef,\n multiValueHeaders: readMultiValueHeadersAttribute(tgProps['TargetGroupAttributes']),\n };\n}\n\n/** Resolve a `redirect` action into its `Location`-template fields + status code. */\nfunction resolveRedirectAction(\n action: Record<string, unknown>,\n label: string,\n warnings: string[]\n): FrontDoorRedirectAction | undefined {\n const cfg = action['RedirectConfig'];\n if (!cfg || typeof cfg !== 'object') {\n warnings.push(`${label} is a redirect with no RedirectConfig; skipping it.`);\n return undefined;\n }\n const c = cfg as Record<string, unknown>;\n const statusCode = parseRedirectStatusCode(c['StatusCode']);\n const out: FrontDoorRedirectAction = { kind: 'redirect', statusCode };\n if (typeof c['Protocol'] === 'string') out.protocol = c['Protocol'];\n if (typeof c['Host'] === 'string') out.host = c['Host'];\n if (typeof c['Port'] === 'string') out.port = c['Port'];\n if (typeof c['Path'] === 'string') out.path = c['Path'];\n if (typeof c['Query'] === 'string') out.query = c['Query'];\n return out;\n}\n\n/** Resolve a `fixed-response` action into its status / content-type / body. */\nfunction resolveFixedResponseAction(\n action: Record<string, unknown>\n): FrontDoorFixedResponseAction | undefined {\n const cfg = action['FixedResponseConfig'];\n const c = cfg && typeof cfg === 'object' ? (cfg as Record<string, unknown>) : {};\n const statusCode = parseFixedResponseStatusCode(c['StatusCode']);\n const out: FrontDoorFixedResponseAction = { kind: 'fixed-response', statusCode };\n if (typeof c['ContentType'] === 'string') out.contentType = c['ContentType'];\n if (typeof c['MessageBody'] === 'string') out.messageBody = c['MessageBody'];\n return out;\n}\n\n/**\n * Resolve a `TargetType: lambda` target group's backing Lambda logical id from\n * its `Targets[].Id`. CDK synthesizes the registration as\n * `Targets: [{ Id: { \"Fn::GetAtt\": [<FnLogicalId>, \"Arn\"] } }]`; a `Ref` to the\n * function (its name) is also accepted. Returns the logical id, or `undefined`\n * when the target is a literal / imported ARN (not an in-stack reference).\n */\nfunction resolveLambdaTargetLogicalId(targets: unknown): string | undefined {\n if (!Array.isArray(targets) || targets.length === 0) return undefined;\n const first = targets[0];\n if (!first || typeof first !== 'object') return undefined;\n const id = (first as Record<string, unknown>)['Id'];\n if (!id || typeof id !== 'object' || Array.isArray(id)) return undefined;\n const idObj = id as Record<string, unknown>;\n const getAtt = idObj['Fn::GetAtt'];\n if (Array.isArray(getAtt) && typeof getAtt[0] === 'string' && getAtt[0].length > 0) {\n return getAtt[0];\n }\n const ref = idObj['Ref'];\n if (typeof ref === 'string' && ref.length > 0) return ref;\n return undefined;\n}\n\n/**\n * Read the `lambda.multi_value_headers.enabled` target-group attribute (a\n * string `\"true\"` / `\"false\"` in CFn). Defaults to `false` when absent.\n */\nfunction readMultiValueHeadersAttribute(attributes: unknown): boolean {\n if (!Array.isArray(attributes)) return false;\n for (const attr of attributes) {\n if (!attr || typeof attr !== 'object') continue;\n const a = attr as Record<string, unknown>;\n if (a['Key'] === 'lambda.multi_value_headers.enabled') {\n return String(a['Value']).toLowerCase() === 'true';\n }\n }\n return false;\n}\n\n/**\n * Build a `targetGroupLogicalId -> backing ECS service` index by scanning every\n * `AWS::ECS::Service.LoadBalancers[]`. First service wins on a shared target\n * group (unusual; would only happen with a hand-rolled template).\n */\nfunction indexTargetGroupToService(\n resources: Record<string, TemplateResource>\n): Map<string, BackingServiceRef> {\n const index = new Map<string, BackingServiceRef>();\n for (const [serviceLogicalId, resource] of Object.entries(resources)) {\n if (resource.Type !== SERVICE_TYPE) continue;\n const lbs = (resource.Properties as Record<string, unknown> | undefined)?.['LoadBalancers'];\n if (!Array.isArray(lbs)) continue;\n for (const entry of lbs) {\n if (!entry || typeof entry !== 'object') continue;\n const e = entry as Record<string, unknown>;\n const tgRef = refOf(e['TargetGroupArn']);\n const containerName = typeof e['ContainerName'] === 'string' ? e['ContainerName'] : undefined;\n const containerPort = parseContainerPort(e['ContainerPort']);\n if (!tgRef || !containerName || containerPort === undefined) continue;\n if (!index.has(tgRef)) index.set(tgRef, { serviceLogicalId, containerName, containerPort });\n }\n }\n return index;\n}\n\n/** Group every `AWS::ElasticLoadBalancingV2::ListenerRule` by the listener it references. */\nfunction indexRulesByListener(\n resources: Record<string, TemplateResource>\n): Map<string, Array<{ ruleLogicalId: string; ruleProps: Record<string, unknown> }>> {\n const index = new Map<\n string,\n Array<{ ruleLogicalId: string; ruleProps: Record<string, unknown> }>\n >();\n for (const [ruleLogicalId, resource] of Object.entries(resources)) {\n if (resource.Type !== LISTENER_RULE_TYPE) continue;\n const ruleProps = (resource.Properties ?? {}) as Record<string, unknown>;\n const listenerRef = refOf(ruleProps['ListenerArn']);\n if (!listenerRef) continue;\n const list = index.get(listenerRef) ?? [];\n list.push({ ruleLogicalId, ruleProps });\n index.set(listenerRef, list);\n }\n return index;\n}\n\ninterface ParsedRuleConditions {\n pathPatterns: string[];\n hostPatterns: string[];\n httpHeaderConditions: AlbHttpHeaderCondition[];\n httpRequestMethods: string[];\n queryStringConditions: AlbQueryStringCondition[];\n sourceIpCidrs: string[];\n}\n\n/**\n * Parse a ListenerRule's `Conditions` into the six supported fields. Returns\n * `undefined` when an unknown field is encountered or a known field is\n * malformed enough that honoring the rule would silently drop a constraint\n * — both surface a warning and skip the whole rule (ALB ANDs conditions of\n * different fields, so dropping any condition would route requests it should\n * not).\n *\n * Multiple `http-header` conditions on different names are kept (they AND).\n * Multiple `path-pattern` / `host-header` / `http-request-method` /\n * `query-string` / `source-ip` conditions on the same field merge their\n * values (each field OR-matches). A `source-ip` value that does not parse as\n * a CIDR makes the whole rule unsupported (the rule's authoring intent was\n * specific; dropping the invalid range would silently widen the allow list).\n */\nfunction parseRuleConditions(\n conditions: unknown,\n ruleLabel: string,\n warnings: string[]\n): ParsedRuleConditions | undefined {\n const out: ParsedRuleConditions = {\n pathPatterns: [],\n hostPatterns: [],\n httpHeaderConditions: [],\n httpRequestMethods: [],\n queryStringConditions: [],\n sourceIpCidrs: [],\n };\n if (!Array.isArray(conditions)) return out;\n for (const cond of conditions) {\n if (!cond || typeof cond !== 'object') continue;\n const c = cond as Record<string, unknown>;\n const field = typeof c['Field'] === 'string' ? c['Field'] : '(unknown)';\n if (field === 'path-pattern') {\n out.pathPatterns.push(...conditionValues(c, 'PathPatternConfig'));\n } else if (field === 'host-header') {\n out.hostPatterns.push(...conditionValues(c, 'HostHeaderConfig'));\n } else if (field === 'http-header') {\n const parsed = parseHttpHeaderCondition(c);\n if (!parsed) {\n warnings.push(\n `${ruleLabel} has an http-header condition with no HttpHeaderName / Values; skipping ` +\n 'the rule.'\n );\n return undefined;\n }\n out.httpHeaderConditions.push(parsed);\n } else if (field === 'http-request-method') {\n out.httpRequestMethods.push(...conditionValues(c, 'HttpRequestMethodConfig'));\n } else if (field === 'query-string') {\n const { parsed, hadEntries } = parseQueryStringConditionValues(c);\n if (hadEntries && parsed.length === 0) {\n warnings.push(\n `${ruleLabel} query-string condition has no parseable { Key?, Value } entries; skipping ` +\n 'the rule.'\n );\n return undefined;\n }\n out.queryStringConditions.push(...parsed);\n } else if (field === 'source-ip') {\n const values = conditionValues(c, 'SourceIpConfig');\n const invalid = values.filter((v) => !isValidCidr(v));\n if (invalid.length > 0) {\n warnings.push(\n `${ruleLabel} source-ip condition has unparseable CIDR(s): ${invalid.join(', ')}. ` +\n 'The local ALB front-door requires valid IPv4 / IPv6 CIDRs; skipping the rule.'\n );\n return undefined;\n }\n out.sourceIpCidrs.push(...values);\n } else {\n warnings.push(`${ruleLabel} uses unsupported condition field '${field}'. Skipping the rule.`);\n return undefined;\n }\n }\n return out;\n}\n\n/**\n * Parse an `http-header` condition into its `{ name, values }` form. Returns\n * `undefined` when `HttpHeaderName` is missing / non-string or `Values` is\n * empty (either makes the rule's authoring intent unrecoverable).\n */\nfunction parseHttpHeaderCondition(\n cond: Record<string, unknown>\n): AlbHttpHeaderCondition | undefined {\n const cfg = cond['HttpHeaderConfig'];\n if (!cfg || typeof cfg !== 'object') return undefined;\n const c = cfg as Record<string, unknown>;\n const name = c['HttpHeaderName'];\n if (typeof name !== 'string' || name.length === 0) return undefined;\n const rawValues = Array.isArray(c['Values']) ? (c['Values'] as unknown[]) : [];\n const values = rawValues.filter((v): v is string => typeof v === 'string');\n if (values.length === 0) return undefined;\n return { name, values };\n}\n\n/**\n * Parse a `query-string` condition's `Values` into `{ key?, value }` pairs.\n * ALB accepts either object entries (`{ Key?, Value }`) or bare strings\n * (legacy v1 shape: a string is treated as `{ value }`). Non-string `Key` /\n * `Value` fields are dropped; an entry with no `Value` is dropped. Returns\n * `hadEntries` so the caller can distinguish an absent / empty `Values` array\n * (drop the field silently) from one whose entries were all unparseable\n * (warn + skip the whole rule — silently dropping would widen routing).\n */\nfunction parseQueryStringConditionValues(cond: Record<string, unknown>): {\n parsed: AlbQueryStringCondition[];\n hadEntries: boolean;\n} {\n const cfg = cond['QueryStringConfig'];\n const rawValues =\n cfg && typeof cfg === 'object' && Array.isArray((cfg as Record<string, unknown>)['Values'])\n ? ((cfg as Record<string, unknown>)['Values'] as unknown[])\n : Array.isArray(cond['Values'])\n ? (cond['Values'] as unknown[])\n : [];\n const parsed: AlbQueryStringCondition[] = [];\n for (const v of rawValues) {\n if (typeof v === 'string') {\n parsed.push({ value: v });\n } else if (v && typeof v === 'object') {\n const e = v as Record<string, unknown>;\n const value = e['Value'];\n if (typeof value !== 'string') continue;\n const key = e['Key'];\n parsed.push(typeof key === 'string' ? { key, value } : { value });\n }\n }\n return { parsed, hadEntries: rawValues.length > 0 };\n}\n\n/**\n * Cheap CIDR validity check used at parse time. Mirrors the more permissive\n * matcher (`albCidrMatches`) but only confirms shape; we do not need to\n * remember the parsed bytes here.\n */\nfunction isValidCidr(value: string): boolean {\n const slash = value.indexOf('/');\n if (slash === -1) return false;\n const addr = value.slice(0, slash);\n const prefix = value.slice(slash + 1);\n if (!/^\\d+$/.test(prefix)) return false;\n const prefixLen = parseInt(prefix, 10);\n if (addr.includes('.') && !addr.includes(':')) {\n const parts = addr.split('.');\n if (parts.length !== 4) return false;\n if (!parts.every((p) => /^\\d+$/.test(p) && parseInt(p, 10) <= 255)) return false;\n return prefixLen >= 0 && prefixLen <= 32;\n }\n if (addr.includes(':')) {\n return prefixLen >= 0 && prefixLen <= 128;\n }\n return false;\n}\n\n/**\n * Extract a condition's string values from either the typed `<Field>Config`\n * sub-object's `Values` or the legacy top-level `Values` array.\n */\nfunction conditionValues(cond: Record<string, unknown>, configKey: string): string[] {\n const cfg = cond[configKey];\n const raw =\n cfg && typeof cfg === 'object' && Array.isArray((cfg as Record<string, unknown>)['Values'])\n ? ((cfg as Record<string, unknown>)['Values'] as unknown[])\n : Array.isArray(cond['Values'])\n ? (cond['Values'] as unknown[])\n : [];\n return raw.filter((v): v is string => typeof v === 'string');\n}\n\n/** Collect a forward action's `(targetGroupRef, weight)` pairs (single + ForwardConfig forms). */\nfunction collectForwardTargetGroupRefs(\n action: Record<string, unknown>\n): Array<{ tgRef: string; weight: number }> {\n const out: Array<{ tgRef: string; weight: number }> = [];\n const direct = refOf(action['TargetGroupArn']);\n if (direct) out.push({ tgRef: direct, weight: 1 });\n const forwardConfig = action['ForwardConfig'];\n if (forwardConfig && typeof forwardConfig === 'object') {\n const groups = (forwardConfig as Record<string, unknown>)['TargetGroups'];\n if (Array.isArray(groups)) {\n for (const g of groups) {\n if (!g || typeof g !== 'object') continue;\n const gObj = g as Record<string, unknown>;\n const ref = refOf(gObj['TargetGroupArn']);\n if (ref) out.push({ tgRef: ref, weight: parseWeight(gObj['Weight']) });\n }\n }\n }\n return out;\n}\n\n/**\n * True when `action` is a `forward` that references a target group via a\n * NON-`Ref` arn (literal / `Fn::GetAtt` / cross-stack) — i.e. a forward we\n * could not resolve to an in-stack target group. Used to warn rather than\n * silently skip.\n */\nfunction hasUnresolvableForward(action: Record<string, unknown>): boolean {\n if (action['TargetGroupArn'] !== undefined && refOf(action['TargetGroupArn']) === undefined) {\n return true;\n }\n const forwardConfig = action['ForwardConfig'];\n if (forwardConfig && typeof forwardConfig === 'object') {\n const groups = (forwardConfig as Record<string, unknown>)['TargetGroups'];\n if (Array.isArray(groups)) {\n for (const g of groups) {\n if (!g || typeof g !== 'object') continue;\n const arn = (g as Record<string, unknown>)['TargetGroupArn'];\n if (arn !== undefined && refOf(arn) === undefined) return true;\n }\n }\n }\n return false;\n}\n\nfunction refOf(raw: unknown): string | undefined {\n if (raw && typeof raw === 'object' && !Array.isArray(raw)) {\n const ref = (raw as Record<string, unknown>)['Ref'];\n if (typeof ref === 'string' && ref.length > 0) return ref;\n }\n return undefined;\n}\n\nfunction parsePort(raw: unknown): number | undefined {\n if (typeof raw === 'number' && Number.isInteger(raw) && raw >= 1 && raw <= 65535) return raw;\n if (typeof raw === 'string' && /^\\d+$/.test(raw)) {\n const n = parseInt(raw, 10);\n if (n >= 1 && n <= 65535) return n;\n }\n return undefined;\n}\n\nfunction parseContainerPort(raw: unknown): number | undefined {\n return parsePort(raw);\n}\n\n/**\n * Parse a `ForwardConfig.TargetGroups[].Weight`. ALB weights are 0-999; a\n * missing weight defaults to 1 (CDK's `weightedForward` always emits one, but\n * a hand-rolled template may omit it). Negative / non-numeric clamps to 0.\n */\nfunction parseWeight(raw: unknown): number {\n if (typeof raw === 'number' && Number.isFinite(raw)) return raw < 0 ? 0 : raw;\n if (typeof raw === 'string' && /^\\d+$/.test(raw)) return parseInt(raw, 10);\n return 1;\n}\n\n/** ALB emits redirect status as `HTTP_301` / `HTTP_302`; default to 302 when absent / unknown. */\nfunction parseRedirectStatusCode(raw: unknown): 301 | 302 {\n if (raw === 'HTTP_301' || raw === '301' || raw === 301) return 301;\n return 302;\n}\n\n/** Parse a `FixedResponseConfig.StatusCode` (a numeric string); default 200 when absent. */\nfunction parseFixedResponseStatusCode(raw: unknown): number {\n if (typeof raw === 'number' && Number.isInteger(raw)) return raw;\n if (typeof raw === 'string' && /^\\d+$/.test(raw)) return parseInt(raw, 10);\n return 200;\n}\n\n/**\n * Parse a ListenerRule `Priority` (ALB priorities are 1-50000, lower = higher\n * precedence). A missing / unparseable priority sorts last so an explicitly\n * prioritized rule always wins over it.\n */\nfunction parsePriority(raw: unknown): number {\n if (typeof raw === 'number' && Number.isFinite(raw)) return raw;\n if (typeof raw === 'string' && /^\\d+$/.test(raw)) return parseInt(raw, 10);\n return Number.MAX_SAFE_INTEGER;\n}\n\n/** ALB's default session cookie name prefix (suffixed `-0` / `-1` / ... by ALB). */\nconst DEFAULT_ALB_SESSION_COOKIE = 'AWSELBAuthSessionCookie';\n\n/**\n * Cognito User Pool ARN shape:\n * `arn:aws:cognito-idp:<region>:<account>:userpool/<pool-id>`\n * The pool id itself contains a `_`, but never a `/`, so anchoring on the\n * `userpool/` segment is robust.\n */\nconst COGNITO_USERPOOL_ARN = /^arn:[^:]+:cognito-idp:([^:]+):[^:]+:userpool\\/([^/]+)$/;\n\n/**\n * Parse an `authenticate-cognito` / `authenticate-oidc` ALB action into a\n * resolvable {@link FrontDoorAuthGuard}, or `undefined` when the config is\n * unresolvable (a Ref / intrinsic in a required field, a malformed\n * UserPoolArn, etc.) — a warning is emitted in that case so the user knows\n * the guard was dropped (the terminal action will still serve unguarded).\n */\nfunction parseAuthenticateAction(\n action: Record<string, unknown>,\n type: 'authenticate-cognito' | 'authenticate-oidc',\n label: string,\n warnings: string[]\n): FrontDoorAuthGuard | undefined {\n if (type === 'authenticate-cognito') {\n const cfg = action['AuthenticateCognitoConfig'];\n if (!cfg || typeof cfg !== 'object') {\n warnings.push(\n `${label}: authenticate-cognito missing AuthenticateCognitoConfig; skipping guard.`\n );\n return undefined;\n }\n const c = cfg as Record<string, unknown>;\n const userPoolArn = c['UserPoolArn'];\n const userPoolClientId = c['UserPoolClientId'];\n if (typeof userPoolArn !== 'string' || typeof userPoolClientId !== 'string') {\n warnings.push(\n `${label}: authenticate-cognito UserPoolArn / UserPoolClientId must be literal strings ` +\n '(Ref / intrinsics cannot be resolved by the local front-door); skipping guard.'\n );\n return undefined;\n }\n const match = COGNITO_USERPOOL_ARN.exec(userPoolArn);\n if (!match) {\n warnings.push(\n `${label}: authenticate-cognito UserPoolArn '${userPoolArn}' is not in the expected ` +\n 'arn:...:cognito-idp:<region>:<account>:userpool/<pool-id> shape; skipping guard.'\n );\n return undefined;\n }\n const region = match[1]!;\n const userPoolId = match[2]!;\n const sessionCookieName =\n typeof c['SessionCookieName'] === 'string' && c['SessionCookieName'] !== ''\n ? c['SessionCookieName']\n : DEFAULT_ALB_SESSION_COOKIE;\n return {\n kind: 'authenticate-cognito',\n issuer: `https://cognito-idp.${region}.amazonaws.com/${userPoolId}`,\n audience: userPoolClientId,\n region,\n userPoolId,\n sessionCookieName,\n label: `authenticate-cognito (UserPool=${userPoolId})`,\n };\n }\n\n // authenticate-oidc\n const cfg = action['AuthenticateOidcConfig'];\n if (!cfg || typeof cfg !== 'object') {\n warnings.push(`${label}: authenticate-oidc missing AuthenticateOidcConfig; skipping guard.`);\n return undefined;\n }\n const c = cfg as Record<string, unknown>;\n const issuer = c['Issuer'];\n const clientId = c['ClientId'];\n if (typeof issuer !== 'string' || typeof clientId !== 'string') {\n warnings.push(\n `${label}: authenticate-oidc Issuer / ClientId must be literal strings ` +\n '(Ref / intrinsics cannot be resolved by the local front-door); skipping guard.'\n );\n return undefined;\n }\n const sessionCookieName =\n typeof c['SessionCookieName'] === 'string' && c['SessionCookieName'] !== ''\n ? c['SessionCookieName']\n : DEFAULT_ALB_SESSION_COOKIE;\n return {\n kind: 'authenticate-oidc',\n issuer: issuer.replace(/\\/+$/, ''),\n audience: clientId,\n sessionCookieName,\n label: `authenticate-oidc (Issuer=${issuer})`,\n };\n}\n","import { Command, Option } from 'commander';\nimport { withErrorHandling, LocalStartServiceError } from '../../utils/error-handler.js';\nimport { listTargets } from '../../local/target-lister.js';\nimport {\n getEmbedConfig,\n setEmbedConfig,\n type CdkLocalEmbedConfig,\n} from '../../local/embed-config.js';\nimport type { StackInfo } from '../../synthesis/assembly-reader.js';\nimport { parseEcsTarget } from '../../local/ecs-task-resolver.js';\nimport { resolveLambdaTarget } from '../../local/lambda-resolver.js';\nimport { matchStacks } from '../stack-matcher.js';\nimport { buildCdkPathIndex, resolveCdkPathToLogicalIds } from '../cdk-path.js';\nimport {\n resolveAlbFrontDoor,\n isApplicationLoadBalancer,\n type ResolvedListenerAction,\n type FrontDoorForwardTarget,\n} from '../../local/elb-front-door-resolver.js';\nimport type { ExtraStateProviders } from './local-state-source.js';\nimport {\n addCommonEcsServiceOptions,\n runEcsServiceEmulator,\n type EcsServiceEmulatorOptions,\n type EmulatorStrategy,\n type ServiceBoot,\n type PlannedAction,\n type PlannedForwardTarget,\n type PlannedFrontDoorListener,\n} from './ecs-service-emulator.js';\n\n/**\n * Factory options for {@link createLocalStartAlbCommand}.\n */\nexport interface CreateLocalStartAlbCommandOptions {\n extraStateProviders?: ExtraStateProviders;\n /** Embed-time branding overrides for a host wrapping this factory. */\n embedConfig?: CdkLocalEmbedConfig;\n}\n\n/**\n * Issue #86 v1 — parse `--lb-port <listenerPort>=<hostPort>` overrides into a\n * `listenerPort -> hostPort` map. The local ALB front-door binds the listener\n * port on the host by default, but a privileged listener port (e.g. 80 / 443)\n * fails to bind as non-root on macOS, so the user opts in to a non-privileged\n * host port (e.g. `--lb-port 80=8080`). Repeatable; each value is\n * `<listenerPort>=<hostPort>` with both in 1-65535.\n */\nexport function parseLbPortOverrides(values: string[] | undefined): Record<number, number> {\n const out: Record<number, number> = {};\n for (const raw of values ?? []) {\n const m = /^(\\d+)=(\\d+)$/.exec(raw.trim());\n if (!m) {\n throw new LocalStartServiceError(\n `Invalid --lb-port '${raw}'. Expected <listenerPort>=<hostPort> (e.g. 80=8080).`\n );\n }\n const listenerPort = Number(m[1]);\n const hostPort = Number(m[2]);\n for (const [label, p] of [\n ['listener', listenerPort],\n ['host', hostPort],\n ] as const) {\n if (p < 1 || p > 65535) {\n throw new LocalStartServiceError(\n `Invalid --lb-port '${raw}': ${label} port must be 1-65535.`\n );\n }\n }\n out[listenerPort] = hostPort;\n }\n return out;\n}\n\n/**\n * Resolve an ALB target string (`Stack/Path` display path or `Stack:LogicalId`)\n * to its stack + `AWS::ElasticLoadBalancingV2::LoadBalancer` logical id. Mirrors\n * the ECS service resolver's target grammar.\n */\nexport function resolveAlbTarget(\n target: string,\n stacks: StackInfo[]\n): { stack: StackInfo; albLogicalId: string } {\n if (stacks.length === 0) {\n throw new LocalStartServiceError('No stacks found in the synthesized assembly.');\n }\n const parsed = parseEcsTarget(target);\n const stack = pickStack(parsed.stackPattern, stacks, target);\n const resources = stack.template.Resources ?? {};\n\n if (parsed.isPath) {\n const index = buildCdkPathIndex(stack.template);\n const resolved = resolveCdkPathToLogicalIds(parsed.pathOrId, index);\n const albs = resolved.filter(({ logicalId }) => {\n const r = resources[logicalId];\n return r !== undefined && isApplicationLoadBalancer(r);\n });\n if (albs.length === 0) {\n throw notFound(target, stack, resources);\n }\n if (albs.length > 1) {\n throw new LocalStartServiceError(\n `Target '${target}' matches ${albs.length} load balancers in ${stack.stackName}: ` +\n `${albs.map((a) => a.logicalId).join(', ')}. Refine the path or use the stack:LogicalId form.`\n );\n }\n return { stack, albLogicalId: albs[0]!.logicalId };\n }\n\n const res = resources[parsed.pathOrId];\n if (!res || !isApplicationLoadBalancer(res)) {\n throw notFound(target, stack, resources);\n }\n return { stack, albLogicalId: parsed.pathOrId };\n}\n\nfunction pickStack(stackPattern: string | null, stacks: StackInfo[], target: string): StackInfo {\n if (stackPattern === null) {\n if (stacks.length === 1) return stacks[0]!;\n throw new LocalStartServiceError(\n `Target '${target}' has no stack prefix, and the assembly contains ${stacks.length} stacks: ` +\n `${stacks.map((s) => s.stackName).join(', ')}. Pass it as 'Stack/Path' or 'Stack:LogicalId'.`\n );\n }\n const matched = matchStacks(stacks, [stackPattern]);\n if (matched.length === 0) {\n throw new LocalStartServiceError(\n `No stack matches '${stackPattern}'. Available stacks: ${stacks.map((s) => s.stackName).join(', ')}.`\n );\n }\n if (matched.length > 1) {\n throw new LocalStartServiceError(\n `Multiple stacks match '${stackPattern}': ${matched.map((s) => s.stackName).join(', ')}. Refine the pattern.`\n );\n }\n return matched[0]!;\n}\n\nfunction notFound(\n target: string,\n stack: StackInfo,\n resources: Record<string, { Type: string }>\n): LocalStartServiceError {\n const albs = Object.entries(resources)\n .filter(([, r]) => r.Type === 'AWS::ElasticLoadBalancingV2::LoadBalancer')\n .map(([logicalId]) => logicalId);\n const available =\n albs.length > 0\n ? ` Available load balancers in ${stack.stackName}: ${albs.join(', ')}.`\n : ` ${stack.stackName} declares no AWS::ElasticLoadBalancingV2::LoadBalancer resources.`;\n return new LocalStartServiceError(\n `Target '${target}' did not match an application Load Balancer in ${stack.stackName}.${available}`\n );\n}\n\n/**\n * `cdkl start-alb` strategy — name the ALB, boot the ECS service(s) behind it,\n * and expose each listener via a local front-door. Mirrors how `start-api`\n * names the API and serves its backing Lambdas.\n */\nexport function albStrategy(options: EcsServiceEmulatorOptions): EmulatorStrategy {\n const lbPortOverrides = parseLbPortOverrides(options.lbPort);\n return {\n pickEntries: (stacks) => listTargets(stacks).loadBalancers,\n pickerMessage: 'Select one or more Application Load Balancers to run',\n pickerNoun: 'Application Load Balancers',\n onMissing: () =>\n new LocalStartServiceError(\n `${getEmbedConfig().cliName} start-alb requires at least one <target>. ` +\n \"Pass one or more ALB paths like 'Stack/MyAlb', or run it in a TTY to pick interactively.\"\n ),\n resolveBoots: (stacks, chosenTargets) => {\n const warnings: string[] = [];\n // Services to boot (deduped) + the per-listener host front-door plan.\n const serviceTargets = new Set<string>();\n const listeners: PlannedFrontDoorListener[] = [];\n // hostPort -> the listener port that claimed it (to explain a collision).\n const claimedHostPorts = new Map<number, number>();\n\n for (const albTarget of chosenTargets) {\n const { stack, albLogicalId } = resolveAlbTarget(albTarget, stacks);\n const resolution = resolveAlbFrontDoor(stack, albLogicalId);\n warnings.push(...resolution.warnings);\n\n // Qualify a resolver target into the front-door plan's target union,\n // carrying its forward weight. An ECS target becomes a `Stack:LogicalId`\n // service-boot target (and is recorded as a service we must run); a\n // Lambda target (#123) is resolved to its `ResolvedLambda` here so the\n // front-door can boot + invoke it locally (no ECS service to run for it).\n const qualifyTarget = (t: FrontDoorForwardTarget): PlannedForwardTarget => {\n if (t.kind === 'lambda') {\n const lambda = resolveLambdaTarget(`${stack.stackName}:${t.lambdaLogicalId}`, stacks);\n return {\n kind: 'lambda',\n lambda,\n // The local front-door has no real target-group ARN; surface the\n // logical id (qualified) so `requestContext.elb.targetGroupArn`\n // is stable + identifiable in handler logs.\n targetGroupArn: `${stack.stackName}:${t.targetGroupLogicalId}`,\n multiValueHeaders: t.multiValueHeaders,\n weight: t.weight,\n };\n }\n const serviceTarget = `${stack.stackName}:${t.serviceLogicalId}`;\n serviceTargets.add(serviceTarget);\n return {\n kind: 'ecs',\n serviceTarget,\n targetContainerName: t.targetContainerName,\n targetContainerPort: t.targetContainerPort,\n weight: t.weight,\n };\n };\n\n // Qualify a resolver action into a planned action: a forward qualifies\n // each weighted target (ECS or Lambda); redirect / fixed-response carry\n // no backing target.\n const qualify = (action: ResolvedListenerAction): PlannedAction => {\n if (action.kind === 'forward') {\n return { kind: 'forward', targets: action.targets.map(qualifyTarget) };\n }\n if (action.kind === 'redirect') {\n return {\n kind: 'redirect',\n statusCode: action.statusCode,\n ...(action.protocol !== undefined && { protocol: action.protocol }),\n ...(action.host !== undefined && { host: action.host }),\n ...(action.port !== undefined && { port: action.port }),\n ...(action.path !== undefined && { path: action.path }),\n ...(action.query !== undefined && { query: action.query }),\n };\n }\n return {\n kind: 'fixed-response',\n statusCode: action.statusCode,\n ...(action.contentType !== undefined && { contentType: action.contentType }),\n ...(action.messageBody !== undefined && { messageBody: action.messageBody }),\n };\n };\n\n for (const listener of resolution.listeners) {\n const hostPort = lbPortOverrides[listener.listenerPort] ?? listener.listenerPort;\n const claimedBy = claimedHostPorts.get(hostPort);\n if (claimedBy !== undefined) {\n warnings.push(\n `Listener port ${listener.listenerPort} would bind host port ${hostPort}, already ` +\n `claimed by listener port ${claimedBy}; the local front-door fronts only the ` +\n 'first. Use --lb-port to remap one of them.'\n );\n continue;\n }\n claimedHostPorts.set(hostPort, listener.listenerPort);\n listeners.push({\n listenerPort: listener.listenerPort,\n hostPort,\n protocol: listener.listenerProtocol,\n ...(listener.defaultAction ? { defaultAction: qualify(listener.defaultAction) } : {}),\n ...(listener.defaultAuthGuard ? { defaultAuthGuard: listener.defaultAuthGuard } : {}),\n rules: listener.rules.map((r) => ({\n priority: r.priority,\n pathPatterns: r.pathPatterns,\n hostPatterns: r.hostPatterns,\n httpHeaderConditions: r.httpHeaderConditions,\n httpRequestMethods: r.httpRequestMethods,\n queryStringConditions: r.queryStringConditions,\n sourceIpCidrs: r.sourceIpCidrs,\n action: qualify(r.action),\n ...(r.authGuard ? { authGuard: r.authGuard } : {}),\n })),\n });\n }\n }\n\n const boots: ServiceBoot[] = [...serviceTargets].map((target) => ({ target }));\n\n // Warn about a `--lb-port <listenerPort>=...` override whose listener\n // port matched NONE of the resolved front-door listeners — almost always\n // a typo, and otherwise a silent no-op.\n const resolvedPorts = new Set(listeners.map((l) => l.listenerPort));\n for (const portStr of Object.keys(lbPortOverrides)) {\n const port = Number(portStr);\n if (!resolvedPorts.has(port)) {\n warnings.push(\n `--lb-port override for listener port ${port} matched no ALB listener resolved for ` +\n 'the named target(s); it was ignored.'\n );\n }\n }\n\n return {\n boots,\n ...(listeners.length > 0 ? { frontDoor: { listeners } } : {}),\n warnings,\n };\n },\n lbPortOverrides,\n };\n}\n\n/**\n * `cdkl start-alb <Stack/Alb>` — Issue #86 v1. Names an\n * `AWS::ElasticLoadBalancingV2::LoadBalancer`, discovers the ECS service(s)\n * behind its HTTP `forward` listeners, boots their replicas, and stands up a\n * local front-door on each listener port that round-robins across the replicas.\n * The symmetric ALB counterpart of `start-api`.\n */\nexport function createLocalStartAlbCommand(opts: CreateLocalStartAlbCommandOptions = {}): Command {\n setEmbedConfig(opts.embedConfig);\n const cmd = new Command('start-alb')\n .description(\n 'Run an Application Load Balancer locally: name the ALB, and cdk-local boots the ECS ' +\n 'service(s) behind its listeners and stands up a local front-door on each listener port ' +\n 'that round-robins across the running replicas and routes its listener rules across the ' +\n 'backing services — a stable host endpoint, like behind a real load balancer. The ' +\n 'symmetric ALB counterpart of `start-api`. Each <target> accepts a CDK display path ' +\n '(MyStack/MyAlb) or stack-qualified logical ID; single-stack apps may omit the stack ' +\n 'prefix. Supports HTTP and HTTPS listeners (TLS terminated locally with --tls-cert/' +\n '--tls-key or an auto-generated self-signed cert); all six ALB rule-condition fields ' +\n '(path-pattern / host-header / http-header / http-request-method / query-string / ' +\n 'source-ip); forward (single and weighted), redirect, and fixed-response actions; and ' +\n 'ECS or Lambda targets (a Lambda target group is invoked locally via the Lambda RIE). ' +\n 'authenticate-cognito / authenticate-oidc actions enforce a local Bearer-JWT check ' +\n '(or AWSELBAuthSessionCookie pass-through) against the same JWKS / OIDC discovery URL ' +\n 'the deployed ALB would; use --bearer-token <jwt> to inject a default token or ' +\n '--no-verify-auth to disable the guard. Omit <targets> in an interactive terminal to ' +\n 'multi-select the load balancers from a list.'\n )\n .argument(\n '[targets...]',\n '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)'\n )\n .addOption(\n new Option(\n '--lb-port <listenerPort=hostPort...>',\n 'Bind the local front-door on a specific host port (e.g. 80=8080); repeatable. ' +\n 'Default: host port == ALB listener port. Use this on macOS to remap a privileged ' +\n 'listener port (< 1024) to a non-privileged host port.'\n )\n )\n .addOption(\n new Option(\n '--tls-cert <path>',\n 'PEM-encoded server certificate for HTTPS front-door listeners. Must be set together ' +\n 'with --tls-key. Omit both flags to auto-generate a self-signed cert ' +\n '(cached under $XDG_CACHE_HOME/cdk-local/alb-https/, default ~/.cache/cdk-local/' +\n 'alb-https/); requires openssl on PATH. The deployed Listener Certificates[] are NOT ' +\n 'fetched (ACM private keys are not retrievable by design). The auto-generated cert ' +\n 'lists DNS:localhost,IP:127.0.0.1 as SubjectAltName, so a client validating a ' +\n 'non-loopback --container-host will fail the SAN check — pass --tls-cert / --tls-key ' +\n 'with a SAN covering that host instead.'\n )\n )\n .addOption(\n new Option(\n '--tls-key <path>',\n 'PEM-encoded server private key matching --tls-cert. Must be set together with --tls-cert.'\n )\n )\n .addOption(\n new Option(\n '--no-verify-auth',\n 'Disable local enforcement of authenticate-cognito / authenticate-oidc actions. Every ' +\n 'request is served as if the auth check passed. Useful for local dev where you do not ' +\n 'want to mint a Bearer token at all.'\n )\n )\n .addOption(\n new Option(\n '--bearer-token <jwt>',\n 'Default Bearer JWT injected as Authorization: Bearer <jwt> when the inbound request has ' +\n 'none. Verified against the same JWKS / OIDC discovery URL the deployed ALB would ' +\n '(signature + iss + aud + exp). Local-dev convenience; cookie pass-through ' +\n '(AWSELBAuthSessionCookie-*) also works.'\n )\n )\n .action(\n withErrorHandling(async (targets: string[], options: EcsServiceEmulatorOptions) => {\n await runEcsServiceEmulator(\n targets,\n options,\n albStrategy(options),\n opts.extraStateProviders\n );\n })\n );\n\n return addCommonEcsServiceOptions(cmd);\n}\n","import { Command, Option } from 'commander';\nimport {\n appOptions,\n commonOptions,\n contextOptions,\n deprecatedRegionOption,\n parseContextOptions,\n warnIfDeprecatedRegion,\n} from '../options.js';\nimport { getLogger } from '../../utils/logger.js';\nimport { applyRoleArnIfSet } from '../../utils/role-arn.js';\nimport { withErrorHandling } from '../../utils/error-handler.js';\nimport { Synthesizer, type SynthesisOptions } from '../../synthesis/synthesizer.js';\nimport { resolveApp } from '../config-loader.js';\nimport {\n getEmbedConfig,\n setEmbedConfig,\n type CdkLocalEmbedConfig,\n} from '../../local/embed-config.js';\nimport {\n countTargets,\n listTargets,\n type TargetEntry,\n type TargetListing,\n} from '../../local/target-lister.js';\n\ninterface LocalListOptions {\n app?: string;\n output: string;\n verbose: boolean;\n profile?: string;\n roleArn?: string;\n context?: string[];\n region?: string;\n /** `-l/--long`: also print each target's stack-qualified logical ID. */\n long: boolean;\n}\n\n/**\n * Factory options for {@link createLocalListCommand}.\n */\nexport interface CreateLocalListCommandOptions {\n /** Embed-time branding overrides for a host wrapping this factory. */\n embedConfig?: CdkLocalEmbedConfig;\n}\n\nasync function localListCommand(options: LocalListOptions): Promise<void> {\n const logger = getLogger();\n if (options.verbose) logger.setLevel('debug');\n\n warnIfDeprecatedRegion(options);\n\n await applyRoleArnIfSet({ roleArn: options.roleArn, region: undefined });\n\n const appCmd = resolveApp(options.app);\n if (!appCmd) {\n throw new Error(\n `No CDK app specified. Pass --app, set ${getEmbedConfig().envPrefix}_APP, or add \"app\" to cdk.json.`\n );\n }\n\n // Status goes to stderr so `cdkl list` stdout is ONLY the target list\n // (clean for `cdkl list | ...`). Synthesis progress from toolkit-lib is\n // routed to stderr too (see CdklIoHost).\n process.stderr.write('Synthesizing CDK app...\\n');\n const synthesizer = new Synthesizer();\n const context = parseContextOptions(options.context);\n const synthOpts: SynthesisOptions = {\n app: appCmd,\n output: options.output,\n ...(options.profile && { profile: options.profile }),\n ...(Object.keys(context).length > 0 && { context }),\n };\n const { stacks } = await synthesizer.synthesize(synthOpts);\n\n const listing = listTargets(stacks);\n process.stdout.write(\n `${formatTargetListing(listing, getEmbedConfig().cliName, { long: options.long })}\\n`\n );\n}\n\n/** Options for {@link formatTargetListing}. */\nexport interface FormatTargetListingOptions {\n /**\n * Also print each target's stack-qualified logical ID (`-l/--long`).\n * Off by default: the CDK display path alone is the recommended,\n * readable target form, and the wide two-column layout wrapped badly\n * for the long auto-generated names CDK emits.\n */\n long?: boolean;\n}\n\n/**\n * Render a {@link TargetListing} as the grouped text list `cdkl list`\n * prints. Each non-empty category is preceded by a blank line and a\n * header naming the command that runs it, then one target per line by\n * CDK display path. With {@link FormatTargetListingOptions.long}, each\n * target's stack-qualified logical ID is printed on an indented line\n * beneath it. Exported so a unit test can assert the output shape\n * without running synthesis.\n */\nexport function formatTargetListing(\n listing: TargetListing,\n cliName: string,\n options: FormatTargetListingOptions = {}\n): string {\n if (countTargets(listing) === 0) {\n return `No runnable targets (Lambda functions, APIs, ECS services / tasks, AgentCore Runtimes, load balancers) found in this CDK app.`;\n }\n\n const long = options.long ?? false;\n const sections: string[][] = [\n formatSection('Lambda Functions', `${cliName} invoke <target>`, listing.lambdas, long),\n formatSection('APIs', `${cliName} start-api [target...]`, listing.apis, long),\n formatSection(\n 'ECS Services',\n `${cliName} start-service <target...>`,\n listing.ecsServices,\n long\n ),\n formatSection(\n 'ECS Task Definitions',\n `${cliName} run-task <target>`,\n listing.ecsTaskDefinitions,\n long\n ),\n formatSection(\n 'AgentCore Runtimes',\n `${cliName} invoke-agentcore <target>`,\n listing.agentCoreRuntimes,\n long\n ),\n formatSection(\n 'Application Load Balancers',\n `${cliName} start-alb <target...>`,\n listing.loadBalancers,\n long\n ),\n ];\n\n // Leading blank line + a blank line between groups so each header is a\n // clear landmark (and the first group is separated from the synth\n // status that precedes it on stderr).\n return (\n '\\n' +\n sections\n .filter((lines) => lines.length > 0)\n .map((lines) => lines.join('\\n'))\n .join('\\n\\n')\n );\n}\n\nfunction formatSection(\n title: string,\n command: string,\n entries: TargetEntry[],\n long: boolean\n): string[] {\n if (entries.length === 0) return [];\n const lines = [`${title} -> ${command}`];\n for (const entry of entries) {\n const primary = entry.displayPath ?? entry.qualifiedId;\n // Append the API surface kind (REST API v1 / HTTP API v2 / Function URL\n // / WebSocket) so the API group's otherwise-similar paths are\n // distinguishable; only `apis` entries carry a kind.\n lines.push(entry.kind ? ` ${primary} (${entry.kind})` : ` ${primary}`);\n // The qualified ID is only extra info when a display path was shown;\n // when it IS the primary, don't repeat it.\n if (long && entry.displayPath) {\n lines.push(` ${entry.qualifiedId}`);\n }\n }\n return lines;\n}\n\nexport function createLocalListCommand(opts: CreateLocalListCommandOptions = {}): Command {\n setEmbedConfig(opts.embedConfig);\n const cmd = new Command('list')\n .alias('ls')\n .description(\n 'List the runnable targets in the synthesized CDK app, grouped by the command that runs them: ' +\n 'Lambda functions (invoke), API Gateway REST v1 / HTTP v2 / Function URL / WebSocket surfaces ' +\n '(start-api), ECS services (start-service), ECS task definitions (run-task), AgentCore ' +\n 'Runtimes (invoke-agentcore), and Application Load Balancers (start-alb). Each target is ' +\n 'shown by its CDK display path; pass -l to also print the stack-qualified logical ID. Tip: you ' +\n 'usually do not need to copy these — just run the command (e.g. `invoke`) with no target in a ' +\n 'terminal and pick from the list.'\n )\n .addOption(\n new Option(\n '-l, --long',\n \"Also print each target's stack-qualified logical ID (<Stack>:<LogicalId>) beneath it\"\n ).default(false)\n )\n .action(\n withErrorHandling(async (options: LocalListOptions) => {\n await localListCommand(options);\n })\n );\n\n [...commonOptions(), ...appOptions(), ...contextOptions].forEach((opt) => cmd.addOption(opt));\n cmd.addOption(deprecatedRegionOption);\n return cmd;\n}\n"],"mappings":"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"}