@go-to-k/cdkd 0.203.0 → 0.205.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.
package/dist/cli.js CHANGED
@@ -534,6 +534,35 @@ function parseAssumeRoleToken(raw, previous) {
534
534
  return acc;
535
535
  }
536
536
  /**
537
+ * Compose `--assume-role` (value-form accumulator) and the separate
538
+ * `--assume-role-auto` boolean flag into the in-process
539
+ * representation `cdkd local start-api` consumes downstream.
540
+ *
541
+ * - both unset -> `undefined` (pass dev creds through)
542
+ * - only `--assume-role-auto` -> `{ perLambda: {}, bareAutoResolve: true }`
543
+ * (per-Lambda auto-resolve for every routed Lambda)
544
+ * - only `--assume-role` -> the value-form accumulator (global ARN
545
+ * and/or per-Lambda map) as-is
546
+ * - both -> accumulator with `bareAutoResolve = true` overlaid;
547
+ * the per-Lambda map still wins for named Lambdas, auto-resolve
548
+ * fills in for every Lambda the map does NOT name
549
+ *
550
+ * Enforces the mutual-exclusion guard: `--assume-role-auto` and a
551
+ * global ARN (`--assume-role <arn>`) both occupy the "default for
552
+ * every other Lambda" slot, so the combination is ambiguous and
553
+ * rejected at boot with a clear error. The per-Lambda map IS
554
+ * compatible with either side.
555
+ */
556
+ function normalizeStartApiAssumeRole(raw, autoResolve) {
557
+ if (raw === void 0) return autoResolve ? {
558
+ perLambda: {},
559
+ bareAutoResolve: true
560
+ } : void 0;
561
+ if (autoResolve && raw.globalArn) throw new Error(`--assume-role-auto auto-resolves EACH routed Lambda's own execution role, but --assume-role ${raw.globalArn} also names a single global default. These are mutually exclusive on the global slot. Either drop the global ARN to keep --assume-role-auto for every Lambda, or drop --assume-role-auto to keep the global default. Per-Lambda overrides (--assume-role <LogicalId>=<arn>) are compatible with either side.`);
562
+ if (autoResolve) raw.bareAutoResolve = true;
563
+ return raw;
564
+ }
565
+ /**
537
566
  * Resolve the effective IAM role ARN for a given Lambda. Per-Lambda
538
567
  * override wins; otherwise the global default; otherwise `undefined`
539
568
  * (no role to assume — pass developer creds through).
@@ -46636,6 +46665,9 @@ async function localStartApiCommand(target, options) {
46636
46665
  roleArn: options.roleArn,
46637
46666
  region: options.region
46638
46667
  });
46668
+ const normalizedAssumeRole = normalizeStartApiAssumeRole(options.assumeRole, options.assumeRoleAuto ?? false);
46669
+ if (normalizedAssumeRole === void 0) delete options.assumeRole;
46670
+ else options.assumeRole = normalizedAssumeRole;
46639
46671
  await ensureDockerAvailable();
46640
46672
  const appCmd = resolveApp(options.app);
46641
46673
  if (!appCmd) throw new Error("No CDK app specified. Pass --app, set CDKD_APP, or add \"app\" to cdk.json.");
@@ -46647,7 +46679,7 @@ async function localStartApiCommand(target, options) {
46647
46679
  let profileCredsFile;
46648
46680
  const authorizerCache = createAuthorizerCache();
46649
46681
  const jwksCache = createJwksCache();
46650
- const jwksWarnedUrls = /* @__PURE__ */ new Set();
46682
+ const jwksWarnedAt = /* @__PURE__ */ new Map();
46651
46683
  let sigV4CredentialsLoader;
46652
46684
  const sigV4WarnedForeignIds = /* @__PURE__ */ new Set();
46653
46685
  const fromCfnTipEmitted = { value: false };
@@ -46811,7 +46843,7 @@ async function localStartApiCommand(target, options) {
46811
46843
  port: basePort === 0 ? 0 : nextPort,
46812
46844
  authorizerCache,
46813
46845
  jwksCache,
46814
- jwksWarnedUrls,
46846
+ jwksWarnedAt,
46815
46847
  sigV4CredentialsLoader,
46816
46848
  sigV4WarnedForeignIds,
46817
46849
  sigV4Strict: options.strictSigv4 === true,
@@ -46858,7 +46890,7 @@ async function localStartApiCommand(target, options) {
46858
46890
  port: basePort === 0 ? 0 : nextPort,
46859
46891
  authorizerCache,
46860
46892
  jwksCache,
46861
- jwksWarnedUrls,
46893
+ jwksWarnedAt,
46862
46894
  sigV4WarnedForeignIds,
46863
46895
  sigV4Strict: options.strictSigv4 === true,
46864
46896
  preDispatch: async (req, res) => {
@@ -47202,6 +47234,57 @@ function warnIamRoutes(routesWithAuth) {
47202
47234
  return true;
47203
47235
  }
47204
47236
  /**
47237
+ * Resolve the effective IAM role ARN to assume for a single routed
47238
+ * Lambda, honoring the four `--assume-role` forms of issue #256
47239
+ * Option 1 (non-breaking step):
47240
+ *
47241
+ * - per-Lambda override (`<LogicalId>=<arn>`) — wins over every other
47242
+ * form for the named Lambda.
47243
+ * - global default (bare ARN) — used when the per-Lambda map misses.
47244
+ * - bare-auto-resolve (`--assume-role-auto`) — when neither the
47245
+ * per-Lambda map nor a global default applies, resolves THIS
47246
+ * Lambda's own `Role` (its `AWS::Lambda::Function`'s `Role`
47247
+ * property -> role ARN). Resolution sources, in order:
47248
+ * 1. literal-ARN form of `Properties.Role` in the SYNTHESIZED
47249
+ * template (rare in CDK output but possible);
47250
+ * 2. deployed state (`stateBundle.state.resources`) when
47251
+ * `--from-state` / `--from-cfn-stack` is active, via the same
47252
+ * shared helper `cdkd local invoke`'s bare-form uses
47253
+ * (`resolveExecutionRoleArnFromState`).
47254
+ * - flag absent / `--no-assume-role-auto` — returns `undefined`
47255
+ * (per-Lambda map and global default already filtered out by the
47256
+ * null-check at the top; bare-auto-resolve = false short-circuits
47257
+ * before the state lookup).
47258
+ *
47259
+ * The shape-level mutual exclusion (bare-auto-resolve + global ARN
47260
+ * together) is caught at boot by `normalizeStartApiAssumeRole`; this
47261
+ * resolver only sees a well-formed `AssumeRoleOption`.
47262
+ *
47263
+ * Bare-auto-resolve misses (no literal-ARN, no state, state did not
47264
+ * carry the role attribute) warn-and-fall-through to the
47265
+ * dev-creds-passed-through branch — same trade-off `cdkd local invoke
47266
+ * --assume-role` makes when bare auto-resolve cannot find an ARN.
47267
+ *
47268
+ * Exported for unit testing.
47269
+ */
47270
+ function resolveStartApiAssumeRoleArn(args) {
47271
+ const { logicalId, assumeRole, lambdaResource, stateBundle } = args;
47272
+ if (!assumeRole) return void 0;
47273
+ const explicit = effectiveAssumeRoleArn(logicalId, assumeRole);
47274
+ if (explicit) return explicit;
47275
+ if (!assumeRole.bareAutoResolve) return void 0;
47276
+ const roleProp = (lambdaResource.Properties ?? {})["Role"];
47277
+ if (typeof roleProp === "string" && roleProp.startsWith("arn:")) return roleProp;
47278
+ if (stateBundle) {
47279
+ const fromState = resolveExecutionRoleArnFromState(stateBundle.state, logicalId);
47280
+ if (fromState) {
47281
+ getLogger().info(`--assume-role: auto-resolved execution role for '${logicalId}' from state: ${fromState}`);
47282
+ return fromState;
47283
+ }
47284
+ }
47285
+ getLogger().warn(`--assume-role: could not auto-resolve the execution role ARN for '${logicalId}'. Pair --assume-role-auto with --from-state or --from-cfn-stack so the deployed Role's ARN can be looked up, OR pin the ARN explicitly with --assume-role ${logicalId}=<arn>. Falling back to the developer's shell credentials for this Lambda.`);
47286
+ }
47287
+ /**
47205
47288
  * Build the per-Lambda container spec — code dir, env vars (template +
47206
47289
  * --env-vars overlay), STS-issued creds when --assume-role names this
47207
47290
  * Lambda, optional --debug-port reservation. Errors out with a clear
@@ -47250,7 +47333,12 @@ async function buildContainerSpec(args) {
47250
47333
  AWS_LAMBDA_LOG_STREAM_NAME: "local",
47251
47334
  ...envResult.resolved
47252
47335
  };
47253
- const roleArn = effectiveAssumeRoleArn(logicalId, assumeRole);
47336
+ const roleArn = resolveStartApiAssumeRoleArn({
47337
+ logicalId,
47338
+ assumeRole,
47339
+ lambdaResource: lambda.resource,
47340
+ stateBundle
47341
+ });
47254
47342
  if (roleArn) {
47255
47343
  const creds = await assumeLambdaExecutionRole$1(roleArn, stsRegion);
47256
47344
  dockerEnv["AWS_ACCESS_KEY_ID"] = creds.accessKeyId;
@@ -48021,7 +48109,7 @@ function resolveMtlsConfig(options) {
48021
48109
  * Builder for the `start-api` subcommand. Wired up by `local.ts`.
48022
48110
  */
48023
48111
  function createLocalStartApiCommand() {
48024
- const startApi = new Command("start-api").description("Run a long-running local HTTP server that maps API Gateway routes (REST v1, HTTP API, Function URL) to Lambda invocations against the AWS Lambda Runtime Interface Emulator (Docker required). Supports Lambda TOKEN/REQUEST authorizers, Cognito User Pool / HTTP v2 JWT authorizers, and AWS_IAM auth (REST v1 `AuthorizationType: AWS_IAM` and Function URL `AuthType: AWS_IAM` — SigV4 signature verification only; IAM policy evaluation is NOT emulated; see https://github.com/go-to-k/cdkd/blob/main/docs/local-emulation.md). When JWKS is unreachable, JWT authorizers fall back to pass-through (every token accepted) with a warn line — local dev fallback. VPC-config Lambdas run locally and surface a warn line at startup; their containers do NOT get attached to the deployed VPC subnets, so calls to private RDS / ElastiCache will fail.").argument("[target]", "Optional API filter. Accepts the bare CDK logical id ('MyHttpApi'; single-stack apps only), stack-qualified logical id ('MyStack:MyHttpApi'), full CDK Construct path ('MyStack/MyHttpApi/Resource'), or an ancestor Construct path that prefix-matches ('MyStack/MyHttpApi'). When omitted, every discovered API gets its own server. Mirrors `cdkd local invoke` / `cdkd local run-task` target syntax.").addOption(new Option("--port <port>", "HTTP server port (default: auto-allocate)").default("0")).addOption(new Option("--host <host>", "Bind address").default("127.0.0.1")).addOption(new Option("--stack <name>", "Stack to start (single-stack apps auto-detect)")).addOption(new Option("--warm", "Pre-start one container per Lambda at server boot").default(false)).addOption(new Option("--per-lambda-concurrency <n>", "Pool size cap per Lambda (default 2, max 4)").default("2")).addOption(new Option("--no-pull", "Skip docker pull (cached image)")).addOption(new Option("--container-host <host>", "IP the host uses to bind/probe the RIE port (must be a numeric IP — `docker run -p <ip>:<port>:8080` rejects hostnames). Defaults to 127.0.0.1.").default("127.0.0.1")).addOption(new Option("--debug-port-base <port>", "Reserve a contiguous --debug-port range (one per Lambda)")).addOption(new Option("--env-vars <file>", "JSON env-var overrides (SAM-compatible: {\"LogicalId\":{\"KEY\":\"VALUE\"}, \"Parameters\": {...}})")).addOption(new Option("--assume-role <arn-or-pair>", "Assume the Lambda's execution role and forward STS-issued temp creds. Bare <arn> = global default; <LogicalId>=<arn> = per-Lambda override (repeatable). Per-Lambda > global > unset (developer creds passed through).").argParser((raw, prev) => parseAssumeRoleToken(raw, prev))).addOption(new Option("--watch", "Hot-reload: watch the CDK app source tree and re-synth + re-discover routes on a source edit (cdk.out / node_modules / .git excluded; honors cdk.json watch.include / watch.exclude). Off by default; the server keeps the previous version serving when synth fails mid-reload.").default(false)).addOption(new Option("--stage <name>", "Select an API Gateway Stage by its 'StageName'. Default: the first Stage attached to each API. Drives event.stageVariables for both REST v1 and HTTP API v2. NOTE: For HTTP API v2 routes, requestContext.stage is always '$default' regardless of this flag (AWS-side limitation — HTTP API only exposes one stage to the integration event); only event.stageVariables is affected for v2 routes. For REST v1 routes the selected StageName is also threaded into requestContext.stage.")).addOption(new Option("--api <id>", "DEPRECATED — use the positional <target> argument instead. Same accepted forms (bare logical id, stack-qualified, Construct path, ancestor prefix). Will be removed in a future major release.")).addOption(new Option("--layer-role-arn <arn>", "Role to sts:AssumeRole before calling lambda:GetLayerVersion on every literal-ARN entry in Properties.Layers (issue #448). Use only when the dev credentials cannot read the layer — typically cross-account layers. AWS-published public layers (e.g. Lambda Powertools) are readable from every account and need no role.")).addOption(new Option("--from-state", "Read cdkd S3 state for every routed stack and substitute Ref / Fn::GetAtt / Fn::Sub / Fn::Join (and AWS pseudo parameters) in Lambda env vars with the deployed physical IDs / attributes. Off by default — pre-PR warn-and-drop semantics are preserved. Turn on for stacks already deployed via cdkd deploy. Mirrors `cdkd local invoke --from-state` / `cdkd local run-task --from-state`. Re-runs against fresh state on every hot-reload firing (--watch).").default(false)).addOption(new Option("--from-cfn-stack [cfn-stack-name]", "Read a deployed CloudFormation stack via DescribeStackResources and substitute Ref / Fn::ImportValue in Lambda env vars with the deployed physical IDs / exports. Use for CDK apps deployed via the upstream CDK CLI (`cdk deploy`). Bare form uses the cdkd stack name per routed stack; pass an explicit value when a single CFn stack should serve every routed stack. Mutually exclusive with --from-state. Fn::GetAtt is warn-and-dropped in v1 (CFn DescribeStackResources does not return per-attribute values).")).addOption(new Option("--stack-region <region>", "Region of the state record to read. Used with --from-state when the same stack name has state in multiple regions, and with --from-cfn-stack as the CFn client region (cdkd does not have a separate --cfn-stack-region flag).")).addOption(new Option("--mtls-truststore <path>", "PEM-encoded CA bundle for client-certificate verification (mutual TLS). When set, the local server switches from HTTP to HTTPS and the TLS handshake rejects clients whose certificate doesn't chain to one of these CAs. Verified certs are surfaced on the Lambda event under requestContext.identity.clientCert (REST v1) / requestContext.authentication.clientCert (HTTP API v2). Must be set together with --mtls-cert + --mtls-key; partial flag sets are rejected. Generate a CA + server + client cert for local dev: openssl req -x509 -newkey rsa:2048 -nodes -keyout ca-key.pem -out ca.pem -subj \"/CN=cdkd-local-ca\" -days 365; openssl req -newkey rsa:2048 -nodes -keyout server-key.pem -out server-csr.pem -subj \"/CN=localhost\"; openssl x509 -req -in server-csr.pem -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out server-cert.pem -days 365; openssl req -newkey rsa:2048 -nodes -keyout client-key.pem -out client-csr.pem -subj \"/CN=client\"; openssl x509 -req -in client-csr.pem -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out client-cert.pem -days 365; curl --cacert ca.pem --cert client-cert.pem --key client-key.pem https://localhost:<port>/...")).addOption(new Option("--mtls-cert <path>", "PEM-encoded server certificate for mutual TLS. Self-signed is fine for local dev. Must be set together with --mtls-truststore + --mtls-key.")).addOption(new Option("--mtls-key <path>", "PEM-encoded server private key matching --mtls-cert. Must be set together with --mtls-truststore + --mtls-cert.")).addOption(new Option("--strict-sigv4", "Opt-in: enforce strict AWS_IAM SigV4 verification. When set, requests whose signature cannot be cryptographically verified (foreign access-key-id, OR no local AWS credentials configured) are denied. DEFAULT off — warn-and-pass with a placeholder principalId, matching cdk-local's `cdkl start-api`. Enable this when you want local parity with the deployed API Gateway's signature enforcement.").default(false)).action(withErrorHandling(localStartApiCommand));
48112
+ const startApi = new Command("start-api").description("Run a long-running local HTTP server that maps API Gateway routes (REST v1, HTTP API, Function URL) to Lambda invocations against the AWS Lambda Runtime Interface Emulator (Docker required). Supports Lambda TOKEN/REQUEST authorizers, Cognito User Pool / HTTP v2 JWT authorizers, and AWS_IAM auth (REST v1 `AuthorizationType: AWS_IAM` and Function URL `AuthType: AWS_IAM` — SigV4 signature verification only; IAM policy evaluation is NOT emulated; see https://github.com/go-to-k/cdkd/blob/main/docs/local-emulation.md). When JWKS is unreachable, JWT authorizers fall back to pass-through (every token accepted) with a warn line — local dev fallback. VPC-config Lambdas run locally and surface a warn line at startup; their containers do NOT get attached to the deployed VPC subnets, so calls to private RDS / ElastiCache will fail.").argument("[target]", "Optional API filter. Accepts the bare CDK logical id ('MyHttpApi'; single-stack apps only), stack-qualified logical id ('MyStack:MyHttpApi'), full CDK Construct path ('MyStack/MyHttpApi/Resource'), or an ancestor Construct path that prefix-matches ('MyStack/MyHttpApi'). When omitted, every discovered API gets its own server. Mirrors `cdkd local invoke` / `cdkd local run-task` target syntax.").addOption(new Option("--port <port>", "HTTP server port (default: auto-allocate)").default("0")).addOption(new Option("--host <host>", "Bind address").default("127.0.0.1")).addOption(new Option("--stack <name>", "Stack to start (single-stack apps auto-detect)")).addOption(new Option("--warm", "Pre-start one container per Lambda at server boot").default(false)).addOption(new Option("--per-lambda-concurrency <n>", "Pool size cap per Lambda (default 2, max 4)").default("2")).addOption(new Option("--no-pull", "Skip docker pull (cached image)")).addOption(new Option("--container-host <host>", "IP the host uses to bind/probe the RIE port (must be a numeric IP — `docker run -p <ip>:<port>:8080` rejects hostnames). Defaults to 127.0.0.1.").default("127.0.0.1")).addOption(new Option("--debug-port-base <port>", "Reserve a contiguous --debug-port range (one per Lambda)")).addOption(new Option("--env-vars <file>", "JSON env-var overrides (SAM-compatible: {\"LogicalId\":{\"KEY\":\"VALUE\"}, \"Parameters\": {...}})")).addOption(new Option("--assume-role <arn-or-pair>", "Assume the Lambda's execution role and forward STS-issued temp creds. Two forms: (1) `--assume-role <arn>` sets a single global default ARN used for every routed Lambda; (2) `--assume-role <LogicalId>=<arn>` (repeatable) is a per-Lambda override (wins over the global default and over --assume-role-auto for the named Lambda). Pair with --assume-role-auto to auto-resolve every other Lambda. Precedence: per-Lambda > (--assume-role-auto OR global default) > unset (developer creds passed through).").argParser((raw, prev) => parseAssumeRoleToken(raw, prev))).addOption(new Option("--assume-role-auto", "Auto-resolve EACH routed Lambda's own execution role per-Lambda (literal ARN / Fn::GetAtt against template / --from-cfn-stack state). Slower boot — one STS call per Lambda — but the right shape when each Lambda's deployed role differs. Mutually exclusive with `--assume-role <arn>` (global default); errors at boot. Compatible with `--assume-role <LogicalId>=<arn>` per-Lambda overrides — the map wins for named Lambdas, auto-resolve handles the rest.").default(false)).addOption(new Option("--watch", "Hot-reload: watch the CDK app source tree and re-synth + re-discover routes on a source edit (cdk.out / node_modules / .git excluded; honors cdk.json watch.include / watch.exclude). Off by default; the server keeps the previous version serving when synth fails mid-reload.").default(false)).addOption(new Option("--stage <name>", "Select an API Gateway Stage by its 'StageName'. Default: the first Stage attached to each API. Drives event.stageVariables for both REST v1 and HTTP API v2. NOTE: For HTTP API v2 routes, requestContext.stage is always '$default' regardless of this flag (AWS-side limitation — HTTP API only exposes one stage to the integration event); only event.stageVariables is affected for v2 routes. For REST v1 routes the selected StageName is also threaded into requestContext.stage.")).addOption(new Option("--api <id>", "DEPRECATED — use the positional <target> argument instead. Same accepted forms (bare logical id, stack-qualified, Construct path, ancestor prefix). Will be removed in a future major release.")).addOption(new Option("--layer-role-arn <arn>", "Role to sts:AssumeRole before calling lambda:GetLayerVersion on every literal-ARN entry in Properties.Layers (issue #448). Use only when the dev credentials cannot read the layer — typically cross-account layers. AWS-published public layers (e.g. Lambda Powertools) are readable from every account and need no role.")).addOption(new Option("--from-state", "Read cdkd S3 state for every routed stack and substitute Ref / Fn::GetAtt / Fn::Sub / Fn::Join (and AWS pseudo parameters) in Lambda env vars with the deployed physical IDs / attributes. Off by default — pre-PR warn-and-drop semantics are preserved. Turn on for stacks already deployed via cdkd deploy. Mirrors `cdkd local invoke --from-state` / `cdkd local run-task --from-state`. Re-runs against fresh state on every hot-reload firing (--watch).").default(false)).addOption(new Option("--from-cfn-stack [cfn-stack-name]", "Read a deployed CloudFormation stack via DescribeStackResources and substitute Ref / Fn::ImportValue in Lambda env vars with the deployed physical IDs / exports. Use for CDK apps deployed via the upstream CDK CLI (`cdk deploy`). Bare form uses the cdkd stack name per routed stack; pass an explicit value when a single CFn stack should serve every routed stack. Mutually exclusive with --from-state. Fn::GetAtt is warn-and-dropped in v1 (CFn DescribeStackResources does not return per-attribute values).")).addOption(new Option("--stack-region <region>", "Region of the state record to read. Used with --from-state when the same stack name has state in multiple regions, and with --from-cfn-stack as the CFn client region (cdkd does not have a separate --cfn-stack-region flag).")).addOption(new Option("--mtls-truststore <path>", "PEM-encoded CA bundle for client-certificate verification (mutual TLS). When set, the local server switches from HTTP to HTTPS and the TLS handshake rejects clients whose certificate doesn't chain to one of these CAs. Verified certs are surfaced on the Lambda event under requestContext.identity.clientCert (REST v1) / requestContext.authentication.clientCert (HTTP API v2). Must be set together with --mtls-cert + --mtls-key; partial flag sets are rejected. Generate a CA + server + client cert for local dev: openssl req -x509 -newkey rsa:2048 -nodes -keyout ca-key.pem -out ca.pem -subj \"/CN=cdkd-local-ca\" -days 365; openssl req -newkey rsa:2048 -nodes -keyout server-key.pem -out server-csr.pem -subj \"/CN=localhost\"; openssl x509 -req -in server-csr.pem -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out server-cert.pem -days 365; openssl req -newkey rsa:2048 -nodes -keyout client-key.pem -out client-csr.pem -subj \"/CN=client\"; openssl x509 -req -in client-csr.pem -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out client-cert.pem -days 365; curl --cacert ca.pem --cert client-cert.pem --key client-key.pem https://localhost:<port>/...")).addOption(new Option("--mtls-cert <path>", "PEM-encoded server certificate for mutual TLS. Self-signed is fine for local dev. Must be set together with --mtls-truststore + --mtls-key.")).addOption(new Option("--mtls-key <path>", "PEM-encoded server private key matching --mtls-cert. Must be set together with --mtls-truststore + --mtls-cert.")).addOption(new Option("--strict-sigv4", "Opt-in: enforce strict AWS_IAM SigV4 verification. When set, requests whose signature cannot be cryptographically verified (foreign access-key-id, OR no local AWS credentials configured) are denied. DEFAULT off — warn-and-pass with a placeholder principalId, matching cdk-local's `cdkl start-api`. Enable this when you want local parity with the deployed API Gateway's signature enforcement.").default(false)).action(withErrorHandling(localStartApiCommand));
48025
48113
  [
48026
48114
  ...commonOptions,
48027
48115
  ...appOptions,
@@ -49153,9 +49241,13 @@ function createLocalRunTaskCommand() {
49153
49241
  * `cdkl start-service` strategy — name one or more ECS services and the engine
49154
49242
  * boots their replicas. There is no front-door listener (services are reached
49155
49243
  * directly via their published container ports). Mirrors `albStrategy` in
49156
- * shape, with `frontDoor` omitted and `lbPortOverrides` empty.
49244
+ * shape, with `frontDoor` omitted and `lbPortOverrides` empty. No-arg to match
49245
+ * cdk-local's bundled `serviceStrategy()` signature exactly — start-service
49246
+ * has no per-invocation options that branch the strategy shape (unlike
49247
+ * `albStrategy(options)`, which threads `--lb-port` parses into
49248
+ * `lbPortOverrides`).
49157
49249
  */
49158
- function serviceStrategy(_options) {
49250
+ function serviceStrategy() {
49159
49251
  return {
49160
49252
  pickEntries: (stacks) => listTargets(stacks).ecsServices,
49161
49253
  pickerMessage: "Select one or more ECS services to run",
@@ -49182,7 +49274,7 @@ function serviceStrategy(_options) {
49182
49274
  */
49183
49275
  function createLocalStartServiceCommand() {
49184
49276
  const cmd = new Command("start-service").description("Run one or more AWS::ECS::Service resources locally as a long-running emulator. Spins up DesiredCount task replicas per service (clamped by --max-tasks) using the same per-task docker network + metadata sidecar pattern as `cdkd local run-task`, then keeps each replica running and restarts it on exit per --restart-policy. ^C tears every replica + sidecar + network down. Each <target> accepts a CDK display path (MyStack/MyService) or stack-qualified logical ID (MyStack:MyServiceXYZ); single-stack apps may omit the stack prefix. When two or more <target>s are supplied, every service is booted into a shared Cloud Map / Service Connect registry so peer services discover each other via docker --add-host overlay (Issue #460). Omit <targets> in an interactive terminal to multi-select the ECS services from a list.").argument("[targets...]", "One or more CDK display paths or stack-qualified logical IDs of the AWS::ECS::Service resources to run (omit to multi-select interactively in a TTY)").addOption(new Option("--from-state", "Read cdkd's S3 state for the target stack and substitute Ref / Fn::GetAtt / Fn::Sub / Fn::ImportValue / Fn::GetStackOutput intrinsics in container images, environment variables, secrets, role ARNs, and volumes. Mutually exclusive with --from-cfn-stack.").default(false)).addOption(new Option("--state-bucket <bucket>", "S3 bucket for --from-state. Falls back to CDKD_STATE_BUCKET env or cdk.json context.cdkd.stateBucket.")).addOption(new Option("--state-prefix <prefix>", "S3 key prefix for --from-state state files.").default("cdkd")).action(withErrorHandling(async (targets, options) => {
49185
- await runEcsServiceEmulator(targets, options, serviceStrategy(options), cdkdExtraStateProviders);
49277
+ await runEcsServiceEmulator(targets, options, serviceStrategy(), cdkdExtraStateProviders);
49186
49278
  }));
49187
49279
  addStartServiceSpecificOptions(cmd);
49188
49280
  return addCommonEcsServiceOptions(cmd);
@@ -49392,7 +49484,7 @@ async function resolveInboundAuthorization(resolved, options) {
49392
49484
  ...authorizer.allowedClients && { allowedClients: authorizer.allowedClients },
49393
49485
  ...authorizer.allowedScopes && { allowedScopes: authorizer.allowedScopes },
49394
49486
  ...authorizer.customClaims && { customClaims: authorizer.customClaims }
49395
- }, header, createJwksCache(), { warned: /* @__PURE__ */ new Set() })).allow) throw new CdkdError(`Inbound JWT rejected by the runtime's customJwtAuthorizer (signature / issuer / expiry / audience check failed against ${authorizer.discoveryUrl}).`, "LOCAL_INVOKE_AGENTCORE_AUTH_DENIED");
49487
+ }, header, createJwksCache(), { warnedAt: /* @__PURE__ */ new Map() })).allow) throw new CdkdError(`Inbound JWT rejected by the runtime's customJwtAuthorizer (signature / issuer / expiry / audience check failed against ${authorizer.discoveryUrl}).`, "LOCAL_INVOKE_AGENTCORE_AUTH_DENIED");
49396
49488
  logger.info(`Inbound JWT verified against ${authorizer.discoveryUrl}.`);
49397
49489
  return header;
49398
49490
  }
@@ -51927,7 +52019,7 @@ function reorderArgs(argv) {
51927
52019
  */
51928
52020
  async function main() {
51929
52021
  const program = new Command();
51930
- program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.203.0");
52022
+ program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.205.0");
51931
52023
  program.addCommand(createBootstrapCommand());
51932
52024
  program.addCommand(createSynthCommand());
51933
52025
  program.addCommand(createListCommand());