cdk-local 0.100.0 → 0.102.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +2 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/internal.d.ts +14 -2
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js +2 -2
- package/dist/{local-studio-BSdQpOpg.js → local-studio-BQBMYRAg.js} +217 -26
- package/dist/local-studio-BQBMYRAg.js.map +1 -0
- package/dist/{local-studio-BGHLCa1m.d.ts → local-studio-D6G31cfP.d.ts} +2 -1
- package/dist/{local-studio-BGHLCa1m.d.ts.map → local-studio-D6G31cfP.d.ts.map} +1 -1
- package/package.json +1 -1
- package/dist/local-studio-BSdQpOpg.js.map +0 -1
|
@@ -17358,6 +17358,7 @@ async function localInvokeCommand(target, options, extraStateProviders) {
|
|
|
17358
17358
|
const result = await invokeRie(containerHost, hostPort, event, Math.max(3e4, lambda.timeoutSec * 2 * 1e3));
|
|
17359
17359
|
await new Promise((resolveDelay) => setTimeout(resolveDelay, 250));
|
|
17360
17360
|
process.stdout.write(`${result.raw}\n`);
|
|
17361
|
+
if (options.responseFile) writeFileSync(options.responseFile, result.raw);
|
|
17361
17362
|
} finally {
|
|
17362
17363
|
if (sigintHandler) process.off("SIGINT", sigintHandler);
|
|
17363
17364
|
await cleanup();
|
|
@@ -17731,7 +17732,7 @@ function createLocalInvokeCommand(opts = {}) {
|
|
|
17731
17732
|
* `--help` clusters. Chainable: returns `cmd`.
|
|
17732
17733
|
*/
|
|
17733
17734
|
function addInvokeSpecificOptions(cmd) {
|
|
17734
|
-
return cmd.addOption(new Option("-e, --event <file>", "JSON event payload file (default: {})")).addOption(new Option("--event-stdin", "Read event JSON from stdin").default(false)).addOption(new Option("--env-vars <file>", "JSON env-var overrides (SAM-compatible: {\"LogicalId\":{\"KEY\":\"VALUE\"}, \"Parameters\": {...}})")).addOption(new Option("--no-pull", "Skip docker pull. Semantics differ by code path: ZIP Lambdas skip pulling the public Lambda base image; Container Lambdas on the local-build path are a no-op (docker build does not refresh the FROM cache by default); Container Lambdas on the ECR-pull fallback skip docker pull AND error if the image is not in the local cache (re-run without --no-pull or pre-pull manually).")).addOption(new Option("--no-build", "Skip docker build on the IMAGE local-build path (use the previously-built tag). Requires the deterministic tag to already be in the local registry; errors with an actionable message when missing. No-op for ZIP Lambdas and the IMAGE ECR-pull path. Compatible with --no-pull.")).addOption(new Option("--debug-port <port>", "Node --inspect-brk port (default: off)")).addOption(new Option("--container-host <host>", "Host IP the host uses to bind the RIE port to. Must be a numeric IP (Docker rejects hostnames here). Defaults to 127.0.0.1.").default("127.0.0.1")).addOption(new Option("--assume-role [arn]", "Assume the Lambda's deployed execution role and forward STS-issued temp credentials to the container so the handler runs with the deployed function's narrow permissions. Three forms: (1) `--assume-role <arn>` assumes the explicit ARN; (2) `--assume-role` (bare) auto-resolves the function's execution role ARN from state (requires an active state source); (3) `--no-assume-role` explicitly opts out. Off by default — when omitted, the developer's shell credentials are forwarded unchanged (SAM-compatible default). STS failures degrade to a warn + dev-creds fallback.")).addOption(new Option("--layer-role-arn <arn>", "Role to sts:AssumeRole before calling lambda:GetLayerVersion on every literal-ARN entry in Properties.Layers. Use only when the dev credentials cannot read the layer — typically cross-account layers. AWS-published public layers (e.g. Lambda Powertools) are readable from every account and need no role.")).addOption(new Option("--ecr-role-arn <arn>", "Role ARN to assume before authenticating against ECR for cross-account / centralized registries. Issues sts:AssumeRole via the default credential chain and uses the temporary credentials for ecr:GetAuthorizationToken + docker pull. Required when the caller does not have direct cross-account access to the target repository. Same-account / same-region pulls do not need this flag.")).addOption(new Option("--from-cfn-stack [cfn-stack-name]", "Read a deployed CloudFormation stack via ListStackResources and substitute Ref / Fn::ImportValue in env vars with the deployed physical IDs / exports. Use for CDK apps deployed via the upstream CDK CLI (`cdk deploy`). Bare form uses the resolved stack name; pass an explicit value when CFn stack name differs. Fn::GetAtt is warn-and-dropped in v1 (CFn ListStackResources does not return per-attribute values).")).addOption(new Option("--stack-region <region>", "Region of the state record to read. Used with --from-cfn-stack as the CFn client region."));
|
|
17735
|
+
return cmd.addOption(new Option("-e, --event <file>", "JSON event payload file (default: {})")).addOption(new Option("--event-stdin", "Read event JSON from stdin").default(false)).addOption(new Option("--env-vars <file>", "JSON env-var overrides (SAM-compatible: {\"LogicalId\":{\"KEY\":\"VALUE\"}, \"Parameters\": {...}})")).addOption(new Option("--no-pull", "Skip docker pull. Semantics differ by code path: ZIP Lambdas skip pulling the public Lambda base image; Container Lambdas on the local-build path are a no-op (docker build does not refresh the FROM cache by default); Container Lambdas on the ECR-pull fallback skip docker pull AND error if the image is not in the local cache (re-run without --no-pull or pre-pull manually).")).addOption(new Option("--no-build", "Skip docker build on the IMAGE local-build path (use the previously-built tag). Requires the deterministic tag to already be in the local registry; errors with an actionable message when missing. No-op for ZIP Lambdas and the IMAGE ECR-pull path. Compatible with --no-pull.")).addOption(new Option("--debug-port <port>", "Node --inspect-brk port (default: off)")).addOption(new Option("--container-host <host>", "Host IP the host uses to bind the RIE port to. Must be a numeric IP (Docker rejects hostnames here). Defaults to 127.0.0.1.").default("127.0.0.1")).addOption(new Option("--assume-role [arn]", "Assume the Lambda's deployed execution role and forward STS-issued temp credentials to the container so the handler runs with the deployed function's narrow permissions. Three forms: (1) `--assume-role <arn>` assumes the explicit ARN; (2) `--assume-role` (bare) auto-resolves the function's execution role ARN from state (requires an active state source); (3) `--no-assume-role` explicitly opts out. Off by default — when omitted, the developer's shell credentials are forwarded unchanged (SAM-compatible default). STS failures degrade to a warn + dev-creds fallback.")).addOption(new Option("--layer-role-arn <arn>", "Role to sts:AssumeRole before calling lambda:GetLayerVersion on every literal-ARN entry in Properties.Layers. Use only when the dev credentials cannot read the layer — typically cross-account layers. AWS-published public layers (e.g. Lambda Powertools) are readable from every account and need no role.")).addOption(new Option("--ecr-role-arn <arn>", "Role ARN to assume before authenticating against ECR for cross-account / centralized registries. Issues sts:AssumeRole via the default credential chain and uses the temporary credentials for ecr:GetAuthorizationToken + docker pull. Required when the caller does not have direct cross-account access to the target repository. Same-account / same-region pulls do not need this flag.")).addOption(new Option("--from-cfn-stack [cfn-stack-name]", "Read a deployed CloudFormation stack via ListStackResources and substitute Ref / Fn::ImportValue in env vars with the deployed physical IDs / exports. Use for CDK apps deployed via the upstream CDK CLI (`cdk deploy`). Bare form uses the resolved stack name; pass an explicit value when CFn stack name differs. Fn::GetAtt is warn-and-dropped in v1 (CFn ListStackResources does not return per-attribute values).")).addOption(new Option("--stack-region <region>", "Region of the state record to read. Used with --from-cfn-stack as the CFn client region.")).addOption(new Option("--response-file <path>", "Also write the raw Lambda response payload to this file. Lets a programmatic caller recover the response without parsing it out of stdout (where it interleaves with synth progress + streamed container logs). stdout output is unchanged."));
|
|
17735
17736
|
}
|
|
17736
17737
|
|
|
17737
17738
|
//#endregion
|
|
@@ -28510,6 +28511,7 @@ const STUDIO_CSS = `
|
|
|
28510
28511
|
.row.sel { background: #2a3550; }
|
|
28511
28512
|
.row .ts { color: #777; }
|
|
28512
28513
|
.row .label { color: #ddd; flex: 1; overflow: hidden; text-overflow: ellipsis; }
|
|
28514
|
+
.row.reinvoke .label::before { content: '\\21A9 '; color: #6aa0ff; margin-right: 2px; }
|
|
28513
28515
|
.row .status { color: #7bd88f; }
|
|
28514
28516
|
.row .status.err { color: #e0707a; }
|
|
28515
28517
|
#workspace { padding: 0 0 24px; }
|
|
@@ -28541,6 +28543,7 @@ const STUDIO_CSS = `
|
|
|
28541
28543
|
.req-composer .req-status { margin-top: 8px; font: 12px ui-monospace, Menlo, monospace; }
|
|
28542
28544
|
.req-composer .req-result pre { background: #0e0e0e; }
|
|
28543
28545
|
.composer button:disabled { background: #333; color: #888; cursor: default; }
|
|
28546
|
+
.composer .reinvoke-btn { margin-top: 6px; padding: 4px 14px; }
|
|
28544
28547
|
.composer .err { color: #e0707a; margin-top: 6px; min-height: 18px; }
|
|
28545
28548
|
.section { padding: 8px 12px; border-bottom: 1px solid #222; }
|
|
28546
28549
|
.section h3 { margin: 0 0 6px; font-size: 11px; color: #888; text-transform: uppercase; }
|
|
@@ -28638,6 +28641,7 @@ const STUDIO_SCRIPT = `
|
|
|
28638
28641
|
let shownInvId = null; // lambda invocation whose result is in the workspace
|
|
28639
28642
|
let shownServeId = null; // serve target whose workspace is shown
|
|
28640
28643
|
let shownDetailId = null; // captured request whose read-only detail is shown
|
|
28644
|
+
let pendingReqPrefill = null; // {method,path,headers,body} to seed the next serve request composer (re-invoke)
|
|
28641
28645
|
let studioDockerfiles = []; // Dockerfiles scanned at boot (pinned-ecs image-override picker)
|
|
28642
28646
|
|
|
28643
28647
|
function el(tag, cls, text) {
|
|
@@ -29270,6 +29274,34 @@ const STUDIO_SCRIPT = `
|
|
|
29270
29274
|
bodyTa.spellcheck = false;
|
|
29271
29275
|
sec.appendChild(bodyTa);
|
|
29272
29276
|
|
|
29277
|
+
// Re-invoke prefill (issue #284): seed the fields from a captured request
|
|
29278
|
+
// when the user clicked [Re-invoke] on a served-request detail. The prefill
|
|
29279
|
+
// is address-tagged with its target; consume it UNCONDITIONALLY (so a stray
|
|
29280
|
+
// one from a since-stopped serve never lingers) but only APPLY it when the
|
|
29281
|
+
// target matches this composer.
|
|
29282
|
+
if (pendingReqPrefill) {
|
|
29283
|
+
const pending = pendingReqPrefill;
|
|
29284
|
+
pendingReqPrefill = null;
|
|
29285
|
+
if (pending.targetId === id && pending.req && typeof pending.req === 'object') {
|
|
29286
|
+
const pf = pending.req;
|
|
29287
|
+
if (pf.method) method.value = String(pf.method).toUpperCase();
|
|
29288
|
+
if (pf.path != null) path.value = pf.path;
|
|
29289
|
+
if (pf.headers && typeof pf.headers === 'object') {
|
|
29290
|
+
// Drop hop-by-hop / transport headers the proxy captured verbatim
|
|
29291
|
+
// (host / content-length / etc.) — they are noise in the editor and
|
|
29292
|
+
// the relay sets them itself.
|
|
29293
|
+
const SKIP = ['host', 'connection', 'content-length', 'transfer-encoding', 'accept-encoding'];
|
|
29294
|
+
headers.value = Object.keys(pf.headers)
|
|
29295
|
+
.filter(function (k) { return SKIP.indexOf(k.toLowerCase()) === -1; })
|
|
29296
|
+
.map(function (k) { return k + ': ' + pf.headers[k]; })
|
|
29297
|
+
.join('\\n');
|
|
29298
|
+
}
|
|
29299
|
+
if (pf.body != null) {
|
|
29300
|
+
bodyTa.value = typeof pf.body === 'string' ? pf.body : JSON.stringify(pf.body);
|
|
29301
|
+
}
|
|
29302
|
+
}
|
|
29303
|
+
}
|
|
29304
|
+
|
|
29273
29305
|
const sendRow = el('div', 'req-send');
|
|
29274
29306
|
const btn = el('button', null, 'Send');
|
|
29275
29307
|
sendRow.appendChild(btn);
|
|
@@ -29470,21 +29502,31 @@ const STUDIO_SCRIPT = `
|
|
|
29470
29502
|
return sec;
|
|
29471
29503
|
}
|
|
29472
29504
|
|
|
29473
|
-
function renderComposer(id, kind, eventText) {
|
|
29505
|
+
function renderComposer(id, kind, eventText, reinvokeOf) {
|
|
29474
29506
|
const ws = document.getElementById('workspace');
|
|
29475
29507
|
ws.innerHTML = '';
|
|
29476
29508
|
|
|
29477
29509
|
const composer = el('div', 'composer');
|
|
29478
|
-
composer.appendChild(el('div', 'target-name', 'Invoke ' + id));
|
|
29510
|
+
composer.appendChild(el('div', 'target-name', (reinvokeOf ? 'Re-invoke ' : 'Invoke ') + id));
|
|
29479
29511
|
const ta = el('textarea');
|
|
29480
29512
|
ta.value = eventText;
|
|
29481
29513
|
ta.spellcheck = false;
|
|
29482
29514
|
composer.appendChild(ta);
|
|
29483
|
-
//
|
|
29484
|
-
|
|
29485
|
-
|
|
29515
|
+
// A re-invoke (issue #284) re-runs the EDITED event against the same
|
|
29516
|
+
// target; per-run options are not carried over, so the options section is
|
|
29517
|
+
// omitted (the payload is the thing being tweaked). A fresh invoke keeps
|
|
29518
|
+
// the per-run options (e.g. env vars) below the event, above Invoke.
|
|
29519
|
+
let opt = { collect: undefined, collectRaw: undefined };
|
|
29520
|
+
if (reinvokeOf) {
|
|
29521
|
+
composer.appendChild(
|
|
29522
|
+
el('div', 'opt-hint', 'Re-invoke runs the edited event through the same target (per-run options use defaults).')
|
|
29523
|
+
);
|
|
29524
|
+
} else {
|
|
29525
|
+
opt = buildOptions(kind);
|
|
29526
|
+
if (opt.node) composer.appendChild(opt.node);
|
|
29527
|
+
}
|
|
29486
29528
|
composer.appendChild(document.createElement('br'));
|
|
29487
|
-
const btn = el('button', null, 'Invoke');
|
|
29529
|
+
const btn = el('button', null, reinvokeOf ? 'Re-invoke' : 'Invoke');
|
|
29488
29530
|
const msg = el('div', 'err');
|
|
29489
29531
|
composer.appendChild(btn);
|
|
29490
29532
|
composer.appendChild(msg);
|
|
@@ -29494,7 +29536,17 @@ const STUDIO_SCRIPT = `
|
|
|
29494
29536
|
ws.appendChild(composer);
|
|
29495
29537
|
ws.appendChild(result);
|
|
29496
29538
|
|
|
29497
|
-
active = {
|
|
29539
|
+
active = {
|
|
29540
|
+
id,
|
|
29541
|
+
kind,
|
|
29542
|
+
ta,
|
|
29543
|
+
btn,
|
|
29544
|
+
msg,
|
|
29545
|
+
result,
|
|
29546
|
+
collectOpts: opt.collect,
|
|
29547
|
+
collectRaw: opt.collectRaw,
|
|
29548
|
+
reinvokeOf: reinvokeOf || null,
|
|
29549
|
+
};
|
|
29498
29550
|
btn.onclick = () => runInvoke();
|
|
29499
29551
|
shownInvId = null;
|
|
29500
29552
|
shownDetailId = null;
|
|
@@ -29511,17 +29563,29 @@ const STUDIO_SCRIPT = `
|
|
|
29511
29563
|
msg.textContent = 'Invalid JSON: ' + err.message;
|
|
29512
29564
|
return;
|
|
29513
29565
|
}
|
|
29566
|
+
const isReinvoke = !!active.reinvokeOf;
|
|
29514
29567
|
msg.textContent = '';
|
|
29515
29568
|
btn.disabled = true;
|
|
29516
|
-
btn.textContent = 'Invoking...';
|
|
29569
|
+
btn.textContent = isReinvoke ? 'Re-invoking...' : 'Invoking...';
|
|
29517
29570
|
result.innerHTML = '';
|
|
29518
29571
|
try {
|
|
29519
|
-
|
|
29520
|
-
|
|
29521
|
-
|
|
29522
|
-
|
|
29523
|
-
|
|
29524
|
-
|
|
29572
|
+
// A re-invoke (issue #284) re-runs a recorded row by id with the edited
|
|
29573
|
+
// payload through POST /api/reinvoke; a fresh invoke runs the target by
|
|
29574
|
+
// POST /api/run with the composed options.
|
|
29575
|
+
let url;
|
|
29576
|
+
let body;
|
|
29577
|
+
if (isReinvoke) {
|
|
29578
|
+
url = '/api/reinvoke';
|
|
29579
|
+
body = { invocationId: active.reinvokeOf, payload: event };
|
|
29580
|
+
} else {
|
|
29581
|
+
url = '/api/run';
|
|
29582
|
+
body = { targetId: id, kind, event };
|
|
29583
|
+
const options = active.collectOpts ? active.collectOpts() : undefined;
|
|
29584
|
+
if (options) body.options = options;
|
|
29585
|
+
const rawArgs = active.collectRaw ? active.collectRaw() : undefined;
|
|
29586
|
+
if (rawArgs) body.rawArgs = rawArgs;
|
|
29587
|
+
}
|
|
29588
|
+
const res = await fetch(url, {
|
|
29525
29589
|
method: 'POST',
|
|
29526
29590
|
headers: { 'content-type': 'application/json' },
|
|
29527
29591
|
body: JSON.stringify(body),
|
|
@@ -29532,13 +29596,14 @@ const STUDIO_SCRIPT = `
|
|
|
29532
29596
|
renderResult(shownInvId);
|
|
29533
29597
|
}
|
|
29534
29598
|
if (!res.ok || data.ok === false) {
|
|
29535
|
-
|
|
29599
|
+
const verb = isReinvoke ? 'Re-invoke' : 'Invoke';
|
|
29600
|
+
msg.textContent = verb + ' failed: ' + (data.error || ('HTTP ' + res.status));
|
|
29536
29601
|
}
|
|
29537
29602
|
} catch (err) {
|
|
29538
29603
|
msg.textContent = 'Request failed: ' + err;
|
|
29539
29604
|
} finally {
|
|
29540
29605
|
btn.disabled = false;
|
|
29541
|
-
btn.textContent = 'Invoke';
|
|
29606
|
+
btn.textContent = isReinvoke ? 'Re-invoke' : 'Invoke';
|
|
29542
29607
|
}
|
|
29543
29608
|
}
|
|
29544
29609
|
|
|
@@ -29596,6 +29661,12 @@ const STUDIO_SCRIPT = `
|
|
|
29596
29661
|
const d = new Date(merged.ts);
|
|
29597
29662
|
row.querySelector('.ts').textContent = d.toLocaleTimeString();
|
|
29598
29663
|
row.querySelector('.label').textContent = (merged.target || '') + ' ' + (merged.label || '');
|
|
29664
|
+
// A re-invoke (issue #284) row is visually linked to its source: a CSS
|
|
29665
|
+
// marker on the label + a tooltip naming the source invocation.
|
|
29666
|
+
if (merged.reinvokeOf) {
|
|
29667
|
+
row.classList.add('reinvoke');
|
|
29668
|
+
row.title = 'Re-invoke of ' + merged.reinvokeOf;
|
|
29669
|
+
}
|
|
29599
29670
|
const statusEl = row.querySelector('.status');
|
|
29600
29671
|
statusEl.textContent =
|
|
29601
29672
|
merged.status != null
|
|
@@ -29618,10 +29689,11 @@ const STUDIO_SCRIPT = `
|
|
|
29618
29689
|
highlightTarget(ev.target);
|
|
29619
29690
|
if (INVOKE_KINDS.includes(ev.kind)) {
|
|
29620
29691
|
// A single-shot invocation row (Lambda or AgentCore) reloads into the
|
|
29621
|
-
// re-invokable composer
|
|
29692
|
+
// re-invokable composer, pre-filled with the captured event and wired to
|
|
29693
|
+
// POST /api/reinvoke (issue #284) so the new row links to this source.
|
|
29622
29694
|
shownDetailId = null;
|
|
29623
29695
|
shownServeId = null;
|
|
29624
|
-
renderComposer(ev.target, ev.kind, ev.request != null ? fmt(ev.request) : '{}');
|
|
29696
|
+
renderComposer(ev.target, ev.kind, ev.request != null ? fmt(ev.request) : '{}', id);
|
|
29625
29697
|
shownInvId = id;
|
|
29626
29698
|
renderResult(id);
|
|
29627
29699
|
} else {
|
|
@@ -29644,6 +29716,36 @@ const STUDIO_SCRIPT = `
|
|
|
29644
29716
|
|
|
29645
29717
|
const head = el('div', 'composer');
|
|
29646
29718
|
head.appendChild(el('div', 'target-name', (ev.label || 'request') + ' — ' + (ev.target || '')));
|
|
29719
|
+
// Re-invoke (issue #284): a captured served request is re-sent through the
|
|
29720
|
+
// live front door by reusing that serve's request composer. Clicking
|
|
29721
|
+
// navigates to the running serve and pre-fills it with this request; the
|
|
29722
|
+
// serve must be running (restart it first if it has stopped).
|
|
29723
|
+
const serveSt = serveState.get(ev.target);
|
|
29724
|
+
const serveRunning = serveSt && serveSt.status === 'running';
|
|
29725
|
+
const reBtn = el('button', 'reinvoke-btn', 'Re-invoke');
|
|
29726
|
+
if (serveRunning) {
|
|
29727
|
+
reBtn.onclick = function () {
|
|
29728
|
+
// Re-check running at CLICK time: the serve may have stopped since the
|
|
29729
|
+
// detail was rendered (the button is not re-rendered on a stop). If so,
|
|
29730
|
+
// do NOT seed a prefill (it would otherwise leak into the next serve
|
|
29731
|
+
// composer). The prefill is address-tagged with the target so a stray
|
|
29732
|
+
// one is dropped on mismatch (see renderRequestComposer).
|
|
29733
|
+
const cur = serveState.get(ev.target);
|
|
29734
|
+
if (!cur || cur.status !== 'running') {
|
|
29735
|
+
renderCapturedDetail(id); // re-render to reflect the now-stopped state
|
|
29736
|
+
return;
|
|
29737
|
+
}
|
|
29738
|
+
pendingReqPrefill =
|
|
29739
|
+
ev.request && typeof ev.request === 'object'
|
|
29740
|
+
? { targetId: ev.target, req: ev.request }
|
|
29741
|
+
: null;
|
|
29742
|
+
selectTarget(ev.target, ev.kind);
|
|
29743
|
+
};
|
|
29744
|
+
} else {
|
|
29745
|
+
reBtn.disabled = true;
|
|
29746
|
+
reBtn.title = 'Start the serve to re-invoke this request.';
|
|
29747
|
+
}
|
|
29748
|
+
head.appendChild(reBtn);
|
|
29647
29749
|
ws.appendChild(head);
|
|
29648
29750
|
|
|
29649
29751
|
const reqSec = el('div', 'section');
|
|
@@ -30131,6 +30233,10 @@ function handleRequest(req, res, bus, html, targetsJson, options) {
|
|
|
30131
30233
|
handleDispatch(req, res, options.onRun);
|
|
30132
30234
|
return;
|
|
30133
30235
|
}
|
|
30236
|
+
if (req.method === "POST" && path === "/api/reinvoke") {
|
|
30237
|
+
handleDispatch(req, res, options.onReinvoke);
|
|
30238
|
+
return;
|
|
30239
|
+
}
|
|
30134
30240
|
if (req.method === "POST" && path === "/api/request") {
|
|
30135
30241
|
handleDispatch(req, res, options.onServeRequest);
|
|
30136
30242
|
return;
|
|
@@ -30457,20 +30563,23 @@ function createStudioDispatcher(config) {
|
|
|
30457
30563
|
target: req.targetId,
|
|
30458
30564
|
kind: req.kind,
|
|
30459
30565
|
label: "invoke",
|
|
30460
|
-
request: req.event
|
|
30566
|
+
request: req.event,
|
|
30567
|
+
...req.reinvokeOf ? { reinvokeOf: req.reinvokeOf } : {}
|
|
30461
30568
|
});
|
|
30462
30569
|
let dir;
|
|
30463
30570
|
try {
|
|
30464
30571
|
dir = mkdtempSync(join(tmpdir(), "cdkl-studio-run-"));
|
|
30465
30572
|
const eventFile = join(dir, "event.json");
|
|
30466
30573
|
writeFileSync(eventFile, JSON.stringify(req.event ?? {}));
|
|
30574
|
+
const responseFilePath = req.kind === "lambda" ? join(dir, "response.json") : void 0;
|
|
30467
30575
|
const args = [
|
|
30468
30576
|
verb,
|
|
30469
30577
|
req.targetId,
|
|
30470
30578
|
"--event",
|
|
30471
30579
|
eventFile,
|
|
30472
30580
|
...buildSharedChildArgs(config, { preferAssembly: true }),
|
|
30473
|
-
...buildPerRunArgs(req.kind, req.options)
|
|
30581
|
+
...buildPerRunArgs(req.kind, req.options),
|
|
30582
|
+
...responseFilePath ? ["--response-file", responseFilePath] : []
|
|
30474
30583
|
];
|
|
30475
30584
|
const envVars = resolveEnvVars(req.kind, req.options);
|
|
30476
30585
|
if (envVars) {
|
|
@@ -30483,7 +30592,12 @@ function createStudioDispatcher(config) {
|
|
|
30483
30592
|
const durationMs = clock() - startedAt;
|
|
30484
30593
|
const ok = code === 0;
|
|
30485
30594
|
const failure = stderr.trim() || `cdkl ${verb} exited ${code}`;
|
|
30486
|
-
|
|
30595
|
+
let fileResponse;
|
|
30596
|
+
if (responseFilePath) try {
|
|
30597
|
+
const content = readFileSync(responseFilePath, "utf8");
|
|
30598
|
+
if (content.length > 0) fileResponse = content;
|
|
30599
|
+
} catch {}
|
|
30600
|
+
const { response, raw, stdoutLogLines } = extractResponse(req.kind, stdout, ok, failure, fileResponse);
|
|
30487
30601
|
stdoutLogLines.forEach((line) => {
|
|
30488
30602
|
config.bus.emit("log", {
|
|
30489
30603
|
ts: clock(),
|
|
@@ -30503,7 +30617,8 @@ function createStudioDispatcher(config) {
|
|
|
30503
30617
|
request: req.event,
|
|
30504
30618
|
response,
|
|
30505
30619
|
status,
|
|
30506
|
-
durationMs
|
|
30620
|
+
durationMs,
|
|
30621
|
+
...req.reinvokeOf ? { reinvokeOf: req.reinvokeOf } : {}
|
|
30507
30622
|
});
|
|
30508
30623
|
const result = {
|
|
30509
30624
|
invocationId,
|
|
@@ -30603,7 +30718,7 @@ function runChild(spawnFn, nodeBin, argv, cwd, invocationId, target, bus, clock)
|
|
|
30603
30718
|
* whole (a single MCP result), else kept as the raw streamed text. There
|
|
30604
30719
|
* are no separate stdout log lines.
|
|
30605
30720
|
*/
|
|
30606
|
-
function extractResponse(kind, stdout, ok, failure) {
|
|
30721
|
+
function extractResponse(kind, stdout, ok, failure, fileResponse) {
|
|
30607
30722
|
if (kind === "agentcore") {
|
|
30608
30723
|
const text = stdout.trim();
|
|
30609
30724
|
if (!ok) return {
|
|
@@ -30624,6 +30739,16 @@ function extractResponse(kind, stdout, ok, failure) {
|
|
|
30624
30739
|
raw: "",
|
|
30625
30740
|
stdoutLogLines: stdoutLines
|
|
30626
30741
|
};
|
|
30742
|
+
if (fileResponse !== void 0) {
|
|
30743
|
+
const trimmed = fileResponse.trim();
|
|
30744
|
+
const parsed = tryParseJson(trimmed);
|
|
30745
|
+
const stdoutLogLines = stdoutLines.filter((l) => l.trim() !== trimmed);
|
|
30746
|
+
return {
|
|
30747
|
+
response: parsed.ok ? parsed.value : fileResponse,
|
|
30748
|
+
raw: fileResponse,
|
|
30749
|
+
stdoutLogLines
|
|
30750
|
+
};
|
|
30751
|
+
}
|
|
30627
30752
|
let responseIdx = -1;
|
|
30628
30753
|
let response;
|
|
30629
30754
|
for (let i = stdoutLines.length - 1; i >= 0; i -= 1) {
|
|
@@ -30665,6 +30790,45 @@ function tryParseJson(raw) {
|
|
|
30665
30790
|
}
|
|
30666
30791
|
}
|
|
30667
30792
|
|
|
30793
|
+
//#endregion
|
|
30794
|
+
//#region src/local/studio-reinvoke.ts
|
|
30795
|
+
/**
|
|
30796
|
+
* The target kinds a timeline row can be re-invoked through the dispatcher.
|
|
30797
|
+
* Single-shot invoke kinds only — a served request (api / alb / ecs) is
|
|
30798
|
+
* re-sent client-side through the request composer + the live front door
|
|
30799
|
+
* (issue #322), not re-dispatched here, so the proxy still captures it.
|
|
30800
|
+
*/
|
|
30801
|
+
const REINVOKABLE_KINDS = new Set(["lambda", "agentcore"]);
|
|
30802
|
+
/**
|
|
30803
|
+
* Re-invoke a past timeline row with an edited payload (issue #284, studio
|
|
30804
|
+
* Phase 3). Resolves the original target from the recorded invocation and
|
|
30805
|
+
* re-fires the edited payload through the SAME single-shot dispatcher
|
|
30806
|
+
* `POST /api/run` uses, threading `reinvokeOf` so the new row links to its
|
|
30807
|
+
* source. The payload REPLACES the original event (the edit IS the point);
|
|
30808
|
+
* run options are not carried over (re-invoke edits the payload, not the
|
|
30809
|
+
* flags).
|
|
30810
|
+
*
|
|
30811
|
+
* Only `lambda` / `agentcore` rows are re-invokable here — a served request
|
|
30812
|
+
* is re-sent through the request composer instead, so this throws for serve
|
|
30813
|
+
* kinds (and for an id that has aged out of the bounded history window).
|
|
30814
|
+
*
|
|
30815
|
+
* Host-side use case: consumed by `cdkl studio`'s `POST /api/reinvoke`
|
|
30816
|
+
* handler; a host CLI embedding the studio building blocks reuses it to wire
|
|
30817
|
+
* the same re-invoke route over its own store + dispatcher.
|
|
30818
|
+
*/
|
|
30819
|
+
async function reinvoke(input, deps) {
|
|
30820
|
+
const original = deps.store.invocation(input.invocationId);
|
|
30821
|
+
if (!original) throw new Error(`No recorded invocation '${input.invocationId}' to re-invoke (it may have aged out of the history window).`);
|
|
30822
|
+
if (!REINVOKABLE_KINDS.has(original.kind)) throw new Error(`Re-invoke from the timeline is server-side only for Lambda / AgentCore targets; re-send a '${original.kind}' request with the request composer instead.`);
|
|
30823
|
+
const req = {
|
|
30824
|
+
targetId: original.target,
|
|
30825
|
+
kind: original.kind,
|
|
30826
|
+
event: input.payload,
|
|
30827
|
+
reinvokeOf: input.invocationId
|
|
30828
|
+
};
|
|
30829
|
+
return deps.dispatcher.run(req);
|
|
30830
|
+
}
|
|
30831
|
+
|
|
30668
30832
|
//#endregion
|
|
30669
30833
|
//#region src/local/studio-request-relay.ts
|
|
30670
30834
|
const DEFAULT_TIMEOUT_MS = 3e4;
|
|
@@ -31349,6 +31513,23 @@ function coerceServeRequest(body) {
|
|
|
31349
31513
|
return out;
|
|
31350
31514
|
}
|
|
31351
31515
|
/**
|
|
31516
|
+
* Validate the `POST /api/reinvoke` body at the HTTP boundary (issue #284):
|
|
31517
|
+
* a non-empty `invocationId` string and a `payload` (the edited event — any
|
|
31518
|
+
* JSON value, including `null`, but the key must be present so an omitted
|
|
31519
|
+
* payload is a clean 4xx rather than a silent re-invoke with `undefined`).
|
|
31520
|
+
*/
|
|
31521
|
+
function coerceReinvokeRequest(body) {
|
|
31522
|
+
if (typeof body !== "object" || body === null) throw new Error("Request body must be a JSON object.");
|
|
31523
|
+
const record = body;
|
|
31524
|
+
const { invocationId } = record;
|
|
31525
|
+
if (typeof invocationId !== "string" || invocationId.trim() === "") throw new Error("Request body must include a non-empty \"invocationId\" string.");
|
|
31526
|
+
if (!("payload" in record)) throw new Error("Request body must include a \"payload\" (the edited event).");
|
|
31527
|
+
return {
|
|
31528
|
+
invocationId,
|
|
31529
|
+
payload: record["payload"]
|
|
31530
|
+
};
|
|
31531
|
+
}
|
|
31532
|
+
/**
|
|
31352
31533
|
* Validate a `PATCH /api/config` body and apply the editable run-time
|
|
31353
31534
|
* bindings (`fromCfnStack` / `assumeRole`) onto `target` in place. Only the
|
|
31354
31535
|
* keys PRESENT in the body are touched (a partial update); `null` / `false` /
|
|
@@ -31527,6 +31708,16 @@ async function localStudioCommand(options) {
|
|
|
31527
31708
|
...req.body !== void 0 ? { body: req.body } : {}
|
|
31528
31709
|
});
|
|
31529
31710
|
},
|
|
31711
|
+
onReinvoke: (body) => {
|
|
31712
|
+
const { invocationId, payload } = coerceReinvokeRequest(body);
|
|
31713
|
+
return reinvoke({
|
|
31714
|
+
invocationId,
|
|
31715
|
+
payload
|
|
31716
|
+
}, {
|
|
31717
|
+
store,
|
|
31718
|
+
dispatcher
|
|
31719
|
+
});
|
|
31720
|
+
},
|
|
31530
31721
|
getRunning: () => ({ running: serveManager.list() }),
|
|
31531
31722
|
getConfig: () => sessionConfigSnapshot(),
|
|
31532
31723
|
patchConfig: (body) => {
|
|
@@ -31609,5 +31800,5 @@ function addStudioSpecificOptions(cmd) {
|
|
|
31609
31800
|
}
|
|
31610
31801
|
|
|
31611
31802
|
//#endregion
|
|
31612
|
-
export {
|
|
31613
|
-
//# sourceMappingURL=local-studio-
|
|
31803
|
+
export { runImageOverrideBuilds as $, resolveCfnStackName as $n, defaultCredentialsLoader as $t, resolveAlbFrontDoor as A, ConnectionRegistry as An, resolveProfileCredentials as Ar, renderCodeDockerfile as At, buildEcsImageResolutionContext$1 as B, resolveRuntimeFileExtension as Bn, createFileWatcher as Bt, formatTargetListing as C, pickResponseTemplate as Cn, resolveAgentCoreTarget as Cr, AGENTCORE_SESSION_ID_HEADER as Ct, parseLbPortOverrides as D, HOST_GATEWAY_MIN_VERSION as Dn, tryResolveImageFnJoin as Dr, SUPPORTED_CODE_RUNTIMES as Dt, createLocalStartAlbCommand as E, VtlEvaluationError as En, substituteImagePlaceholders as Er, downloadAndExtractS3Bundle as Et, createLocalRunTaskCommand as F, buildDisconnectEvent as Fn, addStartApiSpecificOptions as Ft, resolveSharedSidecarCredentials as G, substituteEnvVarsFromState as Gn, availableApiIdentifiers as Gt, parseMaxTasks as H, EcsTaskResolutionError as Hn, buildStageMap as Ht, MAX_TASKS_SUBNET_RANGE_CAP as I, buildMessageEvent as In, createLocalStartApiCommand as It, buildImageOverrideTag as J, createLocalStateProvider as Jn, groupRoutesByServer as Jt, runEcsServiceEmulator as K, substituteEnvVarsFromStateAsync as Kn, filterRoutesByApiIdentifier as Kt, addCommonEcsServiceOptions as L, architectureToPlatform as Ln, createWatchPredicates as Lt, createLocalStartServiceCommand as M, handleConnectionsRequest as Mn, classifySourceChange as Mt, serviceStrategy as N, parseConnectionsPath as Nn, addInvokeSpecificOptions as Nt, resolveAlbTarget as O, probeHostGatewaySupport as On, LocalInvokeBuildError as Or, buildAgentCoreCodeImage as Ot, addRunTaskSpecificOptions as P, buildConnectEvent as Pn, createLocalInvokeCommand as Pt, resolveImageOverrides as Q, resolveCfnRegion as Qn, resolveServiceIntegrationParameters as Qt, addEcsAssumeRoleOptions as R, buildContainerImage as Rn, resolveApiTargetSubset as Rt, createLocalListCommand as S, evaluateResponseParameters as Sn, pickAgentCoreCandidateStack as Sr, signAgentCoreInvocation as St, albStrategy as T, tryParseStatus as Tn, formatStateRemedy as Tr, waitForAgentCorePing as Tt, parseRestartPolicy as U, substituteAgainstState as Un, materializeLayerFromArn as Ut, ecsClusterOption as V, resolveRuntimeImage as Vn, attachStageContext as Vt, resolveEcsAssumeRoleOption as W, substituteAgainstStateAsync as Wn, resolveEnvVars$1 as Wt, mergeForService as X, rejectExplicitCfnStackWithMultipleStacks as Xn, startApiServer as Xt, enforceImageOverrideOrphans as Y, isCfnFlagPresent as Yn, readMtlsMaterialsFromDisk as Yt, parseImageOverrideFlags as Z, resolveCfnFallbackRegion as Zn, resolveSelectionExpression as Zt, toStudioTargetGroups as _, matchRoute as _n, AGENTCORE_AGUI_PROTOCOL as _r, MCP_PATH as _t, createLocalStudioCommand as a, verifyJwtViaDiscovery as an, countTargets as ar, DEFAULT_SHADOW_READY_TIMEOUT_MS as at, StudioEventBus as b, buildHttpApiV2Event as bn, AGENTCORE_RUNTIME_TYPE as br, parseSseForJsonRpc as bt, startStudioProxy as c, evaluateCachedLambdaPolicy as cn, discoverWebSocketApis as cr, getContainerNetworkIp as ct, createStudioDispatcher as d, attachAuthorizers as dn, parseSelectionExpressionPath as dr, createLocalInvokeAgentCoreCommand as dt, buildCognitoJwksUrl as en, CfnLocalStateProvider as er, describePinnedImageUri as et, filterStudioCustomResources as f, applyCorsResponseHeaders as fn, webSocketApiMatchesIdentifier as fr, invokeAgentCoreWs as ft, startStudioServer as g, matchPreflight as gn, AGENTCORE_A2A_PROTOCOL as gr, MCP_CONTAINER_PORT as gt, filterStudioTargetGroups as h, isFunctionUrlOacFronted as hn, resolveLambdaArnIntrinsic as hr, a2aInvokeOnce as ht, coerceStopRequest as i, verifyJwtAuthorizer as in, resolveSingleTarget as ir, CloudMapRegistry as it, addStartServiceSpecificOptions as j, buildMgmtEndpointEnvUrl as jn, toCmdArgv as jt, isApplicationLoadBalancer as k, bufferToBody as kn, buildStsClientConfig as kr, computeCodeImageTag as kt, relayServeRequest as l, invokeRequestAuthorizer as ln, discoverWebSocketApisOrThrow as lr, attachContainerLogStreamer as lt, annotatePinnedEcsTargets as m, buildCorsConfigFromCloudFrontChain as mn, pickRefLogicalId as mr, A2A_PATH as mt, coerceRunRequest as n, createJwksCache as nn, resolveSsmParameters as nr, listPinnedTargets as nt, resolveServeBaseUrl as o, buildMethodArn as on, listTargets as or, SOFT_RELOAD_COMPLETION_LOG_SUFFIX as ot, isCustomResourceLambdaTarget as p, buildCorsConfigByApiId as pn, discoverRoutes as pr, A2A_CONTAINER_PORT as pt, ImageOverrideError as q, LocalStateSourceError as qn, filterRoutesByApiIdentifiers as qt, coerceServeRequest as r, verifyCognitoJwt as rn, resolveWatchConfig as rr, buildCloudMapIndex as rt, createStudioServeManager as s, computeRequestIdentityHash as sn, availableWebSocketApiIdentifiers as sr, setShadowReadyTimeoutMs as st, addStudioSpecificOptions as t, buildJwksUrlFromIssuer as tn, collectSsmParameterRefs as tr, isLocalCdkAssetImage as tt, reinvoke as u, invokeTokenAuthorizer as un, filterWebSocketApisByIdentifiers as ur, addInvokeAgentCoreSpecificOptions as ut, renderStudioHtml as v, translateLambdaResponse as vn, AGENTCORE_HTTP_PROTOCOL as vr, MCP_PROTOCOL_VERSION as vt, addAlbSpecificOptions as w, selectIntegrationResponse as wn, derivePseudoParametersFromRegion as wr, invokeAgentCore as wt, addListSpecificOptions as x, buildRestV1Event as xn, AgentCoreResolutionError as xr, AGENTCORE_SIGV4_SERVICE as xt, createStudioStore as y, applyAuthorizerOverlay as yn, AGENTCORE_MCP_PROTOCOL as yr, mcpInvokeOnce as yt, addImageOverrideOptions as z, resolveRuntimeCodeMountPath as zn, createAuthorizerCache as zt };
|
|
31804
|
+
//# sourceMappingURL=local-studio-BQBMYRAg.js.map
|