cdk-local 0.84.0 → 0.86.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.
@@ -14705,7 +14705,7 @@ function availableApiIdentifiers(routes) {
14705
14705
  * @param overrides Parsed `--env-vars` file contents, or
14706
14706
  * `undefined` when the flag was not passed.
14707
14707
  */
14708
- function resolveEnvVars(logicalId, displayPath, templateEnv, overrides) {
14708
+ function resolveEnvVars$1(logicalId, displayPath, templateEnv, overrides) {
14709
14709
  const resolved = {};
14710
14710
  const unresolved = [];
14711
14711
  if (templateEnv) for (const [key, value] of Object.entries(templateEnv)) if (isLiteralEnvValue(value)) resolved[key] = String(value);
@@ -16196,7 +16196,7 @@ async function buildContainerSpec(args) {
16196
16196
  for (const { key, reason } of unresolved) getLogger().warn(`Lambda ${logicalId}: state source could not substitute env var ${key} (${reason}). Override it via --env-vars or it will be dropped.`);
16197
16197
  }
16198
16198
  const lambdaCdkPath = readCdkPathOrUndefined(lambda.resource);
16199
- const envResult = resolveEnvVars(logicalId, lambdaCdkPath, templateEnv, overrides);
16199
+ const envResult = resolveEnvVars$1(logicalId, lambdaCdkPath, templateEnv, overrides);
16200
16200
  for (const key of envResult.unresolved) {
16201
16201
  if (stateAudit && stateAudit.unresolved.some((u) => u.key === key)) continue;
16202
16202
  const overrideKeyExample = lambdaCdkPath?.replace(/\/Resource$/, "") ?? logicalId;
@@ -17209,7 +17209,7 @@ async function localInvokeCommand(target, options, extraStateProviders) {
17209
17209
  }
17210
17210
  const overrides = readEnvOverridesFile$3(options.envVars);
17211
17211
  const lambdaCdkPath = readCdkPathOrUndefined(lambda.resource);
17212
- const envResult = resolveEnvVars(lambda.logicalId, lambdaCdkPath, templateEnv, overrides);
17212
+ const envResult = resolveEnvVars$1(lambda.logicalId, lambdaCdkPath, templateEnv, overrides);
17213
17213
  for (const key of envResult.unresolved) {
17214
17214
  if (stateAudit && stateAudit.unresolved.some((u) => u.key === key)) continue;
17215
17215
  const overrideKeyExample = lambdaCdkPath?.replace(/\/Resource$/, "") ?? lambda.logicalId;
@@ -19187,7 +19187,7 @@ async function buildContainerEnv(resolved, options, profileCredentials, profileC
19187
19187
  }
19188
19188
  const overrides = readEnvOverridesFile$2(options.envVars);
19189
19189
  const cdkPath = readCdkPathOrUndefined(resolved.resource);
19190
- const envResult = resolveEnvVars(resolved.logicalId, cdkPath, templateEnv, overrides);
19190
+ const envResult = resolveEnvVars$1(resolved.logicalId, cdkPath, templateEnv, overrides);
19191
19191
  for (const key of envResult.unresolved) {
19192
19192
  const overrideKeyExample = cdkPath?.replace(/\/Resource$/, "") ?? resolved.logicalId;
19193
19193
  logger.warn(`Environment variable ${key} contains a CloudFormation intrinsic and was dropped. Override it with --env-vars (e.g. {"${overrideKeyExample}":{"${key}":"<literal>"}}), or pass a state-source flag (e.g. --from-cfn-stack) to recover deployed values.`);
@@ -27971,6 +27971,174 @@ function createStudioStore(bus, options = {}) {
27971
27971
  };
27972
27972
  }
27973
27973
 
27974
+ //#endregion
27975
+ //#region src/local/studio-option-specs.ts
27976
+ /**
27977
+ * The per-kind option table. Kinds absent here (or with an empty list) have
27978
+ * no per-run options — the UI shows just the primary control (the event
27979
+ * composer for `lambda`, a bare Start for a serve).
27980
+ */
27981
+ const OPTION_SPECS = {
27982
+ lambda: [{
27983
+ flag: "--env-vars",
27984
+ kind: "env-kv",
27985
+ label: "Env vars",
27986
+ sep: "=",
27987
+ leftPlaceholder: "KEY",
27988
+ rightPlaceholder: "value",
27989
+ help: "Overlay container env vars — add KEY=VALUE rows or paste a JSON object."
27990
+ }],
27991
+ alb: [
27992
+ {
27993
+ flag: "--tls",
27994
+ kind: "boolean",
27995
+ label: "TLS (terminate HTTPS locally)"
27996
+ },
27997
+ {
27998
+ flag: "--tls-cert",
27999
+ kind: "scalar",
28000
+ label: "TLS cert",
28001
+ placeholder: "./cert.pem",
28002
+ showWhen: "--tls"
28003
+ },
28004
+ {
28005
+ flag: "--tls-key",
28006
+ kind: "scalar",
28007
+ label: "TLS key",
28008
+ placeholder: "./key.pem",
28009
+ showWhen: "--tls"
28010
+ },
28011
+ {
28012
+ flag: "--lb-port",
28013
+ kind: "repeat-pair",
28014
+ label: "Listener port remap",
28015
+ sep: "=",
28016
+ leftPlaceholder: "listenerPort",
28017
+ rightPlaceholder: "hostPort"
28018
+ },
28019
+ {
28020
+ flag: "--bearer-token",
28021
+ kind: "scalar",
28022
+ label: "Bearer token",
28023
+ placeholder: "eyJ...",
28024
+ help: "Default JWT injected for authenticate-cognito / authenticate-oidc actions."
28025
+ },
28026
+ {
28027
+ flag: "--no-verify-auth",
28028
+ kind: "boolean",
28029
+ label: "Disable auth guard"
28030
+ }
28031
+ ],
28032
+ ecs: [{
28033
+ flag: "--max-tasks",
28034
+ kind: "scalar",
28035
+ label: "Max replicas",
28036
+ placeholder: "1",
28037
+ inputType: "number"
28038
+ }, {
28039
+ flag: "--host-port",
28040
+ kind: "repeat-pair",
28041
+ label: "Container port publish",
28042
+ sep: "=",
28043
+ leftPlaceholder: "containerPort",
28044
+ rightPlaceholder: "hostPort"
28045
+ }]
28046
+ };
28047
+ /** True when `value` is a non-empty trimmed string. */
28048
+ function nonEmptyStr(value) {
28049
+ return typeof value === "string" && value.trim() !== "";
28050
+ }
28051
+ /**
28052
+ * Build the per-run argv fragment for a target `kind` from the UI-posted
28053
+ * option `values`, validating each value against {@link OPTION_SPECS}.
28054
+ *
28055
+ * Throws on an unknown flag (a value keyed to an option the kind does not
28056
+ * declare) or a type mismatch (e.g. a string where a boolean is expected) so
28057
+ * a malformed UI / curl body fails loudly rather than spawning a child with
28058
+ * a bogus arg. A `scalar` with `showWhen` is dropped unless its gate boolean
28059
+ * is on. Empty / blank values are omitted.
28060
+ */
28061
+ function buildPerRunArgs(kind, values) {
28062
+ if (values === void 0) return [];
28063
+ const specs = OPTION_SPECS[kind] ?? [];
28064
+ const byFlag = new Map(specs.map((s) => [s.flag, s]));
28065
+ for (const flag of Object.keys(values)) if (!byFlag.has(flag)) throw new Error(`Unknown option '${flag}' for target kind '${kind}'.`);
28066
+ const args = [];
28067
+ for (const spec of specs) {
28068
+ const value = values[spec.flag];
28069
+ if (value === void 0) continue;
28070
+ if (spec.kind === "boolean") {
28071
+ if (typeof value !== "boolean") throw new Error(`Option '${spec.flag}' must be a boolean.`);
28072
+ if (value) args.push(spec.flag);
28073
+ continue;
28074
+ }
28075
+ if (spec.kind === "scalar") {
28076
+ if (value !== "" && typeof value !== "string") throw new Error(`Option '${spec.flag}' must be a string.`);
28077
+ if (spec.showWhen && values[spec.showWhen] !== true) continue;
28078
+ if (nonEmptyStr(value)) args.push(spec.flag, value.trim());
28079
+ continue;
28080
+ }
28081
+ if (spec.kind === "env-kv") {
28082
+ if (!Array.isArray(value) && typeof value !== "string") throw new Error(`Option '${spec.flag}' must be KEY/VALUE rows or a JSON string.`);
28083
+ continue;
28084
+ }
28085
+ if (!Array.isArray(value)) throw new Error(`Option '${spec.flag}' must be an array of { left, right } rows.`);
28086
+ for (const row of value) {
28087
+ if (typeof row !== "object" || row === null) throw new Error(`Option '${spec.flag}' rows must be objects.`);
28088
+ const { left, right } = row;
28089
+ if (!nonEmptyStr(left) && !nonEmptyStr(right)) continue;
28090
+ if (!nonEmptyStr(left) || !nonEmptyStr(right)) throw new Error(`Option '${spec.flag}' rows need both sides (got '${left}${spec.sep}${right}').`);
28091
+ args.push(spec.flag, `${left.trim()}${spec.sep}${right.trim()}`);
28092
+ }
28093
+ }
28094
+ return args;
28095
+ }
28096
+ /**
28097
+ * Materialize the `env-kv` option for a target `kind` into the SAM-shape
28098
+ * object `--env-vars` expects (one level of nesting). The caller writes the
28099
+ * returned object to a temp JSON file and passes `--env-vars <file>` to the
28100
+ * child. Returns `undefined` when there is no env-kv option / no values.
28101
+ *
28102
+ * Two input forms (both produced by the UI's KV / JSON toggle):
28103
+ * - **KV rows** (`PairValue[]`) -> a flat `{KEY: value}` map wrapped as
28104
+ * `{ Parameters: {...} }` (the SAM global scope — applies to the target).
28105
+ * - **JSON string** -> parsed; a full SAM-shape object (a `Parameters` key or
28106
+ * any nested-object value) is used as-is, while a flat `{KEY: "value"}`
28107
+ * object is wrapped in `{ Parameters: {...} }`.
28108
+ *
28109
+ * Throws on malformed JSON / a non-object so a bad value fails as a clean
28110
+ * boundary error rather than writing garbage to the temp file.
28111
+ */
28112
+ function resolveEnvVars(kind, values) {
28113
+ if (values === void 0) return void 0;
28114
+ const spec = (OPTION_SPECS[kind] ?? []).find((s) => s.kind === "env-kv");
28115
+ if (!spec) return void 0;
28116
+ const value = values[spec.flag];
28117
+ if (value === void 0) return void 0;
28118
+ if (Array.isArray(value)) {
28119
+ const flat = {};
28120
+ for (const row of value) {
28121
+ const { left, right } = row ?? {};
28122
+ if (nonEmptyStr(left)) flat[left.trim()] = typeof right === "string" ? right : "";
28123
+ }
28124
+ return Object.keys(flat).length > 0 ? { Parameters: flat } : void 0;
28125
+ }
28126
+ if (typeof value === "string") {
28127
+ const text = value.trim();
28128
+ if (text === "") return void 0;
28129
+ let parsed;
28130
+ try {
28131
+ parsed = JSON.parse(text);
28132
+ } catch {
28133
+ throw new Error("Env vars JSON is not valid JSON.");
28134
+ }
28135
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) throw new Error("Env vars JSON must be a JSON object.");
28136
+ const obj = parsed;
28137
+ return "Parameters" in obj || Object.values(obj).some((v) => typeof v === "object" && v !== null) ? obj : { Parameters: obj };
28138
+ }
28139
+ throw new Error(`Option '${spec.flag}' must be KEY/VALUE rows or a JSON object string.`);
28140
+ }
28141
+
27974
28142
  //#endregion
27975
28143
  //#region src/local/studio-ui.ts
27976
28144
  /**
@@ -28093,6 +28261,35 @@ const STUDIO_CSS = `
28093
28261
  #conn { font-size: 11px; }
28094
28262
  #conn.up { color: #7bd88f; }
28095
28263
  #conn.down { color: #e0707a; }
28264
+ .options .opt-row { display: flex; align-items: center; gap: 8px; margin: 6px 0; }
28265
+ .options .opt-row.opt-col { display: flex; flex-direction: column; align-items: stretch; gap: 4px; }
28266
+ .opt-label { color: #aaa; font-size: 12px; min-width: 120px; }
28267
+ .opt-bool { color: #ddd; font-size: 12px; display: inline-flex; align-items: center; gap: 4px; cursor: pointer; }
28268
+ .options input[type=text], .options input[type=number] {
28269
+ flex: 1; background: #111; color: #ddd; border: 1px solid #333; border-radius: 3px;
28270
+ padding: 4px 6px; font: 12px ui-monospace, Menlo, monospace; min-width: 0;
28271
+ }
28272
+ .options input:focus, .envkv-ta:focus { outline: none; border-color: #4ec97a; }
28273
+ .pair-wrap { display: flex; flex-direction: column; gap: 4px; flex: 1; }
28274
+ .pair-row { display: flex; align-items: center; gap: 6px; }
28275
+ .pair-in { width: 1px; flex: 1; background: #111; color: #ddd; border: 1px solid #333;
28276
+ border-radius: 3px; padding: 4px 6px; font: 12px ui-monospace, Menlo, monospace; min-width: 0; }
28277
+ .pair-sep { color: #888; }
28278
+ .pair-x { background: #2a2a2a; color: #bbb; border: none; border-radius: 3px; cursor: pointer;
28279
+ padding: 2px 7px; font: 12px ui-monospace, monospace; }
28280
+ .pair-x:hover { background: #3a2a2a; color: #e0707a; }
28281
+ .pair-add { align-self: flex-start; background: #1d1d1d; color: #7bd88f; border: 1px solid #2f4030;
28282
+ border-radius: 3px; cursor: pointer; padding: 3px 9px; font: 12px ui-monospace, monospace; }
28283
+ .pair-add:hover { background: #243024; }
28284
+ .envkv-modes { display: flex; gap: 0; }
28285
+ .envkv-mode { background: #1a1a1a; color: #999; border: 1px solid #333; cursor: pointer;
28286
+ padding: 3px 12px; font: 11px ui-monospace, monospace; }
28287
+ .envkv-mode:first-child { border-radius: 3px 0 0 3px; }
28288
+ .envkv-mode:last-child { border-radius: 0 3px 3px 0; border-left: none; }
28289
+ .envkv-mode.active { background: #2a3a2c; color: #7bd88f; }
28290
+ .envkv-ta { width: 100%; box-sizing: border-box; min-height: 70px; resize: vertical;
28291
+ background: #111; color: #ddd; border: 1px solid #333; border-radius: 3px; padding: 6px 8px;
28292
+ font: 12px ui-monospace, Menlo, monospace; }
28096
28293
  `;
28097
28294
  const STUDIO_SCRIPT = `
28098
28295
  const KIND_LABEL = { lambda: 'Lambda', api: 'API', alb: 'ALB', ecs: 'ECS', agentcore: 'AgentCore' };
@@ -28115,6 +28312,142 @@ const STUDIO_SCRIPT = `
28115
28312
  return e;
28116
28313
  }
28117
28314
 
28315
+ // Per-target run options (issue #301 slice 2). The descriptor table is
28316
+ // serialized into the page by the server; we render a control per option
28317
+ // and return { node, collect } so the caller places the section and reads
28318
+ // the values when the user clicks Invoke / Start.
28319
+ const OPTION_SPECS = window.__OPTION_SPECS__ || {};
28320
+
28321
+ function buildOptions(kind) {
28322
+ const specs = OPTION_SPECS[kind] || [];
28323
+ if (!specs.length) return { node: null, collect: function () { return undefined; } };
28324
+ const sec = el('div', 'section options');
28325
+ sec.appendChild(el('h3', null, 'Options'));
28326
+ const getters = [];
28327
+ const bools = {};
28328
+
28329
+ // Shared add-row pair list (used by repeat-pair AND the env-kv KV pane).
28330
+ function pairList(spec) {
28331
+ const list = el('div', 'pair-rows');
28332
+ const pairs = [];
28333
+ const addRow = function () {
28334
+ const r = el('div', 'pair-row');
28335
+ const lv = el('input');
28336
+ lv.placeholder = spec.leftPlaceholder;
28337
+ lv.className = 'pair-in';
28338
+ const rv = el('input');
28339
+ rv.placeholder = spec.rightPlaceholder;
28340
+ rv.className = 'pair-in';
28341
+ const pair = { l: lv, r: rv };
28342
+ const x = el('button', 'pair-x', 'x');
28343
+ x.type = 'button';
28344
+ x.onclick = function () {
28345
+ list.removeChild(r);
28346
+ const i = pairs.indexOf(pair);
28347
+ if (i >= 0) pairs.splice(i, 1);
28348
+ };
28349
+ r.appendChild(lv);
28350
+ r.appendChild(el('span', 'pair-sep', spec.sep));
28351
+ r.appendChild(rv);
28352
+ r.appendChild(x);
28353
+ list.appendChild(r);
28354
+ pairs.push(pair);
28355
+ };
28356
+ const add = el('button', 'pair-add', '+ add');
28357
+ add.type = 'button';
28358
+ add.onclick = addRow;
28359
+ const wrap = el('div', 'pair-wrap');
28360
+ wrap.appendChild(list);
28361
+ wrap.appendChild(add);
28362
+ return {
28363
+ node: wrap,
28364
+ rows: function () {
28365
+ return pairs.map(function (p) { return { left: p.l.value, right: p.r.value }; });
28366
+ },
28367
+ };
28368
+ }
28369
+
28370
+ specs.forEach(function (spec) {
28371
+ const row = el('div', 'opt-row');
28372
+ if (spec.kind === 'boolean') {
28373
+ const cb = el('input');
28374
+ cb.type = 'checkbox';
28375
+ const lab = el('label', 'opt-bool');
28376
+ lab.appendChild(cb);
28377
+ lab.appendChild(document.createTextNode(' ' + spec.label));
28378
+ row.appendChild(lab);
28379
+ bools[spec.flag] = cb;
28380
+ getters.push(function () { return [spec.flag, cb.checked]; });
28381
+ } else if (spec.kind === 'scalar') {
28382
+ row.appendChild(el('span', 'opt-label', spec.label));
28383
+ const inp = el('input');
28384
+ inp.type = spec.inputType === 'number' ? 'number' : 'text';
28385
+ if (spec.placeholder) inp.placeholder = spec.placeholder;
28386
+ row.appendChild(inp);
28387
+ if (spec.showWhen) {
28388
+ const gate = bools[spec.showWhen];
28389
+ const sync = function () { row.style.display = gate && gate.checked ? 'flex' : 'none'; };
28390
+ if (gate) gate.addEventListener('change', sync);
28391
+ sync();
28392
+ }
28393
+ getters.push(function () { return [spec.flag, inp.value]; });
28394
+ } else if (spec.kind === 'env-kv') {
28395
+ // Two input modes — KV add-rows or a raw JSON object; the server
28396
+ // materializes either into a SAM-shape temp file for --env-vars.
28397
+ row.className = 'opt-row opt-col';
28398
+ row.appendChild(el('span', 'opt-label', spec.label));
28399
+ const modes = el('div', 'envkv-modes');
28400
+ const kvBtn = el('button', 'envkv-mode active', 'KV');
28401
+ kvBtn.type = 'button';
28402
+ const jsonBtn = el('button', 'envkv-mode', 'JSON');
28403
+ jsonBtn.type = 'button';
28404
+ modes.appendChild(kvBtn);
28405
+ modes.appendChild(jsonBtn);
28406
+ row.appendChild(modes);
28407
+ const pl = pairList(spec);
28408
+ row.appendChild(pl.node);
28409
+ const ta = el('textarea', 'envkv-ta');
28410
+ ta.placeholder = '{ "KEY": "value" }';
28411
+ ta.spellcheck = false;
28412
+ ta.style.display = 'none';
28413
+ row.appendChild(ta);
28414
+ let mode = 'kv';
28415
+ kvBtn.onclick = function () {
28416
+ mode = 'kv';
28417
+ kvBtn.className = 'envkv-mode active';
28418
+ jsonBtn.className = 'envkv-mode';
28419
+ pl.node.style.display = '';
28420
+ ta.style.display = 'none';
28421
+ };
28422
+ jsonBtn.onclick = function () {
28423
+ mode = 'json';
28424
+ jsonBtn.className = 'envkv-mode active';
28425
+ kvBtn.className = 'envkv-mode';
28426
+ ta.style.display = '';
28427
+ pl.node.style.display = 'none';
28428
+ };
28429
+ getters.push(function () {
28430
+ return mode === 'json' ? [spec.flag, ta.value] : [spec.flag, pl.rows()];
28431
+ });
28432
+ } else {
28433
+ // repeat-pair: an add-row list of left<sep>right inputs.
28434
+ row.appendChild(el('span', 'opt-label', spec.label));
28435
+ const pl = pairList(spec);
28436
+ row.appendChild(pl.node);
28437
+ getters.push(function () { return [spec.flag, pl.rows()]; });
28438
+ }
28439
+ sec.appendChild(row);
28440
+ });
28441
+ return {
28442
+ node: sec,
28443
+ collect: function () {
28444
+ const out = {};
28445
+ getters.forEach(function (g) { const kv = g(); out[kv[0]] = kv[1]; });
28446
+ return out;
28447
+ },
28448
+ };
28449
+ }
28450
+
28118
28451
  async function loadTargets() {
28119
28452
  const pane = document.getElementById('targets');
28120
28453
  try {
@@ -28231,7 +28564,7 @@ const STUDIO_SCRIPT = `
28231
28564
  }
28232
28565
  }
28233
28566
 
28234
- async function startServe(id) {
28567
+ async function startServe(id, options) {
28235
28568
  // The serve kind (api / alb / ecs) drives which headless command the
28236
28569
  // server spawns; it is recorded on the row when the target list loads.
28237
28570
  const meta = serveMeta.get(id);
@@ -28239,10 +28572,12 @@ const STUDIO_SCRIPT = `
28239
28572
  serveState.set(id, { status: 'starting', endpoints: [] });
28240
28573
  updateServeRow(id);
28241
28574
  try {
28575
+ const body = { targetId: id, kind };
28576
+ if (options) body.options = options;
28242
28577
  const res = await fetch('/api/run', {
28243
28578
  method: 'POST',
28244
28579
  headers: { 'content-type': 'application/json' },
28245
- body: JSON.stringify({ targetId: id, kind }),
28580
+ body: JSON.stringify(body),
28246
28581
  });
28247
28582
  const data = await res.json();
28248
28583
  if (!res.ok) {
@@ -28279,12 +28614,17 @@ const STUDIO_SCRIPT = `
28279
28614
  const running = st.status === 'running';
28280
28615
  const starting = st.status === 'starting';
28281
28616
 
28617
+ const meta = serveMeta.get(id);
28618
+ const kind = meta ? meta.kind : 'api';
28619
+
28282
28620
  const head = el('div', 'composer');
28283
28621
  head.appendChild(el('div', 'target-name', 'Serve ' + id));
28284
28622
  const btn = running || starting
28285
28623
  ? el('button', null, 'Stop')
28286
28624
  : el('button', null, starting ? 'Starting…' : 'Start');
28287
- btn.onclick = () => { if (running || starting) stopServe(id); else startServe(id); };
28625
+ // Per-run options are only set before a start; collected on the Start click.
28626
+ let collectOpts = function () { return undefined; };
28627
+ btn.onclick = () => { if (running || starting) stopServe(id); else startServe(id, collectOpts()); };
28288
28628
  head.appendChild(btn);
28289
28629
  if (errMsg) {
28290
28630
  const m = el('div', 'err', errMsg);
@@ -28292,7 +28632,12 @@ const STUDIO_SCRIPT = `
28292
28632
  }
28293
28633
  ws.appendChild(head);
28294
28634
 
28295
- const meta = serveMeta.get(id);
28635
+ if (!running && !starting) {
28636
+ const opt = buildOptions(kind);
28637
+ if (opt.node) ws.appendChild(opt.node);
28638
+ collectOpts = opt.collect;
28639
+ }
28640
+
28296
28641
  const isEcs = meta && meta.kind === 'ecs';
28297
28642
  const epSec = el('div', 'section');
28298
28643
  epSec.appendChild(el('h3', null, 'Endpoints'));
@@ -28340,6 +28685,9 @@ const STUDIO_SCRIPT = `
28340
28685
  ta.value = eventText;
28341
28686
  ta.spellcheck = false;
28342
28687
  composer.appendChild(ta);
28688
+ // Per-run options (e.g. env vars) below the event, above Invoke.
28689
+ const opt = buildOptions(kind);
28690
+ if (opt.node) composer.appendChild(opt.node);
28343
28691
  composer.appendChild(document.createElement('br'));
28344
28692
  const btn = el('button', null, 'Invoke');
28345
28693
  const msg = el('div', 'err');
@@ -28351,7 +28699,7 @@ const STUDIO_SCRIPT = `
28351
28699
  ws.appendChild(composer);
28352
28700
  ws.appendChild(result);
28353
28701
 
28354
- active = { id, kind, ta, btn, msg, result };
28702
+ active = { id, kind, ta, btn, msg, result, collectOpts: opt.collect };
28355
28703
  btn.onclick = () => runInvoke();
28356
28704
  shownInvId = null;
28357
28705
  shownDetailId = null;
@@ -28373,10 +28721,13 @@ const STUDIO_SCRIPT = `
28373
28721
  btn.textContent = 'Invoking...';
28374
28722
  result.innerHTML = '';
28375
28723
  try {
28724
+ const body = { targetId: id, kind, event };
28725
+ const options = active.collectOpts ? active.collectOpts() : undefined;
28726
+ if (options) body.options = options;
28376
28727
  const res = await fetch('/api/run', {
28377
28728
  method: 'POST',
28378
28729
  headers: { 'content-type': 'application/json' },
28379
- body: JSON.stringify({ targetId: id, kind, event }),
28730
+ body: JSON.stringify(body),
28380
28731
  });
28381
28732
  const data = await res.json();
28382
28733
  if (data.invocationId) {
@@ -28688,6 +29039,7 @@ function renderStudioHtml(appLabel, cliName) {
28688
29039
  <div id="log-results"></div>
28689
29040
  </section>
28690
29041
  </main>
29042
+ <script>window.__OPTION_SPECS__ = ${JSON.stringify(OPTION_SPECS).replace(/</g, "\\u003c")};<\/script>
28691
29043
  <script>${STUDIO_SCRIPT}<\/script>
28692
29044
  </body>
28693
29045
  </html>`;
@@ -28962,6 +29314,25 @@ function listenWithBump(server, host, port, maxBump) {
28962
29314
  });
28963
29315
  }
28964
29316
 
29317
+ //#endregion
29318
+ //#region src/local/studio-child-args.ts
29319
+ /**
29320
+ * Build the shared `--app` / `--profile` / `--region` / `-c` /
29321
+ * `--from-cfn-stack` / `--assume-role` args for a studio child command.
29322
+ * Returns a flat argv fragment to spread after the subcommand + target.
29323
+ */
29324
+ function buildSharedChildArgs(config) {
29325
+ const args = [];
29326
+ if (config.app) args.push("--app", config.app);
29327
+ if (config.profile) args.push("--profile", config.profile);
29328
+ if (config.region) args.push("--region", config.region);
29329
+ for (const [k, v] of Object.entries(config.context ?? {})) args.push("-c", `${k}=${v}`);
29330
+ if (config.fromCfnStack === true) args.push("--from-cfn-stack");
29331
+ else if (typeof config.fromCfnStack === "string" && config.fromCfnStack !== "") args.push("--from-cfn-stack", config.fromCfnStack);
29332
+ if (typeof config.assumeRole === "string" && config.assumeRole !== "") args.push("--assume-role", config.assumeRole);
29333
+ return args;
29334
+ }
29335
+
28965
29336
  //#endregion
28966
29337
  //#region src/local/studio-dispatch.ts
28967
29338
  let idCounter = 0;
@@ -29028,12 +29399,16 @@ function createStudioDispatcher(config) {
29028
29399
  "invoke",
29029
29400
  req.targetId,
29030
29401
  "--event",
29031
- eventFile
29402
+ eventFile,
29403
+ ...buildSharedChildArgs(config),
29404
+ ...buildPerRunArgs("lambda", req.options)
29032
29405
  ];
29033
- if (config.app) args.push("--app", config.app);
29034
- if (config.profile) args.push("--profile", config.profile);
29035
- if (config.region) args.push("--region", config.region);
29036
- for (const [k, v] of Object.entries(config.context ?? {})) args.push("-c", `${k}=${v}`);
29406
+ const envVars = resolveEnvVars("lambda", req.options);
29407
+ if (envVars) {
29408
+ const envFile = join(dir, "env-vars.json");
29409
+ writeFileSync(envFile, JSON.stringify(envVars));
29410
+ args.push("--env-vars", envFile);
29411
+ }
29037
29412
  const { code, stdout, stderr } = await runChild(spawnFn, nodeBin, [config.cliEntry, ...args], config.cwd ?? process.cwd(), invocationId, req.targetId, config.bus, clock);
29038
29413
  const durationMs = clock() - startedAt;
29039
29414
  const ok = code === 0;
@@ -29469,17 +29844,14 @@ function createStudioServeManager(config) {
29469
29844
  if (message !== void 0) ev.message = message;
29470
29845
  config.bus.emit("serve", ev);
29471
29846
  }
29472
- function buildArgs(targetId, spec) {
29473
- const args = [
29847
+ function buildArgs(req, spec) {
29848
+ return [
29474
29849
  spec.command,
29475
- targetId,
29476
- ...spec.portArgs
29850
+ req.targetId,
29851
+ ...spec.portArgs,
29852
+ ...buildSharedChildArgs(config),
29853
+ ...buildPerRunArgs(req.kind, req.options)
29477
29854
  ];
29478
- if (config.app) args.push("--app", config.app);
29479
- if (config.profile) args.push("--profile", config.profile);
29480
- if (config.region) args.push("--region", config.region);
29481
- for (const [k, v] of Object.entries(config.context ?? {})) args.push("-c", `${k}=${v}`);
29482
- return args;
29483
29855
  }
29484
29856
  async function start(req) {
29485
29857
  const spec = SERVE_SPECS[req.kind];
@@ -29489,7 +29861,7 @@ function createStudioServeManager(config) {
29489
29861
  const startedAt = clock();
29490
29862
  let child;
29491
29863
  try {
29492
- child = spawnFn(nodeBin, [config.cliEntry, ...buildArgs(req.targetId, spec)], { cwd });
29864
+ child = spawnFn(nodeBin, [config.cliEntry, ...buildArgs(req, spec)], { cwd });
29493
29865
  } catch (err) {
29494
29866
  throw err instanceof Error ? err : new Error(String(err));
29495
29867
  }
@@ -29699,13 +30071,21 @@ const STUDIO_TARGET_KINDS = [
29699
30071
  */
29700
30072
  function coerceRunRequest(body) {
29701
30073
  if (typeof body !== "object" || body === null) throw new Error("Request body must be a JSON object.");
29702
- const { targetId, kind, event } = body;
30074
+ const { targetId, kind, event, options } = body;
29703
30075
  if (typeof targetId !== "string" || targetId.trim() === "") throw new Error("Request body must include a non-empty \"targetId\" string.");
29704
30076
  if (typeof kind !== "string" || !STUDIO_TARGET_KINDS.includes(kind)) throw new Error(`Request body "kind" must be one of: ${STUDIO_TARGET_KINDS.join(", ")}.`);
30077
+ let runOptions;
30078
+ if (options !== void 0) {
30079
+ if (typeof options !== "object" || options === null || Array.isArray(options)) throw new Error("Request body \"options\" must be a JSON object keyed by option flag.");
30080
+ runOptions = options;
30081
+ buildPerRunArgs(kind, runOptions);
30082
+ resolveEnvVars(kind, runOptions);
30083
+ }
29705
30084
  return {
29706
30085
  targetId,
29707
30086
  kind,
29708
- event
30087
+ event,
30088
+ ...runOptions !== void 0 ? { options: runOptions } : {}
29709
30089
  };
29710
30090
  }
29711
30091
  /**
@@ -29762,7 +30142,9 @@ async function localStudioCommand(options) {
29762
30142
  ...appCmd ? { app: appCmd } : {},
29763
30143
  ...options.profile ? { profile: options.profile } : {},
29764
30144
  ...options.region ? { region: options.region } : {},
29765
- ...Object.keys(context).length > 0 ? { context } : {}
30145
+ ...Object.keys(context).length > 0 ? { context } : {},
30146
+ ...options.fromCfnStack !== void 0 ? { fromCfnStack: options.fromCfnStack } : {},
30147
+ ...options.assumeRole ? { assumeRole: options.assumeRole } : {}
29766
30148
  };
29767
30149
  const dispatcher = createStudioDispatcher(childConfig);
29768
30150
  const serveManager = createStudioServeManager(childConfig);
@@ -29852,9 +30234,11 @@ function createLocalStudioCommand(opts = {}) {
29852
30234
  function addStudioSpecificOptions(cmd) {
29853
30235
  cmd.addOption(new Option("--studio-port <port>", "Preferred port for the studio web server (bumps to the next free port on collision)").default(String(DEFAULT_STUDIO_PORT)));
29854
30236
  cmd.addOption(new Option("--no-open", "Do not auto-open the browser when studio starts (TTY only)"));
30237
+ cmd.addOption(new Option("--from-cfn-stack [cfn-stack-name]", "Bind the whole studio session to a deployed CloudFormation stack: every invoke / serve started from the UI runs against the deployed stack real ARNs / Secret values. Bare flag auto-resolves a single-stack app; pass a name to pick the stack. Forwarded to each child command."));
30238
+ cmd.addOption(new Option("--assume-role <arn>", "IAM role ARN to assume for every invoke / serve started from the UI (temp credentials forwarded into the containers). Forwarded to each child command."));
29855
30239
  return cmd;
29856
30240
  }
29857
30241
 
29858
30242
  //#endregion
29859
- export { setShadowReadyTimeoutMs as $, discoverWebSocketApis as $n, computeRequestIdentityHash as $t, addEcsAssumeRoleOptions as A, buildContainerImage as An, resolveApiTargetSubset as At, buildImageOverrideTag as B, createLocalStateProvider as Bn, groupRoutesByServer as Bt, addStartServiceSpecificOptions as C, buildMgmtEndpointEnvUrl as Cn, toCmdArgv as Ct, createLocalRunTaskCommand as D, buildDisconnectEvent as Dn, addStartApiSpecificOptions as Dt, addRunTaskSpecificOptions as E, buildConnectEvent as En, createLocalInvokeCommand as Et, parseRestartPolicy as F, substituteAgainstState as Fn, materializeLayerFromArn as Ft, runImageOverrideBuilds as G, resolveCfnStackName as Gn, defaultCredentialsLoader as Gt, mergeForService as H, rejectExplicitCfnStackWithMultipleStacks as Hn, startApiServer as Ht, resolveEcsAssumeRoleOption as I, substituteAgainstStateAsync as In, resolveEnvVars as It, listPinnedTargets as J, resolveSsmParameters as Jn, createJwksCache as Jt, describePinnedImageUri as K, CfnLocalStateProvider as Kn, buildCognitoJwksUrl as Kt, resolveSharedSidecarCredentials as L, substituteEnvVarsFromState as Ln, availableApiIdentifiers as Lt, buildEcsImageResolutionContext$1 as M, resolveRuntimeFileExtension as Mn, createFileWatcher as Mt, ecsClusterOption as N, resolveRuntimeImage as Nn, attachStageContext as Nt, MAX_TASKS_SUBNET_RANGE_CAP as O, buildMessageEvent as On, createLocalStartApiCommand as Ot, parseMaxTasks as P, EcsTaskResolutionError as Pn, buildStageMap as Pt, SOFT_RELOAD_COMPLETION_LOG_SUFFIX as Q, listTargets as Qn, buildMethodArn as Qt, runEcsServiceEmulator as R, substituteEnvVarsFromStateAsync as Rn, filterRoutesByApiIdentifier as Rt, resolveAlbFrontDoor as S, ConnectionRegistry as Sn, renderCodeDockerfile as St, serviceStrategy as T, parseConnectionsPath as Tn, addInvokeSpecificOptions as Tt, parseImageOverrideFlags as U, resolveCfnFallbackRegion as Un, resolveSelectionExpression as Ut, enforceImageOverrideOrphans as V, isCfnFlagPresent as Vn, readMtlsMaterialsFromDisk as Vt, resolveImageOverrides as W, resolveCfnRegion as Wn, resolveServiceIntegrationParameters as Wt, CloudMapRegistry as X, resolveSingleTarget as Xn, verifyJwtAuthorizer as Xt, buildCloudMapIndex as Y, resolveWatchConfig as Yn, verifyCognitoJwt as Yt, DEFAULT_SHADOW_READY_TIMEOUT_MS as Z, countTargets as Zn, verifyJwtViaDiscovery as Zt, albStrategy as _, tryParseStatus as _n, LocalInvokeBuildError as _r, waitForAgentCorePing as _t, createStudioServeManager as a, buildCorsConfigByApiId as an, AGENTCORE_A2A_PROTOCOL as ar, A2A_CONTAINER_PORT as at, resolveAlbTarget as b, probeHostGatewaySupport as bn, buildAgentCoreCodeImage as bt, startStudioServer as c, matchPreflight as cn, AGENTCORE_MCP_PROTOCOL as cr, MCP_CONTAINER_PORT as ct, createStudioStore as d, applyAuthorizerOverlay as dn, pickAgentCoreCandidateStack as dr, mcpInvokeOnce as dt, evaluateCachedLambdaPolicy as en, discoverWebSocketApisOrThrow as er, getContainerNetworkIp as et, StudioEventBus as f, buildHttpApiV2Event as fn, resolveAgentCoreTarget as fr, parseSseForJsonRpc as ft, addAlbSpecificOptions as g, selectIntegrationResponse as gn, tryResolveImageFnJoin as gr, invokeAgentCore as gt, formatTargetListing as h, pickResponseTemplate as hn, substituteImagePlaceholders as hr, AGENTCORE_SESSION_ID_HEADER as ht, createLocalStudioCommand as i, applyCorsResponseHeaders as in, resolveLambdaArnIntrinsic as ir, invokeAgentCoreWs as it, addImageOverrideOptions as j, resolveRuntimeCodeMountPath as jn, createAuthorizerCache as jt, addCommonEcsServiceOptions as k, architectureToPlatform as kn, createWatchPredicates as kt, toStudioTargetGroups as l, matchRoute as ln, AGENTCORE_RUNTIME_TYPE as lr, MCP_PATH as lt, createLocalListCommand as m, evaluateResponseParameters as mn, formatStateRemedy as mr, signAgentCoreInvocation as mt, coerceRunRequest as n, invokeTokenAuthorizer as nn, discoverRoutes as nr, addInvokeAgentCoreSpecificOptions as nt, startStudioProxy as o, buildCorsConfigFromCloudFrontChain as on, AGENTCORE_AGUI_PROTOCOL as or, A2A_PATH as ot, addListSpecificOptions as p, buildRestV1Event as pn, derivePseudoParametersFromRegion as pr, AGENTCORE_SIGV4_SERVICE as pt, isLocalCdkAssetImage as q, collectSsmParameterRefs as qn, buildJwksUrlFromIssuer as qt, coerceStopRequest as r, attachAuthorizers as rn, pickRefLogicalId as rr, createLocalInvokeAgentCoreCommand as rt, createStudioDispatcher as s, isFunctionUrlOacFronted as sn, AGENTCORE_HTTP_PROTOCOL as sr, a2aInvokeOnce as st, addStudioSpecificOptions as t, invokeRequestAuthorizer as tn, parseSelectionExpressionPath as tr, attachContainerLogStreamer as tt, renderStudioHtml as u, translateLambdaResponse as un, AgentCoreResolutionError as ur, MCP_PROTOCOL_VERSION as ut, createLocalStartAlbCommand as v, VtlEvaluationError as vn, buildStsClientConfig as vr, downloadAndExtractS3Bundle as vt, createLocalStartServiceCommand as w, handleConnectionsRequest as wn, classifySourceChange as wt, isApplicationLoadBalancer as x, bufferToBody as xn, computeCodeImageTag as xt, parseLbPortOverrides as y, HOST_GATEWAY_MIN_VERSION as yn, resolveProfileCredentials as yr, SUPPORTED_CODE_RUNTIMES as yt, ImageOverrideError as z, LocalStateSourceError as zn, filterRoutesByApiIdentifiers as zt };
29860
- //# sourceMappingURL=local-studio-B32lmdOF.js.map
30243
+ export { setShadowReadyTimeoutMs as $, discoverWebSocketApis as $n, computeRequestIdentityHash as $t, addEcsAssumeRoleOptions as A, buildContainerImage as An, resolveApiTargetSubset as At, buildImageOverrideTag as B, createLocalStateProvider as Bn, groupRoutesByServer as Bt, addStartServiceSpecificOptions as C, buildMgmtEndpointEnvUrl as Cn, toCmdArgv as Ct, createLocalRunTaskCommand as D, buildDisconnectEvent as Dn, addStartApiSpecificOptions as Dt, addRunTaskSpecificOptions as E, buildConnectEvent as En, createLocalInvokeCommand as Et, parseRestartPolicy as F, substituteAgainstState as Fn, materializeLayerFromArn as Ft, runImageOverrideBuilds as G, resolveCfnStackName as Gn, defaultCredentialsLoader as Gt, mergeForService as H, rejectExplicitCfnStackWithMultipleStacks as Hn, startApiServer as Ht, resolveEcsAssumeRoleOption as I, substituteAgainstStateAsync as In, resolveEnvVars$1 as It, listPinnedTargets as J, resolveSsmParameters as Jn, createJwksCache as Jt, describePinnedImageUri as K, CfnLocalStateProvider as Kn, buildCognitoJwksUrl as Kt, resolveSharedSidecarCredentials as L, substituteEnvVarsFromState as Ln, availableApiIdentifiers as Lt, buildEcsImageResolutionContext$1 as M, resolveRuntimeFileExtension as Mn, createFileWatcher as Mt, ecsClusterOption as N, resolveRuntimeImage as Nn, attachStageContext as Nt, MAX_TASKS_SUBNET_RANGE_CAP as O, buildMessageEvent as On, createLocalStartApiCommand as Ot, parseMaxTasks as P, EcsTaskResolutionError as Pn, buildStageMap as Pt, SOFT_RELOAD_COMPLETION_LOG_SUFFIX as Q, listTargets as Qn, buildMethodArn as Qt, runEcsServiceEmulator as R, substituteEnvVarsFromStateAsync as Rn, filterRoutesByApiIdentifier as Rt, resolveAlbFrontDoor as S, ConnectionRegistry as Sn, renderCodeDockerfile as St, serviceStrategy as T, parseConnectionsPath as Tn, addInvokeSpecificOptions as Tt, parseImageOverrideFlags as U, resolveCfnFallbackRegion as Un, resolveSelectionExpression as Ut, enforceImageOverrideOrphans as V, isCfnFlagPresent as Vn, readMtlsMaterialsFromDisk as Vt, resolveImageOverrides as W, resolveCfnRegion as Wn, resolveServiceIntegrationParameters as Wt, CloudMapRegistry as X, resolveSingleTarget as Xn, verifyJwtAuthorizer as Xt, buildCloudMapIndex as Y, resolveWatchConfig as Yn, verifyCognitoJwt as Yt, DEFAULT_SHADOW_READY_TIMEOUT_MS as Z, countTargets as Zn, verifyJwtViaDiscovery as Zt, albStrategy as _, tryParseStatus as _n, LocalInvokeBuildError as _r, waitForAgentCorePing as _t, createStudioServeManager as a, buildCorsConfigByApiId as an, AGENTCORE_A2A_PROTOCOL as ar, A2A_CONTAINER_PORT as at, resolveAlbTarget as b, probeHostGatewaySupport as bn, buildAgentCoreCodeImage as bt, startStudioServer as c, matchPreflight as cn, AGENTCORE_MCP_PROTOCOL as cr, MCP_CONTAINER_PORT as ct, createStudioStore as d, applyAuthorizerOverlay as dn, pickAgentCoreCandidateStack as dr, mcpInvokeOnce as dt, evaluateCachedLambdaPolicy as en, discoverWebSocketApisOrThrow as er, getContainerNetworkIp as et, StudioEventBus as f, buildHttpApiV2Event as fn, resolveAgentCoreTarget as fr, parseSseForJsonRpc as ft, addAlbSpecificOptions as g, selectIntegrationResponse as gn, tryResolveImageFnJoin as gr, invokeAgentCore as gt, formatTargetListing as h, pickResponseTemplate as hn, substituteImagePlaceholders as hr, AGENTCORE_SESSION_ID_HEADER as ht, createLocalStudioCommand as i, applyCorsResponseHeaders as in, resolveLambdaArnIntrinsic as ir, invokeAgentCoreWs as it, addImageOverrideOptions as j, resolveRuntimeCodeMountPath as jn, createAuthorizerCache as jt, addCommonEcsServiceOptions as k, architectureToPlatform as kn, createWatchPredicates as kt, toStudioTargetGroups as l, matchRoute as ln, AGENTCORE_RUNTIME_TYPE as lr, MCP_PATH as lt, createLocalListCommand as m, evaluateResponseParameters as mn, formatStateRemedy as mr, signAgentCoreInvocation as mt, coerceRunRequest as n, invokeTokenAuthorizer as nn, discoverRoutes as nr, addInvokeAgentCoreSpecificOptions as nt, startStudioProxy as o, buildCorsConfigFromCloudFrontChain as on, AGENTCORE_AGUI_PROTOCOL as or, A2A_PATH as ot, addListSpecificOptions as p, buildRestV1Event as pn, derivePseudoParametersFromRegion as pr, AGENTCORE_SIGV4_SERVICE as pt, isLocalCdkAssetImage as q, collectSsmParameterRefs as qn, buildJwksUrlFromIssuer as qt, coerceStopRequest as r, attachAuthorizers as rn, pickRefLogicalId as rr, createLocalInvokeAgentCoreCommand as rt, createStudioDispatcher as s, isFunctionUrlOacFronted as sn, AGENTCORE_HTTP_PROTOCOL as sr, a2aInvokeOnce as st, addStudioSpecificOptions as t, invokeRequestAuthorizer as tn, parseSelectionExpressionPath as tr, attachContainerLogStreamer as tt, renderStudioHtml as u, translateLambdaResponse as un, AgentCoreResolutionError as ur, MCP_PROTOCOL_VERSION as ut, createLocalStartAlbCommand as v, VtlEvaluationError as vn, buildStsClientConfig as vr, downloadAndExtractS3Bundle as vt, createLocalStartServiceCommand as w, handleConnectionsRequest as wn, classifySourceChange as wt, isApplicationLoadBalancer as x, bufferToBody as xn, computeCodeImageTag as xt, parseLbPortOverrides as y, HOST_GATEWAY_MIN_VERSION as yn, resolveProfileCredentials as yr, SUPPORTED_CODE_RUNTIMES as yt, ImageOverrideError as z, LocalStateSourceError as zn, filterRoutesByApiIdentifiers as zt };
30244
+ //# sourceMappingURL=local-studio-DliL-4cC.js.map