cdk-local 0.94.0 → 0.96.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.
@@ -28024,6 +28024,160 @@ function createStudioStore(bus, options = {}) {
28024
28024
  };
28025
28025
  }
28026
28026
 
28027
+ //#endregion
28028
+ //#region src/local/studio-option-catalog.ts
28029
+ /**
28030
+ * Auto-derived full-flag catalog for `cdkl studio` (issue #301).
28031
+ *
28032
+ * The per-target {@link OPTION_SPECS} in `studio-option-specs.ts` is a
28033
+ * CURATED subset — the handful of flags worth a rich control (a checkbox, a
28034
+ * KV editor, an add-row list). But a command like `cdkl start-api` accepts
28035
+ * many more flags than studio renders a control for, and hiding them makes
28036
+ * the UI strictly less capable than the headless CLI.
28037
+ *
28038
+ * This module closes that gap by INTROSPECTING each runnable kind's Commander
28039
+ * command factory and emitting the complete flag list (name + description).
28040
+ * The studio UI serializes it into the page and renders, inside a collapsed
28041
+ * "All options" section, (a) the full catalog as a read-only reference and
28042
+ * (b) a raw extra-args input (see {@link tokenizeRawArgs}) so any flag the
28043
+ * curated controls don't expose can still be passed verbatim. Auto-derivation
28044
+ * means the catalog can never drift from the command's real option set.
28045
+ *
28046
+ * Session-global flags (handled by the studio Session bar / `studio-child-args`)
28047
+ * and the auto-added `--help` / `--version` are excluded — passing them per-run
28048
+ * would conflict with the session-wide wiring.
28049
+ */
28050
+ /**
28051
+ * Long flag names handled by the session bar / `studio-child-args` (forwarded
28052
+ * to every spawned child once, session-wide). Excluded from the per-target
28053
+ * catalog so a user does not re-specify them per-run and collide with the
28054
+ * session-wide value. Plus the Commander-managed `--help` / `--version`.
28055
+ */
28056
+ const CATALOG_EXCLUDED_FLAGS = new Set([
28057
+ "--app",
28058
+ "--profile",
28059
+ "--region",
28060
+ "--context",
28061
+ "--from-cfn-stack",
28062
+ "--assume-role",
28063
+ "--help",
28064
+ "--version"
28065
+ ]);
28066
+ const KIND_FACTORIES = {
28067
+ lambda: {
28068
+ command: "invoke",
28069
+ factory: createLocalInvokeCommand
28070
+ },
28071
+ agentcore: {
28072
+ command: "invoke-agentcore",
28073
+ factory: createLocalInvokeAgentCoreCommand
28074
+ },
28075
+ api: {
28076
+ command: "start-api",
28077
+ factory: createLocalStartApiCommand
28078
+ },
28079
+ alb: {
28080
+ command: "start-alb",
28081
+ factory: createLocalStartAlbCommand
28082
+ },
28083
+ ecs: {
28084
+ command: "start-service",
28085
+ factory: createLocalStartServiceCommand
28086
+ }
28087
+ };
28088
+ let cached;
28089
+ /**
28090
+ * Build (and memoize) the full per-kind flag catalog by introspecting each
28091
+ * runnable kind's Commander command factory.
28092
+ *
28093
+ * Each factory calls `setEmbedConfig(opts.embedConfig)` at construction — with
28094
+ * no opts that resets the active embed config to cdk-local defaults, which
28095
+ * would wipe a host CLI's branding. So the active config is snapshotted and
28096
+ * each factory is re-handed it: branding is preserved AND the derived flag
28097
+ * descriptions reflect the host's active branding. The `finally` restore is a
28098
+ * belt-and-suspenders against a factory that ignores the passed config.
28099
+ * Memoized: the factories are instantiated exactly once per process, not per
28100
+ * page render.
28101
+ */
28102
+ function buildFlagCatalog() {
28103
+ if (cached) return cached;
28104
+ const savedEmbedConfig = getEmbedConfig();
28105
+ try {
28106
+ const out = {};
28107
+ for (const kind of Object.keys(KIND_FACTORIES)) {
28108
+ const { command, factory } = KIND_FACTORIES[kind];
28109
+ const cmd = factory({ embedConfig: savedEmbedConfig });
28110
+ const flags = [];
28111
+ for (const opt of cmd.options) {
28112
+ if (opt.hidden) continue;
28113
+ if (opt.long && CATALOG_EXCLUDED_FLAGS.has(opt.long)) continue;
28114
+ flags.push({
28115
+ flags: opt.flags,
28116
+ description: opt.description ?? ""
28117
+ });
28118
+ }
28119
+ out[kind] = {
28120
+ command,
28121
+ flags
28122
+ };
28123
+ }
28124
+ cached = out;
28125
+ return out;
28126
+ } finally {
28127
+ setEmbedConfig(savedEmbedConfig);
28128
+ }
28129
+ }
28130
+ /**
28131
+ * Tokenize a raw extra-args string into discrete argv elements, honoring
28132
+ * single / double quotes and backslash escaping so values with spaces survive
28133
+ * (`--name "two words"` -> `['--name', 'two words']`). studio spawns children
28134
+ * WITHOUT a shell (argv array), so the tokens are appended verbatim — there is
28135
+ * no shell-injection surface; the child command still validates each arg.
28136
+ *
28137
+ * Returns `[]` for an empty / whitespace-only input. Throws on an unterminated
28138
+ * quote so a malformed raw-args string fails as a clean boundary error rather
28139
+ * than spawning a child with a mis-split argv.
28140
+ */
28141
+ function tokenizeRawArgs(raw) {
28142
+ if (raw === void 0) return [];
28143
+ const tokens = [];
28144
+ let current = "";
28145
+ let inToken = false;
28146
+ let quote = null;
28147
+ for (let i = 0; i < raw.length; i++) {
28148
+ const ch = raw[i];
28149
+ if (quote) {
28150
+ if (ch === "\\" && quote === "\"" && i + 1 < raw.length) current += raw[++i];
28151
+ else if (ch === quote) quote = null;
28152
+ else current += ch;
28153
+ continue;
28154
+ }
28155
+ if (ch === "\"" || ch === "'") {
28156
+ quote = ch;
28157
+ inToken = true;
28158
+ continue;
28159
+ }
28160
+ if (ch === "\\" && i + 1 < raw.length) {
28161
+ current += raw[++i];
28162
+ inToken = true;
28163
+ continue;
28164
+ }
28165
+ if (ch === " " || ch === " " || ch === "\n" || ch === "\r") {
28166
+ if (inToken) {
28167
+ tokens.push(current);
28168
+ current = "";
28169
+ inToken = false;
28170
+ }
28171
+ continue;
28172
+ }
28173
+ current += ch;
28174
+ inToken = true;
28175
+ }
28176
+ if (quote) throw new Error(`Raw extra args have an unterminated ${quote} quote.`);
28177
+ if (inToken) tokens.push(current);
28178
+ return tokens;
28179
+ }
28180
+
28027
28181
  //#endregion
28028
28182
  //#region src/local/studio-option-specs.ts
28029
28183
  /**
@@ -28407,6 +28561,23 @@ const STUDIO_CSS = `
28407
28561
  .pair-add { align-self: flex-start; background: #1d1d1d; color: #7bd88f; border: 1px solid #2f4030;
28408
28562
  border-radius: 3px; cursor: pointer; padding: 3px 9px; font: 12px ui-monospace, monospace; }
28409
28563
  .pair-add:hover { background: #243024; }
28564
+ .options select { flex: 1; background: #111; color: #ddd; border: 1px solid #333; border-radius: 3px;
28565
+ padding: 4px 6px; font: 12px ui-monospace, Menlo, monospace; min-width: 0; }
28566
+ .options select:focus { outline: none; border-color: #4ec97a; }
28567
+ details.all-options { margin: 8px 0; border-top: 1px solid #2a2a2a; padding-top: 6px; }
28568
+ details.all-options > summary { color: #8a8a8a; font-size: 12px; cursor: pointer; user-select: none; }
28569
+ details.all-options > summary:hover { color: #bbb; }
28570
+ .all-options .opt-row { display: flex; flex-direction: column; align-items: stretch; gap: 4px; margin: 6px 0; }
28571
+ .all-options input.raw-args {
28572
+ width: 100%; box-sizing: border-box; background: #111; color: #ddd; border: 1px solid #333;
28573
+ border-radius: 3px; padding: 4px 6px; font: 12px ui-monospace, Menlo, monospace;
28574
+ }
28575
+ .all-options input.raw-args:focus { outline: none; border-color: #4ec97a; }
28576
+ .opt-hint { color: #777; font-size: 11px; }
28577
+ .flag-catalog { margin-top: 8px; display: flex; flex-direction: column; gap: 2px; }
28578
+ .flag-row { display: flex; gap: 8px; align-items: baseline; font-size: 11px; }
28579
+ .flag-name { color: #7bd88f; font-family: ui-monospace, Menlo, monospace; white-space: nowrap; }
28580
+ .flag-desc { color: #999; }
28410
28581
  .envkv-modes { display: flex; gap: 0; }
28411
28582
  .envkv-mode { background: #1a1a1a; color: #999; border: 1px solid #333; cursor: pointer;
28412
28583
  padding: 3px 12px; font: 11px ui-monospace, monospace; }
@@ -28431,6 +28602,7 @@ const STUDIO_SCRIPT = `
28431
28602
  let shownInvId = null; // lambda invocation whose result is in the workspace
28432
28603
  let shownServeId = null; // serve target whose workspace is shown
28433
28604
  let shownDetailId = null; // captured request whose read-only detail is shown
28605
+ let studioDockerfiles = []; // Dockerfiles scanned at boot (pinned-ecs image-override picker)
28434
28606
 
28435
28607
  function el(tag, cls, text) {
28436
28608
  const e = document.createElement(tag);
@@ -28447,9 +28619,14 @@ const STUDIO_SCRIPT = `
28447
28619
 
28448
28620
  function buildOptions(kind) {
28449
28621
  const specs = OPTION_SPECS[kind] || [];
28450
- if (!specs.length) return { node: null, collect: function () { return undefined; } };
28622
+ // The composer always shows an "All options" section (raw extra args + the
28623
+ // auto-derived flag reference), even for kinds with no curated controls.
28624
+ const wrap = el('div', 'options-wrap');
28451
28625
  const sec = el('div', 'section options');
28452
- sec.appendChild(el('h3', null, 'Options'));
28626
+ if (specs.length) {
28627
+ sec.appendChild(el('h3', null, 'Options'));
28628
+ wrap.appendChild(sec);
28629
+ }
28453
28630
  const getters = [];
28454
28631
  const bools = {};
28455
28632
 
@@ -28565,12 +28742,96 @@ const STUDIO_SCRIPT = `
28565
28742
  }
28566
28743
  sec.appendChild(row);
28567
28744
  });
28745
+ const allOpts = buildAllOptions(kind);
28746
+ wrap.appendChild(allOpts.node);
28568
28747
  return {
28569
- node: sec,
28748
+ node: wrap,
28570
28749
  collect: function () {
28571
28750
  const out = {};
28572
28751
  getters.forEach(function (g) { const kv = g(); out[kv[0]] = kv[1]; });
28573
- return out;
28752
+ // Omit-when-empty: a kind with no curated controls (e.g. api) collects
28753
+ // nothing, so return undefined rather than {} to keep the run/serve
28754
+ // body identical to before this section existed.
28755
+ return Object.keys(out).length ? out : undefined;
28756
+ },
28757
+ collectRaw: allOpts.collectRaw,
28758
+ };
28759
+ }
28760
+
28761
+ // The collapsed "All options" section: a raw extra-args input (appended
28762
+ // verbatim to the spawned child) + the auto-derived, read-only catalog of
28763
+ // every flag the underlying command accepts. The curated controls above
28764
+ // cover the common flags with rich UI; this exposes the rest so the studio
28765
+ // UI is never strictly less capable than the headless CLI (issue #301).
28766
+ const FLAG_CATALOG = window.__FLAG_CATALOG__ || {};
28767
+
28768
+ function buildAllOptions(kind) {
28769
+ const cat = FLAG_CATALOG[kind] || { command: '', flags: [] };
28770
+ const det = el('details', 'all-options');
28771
+ det.appendChild(el('summary', null, 'All options'));
28772
+
28773
+ const rawRow = el('div', 'opt-row opt-col');
28774
+ rawRow.appendChild(el('span', 'opt-label', 'Raw extra args'));
28775
+ const rawIn = el('input', 'raw-args');
28776
+ rawIn.placeholder = '--flag value --other "with spaces"';
28777
+ rawRow.appendChild(rawIn);
28778
+ const hint = cat.command
28779
+ ? 'Appended verbatim to the spawned ' + cat.command + ' command. Quote values with spaces.'
28780
+ : 'Appended verbatim to the spawned command. Quote values with spaces.';
28781
+ rawRow.appendChild(el('div', 'opt-hint', hint));
28782
+ det.appendChild(rawRow);
28783
+
28784
+ if (cat.flags.length) {
28785
+ const ref = el('div', 'flag-catalog');
28786
+ ref.appendChild(el('div', 'opt-label', 'Available flags'));
28787
+ cat.flags.forEach(function (f) {
28788
+ const row = el('div', 'flag-row');
28789
+ row.appendChild(el('code', 'flag-name', f.flags));
28790
+ if (f.description) row.appendChild(el('span', 'flag-desc', f.description));
28791
+ ref.appendChild(row);
28792
+ });
28793
+ det.appendChild(ref);
28794
+ }
28795
+
28796
+ return {
28797
+ node: det,
28798
+ collectRaw: function () {
28799
+ const v = rawIn.value.trim();
28800
+ return v === '' ? undefined : v;
28801
+ },
28802
+ };
28803
+ }
28804
+
28805
+ // Image-override picker for a pinned ECS service (issue #301): a select of
28806
+ // the Dockerfiles discovered at boot. Picking one threads an
28807
+ // --image-override flag to start-service so the deployed-registry-pinned
28808
+ // image is rebuilt from local source. Default "(keep pinned image)" => no
28809
+ // override.
28810
+ function buildImageOverridePicker() {
28811
+ const sec = el('div', 'section options');
28812
+ sec.appendChild(el('h3', null, 'Image override'));
28813
+ const row = el('div', 'opt-row');
28814
+ row.appendChild(el('span', 'opt-label', 'Local Dockerfile'));
28815
+ const sel = el('select', 'image-override-select');
28816
+ const none = el('option', null, '(keep pinned image)');
28817
+ none.value = '';
28818
+ sel.appendChild(none);
28819
+ studioDockerfiles.forEach(function (df) {
28820
+ const o = el('option', null, df);
28821
+ o.value = df;
28822
+ sel.appendChild(o);
28823
+ });
28824
+ row.appendChild(sel);
28825
+ sec.appendChild(row);
28826
+ const hint = studioDockerfiles.length
28827
+ ? 'This image is pinned to a deployed registry — local edits do not take effect. Pick a Dockerfile to rebuild it locally.'
28828
+ : 'This image is pinned to a deployed registry, but no Dockerfile was found under the app directory.';
28829
+ sec.appendChild(el('div', 'opt-hint', hint));
28830
+ return {
28831
+ node: sec,
28832
+ collect: function () {
28833
+ const v = sel.value.trim();
28834
+ return v === '' ? undefined : v;
28574
28835
  },
28575
28836
  };
28576
28837
  }
@@ -28580,6 +28841,9 @@ const STUDIO_SCRIPT = `
28580
28841
  try {
28581
28842
  const res = await fetch('/api/targets');
28582
28843
  const data = await res.json();
28844
+ // Dockerfiles discovered at boot — offered in a pinned ecs service's
28845
+ // image-override picker (issue #301).
28846
+ studioDockerfiles = Array.isArray(data.dockerfiles) ? data.dockerfiles : [];
28583
28847
  pane.querySelectorAll('.group-title,.target,.empty').forEach((n) => n.remove());
28584
28848
  let total = 0;
28585
28849
  for (const group of data.groups) {
@@ -28613,7 +28877,7 @@ const STUDIO_SCRIPT = `
28613
28877
  t.appendChild(btnSlot);
28614
28878
  t.onclick = () => selectTarget(entry.id, group.kind);
28615
28879
  targetEls.set(entry.id, t);
28616
- serveMeta.set(entry.id, { dot, btnSlot, kind: group.kind });
28880
+ serveMeta.set(entry.id, { dot, btnSlot, kind: group.kind, pinned: entry.pinned === true });
28617
28881
  updateServeRow(entry.id);
28618
28882
  }
28619
28883
  pane.appendChild(t);
@@ -28696,7 +28960,7 @@ const STUDIO_SCRIPT = `
28696
28960
  }
28697
28961
  }
28698
28962
 
28699
- async function startServe(id, options) {
28963
+ async function startServe(id, options, rawArgs, imageOverride) {
28700
28964
  // The serve kind (api / alb / ecs) drives which headless command the
28701
28965
  // server spawns; it is recorded on the row when the target list loads.
28702
28966
  const meta = serveMeta.get(id);
@@ -28706,6 +28970,8 @@ const STUDIO_SCRIPT = `
28706
28970
  try {
28707
28971
  const body = { targetId: id, kind };
28708
28972
  if (options) body.options = options;
28973
+ if (rawArgs) body.rawArgs = rawArgs;
28974
+ if (imageOverride) body.imageOverride = imageOverride;
28709
28975
  const res = await fetch('/api/run', {
28710
28976
  method: 'POST',
28711
28977
  headers: { 'content-type': 'application/json' },
@@ -28762,7 +29028,9 @@ const STUDIO_SCRIPT = `
28762
29028
  : el('button', null, starting ? 'Starting…' : 'Start');
28763
29029
  // Per-run options are only set before a start; collected on the Start click.
28764
29030
  let collectOpts = function () { return undefined; };
28765
- btn.onclick = () => { if (running || starting) stopServe(id); else startServe(id, collectOpts()); };
29031
+ let collectRaw = function () { return undefined; };
29032
+ let collectImageOverride = function () { return undefined; };
29033
+ btn.onclick = () => { if (running || starting) stopServe(id); else startServe(id, collectOpts(), collectRaw(), collectImageOverride()); };
28766
29034
  head.appendChild(btn);
28767
29035
  if (errMsg) {
28768
29036
  const m = el('div', 'err', errMsg);
@@ -28771,9 +29039,19 @@ const STUDIO_SCRIPT = `
28771
29039
  ws.appendChild(head);
28772
29040
 
28773
29041
  if (!running && !starting) {
29042
+ // A pinned ECS service (deployed-registry image) does not pick up local
29043
+ // source edits — offer an image-override Dockerfile picker so it can be
29044
+ // rebuilt locally (issue #301). Local-asset services hot-reload under
29045
+ // --watch and get no picker.
29046
+ if (meta && meta.kind === 'ecs' && meta.pinned) {
29047
+ const io = buildImageOverridePicker();
29048
+ ws.appendChild(io.node);
29049
+ collectImageOverride = io.collect;
29050
+ }
28774
29051
  const opt = buildOptions(kind);
28775
29052
  if (opt.node) ws.appendChild(opt.node);
28776
29053
  collectOpts = opt.collect;
29054
+ collectRaw = opt.collectRaw;
28777
29055
  }
28778
29056
 
28779
29057
  const isEcs = meta && meta.kind === 'ecs';
@@ -28973,7 +29251,7 @@ const STUDIO_SCRIPT = `
28973
29251
  ws.appendChild(composer);
28974
29252
  ws.appendChild(result);
28975
29253
 
28976
- active = { id, kind, ta, btn, msg, result, collectOpts: opt.collect };
29254
+ active = { id, kind, ta, btn, msg, result, collectOpts: opt.collect, collectRaw: opt.collectRaw };
28977
29255
  btn.onclick = () => runInvoke();
28978
29256
  shownInvId = null;
28979
29257
  shownDetailId = null;
@@ -28998,6 +29276,8 @@ const STUDIO_SCRIPT = `
28998
29276
  const body = { targetId: id, kind, event };
28999
29277
  const options = active.collectOpts ? active.collectOpts() : undefined;
29000
29278
  if (options) body.options = options;
29279
+ const rawArgs = active.collectRaw ? active.collectRaw() : undefined;
29280
+ if (rawArgs) body.rawArgs = rawArgs;
29001
29281
  const res = await fetch('/api/run', {
29002
29282
  method: 'POST',
29003
29283
  headers: { 'content-type': 'application/json' },
@@ -29396,6 +29676,7 @@ function renderStudioHtml(appLabel, cliName) {
29396
29676
  </section>
29397
29677
  </main>
29398
29678
  <script>window.__OPTION_SPECS__ = ${JSON.stringify(OPTION_SPECS).replace(/</g, "\\u003c")};<\/script>
29679
+ <script>window.__FLAG_CATALOG__ = ${JSON.stringify(buildFlagCatalog()).replace(/</g, "\\u003c")};<\/script>
29399
29680
  <script>${STUDIO_SCRIPT}<\/script>
29400
29681
  </body>
29401
29682
  </html>`;
@@ -29452,6 +29733,33 @@ function toStudioTargetGroups(listing) {
29452
29733
  }
29453
29734
  ];
29454
29735
  }
29736
+ /**
29737
+ * Annotate the servable `ecs` service entries of `groups` with `pinned: true`
29738
+ * when `classify(targetId)` returns true (issue #301). `classify` decides
29739
+ * pinned (deployed-registry image) vs local CDK asset for one service id; the
29740
+ * caller supplies it (studio's boot does `resolveEcsServiceTarget` +
29741
+ * `isLocalCdkAssetImage`, swallowing resolution failures as "not pinned").
29742
+ * Mutates the entries in place and returns whether ANY service was pinned, so
29743
+ * the caller can skip the (otherwise pointless) Dockerfile scan for an
29744
+ * all-local-asset app. Non-ecs groups and non-servable entries (task defs) are
29745
+ * left untouched. Exported so a host CLI building its own studio can reuse the
29746
+ * same pinned-target annotation, and so the boot logic is unit-testable
29747
+ * without a real synth.
29748
+ */
29749
+ function annotatePinnedEcsTargets(groups, classify) {
29750
+ let anyPinned = false;
29751
+ for (const group of groups) {
29752
+ if (group.kind !== "ecs") continue;
29753
+ for (const entry of group.entries) {
29754
+ if (!entry.servable) continue;
29755
+ if (classify(entry.id)) {
29756
+ entry.pinned = true;
29757
+ anyPinned = true;
29758
+ }
29759
+ }
29760
+ }
29761
+ return anyPinned;
29762
+ }
29455
29763
  /** Compile a `*` / `?` glob to an anchored RegExp matched against a target id. */
29456
29764
  function globToRegExp(glob) {
29457
29765
  const escaped = glob.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
@@ -29485,7 +29793,10 @@ async function startStudioServer(options) {
29485
29793
  const host = options.host ?? "127.0.0.1";
29486
29794
  const maxBump = options.maxPortBump ?? 20;
29487
29795
  const html = renderStudioHtml(options.appLabel, options.cliName);
29488
- const targetsJson = JSON.stringify({ groups: options.targetGroups });
29796
+ const targetsJson = JSON.stringify({
29797
+ groups: options.targetGroups,
29798
+ dockerfiles: options.dockerfiles ?? []
29799
+ });
29489
29800
  const server = createServer$1((req, res) => handleRequest(req, res, options.bus, html, targetsJson, options));
29490
29801
  const boundPort = await listenWithBump(server, host, options.port, maxBump);
29491
29802
  return {
@@ -29815,6 +30126,7 @@ function createStudioDispatcher(config) {
29815
30126
  writeFileSync(envFile, JSON.stringify(envVars));
29816
30127
  args.push("--env-vars", envFile);
29817
30128
  }
30129
+ args.push(...tokenizeRawArgs(req.rawArgs));
29818
30130
  const { code, stdout, stderr } = await runChild(spawnFn, nodeBin, [config.cliEntry, ...args], config.cwd ?? process.cwd(), invocationId, req.targetId, config.bus, clock);
29819
30131
  const durationMs = clock() - startedAt;
29820
30132
  const ok = code === 0;
@@ -30300,7 +30612,9 @@ function createStudioServeManager(config) {
30300
30612
  ...spec.portArgs,
30301
30613
  ...buildSharedChildArgs(config),
30302
30614
  ...buildPerRunArgs(req.kind, req.options),
30303
- ...config.watch === true ? ["--watch"] : []
30615
+ ...req.imageOverride && req.imageOverride.trim() !== "" ? ["--image-override", req.targetId + "=" + req.imageOverride.trim()] : [],
30616
+ ...config.watch === true ? ["--watch"] : [],
30617
+ ...tokenizeRawArgs(req.rawArgs)
30304
30618
  ];
30305
30619
  }
30306
30620
  async function start(req) {
@@ -30521,7 +30835,7 @@ const STUDIO_TARGET_KINDS = [
30521
30835
  */
30522
30836
  function coerceRunRequest(body) {
30523
30837
  if (typeof body !== "object" || body === null) throw new Error("Request body must be a JSON object.");
30524
- const { targetId, kind, event, options } = body;
30838
+ const { targetId, kind, event, options, rawArgs, imageOverride } = body;
30525
30839
  if (typeof targetId !== "string" || targetId.trim() === "") throw new Error("Request body must include a non-empty \"targetId\" string.");
30526
30840
  if (typeof kind !== "string" || !STUDIO_TARGET_KINDS.includes(kind)) throw new Error(`Request body "kind" must be one of: ${STUDIO_TARGET_KINDS.join(", ")}.`);
30527
30841
  let runOptions;
@@ -30531,11 +30845,24 @@ function coerceRunRequest(body) {
30531
30845
  buildPerRunArgs(kind, runOptions);
30532
30846
  resolveEnvVars(kind, runOptions);
30533
30847
  }
30848
+ let runRawArgs;
30849
+ if (rawArgs !== void 0) {
30850
+ if (typeof rawArgs !== "string") throw new Error("Request body \"rawArgs\" must be a string.");
30851
+ tokenizeRawArgs(rawArgs);
30852
+ runRawArgs = rawArgs;
30853
+ }
30854
+ let runImageOverride;
30855
+ if (imageOverride !== void 0) {
30856
+ if (typeof imageOverride !== "string") throw new Error("Request body \"imageOverride\" must be a string.");
30857
+ if (imageOverride.trim() !== "") runImageOverride = imageOverride;
30858
+ }
30534
30859
  return {
30535
30860
  targetId,
30536
30861
  kind,
30537
30862
  event,
30538
- ...runOptions !== void 0 ? { options: runOptions } : {}
30863
+ ...runOptions !== void 0 ? { options: runOptions } : {},
30864
+ ...runRawArgs !== void 0 ? { rawArgs: runRawArgs } : {},
30865
+ ...runImageOverride !== void 0 ? { imageOverride: runImageOverride } : {}
30539
30866
  };
30540
30867
  }
30541
30868
  /**
@@ -30621,6 +30948,14 @@ async function localStudioCommand(options) {
30621
30948
  }
30622
30949
  const appLabel = stacks.map((s) => s.stackName).join(", ") || appCmd;
30623
30950
  const servableEcs = new Set(targetGroups.filter((g) => g.kind === "ecs").flatMap((g) => g.entries.filter((e) => e.servable).map((e) => e.id)));
30951
+ const dockerfiles = annotatePinnedEcsTargets(targetGroups, (id) => {
30952
+ try {
30953
+ return !isLocalCdkAssetImage(resolveEcsServiceTarget(id, stacks));
30954
+ } catch (err) {
30955
+ logger.debug(`studio: could not classify pin status for '${id}': ${err instanceof Error ? err.message : String(err)}`);
30956
+ return false;
30957
+ }
30958
+ }) ? discoverDockerfiles(process.cwd()) : [];
30624
30959
  const bus = new StudioEventBus();
30625
30960
  const childConfig = {
30626
30961
  cliEntry: process.argv[1] ?? "",
@@ -30651,6 +30986,7 @@ async function localStudioCommand(options) {
30651
30986
  port,
30652
30987
  bus,
30653
30988
  targetGroups,
30989
+ dockerfiles,
30654
30990
  appLabel,
30655
30991
  cliName: getEmbedConfig().cliName,
30656
30992
  store,
@@ -30746,5 +31082,5 @@ function addStudioSpecificOptions(cmd) {
30746
31082
  }
30747
31083
 
30748
31084
  //#endregion
30749
- export { SOFT_RELOAD_COMPLETION_LOG_SUFFIX as $, listTargets as $n, buildMethodArn as $t, addCommonEcsServiceOptions as A, architectureToPlatform as An, createWatchPredicates as At, ImageOverrideError as B, LocalStateSourceError as Bn, filterRoutesByApiIdentifiers as Bt, resolveAlbFrontDoor as C, ConnectionRegistry as Cn, resolveProfileCredentials as Cr, renderCodeDockerfile as Ct, addRunTaskSpecificOptions as D, buildConnectEvent as Dn, createLocalInvokeCommand as Dt, serviceStrategy as E, parseConnectionsPath as En, addInvokeSpecificOptions as Et, parseMaxTasks as F, EcsTaskResolutionError as Fn, buildStageMap as Ft, resolveImageOverrides as G, resolveCfnRegion as Gn, resolveServiceIntegrationParameters as Gt, enforceImageOverrideOrphans as H, isCfnFlagPresent as Hn, readMtlsMaterialsFromDisk as Ht, parseRestartPolicy as I, substituteAgainstState as In, materializeLayerFromArn as It, isLocalCdkAssetImage as J, collectSsmParameterRefs as Jn, buildJwksUrlFromIssuer as Jt, runImageOverrideBuilds as K, resolveCfnStackName as Kn, defaultCredentialsLoader as Kt, resolveEcsAssumeRoleOption as L, substituteAgainstStateAsync as Ln, resolveEnvVars$1 as Lt, addImageOverrideOptions as M, resolveRuntimeCodeMountPath as Mn, createAuthorizerCache as Mt, buildEcsImageResolutionContext$1 as N, resolveRuntimeFileExtension as Nn, createFileWatcher as Nt, createLocalRunTaskCommand as O, buildDisconnectEvent as On, addStartApiSpecificOptions as Ot, ecsClusterOption as P, resolveRuntimeImage as Pn, attachStageContext as Pt, DEFAULT_SHADOW_READY_TIMEOUT_MS as Q, countTargets as Qn, verifyJwtViaDiscovery as Qt, resolveSharedSidecarCredentials as R, substituteEnvVarsFromState as Rn, availableApiIdentifiers as Rt, isApplicationLoadBalancer as S, bufferToBody as Sn, buildStsClientConfig as Sr, computeCodeImageTag as St, createLocalStartServiceCommand as T, handleConnectionsRequest as Tn, classifySourceChange as Tt, mergeForService as U, rejectExplicitCfnStackWithMultipleStacks as Un, startApiServer as Ut, buildImageOverrideTag as V, createLocalStateProvider as Vn, groupRoutesByServer as Vt, parseImageOverrideFlags as W, resolveCfnFallbackRegion as Wn, resolveSelectionExpression as Wt, buildCloudMapIndex as X, resolveWatchConfig as Xn, verifyCognitoJwt as Xt, listPinnedTargets as Y, resolveSsmParameters as Yn, createJwksCache as Yt, CloudMapRegistry as Z, resolveSingleTarget as Zn, verifyJwtAuthorizer as Zt, addAlbSpecificOptions as _, selectIntegrationResponse as _n, derivePseudoParametersFromRegion as _r, invokeAgentCore as _t, createStudioServeManager as a, applyCorsResponseHeaders as an, webSocketApiMatchesIdentifier as ar, invokeAgentCoreWs as at, parseLbPortOverrides as b, HOST_GATEWAY_MIN_VERSION as bn, tryResolveImageFnJoin as br, SUPPORTED_CODE_RUNTIMES as bt, filterStudioTargetGroups as c, isFunctionUrlOacFronted as cn, resolveLambdaArnIntrinsic as cr, a2aInvokeOnce as ct, renderStudioHtml as d, translateLambdaResponse as dn, AGENTCORE_HTTP_PROTOCOL as dr, MCP_PROTOCOL_VERSION as dt, computeRequestIdentityHash as en, availableWebSocketApiIdentifiers as er, setShadowReadyTimeoutMs as et, createStudioStore as f, applyAuthorizerOverlay as fn, AGENTCORE_MCP_PROTOCOL as fr, mcpInvokeOnce as ft, formatTargetListing as g, pickResponseTemplate as gn, resolveAgentCoreTarget as gr, AGENTCORE_SESSION_ID_HEADER as gt, createLocalListCommand as h, evaluateResponseParameters as hn, pickAgentCoreCandidateStack as hr, signAgentCoreInvocation as ht, createLocalStudioCommand as i, attachAuthorizers as in, parseSelectionExpressionPath as ir, createLocalInvokeAgentCoreCommand as it, addEcsAssumeRoleOptions as j, buildContainerImage as jn, resolveApiTargetSubset as jt, MAX_TASKS_SUBNET_RANGE_CAP as k, buildMessageEvent as kn, createLocalStartApiCommand as kt, startStudioServer as l, matchPreflight as ln, AGENTCORE_A2A_PROTOCOL as lr, MCP_CONTAINER_PORT as lt, addListSpecificOptions as m, buildRestV1Event as mn, AgentCoreResolutionError as mr, AGENTCORE_SIGV4_SERVICE as mt, coerceRunRequest as n, invokeRequestAuthorizer as nn, discoverWebSocketApisOrThrow as nr, attachContainerLogStreamer as nt, startStudioProxy as o, buildCorsConfigByApiId as on, discoverRoutes as or, A2A_CONTAINER_PORT as ot, StudioEventBus as p, buildHttpApiV2Event as pn, AGENTCORE_RUNTIME_TYPE as pr, parseSseForJsonRpc as pt, describePinnedImageUri as q, CfnLocalStateProvider as qn, buildCognitoJwksUrl as qt, coerceStopRequest as r, invokeTokenAuthorizer as rn, filterWebSocketApisByIdentifiers as rr, addInvokeAgentCoreSpecificOptions as rt, createStudioDispatcher as s, buildCorsConfigFromCloudFrontChain as sn, pickRefLogicalId as sr, A2A_PATH as st, addStudioSpecificOptions as t, evaluateCachedLambdaPolicy as tn, discoverWebSocketApis as tr, getContainerNetworkIp as tt, toStudioTargetGroups as u, matchRoute as un, AGENTCORE_AGUI_PROTOCOL as ur, MCP_PATH as ut, albStrategy as v, tryParseStatus as vn, formatStateRemedy as vr, waitForAgentCorePing as vt, addStartServiceSpecificOptions as w, buildMgmtEndpointEnvUrl as wn, toCmdArgv as wt, resolveAlbTarget as x, probeHostGatewaySupport as xn, LocalInvokeBuildError as xr, buildAgentCoreCodeImage as xt, createLocalStartAlbCommand as y, VtlEvaluationError as yn, substituteImagePlaceholders as yr, downloadAndExtractS3Bundle as yt, runEcsServiceEmulator as z, substituteEnvVarsFromStateAsync as zn, filterRoutesByApiIdentifier as zt };
30750
- //# sourceMappingURL=local-studio-MTJUk4NF.js.map
31085
+ export { DEFAULT_SHADOW_READY_TIMEOUT_MS as $, countTargets as $n, verifyJwtViaDiscovery as $t, MAX_TASKS_SUBNET_RANGE_CAP as A, buildMessageEvent as An, createLocalStartApiCommand as At, runEcsServiceEmulator as B, substituteEnvVarsFromStateAsync as Bn, filterRoutesByApiIdentifier as Bt, isApplicationLoadBalancer as C, bufferToBody as Cn, buildStsClientConfig as Cr, computeCodeImageTag as Ct, serviceStrategy as D, parseConnectionsPath as Dn, addInvokeSpecificOptions as Dt, createLocalStartServiceCommand as E, handleConnectionsRequest as En, classifySourceChange as Et, ecsClusterOption as F, resolveRuntimeImage as Fn, attachStageContext as Ft, parseImageOverrideFlags as G, resolveCfnFallbackRegion as Gn, resolveSelectionExpression as Gt, buildImageOverrideTag as H, createLocalStateProvider as Hn, groupRoutesByServer as Ht, parseMaxTasks as I, EcsTaskResolutionError as In, buildStageMap as It, describePinnedImageUri as J, CfnLocalStateProvider as Jn, buildCognitoJwksUrl as Jt, resolveImageOverrides as K, resolveCfnRegion as Kn, resolveServiceIntegrationParameters as Kt, parseRestartPolicy as L, substituteAgainstState as Ln, materializeLayerFromArn as Lt, addEcsAssumeRoleOptions as M, buildContainerImage as Mn, resolveApiTargetSubset as Mt, addImageOverrideOptions as N, resolveRuntimeCodeMountPath as Nn, createAuthorizerCache as Nt, addRunTaskSpecificOptions as O, buildConnectEvent as On, createLocalInvokeCommand as Ot, buildEcsImageResolutionContext$1 as P, resolveRuntimeFileExtension as Pn, createFileWatcher as Pt, CloudMapRegistry as Q, resolveSingleTarget as Qn, verifyJwtAuthorizer as Qt, resolveEcsAssumeRoleOption as R, substituteAgainstStateAsync as Rn, resolveEnvVars$1 as Rt, resolveAlbTarget as S, probeHostGatewaySupport as Sn, LocalInvokeBuildError as Sr, buildAgentCoreCodeImage as St, addStartServiceSpecificOptions as T, buildMgmtEndpointEnvUrl as Tn, toCmdArgv as Tt, enforceImageOverrideOrphans as U, isCfnFlagPresent as Un, readMtlsMaterialsFromDisk as Ut, ImageOverrideError as V, LocalStateSourceError as Vn, filterRoutesByApiIdentifiers as Vt, mergeForService as W, rejectExplicitCfnStackWithMultipleStacks as Wn, startApiServer as Wt, listPinnedTargets as X, resolveSsmParameters as Xn, createJwksCache as Xt, isLocalCdkAssetImage as Y, collectSsmParameterRefs as Yn, buildJwksUrlFromIssuer as Yt, buildCloudMapIndex as Z, resolveWatchConfig as Zn, verifyCognitoJwt as Zt, formatTargetListing as _, pickResponseTemplate as _n, resolveAgentCoreTarget as _r, AGENTCORE_SESSION_ID_HEADER as _t, createStudioServeManager as a, attachAuthorizers as an, parseSelectionExpressionPath as ar, createLocalInvokeAgentCoreCommand as at, createLocalStartAlbCommand as b, VtlEvaluationError as bn, substituteImagePlaceholders as br, downloadAndExtractS3Bundle as bt, annotatePinnedEcsTargets as c, buildCorsConfigFromCloudFrontChain as cn, pickRefLogicalId as cr, A2A_PATH as ct, toStudioTargetGroups as d, matchRoute as dn, AGENTCORE_AGUI_PROTOCOL as dr, MCP_PATH as dt, buildMethodArn as en, listTargets as er, SOFT_RELOAD_COMPLETION_LOG_SUFFIX as et, renderStudioHtml as f, translateLambdaResponse as fn, AGENTCORE_HTTP_PROTOCOL as fr, MCP_PROTOCOL_VERSION as ft, createLocalListCommand as g, evaluateResponseParameters as gn, pickAgentCoreCandidateStack as gr, signAgentCoreInvocation as gt, addListSpecificOptions as h, buildRestV1Event as hn, AgentCoreResolutionError as hr, AGENTCORE_SIGV4_SERVICE as ht, createLocalStudioCommand as i, invokeTokenAuthorizer as in, filterWebSocketApisByIdentifiers as ir, addInvokeAgentCoreSpecificOptions as it, addCommonEcsServiceOptions as j, architectureToPlatform as jn, createWatchPredicates as jt, createLocalRunTaskCommand as k, buildDisconnectEvent as kn, addStartApiSpecificOptions as kt, filterStudioTargetGroups as l, isFunctionUrlOacFronted as ln, resolveLambdaArnIntrinsic as lr, a2aInvokeOnce as lt, StudioEventBus as m, buildHttpApiV2Event as mn, AGENTCORE_RUNTIME_TYPE as mr, parseSseForJsonRpc as mt, coerceRunRequest as n, evaluateCachedLambdaPolicy as nn, discoverWebSocketApis as nr, getContainerNetworkIp as nt, startStudioProxy as o, applyCorsResponseHeaders as on, webSocketApiMatchesIdentifier as or, invokeAgentCoreWs as ot, createStudioStore as p, applyAuthorizerOverlay as pn, AGENTCORE_MCP_PROTOCOL as pr, mcpInvokeOnce as pt, runImageOverrideBuilds as q, resolveCfnStackName as qn, defaultCredentialsLoader as qt, coerceStopRequest as r, invokeRequestAuthorizer as rn, discoverWebSocketApisOrThrow as rr, attachContainerLogStreamer as rt, createStudioDispatcher as s, buildCorsConfigByApiId as sn, discoverRoutes as sr, A2A_CONTAINER_PORT as st, addStudioSpecificOptions as t, computeRequestIdentityHash as tn, availableWebSocketApiIdentifiers as tr, setShadowReadyTimeoutMs as tt, startStudioServer as u, matchPreflight as un, AGENTCORE_A2A_PROTOCOL as ur, MCP_CONTAINER_PORT as ut, addAlbSpecificOptions as v, selectIntegrationResponse as vn, derivePseudoParametersFromRegion as vr, invokeAgentCore as vt, resolveAlbFrontDoor as w, ConnectionRegistry as wn, resolveProfileCredentials as wr, renderCodeDockerfile as wt, parseLbPortOverrides as x, HOST_GATEWAY_MIN_VERSION as xn, tryResolveImageFnJoin as xr, SUPPORTED_CODE_RUNTIMES as xt, albStrategy as y, tryParseStatus as yn, formatStateRemedy as yr, waitForAgentCorePing as yt, resolveSharedSidecarCredentials as z, substituteEnvVarsFromState as zn, availableApiIdentifiers as zt };
31086
+ //# sourceMappingURL=local-studio-DMYaMyCf.js.map