cdk-local 0.139.3 → 0.141.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.
@@ -30533,22 +30533,70 @@ function parseOriginOverrides(values) {
30533
30533
  return out;
30534
30534
  }
30535
30535
  /**
30536
- * Parse the repeatable `--kvs-file <kvsLogicalId>=<file.json>` overrides into a
30536
+ * Parse the repeatable `--kvs-file <key>=<file.json>` overrides into a
30537
30537
  * map. Each entry backs a CloudFront Function's `cf.kvs()` reads with a local
30538
30538
  * JSON map instead of the deployed store — the AWS-free escape hatch for KVS,
30539
- * symmetric with `--origin <id>=<dir>`. The key is the
30540
- * `AWS::CloudFront::KeyValueStore` resource logical id (surfaced in the
30541
- * unbound-KVS boot warning when missing).
30539
+ * symmetric with `--origin <id>=<dir>`. The key is a KeyValueStore handle —
30540
+ * its `AWS::CloudFront::KeyValueStore` resource logical id, its construct path,
30541
+ * or its bare construct id — resolved to a logical id by
30542
+ * {@link normalizeKvsFileKeys} once the template is known.
30542
30543
  */
30543
30544
  function parseKvsFileOverrides(values) {
30544
30545
  const out = /* @__PURE__ */ new Map();
30545
30546
  for (const raw of values ?? []) {
30546
30547
  const eq = raw.indexOf("=");
30547
- if (eq <= 0 || eq === raw.length - 1) throw new LocalStartCloudFrontError(`Invalid --kvs-file '${raw}'. Expected <kvsLogicalId>=<file.json> (e.g. MyKvsStore=./kvs.json).`);
30548
+ if (eq <= 0 || eq === raw.length - 1) throw new LocalStartCloudFrontError(`Invalid --kvs-file '${raw}'. Expected <key>=<file.json> (e.g. RoutesKvs=./kvs.json).`);
30548
30549
  out.set(raw.slice(0, eq).trim(), raw.slice(eq + 1).trim());
30549
30550
  }
30550
30551
  return out;
30551
30552
  }
30553
+ /** Index every `AWS::CloudFront::KeyValueStore` in a template for key matching. */
30554
+ function collectKvsStoreIdentities(template) {
30555
+ const out = [];
30556
+ for (const [logicalId, resource] of Object.entries(template.Resources ?? {})) {
30557
+ if (resource.Type !== "AWS::CloudFront::KeyValueStore") continue;
30558
+ const cdkPath = readCdkPath(resource) || void 0;
30559
+ const constructPath = cdkPath?.endsWith("/Resource") ? cdkPath.slice(0, -9) : cdkPath;
30560
+ const constructId = constructPath?.split("/").pop();
30561
+ out.push({
30562
+ logicalId,
30563
+ ...cdkPath !== void 0 && { cdkPath },
30564
+ ...constructPath !== void 0 && { constructPath },
30565
+ ...constructId !== void 0 && constructId !== "" && { constructId }
30566
+ });
30567
+ }
30568
+ return out;
30569
+ }
30570
+ /**
30571
+ * Resolve the `--kvs-file` keys (a logical id, a construct path, or a bare
30572
+ * construct id) to `AWS::CloudFront::KeyValueStore` resource logical ids, so the
30573
+ * binding layer — which matches strictly by logical id — finds them. Mirrors the
30574
+ * construct-path ergonomics the `start-cloudfront` target argument already has,
30575
+ * removing the need to synth + grep the template for the hash-suffixed logical
30576
+ * id (issue #465).
30577
+ *
30578
+ * Match priority per key: exact logical id, then exact construct path (the
30579
+ * `aws:cdk:path` with or without the synthesized `/Resource` leaf), then bare
30580
+ * construct id. A key matching no store, or a bare id matching more than one,
30581
+ * throws a {@link LocalStartCloudFrontError} listing the candidates.
30582
+ */
30583
+ function normalizeKvsFileKeys(kvsFiles, template) {
30584
+ if (kvsFiles.size === 0) return kvsFiles;
30585
+ const stores = collectKvsStoreIdentities(template);
30586
+ const candidates = () => stores.map((s) => s.constructPath ? `${s.logicalId} (${s.constructPath})` : s.logicalId).join(", ") || "(none in this distribution)";
30587
+ const out = /* @__PURE__ */ new Map();
30588
+ for (const [key, filePath] of kvsFiles) {
30589
+ const matched = [
30590
+ stores.filter((s) => s.logicalId === key),
30591
+ stores.filter((s) => s.constructPath === key || s.cdkPath === key),
30592
+ stores.filter((s) => s.constructId === key)
30593
+ ].find((t) => t.length > 0) ?? [];
30594
+ if (matched.length === 0) throw new LocalStartCloudFrontError(`--kvs-file key '${key}' matched no AWS::CloudFront::KeyValueStore in the distribution. Pass a store's logical id, construct path, or bare construct id. Candidates: ${candidates()}.`);
30595
+ if (matched.length > 1) throw new LocalStartCloudFrontError(`--kvs-file key '${key}' is ambiguous — it matched ${matched.length} KeyValueStores (${matched.map((s) => s.logicalId).join(", ")}). Use the logical id or full construct path.`);
30596
+ out.set(matched[0].logicalId, filePath);
30597
+ }
30598
+ return out;
30599
+ }
30552
30600
  /**
30553
30601
  * Resolve + attach the `cf` KeyValueStore module to every KVS-reading
30554
30602
  * CloudFront Function in the distribution (run after each synth, initial +
@@ -30558,8 +30606,9 @@ function parseKvsFileOverrides(values) {
30558
30606
  * runtime then fails the read with the same guidance).
30559
30607
  */
30560
30608
  async function attachKvsModules(distribution, stacks, options, profileCredentials, logger, extraStateProviders) {
30561
- const kvsFiles = parseKvsFileOverrides(options.kvsFile);
30562
- const synthRegion = stacks.find((s) => s.stackName === distribution.stackName)?.region;
30609
+ const stack = stacks.find((s) => s.stackName === distribution.stackName);
30610
+ const synthRegion = stack?.region;
30611
+ const kvsFiles = stack ? normalizeKvsFileKeys(parseKvsFileOverrides(options.kvsFile), stack.template) : parseKvsFileOverrides(options.kvsFile);
30563
30612
  const resolveDeployedKvs = isCfnFlagPresent(options) ? async (kvsLogicalId) => {
30564
30613
  const provider = createLocalStateProvider(options, distribution.stackName, synthRegion, extraStateProviders);
30565
30614
  if (!provider) return void 0;
@@ -30962,7 +31011,7 @@ function createLocalStartCloudFrontCommand(opts = {}) {
30962
31011
  * `cmd`.
30963
31012
  */
30964
31013
  function addStartCloudFrontSpecificOptions(cmd) {
30965
- return cmd.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("--origin <originId=dir>", "Point a distribution origin at a local directory (repeatable). Use when cdk-local cannot resolve the origin's BucketDeployment source automatically (content uploaded out of band, or a non-CDK bucket).").argParser((value, prev) => [...prev ?? [], value])).addOption(new Option("--kvs-file <kvsLogicalId=file.json>", "Back a CloudFront Function's KeyValueStore reads (cf.kvs().get()) with a local JSON map (repeatable). The key is the AWS::CloudFront::KeyValueStore resource logical id; the file is a flat { \"key\": \"value\" } object. The AWS-free alternative to --from-cfn-stack, which instead reads the deployed store via the GetKey API.").argParser((value, prev) => [...prev ?? [], value])).addOption(new Option("--tls", "Terminate real TLS (HTTPS). Uses --tls-cert / --tls-key when supplied, else an auto-generated self-signed cert.").default(false)).addOption(new Option("--tls-cert <path>", "PEM server certificate for --tls (implies --tls).")).addOption(new Option("--tls-key <path>", "PEM server private key for --tls (implies --tls).")).addOption(new Option("--no-pull", "Skip 'docker pull' for a Lambda Function URL origin's base image (use the locally cached image).")).addOption(new Option("--from-cfn-stack [cfn-stack-name]", "Bind to a deployed CloudFormation stack (ListStackResources). Resolves an S3 origin that has no local BucketDeployment source to its deployed bucket and serves it from real S3 on demand (the front/back-split case: files uploaded out of band), and resolves a Lambda Function URL / Lambda@Edge function's env vars to the deployed physical IDs / exports. Use for CDK apps deployed via the upstream CDK CLI (`cdk deploy`). Bare form uses the resolved stack name; pass a value when the CFn stack name differs.")).addOption(new Option("--cache-origin", "For a deployed-S3 origin (served from real S3 under --from-cfn-stack): keep fetched objects in memory for the session instead of re-reading on every request. Faster repeat reads / fewer S3 GETs, but an out-of-band S3 content change is not reflected until a --watch reload (which clears the cache) or a restart. Off by default (every request re-reads, always current). Not CloudFront CDN caching.").default(false)).addOption(new Option("--stack-region <region>", "Region of the state record to read. Used with --from-cfn-stack as the CFn client region.")).addOption(new Option("--assume-role [arn]", "Assume a Lambda Function URL origin's deployed execution role and forward STS-issued temp credentials into its container so the handler runs with the deployed permissions. Three forms: `--assume-role <arn>` (explicit ARN); `--assume-role` (bare, auto-resolves from state — requires --from-cfn-stack); `--no-assume-role` (opt out). Off by default (the dev shell credentials are forwarded).")).addOption(new Option("--watch", "Hot-reload: re-synth + re-resolve the distribution when the CDK app's source changes (honors cdk.json watch.include/exclude; cdk.out, node_modules, .git are always excluded). The server keeps the previous version serving when synth fails mid-reload.").default(false));
31014
+ return cmd.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("--origin <originId=dir>", "Point a distribution origin at a local directory (repeatable). Use when cdk-local cannot resolve the origin's BucketDeployment source automatically (content uploaded out of band, or a non-CDK bucket).").argParser((value, prev) => [...prev ?? [], value])).addOption(new Option("--kvs-file <key=file.json>", "Back a CloudFront Function's KeyValueStore reads (cf.kvs().get()) with a local JSON map (repeatable). The key is a KeyValueStore handle — its AWS::CloudFront::KeyValueStore resource logical id, its construct path, or its bare construct id; the file is a flat { \"key\": \"value\" } object. The AWS-free alternative to --from-cfn-stack, which instead reads the deployed store via the GetKey API.").argParser((value, prev) => [...prev ?? [], value])).addOption(new Option("--tls", "Terminate real TLS (HTTPS). Uses --tls-cert / --tls-key when supplied, else an auto-generated self-signed cert.").default(false)).addOption(new Option("--tls-cert <path>", "PEM server certificate for --tls (implies --tls).")).addOption(new Option("--tls-key <path>", "PEM server private key for --tls (implies --tls).")).addOption(new Option("--no-pull", "Skip 'docker pull' for a Lambda Function URL origin's base image (use the locally cached image).")).addOption(new Option("--from-cfn-stack [cfn-stack-name]", "Bind to a deployed CloudFormation stack (ListStackResources). Resolves an S3 origin that has no local BucketDeployment source to its deployed bucket and serves it from real S3 on demand (the front/back-split case: files uploaded out of band), and resolves a Lambda Function URL / Lambda@Edge function's env vars to the deployed physical IDs / exports. Use for CDK apps deployed via the upstream CDK CLI (`cdk deploy`). Bare form uses the resolved stack name; pass a value when the CFn stack name differs.")).addOption(new Option("--cache-origin", "For a deployed-S3 origin (served from real S3 under --from-cfn-stack): keep fetched objects in memory for the session instead of re-reading on every request. Faster repeat reads / fewer S3 GETs, but an out-of-band S3 content change is not reflected until a --watch reload (which clears the cache) or a restart. Off by default (every request re-reads, always current). Not CloudFront CDN caching.").default(false)).addOption(new Option("--stack-region <region>", "Region of the state record to read. Used with --from-cfn-stack as the CFn client region.")).addOption(new Option("--assume-role [arn]", "Assume a Lambda Function URL origin's deployed execution role and forward STS-issued temp credentials into its container so the handler runs with the deployed permissions. Three forms: `--assume-role <arn>` (explicit ARN); `--assume-role` (bare, auto-resolves from state — requires --from-cfn-stack); `--no-assume-role` (opt out). Off by default (the dev shell credentials are forwarded).")).addOption(new Option("--watch", "Hot-reload: re-synth + re-resolve the distribution when the CDK app's source changes (honors cdk.json watch.include/exclude; cdk.out, node_modules, .git are always excluded). The server keeps the previous version serving when synth fails mid-reload.").default(false));
30966
31015
  }
30967
31016
 
30968
31017
  //#endregion
@@ -31928,172 +31977,6 @@ function createStudioStore(bus, options = {}) {
31928
31977
  };
31929
31978
  }
31930
31979
 
31931
- //#endregion
31932
- //#region src/local/studio-option-catalog.ts
31933
- /**
31934
- * Auto-derived full-flag catalog for `cdkl studio` (issue #301).
31935
- *
31936
- * The per-target {@link OPTION_SPECS} in `studio-option-specs.ts` is a
31937
- * CURATED subset — the handful of flags worth a rich control (a checkbox, a
31938
- * KV editor, an add-row list). But a command like `cdkl start-api` accepts
31939
- * many more flags than studio renders a control for, and hiding them makes
31940
- * the UI strictly less capable than the headless CLI.
31941
- *
31942
- * This module closes that gap by INTROSPECTING each runnable kind's Commander
31943
- * command factory and emitting the complete flag list (name + description).
31944
- * The studio UI serializes it into the page and renders, inside a collapsed
31945
- * "All options" section, (a) the full catalog as a read-only reference and
31946
- * (b) a raw extra-args input (see {@link tokenizeRawArgs}) so any flag the
31947
- * curated controls don't expose can still be passed verbatim. Auto-derivation
31948
- * means the catalog can never drift from the command's real option set.
31949
- *
31950
- * Session-global flags (handled by the studio Session bar / `studio-child-args`)
31951
- * and the auto-added `--help` / `--version` are excluded — passing them per-run
31952
- * would conflict with the session-wide wiring.
31953
- */
31954
- /**
31955
- * Long flag names handled by the session bar / `studio-child-args` (forwarded
31956
- * to every spawned child once, session-wide). Excluded from the per-target
31957
- * catalog so a user does not re-specify them per-run and collide with the
31958
- * session-wide value. Plus the Commander-managed `--help` / `--version`.
31959
- */
31960
- const CATALOG_EXCLUDED_FLAGS = new Set([
31961
- "--app",
31962
- "--profile",
31963
- "--region",
31964
- "--context",
31965
- "--from-cfn-stack",
31966
- "--assume-role",
31967
- "--help",
31968
- "--version"
31969
- ]);
31970
- const KIND_FACTORIES = {
31971
- lambda: {
31972
- command: "invoke",
31973
- factory: createLocalInvokeCommand
31974
- },
31975
- agentcore: {
31976
- command: "invoke-agentcore",
31977
- factory: createLocalInvokeAgentCoreCommand
31978
- },
31979
- api: {
31980
- command: "start-api",
31981
- factory: createLocalStartApiCommand
31982
- },
31983
- alb: {
31984
- command: "start-alb",
31985
- factory: createLocalStartAlbCommand
31986
- },
31987
- ecs: {
31988
- command: "start-service",
31989
- factory: createLocalStartServiceCommand
31990
- },
31991
- "ecs-task": {
31992
- command: "run-task",
31993
- factory: createLocalRunTaskCommand
31994
- },
31995
- cloudfront: {
31996
- command: "start-cloudfront",
31997
- factory: createLocalStartCloudFrontCommand
31998
- },
31999
- "agentcore-ws": {
32000
- command: "start-agentcore",
32001
- factory: createLocalStartAgentCoreCommand
32002
- }
32003
- };
32004
- let cached;
32005
- /**
32006
- * Build (and memoize) the full per-kind flag catalog by introspecting each
32007
- * runnable kind's Commander command factory.
32008
- *
32009
- * Each factory calls `setEmbedConfig(opts.embedConfig)` at construction — with
32010
- * no opts that resets the active embed config to cdk-local defaults, which
32011
- * would wipe a host CLI's branding. So the active config is snapshotted and
32012
- * each factory is re-handed it: branding is preserved AND the derived flag
32013
- * descriptions reflect the host's active branding. The `finally` restore is a
32014
- * belt-and-suspenders against a factory that ignores the passed config.
32015
- * Memoized: the factories are instantiated exactly once per process, not per
32016
- * page render.
32017
- */
32018
- function buildFlagCatalog() {
32019
- if (cached) return cached;
32020
- const savedEmbedConfig = getEmbedConfig();
32021
- try {
32022
- const out = {};
32023
- for (const kind of Object.keys(KIND_FACTORIES)) {
32024
- const { command, factory } = KIND_FACTORIES[kind];
32025
- const cmd = factory({ embedConfig: savedEmbedConfig });
32026
- const flags = [];
32027
- for (const opt of cmd.options) {
32028
- if (opt.hidden) continue;
32029
- if (opt.long && CATALOG_EXCLUDED_FLAGS.has(opt.long)) continue;
32030
- flags.push({
32031
- flags: opt.flags,
32032
- description: opt.description ?? ""
32033
- });
32034
- }
32035
- out[kind] = {
32036
- command,
32037
- flags
32038
- };
32039
- }
32040
- cached = out;
32041
- return out;
32042
- } finally {
32043
- setEmbedConfig(savedEmbedConfig);
32044
- }
32045
- }
32046
- /**
32047
- * Tokenize a raw extra-args string into discrete argv elements, honoring
32048
- * single / double quotes and backslash escaping so values with spaces survive
32049
- * (`--name "two words"` -> `['--name', 'two words']`). studio spawns children
32050
- * WITHOUT a shell (argv array), so the tokens are appended verbatim — there is
32051
- * no shell-injection surface; the child command still validates each arg.
32052
- *
32053
- * Returns `[]` for an empty / whitespace-only input. Throws on an unterminated
32054
- * quote so a malformed raw-args string fails as a clean boundary error rather
32055
- * than spawning a child with a mis-split argv.
32056
- */
32057
- function tokenizeRawArgs(raw) {
32058
- if (raw === void 0) return [];
32059
- const tokens = [];
32060
- let current = "";
32061
- let inToken = false;
32062
- let quote = null;
32063
- for (let i = 0; i < raw.length; i++) {
32064
- const ch = raw[i];
32065
- if (quote) {
32066
- if (ch === "\\" && quote === "\"" && i + 1 < raw.length) current += raw[++i];
32067
- else if (ch === quote) quote = null;
32068
- else current += ch;
32069
- continue;
32070
- }
32071
- if (ch === "\"" || ch === "'") {
32072
- quote = ch;
32073
- inToken = true;
32074
- continue;
32075
- }
32076
- if (ch === "\\" && i + 1 < raw.length) {
32077
- current += raw[++i];
32078
- inToken = true;
32079
- continue;
32080
- }
32081
- if (ch === " " || ch === " " || ch === "\n" || ch === "\r") {
32082
- if (inToken) {
32083
- tokens.push(current);
32084
- current = "";
32085
- inToken = false;
32086
- }
32087
- continue;
32088
- }
32089
- current += ch;
32090
- inToken = true;
32091
- }
32092
- if (quote) throw new Error(`Raw extra args have an unterminated ${quote} quote.`);
32093
- if (inToken) tokens.push(current);
32094
- return tokens;
32095
- }
32096
-
32097
31980
  //#endregion
32098
31981
  //#region src/local/studio-option-specs.ts
32099
31982
  /**
@@ -32397,6 +32280,264 @@ function resolveEnvVars(kind, values) {
32397
32280
  throw new Error(`Option '${spec.flag}' must be KEY/VALUE rows or a JSON object string.`);
32398
32281
  }
32399
32282
 
32283
+ //#endregion
32284
+ //#region src/local/studio-option-catalog.ts
32285
+ /**
32286
+ * Auto-derived full-flag catalog for `cdkl studio` (issue #301).
32287
+ *
32288
+ * The per-target {@link OPTION_SPECS} in `studio-option-specs.ts` is a
32289
+ * CURATED subset — the handful of flags worth a rich control (a checkbox, a
32290
+ * KV editor, an add-row list). But a command like `cdkl start-api` accepts
32291
+ * many more flags than studio renders a control for, and hiding them makes
32292
+ * the UI strictly less capable than the headless CLI.
32293
+ *
32294
+ * This module closes that gap by INTROSPECTING each runnable kind's Commander
32295
+ * command factory and emitting the complete flag list (name + description).
32296
+ * The studio UI serializes it into the page and renders, inside a collapsed
32297
+ * "All options" section, (a) the full catalog as a read-only reference and
32298
+ * (b) a raw extra-args input (see {@link tokenizeRawArgs}) so any flag the
32299
+ * curated controls don't expose can still be passed verbatim. Auto-derivation
32300
+ * means the catalog can never drift from the command's real option set.
32301
+ *
32302
+ * Session-global flags (handled by the studio Session bar / `studio-child-args`)
32303
+ * and the auto-added `--help` / `--version` are excluded — passing them per-run
32304
+ * would conflict with the session-wide wiring.
32305
+ */
32306
+ /**
32307
+ * Long flag names handled by the session bar / `studio-child-args` (forwarded
32308
+ * to every spawned child once, session-wide). Excluded from the per-target
32309
+ * catalog so a user does not re-specify them per-run and collide with the
32310
+ * session-wide value. Plus the Commander-managed `--help` / `--version`.
32311
+ */
32312
+ const CATALOG_EXCLUDED_FLAGS = new Set([
32313
+ "--app",
32314
+ "--profile",
32315
+ "--region",
32316
+ "--context",
32317
+ "--from-cfn-stack",
32318
+ "--assume-role",
32319
+ "--help",
32320
+ "--version"
32321
+ ]);
32322
+ /**
32323
+ * Long flags studio INJECTS or binds itself per run, so the "All options"
32324
+ * section must NOT auto-render an editable control for them (a user-set value
32325
+ * would collide with — or break — studio's own wiring). They still appear in
32326
+ * the catalog (so it stays a complete reference), just `renderable: false`; a
32327
+ * power user can still force one via the raw extra-args input, which is
32328
+ * appended last.
32329
+ *
32330
+ * - `--event` / `--response-file`: studio writes the composed event /
32331
+ * response-capture file and passes these itself (the invoke kinds).
32332
+ * - `--host` / `--port`: the serve-manager binds the listen host/port and the
32333
+ * capture proxy fronts it; a user override would desync the proxy URL.
32334
+ * - `--watch`: the session-global Session-bar toggle (appended by the
32335
+ * serve-manager from the mutable config), not a per-run flag.
32336
+ * - the `--image-*` override family: handled by the dedicated Dockerfile
32337
+ * picker (a partial control here would produce a half-wired override).
32338
+ */
32339
+ const CATALOG_MANAGED_FLAGS = new Set([
32340
+ "--event",
32341
+ "--response-file",
32342
+ "--host",
32343
+ "--port",
32344
+ "--watch",
32345
+ "--image-override",
32346
+ "--image-build-arg",
32347
+ "--image-build-secret",
32348
+ "--image-target",
32349
+ "--no-interactive-overrides",
32350
+ "--strict-overrides"
32351
+ ]);
32352
+ const KIND_FACTORIES = {
32353
+ lambda: {
32354
+ command: "invoke",
32355
+ factory: createLocalInvokeCommand
32356
+ },
32357
+ agentcore: {
32358
+ command: "invoke-agentcore",
32359
+ factory: createLocalInvokeAgentCoreCommand
32360
+ },
32361
+ api: {
32362
+ command: "start-api",
32363
+ factory: createLocalStartApiCommand
32364
+ },
32365
+ alb: {
32366
+ command: "start-alb",
32367
+ factory: createLocalStartAlbCommand
32368
+ },
32369
+ ecs: {
32370
+ command: "start-service",
32371
+ factory: createLocalStartServiceCommand
32372
+ },
32373
+ "ecs-task": {
32374
+ command: "run-task",
32375
+ factory: createLocalRunTaskCommand
32376
+ },
32377
+ cloudfront: {
32378
+ command: "start-cloudfront",
32379
+ factory: createLocalStartCloudFrontCommand
32380
+ },
32381
+ "agentcore-ws": {
32382
+ command: "start-agentcore",
32383
+ factory: createLocalStartAgentCoreCommand
32384
+ }
32385
+ };
32386
+ let cached;
32387
+ /**
32388
+ * Parse the value-token placeholder out of a Commander flags string for a
32389
+ * value-taking option: `-e, --event <file>` -> `file`,
32390
+ * `--platform <platform>` -> `platform`, `--lb-port <listener=host>` ->
32391
+ * `listener=host`. The trailing `...` of a variadic token is dropped. Returns
32392
+ * undefined for a boolean flag (no `<...>` / `[...]` token), so the input
32393
+ * placeholder falls back to a generic hint.
32394
+ */
32395
+ function parseFlagPlaceholder(flags) {
32396
+ const m = /[<[]([^>\]]+)[>\]]/.exec(flags);
32397
+ if (!m || m[1] === void 0) return void 0;
32398
+ return m[1].replace(/\.\.\.$/, "").trim() || void 0;
32399
+ }
32400
+ /**
32401
+ * Build (and memoize) the full per-kind flag catalog by introspecting each
32402
+ * runnable kind's Commander command factory.
32403
+ *
32404
+ * Each factory calls `setEmbedConfig(opts.embedConfig)` at construction — with
32405
+ * no opts that resets the active embed config to cdk-local defaults, which
32406
+ * would wipe a host CLI's branding. So the active config is snapshotted and
32407
+ * each factory is re-handed it: branding is preserved AND the derived flag
32408
+ * descriptions reflect the host's active branding. The `finally` restore is a
32409
+ * belt-and-suspenders against a factory that ignores the passed config.
32410
+ * Memoized: the factories are instantiated exactly once per process, not per
32411
+ * page render.
32412
+ */
32413
+ function buildFlagCatalog() {
32414
+ if (cached) return cached;
32415
+ const savedEmbedConfig = getEmbedConfig();
32416
+ try {
32417
+ const out = {};
32418
+ for (const kind of Object.keys(KIND_FACTORIES)) {
32419
+ const { command, factory } = KIND_FACTORIES[kind];
32420
+ const cmd = factory({ embedConfig: savedEmbedConfig });
32421
+ const curated = new Set((OPTION_SPECS[kind] ?? []).map((s) => s.flag));
32422
+ const flags = [];
32423
+ for (const opt of cmd.options) {
32424
+ if (opt.hidden) continue;
32425
+ if (opt.long && CATALOG_EXCLUDED_FLAGS.has(opt.long)) continue;
32426
+ const long = opt.long ?? "";
32427
+ const takesValue = Boolean(opt.required || opt.optional);
32428
+ const negate = Boolean(opt.negate);
32429
+ const variadic = Boolean(opt.variadic);
32430
+ const choices = Array.isArray(opt.argChoices) ? [...opt.argChoices] : void 0;
32431
+ const placeholder = parseFlagPlaceholder(opt.flags);
32432
+ const renderable = long !== "" && !curated.has(long) && !CATALOG_MANAGED_FLAGS.has(long);
32433
+ const info = {
32434
+ flags: opt.flags,
32435
+ description: opt.description ?? "",
32436
+ long,
32437
+ takesValue,
32438
+ negate,
32439
+ variadic,
32440
+ renderable
32441
+ };
32442
+ if (placeholder !== void 0) info.placeholder = placeholder;
32443
+ if (choices) info.choices = choices;
32444
+ flags.push(info);
32445
+ }
32446
+ out[kind] = {
32447
+ command,
32448
+ flags
32449
+ };
32450
+ }
32451
+ cached = out;
32452
+ return out;
32453
+ } finally {
32454
+ setEmbedConfig(savedEmbedConfig);
32455
+ }
32456
+ }
32457
+ /**
32458
+ * Tokenize a raw extra-args string into discrete argv elements, honoring
32459
+ * single / double quotes and backslash escaping so values with spaces survive
32460
+ * (`--name "two words"` -> `['--name', 'two words']`). studio spawns children
32461
+ * WITHOUT a shell (argv array), so the tokens are appended verbatim — there is
32462
+ * no shell-injection surface; the child command still validates each arg.
32463
+ *
32464
+ * Returns `[]` for an empty / whitespace-only input. Throws on an unterminated
32465
+ * quote so a malformed raw-args string fails as a clean boundary error rather
32466
+ * than spawning a child with a mis-split argv.
32467
+ */
32468
+ function tokenizeRawArgs(raw) {
32469
+ if (raw === void 0) return [];
32470
+ const tokens = [];
32471
+ let current = "";
32472
+ let inToken = false;
32473
+ let quote = null;
32474
+ for (let i = 0; i < raw.length; i++) {
32475
+ const ch = raw[i];
32476
+ if (quote) {
32477
+ if (ch === "\\" && quote === "\"" && i + 1 < raw.length) current += raw[++i];
32478
+ else if (ch === quote) quote = null;
32479
+ else current += ch;
32480
+ continue;
32481
+ }
32482
+ if (ch === "\"" || ch === "'") {
32483
+ quote = ch;
32484
+ inToken = true;
32485
+ continue;
32486
+ }
32487
+ if (ch === "\\" && i + 1 < raw.length) {
32488
+ current += raw[++i];
32489
+ inToken = true;
32490
+ continue;
32491
+ }
32492
+ if (ch === " " || ch === " " || ch === "\n" || ch === "\r") {
32493
+ if (inToken) {
32494
+ tokens.push(current);
32495
+ current = "";
32496
+ inToken = false;
32497
+ }
32498
+ continue;
32499
+ }
32500
+ current += ch;
32501
+ inToken = true;
32502
+ }
32503
+ if (quote) throw new Error(`Raw extra args have an unterminated ${quote} quote.`);
32504
+ if (inToken) tokens.push(current);
32505
+ return tokens;
32506
+ }
32507
+ /**
32508
+ * Build the argv fragment for the auto-rendered "All options" controls from the
32509
+ * UI-posted {@link CatalogValues}, validating each key against the kind's flag
32510
+ * catalog. Emits `--flag` for a checked boolean (bare/negate) flag and
32511
+ * `--flag <value>` for a non-empty value flag. Blank / false values are
32512
+ * omitted.
32513
+ *
32514
+ * Throws (→ a clean 400 at the `/api/run` boundary) on a key that is not a
32515
+ * RENDERABLE catalog flag for the kind — an unknown flag, a session-global /
32516
+ * studio-managed flag, or a curated flag (which belongs to the `options` path,
32517
+ * not here). The studio UI only ever posts renderable flags; the validation
32518
+ * guards a hand-rolled curl body. studio spawns children WITHOUT a shell, so
32519
+ * each emitted token is a discrete argv element with no injection surface.
32520
+ */
32521
+ function buildCatalogArgs(kind, values) {
32522
+ if (values === void 0) return [];
32523
+ const catalog = buildFlagCatalog()[kind];
32524
+ const byFlag = new Map((catalog?.flags ?? []).filter((f) => f.renderable).map((f) => [f.long, f]));
32525
+ const args = [];
32526
+ for (const [flag, value] of Object.entries(values)) {
32527
+ const info = byFlag.get(flag);
32528
+ if (!info) throw new Error(`Unknown / non-overridable option '${flag}' for target kind '${kind}'.`);
32529
+ if (info.takesValue) {
32530
+ if (typeof value !== "string") throw new Error(`Option '${flag}' must be a string value.`);
32531
+ const trimmed = value.trim();
32532
+ if (trimmed !== "") args.push(flag, trimmed);
32533
+ } else {
32534
+ if (typeof value !== "boolean") throw new Error(`Option '${flag}' must be a boolean.`);
32535
+ if (value) args.push(flag);
32536
+ }
32537
+ }
32538
+ return args;
32539
+ }
32540
+
32400
32541
  //#endregion
32401
32542
  //#region src/local/studio-ui.ts
32402
32543
  /**
@@ -32732,10 +32873,18 @@ const STUDIO_CSS = `
32732
32873
  read — kept small as it is secondary. */
32733
32874
  .io-label { color: #d7dde5; font-weight: 500; overflow-wrap: anywhere; min-width: 0; }
32734
32875
  .io-hint { color: #e3b34a; font-size: 11px; }
32735
- .flag-catalog { margin-top: 8px; display: flex; flex-direction: column; gap: 2px; }
32736
- .flag-row { display: flex; gap: 8px; align-items: baseline; font-size: 11px; }
32737
- .flag-name { color: #7bd88f; font-family: ui-monospace, Menlo, monospace; white-space: nowrap; }
32738
- .flag-desc { color: #999; }
32876
+ /* Auto-rendered controls for the residual (non-curated) command flags in the
32877
+ "All options" section: one stacked row per flag (label + input/select +
32878
+ description hint), styled to match the curated .options controls. */
32879
+ .flag-controls { display: flex; flex-direction: column; gap: 6px; margin-bottom: 8px; }
32880
+ .all-options input.flag-control, .all-options select.flag-control {
32881
+ width: 100%; box-sizing: border-box; background: #111; color: #ddd; border: 1px solid #333;
32882
+ border-radius: 3px; padding: 4px 6px; font: 12px ui-monospace, Menlo, monospace;
32883
+ }
32884
+ .all-options input.flag-control:focus, .all-options select.flag-control:focus {
32885
+ outline: none; border-color: #4ec97a;
32886
+ }
32887
+ .all-options .opt-label { color: #9aa4ad; }
32739
32888
  .started-list { display: flex; flex-direction: column; gap: 3px; margin-top: 4px; }
32740
32889
  .started-flag { color: #7bd88f; font-family: ui-monospace, Menlo, monospace; font-size: 11px; white-space: pre-wrap; word-break: break-all; }
32741
32890
  .envkv-modes { display: flex; gap: 0; }
@@ -32829,7 +32978,7 @@ const STUDIO_SCRIPT = `
32829
32978
  // prefill (issue #398): the option map a stopped serve was last Started with
32830
32979
  // (serveApplied.options), so the re-rendered composer comes back filled.
32831
32980
  // rawPrefill is the raw-extra-args string (serveApplied.rawArgs).
32832
- function buildOptions(kind, prefill, rawPrefill) {
32981
+ function buildOptions(kind, prefill, rawPrefill, catalogPrefill) {
32833
32982
  const specs = OPTION_SPECS[kind] || [];
32834
32983
  // The composer always shows an "All options" section (raw extra args + the
32835
32984
  // auto-derived flag reference), even for kinds with no curated controls.
@@ -32980,7 +33129,7 @@ const STUDIO_SCRIPT = `
32980
33129
  }
32981
33130
  sec.appendChild(row);
32982
33131
  });
32983
- const allOpts = buildAllOptions(kind, rawPrefill);
33132
+ const allOpts = buildAllOptions(kind, rawPrefill, catalogPrefill);
32984
33133
  wrap.appendChild(allOpts.node);
32985
33134
  return {
32986
33135
  node: wrap,
@@ -32992,22 +33141,97 @@ const STUDIO_SCRIPT = `
32992
33141
  // body identical to before this section existed.
32993
33142
  return Object.keys(out).length ? out : undefined;
32994
33143
  },
33144
+ collectCatalog: allOpts.collectCatalog,
32995
33145
  collectRaw: allOpts.collectRaw,
32996
33146
  };
32997
33147
  }
32998
33148
 
32999
- // The collapsed "All options" section: a raw extra-args input (appended
33000
- // verbatim to the spawned child) + the auto-derived, read-only catalog of
33001
- // every flag the underlying command accepts. The curated controls above
33002
- // cover the common flags with rich UI; this exposes the rest so the studio
33003
- // UI is never strictly less capable than the headless CLI (issue #301).
33149
+ // The collapsed "All options" section. The curated controls above cover the
33150
+ // common flags with rich UI; this section auto-renders a real control
33151
+ // (checkbox / input / select) for EVERY other flag the underlying command
33152
+ // accepts (the catalog's renderable flags), so the studio UI is never
33153
+ // strictly less capable than the headless CLI AND a user does not have to
33154
+ // hand-type "--flag value" for the long tail of simple flags. A raw
33155
+ // extra-args input remains as the final escape hatch for anything the
33156
+ // auto-render cannot express (issue #301 + the all-options-controls slice).
33004
33157
  const FLAG_CATALOG = window.__FLAG_CATALOG__ || {};
33005
33158
 
33006
- function buildAllOptions(kind, rawPrefill) {
33159
+ function buildAllOptions(kind, rawPrefill, catalogPrefill) {
33007
33160
  const cat = FLAG_CATALOG[kind] || { command: '', flags: [] };
33008
33161
  const det = el('details', 'all-options');
33009
33162
  det.appendChild(el('summary', null, 'All options'));
33010
33163
 
33164
+ // Auto-rendered controls for every renderable (non-curated, non-managed)
33165
+ // flag. The collected value map is keyed by the long flag.
33166
+ const catGetters = [];
33167
+ const renderable = (cat.flags || []).filter(function (f) {
33168
+ return f.renderable;
33169
+ });
33170
+ const preCat = function (flag) {
33171
+ return catalogPrefill ? catalogPrefill[flag] : undefined;
33172
+ };
33173
+ if (renderable.length) {
33174
+ const grid = el('div', 'flag-controls');
33175
+ renderable.forEach(function (f) {
33176
+ const row = el('div', 'opt-row opt-col');
33177
+ if (!f.takesValue) {
33178
+ // Boolean (bare / negate) flag -> checkbox; emits the bare flag.
33179
+ const cb = el('input');
33180
+ cb.type = 'checkbox';
33181
+ if (preCat(f.long) === true) {
33182
+ cb.checked = true;
33183
+ det.open = true;
33184
+ }
33185
+ const lab = el('label', 'opt-bool');
33186
+ lab.appendChild(cb);
33187
+ lab.appendChild(document.createTextNode(' ' + f.long));
33188
+ row.appendChild(lab);
33189
+ catGetters.push(function () {
33190
+ return [f.long, cb.checked];
33191
+ });
33192
+ } else if (f.choices && f.choices.length) {
33193
+ // Value flag with a fixed choice set -> select (blank = unset).
33194
+ row.appendChild(el('span', 'opt-label', f.long));
33195
+ const sel = el('select', 'flag-control');
33196
+ const none = el('option', null, '(default)');
33197
+ none.value = '';
33198
+ sel.appendChild(none);
33199
+ f.choices.forEach(function (c) {
33200
+ const o = el('option', null, c);
33201
+ o.value = c;
33202
+ sel.appendChild(o);
33203
+ });
33204
+ const pv = preCat(f.long);
33205
+ if (pv != null && pv !== '') {
33206
+ sel.value = String(pv);
33207
+ det.open = true;
33208
+ }
33209
+ row.appendChild(sel);
33210
+ catGetters.push(function () {
33211
+ return [f.long, sel.value];
33212
+ });
33213
+ } else {
33214
+ // Value flag -> text input. The placeholder is the flag's own value
33215
+ // token (e.g. file, from the flags' angle-bracket token).
33216
+ row.appendChild(el('span', 'opt-label', f.long));
33217
+ const inp = el('input', 'flag-control');
33218
+ inp.placeholder = f.placeholder || 'value';
33219
+ const pv = preCat(f.long);
33220
+ if (pv != null && pv !== '') {
33221
+ inp.value = String(pv);
33222
+ det.open = true;
33223
+ }
33224
+ row.appendChild(inp);
33225
+ catGetters.push(function () {
33226
+ return [f.long, inp.value];
33227
+ });
33228
+ }
33229
+ if (f.description) row.appendChild(el('div', 'opt-hint', f.description));
33230
+ grid.appendChild(row);
33231
+ });
33232
+ det.appendChild(grid);
33233
+ }
33234
+
33011
33235
  const rawRow = el('div', 'opt-row opt-col');
33012
33236
  rawRow.appendChild(el('span', 'opt-label', 'Raw extra args'));
33013
33237
  const rawIn = el('input', 'raw-args');
@@ -33024,20 +33248,25 @@ const STUDIO_SCRIPT = `
33024
33248
  rawRow.appendChild(el('div', 'opt-hint', hint));
33025
33249
  det.appendChild(rawRow);
33026
33250
 
33027
- if (cat.flags.length) {
33028
- const ref = el('div', 'flag-catalog');
33029
- ref.appendChild(el('div', 'opt-label', 'Available flags'));
33030
- cat.flags.forEach(function (f) {
33031
- const row = el('div', 'flag-row');
33032
- row.appendChild(el('code', 'flag-name', f.flags));
33033
- if (f.description) row.appendChild(el('span', 'flag-desc', f.description));
33034
- ref.appendChild(row);
33035
- });
33036
- det.appendChild(ref);
33037
- }
33038
-
33039
33251
  return {
33040
33252
  node: det,
33253
+ // Collect the auto-rendered control values, omitting unset ones (an
33254
+ // unchecked checkbox / a blank input or select) so the posted map carries
33255
+ // only flags the user actually set. Returns undefined when nothing is set.
33256
+ collectCatalog: function () {
33257
+ const out = {};
33258
+ catGetters.forEach(function (g) {
33259
+ const kv = g();
33260
+ const flag = kv[0];
33261
+ const val = kv[1];
33262
+ if (typeof val === 'boolean') {
33263
+ if (val) out[flag] = true;
33264
+ } else if (typeof val === 'string' && val.trim() !== '') {
33265
+ out[flag] = val.trim();
33266
+ }
33267
+ });
33268
+ return Object.keys(out).length ? out : undefined;
33269
+ },
33041
33270
  collectRaw: function () {
33042
33271
  const v = rawIn.value.trim();
33043
33272
  return v === '' ? undefined : v;
@@ -33456,6 +33685,16 @@ const STUDIO_SCRIPT = `
33456
33685
  }
33457
33686
  });
33458
33687
  }
33688
+ // Auto-rendered "All options" controls (the catalog flags). Surface each
33689
+ // set flag so the running view does not look like the picks vanished (the
33690
+ // issue #356 contract): the bare flag for a checked boolean, flag + value
33691
+ // otherwise.
33692
+ if (applied && applied.catalogArgs) {
33693
+ Object.keys(applied.catalogArgs).forEach(function (flag) {
33694
+ const v = applied.catalogArgs[flag];
33695
+ lines.push(v === true ? flag : flag + ' ' + v);
33696
+ });
33697
+ }
33459
33698
  if (applied && applied.imageOverride) {
33460
33699
  lines.push('--image-override ' + applied.imageOverride);
33461
33700
  }
@@ -33474,7 +33713,7 @@ const STUDIO_SCRIPT = `
33474
33713
  return lines;
33475
33714
  }
33476
33715
 
33477
- async function startServe(id, options, rawArgs, imageOverride, imageOverrides) {
33716
+ async function startServe(id, options, catalogArgs, rawArgs, imageOverride, imageOverrides) {
33478
33717
  // The serve kind (api / alb / ecs) drives which headless command the
33479
33718
  // server spawns; it is recorded on the row when the target list loads.
33480
33719
  const meta = serveMeta.get(id);
@@ -33490,6 +33729,7 @@ const STUDIO_SCRIPT = `
33490
33729
  const watchEl = document.getElementById('sess-watch');
33491
33730
  serveApplied.set(id, {
33492
33731
  options: options,
33732
+ catalogArgs: catalogArgs,
33493
33733
  rawArgs: rawArgs,
33494
33734
  imageOverride: imageOverride,
33495
33735
  imageOverrides: imageOverrides,
@@ -33504,6 +33744,7 @@ const STUDIO_SCRIPT = `
33504
33744
  try {
33505
33745
  const body = { targetId: id, kind };
33506
33746
  if (options) body.options = options;
33747
+ if (catalogArgs) body.catalogArgs = catalogArgs;
33507
33748
  if (rawArgs) body.rawArgs = rawArgs;
33508
33749
  if (imageOverride) body.imageOverride = imageOverride;
33509
33750
  if (imageOverrides) body.imageOverrides = imageOverrides;
@@ -33610,6 +33851,7 @@ const STUDIO_SCRIPT = `
33610
33851
  }
33611
33852
  // Per-run options are only set before a start; collected on the Start click.
33612
33853
  let collectOpts = function () { return undefined; };
33854
+ let collectCatalog = function () { return undefined; };
33613
33855
  let collectRaw = function () { return undefined; };
33614
33856
  let collectImageOverride = function () { return undefined; };
33615
33857
  let collectImageOverrides = function () { return undefined; };
@@ -33623,7 +33865,14 @@ const STUDIO_SCRIPT = `
33623
33865
  serveState.set(id, { status: 'stopped', endpoints: [] });
33624
33866
  renderServeWorkspace(id);
33625
33867
  } else {
33626
- startServe(id, collectOpts(), collectRaw(), collectImageOverride(), collectImageOverrides());
33868
+ startServe(
33869
+ id,
33870
+ collectOpts(),
33871
+ collectCatalog(),
33872
+ collectRaw(),
33873
+ collectImageOverride(),
33874
+ collectImageOverrides()
33875
+ );
33627
33876
  }
33628
33877
  };
33629
33878
  head.appendChild(btn);
@@ -33663,9 +33912,15 @@ const STUDIO_SCRIPT = `
33663
33912
  ws.appendChild(io.node);
33664
33913
  collectImageOverrides = io.collect;
33665
33914
  }
33666
- const opt = buildOptions(kind, applied && applied.options, applied && applied.rawArgs);
33915
+ const opt = buildOptions(
33916
+ kind,
33917
+ applied && applied.options,
33918
+ applied && applied.rawArgs,
33919
+ applied && applied.catalogArgs
33920
+ );
33667
33921
  if (opt.node) ws.appendChild(opt.node);
33668
33922
  collectOpts = opt.collect;
33923
+ collectCatalog = opt.collectCatalog;
33669
33924
  collectRaw = opt.collectRaw;
33670
33925
  }
33671
33926
 
@@ -34284,7 +34539,7 @@ const STUDIO_SCRIPT = `
34284
34539
  // target; per-run options are not carried over, so the options section is
34285
34540
  // omitted (the payload is the thing being tweaked). A fresh invoke keeps
34286
34541
  // the per-run options (e.g. env vars) below the event, above Invoke.
34287
- let opt = { collect: undefined, collectRaw: undefined };
34542
+ let opt = { collect: undefined, collectCatalog: undefined, collectRaw: undefined };
34288
34543
  if (reinvokeOf) {
34289
34544
  composer.appendChild(
34290
34545
  el('div', 'opt-hint', 'Re-invoke runs the edited event through the same target (per-run options use defaults).')
@@ -34312,6 +34567,7 @@ const STUDIO_SCRIPT = `
34312
34567
  msg,
34313
34568
  result,
34314
34569
  collectOpts: opt.collect,
34570
+ collectCatalog: opt.collectCatalog,
34315
34571
  collectRaw: opt.collectRaw,
34316
34572
  reinvokeOf: reinvokeOf || null,
34317
34573
  };
@@ -34350,6 +34606,8 @@ const STUDIO_SCRIPT = `
34350
34606
  body = { targetId: id, kind, event };
34351
34607
  const options = active.collectOpts ? active.collectOpts() : undefined;
34352
34608
  if (options) body.options = options;
34609
+ const catalogArgs = active.collectCatalog ? active.collectCatalog() : undefined;
34610
+ if (catalogArgs) body.catalogArgs = catalogArgs;
34353
34611
  const rawArgs = active.collectRaw ? active.collectRaw() : undefined;
34354
34612
  if (rawArgs) body.rawArgs = rawArgs;
34355
34613
  }
@@ -35544,6 +35802,7 @@ function createStudioDispatcher(config) {
35544
35802
  eventFile,
35545
35803
  ...buildSharedChildArgs(config, { preferAssembly: true }),
35546
35804
  ...buildPerRunArgs(req.kind, req.options),
35805
+ ...buildCatalogArgs(req.kind, req.catalogArgs),
35547
35806
  ...responseFilePath ? ["--response-file", responseFilePath] : []
35548
35807
  ];
35549
35808
  const envVars = resolveEnvVars(req.kind, req.options);
@@ -36210,6 +36469,7 @@ function createStudioServeManager(config) {
36210
36469
  ...spec.portArgs,
36211
36470
  ...buildSharedChildArgs(config, { preferAssembly }),
36212
36471
  ...buildPerRunArgs(req.kind, req.options),
36472
+ ...buildCatalogArgs(req.kind, req.catalogArgs),
36213
36473
  ...envFile ? ["--env-vars", envFile] : [],
36214
36474
  ...req.imageOverride && req.imageOverride.trim() !== "" ? ["--image-override", req.targetId + "=" + req.imageOverride.trim()] : [],
36215
36475
  ...Object.entries(req.imageOverrides ?? {}).flatMap(([svc, df]) => df && df.trim() !== "" ? ["--image-override", svc + "=" + df.trim()] : []),
@@ -36476,7 +36736,7 @@ const STUDIO_TARGET_KINDS = [
36476
36736
  */
36477
36737
  function coerceRunRequest(body) {
36478
36738
  if (typeof body !== "object" || body === null) throw new Error("Request body must be a JSON object.");
36479
- const { targetId, kind, event, options, rawArgs, imageOverride, imageOverrides } = body;
36739
+ const { targetId, kind, event, options, catalogArgs, rawArgs, imageOverride, imageOverrides } = body;
36480
36740
  if (typeof targetId !== "string" || targetId.trim() === "") throw new Error("Request body must include a non-empty \"targetId\" string.");
36481
36741
  if (typeof kind !== "string" || !STUDIO_TARGET_KINDS.includes(kind)) throw new Error(`Request body "kind" must be one of: ${STUDIO_TARGET_KINDS.join(", ")}.`);
36482
36742
  let runOptions;
@@ -36486,6 +36746,12 @@ function coerceRunRequest(body) {
36486
36746
  buildPerRunArgs(kind, runOptions);
36487
36747
  resolveEnvVars(kind, runOptions);
36488
36748
  }
36749
+ let runCatalogArgs;
36750
+ if (catalogArgs !== void 0) {
36751
+ if (typeof catalogArgs !== "object" || catalogArgs === null || Array.isArray(catalogArgs)) throw new Error("Request body \"catalogArgs\" must be a JSON object keyed by flag.");
36752
+ runCatalogArgs = catalogArgs;
36753
+ buildCatalogArgs(kind, runCatalogArgs);
36754
+ }
36489
36755
  let runRawArgs;
36490
36756
  if (rawArgs !== void 0) {
36491
36757
  if (typeof rawArgs !== "string") throw new Error("Request body \"rawArgs\" must be a string.");
@@ -36512,6 +36778,7 @@ function coerceRunRequest(body) {
36512
36778
  kind,
36513
36779
  event,
36514
36780
  ...runOptions !== void 0 ? { options: runOptions } : {},
36781
+ ...runCatalogArgs !== void 0 ? { catalogArgs: runCatalogArgs } : {},
36515
36782
  ...runRawArgs !== void 0 ? { rawArgs: runRawArgs } : {},
36516
36783
  ...runImageOverride !== void 0 ? { imageOverride: runImageOverride } : {},
36517
36784
  ...runImageOverrides !== void 0 ? { imageOverrides: runImageOverrides } : {}
@@ -37317,5 +37584,5 @@ function addStudioSpecificOptions(cmd) {
37317
37584
  }
37318
37585
 
37319
37586
  //#endregion
37320
- export { buildEdgeRequestEvent as $, createJwksCache as $n, resolveSsmParameters as $r, CloudMapRegistry as $t, startAgentCoreHttpServer as A, addInvokeSpecificOptions as An, parseConnectionsPath as Ar, createLocalRunTaskCommand as At, resolveKvsModulesForDistribution as B, materializeLayerFromArn as Bn, substituteAgainstState as Br, resolveSharedSidecarCredentials as Bt, addListSpecificOptions as C, substituteImagePlaceholders as Ci, downloadAndExtractS3Bundle as Cn, VtlEvaluationError as Cr, resolveAlbTarget as Ct, createLocalStartAgentCoreCommand as D, resolveProfileCredentials as Di, renderCodeDockerfile as Dn, ConnectionRegistry as Dr, createLocalStartServiceCommand as Dt, addStartAgentCoreSpecificOptions as E, buildStsClientConfig as Ei, computeCodeImageTag as En, bufferToBody as Er, addStartServiceSpecificOptions as Et, createLocalStartCloudFrontCommand as F, resolveApiTargetSubset as Fn, buildContainerImage as Fr, buildEcsImageResolutionContext$1 as Ft, createS3OriginReader as G, groupRoutesByServer as Gn, createLocalStateProvider as Gr, mergeForService as Gt, resolveDeployedKvsArnByName as H, availableApiIdentifiers as Hn, substituteEnvVarsFromState as Hr, ImageOverrideError as Ht, parseKvsFileOverrides as I, createAuthorizerCache as In, resolveRuntimeCodeMountPath as Ir, ecsClusterOption as It, resolveErrorResponseCandidates as J, resolveSelectionExpression as Jn, resolveCfnFallbackRegion as Jr, runImageOverrideBuilds as Jt, matchBehavior as K, readMtlsMaterialsFromDisk as Kn, isCfnFlagPresent as Kr, parseImageOverrideFlags as Kt, parseOriginOverrides as L, createFileWatcher as Ln, resolveRuntimeFileExtension as Lr, parseMaxTasks as Lt, startAgentCoreWsBridge as M, addStartApiSpecificOptions as Mn, buildDisconnectEvent as Mr, addCommonEcsServiceOptions as Mt, LocalStartCloudFrontError as N, createLocalStartApiCommand as Nn, buildMessageEvent as Nr, addEcsAssumeRoleOptions as Nt, buildAgentCoreServeAuthCheck as O, toCmdArgv as On, buildMgmtEndpointEnvUrl as Or, serviceStrategy as Ot, addStartCloudFrontSpecificOptions as P, createWatchPredicates as Pn, architectureToPlatform as Pr, addImageOverrideOptions as Pt, applyEdgeResponseResult as Q, buildJwksUrlFromIssuer as Qn, collectSsmParameterRefs as Qr, buildCloudMapIndex as Qt, resolveCloudFrontTarget as R, attachStageContext as Rn, resolveRuntimeImage as Rr, parseRestartPolicy as Rt, StudioEventBus as S, formatStateRemedy as Si, waitForAgentCorePing as Sn, tryParseStatus as Sr, parseLbPortOverrides as St, formatTargetListing as T, LocalInvokeBuildError as Ti, buildAgentCoreCodeImage as Tn, probeHostGatewaySupport as Tr, resolveAlbFrontDoor as Tt, resolveDeployedOriginBucket as U, filterRoutesByApiIdentifier as Un, substituteEnvVarsFromStateAsync as Ur, buildImageOverrideTag as Ut, createDeployedKvsDataSource as V, resolveEnvVars$1 as Vn, substituteAgainstStateAsync as Vr, runEcsServiceEmulator as Vt, classifyS3Error as W, filterRoutesByApiIdentifiers as Wn, LocalStateSourceError as Wr, enforceImageOverrideOrphans as Wt, serveLambdaUrlOrigin as X, defaultCredentialsLoader as Xn, resolveCfnStackName as Xr, isLocalCdkAssetImage as Xt, serveFromStaticOrigin as Y, resolveServiceIntegrationParameters as Yn, resolveCfnRegion as Yr, describePinnedImageUri as Yt, applyEdgeRequestResult as Z, buildCognitoJwksUrl as Zn, CfnLocalStateProvider as Zr, listPinnedTargets as Zt, filterStudioTargetGroups as _, AGENTCORE_RUNTIME_TYPE as _i, AGENTCORE_SIGV4_SERVICE as _n, buildHttpApiV2Event as _r, createLocalFileKvsDataSource as _t, createLocalStudioCommand as a, discoverWebSocketApis as ai, addInvokeAgentCoreSpecificOptions as an, evaluateCachedLambdaPolicy as ar, extractKvsAssociations as at, renderStudioHtml as b, resolveAgentCoreTarget as bi, invokeAgentCore as bn, pickResponseTemplate as br, albStrategy as bt, startStudioProxy as c, parseSelectionExpressionPath as ci, invokeAgentCoreWs as cn, attachAuthorizers as cr, pickKvsLogicalIdFromArn as ct, createStudioDispatcher as d, pickRefLogicalId as di, a2aInvokeOnce as dn, buildCorsConfigFromCloudFrontChain as dr, resolveCloudFrontDistribution as dt, resolveWatchConfig as ei, DEFAULT_SHADOW_READY_TIMEOUT_MS as en, verifyCognitoJwt as er, buildEdgeResponseEvent as et, filterStudioCustomResources as f, resolveLambdaArnIntrinsic as fi, MCP_CONTAINER_PORT as fn, isFunctionUrlOacFronted as fr, compileCloudFrontFunction as ft, annotatePinnedEcsTargets as g, AGENTCORE_MCP_PROTOCOL as gi, parseSseForJsonRpc as gn, applyAuthorizerOverlay as gr, createCloudFrontModule as gt, annotateEcsTaskPinnedTargets as h, AGENTCORE_HTTP_PROTOCOL as hi, mcpInvokeOnce as hn, translateLambdaResponse as hr, stripCloudFrontImport as ht, coerceStopRequest as i, availableWebSocketApiIdentifiers as ii, attachContainerLogStreamer as in, computeRequestIdentityHash as ir, describeS3OriginDomain as it, attachAgentCoreWsBridge as j, createLocalInvokeCommand as jn, buildConnectEvent as jr, MAX_TASKS_SUBNET_RANGE_CAP as jt, selectServeInboundAuth as k, classifySourceChange as kn, handleConnectionsRequest as kr, addRunTaskSpecificOptions as kt, relayServeRequest as l, webSocketApiMatchesIdentifier as li, A2A_CONTAINER_PORT as ln, applyCorsResponseHeaders as lr, pickLambdaEdgeFunctionLogicalId as lt, annotateAlbPinnedBackingServices as m, AGENTCORE_AGUI_PROTOCOL as mi, MCP_PROTOCOL_VERSION as mn, matchRoute as mr, runViewerResponse as mt, coerceRunRequest as n, countTargets as ni, setShadowReadyTimeoutMs as nn, verifyJwtViaDiscovery as nr, httpHeadersToEdge as nt, resolveServeBaseUrl as o, discoverWebSocketApisOrThrow as oi, createLocalInvokeAgentCoreCommand as on, invokeRequestAuthorizer as or, isCloudFrontDistribution as ot, isCustomResourceLambdaTarget as p, AGENTCORE_A2A_PROTOCOL as pi, MCP_PATH as pn, matchPreflight as pr, runViewerRequest as pt, startCloudFrontServer as q, startApiServer as qn, rejectExplicitCfnStackWithMultipleStacks as qr, resolveImageOverrides as qt, coerceServeRequest as r, listTargets as ri, getContainerNetworkIp as rn, buildMethodArn as rr, CLOUDFRONT_DISTRIBUTION_TYPE as rt, createStudioServeManager as s, filterWebSocketApisByIdentifiers as si, bridgeAgentCoreWs as sn, invokeTokenAuthorizer as sr, pickFunctionUrlLogicalIdFromOrigin as st, addStudioSpecificOptions as t, resolveSingleTarget as ti, SOFT_RELOAD_COMPLETION_LOG_SUFFIX as tn, verifyJwtAuthorizer as tr, edgeHeadersToHttp as tt, reinvoke as u, discoverRoutes as ui, A2A_PATH as un, buildCorsConfigByApiId as ur, pickTargetFunctionLogicalId as ut, startStudioServer as v, AgentCoreResolutionError as vi, signAgentCoreInvocation as vn, buildRestV1Event as vr, createUnboundCloudFrontModule as vt, createLocalListCommand as w, tryResolveImageFnJoin as wi, SUPPORTED_CODE_RUNTIMES as wn, HOST_GATEWAY_MIN_VERSION as wr, isApplicationLoadBalancer as wt, createStudioStore as x, derivePseudoParametersFromRegion as xi, waitForAgentCoreHttpReady as xn, selectIntegrationResponse as xr, createLocalStartAlbCommand as xt, toStudioTargetGroups as y, pickAgentCoreCandidateStack as yi, AGENTCORE_SESSION_ID_HEADER as yn, evaluateResponseParameters as yr, addAlbSpecificOptions as yt, idFromArn as z, buildStageMap as zn, EcsTaskResolutionError as zr, resolveEcsAssumeRoleOption as zt };
37321
- //# sourceMappingURL=local-studio-BvuYWUHm.js.map
37587
+ export { applyEdgeResponseResult as $, buildJwksUrlFromIssuer as $n, collectSsmParameterRefs as $r, buildCloudMapIndex as $t, startAgentCoreHttpServer as A, classifySourceChange as An, handleConnectionsRequest as Ar, addRunTaskSpecificOptions as At, idFromArn as B, buildStageMap as Bn, EcsTaskResolutionError as Br, resolveEcsAssumeRoleOption as Bt, addListSpecificOptions as C, formatStateRemedy as Ci, waitForAgentCorePing as Cn, tryParseStatus as Cr, parseLbPortOverrides as Ct, createLocalStartAgentCoreCommand as D, buildStsClientConfig as Di, computeCodeImageTag as Dn, bufferToBody as Dr, addStartServiceSpecificOptions as Dt, addStartAgentCoreSpecificOptions as E, LocalInvokeBuildError as Ei, buildAgentCoreCodeImage as En, probeHostGatewaySupport as Er, resolveAlbFrontDoor as Et, createLocalStartCloudFrontCommand as F, createWatchPredicates as Fn, architectureToPlatform as Fr, addImageOverrideOptions as Ft, classifyS3Error as G, filterRoutesByApiIdentifiers as Gn, LocalStateSourceError as Gr, enforceImageOverrideOrphans as Gt, createDeployedKvsDataSource as H, resolveEnvVars$1 as Hn, substituteAgainstStateAsync as Hr, runEcsServiceEmulator as Ht, normalizeKvsFileKeys as I, resolveApiTargetSubset as In, buildContainerImage as Ir, buildEcsImageResolutionContext$1 as It, startCloudFrontServer as J, startApiServer as Jn, rejectExplicitCfnStackWithMultipleStacks as Jr, resolveImageOverrides as Jt, createS3OriginReader as K, groupRoutesByServer as Kn, createLocalStateProvider as Kr, mergeForService as Kt, parseKvsFileOverrides as L, createAuthorizerCache as Ln, resolveRuntimeCodeMountPath as Lr, ecsClusterOption as Lt, startAgentCoreWsBridge as M, createLocalInvokeCommand as Mn, buildConnectEvent as Mr, MAX_TASKS_SUBNET_RANGE_CAP as Mt, LocalStartCloudFrontError as N, addStartApiSpecificOptions as Nn, buildDisconnectEvent as Nr, addCommonEcsServiceOptions as Nt, buildAgentCoreServeAuthCheck as O, resolveProfileCredentials as Oi, renderCodeDockerfile as On, ConnectionRegistry as Or, createLocalStartServiceCommand as Ot, addStartCloudFrontSpecificOptions as P, createLocalStartApiCommand as Pn, buildMessageEvent as Pr, addEcsAssumeRoleOptions as Pt, applyEdgeRequestResult as Q, buildCognitoJwksUrl as Qn, CfnLocalStateProvider as Qr, listPinnedTargets as Qt, parseOriginOverrides as R, createFileWatcher as Rn, resolveRuntimeFileExtension as Rr, parseMaxTasks as Rt, StudioEventBus as S, derivePseudoParametersFromRegion as Si, waitForAgentCoreHttpReady as Sn, selectIntegrationResponse as Sr, createLocalStartAlbCommand as St, formatTargetListing as T, tryResolveImageFnJoin as Ti, SUPPORTED_CODE_RUNTIMES as Tn, HOST_GATEWAY_MIN_VERSION as Tr, isApplicationLoadBalancer as Tt, resolveDeployedKvsArnByName as U, availableApiIdentifiers as Un, substituteEnvVarsFromState as Ur, ImageOverrideError as Ut, resolveKvsModulesForDistribution as V, materializeLayerFromArn as Vn, substituteAgainstState as Vr, resolveSharedSidecarCredentials as Vt, resolveDeployedOriginBucket as W, filterRoutesByApiIdentifier as Wn, substituteEnvVarsFromStateAsync as Wr, buildImageOverrideTag as Wt, serveFromStaticOrigin as X, resolveServiceIntegrationParameters as Xn, resolveCfnRegion as Xr, describePinnedImageUri as Xt, resolveErrorResponseCandidates as Y, resolveSelectionExpression as Yn, resolveCfnFallbackRegion as Yr, runImageOverrideBuilds as Yt, serveLambdaUrlOrigin as Z, defaultCredentialsLoader as Zn, resolveCfnStackName as Zr, isLocalCdkAssetImage as Zt, filterStudioTargetGroups as _, AGENTCORE_MCP_PROTOCOL as _i, parseSseForJsonRpc as _n, applyAuthorizerOverlay as _r, createCloudFrontModule as _t, createLocalStudioCommand as a, availableWebSocketApiIdentifiers as ai, attachContainerLogStreamer as an, computeRequestIdentityHash as ar, describeS3OriginDomain as at, renderStudioHtml as b, pickAgentCoreCandidateStack as bi, AGENTCORE_SESSION_ID_HEADER as bn, evaluateResponseParameters as br, addAlbSpecificOptions as bt, startStudioProxy as c, filterWebSocketApisByIdentifiers as ci, bridgeAgentCoreWs as cn, invokeTokenAuthorizer as cr, pickFunctionUrlLogicalIdFromOrigin as ct, createStudioDispatcher as d, discoverRoutes as di, A2A_PATH as dn, buildCorsConfigByApiId as dr, pickTargetFunctionLogicalId as dt, resolveSsmParameters as ei, CloudMapRegistry as en, createJwksCache as er, buildEdgeRequestEvent as et, filterStudioCustomResources as f, pickRefLogicalId as fi, a2aInvokeOnce as fn, buildCorsConfigFromCloudFrontChain as fr, resolveCloudFrontDistribution as ft, annotatePinnedEcsTargets as g, AGENTCORE_HTTP_PROTOCOL as gi, mcpInvokeOnce as gn, translateLambdaResponse as gr, stripCloudFrontImport as gt, annotateEcsTaskPinnedTargets as h, AGENTCORE_AGUI_PROTOCOL as hi, MCP_PROTOCOL_VERSION as hn, matchRoute as hr, runViewerResponse as ht, coerceStopRequest as i, listTargets as ii, getContainerNetworkIp as in, buildMethodArn as ir, CLOUDFRONT_DISTRIBUTION_TYPE as it, attachAgentCoreWsBridge as j, addInvokeSpecificOptions as jn, parseConnectionsPath as jr, createLocalRunTaskCommand as jt, selectServeInboundAuth as k, toCmdArgv as kn, buildMgmtEndpointEnvUrl as kr, serviceStrategy as kt, relayServeRequest as l, parseSelectionExpressionPath as li, invokeAgentCoreWs as ln, attachAuthorizers as lr, pickKvsLogicalIdFromArn as lt, annotateAlbPinnedBackingServices as m, AGENTCORE_A2A_PROTOCOL as mi, MCP_PATH as mn, matchPreflight as mr, runViewerRequest as mt, coerceRunRequest as n, resolveSingleTarget as ni, SOFT_RELOAD_COMPLETION_LOG_SUFFIX as nn, verifyJwtAuthorizer as nr, edgeHeadersToHttp as nt, resolveServeBaseUrl as o, discoverWebSocketApis as oi, addInvokeAgentCoreSpecificOptions as on, evaluateCachedLambdaPolicy as or, extractKvsAssociations as ot, isCustomResourceLambdaTarget as p, resolveLambdaArnIntrinsic as pi, MCP_CONTAINER_PORT as pn, isFunctionUrlOacFronted as pr, compileCloudFrontFunction as pt, matchBehavior as q, readMtlsMaterialsFromDisk as qn, isCfnFlagPresent as qr, parseImageOverrideFlags as qt, coerceServeRequest as r, countTargets as ri, setShadowReadyTimeoutMs as rn, verifyJwtViaDiscovery as rr, httpHeadersToEdge as rt, createStudioServeManager as s, discoverWebSocketApisOrThrow as si, createLocalInvokeAgentCoreCommand as sn, invokeRequestAuthorizer as sr, isCloudFrontDistribution as st, addStudioSpecificOptions as t, resolveWatchConfig as ti, DEFAULT_SHADOW_READY_TIMEOUT_MS as tn, verifyCognitoJwt as tr, buildEdgeResponseEvent as tt, reinvoke as u, webSocketApiMatchesIdentifier as ui, A2A_CONTAINER_PORT as un, applyCorsResponseHeaders as ur, pickLambdaEdgeFunctionLogicalId as ut, startStudioServer as v, AGENTCORE_RUNTIME_TYPE as vi, AGENTCORE_SIGV4_SERVICE as vn, buildHttpApiV2Event as vr, createLocalFileKvsDataSource as vt, createLocalListCommand as w, substituteImagePlaceholders as wi, downloadAndExtractS3Bundle as wn, VtlEvaluationError as wr, resolveAlbTarget as wt, createStudioStore as x, resolveAgentCoreTarget as xi, invokeAgentCore as xn, pickResponseTemplate as xr, albStrategy as xt, toStudioTargetGroups as y, AgentCoreResolutionError as yi, signAgentCoreInvocation as yn, buildRestV1Event as yr, createUnboundCloudFrontModule as yt, resolveCloudFrontTarget as z, attachStageContext as zn, resolveRuntimeImage as zr, parseRestartPolicy as zt };
37588
+ //# sourceMappingURL=local-studio-fdztI6b8.js.map