cdk-local 0.97.1 → 0.99.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.
@@ -28525,6 +28525,21 @@ const STUDIO_CSS = `
28525
28525
  border: 0; border-radius: 3px; cursor: pointer; font: inherit; font-weight: 700;
28526
28526
  }
28527
28527
  .composer button:hover { background: #339152; }
28528
+ .req-composer .req-row { display: flex; gap: 6px; margin-bottom: 6px; }
28529
+ .req-composer .req-method { background: #111; color: #ddd; border: 1px solid #333; border-radius: 3px;
28530
+ padding: 4px 6px; font: 12px ui-monospace, Menlo, monospace; }
28531
+ .req-composer .req-path { flex: 1; min-width: 0; background: #111; color: #ddd; border: 1px solid #333;
28532
+ border-radius: 3px; padding: 4px 6px; font: 12px ui-monospace, Menlo, monospace; }
28533
+ .req-composer textarea { width: 100%; box-sizing: border-box; min-height: 48px; resize: vertical;
28534
+ background: #111; color: #ddd; border: 1px solid #333; border-radius: 3px; padding: 5px;
28535
+ font: 12px/1.5 ui-monospace, Menlo, monospace; margin-bottom: 6px; }
28536
+ .req-composer .req-body { min-height: 70px; }
28537
+ .req-composer select:focus, .req-composer input:focus, .req-composer textarea:focus {
28538
+ outline: none; border-color: #4ec97a; }
28539
+ .req-composer .req-send { display: flex; align-items: center; gap: 10px; }
28540
+ .req-composer .req-send button { margin-top: 0; padding: 4px 16px; }
28541
+ .req-composer .req-status { margin-top: 8px; font: 12px ui-monospace, Menlo, monospace; }
28542
+ .req-composer .req-result pre { background: #0e0e0e; }
28528
28543
  .composer button:disabled { background: #333; color: #888; cursor: default; }
28529
28544
  .composer .err { color: #e0707a; margin-top: 6px; min-height: 18px; }
28530
28545
  .section { padding: 8px 12px; border-bottom: 1px solid #222; }
@@ -29143,6 +29158,12 @@ const STUDIO_SCRIPT = `
29143
29158
  const link = href(url);
29144
29159
  epSec.appendChild(link);
29145
29160
  }
29161
+ } else if (running && isEcs && st.hostUrl) {
29162
+ // An ecs service published via --host-port IS reachable on the host
29163
+ // (issue #322); show its host URL. No proxy fronts it, so requests are
29164
+ // not captured on the timeline.
29165
+ epSec.appendChild(href(st.hostUrl));
29166
+ epSec.appendChild(el('div', 'opt-hint', '(direct host port — not captured on the timeline)'));
29146
29167
  } else if (running && isEcs) {
29147
29168
  // A pure-compute ECS service has no host endpoint — it just runs the
29148
29169
  // replicas (reach them container-to-container via Cloud Map).
@@ -29152,6 +29173,18 @@ const STUDIO_SCRIPT = `
29152
29173
  }
29153
29174
  ws.appendChild(epSec);
29154
29175
 
29176
+ // In-workspace HTTP request composer for a running api / alb (or ecs with
29177
+ // --host-port) serve (issue #322): compose a request and Send it; studio
29178
+ // relays it server-side (same-origin) so it works cross-port and, for
29179
+ // api / alb, lands on the timeline via the capture proxy.
29180
+ const httpBase = running
29181
+ ? (st.endpoints || []).find((u) => /^https?:/.test(u)) || (isEcs ? st.hostUrl : null)
29182
+ : null;
29183
+ if (httpBase) {
29184
+ const captured = (st.endpoints || []).indexOf(httpBase) >= 0;
29185
+ ws.appendChild(renderRequestComposer(id, httpBase, captured));
29186
+ }
29187
+
29155
29188
  // A served WebSocket API exposes a ws:// endpoint — attach a WebSocket
29156
29189
  // console so the browser can connect + exchange frames (issue #303).
29157
29190
  const wsEndpoint = running ? (st.endpoints || []).find((u) => /^wss?:/.test(u)) : null;
@@ -29166,6 +29199,111 @@ const STUDIO_SCRIPT = `
29166
29199
  ws.appendChild(logSec);
29167
29200
  }
29168
29201
 
29202
+ // In-workspace HTTP request composer for a running serve (issue #322):
29203
+ // Method + Path + Headers + Body -> Send. The request is relayed by studio's
29204
+ // OWN server (POST /api/request) so it reaches the served port without a
29205
+ // cross-origin fetch; for an api / alb serve it flows through the capture
29206
+ // proxy and lands on the timeline.
29207
+ const REQ_METHODS = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS'];
29208
+
29209
+ function parseHeaderLines(text) {
29210
+ const out = {};
29211
+ String(text || '').split('\\n').forEach(function (line) {
29212
+ const i = line.indexOf(':');
29213
+ if (i <= 0) return;
29214
+ const k = line.slice(0, i).trim();
29215
+ if (k) out[k] = line.slice(i + 1).trim();
29216
+ });
29217
+ return out;
29218
+ }
29219
+
29220
+ function renderRequestComposer(id, baseUrl, captured) {
29221
+ const sec = el('div', 'section req-composer');
29222
+ sec.appendChild(el('h3', null, 'Request'));
29223
+ const row = el('div', 'req-row');
29224
+ const method = el('select', 'req-method');
29225
+ REQ_METHODS.forEach(function (m) {
29226
+ const o = el('option', null, m);
29227
+ o.value = m;
29228
+ method.appendChild(o);
29229
+ });
29230
+ row.appendChild(method);
29231
+ const path = el('input', 'req-path');
29232
+ path.value = '/';
29233
+ path.placeholder = '/path?query';
29234
+ row.appendChild(path);
29235
+ sec.appendChild(row);
29236
+
29237
+ sec.appendChild(el('div', 'opt-label', 'Headers (one per line: Name: value)'));
29238
+ const headers = el('textarea', 'req-headers');
29239
+ headers.placeholder = 'Authorization: Bearer demo';
29240
+ headers.spellcheck = false;
29241
+ sec.appendChild(headers);
29242
+
29243
+ sec.appendChild(el('div', 'opt-label', 'Body'));
29244
+ const bodyTa = el('textarea', 'req-body');
29245
+ bodyTa.placeholder = '{ }';
29246
+ bodyTa.spellcheck = false;
29247
+ sec.appendChild(bodyTa);
29248
+
29249
+ const sendRow = el('div', 'req-send');
29250
+ const btn = el('button', null, 'Send');
29251
+ sendRow.appendChild(btn);
29252
+ sendRow.appendChild(
29253
+ el('span', 'opt-hint', captured
29254
+ ? 'Relayed via studio to ' + baseUrl + ' — captured on the timeline.'
29255
+ : 'Relayed direct to ' + baseUrl + ' (ecs host port) — not captured.')
29256
+ );
29257
+ sec.appendChild(sendRow);
29258
+ const msg = el('div', 'err');
29259
+ sec.appendChild(msg);
29260
+ const result = el('div', 'req-result');
29261
+ sec.appendChild(result);
29262
+
29263
+ btn.onclick = async function () {
29264
+ msg.textContent = '';
29265
+ btn.disabled = true;
29266
+ btn.textContent = 'Sending…';
29267
+ result.innerHTML = '';
29268
+ try {
29269
+ const payload = {
29270
+ targetId: id,
29271
+ method: method.value,
29272
+ path: path.value || '/',
29273
+ headers: parseHeaderLines(headers.value),
29274
+ };
29275
+ if (bodyTa.value !== '') payload.body = bodyTa.value;
29276
+ const res = await fetch('/api/request', {
29277
+ method: 'POST',
29278
+ headers: { 'content-type': 'application/json' },
29279
+ body: JSON.stringify(payload),
29280
+ });
29281
+ const data = await res.json();
29282
+ if (!res.ok) {
29283
+ msg.textContent = 'Request failed: ' + (data.error || ('HTTP ' + res.status));
29284
+ return;
29285
+ }
29286
+ const statusLine = el('div', 'req-status');
29287
+ const cls = data.status >= 200 && data.status < 300 ? 'ok' : 'bad';
29288
+ statusLine.appendChild(
29289
+ el('span', cls, data.status + (data.durationMs != null ? ' · ' + data.durationMs + 'ms' : ''))
29290
+ );
29291
+ result.appendChild(statusLine);
29292
+ const hdrs = Object.keys(data.headers || {})
29293
+ .map(function (k) { return k + ': ' + data.headers[k]; })
29294
+ .join('\\n');
29295
+ if (hdrs) result.appendChild(el('pre', 'req-resp-headers', hdrs));
29296
+ result.appendChild(el('pre', null, data.body != null ? data.body : ''));
29297
+ } catch (err) {
29298
+ msg.textContent = 'Request failed: ' + err;
29299
+ } finally {
29300
+ btn.disabled = false;
29301
+ btn.textContent = 'Send';
29302
+ }
29303
+ };
29304
+ return sec;
29305
+ }
29306
+
29169
29307
  // Build an <a> that opens an http(s) endpoint in a new tab; ws:// URLs
29170
29308
  // are shown as plain text (not navigable in a browser tab — the WebSocket
29171
29309
  // console below connects to them instead).
@@ -29601,7 +29739,7 @@ const STUDIO_SCRIPT = `
29601
29739
  if (ev.status === 'stopped' || ev.status === 'error') {
29602
29740
  serveState.set(ev.target, { status: ev.status, endpoints: [] });
29603
29741
  } else {
29604
- serveState.set(ev.target, { status: ev.status, endpoints: ev.endpoints || [] });
29742
+ serveState.set(ev.target, { status: ev.status, endpoints: ev.endpoints || [], hostUrl: ev.hostUrl });
29605
29743
  }
29606
29744
  updateServeRow(ev.target);
29607
29745
  }
@@ -29969,6 +30107,10 @@ function handleRequest(req, res, bus, html, targetsJson, options) {
29969
30107
  handleDispatch(req, res, options.onRun);
29970
30108
  return;
29971
30109
  }
30110
+ if (req.method === "POST" && path === "/api/request") {
30111
+ handleDispatch(req, res, options.onServeRequest);
30112
+ return;
30113
+ }
29972
30114
  if (req.method === "POST" && path === "/api/stop") {
29973
30115
  handleDispatch(req, res, options.onStop);
29974
30116
  return;
@@ -30111,6 +30253,88 @@ function listenWithBump(server, host, port, maxBump) {
30111
30253
  });
30112
30254
  }
30113
30255
 
30256
+ //#endregion
30257
+ //#region src/local/studio-custom-resource-filter.ts
30258
+ /**
30259
+ * Substrings / patterns that identify a Lambda as part of CDK's custom-resource
30260
+ * or provider-framework plumbing rather than the user's own application code.
30261
+ *
30262
+ * Matched case-INSENSITIVELY against a target's display path / qualified id.
30263
+ * Each entry corresponds to a well-known CDK-generated construct:
30264
+ *
30265
+ * - `framework-onEvent` / `framework-onTimeout` / `framework-isComplete` —
30266
+ * the provider-framework (`@aws-cdk/custom-resources` `Provider`) handlers.
30267
+ * - `/Provider/` — the `Provider` construct segment that wraps a custom
30268
+ * resource's lifecycle Lambdas.
30269
+ * - `LogRetention` — the singleton log-retention custom resource CDK injects
30270
+ * when a construct sets `logRetention`.
30271
+ * - `BucketNotificationsHandler` — the S3 bucket-notifications custom resource.
30272
+ * - `AwsCustomResource` — the `aws-custom-resource` SDK-call construct.
30273
+ * - `AWS679f53fac002430cb0da5b7982bd2287` — the singleton logical id CDK emits
30274
+ * for the `AwsCustomResource` provider Lambda.
30275
+ * - `CustomResourceProvider` — the low-level `CustomResourceProvider` framework
30276
+ * Lambda (used by many L2 constructs).
30277
+ * - `cdkbucketdeployment` — the `BucketDeployment` asset-copy Lambda (the
30278
+ * logical id is `CustomCDKBucketDeployment<hash>`, the construct path node is
30279
+ * `Custom::CDKBucketDeployment<hash>` — the shared substring covers both).
30280
+ * - `CDKMetadata` — the CDK metadata resource (not a Lambda, but cheap to
30281
+ * exclude defensively if it ever surfaces as one).
30282
+ */
30283
+ const CUSTOM_RESOURCE_PATTERNS = [
30284
+ "framework-onevent",
30285
+ "framework-ontimeout",
30286
+ "framework-iscomplete",
30287
+ "/provider/",
30288
+ "logretention",
30289
+ "bucketnotificationshandler",
30290
+ "awscustomresource",
30291
+ "aws679f53fac002430cb0da5b7982bd2287",
30292
+ "customresourceprovider",
30293
+ "cdkbucketdeployment",
30294
+ "cdkmetadata"
30295
+ ];
30296
+ /**
30297
+ * Classify a studio Lambda target as a CDK custom-resource / provider-framework
30298
+ * Lambda (vs the user's own application code). Matches the target's display
30299
+ * path / qualified id against {@link CUSTOM_RESOURCE_PATTERNS}
30300
+ * case-insensitively.
30301
+ *
30302
+ * Host-side use case: a host CLI building its own studio (or `cdkl list`-style
30303
+ * UI) reuses this to hide CDK-generated plumbing Lambdas — provider-framework
30304
+ * onEvent/onTimeout/isComplete handlers, log-retention, bucket-notifications,
30305
+ * AwsCustomResource, BucketDeployment — from the default target list, so the
30306
+ * user sees only their own functions. Pure / side-effect-free.
30307
+ */
30308
+ function isCustomResourceLambdaTarget(entry) {
30309
+ const haystack = `${entry.id} ${entry.qualifiedId}`.toLowerCase();
30310
+ return CUSTOM_RESOURCE_PATTERNS.some((p) => haystack.includes(p));
30311
+ }
30312
+ /**
30313
+ * Drop CDK custom-resource / provider-framework Lambdas from the `lambda` group
30314
+ * of `groups` (issue #323). Only the `lambda` group is touched — every other
30315
+ * group (api / ecs / alb / agentcore) is returned unchanged — and within it
30316
+ * only entries {@link isCustomResourceLambdaTarget} matches are removed.
30317
+ *
30318
+ * Pass `{ include: true }` to keep them (the `--include-custom-resources`
30319
+ * opt-in), in which case `groups` is returned unchanged.
30320
+ *
30321
+ * Host-side use case: `cdkl studio` (and any host CLI embedding the studio
30322
+ * building blocks) applies this after the `--stack` display filter so the
30323
+ * default target list shows only the user's own Lambdas, not CDK's generated
30324
+ * plumbing. Returns a new array (the matching group is rebuilt); inputs are not
30325
+ * mutated.
30326
+ */
30327
+ function filterStudioCustomResources(groups, opts = {}) {
30328
+ if (opts.include) return groups;
30329
+ return groups.map((g) => {
30330
+ if (g.kind !== "lambda") return g;
30331
+ return {
30332
+ ...g,
30333
+ entries: g.entries.filter((e) => !isCustomResourceLambdaTarget(e))
30334
+ };
30335
+ });
30336
+ }
30337
+
30114
30338
  //#endregion
30115
30339
  //#region src/local/studio-child-args.ts
30116
30340
  /**
@@ -30417,6 +30641,62 @@ function tryParseJson(raw) {
30417
30641
  }
30418
30642
  }
30419
30643
 
30644
+ //#endregion
30645
+ //#region src/local/studio-request-relay.ts
30646
+ const DEFAULT_TIMEOUT_MS = 3e4;
30647
+ const DEFAULT_MAX_BODY = 512 * 1024;
30648
+ const BODY_METHODS = new Set([
30649
+ "POST",
30650
+ "PUT",
30651
+ "PATCH",
30652
+ "DELETE"
30653
+ ]);
30654
+ /** Join a base URL and a path without doubling or dropping the `/`. */
30655
+ function joinUrl(baseUrl, path) {
30656
+ const base = baseUrl.replace(/\/+$/, "");
30657
+ if (!path || path === "") return base + "/";
30658
+ return base + (path.startsWith("/") ? path : "/" + path);
30659
+ }
30660
+ /**
30661
+ * Perform one HTTP request against a running serve and return a bounded
30662
+ * response. `fetchFn` is injectable for tests (defaults to the global `fetch`).
30663
+ * Throws on a network / timeout error (the caller maps it to a 502/500); an
30664
+ * HTTP error status is a NORMAL result (returned, not thrown).
30665
+ */
30666
+ async function relayServeRequest(input, fetchFn = fetch, clock = Date.now) {
30667
+ const method = input.method.toUpperCase();
30668
+ const url = joinUrl(input.baseUrl, input.path);
30669
+ const maxBody = input.maxBodyChars ?? DEFAULT_MAX_BODY;
30670
+ const controller = new AbortController();
30671
+ const timer = setTimeout(() => controller.abort(), input.timeoutMs ?? DEFAULT_TIMEOUT_MS);
30672
+ const startedAt = clock();
30673
+ try {
30674
+ const res = await fetchFn(url, {
30675
+ method,
30676
+ headers: input.headers ?? {},
30677
+ ...BODY_METHODS.has(method) && input.body !== void 0 ? { body: input.body } : {},
30678
+ signal: controller.signal,
30679
+ redirect: "manual"
30680
+ });
30681
+ const headers = {};
30682
+ res.headers.forEach((value, key) => {
30683
+ headers[key] = value;
30684
+ });
30685
+ const full = await res.text();
30686
+ const truncated = full.length > maxBody;
30687
+ const body = truncated ? full.slice(0, maxBody) + "\n…[truncated]" : full;
30688
+ return {
30689
+ status: res.status,
30690
+ headers,
30691
+ body,
30692
+ truncated,
30693
+ durationMs: clock() - startedAt
30694
+ };
30695
+ } finally {
30696
+ clearTimeout(timer);
30697
+ }
30698
+ }
30699
+
30420
30700
  //#endregion
30421
30701
  //#region src/local/studio-proxy.ts
30422
30702
  let proxyIdCounter = 0;
@@ -30695,6 +30975,7 @@ function createStudioServeManager(config) {
30695
30975
  startedAt: e.startedAt
30696
30976
  };
30697
30977
  if (e.pid !== void 0) s.pid = e.pid;
30978
+ if (e.hostUrl !== void 0) s.hostUrl = e.hostUrl;
30698
30979
  return s;
30699
30980
  }
30700
30981
  function emitServe(e, message) {
@@ -30706,6 +30987,7 @@ function createStudioServeManager(config) {
30706
30987
  endpoints: [...e.endpoints]
30707
30988
  };
30708
30989
  if (e.pid !== void 0) ev.pid = e.pid;
30990
+ if (e.hostUrl !== void 0) ev.hostUrl = e.hostUrl;
30709
30991
  if (message !== void 0) ev.message = message;
30710
30992
  config.bus.emit("serve", ev);
30711
30993
  }
@@ -30744,6 +31026,13 @@ function createStudioServeManager(config) {
30744
31026
  proxies: []
30745
31027
  };
30746
31028
  if (child.pid !== void 0) entry.pid = child.pid;
31029
+ if (req.kind === "ecs") {
31030
+ const hp = req.options?.["--host-port"];
31031
+ if (Array.isArray(hp)) {
31032
+ const first = hp.find((r) => r && typeof r === "object" && typeof r.right === "string" && r.right.trim() !== "");
31033
+ if (first) entry.hostUrl = "http://127.0.0.1:" + first.right.trim();
31034
+ }
31035
+ }
30747
31036
  entries.set(req.targetId, entry);
30748
31037
  emitServe(entry);
30749
31038
  return new Promise((resolve, reject) => {
@@ -30981,6 +31270,60 @@ function coerceStopRequest(body) {
30981
31270
  if (typeof targetId !== "string" || targetId.trim() === "") throw new Error("Request body must include a non-empty \"targetId\" string.");
30982
31271
  return { targetId };
30983
31272
  }
31273
+ const SERVE_REQUEST_METHODS = new Set([
31274
+ "GET",
31275
+ "POST",
31276
+ "PUT",
31277
+ "PATCH",
31278
+ "DELETE",
31279
+ "HEAD",
31280
+ "OPTIONS"
31281
+ ]);
31282
+ /**
31283
+ * Pick the HTTP base URL the request composer relays to for a running serve
31284
+ * (issue #322): the first `http(s)://` endpoint (the api / alb capture-proxy
31285
+ * URL — a `ws://` WebSocket-API endpoint is NOT relayable, so it is skipped),
31286
+ * else the ecs `--host-port` host URL, else `undefined` (no reachable HTTP
31287
+ * endpoint). Exported so the relay base-URL choice is unit-testable.
31288
+ */
31289
+ function resolveServeBaseUrl(state) {
31290
+ return (state.endpoints || []).find((u) => /^https?:/.test(u)) ?? state.hostUrl;
31291
+ }
31292
+ /**
31293
+ * Validate + narrow the untyped `POST /api/request` body (issue #322). Throws
31294
+ * on a malformed body; the studio server surfaces a thrown handler error as a
31295
+ * 500 (the same convention as {@link coerceRunRequest} / {@link
31296
+ * coerceStopRequest}) so a bad UI / curl payload fails loudly rather than
31297
+ * relaying a bogus request.
31298
+ */
31299
+ function coerceServeRequest(body) {
31300
+ if (typeof body !== "object" || body === null) throw new Error("Request body must be a JSON object.");
31301
+ const { targetId, method, path, headers, body: reqBody } = body;
31302
+ if (typeof targetId !== "string" || targetId.trim() === "") throw new Error("Request body must include a non-empty \"targetId\" string.");
31303
+ if (typeof method !== "string" || !SERVE_REQUEST_METHODS.has(method.toUpperCase())) throw new Error(`Request body "method" must be one of: ${[...SERVE_REQUEST_METHODS].join(", ")}.`);
31304
+ const out = {
31305
+ targetId,
31306
+ method: method.toUpperCase()
31307
+ };
31308
+ if (path !== void 0) {
31309
+ if (typeof path !== "string") throw new Error("Request body \"path\" must be a string.");
31310
+ out.path = path;
31311
+ }
31312
+ if (headers !== void 0) {
31313
+ if (typeof headers !== "object" || headers === null || Array.isArray(headers)) throw new Error("Request body \"headers\" must be a JSON object of string values.");
31314
+ const h = {};
31315
+ for (const [k, v] of Object.entries(headers)) {
31316
+ if (typeof v !== "string") throw new Error(`Request header "${k}" must be a string.`);
31317
+ if (k.trim() !== "") h[k] = v;
31318
+ }
31319
+ out.headers = h;
31320
+ }
31321
+ if (reqBody !== void 0) {
31322
+ if (typeof reqBody !== "string") throw new Error("Request body \"body\" must be a string.");
31323
+ out.body = reqBody;
31324
+ }
31325
+ return out;
31326
+ }
30984
31327
  /**
30985
31328
  * Validate a `PATCH /api/config` body and apply the editable run-time
30986
31329
  * bindings (`fromCfnStack` / `assumeRole`) onto `target` in place. Only the
@@ -31078,7 +31421,13 @@ async function localStudioCommand(options) {
31078
31421
  };
31079
31422
  const { stacks } = await synthesizer.synthesize(synthOpts);
31080
31423
  const assemblyDir = resolveBootAssemblyDir(appCmd, options.output);
31081
- const targetGroups = filterStudioTargetGroups(toStudioTargetGroups(listTargets(stacks)), options.stack);
31424
+ const stackFiltered = filterStudioTargetGroups(toStudioTargetGroups(listTargets(stacks)), options.stack);
31425
+ const lambdasBefore = stackFiltered.find((g) => g.kind === "lambda")?.entries.length ?? 0;
31426
+ const targetGroups = filterStudioCustomResources(stackFiltered, { include: options.includeCustomResources === true });
31427
+ if (!options.includeCustomResources) {
31428
+ const hidden = lambdasBefore - (targetGroups.find((g) => g.kind === "lambda")?.entries.length ?? 0);
31429
+ if (hidden > 0) logger.info(`Hid ${hidden} CDK custom-resource / provider Lambda(s); pass --include-custom-resources to show them.`);
31430
+ }
31082
31431
  if (options.stack && options.stack.length > 0) {
31083
31432
  const shown = targetGroups.reduce((n, g) => n + g.entries.length, 0);
31084
31433
  if (shown === 0) logger.warn(`--stack ${options.stack.join(" ")} matched no targets; the UI list is empty.`);
@@ -31140,6 +31489,20 @@ async function localStudioCommand(options) {
31140
31489
  await serveManager.stop(req);
31141
31490
  return { stopped: req.targetId };
31142
31491
  },
31492
+ onServeRequest: async (body) => {
31493
+ const req = coerceServeRequest(body);
31494
+ const state = serveManager.list().find((s) => s.targetId === req.targetId);
31495
+ if (!state || state.status !== "running") throw new Error(`'${req.targetId}' is not a running serve target.`);
31496
+ const baseUrl = resolveServeBaseUrl(state);
31497
+ if (!baseUrl) throw new Error(`'${req.targetId}' has no reachable HTTP endpoint (an ecs service needs --host-port).`);
31498
+ return await relayServeRequest({
31499
+ baseUrl,
31500
+ method: req.method,
31501
+ ...req.path !== void 0 ? { path: req.path } : {},
31502
+ ...req.headers !== void 0 ? { headers: req.headers } : {},
31503
+ ...req.body !== void 0 ? { body: req.body } : {}
31504
+ });
31505
+ },
31143
31506
  getRunning: () => ({ running: serveManager.list() }),
31144
31507
  getConfig: () => sessionConfigSnapshot(),
31145
31508
  patchConfig: (body) => {
@@ -31217,9 +31580,10 @@ function addStudioSpecificOptions(cmd) {
31217
31580
  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."));
31218
31581
  cmd.addOption(new Option("--stack <glob...>", "Filter the DISPLAYED targets by stack glob (e.g. \"dev/*\"); a target id is \"Stack/Construct\". Display-only — does NOT scope synth (the whole app is still synthesized; gate synth with the app's own -c context or a committed cdk.context.json). Space-separate multiple globs; a target matching ANY glob is shown."));
31219
31582
  cmd.addOption(new Option("--watch", "Spawn serves started from the UI (start-api / start-alb / start-service) with --watch, so they re-synth + rolling-reload on CDK source changes. Toggleable from the Session bar. No effect on single-shot invokes (each invoke re-synths anyway); the target list is not re-synthed (restart studio to pick up newly-added resources)."));
31583
+ cmd.addOption(new Option("--include-custom-resources", "Show CDK custom-resource / provider-framework Lambdas in the target list (provider framework onEvent/onTimeout/isComplete handlers, log-retention, bucket-notifications, AwsCustomResource, BucketDeployment, etc.). Hidden by default so the list shows only your own functions."));
31220
31584
  return cmd;
31221
31585
  }
31222
31586
 
31223
31587
  //#endregion
31224
- 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 };
31225
- //# sourceMappingURL=local-studio-DFo7-QhO.js.map
31588
+ export { describePinnedImageUri as $, CfnLocalStateProvider as $n, buildCognitoJwksUrl as $t, addStartServiceSpecificOptions as A, buildMgmtEndpointEnvUrl as An, toCmdArgv as At, ecsClusterOption as B, resolveRuntimeImage as Bn, attachStageContext as Bt, addAlbSpecificOptions as C, selectIntegrationResponse as Cn, derivePseudoParametersFromRegion as Cr, invokeAgentCore as Ct, resolveAlbTarget as D, probeHostGatewaySupport as Dn, LocalInvokeBuildError as Dr, buildAgentCoreCodeImage as Dt, parseLbPortOverrides as E, HOST_GATEWAY_MIN_VERSION as En, tryResolveImageFnJoin as Er, SUPPORTED_CODE_RUNTIMES as Et, MAX_TASKS_SUBNET_RANGE_CAP as F, buildMessageEvent as Fn, createLocalStartApiCommand as Ft, runEcsServiceEmulator as G, substituteEnvVarsFromStateAsync as Gn, filterRoutesByApiIdentifier as Gt, parseRestartPolicy as H, substituteAgainstState as Hn, materializeLayerFromArn as Ht, addCommonEcsServiceOptions as I, architectureToPlatform as In, createWatchPredicates as It, enforceImageOverrideOrphans as J, isCfnFlagPresent as Jn, readMtlsMaterialsFromDisk as Jt, ImageOverrideError as K, LocalStateSourceError as Kn, filterRoutesByApiIdentifiers as Kt, addEcsAssumeRoleOptions as L, buildContainerImage as Ln, resolveApiTargetSubset as Lt, serviceStrategy as M, parseConnectionsPath as Mn, addInvokeSpecificOptions as Mt, addRunTaskSpecificOptions as N, buildConnectEvent as Nn, createLocalInvokeCommand as Nt, isApplicationLoadBalancer as O, bufferToBody as On, buildStsClientConfig as Or, computeCodeImageTag as Ot, createLocalRunTaskCommand as P, buildDisconnectEvent as Pn, addStartApiSpecificOptions as Pt, runImageOverrideBuilds as Q, resolveCfnStackName as Qn, defaultCredentialsLoader as Qt, addImageOverrideOptions as R, resolveRuntimeCodeMountPath as Rn, createAuthorizerCache as Rt, formatTargetListing as S, pickResponseTemplate as Sn, resolveAgentCoreTarget as Sr, AGENTCORE_SESSION_ID_HEADER as St, createLocalStartAlbCommand as T, VtlEvaluationError as Tn, substituteImagePlaceholders as Tr, downloadAndExtractS3Bundle as Tt, resolveEcsAssumeRoleOption as U, substituteAgainstStateAsync as Un, resolveEnvVars$1 as Ut, parseMaxTasks as V, EcsTaskResolutionError as Vn, buildStageMap as Vt, resolveSharedSidecarCredentials as W, substituteEnvVarsFromState as Wn, availableApiIdentifiers as Wt, parseImageOverrideFlags as X, resolveCfnFallbackRegion as Xn, resolveSelectionExpression as Xt, mergeForService as Y, rejectExplicitCfnStackWithMultipleStacks as Yn, startApiServer as Yt, resolveImageOverrides as Z, resolveCfnRegion as Zn, resolveServiceIntegrationParameters as Zt, renderStudioHtml as _, translateLambdaResponse as _n, AGENTCORE_HTTP_PROTOCOL as _r, MCP_PROTOCOL_VERSION as _t, createLocalStudioCommand as a, buildMethodArn as an, listTargets as ar, SOFT_RELOAD_COMPLETION_LOG_SUFFIX as at, addListSpecificOptions as b, buildRestV1Event as bn, AgentCoreResolutionError as br, AGENTCORE_SIGV4_SERVICE as bt, startStudioProxy as c, invokeRequestAuthorizer as cn, discoverWebSocketApisOrThrow as cr, attachContainerLogStreamer as ct, filterStudioCustomResources as d, applyCorsResponseHeaders as dn, webSocketApiMatchesIdentifier as dr, invokeAgentCoreWs as dt, buildJwksUrlFromIssuer as en, collectSsmParameterRefs as er, isLocalCdkAssetImage as et, isCustomResourceLambdaTarget as f, buildCorsConfigByApiId as fn, discoverRoutes as fr, A2A_CONTAINER_PORT as ft, toStudioTargetGroups as g, matchRoute as gn, AGENTCORE_AGUI_PROTOCOL as gr, MCP_PATH as gt, startStudioServer as h, matchPreflight as hn, AGENTCORE_A2A_PROTOCOL as hr, MCP_CONTAINER_PORT as ht, coerceStopRequest as i, verifyJwtViaDiscovery as in, countTargets as ir, DEFAULT_SHADOW_READY_TIMEOUT_MS as it, createLocalStartServiceCommand as j, handleConnectionsRequest as jn, classifySourceChange as jt, resolveAlbFrontDoor as k, ConnectionRegistry as kn, resolveProfileCredentials as kr, renderCodeDockerfile as kt, relayServeRequest as l, invokeTokenAuthorizer as ln, filterWebSocketApisByIdentifiers as lr, addInvokeAgentCoreSpecificOptions as lt, filterStudioTargetGroups as m, isFunctionUrlOacFronted as mn, resolveLambdaArnIntrinsic as mr, a2aInvokeOnce as mt, coerceRunRequest as n, verifyCognitoJwt as nn, resolveWatchConfig as nr, buildCloudMapIndex as nt, resolveServeBaseUrl as o, computeRequestIdentityHash as on, availableWebSocketApiIdentifiers as or, setShadowReadyTimeoutMs as ot, annotatePinnedEcsTargets as p, buildCorsConfigFromCloudFrontChain as pn, pickRefLogicalId as pr, A2A_PATH as pt, buildImageOverrideTag as q, createLocalStateProvider as qn, groupRoutesByServer as qt, coerceServeRequest as r, verifyJwtAuthorizer as rn, resolveSingleTarget as rr, CloudMapRegistry as rt, createStudioServeManager as s, evaluateCachedLambdaPolicy as sn, discoverWebSocketApis as sr, getContainerNetworkIp as st, addStudioSpecificOptions as t, createJwksCache as tn, resolveSsmParameters as tr, listPinnedTargets as tt, createStudioDispatcher as u, attachAuthorizers as un, parseSelectionExpressionPath as ur, createLocalInvokeAgentCoreCommand as ut, createStudioStore as v, applyAuthorizerOverlay as vn, AGENTCORE_MCP_PROTOCOL as vr, mcpInvokeOnce as vt, albStrategy as w, tryParseStatus as wn, formatStateRemedy as wr, waitForAgentCorePing as wt, createLocalListCommand as x, evaluateResponseParameters as xn, pickAgentCoreCandidateStack as xr, signAgentCoreInvocation as xt, StudioEventBus as y, buildHttpApiV2Event as yn, AGENTCORE_RUNTIME_TYPE as yr, parseSseForJsonRpc as yt, buildEcsImageResolutionContext$1 as z, resolveRuntimeFileExtension as zn, createFileWatcher as zt };
31589
+ //# sourceMappingURL=local-studio-Cm3Qt0MG.js.map