cdk-local 0.80.0 → 0.81.1

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { G as createLocalInvokeAgentCoreCommand, f as createLocalStartServiceCommand, gt as createLocalStartApiCommand, h as createLocalRunTaskCommand, mt as createLocalInvokeCommand, n as createLocalListCommand, o as createLocalStartAlbCommand } from "./local-list-DBlBRfA-.js";
3
- import { i as createLocalStudioCommand } from "./local-studio-MXuSiMqC.js";
3
+ import { i as createLocalStudioCommand } from "./local-studio-DvNmnaF0.js";
4
4
  import { Command } from "commander";
5
5
 
6
6
  //#region src/cli/index.ts
@@ -13,7 +13,7 @@ import { Command } from "commander";
13
13
  */
14
14
  const STUDIO_PREVIEW_ENABLED = process.env["CDKL_STUDIO_PREVIEW"] === "1";
15
15
  const program = new Command();
16
- program.name("cdkl").description("Run AWS CDK stacks locally with Docker.").version("0.80.0");
16
+ program.name("cdkl").description("Run AWS CDK stacks locally with Docker.").version("0.81.1");
17
17
  program.addCommand(createLocalInvokeCommand());
18
18
  program.addCommand(createLocalInvokeAgentCoreCommand());
19
19
  program.addCommand(createLocalStartApiCommand());
@@ -574,6 +574,24 @@ interface StudioDispatcher {
574
574
  }
575
575
  declare function createStudioDispatcher(config: StudioDispatchConfig): StudioDispatcher;
576
576
  //#endregion
577
+ //#region src/local/studio-proxy.d.ts
578
+ interface StudioProxyConfig {
579
+ bus: StudioEventBus;
580
+ target: string;
581
+ kind: StudioTargetKind;
582
+ upstream: string;
583
+ host?: string;
584
+ clock?: () => number;
585
+ idFactory?: () => string;
586
+ maxCaptureBytes?: number;
587
+ }
588
+ interface RunningStudioProxy {
589
+ url: string;
590
+ port: number;
591
+ close: () => Promise<void>;
592
+ }
593
+ declare function startStudioProxy(config: StudioProxyConfig): Promise<RunningStudioProxy>;
594
+ //#endregion
577
595
  //#region src/local/studio-serve-manager.d.ts
578
596
  interface StudioServeRequest {
579
597
  targetId: string;
@@ -605,6 +623,8 @@ interface StudioServeManagerConfig {
605
623
  stopGraceMs?: number;
606
624
  setTimeoutFn?: typeof setTimeout;
607
625
  clearTimeoutFn?: typeof clearTimeout;
626
+ proxyFactory?: (config: StudioProxyConfig) => Promise<RunningStudioProxy>;
627
+ captureRequests?: boolean;
608
628
  }
609
629
  interface StudioServeManager {
610
630
  start: (req: StudioServeRequest) => Promise<StudioServeState>;
@@ -653,5 +673,5 @@ declare function startStudioServer(options: StudioServerOptions): Promise<Runnin
653
673
  //#region src/local/studio-ui.d.ts
654
674
  declare function renderStudioHtml(appLabel: string, cliName: string): string;
655
675
  //#endregion
656
- export { A2A_CONTAINER_PORT, A2A_PATH, type A2aInvokeOptions, type A2aInvokeResult, type A2aJsonRpcRequest, AGENTCORE_A2A_PROTOCOL, AGENTCORE_AGUI_PROTOCOL, AGENTCORE_HTTP_PROTOCOL, AGENTCORE_MCP_PROTOCOL, AGENTCORE_RUNTIME_TYPE, AGENTCORE_SESSION_ID_HEADER, AGENTCORE_SIGV4_SERVICE, type AgentCoreCodeArtifact, type AgentCoreCustomClaim, type AgentCoreInvokeResult, type AgentCoreJwtAuthorizer, AgentCoreResolutionError, type AgentCoreWsResult, type ApiServerGroup, type ApiTargetSubset, type AuthorizerCache, type AuthorizerEventOverlay, type AuthorizerInfo, type BuildAgentCoreCodeImageOptions, type BuildContainerImageOptions, type CachedAuthorizerResult, type CdkWatchConfig, type CloudMapIndex, CloudMapRegistry, ConnectionRegistry, type ConnectionRegistryEntry, type CorsConfig, type CredentialsLoader, DEFAULT_SHADOW_READY_TIMEOUT_MS, type DiscoveredRoute, type DiscoveredWebSocketApi, type DiscoveryJwtAuthorizer, type DownloadS3BundleOptions, type EcsServiceEmulatorOptions, EcsTaskResolutionError, type EmulatorStrategy, type EnvOverrideFile, type ExtractedS3Bundle, type FileWatcher, type FileWatcherOptions, type FrontDoorForwardTarget, type FrontDoorPlan, HOST_GATEWAY_MIN_VERSION, type HttpRequestSnapshot, type ImageOverrideEntry, ImageOverrideError, type ImageOverrideGlobals, type ImageOverrideMap, type ImageResolutionContext, type IntegrationResponseEntry, type InvokeAgentCoreOptions, type InvokeAgentCoreWsOptions, type JwksCache, type JwtCustomClaim, type LambdaArnResolveOutcome, LocalInvokeBuildError, MAX_TASKS_SUBNET_RANGE_CAP, MCP_CONTAINER_PORT, MCP_PATH, MCP_PROTOCOL_VERSION, type MatchedRouteContext, type McpInvokeOptions, type McpInvokeResult, type McpJsonRpcRequest, type MtlsServerConfig, type PerServiceBuildInputs, type PinnedTargetEntry, type PlannedAction, type PlannedEcsForwardTarget, type PlannedFixedResponseAction, type PlannedForwardAction, type PlannedForwardTarget, type PlannedFrontDoorListener, type PlannedLambdaForwardTarget, type PlannedRedirectAction, type RawImageOverrideFlags, type RegistrationHandle, type ReloadAssetContext, type ReloadVerdict, type RequestParameterContext, type ResolveParametersOutcome, type ResolvedAgentCoreRuntime, type ResolvedListenerAction, type ResolvedStage, type RestV1IntegrationConfig, type RouteMatchResult, type RouteWithAuth, type RunningStudioServer, type S3BundleCredentials, type S3BundleLocation, SOFT_RELOAD_COMPLETION_LOG_SUFFIX, SUPPORTED_CODE_RUNTIMES, type ServerState, type ServiceBoot, type SigV4Credentials, type SignAgentCoreInvocationOptions, type SignedAgentCoreHeaders, type StartedApiServer, type StudioDispatchConfig, type StudioDispatcher, StudioEventBus, type StudioInvocationEvent, type StudioLogEvent, type StudioRunRequest, type StudioRunResult, type StudioServeEvent, type StudioServeManager, type StudioServeManagerConfig, type StudioServeRequest, type StudioServeState, type StudioServerOptions, type StudioStopRequest, type StudioTarget, type StudioTargetGroup, type StudioTargetKind, type TranslatedHttpResponse, VtlEvaluationError, type WarnedAt, type WatchPredicates, type WebSocketHandshakeSnapshot, type WebSocketLambdaEvent, type WebSocketRouteEntry, a2aInvokeOnce, addAlbSpecificOptions, addCommonEcsServiceOptions, addEcsAssumeRoleOptions, addImageOverrideOptions, addInvokeAgentCoreSpecificOptions, addInvokeSpecificOptions, addListSpecificOptions, addRunTaskSpecificOptions, addStartApiSpecificOptions, addStartServiceSpecificOptions, addStudioSpecificOptions, albStrategy, applyAuthorizerOverlay, applyCorsResponseHeaders, architectureToPlatform, attachAuthorizers, attachContainerLogStreamer, attachStageContext, availableApiIdentifiers, bufferToBody, buildAgentCoreCodeImage, buildCloudMapIndex, buildCognitoJwksUrl, buildConnectEvent, buildContainerImage, buildCorsConfigByApiId, buildCorsConfigFromCloudFrontChain, buildDisconnectEvent, buildEcsImageResolutionContext, buildHttpApiV2Event, buildImageOverrideTag, buildJwksUrlFromIssuer, buildMessageEvent, buildMethodArn, buildMgmtEndpointEnvUrl, buildRestV1Event, buildStageMap, buildStsClientConfig, classifySourceChange, coerceRunRequest, coerceStopRequest, computeCodeImageTag, computeRequestIdentityHash, createAuthorizerCache, createFileWatcher, createJwksCache, createStudioDispatcher, createStudioServeManager, createWatchPredicates, defaultCredentialsLoader, derivePseudoParametersFromRegion, describePinnedImageUri, discoverRoutes, discoverWebSocketApis, discoverWebSocketApisOrThrow, downloadAndExtractS3Bundle, ecsClusterOption, enforceImageOverrideOrphans, evaluateCachedLambdaPolicy, evaluateResponseParameters, filterRoutesByApiIdentifier, filterRoutesByApiIdentifiers, formatStateRemedy, getContainerNetworkIp, groupRoutesByServer, handleConnectionsRequest, invokeAgentCore, invokeAgentCoreWs, invokeRequestAuthorizer, invokeTokenAuthorizer, isApplicationLoadBalancer, isFunctionUrlOacFronted, isLocalCdkAssetImage, listPinnedTargets, matchPreflight, matchRoute, materializeLayerFromArn, mcpInvokeOnce, mergeForService, parseConnectionsPath, parseImageOverrideFlags, parseLbPortOverrides, parseMaxTasks, parseRestartPolicy, parseSelectionExpressionPath, parseSseForJsonRpc, pickAgentCoreCandidateStack, pickRefLogicalId, pickResponseTemplate, probeHostGatewaySupport, readMtlsMaterialsFromDisk, renderCodeDockerfile, renderStudioHtml, resolveAgentCoreTarget, resolveAlbFrontDoor, resolveAlbTarget, resolveApiTargetSubset, resolveEcsAssumeRoleOption, resolveEnvVars, resolveImageOverrides, resolveLambdaArnIntrinsic, resolveProfileCredentials, resolveRuntimeCodeMountPath, resolveRuntimeFileExtension, resolveRuntimeImage, resolveSelectionExpression, resolveServiceIntegrationParameters, resolveSharedSidecarCredentials, resolveSingleTarget, resolveWatchConfig, runEcsServiceEmulator, runImageOverrideBuilds, selectIntegrationResponse, serviceStrategy, setShadowReadyTimeoutMs, signAgentCoreInvocation, startApiServer, startStudioServer, substituteImagePlaceholders, toCmdArgv, toStudioTargetGroups, translateLambdaResponse, tryParseStatus, tryResolveImageFnJoin, verifyCognitoJwt, verifyJwtAuthorizer, verifyJwtViaDiscovery, waitForAgentCorePing };
676
+ export { A2A_CONTAINER_PORT, A2A_PATH, type A2aInvokeOptions, type A2aInvokeResult, type A2aJsonRpcRequest, AGENTCORE_A2A_PROTOCOL, AGENTCORE_AGUI_PROTOCOL, AGENTCORE_HTTP_PROTOCOL, AGENTCORE_MCP_PROTOCOL, AGENTCORE_RUNTIME_TYPE, AGENTCORE_SESSION_ID_HEADER, AGENTCORE_SIGV4_SERVICE, type AgentCoreCodeArtifact, type AgentCoreCustomClaim, type AgentCoreInvokeResult, type AgentCoreJwtAuthorizer, AgentCoreResolutionError, type AgentCoreWsResult, type ApiServerGroup, type ApiTargetSubset, type AuthorizerCache, type AuthorizerEventOverlay, type AuthorizerInfo, type BuildAgentCoreCodeImageOptions, type BuildContainerImageOptions, type CachedAuthorizerResult, type CdkWatchConfig, type CloudMapIndex, CloudMapRegistry, ConnectionRegistry, type ConnectionRegistryEntry, type CorsConfig, type CredentialsLoader, DEFAULT_SHADOW_READY_TIMEOUT_MS, type DiscoveredRoute, type DiscoveredWebSocketApi, type DiscoveryJwtAuthorizer, type DownloadS3BundleOptions, type EcsServiceEmulatorOptions, EcsTaskResolutionError, type EmulatorStrategy, type EnvOverrideFile, type ExtractedS3Bundle, type FileWatcher, type FileWatcherOptions, type FrontDoorForwardTarget, type FrontDoorPlan, HOST_GATEWAY_MIN_VERSION, type HttpRequestSnapshot, type ImageOverrideEntry, ImageOverrideError, type ImageOverrideGlobals, type ImageOverrideMap, type ImageResolutionContext, type IntegrationResponseEntry, type InvokeAgentCoreOptions, type InvokeAgentCoreWsOptions, type JwksCache, type JwtCustomClaim, type LambdaArnResolveOutcome, LocalInvokeBuildError, MAX_TASKS_SUBNET_RANGE_CAP, MCP_CONTAINER_PORT, MCP_PATH, MCP_PROTOCOL_VERSION, type MatchedRouteContext, type McpInvokeOptions, type McpInvokeResult, type McpJsonRpcRequest, type MtlsServerConfig, type PerServiceBuildInputs, type PinnedTargetEntry, type PlannedAction, type PlannedEcsForwardTarget, type PlannedFixedResponseAction, type PlannedForwardAction, type PlannedForwardTarget, type PlannedFrontDoorListener, type PlannedLambdaForwardTarget, type PlannedRedirectAction, type RawImageOverrideFlags, type RegistrationHandle, type ReloadAssetContext, type ReloadVerdict, type RequestParameterContext, type ResolveParametersOutcome, type ResolvedAgentCoreRuntime, type ResolvedListenerAction, type ResolvedStage, type RestV1IntegrationConfig, type RouteMatchResult, type RouteWithAuth, type RunningStudioProxy, type RunningStudioServer, type S3BundleCredentials, type S3BundleLocation, SOFT_RELOAD_COMPLETION_LOG_SUFFIX, SUPPORTED_CODE_RUNTIMES, type ServerState, type ServiceBoot, type SigV4Credentials, type SignAgentCoreInvocationOptions, type SignedAgentCoreHeaders, type StartedApiServer, type StudioDispatchConfig, type StudioDispatcher, StudioEventBus, type StudioInvocationEvent, type StudioLogEvent, type StudioProxyConfig, type StudioRunRequest, type StudioRunResult, type StudioServeEvent, type StudioServeManager, type StudioServeManagerConfig, type StudioServeRequest, type StudioServeState, type StudioServerOptions, type StudioStopRequest, type StudioTarget, type StudioTargetGroup, type StudioTargetKind, type TranslatedHttpResponse, VtlEvaluationError, type WarnedAt, type WatchPredicates, type WebSocketHandshakeSnapshot, type WebSocketLambdaEvent, type WebSocketRouteEntry, a2aInvokeOnce, addAlbSpecificOptions, addCommonEcsServiceOptions, addEcsAssumeRoleOptions, addImageOverrideOptions, addInvokeAgentCoreSpecificOptions, addInvokeSpecificOptions, addListSpecificOptions, addRunTaskSpecificOptions, addStartApiSpecificOptions, addStartServiceSpecificOptions, addStudioSpecificOptions, albStrategy, applyAuthorizerOverlay, applyCorsResponseHeaders, architectureToPlatform, attachAuthorizers, attachContainerLogStreamer, attachStageContext, availableApiIdentifiers, bufferToBody, buildAgentCoreCodeImage, buildCloudMapIndex, buildCognitoJwksUrl, buildConnectEvent, buildContainerImage, buildCorsConfigByApiId, buildCorsConfigFromCloudFrontChain, buildDisconnectEvent, buildEcsImageResolutionContext, buildHttpApiV2Event, buildImageOverrideTag, buildJwksUrlFromIssuer, buildMessageEvent, buildMethodArn, buildMgmtEndpointEnvUrl, buildRestV1Event, buildStageMap, buildStsClientConfig, classifySourceChange, coerceRunRequest, coerceStopRequest, computeCodeImageTag, computeRequestIdentityHash, createAuthorizerCache, createFileWatcher, createJwksCache, createStudioDispatcher, createStudioServeManager, createWatchPredicates, defaultCredentialsLoader, derivePseudoParametersFromRegion, describePinnedImageUri, discoverRoutes, discoverWebSocketApis, discoverWebSocketApisOrThrow, downloadAndExtractS3Bundle, ecsClusterOption, enforceImageOverrideOrphans, evaluateCachedLambdaPolicy, evaluateResponseParameters, filterRoutesByApiIdentifier, filterRoutesByApiIdentifiers, formatStateRemedy, getContainerNetworkIp, groupRoutesByServer, handleConnectionsRequest, invokeAgentCore, invokeAgentCoreWs, invokeRequestAuthorizer, invokeTokenAuthorizer, isApplicationLoadBalancer, isFunctionUrlOacFronted, isLocalCdkAssetImage, listPinnedTargets, matchPreflight, matchRoute, materializeLayerFromArn, mcpInvokeOnce, mergeForService, parseConnectionsPath, parseImageOverrideFlags, parseLbPortOverrides, parseMaxTasks, parseRestartPolicy, parseSelectionExpressionPath, parseSseForJsonRpc, pickAgentCoreCandidateStack, pickRefLogicalId, pickResponseTemplate, probeHostGatewaySupport, readMtlsMaterialsFromDisk, renderCodeDockerfile, renderStudioHtml, resolveAgentCoreTarget, resolveAlbFrontDoor, resolveAlbTarget, resolveApiTargetSubset, resolveEcsAssumeRoleOption, resolveEnvVars, resolveImageOverrides, resolveLambdaArnIntrinsic, resolveProfileCredentials, resolveRuntimeCodeMountPath, resolveRuntimeFileExtension, resolveRuntimeImage, resolveSelectionExpression, resolveServiceIntegrationParameters, resolveSharedSidecarCredentials, resolveSingleTarget, resolveWatchConfig, runEcsServiceEmulator, runImageOverrideBuilds, selectIntegrationResponse, serviceStrategy, setShadowReadyTimeoutMs, signAgentCoreInvocation, startApiServer, startStudioProxy, startStudioServer, substituteImagePlaceholders, toCmdArgv, toStudioTargetGroups, translateLambdaResponse, tryParseStatus, tryResolveImageFnJoin, verifyCognitoJwt, verifyJwtAuthorizer, verifyJwtViaDiscovery, waitForAgentCorePing };
657
677
  //# sourceMappingURL=internal.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"internal.d.ts","names":[],"sources":["../src/types/assets.ts","../src/local/intrinsic-utils.ts","../src/local/intrinsic-lambda-arn.ts","../src/local/parameter-mapping.ts","../src/local/api-gateway-response.ts","../src/local/docker-inspect.ts","../src/local/route-matcher.ts","../src/local/api-gateway-event.ts","../src/local/websocket-route-discovery.ts","../src/local/lambda-authorizer.ts","../src/local/stage-resolver.ts","../src/local/runtime-image.ts","../src/local/websocket-event.ts","../src/local/websocket-mgmt-api.ts","../src/local/websocket-body.ts","../src/local/docker-version.ts","../src/local/api-server-grouping.ts","../src/local/layer-arn-materializer.ts","../src/local/agentcore-code-build.ts","../src/local/agentcore-s3-bundle.ts","../src/local/agentcore-sigv4-sign.ts","../src/local/docker-image-builder.ts","../src/local/image-pin-detector.ts","../src/local/target-picker.ts","../src/local/image-override-engine.ts","../src/local/container-log-streamer.ts","../src/local/studio-events.ts","../src/local/studio-dispatch.ts","../src/local/studio-serve-manager.ts","../src/cli/commands/local-studio.ts","../src/local/studio-server.ts","../src/local/studio-ui.ts"],"mappings":"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"}
1
+ {"version":3,"file":"internal.d.ts","names":[],"sources":["../src/types/assets.ts","../src/local/intrinsic-utils.ts","../src/local/intrinsic-lambda-arn.ts","../src/local/parameter-mapping.ts","../src/local/api-gateway-response.ts","../src/local/docker-inspect.ts","../src/local/route-matcher.ts","../src/local/api-gateway-event.ts","../src/local/websocket-route-discovery.ts","../src/local/lambda-authorizer.ts","../src/local/stage-resolver.ts","../src/local/runtime-image.ts","../src/local/websocket-event.ts","../src/local/websocket-mgmt-api.ts","../src/local/websocket-body.ts","../src/local/docker-version.ts","../src/local/api-server-grouping.ts","../src/local/layer-arn-materializer.ts","../src/local/agentcore-code-build.ts","../src/local/agentcore-s3-bundle.ts","../src/local/agentcore-sigv4-sign.ts","../src/local/docker-image-builder.ts","../src/local/image-pin-detector.ts","../src/local/target-picker.ts","../src/local/image-override-engine.ts","../src/local/container-log-streamer.ts","../src/local/studio-events.ts","../src/local/studio-dispatch.ts","../src/local/studio-proxy.ts","../src/local/studio-serve-manager.ts","../src/cli/commands/local-studio.ts","../src/local/studio-server.ts","../src/local/studio-ui.ts"],"mappings":"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"}
package/dist/internal.js CHANGED
@@ -1,4 +1,4 @@
1
1
  import { $ as mcpInvokeOnce, $n as AGENTCORE_RUNTIME_TYPE, $t as applyAuthorizerOverlay, A as mergeForService, At as startApiServer, B as SOFT_RELOAD_COMPLETION_LOG_SUFFIX, Bn as resolveSingleTarget, Bt as buildMethodArn, C as parseRestartPolicy, Ct as materializeLayerFromArn, D as ImageOverrideError, Dt as filterRoutesByApiIdentifiers, E as runEcsServiceEmulator, Et as filterRoutesByApiIdentifier, F as isLocalCdkAssetImage, Ft as buildJwksUrlFromIssuer, Gn as parseSelectionExpressionPath, Gt as attachAuthorizers, H as getContainerNetworkIp, Ht as evaluateCachedLambdaPolicy, I as listPinnedTargets, It as createJwksCache, J as A2A_PATH, Jn as resolveLambdaArnIntrinsic, Jt as buildCorsConfigFromCloudFrontChain, K as invokeAgentCoreWs, Kn as discoverRoutes, Kt as applyCorsResponseHeaders, L as buildCloudMapIndex, Lt as verifyCognitoJwt, M as resolveImageOverrides, Mt as resolveServiceIntegrationParameters, N as runImageOverrideBuilds, Nt as defaultCredentialsLoader, O as buildImageOverrideTag, Ot as groupRoutesByServer, P as describePinnedImageUri, Pt as buildCognitoJwksUrl, Q as MCP_PROTOCOL_VERSION, Qn as AGENTCORE_MCP_PROTOCOL, Qt as translateLambdaResponse, R as CloudMapRegistry, Rn as resolveWatchConfig, Rt as verifyJwtAuthorizer, S as parseMaxTasks, Sn as EcsTaskResolutionError, St as buildStageMap, T as resolveSharedSidecarCredentials, Tt as availableApiIdentifiers, U as attachContainerLogStreamer, Un as discoverWebSocketApis, Ut as invokeRequestAuthorizer, V as setShadowReadyTimeoutMs, Vt as computeRequestIdentityHash, W as addInvokeAgentCoreSpecificOptions, Wn as discoverWebSocketApisOrThrow, Wt as invokeTokenAuthorizer, X as MCP_CONTAINER_PORT, Xn as AGENTCORE_AGUI_PROTOCOL, Xt as matchPreflight, Y as a2aInvokeOnce, Yn as AGENTCORE_A2A_PROTOCOL, Yt as isFunctionUrlOacFronted, Z as MCP_PATH, Zn as AGENTCORE_HTTP_PROTOCOL, Zt as matchRoute, _ as addCommonEcsServiceOptions, _n as architectureToPlatform, _t as createWatchPredicates, a as albStrategy, an as tryParseStatus, ar as substituteImagePlaceholders, at as waitForAgentCorePing, b as buildEcsImageResolutionContext, bn as resolveRuntimeFileExtension, bt as createFileWatcher, c as resolveAlbTarget, cn as probeHostGatewaySupport, ct as buildAgentCoreCodeImage, d as addStartServiceSpecificOptions, dn as buildMgmtEndpointEnvUrl, dr as resolveProfileCredentials, dt as toCmdArgv, en as buildHttpApiV2Event, er as AgentCoreResolutionError, et as parseSseForJsonRpc, fn as handleConnectionsRequest, ft as classifySourceChange, g as MAX_TASKS_SUBNET_RANGE_CAP, gn as buildMessageEvent, hn as buildDisconnectEvent, ht as addStartApiSpecificOptions, i as addAlbSpecificOptions, in as selectIntegrationResponse, ir as formatStateRemedy, it as invokeAgentCore, j as parseImageOverrideFlags, jt as resolveSelectionExpression, k as enforceImageOverrideOrphans, kt as readMtlsMaterialsFromDisk, l as isApplicationLoadBalancer, ln as bufferToBody, lt as computeCodeImageTag, m as addRunTaskSpecificOptions, mn as buildConnectEvent, nn as evaluateResponseParameters, nr as resolveAgentCoreTarget, nt as signAgentCoreInvocation, on as VtlEvaluationError, or as tryResolveImageFnJoin, ot as downloadAndExtractS3Bundle, p as serviceStrategy, pn as parseConnectionsPath, pt as addInvokeSpecificOptions, q as A2A_CONTAINER_PORT, qn as pickRefLogicalId, qt as buildCorsConfigByApiId, rn as pickResponseTemplate, rr as derivePseudoParametersFromRegion, rt as AGENTCORE_SESSION_ID_HEADER, s as parseLbPortOverrides, sn as HOST_GATEWAY_MIN_VERSION, sr as LocalInvokeBuildError, st as SUPPORTED_CODE_RUNTIMES, t as addListSpecificOptions, tn as buildRestV1Event, tr as pickAgentCoreCandidateStack, tt as AGENTCORE_SIGV4_SERVICE, u as resolveAlbFrontDoor, un as ConnectionRegistry, ur as buildStsClientConfig, ut as renderCodeDockerfile, v as addEcsAssumeRoleOptions, vn as buildContainerImage, vt as resolveApiTargetSubset, w as resolveEcsAssumeRoleOption, wt as resolveEnvVars, x as ecsClusterOption, xn as resolveRuntimeImage, xt as attachStageContext, y as addImageOverrideOptions, yn as resolveRuntimeCodeMountPath, yt as createAuthorizerCache, z as DEFAULT_SHADOW_READY_TIMEOUT_MS, zt as verifyJwtViaDiscovery } from "./local-list-DBlBRfA-.js";
2
- import { a as createStudioServeManager, c as toStudioTargetGroups, l as renderStudioHtml, n as coerceRunRequest, o as createStudioDispatcher, r as coerceStopRequest, s as startStudioServer, t as addStudioSpecificOptions, u as StudioEventBus } from "./local-studio-MXuSiMqC.js";
2
+ import { a as createStudioServeManager, c as startStudioServer, d as StudioEventBus, l as toStudioTargetGroups, n as coerceRunRequest, o as startStudioProxy, r as coerceStopRequest, s as createStudioDispatcher, t as addStudioSpecificOptions, u as renderStudioHtml } from "./local-studio-DvNmnaF0.js";
3
3
 
4
- export { A2A_CONTAINER_PORT, A2A_PATH, AGENTCORE_A2A_PROTOCOL, AGENTCORE_AGUI_PROTOCOL, AGENTCORE_HTTP_PROTOCOL, AGENTCORE_MCP_PROTOCOL, AGENTCORE_RUNTIME_TYPE, AGENTCORE_SESSION_ID_HEADER, AGENTCORE_SIGV4_SERVICE, AgentCoreResolutionError, CloudMapRegistry, ConnectionRegistry, DEFAULT_SHADOW_READY_TIMEOUT_MS, EcsTaskResolutionError, HOST_GATEWAY_MIN_VERSION, ImageOverrideError, LocalInvokeBuildError, MAX_TASKS_SUBNET_RANGE_CAP, MCP_CONTAINER_PORT, MCP_PATH, MCP_PROTOCOL_VERSION, SOFT_RELOAD_COMPLETION_LOG_SUFFIX, SUPPORTED_CODE_RUNTIMES, StudioEventBus, VtlEvaluationError, a2aInvokeOnce, addAlbSpecificOptions, addCommonEcsServiceOptions, addEcsAssumeRoleOptions, addImageOverrideOptions, addInvokeAgentCoreSpecificOptions, addInvokeSpecificOptions, addListSpecificOptions, addRunTaskSpecificOptions, addStartApiSpecificOptions, addStartServiceSpecificOptions, addStudioSpecificOptions, albStrategy, applyAuthorizerOverlay, applyCorsResponseHeaders, architectureToPlatform, attachAuthorizers, attachContainerLogStreamer, attachStageContext, availableApiIdentifiers, bufferToBody, buildAgentCoreCodeImage, buildCloudMapIndex, buildCognitoJwksUrl, buildConnectEvent, buildContainerImage, buildCorsConfigByApiId, buildCorsConfigFromCloudFrontChain, buildDisconnectEvent, buildEcsImageResolutionContext, buildHttpApiV2Event, buildImageOverrideTag, buildJwksUrlFromIssuer, buildMessageEvent, buildMethodArn, buildMgmtEndpointEnvUrl, buildRestV1Event, buildStageMap, buildStsClientConfig, classifySourceChange, coerceRunRequest, coerceStopRequest, computeCodeImageTag, computeRequestIdentityHash, createAuthorizerCache, createFileWatcher, createJwksCache, createStudioDispatcher, createStudioServeManager, createWatchPredicates, defaultCredentialsLoader, derivePseudoParametersFromRegion, describePinnedImageUri, discoverRoutes, discoverWebSocketApis, discoverWebSocketApisOrThrow, downloadAndExtractS3Bundle, ecsClusterOption, enforceImageOverrideOrphans, evaluateCachedLambdaPolicy, evaluateResponseParameters, filterRoutesByApiIdentifier, filterRoutesByApiIdentifiers, formatStateRemedy, getContainerNetworkIp, groupRoutesByServer, handleConnectionsRequest, invokeAgentCore, invokeAgentCoreWs, invokeRequestAuthorizer, invokeTokenAuthorizer, isApplicationLoadBalancer, isFunctionUrlOacFronted, isLocalCdkAssetImage, listPinnedTargets, matchPreflight, matchRoute, materializeLayerFromArn, mcpInvokeOnce, mergeForService, parseConnectionsPath, parseImageOverrideFlags, parseLbPortOverrides, parseMaxTasks, parseRestartPolicy, parseSelectionExpressionPath, parseSseForJsonRpc, pickAgentCoreCandidateStack, pickRefLogicalId, pickResponseTemplate, probeHostGatewaySupport, readMtlsMaterialsFromDisk, renderCodeDockerfile, renderStudioHtml, resolveAgentCoreTarget, resolveAlbFrontDoor, resolveAlbTarget, resolveApiTargetSubset, resolveEcsAssumeRoleOption, resolveEnvVars, resolveImageOverrides, resolveLambdaArnIntrinsic, resolveProfileCredentials, resolveRuntimeCodeMountPath, resolveRuntimeFileExtension, resolveRuntimeImage, resolveSelectionExpression, resolveServiceIntegrationParameters, resolveSharedSidecarCredentials, resolveSingleTarget, resolveWatchConfig, runEcsServiceEmulator, runImageOverrideBuilds, selectIntegrationResponse, serviceStrategy, setShadowReadyTimeoutMs, signAgentCoreInvocation, startApiServer, startStudioServer, substituteImagePlaceholders, toCmdArgv, toStudioTargetGroups, translateLambdaResponse, tryParseStatus, tryResolveImageFnJoin, verifyCognitoJwt, verifyJwtAuthorizer, verifyJwtViaDiscovery, waitForAgentCorePing };
4
+ export { A2A_CONTAINER_PORT, A2A_PATH, AGENTCORE_A2A_PROTOCOL, AGENTCORE_AGUI_PROTOCOL, AGENTCORE_HTTP_PROTOCOL, AGENTCORE_MCP_PROTOCOL, AGENTCORE_RUNTIME_TYPE, AGENTCORE_SESSION_ID_HEADER, AGENTCORE_SIGV4_SERVICE, AgentCoreResolutionError, CloudMapRegistry, ConnectionRegistry, DEFAULT_SHADOW_READY_TIMEOUT_MS, EcsTaskResolutionError, HOST_GATEWAY_MIN_VERSION, ImageOverrideError, LocalInvokeBuildError, MAX_TASKS_SUBNET_RANGE_CAP, MCP_CONTAINER_PORT, MCP_PATH, MCP_PROTOCOL_VERSION, SOFT_RELOAD_COMPLETION_LOG_SUFFIX, SUPPORTED_CODE_RUNTIMES, StudioEventBus, VtlEvaluationError, a2aInvokeOnce, addAlbSpecificOptions, addCommonEcsServiceOptions, addEcsAssumeRoleOptions, addImageOverrideOptions, addInvokeAgentCoreSpecificOptions, addInvokeSpecificOptions, addListSpecificOptions, addRunTaskSpecificOptions, addStartApiSpecificOptions, addStartServiceSpecificOptions, addStudioSpecificOptions, albStrategy, applyAuthorizerOverlay, applyCorsResponseHeaders, architectureToPlatform, attachAuthorizers, attachContainerLogStreamer, attachStageContext, availableApiIdentifiers, bufferToBody, buildAgentCoreCodeImage, buildCloudMapIndex, buildCognitoJwksUrl, buildConnectEvent, buildContainerImage, buildCorsConfigByApiId, buildCorsConfigFromCloudFrontChain, buildDisconnectEvent, buildEcsImageResolutionContext, buildHttpApiV2Event, buildImageOverrideTag, buildJwksUrlFromIssuer, buildMessageEvent, buildMethodArn, buildMgmtEndpointEnvUrl, buildRestV1Event, buildStageMap, buildStsClientConfig, classifySourceChange, coerceRunRequest, coerceStopRequest, computeCodeImageTag, computeRequestIdentityHash, createAuthorizerCache, createFileWatcher, createJwksCache, createStudioDispatcher, createStudioServeManager, createWatchPredicates, defaultCredentialsLoader, derivePseudoParametersFromRegion, describePinnedImageUri, discoverRoutes, discoverWebSocketApis, discoverWebSocketApisOrThrow, downloadAndExtractS3Bundle, ecsClusterOption, enforceImageOverrideOrphans, evaluateCachedLambdaPolicy, evaluateResponseParameters, filterRoutesByApiIdentifier, filterRoutesByApiIdentifiers, formatStateRemedy, getContainerNetworkIp, groupRoutesByServer, handleConnectionsRequest, invokeAgentCore, invokeAgentCoreWs, invokeRequestAuthorizer, invokeTokenAuthorizer, isApplicationLoadBalancer, isFunctionUrlOacFronted, isLocalCdkAssetImage, listPinnedTargets, matchPreflight, matchRoute, materializeLayerFromArn, mcpInvokeOnce, mergeForService, parseConnectionsPath, parseImageOverrideFlags, parseLbPortOverrides, parseMaxTasks, parseRestartPolicy, parseSelectionExpressionPath, parseSseForJsonRpc, pickAgentCoreCandidateStack, pickRefLogicalId, pickResponseTemplate, probeHostGatewaySupport, readMtlsMaterialsFromDisk, renderCodeDockerfile, renderStudioHtml, resolveAgentCoreTarget, resolveAlbFrontDoor, resolveAlbTarget, resolveApiTargetSubset, resolveEcsAssumeRoleOption, resolveEnvVars, resolveImageOverrides, resolveLambdaArnIntrinsic, resolveProfileCredentials, resolveRuntimeCodeMountPath, resolveRuntimeFileExtension, resolveRuntimeImage, resolveSelectionExpression, resolveServiceIntegrationParameters, resolveSharedSidecarCredentials, resolveSingleTarget, resolveWatchConfig, runEcsServiceEmulator, runImageOverrideBuilds, selectIntegrationResponse, serviceStrategy, setShadowReadyTimeoutMs, signAgentCoreInvocation, startApiServer, startStudioProxy, startStudioServer, substituteImagePlaceholders, toCmdArgv, toStudioTargetGroups, translateLambdaResponse, tryParseStatus, tryResolveImageFnJoin, verifyCognitoJwt, verifyJwtAuthorizer, verifyJwtViaDiscovery, waitForAgentCorePing };
@@ -5,7 +5,8 @@ import { tmpdir } from "node:os";
5
5
  import { join } from "node:path";
6
6
  import { Command, Option } from "commander";
7
7
  import { spawn } from "node:child_process";
8
- import { createServer } from "node:http";
8
+ import { connect } from "node:net";
9
+ import { createServer as createServer$1, request } from "node:http";
9
10
  import { EventEmitter } from "node:events";
10
11
 
11
12
  //#region src/local/studio-events.ts
@@ -60,8 +61,10 @@ var StudioEventBus = class {
60
61
  * event composer (textarea + Invoke button) with the latest run's
61
62
  * Request / Response / Logs shown below; for an API, a Start/Stop
62
63
  * control with the served endpoints + streaming logs.
63
- * - right = the timeline (history) of every invocation; clicking a
64
- * row loads it back into the workspace.
64
+ * - right = the timeline (history) of every invocation AND every
65
+ * captured serve request (slice C2); clicking a Lambda row reloads
66
+ * it into the composer, clicking a captured request row opens a
67
+ * read-only Request / Response detail.
65
68
  *
66
69
  * The center workspace is deliberately adjacent to the left target list
67
70
  * (short eye-travel: pick a target -> compose right next to it), and is
@@ -160,8 +163,9 @@ const STUDIO_SCRIPT = `
160
163
  const serveMeta = new Map(); // serve targetId -> { dot, btnSlot } row controls
161
164
  const serveState = new Map(); // serve targetId -> { status, endpoints }
162
165
  let active = null; // { id, kind, ta, btn, msg, result }
163
- let shownInvId = null; // invocation whose result is in the workspace
166
+ let shownInvId = null; // lambda invocation whose result is in the workspace
164
167
  let shownServeId = null; // serve target whose workspace is shown
168
+ let shownDetailId = null; // captured request whose read-only detail is shown
165
169
 
166
170
  function el(tag, cls, text) {
167
171
  const e = document.createElement(tag);
@@ -270,6 +274,7 @@ const STUDIO_SCRIPT = `
270
274
 
271
275
  function selectTarget(id, kind) {
272
276
  highlightTarget(id);
277
+ shownDetailId = null;
273
278
  if (kind === 'api') {
274
279
  shownServeId = id;
275
280
  shownInvId = null;
@@ -394,6 +399,7 @@ const STUDIO_SCRIPT = `
394
399
  active = { id, kind, ta, btn, msg, result };
395
400
  btn.onclick = () => runInvoke();
396
401
  shownInvId = null;
402
+ shownDetailId = null;
397
403
  ta.focus();
398
404
  }
399
405
 
@@ -494,8 +500,9 @@ const STUDIO_SCRIPT = `
494
500
  : '…';
495
501
  statusEl.className = 'status' + (merged.status != null && (merged.status < 200 || merged.status >= 300) ? ' err' : '');
496
502
 
497
- // Live-refresh the workspace result if it is showing this invocation.
503
+ // Live-refresh the workspace if it is showing this invocation.
498
504
  if (shownInvId === ev.id) renderResult(ev.id);
505
+ if (shownDetailId === ev.id) renderCapturedDetail(ev.id);
499
506
  }
500
507
 
501
508
  function loadInvocation(id) {
@@ -505,9 +512,49 @@ const STUDIO_SCRIPT = `
505
512
  const row = rowsById.get(id);
506
513
  if (row) row.classList.add('sel');
507
514
  highlightTarget(ev.target);
508
- renderComposer(ev.target, ev.kind, ev.request != null ? fmt(ev.request) : '{}');
509
- shownInvId = id;
510
- renderResult(id);
515
+ if (ev.kind === 'lambda') {
516
+ // A Lambda invocation row reloads into the re-invokable composer.
517
+ shownDetailId = null;
518
+ shownServeId = null;
519
+ renderComposer(ev.target, ev.kind, ev.request != null ? fmt(ev.request) : '{}');
520
+ shownInvId = id;
521
+ renderResult(id);
522
+ } else {
523
+ // A captured serve request (slice C2) opens a READ-ONLY detail —
524
+ // re-invoking a captured request is Phase 3.
525
+ shownInvId = null;
526
+ shownServeId = null;
527
+ active = null;
528
+ renderCapturedDetail(id);
529
+ }
530
+ }
531
+
532
+ // Read-only Request / Response detail for a captured serve request.
533
+ function renderCapturedDetail(id) {
534
+ shownDetailId = id;
535
+ const ev = invById.get(id);
536
+ const ws = document.getElementById('workspace');
537
+ ws.innerHTML = '';
538
+ if (!ev) return;
539
+
540
+ const head = el('div', 'composer');
541
+ head.appendChild(el('div', 'target-name', (ev.label || 'request') + ' — ' + (ev.target || '')));
542
+ ws.appendChild(head);
543
+
544
+ const reqSec = el('div', 'section');
545
+ reqSec.appendChild(el('h3', null, 'Request'));
546
+ reqSec.appendChild(el('pre', null, ev.request != null ? fmt(ev.request) : '(none)'));
547
+ ws.appendChild(reqSec);
548
+
549
+ const respSec = el('div', 'section');
550
+ const h = el('h3', null, 'Response');
551
+ if (ev.status != null) {
552
+ const cls = ev.status >= 200 && ev.status < 300 ? 'ok' : 'bad';
553
+ h.appendChild(el('span', cls, ' ' + ev.status + (ev.durationMs != null ? ' · ' + ev.durationMs + 'ms' : '')));
554
+ }
555
+ respSec.appendChild(h);
556
+ respSec.appendChild(el('pre', null, ev.response != null ? fmt(ev.response) : '(pending…)'));
557
+ ws.appendChild(respSec);
511
558
  }
512
559
 
513
560
  function connect() {
@@ -668,7 +715,7 @@ async function startStudioServer(options) {
668
715
  const maxBump = options.maxPortBump ?? 20;
669
716
  const html = renderStudioHtml(options.appLabel, options.cliName);
670
717
  const targetsJson = JSON.stringify({ groups: options.targetGroups });
671
- const server = createServer((req, res) => handleRequest(req, res, options.bus, html, targetsJson, options));
718
+ const server = createServer$1((req, res) => handleRequest(req, res, options.bus, html, targetsJson, options));
672
719
  const boundPort = await listenWithBump(server, host, options.port, maxBump);
673
720
  return {
674
721
  url: `http://${host}:${boundPort}`,
@@ -1056,6 +1103,201 @@ function tryParseJson(raw) {
1056
1103
  }
1057
1104
  }
1058
1105
 
1106
+ //#endregion
1107
+ //#region src/local/studio-proxy.ts
1108
+ let proxyIdCounter = 0;
1109
+ /**
1110
+ * Start a capturing reverse proxy in front of a studio serve target
1111
+ * (decision D4a: because every request to the served port flows through
1112
+ * `cdkl studio`, the timeline observes them regardless of source —
1113
+ * browser, curl, or the in-UI pad alike).
1114
+ *
1115
+ * Each HTTP request is forwarded to `upstream` and, in parallel,
1116
+ * captured (method / path / headers / bounded body) and emitted as an
1117
+ * `invocation` start event; when the upstream response completes, an end
1118
+ * event carries the status / headers / bounded body / duration. The full
1119
+ * bodies stream through untouched — only the captured copies are bounded.
1120
+ * `Upgrade` (WebSocket) requests are bridged raw to the upstream without
1121
+ * capture so they keep working.
1122
+ *
1123
+ * Studio is a control plane over the CLI, so this proxy sits in front of
1124
+ * the long-running `cdkl start-api` child the serve manager spawned; it
1125
+ * does NOT re-implement any routing — it forwards verbatim.
1126
+ */
1127
+ function startStudioProxy(config) {
1128
+ const host = config.host ?? "127.0.0.1";
1129
+ const clock = config.clock ?? Date.now;
1130
+ const maxCapture = config.maxCaptureBytes ?? 64 * 1024;
1131
+ const idFactory = config.idFactory ?? (() => {
1132
+ proxyIdCounter += 1;
1133
+ return `req-${clock()}-${proxyIdCounter}`;
1134
+ });
1135
+ const upstreamUrl = new URL(config.upstream);
1136
+ const upstreamHost = upstreamUrl.hostname;
1137
+ const upstreamPort = Number(upstreamUrl.port) || 80;
1138
+ const server = createServer$1((clientReq, clientRes) => {
1139
+ const id = idFactory();
1140
+ const startedAt = clock();
1141
+ const path = clientReq.url ?? "/";
1142
+ const method = clientReq.method ?? "GET";
1143
+ const label = `${method} ${path.split("?")[0]}`;
1144
+ const reqBody = boundedCollector(maxCapture);
1145
+ config.bus.emit("invocation", {
1146
+ id,
1147
+ ts: startedAt,
1148
+ target: config.target,
1149
+ kind: config.kind,
1150
+ label,
1151
+ request: {
1152
+ method,
1153
+ path,
1154
+ headers: { ...clientReq.headers }
1155
+ }
1156
+ });
1157
+ let ended = false;
1158
+ const emitEnd = (status, response) => {
1159
+ if (ended) return;
1160
+ ended = true;
1161
+ config.bus.emit("invocation", {
1162
+ id,
1163
+ ts: startedAt,
1164
+ target: config.target,
1165
+ kind: config.kind,
1166
+ label,
1167
+ request: {
1168
+ method,
1169
+ path,
1170
+ headers: { ...clientReq.headers },
1171
+ body: reqBody.text()
1172
+ },
1173
+ response,
1174
+ status,
1175
+ durationMs: clock() - startedAt
1176
+ });
1177
+ };
1178
+ const upstreamReq = request({
1179
+ host: upstreamHost,
1180
+ port: upstreamPort,
1181
+ method,
1182
+ path,
1183
+ headers: clientReq.headers
1184
+ }, (upstreamRes) => {
1185
+ const respBody = boundedCollector(maxCapture);
1186
+ clientRes.writeHead(upstreamRes.statusCode ?? 502, stripHopByHop(upstreamRes.headers));
1187
+ upstreamRes.on("data", (chunk) => respBody.push(chunk));
1188
+ upstreamRes.pipe(clientRes);
1189
+ upstreamRes.on("end", () => emitEnd(upstreamRes.statusCode ?? 502, {
1190
+ status: upstreamRes.statusCode,
1191
+ headers: { ...upstreamRes.headers },
1192
+ body: respBody.text()
1193
+ }));
1194
+ upstreamRes.on("error", () => {
1195
+ if (!clientRes.writableEnded) clientRes.destroy();
1196
+ emitEnd(502, "upstream response stream error");
1197
+ });
1198
+ });
1199
+ upstreamReq.on("error", (err) => {
1200
+ if (!clientRes.headersSent) clientRes.writeHead(502, { "content-type": "text/plain" });
1201
+ clientRes.end(`studio proxy: upstream error: ${err.message}`);
1202
+ emitEnd(502, `upstream error: ${err.message}`);
1203
+ });
1204
+ clientReq.on("data", (chunk) => reqBody.push(chunk));
1205
+ clientReq.on("error", () => {
1206
+ upstreamReq.destroy();
1207
+ emitEnd(499, "client aborted the request");
1208
+ });
1209
+ clientReq.pipe(upstreamReq);
1210
+ });
1211
+ const upgradeSockets = /* @__PURE__ */ new Set();
1212
+ server.on("upgrade", (req, clientSocket, head) => {
1213
+ const sock = clientSocket;
1214
+ upgradeSockets.add(sock);
1215
+ sock.on("close", () => upgradeSockets.delete(sock));
1216
+ bridgeUpgrade(req, sock, head, upstreamHost, upstreamPort);
1217
+ });
1218
+ return new Promise((resolve, reject) => {
1219
+ server.once("error", reject);
1220
+ server.listen(0, host, () => {
1221
+ server.removeListener("error", reject);
1222
+ const port = server.address().port;
1223
+ resolve({
1224
+ url: `http://${host}:${port}`,
1225
+ port,
1226
+ close: () => new Promise((resolveClose, rejectClose) => {
1227
+ for (const sock of upgradeSockets) sock.destroy();
1228
+ upgradeSockets.clear();
1229
+ server.close((err) => err ? rejectClose(err) : resolveClose());
1230
+ server.closeAllConnections?.();
1231
+ })
1232
+ });
1233
+ });
1234
+ });
1235
+ }
1236
+ /** RFC 7230 hop-by-hop headers — never forwarded across a proxy. */
1237
+ const HOP_BY_HOP = new Set([
1238
+ "connection",
1239
+ "keep-alive",
1240
+ "proxy-authenticate",
1241
+ "proxy-authorization",
1242
+ "te",
1243
+ "trailer",
1244
+ "transfer-encoding",
1245
+ "upgrade"
1246
+ ]);
1247
+ /** Copy `headers` without the hop-by-hop ones (which describe one connection). */
1248
+ function stripHopByHop(headers) {
1249
+ const out = {};
1250
+ for (const [k, v] of Object.entries(headers)) if (!HOP_BY_HOP.has(k.toLowerCase())) out[k] = v;
1251
+ return out;
1252
+ }
1253
+ /** A bounded byte collector that decodes to a (possibly truncated) utf8 string. */
1254
+ function boundedCollector(maxBytes) {
1255
+ const chunks = [];
1256
+ let size = 0;
1257
+ let truncated = false;
1258
+ return {
1259
+ push: (c) => {
1260
+ if (size >= maxBytes) {
1261
+ truncated = true;
1262
+ return;
1263
+ }
1264
+ const room = maxBytes - size;
1265
+ if (c.length > room) {
1266
+ chunks.push(c.subarray(0, room));
1267
+ size = maxBytes;
1268
+ truncated = true;
1269
+ } else {
1270
+ chunks.push(c);
1271
+ size += c.length;
1272
+ }
1273
+ },
1274
+ text: () => {
1275
+ const s = Buffer.concat(chunks).toString("utf8");
1276
+ return truncated ? `${s}… (truncated)` : s;
1277
+ }
1278
+ };
1279
+ }
1280
+ /** Raw-bridge an `Upgrade` request (e.g. WebSocket) to the upstream. */
1281
+ function bridgeUpgrade(req, clientSocket, head, upstreamHost, upstreamPort) {
1282
+ const upstream = connect(upstreamPort, upstreamHost, () => {
1283
+ let raw = `${req.method} ${req.url} HTTP/1.1\r\n`;
1284
+ for (let i = 0; i < req.rawHeaders.length; i += 2) raw += `${req.rawHeaders[i]}: ${req.rawHeaders[i + 1]}\r\n`;
1285
+ raw += "\r\n";
1286
+ upstream.write(raw);
1287
+ if (head && head.length > 0) upstream.write(head);
1288
+ clientSocket.pipe(upstream);
1289
+ upstream.pipe(clientSocket);
1290
+ });
1291
+ const teardown = () => {
1292
+ clientSocket.destroy();
1293
+ upstream.destroy();
1294
+ };
1295
+ upstream.on("error", teardown);
1296
+ upstream.on("close", teardown);
1297
+ clientSocket.on("error", teardown);
1298
+ clientSocket.on("close", teardown);
1299
+ }
1300
+
1059
1301
  //#endregion
1060
1302
  //#region src/local/studio-serve-manager.ts
1061
1303
  /** Kinds the serve manager can start in this build. */
@@ -1077,9 +1319,11 @@ const LISTENING_RE = /Server listening on (\S+)/;
1077
1319
  * with the discovered endpoints. `stop()` SIGTERMs the child (SIGKILL
1078
1320
  * after a grace window) and emits `stopped`.
1079
1321
  *
1080
- * Request capture for traffic to the served port (decision D4a / D5) and
1081
- * full-text log search arrive in slice C2; C1 is lifecycle + log
1082
- * streaming only.
1322
+ * Slice C2 fronts each HTTP serve endpoint with a capture proxy
1323
+ * ({@link startStudioProxy}) so every request to the served port lands
1324
+ * on the studio timeline (decision D4a); the `endpoints` the UI is
1325
+ * handed are the proxy URLs. Full-text log search and alb / ecs serve
1326
+ * kinds are still to come.
1083
1327
  */
1084
1328
  function createStudioServeManager(config) {
1085
1329
  const spawnFn = config.spawnFn ?? spawn;
@@ -1090,7 +1334,14 @@ function createStudioServeManager(config) {
1090
1334
  const setTimeoutFn = config.setTimeoutFn ?? setTimeout;
1091
1335
  const clearTimeoutFn = config.clearTimeoutFn ?? clearTimeout;
1092
1336
  const cwd = config.cwd ?? process.cwd();
1337
+ const proxyFactory = config.proxyFactory ?? startStudioProxy;
1338
+ const captureRequests = config.captureRequests ?? true;
1093
1339
  const entries = /* @__PURE__ */ new Map();
1340
+ /** Close every capture proxy fronting `e` (best-effort; idempotent). */
1341
+ async function closeProxies(e) {
1342
+ const proxies = e.proxies.splice(0);
1343
+ await Promise.all(proxies.map((p) => p.close().catch(() => void 0)));
1344
+ }
1094
1345
  function publicState(e) {
1095
1346
  const s = {
1096
1347
  targetId: e.targetId,
@@ -1146,7 +1397,8 @@ function createStudioServeManager(config) {
1146
1397
  status: "starting",
1147
1398
  endpoints: [],
1148
1399
  startedAt,
1149
- child
1400
+ child,
1401
+ proxies: []
1150
1402
  };
1151
1403
  if (child.pid !== void 0) entry.pid = child.pid;
1152
1404
  entries.set(req.targetId, entry);
@@ -1161,6 +1413,7 @@ function createStudioServeManager(config) {
1161
1413
  entry.status = "error";
1162
1414
  emitServe(entry, `Timed out after ${readyTimeoutMs}ms waiting for the server to listen.`);
1163
1415
  stopChild(child, stopGraceMs, setTimeoutFn, clearTimeoutFn);
1416
+ closeProxies(entry);
1164
1417
  entries.delete(req.targetId);
1165
1418
  reject(/* @__PURE__ */ new Error(`'${req.targetId}' did not start within ${readyTimeoutMs}ms.`));
1166
1419
  }, readyTimeoutMs);
@@ -1173,13 +1426,31 @@ function createStudioServeManager(config) {
1173
1426
  emitServe(entry);
1174
1427
  resolve(publicState(entry));
1175
1428
  };
1429
+ const onListening = async (childUrl) => {
1430
+ let endpoint = childUrl;
1431
+ if (captureRequests && /^https?:/i.test(childUrl)) try {
1432
+ const proxy = await proxyFactory({
1433
+ bus: config.bus,
1434
+ target: req.targetId,
1435
+ kind: req.kind,
1436
+ upstream: childUrl
1437
+ });
1438
+ entry.proxies.push(proxy);
1439
+ endpoint = proxy.url;
1440
+ } catch {
1441
+ endpoint = childUrl;
1442
+ }
1443
+ if (entry.stopping || settled && !entries.has(req.targetId)) {
1444
+ await closeProxies(entry);
1445
+ return;
1446
+ }
1447
+ if (!entry.endpoints.includes(endpoint)) entry.endpoints.push(endpoint);
1448
+ if (settled) emitServe(entry);
1449
+ else becomeRunning();
1450
+ };
1176
1451
  streamLines(child.stdout, (line) => {
1177
1452
  const m = LISTENING_RE.exec(line);
1178
- if (m?.[1]) {
1179
- if (!entry.endpoints.includes(m[1])) entry.endpoints.push(m[1]);
1180
- if (settled) emitServe(entry);
1181
- else becomeRunning();
1182
- }
1453
+ if (m?.[1]) onListening(m[1]);
1183
1454
  emitLog(config.bus, clock, req.targetId, line, "stdout");
1184
1455
  });
1185
1456
  streamLines(child.stderr, (line) => {
@@ -1189,6 +1460,7 @@ function createStudioServeManager(config) {
1189
1460
  if (settled) {
1190
1461
  entry.status = "error";
1191
1462
  emitServe(entry, err.message);
1463
+ closeProxies(entry);
1192
1464
  entries.delete(req.targetId);
1193
1465
  return;
1194
1466
  }
@@ -1196,6 +1468,7 @@ function createStudioServeManager(config) {
1196
1468
  clearTimeoutFn(timer);
1197
1469
  entry.status = "error";
1198
1470
  emitServe(entry, err.message);
1471
+ closeProxies(entry);
1199
1472
  entries.delete(req.targetId);
1200
1473
  reject(err);
1201
1474
  });
@@ -1210,6 +1483,7 @@ function createStudioServeManager(config) {
1210
1483
  entry.status = "error";
1211
1484
  const msg = `Server exited before listening (code ${code ?? "null"}).`;
1212
1485
  emitServe(entry, msg);
1486
+ closeProxies(entry);
1213
1487
  entries.delete(req.targetId);
1214
1488
  reject(new Error(msg));
1215
1489
  return;
@@ -1217,6 +1491,7 @@ function createStudioServeManager(config) {
1217
1491
  if (entries.get(req.targetId) === entry && entry.status === "running") {
1218
1492
  entry.status = "stopped";
1219
1493
  emitServe(entry, `Server process exited (code ${code ?? "null"}).`);
1494
+ closeProxies(entry);
1220
1495
  entries.delete(req.targetId);
1221
1496
  }
1222
1497
  });
@@ -1227,7 +1502,7 @@ function createStudioServeManager(config) {
1227
1502
  if (!entry) throw new Error(`'${req.targetId}' is not running.`);
1228
1503
  entry.stopping = true;
1229
1504
  entries.delete(req.targetId);
1230
- await stopChild(entry.child, stopGraceMs, setTimeoutFn, clearTimeoutFn);
1505
+ await Promise.all([closeProxies(entry), stopChild(entry.child, stopGraceMs, setTimeoutFn, clearTimeoutFn)]);
1231
1506
  entry.status = "stopped";
1232
1507
  emitServe(entry);
1233
1508
  }
@@ -1473,5 +1748,5 @@ function addStudioSpecificOptions(cmd) {
1473
1748
  }
1474
1749
 
1475
1750
  //#endregion
1476
- export { createStudioServeManager as a, toStudioTargetGroups as c, createLocalStudioCommand as i, renderStudioHtml as l, coerceRunRequest as n, createStudioDispatcher as o, coerceStopRequest as r, startStudioServer as s, addStudioSpecificOptions as t, StudioEventBus as u };
1477
- //# sourceMappingURL=local-studio-MXuSiMqC.js.map
1751
+ export { createStudioServeManager as a, startStudioServer as c, StudioEventBus as d, createLocalStudioCommand as i, toStudioTargetGroups as l, coerceRunRequest as n, startStudioProxy as o, coerceStopRequest as r, createStudioDispatcher as s, addStudioSpecificOptions as t, renderStudioHtml as u };
1752
+ //# sourceMappingURL=local-studio-DvNmnaF0.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local-studio-DvNmnaF0.js","names":["createServer","nodeSpawn","createServer","httpRequest","netConnect","nodeSpawn"],"sources":["../src/local/studio-events.ts","../src/local/studio-ui.ts","../src/local/studio-server.ts","../src/local/studio-dispatch.ts","../src/local/studio-proxy.ts","../src/local/studio-serve-manager.ts","../src/cli/commands/local-studio.ts"],"sourcesContent":["import { EventEmitter } from 'node:events';\n\n/**\n * The kind of runnable target an invocation ran against. Mirrors the\n * categories `cdkl list` / the studio target list expose.\n */\nexport type StudioTargetKind = 'lambda' | 'api' | 'alb' | 'ecs' | 'agentcore';\n\n/**\n * One request observed by `cdkl studio` — a single-shot invoke or one\n * request to a served target. Emitted twice per request: once at start\n * (status/durationMs/response absent) and once at end (filled in). The\n * UI threads the two by {@link StudioInvocationEvent.id}.\n *\n * Slice A defines the shape; the dispatch layer that emits populated\n * events lands with the invoke / serve slices.\n */\nexport interface StudioInvocationEvent {\n /** Correlation id, unique per request. Stable across the start/end pair. */\n id: string;\n /** Wall-clock epoch ms when the request started. */\n ts: number;\n /** Target id the request ran against (stack-qualified or display path). */\n target: string;\n /** The target's kind, for the UI's per-kind affordances. */\n kind: StudioTargetKind;\n /** Short request label for the timeline row (e.g. `GET /orders`). */\n label: string;\n /** The request payload (Lambda event JSON or the HTTP request shape). */\n request?: unknown;\n /** Response payload, set on the end event. */\n response?: unknown;\n /** HTTP status / invoke outcome code, set on the end event. */\n status?: number;\n /** Wall-clock duration in ms, set on the end event. */\n durationMs?: number;\n /**\n * When this invocation is a re-run of an earlier one (Phase 3), the id\n * of the source invocation. The UI links the new row to its origin.\n */\n reinvokeOf?: string;\n}\n\n/**\n * One container stdout/stderr line, tagged with its emitting container\n * so the detail panel can scope logs at CloudWatch granularity (Lambda\n * per-invocation, ECS per-container).\n */\nexport interface StudioLogEvent {\n /** Wall-clock epoch ms when the line was observed. */\n ts: number;\n /** Container id that emitted the line. */\n containerId: string;\n /** Target id the container backs, for the UI grouping. */\n target: string;\n /** The raw log line (without trailing newline). */\n line: string;\n /** Parsed stream, when known. */\n stream?: 'stdout' | 'stderr';\n}\n\n/**\n * Lifecycle of a long-running served target (`api` / `alb` / `ecs`\n * service) the studio started. Unlike {@link StudioInvocationEvent}\n * (one per request), a serve emits one event per status TRANSITION:\n * `starting` when the child is spawned, `running` once it is listening\n * (with the served endpoints), `stopped` after a clean stop, `error`\n * when it failed to come up or crashed. The UI keys these by\n * {@link StudioServeEvent.target} to drive the per-target `running ●\n * :port [Stop]` affordance.\n */\nexport interface StudioServeEvent {\n /** Wall-clock epoch ms when the status was observed. */\n ts: number;\n /** Target id of the served target (stack-qualified or display path). */\n target: string;\n /** The served target's kind. */\n kind: StudioTargetKind;\n /** Lifecycle status this event reports. */\n status: 'starting' | 'running' | 'stopped' | 'error';\n /**\n * Served endpoint URLs, present on `running` (e.g.\n * `['http://127.0.0.1:51234']`). A single served target can expose\n * several (multiple APIs / WebSocket listeners), so this is a list.\n */\n endpoints?: string[];\n /** Child process id, present from `starting` onward. */\n pid?: number;\n /** Status / error detail (e.g. the failure reason on `error`). */\n message?: string;\n}\n\n/**\n * Map of event name -> listener argument, for the typed wrapper below.\n */\ninterface StudioEventMap {\n invocation: [StudioInvocationEvent];\n log: [StudioLogEvent];\n serve: [StudioServeEvent];\n}\n\n/**\n * In-process event bus that every studio observation flows through. The\n * studio HTTP server subscribes and forwards events to the browser over\n * SSE; the dispatch / log-streaming layers emit onto it.\n *\n * A thin typed wrapper over {@link EventEmitter} so producers and the\n * server agree on the event shapes without `any`. Re-exported from\n * `cdk-local/internal` so a host CLI embedding studio can subscribe.\n */\nexport class StudioEventBus {\n private readonly emitter = new EventEmitter();\n\n constructor() {\n // The bus can accumulate many SSE subscribers across a long session;\n // raise the cap so Node does not warn about a suspected leak.\n this.emitter.setMaxListeners(0);\n }\n\n emit<E extends keyof StudioEventMap>(event: E, ...args: StudioEventMap[E]): void {\n this.emitter.emit(event, ...args);\n }\n\n on<E extends keyof StudioEventMap>(\n event: E,\n listener: (...args: StudioEventMap[E]) => void\n ): this {\n this.emitter.on(event, listener as (...args: unknown[]) => void);\n return this;\n }\n\n off<E extends keyof StudioEventMap>(\n event: E,\n listener: (...args: StudioEventMap[E]) => void\n ): this {\n this.emitter.off(event, listener as (...args: unknown[]) => void);\n return this;\n }\n\n /**\n * Number of listeners currently subscribed to `event`. Exposed so the\n * SSE server's subscribe / unsubscribe symmetry can be asserted (a\n * dropped client must not leak a listener).\n */\n listenerCount<E extends keyof StudioEventMap>(event: E): number {\n return this.emitter.listenerCount(event);\n }\n}\n","/**\n * The studio web UI, embedded as a string so it ships inside the\n * `cdk-local` npm package (decision D9) with no asset-copy build step —\n * `tsdown` bundles this module like any other source file. Served by\n * the studio HTTP server (`startStudioServer`) at `GET /`.\n *\n * 3-pane shell (decision D6), framework-free vanilla JS (decision D7):\n * - left = target list (from `GET /api/targets`); each Lambda has an\n * [Invoke] button, each API a [Start] / [Stop] serve control with a\n * `running ● :port` indicator (slice C1), plus a selected-highlight.\n * - center = the WORKSPACE for the selected target: for a Lambda, an\n * event composer (textarea + Invoke button) with the latest run's\n * Request / Response / Logs shown below; for an API, a Start/Stop\n * control with the served endpoints + streaming logs.\n * - right = the timeline (history) of every invocation AND every\n * captured serve request (slice C2); clicking a Lambda row reloads\n * it into the composer, clicking a captured request row opens a\n * read-only Request / Response detail.\n *\n * The center workspace is deliberately adjacent to the left target list\n * (short eye-travel: pick a target -> compose right next to it), and is\n * the primary surface — the timeline is secondary history.\n */\n\nconst STUDIO_CSS = `\n * { box-sizing: border-box; }\n body {\n margin: 0; font: 13px/1.5 ui-monospace, SFMono-Regular, Menlo, monospace;\n color: #e6e6e6; background: #1a1a1a; height: 100vh; overflow: hidden;\n }\n header {\n padding: 8px 14px; background: #111; border-bottom: 1px solid #333;\n display: flex; align-items: center; gap: 10px;\n }\n header .brand { font-weight: 700; color: #fff; }\n header .meta { color: #888; font-size: 12px; }\n main {\n display: grid; grid-template-columns: 280px 5px 1fr 5px 320px;\n height: calc(100vh - 38px);\n }\n .pane { overflow: auto; }\n .splitter { background: #2a2a2a; cursor: col-resize; }\n .splitter:hover, .splitter.dragging { background: #4ec97a; }\n .pane h2 {\n margin: 0; padding: 8px 12px; font-size: 11px; text-transform: uppercase;\n letter-spacing: 0.5px; color: #888; background: #151515;\n position: sticky; top: 0; border-bottom: 1px solid #2a2a2a; z-index: 1;\n }\n .group-title { padding: 8px 12px 2px; color: #6aa9ff; font-size: 11px; }\n .target {\n padding: 6px 12px; border-bottom: 1px solid #222; display: flex;\n align-items: center; gap: 8px;\n }\n .target.runnable { cursor: pointer; }\n .target.runnable:hover { background: #202020; }\n .target.sel { background: #2a3550; }\n .target .name { color: #ddd; flex: 1; overflow: hidden; text-overflow: ellipsis; }\n .target .kind { color: #777; font-size: 11px; }\n .target .invoke-btn {\n padding: 2px 10px; font: 11px ui-monospace, Menlo, monospace; font-weight: 700;\n color: #0d1f12; background: #4ec97a; border: 0; border-radius: 3px; cursor: pointer;\n }\n .target .invoke-btn:hover { background: #6fe097; }\n .target.sel .invoke-btn { background: #6fe097; }\n .target .stop-btn {\n padding: 2px 10px; font: 11px ui-monospace, Menlo, monospace; font-weight: 700;\n color: #2a0d0d; background: #e0707a; border: 0; border-radius: 3px; cursor: pointer;\n }\n .target .stop-btn:hover { background: #ec8a92; }\n .target .run-dot { color: #7bd88f; font-size: 11px; white-space: nowrap; }\n .target .run-dot.starting { color: #e0b54e; }\n .empty { padding: 16px 12px; color: #666; }\n .row {\n padding: 5px 12px; border-bottom: 1px solid #222; cursor: pointer;\n display: flex; gap: 8px; white-space: nowrap;\n }\n .row:hover { background: #222; }\n .row.sel { background: #2a3550; }\n .row .ts { color: #777; }\n .row .label { color: #ddd; flex: 1; overflow: hidden; text-overflow: ellipsis; }\n .row .status { color: #7bd88f; }\n .row .status.err { color: #e0707a; }\n #workspace { padding: 0 0 24px; }\n .composer { padding: 10px 12px; border-bottom: 1px solid #2a2a2a; }\n .composer .target-name { color: #fff; font-weight: 700; margin-bottom: 6px; }\n .composer textarea {\n width: 100%; min-height: 130px; resize: vertical; background: #111; color: #ddd;\n border: 1px solid #333; border-radius: 3px; padding: 6px;\n font: 12px/1.5 ui-monospace, SFMono-Regular, Menlo, monospace;\n }\n .composer button {\n margin-top: 8px; padding: 6px 18px; background: #2a7d46; color: #fff;\n border: 0; border-radius: 3px; cursor: pointer; font: inherit; font-weight: 700;\n }\n .composer button:hover { background: #339152; }\n .composer button:disabled { background: #333; color: #888; cursor: default; }\n .composer .err { color: #e0707a; margin-top: 6px; min-height: 18px; }\n .section { padding: 8px 12px; border-bottom: 1px solid #222; }\n .section h3 { margin: 0 0 6px; font-size: 11px; color: #888; text-transform: uppercase; }\n .section h3 .ok { color: #7bd88f; }\n .section h3 .bad { color: #e0707a; }\n .section pre { margin: 0; white-space: pre-wrap; word-break: break-word; color: #cfcfcf; }\n .endpoint { display: block; color: #6aa9ff; text-decoration: none; padding: 2px 0; }\n .endpoint:hover { text-decoration: underline; }\n #conn { font-size: 11px; }\n #conn.up { color: #7bd88f; }\n #conn.down { color: #e0707a; }\n`;\n\nconst STUDIO_SCRIPT = `\n const KIND_LABEL = { lambda: 'Lambda', api: 'API', alb: 'ALB', ecs: 'ECS', agentcore: 'AgentCore' };\n const rowsById = new Map(); // invocationId -> timeline row element\n const invById = new Map(); // invocationId -> latest invocation event\n const logsById = new Map(); // invocationId / serve targetId -> [log lines]\n const targetEls = new Map(); // targetId -> left-pane element\n const serveMeta = new Map(); // serve targetId -> { dot, btnSlot } row controls\n const serveState = new Map(); // serve targetId -> { status, endpoints }\n let active = null; // { id, kind, ta, btn, msg, result }\n let shownInvId = null; // lambda invocation whose result is in the workspace\n let shownServeId = null; // serve target whose workspace is shown\n let shownDetailId = null; // captured request whose read-only detail is shown\n\n function el(tag, cls, text) {\n const e = document.createElement(tag);\n if (cls) e.className = cls;\n if (text != null) e.textContent = text;\n return e;\n }\n\n async function loadTargets() {\n const pane = document.getElementById('targets');\n try {\n const res = await fetch('/api/targets');\n const data = await res.json();\n pane.querySelectorAll('.group-title,.target,.empty').forEach((n) => n.remove());\n let total = 0;\n for (const group of data.groups) {\n if (!group.entries.length) continue;\n pane.appendChild(el('div', 'group-title', group.title));\n for (const entry of group.entries) {\n total += 1;\n // Lambda targets are single-shot invokes; API targets are\n // long-running serves (slice C1). Other kinds list but are not\n // yet runnable.\n const runnable = group.kind === 'lambda' || group.kind === 'api';\n const t = el('div', runnable ? 'target runnable' : 'target');\n const name = el('span', 'name', entry.id);\n name.title = entry.id; // full path on hover even when truncated\n t.appendChild(name);\n t.appendChild(el('span', 'kind', '(' + (KIND_LABEL[group.kind] || group.kind) + ')'));\n if (group.kind === 'lambda') {\n const btn = el('button', 'invoke-btn', 'Invoke');\n btn.onclick = (e) => { e.stopPropagation(); selectTarget(entry.id, 'lambda'); };\n t.appendChild(btn);\n t.onclick = () => selectTarget(entry.id, 'lambda');\n targetEls.set(entry.id, t);\n } else if (group.kind === 'api') {\n // A serve target: a running-state dot + a Start/Stop button\n // slot, both refreshed by updateServeRow on serve events.\n const dot = el('span', 'run-dot');\n const btnSlot = el('span', 'btn-slot');\n t.appendChild(dot);\n t.appendChild(btnSlot);\n t.onclick = () => selectTarget(entry.id, 'api');\n targetEls.set(entry.id, t);\n serveMeta.set(entry.id, { dot, btnSlot });\n updateServeRow(entry.id);\n }\n pane.appendChild(t);\n }\n }\n if (!total) pane.appendChild(el('div', 'empty', 'No runnable targets found.'));\n } catch (err) {\n pane.appendChild(el('div', 'empty', 'Failed to load targets: ' + err));\n }\n }\n\n // Pull any already-running serves (e.g. after a UI reload) so the rows\n // and workspace reflect them without waiting for a fresh serve event.\n async function loadRunning() {\n try {\n const res = await fetch('/api/running');\n const data = await res.json();\n for (const s of (data.running || [])) {\n serveState.set(s.targetId, { status: s.status, endpoints: s.endpoints || [] });\n updateServeRow(s.targetId);\n }\n } catch (err) {\n /* best-effort; the serve SSE stream still drives live updates */\n }\n }\n\n function firstPort(endpoints) {\n const u = (endpoints || [])[0];\n if (!u) return '';\n const m = /:(\\\\d+)/.exec(u);\n return m ? ':' + m[1] : '';\n }\n\n function updateServeRow(id) {\n const meta = serveMeta.get(id);\n if (!meta) return;\n const st = serveState.get(id);\n const status = st ? st.status : 'stopped';\n const running = status === 'running';\n const starting = status === 'starting';\n meta.dot.textContent = running ? '● ' + firstPort(st.endpoints) : starting ? '○ starting' : '';\n meta.dot.className = 'run-dot' + (starting ? ' starting' : '');\n meta.btnSlot.innerHTML = '';\n const btn = running || starting\n ? el('button', 'stop-btn', 'Stop')\n : el('button', 'invoke-btn', 'Start');\n btn.onclick = (e) => {\n e.stopPropagation();\n if (running || starting) stopServe(id); else startServe(id);\n };\n meta.btnSlot.appendChild(btn);\n // Refresh the workspace if it is showing this serve.\n if (shownServeId === id) renderServeWorkspace(id);\n }\n\n function highlightTarget(id) {\n document.querySelectorAll('.target.sel').forEach((n) => n.classList.remove('sel'));\n const t = targetEls.get(id);\n if (t) t.classList.add('sel');\n }\n\n function selectTarget(id, kind) {\n highlightTarget(id);\n shownDetailId = null;\n if (kind === 'api') {\n shownServeId = id;\n shownInvId = null;\n active = null;\n renderServeWorkspace(id);\n } else {\n shownServeId = null;\n renderComposer(id, kind, '{}');\n }\n }\n\n async function startServe(id) {\n serveState.set(id, { status: 'starting', endpoints: [] });\n updateServeRow(id);\n try {\n const res = await fetch('/api/run', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ targetId: id, kind: 'api' }),\n });\n const data = await res.json();\n if (!res.ok) {\n // Roll back the optimistic 'starting' on a rejected start.\n serveState.set(id, { status: 'error', endpoints: [] });\n updateServeRow(id);\n if (shownServeId === id) renderServeWorkspace(id, data.error || ('HTTP ' + res.status));\n }\n // On success the serve SSE 'running' event fills in the endpoints.\n } catch (err) {\n serveState.set(id, { status: 'error', endpoints: [] });\n updateServeRow(id);\n if (shownServeId === id) renderServeWorkspace(id, String(err));\n }\n }\n\n async function stopServe(id) {\n try {\n await fetch('/api/stop', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ targetId: id }),\n });\n // The serve SSE 'stopped' event clears the running state.\n } catch (err) {\n /* the stop SSE event (or a later refresh) reconciles state */\n }\n }\n\n function renderServeWorkspace(id, errMsg) {\n const ws = document.getElementById('workspace');\n ws.innerHTML = '';\n const st = serveState.get(id) || { status: 'stopped', endpoints: [] };\n const running = st.status === 'running';\n const starting = st.status === 'starting';\n\n const head = el('div', 'composer');\n head.appendChild(el('div', 'target-name', 'Serve ' + id));\n const btn = running || starting\n ? el('button', null, 'Stop')\n : el('button', null, starting ? 'Starting…' : 'Start');\n btn.onclick = () => { if (running || starting) stopServe(id); else startServe(id); };\n head.appendChild(btn);\n if (errMsg) {\n const m = el('div', 'err', errMsg);\n head.appendChild(m);\n }\n ws.appendChild(head);\n\n const epSec = el('div', 'section');\n epSec.appendChild(el('h3', null, 'Endpoints'));\n if (running && st.endpoints.length) {\n for (const url of st.endpoints) {\n const link = href(url);\n epSec.appendChild(link);\n }\n } else {\n epSec.appendChild(el('pre', null, starting ? '(starting…)' : '(not running)'));\n }\n ws.appendChild(epSec);\n\n const logs = logsById.get(id) || [];\n const logSec = el('div', 'section');\n logSec.appendChild(el('h3', null, 'Logs'));\n logSec.appendChild(el('pre', null, logs.length ? logs.join('\\\\n') : '(none)'));\n ws.appendChild(logSec);\n }\n\n // Build an <a> that opens an http(s) endpoint in a new tab; ws:// URLs\n // are shown as plain text (not navigable in a browser tab).\n function href(url) {\n if (/^https?:/.test(url)) {\n const a = el('a', 'endpoint', url);\n a.href = url;\n a.target = '_blank';\n a.rel = 'noopener';\n return a;\n }\n return el('div', 'endpoint', url);\n }\n\n function renderComposer(id, kind, eventText) {\n const ws = document.getElementById('workspace');\n ws.innerHTML = '';\n\n const composer = el('div', 'composer');\n composer.appendChild(el('div', 'target-name', 'Invoke ' + id));\n const ta = el('textarea');\n ta.value = eventText;\n ta.spellcheck = false;\n composer.appendChild(ta);\n composer.appendChild(document.createElement('br'));\n const btn = el('button', null, 'Invoke');\n const msg = el('div', 'err');\n composer.appendChild(btn);\n composer.appendChild(msg);\n\n const result = el('div', 'result');\n\n ws.appendChild(composer);\n ws.appendChild(result);\n\n active = { id, kind, ta, btn, msg, result };\n btn.onclick = () => runInvoke();\n shownInvId = null;\n shownDetailId = null;\n ta.focus();\n }\n\n async function runInvoke() {\n if (!active) return;\n const { id, kind, ta, btn, msg, result } = active;\n let event;\n try {\n event = ta.value.trim() === '' ? {} : JSON.parse(ta.value);\n } catch (err) {\n msg.textContent = 'Invalid JSON: ' + err.message;\n return;\n }\n msg.textContent = '';\n btn.disabled = true;\n btn.textContent = 'Invoking...';\n result.innerHTML = '';\n try {\n const res = await fetch('/api/run', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ targetId: id, kind, event }),\n });\n const data = await res.json();\n if (data.invocationId) {\n shownInvId = data.invocationId;\n renderResult(shownInvId);\n }\n if (!res.ok || data.ok === false) {\n msg.textContent = 'Invoke failed: ' + (data.error || ('HTTP ' + res.status));\n }\n } catch (err) {\n msg.textContent = 'Request failed: ' + err;\n } finally {\n btn.disabled = false;\n btn.textContent = 'Invoke';\n }\n }\n\n function fmt(body) {\n return typeof body === 'string' ? body : JSON.stringify(body, null, 2);\n }\n\n function renderResult(invId) {\n if (!active) return;\n const result = active.result;\n result.innerHTML = '';\n const ev = invById.get(invId);\n if (!ev) return;\n\n const reqSec = el('div', 'section');\n reqSec.appendChild(el('h3', null, 'Request'));\n reqSec.appendChild(el('pre', null, ev.request != null ? fmt(ev.request) : '(none)'));\n result.appendChild(reqSec);\n\n const respSec = el('div', 'section');\n const h = el('h3', null, 'Response');\n if (ev.status != null) {\n const cls = ev.status >= 200 && ev.status < 300 ? 'ok' : 'bad';\n const meta = el('span', cls, ' ' + ev.status + (ev.durationMs != null ? ' · ' + ev.durationMs + 'ms' : ''));\n h.appendChild(meta);\n }\n respSec.appendChild(h);\n respSec.appendChild(el('pre', null, ev.response != null ? fmt(ev.response) : '(pending…)'));\n result.appendChild(respSec);\n\n const logs = logsById.get(invId) || [];\n const logSec = el('div', 'section');\n logSec.appendChild(el('h3', null, 'Logs'));\n logSec.appendChild(el('pre', null, logs.length ? logs.join('\\\\n') : '(none)'));\n result.appendChild(logSec);\n }\n\n function addInvocation(ev) {\n invById.set(ev.id, Object.assign(invById.get(ev.id) || {}, ev));\n const timeline = document.getElementById('timeline');\n const placeholder = timeline.querySelector('.empty');\n if (placeholder) placeholder.remove();\n\n let row = rowsById.get(ev.id);\n if (!row) {\n row = el('div', 'row');\n row.appendChild(el('span', 'ts'));\n row.appendChild(el('span', 'label'));\n row.appendChild(el('span', 'status'));\n row.onclick = () => loadInvocation(ev.id);\n rowsById.set(ev.id, row);\n timeline.insertBefore(row, timeline.querySelector('.row')); // newest on top\n }\n const merged = invById.get(ev.id);\n const d = new Date(merged.ts);\n row.querySelector('.ts').textContent = d.toLocaleTimeString();\n row.querySelector('.label').textContent = (merged.target || '') + ' ' + (merged.label || '');\n const statusEl = row.querySelector('.status');\n statusEl.textContent =\n merged.status != null\n ? merged.status + (merged.durationMs != null ? ' ' + merged.durationMs + 'ms' : '')\n : '…';\n statusEl.className = 'status' + (merged.status != null && (merged.status < 200 || merged.status >= 300) ? ' err' : '');\n\n // Live-refresh the workspace if it is showing this invocation.\n if (shownInvId === ev.id) renderResult(ev.id);\n if (shownDetailId === ev.id) renderCapturedDetail(ev.id);\n }\n\n function loadInvocation(id) {\n const ev = invById.get(id);\n if (!ev) return;\n document.querySelectorAll('.row.sel').forEach((n) => n.classList.remove('sel'));\n const row = rowsById.get(id);\n if (row) row.classList.add('sel');\n highlightTarget(ev.target);\n if (ev.kind === 'lambda') {\n // A Lambda invocation row reloads into the re-invokable composer.\n shownDetailId = null;\n shownServeId = null;\n renderComposer(ev.target, ev.kind, ev.request != null ? fmt(ev.request) : '{}');\n shownInvId = id;\n renderResult(id);\n } else {\n // A captured serve request (slice C2) opens a READ-ONLY detail —\n // re-invoking a captured request is Phase 3.\n shownInvId = null;\n shownServeId = null;\n active = null;\n renderCapturedDetail(id);\n }\n }\n\n // Read-only Request / Response detail for a captured serve request.\n function renderCapturedDetail(id) {\n shownDetailId = id;\n const ev = invById.get(id);\n const ws = document.getElementById('workspace');\n ws.innerHTML = '';\n if (!ev) return;\n\n const head = el('div', 'composer');\n head.appendChild(el('div', 'target-name', (ev.label || 'request') + ' — ' + (ev.target || '')));\n ws.appendChild(head);\n\n const reqSec = el('div', 'section');\n reqSec.appendChild(el('h3', null, 'Request'));\n reqSec.appendChild(el('pre', null, ev.request != null ? fmt(ev.request) : '(none)'));\n ws.appendChild(reqSec);\n\n const respSec = el('div', 'section');\n const h = el('h3', null, 'Response');\n if (ev.status != null) {\n const cls = ev.status >= 200 && ev.status < 300 ? 'ok' : 'bad';\n h.appendChild(el('span', cls, ' ' + ev.status + (ev.durationMs != null ? ' · ' + ev.durationMs + 'ms' : '')));\n }\n respSec.appendChild(h);\n respSec.appendChild(el('pre', null, ev.response != null ? fmt(ev.response) : '(pending…)'));\n ws.appendChild(respSec);\n }\n\n function connect() {\n const conn = document.getElementById('conn');\n const es = new EventSource('/api/events');\n es.addEventListener('open', () => { conn.textContent = '● live'; conn.className = 'up'; });\n es.addEventListener('error', () => { conn.textContent = '● disconnected'; conn.className = 'down'; });\n es.addEventListener('invocation', (e) => addInvocation(JSON.parse(e.data)));\n es.addEventListener('serve', (e) => onServeEvent(JSON.parse(e.data)));\n es.addEventListener('log', (e) => {\n const ev = JSON.parse(e.data);\n const arr = logsById.get(ev.containerId) || [];\n arr.push(ev.line);\n logsById.set(ev.containerId, arr);\n if (shownInvId === ev.containerId) renderResult(ev.containerId);\n if (shownServeId === ev.containerId) renderServeWorkspace(ev.containerId);\n });\n }\n\n function onServeEvent(ev) {\n // A 'stopped' / 'error' transition clears the running state; otherwise\n // record the latest status + endpoints for the row + workspace.\n if (ev.status === 'stopped' || ev.status === 'error') {\n serveState.set(ev.target, { status: ev.status, endpoints: [] });\n } else {\n serveState.set(ev.target, { status: ev.status, endpoints: ev.endpoints || [] });\n }\n updateServeRow(ev.target);\n }\n\n function initSplitters() {\n const main = document.querySelector('main');\n let left = 280, right = 320;\n const apply = () => {\n main.style.gridTemplateColumns = left + 'px 5px 1fr 5px ' + right + 'px';\n };\n const wire = (splitterId, onMove) => {\n const s = document.getElementById(splitterId);\n s.addEventListener('mousedown', (e) => {\n e.preventDefault();\n const startX = e.clientX;\n const l0 = left, r0 = right;\n s.classList.add('dragging');\n document.body.style.userSelect = 'none';\n const move = (ev) => { onMove(ev.clientX - startX, l0, r0); apply(); };\n const up = () => {\n s.classList.remove('dragging');\n document.body.style.userSelect = '';\n document.removeEventListener('mousemove', move);\n document.removeEventListener('mouseup', up);\n };\n document.addEventListener('mousemove', move);\n document.addEventListener('mouseup', up);\n });\n };\n const clamp = (v) => Math.max(160, Math.min(760, v));\n wire('split-left', (dx, l0) => { left = clamp(l0 + dx); });\n wire('split-right', (dx, l0, r0) => { right = clamp(r0 - dx); });\n }\n\n loadTargets().then(loadRunning);\n connect();\n initSplitters();\n`;\n\n/**\n * Render the full studio HTML document. `appLabel` is shown in the\n * header (the CDK app / stack context); `cliName` brands the title for\n * host CLIs that rebrand `cdkl`.\n */\nexport function renderStudioHtml(appLabel: string, cliName: string): string {\n const safeApp = escapeHtml(appLabel);\n const safeCli = escapeHtml(cliName);\n return `<!doctype html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\" />\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n<title>${safeCli} studio</title>\n<style>${STUDIO_CSS}</style>\n</head>\n<body>\n<header>\n <span class=\"brand\">${safeCli} studio</span>\n <span class=\"meta\">${safeApp}</span>\n <span id=\"conn\" class=\"down\">● connecting</span>\n</header>\n<main>\n <section class=\"pane\" id=\"targets\"><h2>Targets</h2></section>\n <div class=\"splitter\" id=\"split-left\"></div>\n <section class=\"pane\" id=\"workspace\"><div class=\"empty\">Pick a Lambda to invoke, or an API to serve, on the left.</div></section>\n <div class=\"splitter\" id=\"split-right\"></div>\n <section class=\"pane\" id=\"timeline\"><h2>Timeline</h2><div class=\"empty\">No requests yet.</div></section>\n</main>\n<script>${STUDIO_SCRIPT}</script>\n</body>\n</html>`;\n}\n\n/** Minimal HTML-escape for the few interpolated text values. */\nfunction escapeHtml(s: string): string {\n return s\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;');\n}\n","import { createServer, type IncomingMessage, type Server, type ServerResponse } from 'node:http';\nimport type { AddressInfo } from 'node:net';\nimport {\n StudioEventBus,\n type StudioInvocationEvent,\n type StudioLogEvent,\n type StudioServeEvent,\n} from './studio-events.js';\nimport { renderStudioHtml } from './studio-ui.js';\nimport type { TargetListing } from './target-lister.js';\n\n/** One target as the studio UI consumes it (`GET /api/targets`). */\nexport interface StudioTarget {\n /** Stable target id — the display path when available, else qualified id. */\n id: string;\n /** Stack-qualified `<Stack>:<LogicalId>` for disambiguation. */\n qualifiedId: string;\n /** API surface kind (REST v1 / HTTP v2 / ...), only for `api` entries. */\n surface?: string;\n}\n\n/** A category of targets, grouped by the studio kind that runs them. */\nexport interface StudioTargetGroup {\n /** Studio kind discriminator shared with {@link StudioInvocationEvent}. */\n kind: 'lambda' | 'api' | 'alb' | 'ecs' | 'agentcore';\n /** Human-readable group heading. */\n title: string;\n entries: StudioTarget[];\n}\n\n/**\n * Project a {@link TargetListing} (the same enumeration `cdkl list`\n * prints) into the grouped shape the studio UI renders. ECS services and\n * task definitions are folded into one `ecs` group; everything else maps\n * one category to one group. Exported so a unit test can assert the\n * projection without booting the server.\n */\nexport function toStudioTargetGroups(listing: TargetListing): StudioTargetGroup[] {\n const map = (entries: TargetListing['lambdas']): StudioTarget[] =>\n entries.map((e) => {\n const t: StudioTarget = { id: e.displayPath ?? e.qualifiedId, qualifiedId: e.qualifiedId };\n if (e.kind) t.surface = e.kind;\n return t;\n });\n return [\n { kind: 'lambda', title: 'Lambda Functions', entries: map(listing.lambdas) },\n { kind: 'api', title: 'APIs', entries: map(listing.apis) },\n {\n kind: 'ecs',\n title: 'ECS Services / Tasks',\n entries: [...map(listing.ecsServices), ...map(listing.ecsTaskDefinitions)],\n },\n { kind: 'agentcore', title: 'AgentCore Runtimes', entries: map(listing.agentCoreRuntimes) },\n { kind: 'alb', title: 'Load Balancers', entries: map(listing.loadBalancers) },\n ];\n}\n\n/** Inputs to {@link startStudioServer}. */\nexport interface StudioServerOptions {\n /** Preferred listen port; bumps on collision (decision: collision-safe). */\n port: number;\n /** Listen host. Defaults to `127.0.0.1` (localhost-only). */\n host?: string;\n /** The shared event bus the SSE stream forwards. */\n bus: StudioEventBus;\n /** Target groups to serve at `GET /api/targets`. */\n targetGroups: StudioTargetGroup[];\n /** Header label for the running app / stack context. */\n appLabel: string;\n /** CLI brand name (`cdkl`, or a host rebrand). */\n cliName: string;\n /**\n * Max consecutive ports to try on `EADDRINUSE` before giving up.\n * Defaults to 20.\n */\n maxPortBump?: number;\n /**\n * Handler for `POST /api/run` — runs a target (single-shot Lambda\n * invoke, or start a long-running serve target) and returns the\n * result. When omitted, `/api/run` answers 501 (the observe-only\n * shell). The body is the parsed JSON request; the handler emits its\n * own invocation / serve / log events onto the bus, so the UI's\n * timeline + running state update over SSE independently of the\n * response.\n */\n onRun?: (body: unknown) => Promise<unknown>;\n /**\n * Handler for `POST /api/stop` — stop a running serve target. When\n * omitted, `/api/stop` answers 501. The body identifies the target.\n */\n onStop?: (body: unknown) => Promise<unknown>;\n /**\n * Snapshot of the currently-running serve targets, served at\n * `GET /api/running`. When omitted, the endpoint returns an empty\n * list (the observe-only shell never runs anything).\n */\n getRunning?: () => unknown;\n}\n\n/** A running studio server. */\nexport interface RunningStudioServer {\n /** The URL the UI is served at, e.g. `http://127.0.0.1:9999`. */\n url: string;\n /** The actually-bound port (may differ from the requested one). */\n port: number;\n /** Stop the server and release the port. */\n close: () => Promise<void>;\n}\n\nconst SSE_HEARTBEAT_MS = 15_000;\n\n/**\n * Boot the studio HTTP server: serves the embedded UI at `/`, the target\n * list at `/api/targets`, and a Server-Sent-Events stream of the bus's\n * `invocation` / `log` events at `/api/events`. Localhost-only by\n * default. Resolves once the socket is listening.\n */\nexport async function startStudioServer(\n options: StudioServerOptions\n): Promise<RunningStudioServer> {\n const host = options.host ?? '127.0.0.1';\n const maxBump = options.maxPortBump ?? 20;\n const html = renderStudioHtml(options.appLabel, options.cliName);\n const targetsJson = JSON.stringify({ groups: options.targetGroups });\n\n const server = createServer((req, res) =>\n handleRequest(req, res, options.bus, html, targetsJson, options)\n );\n\n const boundPort = await listenWithBump(server, host, options.port, maxBump);\n\n return {\n url: `http://${host}:${boundPort}`,\n port: boundPort,\n close: () =>\n new Promise<void>((resolveClose, reject) => {\n server.close((err) => (err ? reject(err) : resolveClose()));\n // closeAllConnections exists on Node 18.2+; SSE keeps sockets open\n // so without this `close()` would hang on live EventSource clients.\n server.closeAllConnections?.();\n }),\n };\n}\n\nfunction handleRequest(\n req: IncomingMessage,\n res: ServerResponse,\n bus: StudioEventBus,\n html: string,\n targetsJson: string,\n options: StudioServerOptions\n): void {\n const url = req.url ?? '/';\n const path = url.split('?')[0];\n\n if (req.method === 'GET' && (path === '/' || path === '/index.html')) {\n res.writeHead(200, { 'content-type': 'text/html; charset=utf-8' });\n res.end(html);\n return;\n }\n if (req.method === 'GET' && path === '/api/targets') {\n res.writeHead(200, { 'content-type': 'application/json; charset=utf-8' });\n res.end(targetsJson);\n return;\n }\n if (req.method === 'GET' && path === '/api/running') {\n const running = options.getRunning ? options.getRunning() : { running: [] };\n res.writeHead(200, { 'content-type': 'application/json; charset=utf-8' });\n res.end(JSON.stringify(running));\n return;\n }\n if (req.method === 'GET' && path === '/api/events') {\n serveSse(req, res, bus);\n return;\n }\n if (req.method === 'POST' && path === '/api/run') {\n void handleDispatch(req, res, options.onRun);\n return;\n }\n if (req.method === 'POST' && path === '/api/stop') {\n void handleDispatch(req, res, options.onStop);\n return;\n }\n res.writeHead(404, { 'content-type': 'text/plain; charset=utf-8' });\n res.end('Not found');\n}\n\nconst MAX_RUN_BODY_BYTES = 5 * 1024 * 1024;\n\n/**\n * Reply to a JSON POST endpoint (`/api/run` / `/api/stop`): parse the\n * bounded JSON body and dispatch via `handler`. 501 when no handler is\n * wired (the observe-only shell), 400 on a malformed body, 500 when the\n * handler throws.\n */\nasync function handleDispatch(\n req: IncomingMessage,\n res: ServerResponse,\n handler?: (body: unknown) => Promise<unknown>\n): Promise<void> {\n const sendJson = (statusCode: number, payload: unknown): void => {\n res.writeHead(statusCode, { 'content-type': 'application/json; charset=utf-8' });\n res.end(JSON.stringify(payload));\n };\n\n if (!handler) {\n // The observe-only shell (no dispatcher wired) cannot run targets.\n sendJson(501, { error: 'Running targets is not supported by this studio server.' });\n return;\n }\n\n let body: unknown;\n try {\n body = await readJsonBody(req);\n } catch (err) {\n sendJson(400, { error: err instanceof Error ? err.message : String(err) });\n return;\n }\n\n try {\n const result = await handler(body);\n sendJson(200, result);\n } catch (err) {\n sendJson(500, { error: err instanceof Error ? err.message : String(err) });\n }\n}\n\n/** Read + JSON-parse a request body, bounded to {@link MAX_RUN_BODY_BYTES}. */\nfunction readJsonBody(req: IncomingMessage): Promise<unknown> {\n return new Promise<unknown>((resolveBody, reject) => {\n let raw = '';\n let bytes = 0;\n // Single-flight: once the promise settles (over-limit / end / error) the\n // handlers short-circuit so a late chunk or the destroy-triggered `error`\n // cannot re-settle it.\n let done = false;\n const settle = (fn: () => void): void => {\n if (done) return;\n done = true;\n fn();\n };\n req.setEncoding('utf8');\n req.on('data', (chunk: string) => {\n if (done) return;\n bytes += Buffer.byteLength(chunk);\n if (bytes > MAX_RUN_BODY_BYTES) {\n settle(() => reject(new Error('Request body too large.')));\n req.destroy();\n return;\n }\n raw += chunk;\n });\n req.on('end', () => {\n settle(() => {\n if (raw.trim() === '') {\n resolveBody(undefined);\n return;\n }\n try {\n resolveBody(JSON.parse(raw));\n } catch {\n reject(new Error('Invalid JSON body.'));\n }\n });\n });\n req.on('error', (err) => settle(() => reject(err)));\n });\n}\n\nfunction serveSse(req: IncomingMessage, res: ServerResponse, bus: StudioEventBus): void {\n res.writeHead(200, {\n 'content-type': 'text/event-stream; charset=utf-8',\n 'cache-control': 'no-cache, no-transform',\n connection: 'keep-alive',\n });\n\n let closed = false;\n const heartbeat = setInterval(() => safeWrite(':hb\\n\\n'), SSE_HEARTBEAT_MS);\n heartbeat.unref?.();\n\n const onInvocation = (ev: StudioInvocationEvent): void => {\n safeWrite(`event: invocation\\ndata: ${JSON.stringify(ev)}\\n\\n`);\n };\n const onLog = (ev: StudioLogEvent): void => {\n safeWrite(`event: log\\ndata: ${JSON.stringify(ev)}\\n\\n`);\n };\n const onServe = (ev: StudioServeEvent): void => {\n safeWrite(`event: serve\\ndata: ${JSON.stringify(ev)}\\n\\n`);\n };\n\n // Idempotent teardown: unsubscribe from the bus + stop the heartbeat so\n // a dropped EventSource client never leaks a listener.\n function cleanup(): void {\n if (closed) return;\n closed = true;\n clearInterval(heartbeat);\n bus.off('invocation', onInvocation);\n bus.off('log', onLog);\n bus.off('serve', onServe);\n }\n\n // Guard every write: on shutdown `close()` destroys live SSE sockets\n // (closeAllConnections), and a bus emit / heartbeat tick can race that\n // destroy. Writing to a destroyed socket emits an unhandled `error` on\n // the response — which, with no top-level uncaughtException handler on\n // the studio path, would crash the process on Ctrl-C. Drop the write\n // (and tear down) instead.\n function safeWrite(chunk: string): void {\n if (closed || res.writableEnded || res.destroyed) {\n cleanup();\n return;\n }\n res.write(chunk);\n }\n\n bus.on('invocation', onInvocation);\n bus.on('log', onLog);\n bus.on('serve', onServe);\n\n // `close` fires on client disconnect AND on server-side socket destroy;\n // `error` fires if a write loses the race with the destroy. All three\n // route through the idempotent cleanup so the bus listeners + heartbeat\n // never leak and a write error never propagates as uncaught.\n req.on('close', cleanup);\n res.on('close', cleanup);\n res.on('error', cleanup);\n\n // Open the stream so EventSource fires `open` immediately.\n safeWrite(':ok\\n\\n');\n}\n\n/**\n * Listen on `port`, retrying `port+1`, `port+2`, ... on `EADDRINUSE` up\n * to `maxBump` extra attempts. Resolves with the bound port.\n */\nfunction listenWithBump(\n server: Server,\n host: string,\n port: number,\n maxBump: number\n): Promise<number> {\n return new Promise<number>((resolveListen, reject) => {\n let attempt = 0;\n const tryListen = (p: number): void => {\n const onError = (err: NodeJS.ErrnoException): void => {\n if (err.code === 'EADDRINUSE' && attempt < maxBump) {\n attempt += 1;\n server.removeListener('error', onError);\n tryListen(p + 1);\n return;\n }\n reject(err);\n };\n server.once('error', onError);\n server.listen(p, host, () => {\n server.removeListener('error', onError);\n // Resolve with the ACTUAL bound port from the socket — when the\n // requested port is 0 the OS assigns a free one, which `p` does\n // not reflect.\n resolveListen((server.address() as AddressInfo).port);\n });\n };\n tryListen(port);\n });\n}\n","import { spawn as nodeSpawn, type ChildProcessWithoutNullStreams } from 'node:child_process';\nimport { mkdtempSync, rmSync, writeFileSync } from 'node:fs';\nimport { tmpdir } from 'node:os';\nimport { join } from 'node:path';\nimport { StudioEventBus, type StudioTargetKind } from './studio-events.js';\n\n/** A request to run a target, as the studio UI posts it to `/api/run`. */\nexport interface StudioRunRequest {\n /** Target id the user picked (display path or stack-qualified id). */\n targetId: string;\n /** Target kind. Slice B handles `'lambda'` (single-shot invoke) only. */\n kind: StudioTargetKind;\n /** The event payload to invoke with. */\n event: unknown;\n}\n\n/** The outcome of a single-shot run, returned from `/api/run`. */\nexport interface StudioRunResult {\n /** Correlation id shared with the emitted invocation events. */\n invocationId: string;\n /** Whether the underlying `cdkl invoke` exited 0. */\n ok: boolean;\n /**\n * Synthetic status for the timeline: 200 when the invoke succeeded,\n * 500 when it failed. A direct Lambda invoke has no HTTP status; this\n * is the UI's success/failure signal only.\n */\n status: number;\n /** Parsed Lambda response (JSON when parseable, else the raw string). */\n response?: unknown;\n /** Raw stdout payload from `cdkl invoke`. */\n raw?: string;\n /** Error summary when the invoke failed. */\n error?: string;\n /** Wall-clock duration in ms. */\n durationMs: number;\n}\n\n/** Config for {@link createStudioDispatcher}. */\nexport interface StudioDispatchConfig {\n /** Path to the `cdkl` CLI entry (`dist/cli.js`) — usually `process.argv[1]`. */\n cliEntry: string;\n /** The shared event bus; invocation + log events are emitted onto it. */\n bus: StudioEventBus;\n /** Working directory for the child invoke (defaults to `process.cwd()`). */\n cwd?: string;\n /** `--app` value to thread into the child invoke, if studio was given one. */\n app?: string;\n /** `-c key=value` context overrides to thread through. */\n context?: Record<string, string>;\n /** `--profile` to thread through. */\n profile?: string;\n /** `--region` to thread through. */\n region?: string;\n /** Node binary to spawn (defaults to `process.execPath`; injectable for tests). */\n nodeBin?: string;\n /** Spawn implementation (injectable for tests). */\n spawnFn?: typeof nodeSpawn;\n /** Clock (injectable for tests; defaults to `Date.now`). */\n clock?: () => number;\n /** Invocation-id factory (injectable for tests). */\n idFactory?: () => string;\n}\n\n/** A bound dispatcher exposing the `/api/run` handler. */\nexport interface StudioDispatcher {\n run: (req: StudioRunRequest) => Promise<StudioRunResult>;\n}\n\nlet idCounter = 0;\n\n/**\n * Build the studio run dispatcher. Slice B drives a single-shot Lambda\n * invoke by spawning the SAME `cdkl invoke` the headless command runs —\n * studio is a control plane over the CLI, so re-using the whole command\n * (rather than re-wiring its internals) guarantees byte-for-byte parity\n * and keeps all of `cdkl invoke`'s process-global behavior\n * (`process.exit`, env mutation, stdin) isolated in a child process.\n *\n * The child's stdout is the Lambda response payload; its stderr (status\n * + container logs) is streamed line-by-line onto the bus as `log`\n * events. An `invocation` start event is emitted before spawn and an end\n * event (with response + status + duration) after exit, both keyed by\n * the same correlation id so the UI threads them into one timeline row.\n */\nexport function createStudioDispatcher(config: StudioDispatchConfig): StudioDispatcher {\n const spawnFn = config.spawnFn ?? nodeSpawn;\n const nodeBin = config.nodeBin ?? process.execPath;\n const clock = config.clock ?? Date.now;\n const idFactory =\n config.idFactory ??\n (() => {\n idCounter += 1;\n return `inv-${clock()}-${idCounter}`;\n });\n\n async function run(req: StudioRunRequest): Promise<StudioRunResult> {\n const invocationId = idFactory();\n const startedAt = clock();\n\n if (req.kind !== 'lambda') {\n // Serve targets (API / ALB / ECS) arrive in slice C; reject clearly\n // rather than silently mis-dispatching.\n const error = `Running '${req.kind}' targets from studio is not supported yet (Lambda only).`;\n config.bus.emit('invocation', {\n id: invocationId,\n ts: startedAt,\n target: req.targetId,\n kind: req.kind,\n label: 'invoke',\n request: req.event,\n response: error,\n status: 501,\n durationMs: clock() - startedAt,\n });\n return { invocationId, ok: false, status: 501, error, durationMs: clock() - startedAt };\n }\n\n config.bus.emit('invocation', {\n id: invocationId,\n ts: startedAt,\n target: req.targetId,\n kind: req.kind,\n label: 'invoke',\n request: req.event,\n });\n\n // `dir` is created inside the try so a `writeFileSync` / `JSON.stringify`\n // throw still hits the finally that cleans it up.\n let dir: string | undefined;\n try {\n dir = mkdtempSync(join(tmpdir(), 'cdkl-studio-run-'));\n const eventFile = join(dir, 'event.json');\n writeFileSync(eventFile, JSON.stringify(req.event ?? {}));\n\n const args = ['invoke', req.targetId, '--event', eventFile];\n if (config.app) args.push('--app', config.app);\n if (config.profile) args.push('--profile', config.profile);\n if (config.region) args.push('--region', config.region);\n for (const [k, v] of Object.entries(config.context ?? {})) {\n args.push('-c', `${k}=${v}`);\n }\n\n const { code, stdout, stderr } = await runChild(\n spawnFn,\n nodeBin,\n [config.cliEntry, ...args],\n config.cwd ?? process.cwd(),\n invocationId,\n req.targetId,\n config.bus,\n clock\n );\n\n const durationMs = clock() - startedAt;\n const ok = code === 0;\n const failure = stderr.trim() || `cdkl invoke exited ${code}`;\n\n // `cdkl invoke` interleaves synth progress + streamed container logs on\n // stdout and writes the Lambda response there too. The RIE response is\n // always a single line of valid JSON (the serialized handler return\n // value or error object), whereas progress / container lines\n // (`Synthesizing…`, `Starting container…`, `START/END/REPORT`) are NOT\n // JSON — so the response is the LAST JSON-parseable stdout line, which is\n // robust against a container log line flushing AFTER the response. Every\n // other stdout line is surfaced as a log event. Falls back to the last\n // line when nothing parses (an unusual non-JSON / empty response).\n const stdoutLines = stdout\n .split('\\n')\n .map((l) => l.replace(/\\r$/, '').trimEnd())\n .filter((l) => l.trim().length > 0);\n\n let responseIdx = -1;\n let response: unknown;\n if (ok) {\n for (let i = stdoutLines.length - 1; i >= 0; i -= 1) {\n const parsed = tryParseJson(stdoutLines[i] ?? '');\n if (parsed.ok) {\n responseIdx = i;\n response = parsed.value;\n break;\n }\n }\n if (responseIdx === -1 && stdoutLines.length > 0) {\n responseIdx = stdoutLines.length - 1;\n response = stdoutLines[responseIdx];\n }\n } else {\n response = failure;\n }\n\n // Every stdout line that is not the response line is a log line.\n stdoutLines.forEach((line, i) => {\n if (i === responseIdx) return;\n config.bus.emit('log', {\n ts: clock(),\n containerId: invocationId,\n target: req.targetId,\n line,\n stream: 'stdout',\n });\n });\n\n const raw = responseIdx >= 0 ? (stdoutLines[responseIdx] ?? '') : '';\n const status = ok ? 200 : 500;\n\n config.bus.emit('invocation', {\n id: invocationId,\n ts: startedAt,\n target: req.targetId,\n kind: req.kind,\n label: 'invoke',\n request: req.event,\n response,\n status,\n durationMs,\n });\n\n const result: StudioRunResult = { invocationId, ok, status, durationMs };\n if (raw) result.raw = raw;\n if (response !== undefined) result.response = response;\n if (!ok) result.error = failure;\n return result;\n } finally {\n if (dir) rmSync(dir, { recursive: true, force: true });\n }\n }\n\n return { run };\n}\n\ninterface ChildOutcome {\n code: number | null;\n stdout: string;\n stderr: string;\n}\n\n/**\n * Spawn the child invoke, accumulate stdout, and stream stderr to the\n * bus line-by-line as `log` events. Resolves on process close.\n */\nfunction runChild(\n spawnFn: typeof nodeSpawn,\n nodeBin: string,\n argv: string[],\n cwd: string,\n invocationId: string,\n target: string,\n bus: StudioEventBus,\n clock: () => number\n): Promise<ChildOutcome> {\n return new Promise<ChildOutcome>((resolve, reject) => {\n let child: ChildProcessWithoutNullStreams;\n try {\n child = spawnFn(nodeBin, argv, { cwd });\n } catch (err) {\n reject(err instanceof Error ? err : new Error(String(err)));\n return;\n }\n\n let stdout = '';\n let stderr = '';\n let lineBuf = '';\n\n child.stdout.setEncoding('utf8');\n child.stderr.setEncoding('utf8');\n\n child.stdout.on('data', (chunk: string) => {\n stdout += chunk;\n });\n child.stderr.on('data', (chunk: string) => {\n stderr += chunk;\n lineBuf += chunk;\n let nl = lineBuf.indexOf('\\n');\n while (nl !== -1) {\n const line = lineBuf.slice(0, nl);\n lineBuf = lineBuf.slice(nl + 1);\n if (line.length > 0) {\n bus.emit('log', {\n ts: clock(),\n containerId: invocationId,\n target,\n line,\n stream: 'stderr',\n });\n }\n nl = lineBuf.indexOf('\\n');\n }\n });\n\n child.on('error', (err) => reject(err));\n child.on('close', (code) => {\n if (lineBuf.length > 0) {\n bus.emit('log', {\n ts: clock(),\n containerId: invocationId,\n target,\n line: lineBuf,\n stream: 'stderr',\n });\n }\n resolve({ code, stdout, stderr });\n });\n });\n}\n\n/** Try to JSON-parse `raw`; `ok` distinguishes a parsed value from a failure. */\nfunction tryParseJson(raw: string): { ok: boolean; value: unknown } {\n if (raw === '') return { ok: false, value: undefined };\n try {\n return { ok: true, value: JSON.parse(raw) };\n } catch {\n return { ok: false, value: undefined };\n }\n}\n","import { createServer, request as httpRequest, type IncomingMessage } from 'node:http';\nimport { connect as netConnect, type Socket } from 'node:net';\nimport type { AddressInfo } from 'node:net';\nimport { StudioEventBus, type StudioTargetKind } from './studio-events.js';\n\n/** Config for {@link startStudioProxy}. */\nexport interface StudioProxyConfig {\n /** The shared event bus; `invocation` start/end events are emitted onto it. */\n bus: StudioEventBus;\n /** Serve target id the proxied traffic belongs to. */\n target: string;\n /** The served target's kind (the timeline row's per-kind affordance). */\n kind: StudioTargetKind;\n /** Upstream base URL to forward to, e.g. `http://127.0.0.1:51234`. */\n upstream: string;\n /** Listen host. Defaults to `127.0.0.1` (localhost-only). */\n host?: string;\n /** Clock (injectable for tests; defaults to `Date.now`). */\n clock?: () => number;\n /** Per-request id factory (injectable for tests). */\n idFactory?: () => string;\n /**\n * Max bytes of each captured request / response body retained for the\n * timeline (the FULL body is still streamed through untouched — only\n * the captured copy is bounded). Defaults to 64 KiB.\n */\n maxCaptureBytes?: number;\n}\n\n/** A running studio capture proxy. */\nexport interface RunningStudioProxy {\n /** The URL the proxy listens on (hand this to the user instead of upstream). */\n url: string;\n /** The actually-bound port. */\n port: number;\n /** Stop the proxy and release the port. */\n close: () => Promise<void>;\n}\n\nlet proxyIdCounter = 0;\n\n/**\n * Start a capturing reverse proxy in front of a studio serve target\n * (decision D4a: because every request to the served port flows through\n * `cdkl studio`, the timeline observes them regardless of source —\n * browser, curl, or the in-UI pad alike).\n *\n * Each HTTP request is forwarded to `upstream` and, in parallel,\n * captured (method / path / headers / bounded body) and emitted as an\n * `invocation` start event; when the upstream response completes, an end\n * event carries the status / headers / bounded body / duration. The full\n * bodies stream through untouched — only the captured copies are bounded.\n * `Upgrade` (WebSocket) requests are bridged raw to the upstream without\n * capture so they keep working.\n *\n * Studio is a control plane over the CLI, so this proxy sits in front of\n * the long-running `cdkl start-api` child the serve manager spawned; it\n * does NOT re-implement any routing — it forwards verbatim.\n */\nexport function startStudioProxy(config: StudioProxyConfig): Promise<RunningStudioProxy> {\n const host = config.host ?? '127.0.0.1';\n const clock = config.clock ?? Date.now;\n const maxCapture = config.maxCaptureBytes ?? 64 * 1024;\n const idFactory =\n config.idFactory ??\n (() => {\n proxyIdCounter += 1;\n return `req-${clock()}-${proxyIdCounter}`;\n });\n\n const upstreamUrl = new URL(config.upstream);\n const upstreamHost = upstreamUrl.hostname;\n const upstreamPort = Number(upstreamUrl.port) || 80;\n\n const server = createServer((clientReq, clientRes) => {\n const id = idFactory();\n const startedAt = clock();\n const path = clientReq.url ?? '/';\n const method = clientReq.method ?? 'GET';\n const label = `${method} ${path.split('?')[0]}`;\n\n // Capture the request body (bounded) while it streams to the upstream.\n const reqBody = boundedCollector(maxCapture);\n\n config.bus.emit('invocation', {\n id,\n ts: startedAt,\n target: config.target,\n kind: config.kind,\n label,\n request: { method, path, headers: { ...clientReq.headers } },\n });\n\n // Exactly ONE end event per request — whether it ends via the upstream\n // response, an upstream error, or a client abort. A second terminal\n // event (e.g. upstream socket erroring AFTER the response started) would\n // overwrite the real status on the timeline.\n let ended = false;\n const emitEnd = (status: number, response: unknown): void => {\n if (ended) return;\n ended = true;\n config.bus.emit('invocation', {\n id,\n ts: startedAt,\n target: config.target,\n kind: config.kind,\n label,\n request: { method, path, headers: { ...clientReq.headers }, body: reqBody.text() },\n response,\n status,\n durationMs: clock() - startedAt,\n });\n };\n\n const upstreamReq = httpRequest(\n {\n host: upstreamHost,\n port: upstreamPort,\n method,\n path,\n headers: clientReq.headers,\n },\n (upstreamRes) => {\n const respBody = boundedCollector(maxCapture);\n // Strip hop-by-hop headers before forwarding: `transfer-encoding` /\n // `connection` describe THIS connection, not the entity, and Node's\n // client already decoded a chunked body — re-declaring it (or a\n // stale `content-length`) can hang the client. `content-length` is\n // end-to-end and kept.\n clientRes.writeHead(upstreamRes.statusCode ?? 502, stripHopByHop(upstreamRes.headers));\n upstreamRes.on('data', (chunk: Buffer) => respBody.push(chunk));\n upstreamRes.pipe(clientRes);\n upstreamRes.on('end', () =>\n emitEnd(upstreamRes.statusCode ?? 502, {\n status: upstreamRes.statusCode,\n headers: { ...upstreamRes.headers },\n body: respBody.text(),\n })\n );\n upstreamRes.on('error', () => {\n // The upstream died mid-response: terminate the client response so\n // it does not hang half-open (a dangling client socket that the\n // server's later closeAllConnections would destroy, surfacing as an\n // unhandled error). pipe() does not auto-close the destination on a\n // source error, so do it explicitly.\n if (!clientRes.writableEnded) clientRes.destroy();\n emitEnd(502, 'upstream response stream error');\n });\n }\n );\n\n upstreamReq.on('error', (err) => {\n if (!clientRes.headersSent) clientRes.writeHead(502, { 'content-type': 'text/plain' });\n clientRes.end(`studio proxy: upstream error: ${err.message}`);\n emitEnd(502, `upstream error: ${err.message}`);\n });\n\n clientReq.on('data', (chunk: Buffer) => reqBody.push(chunk));\n clientReq.on('error', () => {\n // The client hung up mid-flight — tear down the upstream and close the\n // timeline row (499, the \"client closed request\" convention) so it\n // does not dangle as pending forever.\n upstreamReq.destroy();\n emitEnd(499, 'client aborted the request');\n });\n clientReq.pipe(upstreamReq);\n });\n\n // WebSocket / other `Upgrade` requests: bridge the raw socket to the\n // upstream without capture so they keep working through the proxy.\n // Hijacked upgrade sockets are detached from the server's request flow,\n // so track them and destroy them on close() (otherwise a live WebSocket\n // keeps the server from closing).\n const upgradeSockets = new Set<Socket>();\n server.on('upgrade', (req, clientSocket, head) => {\n const sock = clientSocket as Socket;\n upgradeSockets.add(sock);\n sock.on('close', () => upgradeSockets.delete(sock));\n bridgeUpgrade(req, sock, head, upstreamHost, upstreamPort);\n });\n\n return new Promise<RunningStudioProxy>((resolve, reject) => {\n server.once('error', reject);\n server.listen(0, host, () => {\n server.removeListener('error', reject);\n const port = (server.address() as AddressInfo).port;\n resolve({\n url: `http://${host}:${port}`,\n port,\n close: () =>\n new Promise<void>((resolveClose, rejectClose) => {\n for (const sock of upgradeSockets) sock.destroy();\n upgradeSockets.clear();\n server.close((err) => (err ? rejectClose(err) : resolveClose()));\n server.closeAllConnections?.();\n }),\n });\n });\n });\n}\n\n/** RFC 7230 hop-by-hop headers — never forwarded across a proxy. */\nconst HOP_BY_HOP = new Set([\n 'connection',\n 'keep-alive',\n 'proxy-authenticate',\n 'proxy-authorization',\n 'te',\n 'trailer',\n 'transfer-encoding',\n 'upgrade',\n]);\n\n/** Copy `headers` without the hop-by-hop ones (which describe one connection). */\nfunction stripHopByHop(\n headers: Record<string, string | string[] | undefined>\n): Record<string, string | string[] | undefined> {\n const out: Record<string, string | string[] | undefined> = {};\n for (const [k, v] of Object.entries(headers)) {\n if (!HOP_BY_HOP.has(k.toLowerCase())) out[k] = v;\n }\n return out;\n}\n\n/** A bounded byte collector that decodes to a (possibly truncated) utf8 string. */\nfunction boundedCollector(maxBytes: number): { push: (c: Buffer) => void; text: () => string } {\n const chunks: Buffer[] = [];\n let size = 0;\n let truncated = false;\n return {\n push: (c: Buffer): void => {\n if (size >= maxBytes) {\n truncated = true;\n return;\n }\n const room = maxBytes - size;\n if (c.length > room) {\n chunks.push(c.subarray(0, room));\n size = maxBytes;\n truncated = true;\n } else {\n chunks.push(c);\n size += c.length;\n }\n },\n text: (): string => {\n const s = Buffer.concat(chunks).toString('utf8');\n return truncated ? `${s}… (truncated)` : s;\n },\n };\n}\n\n/** Raw-bridge an `Upgrade` request (e.g. WebSocket) to the upstream. */\nfunction bridgeUpgrade(\n req: IncomingMessage,\n clientSocket: Socket,\n head: Buffer,\n upstreamHost: string,\n upstreamPort: number\n): void {\n const upstream = netConnect(upstreamPort, upstreamHost, () => {\n let raw = `${req.method} ${req.url} HTTP/1.1\\r\\n`;\n for (let i = 0; i < req.rawHeaders.length; i += 2) {\n raw += `${req.rawHeaders[i]}: ${req.rawHeaders[i + 1]}\\r\\n`;\n }\n raw += '\\r\\n';\n upstream.write(raw);\n if (head && head.length > 0) upstream.write(head);\n clientSocket.pipe(upstream);\n upstream.pipe(clientSocket);\n });\n const teardown = (): void => {\n clientSocket.destroy();\n upstream.destroy();\n };\n // Tear down BOTH sockets when either errors OR closes — a clean half-open\n // close (FIN, no error) on one side must not leave the peer dangling.\n upstream.on('error', teardown);\n upstream.on('close', teardown);\n clientSocket.on('error', teardown);\n clientSocket.on('close', teardown);\n}\n","import { spawn as nodeSpawn, type ChildProcessWithoutNullStreams } from 'node:child_process';\nimport { StudioEventBus, type StudioServeEvent, type StudioTargetKind } from './studio-events.js';\nimport {\n startStudioProxy,\n type RunningStudioProxy,\n type StudioProxyConfig,\n} from './studio-proxy.js';\n\n/** A request to start serving a target, as the studio UI posts it. */\nexport interface StudioServeRequest {\n /** Target id the user picked (display path or stack-qualified id). */\n targetId: string;\n /** Target kind. Slice C1 supports `'api'` (long-running `start-api`) only. */\n kind: StudioTargetKind;\n}\n\n/** A request to stop a running served target. */\nexport interface StudioStopRequest {\n /** Target id of the serve to stop. */\n targetId: string;\n}\n\n/** The public state of one running served target (returned to the UI). */\nexport interface StudioServeState {\n /** Target id of the served target. */\n targetId: string;\n /** The served target's kind. */\n kind: StudioTargetKind;\n /** Lifecycle status. */\n status: 'starting' | 'running' | 'stopped' | 'error';\n /** Served endpoint URLs, populated once running. */\n endpoints: string[];\n /** Child process id. */\n pid?: number;\n /** Wall-clock epoch ms when the serve started. */\n startedAt: number;\n}\n\n/** Config for {@link createStudioServeManager}. */\nexport interface StudioServeManagerConfig {\n /** Path to the `cdkl` CLI entry (`dist/cli.js`) — usually `process.argv[1]`. */\n cliEntry: string;\n /** The shared event bus; serve + log events are emitted onto it. */\n bus: StudioEventBus;\n /** Working directory for the child serve (defaults to `process.cwd()`). */\n cwd?: string;\n /** `--app` value to thread into the child serve, if studio was given one. */\n app?: string;\n /** `-c key=value` context overrides to thread through. */\n context?: Record<string, string>;\n /** `--profile` to thread through. */\n profile?: string;\n /** `--region` to thread through. */\n region?: string;\n /** Node binary to spawn (defaults to `process.execPath`; injectable for tests). */\n nodeBin?: string;\n /** Spawn implementation (injectable for tests). */\n spawnFn?: typeof nodeSpawn;\n /** Clock (injectable for tests; defaults to `Date.now`). */\n clock?: () => number;\n /**\n * Max ms to wait for the first `Server listening on ...` line before\n * giving up and killing the child. Generous by default to cover the\n * first-run base-image pull `start-api` triggers. Defaults to 120_000.\n */\n readyTimeoutMs?: number;\n /**\n * Ms to wait for a SIGTERM'd child to exit before sending SIGKILL.\n * Defaults to 10_000.\n */\n stopGraceMs?: number;\n /** `setTimeout` (injectable for tests). */\n setTimeoutFn?: typeof setTimeout;\n /** `clearTimeout` (injectable for tests). */\n clearTimeoutFn?: typeof clearTimeout;\n /**\n * Factory for the capture proxy fronting each HTTP serve endpoint\n * (slice C2). Injectable for tests; defaults to {@link startStudioProxy}.\n * When `captureRequests` is false this is never called.\n */\n proxyFactory?: (config: StudioProxyConfig) => Promise<RunningStudioProxy>;\n /**\n * Front each HTTP serve endpoint with a capture proxy so every request\n * to the served port lands on the studio timeline (decision D4a).\n * Defaults to true; set false to hand the child's port through\n * unproxied (the slice C1 behavior).\n */\n captureRequests?: boolean;\n}\n\n/** A bound serve manager exposing the start / stop / list surface. */\nexport interface StudioServeManager {\n /** Start serving a target; resolves once it is listening (or rejects). */\n start: (req: StudioServeRequest) => Promise<StudioServeState>;\n /** Stop a running served target; resolves once the child has exited. */\n stop: (req: StudioStopRequest) => Promise<void>;\n /** Snapshot of every running served target. */\n list: () => StudioServeState[];\n /** Stop every running served target (graceful shutdown). */\n stopAll: () => Promise<void>;\n}\n\n/** Kinds the serve manager can start in this build. */\nconst SERVE_SUPPORTED: readonly StudioTargetKind[] = ['api'];\n\n/** `Server listening on <url>` is the stable ready marker `start-api` prints. */\nconst LISTENING_RE = /Server listening on (\\S+)/;\n\ninterface ServeEntry extends StudioServeState {\n child: ChildProcessWithoutNullStreams;\n /** Capture proxies fronting this serve's HTTP endpoints (slice C2). */\n proxies: RunningStudioProxy[];\n /**\n * Set by `stop()` when it tears the child down WHILE it is still\n * `starting` (the ready promise has not settled). The child's `close`\n * handler reads it so a user-initiated stop is not misreported as a\n * boot failure (no `error` event, no `start()` reject as a crash).\n */\n stopping?: boolean;\n}\n\n/**\n * Build the studio serve manager. Slice C1 drives a long-running\n * `cdkl start-api <target>` child — studio is a control plane over the\n * CLI (the same pattern as the single-shot invoke dispatcher), so it\n * spawns the SAME headless serve command rather than re-wiring its\n * internals. This preserves byte-for-byte parity and isolates the\n * server's process-global behavior in a child.\n *\n * `start()` spawns the child with `--port 0` (OS-assigned, collision\n * free), streams its stdout/stderr onto the bus as `log` events keyed by\n * the target id, and resolves once the child prints its first\n * `Server listening on <url>` line — emitting a `serve` `running` event\n * with the discovered endpoints. `stop()` SIGTERMs the child (SIGKILL\n * after a grace window) and emits `stopped`.\n *\n * Slice C2 fronts each HTTP serve endpoint with a capture proxy\n * ({@link startStudioProxy}) so every request to the served port lands\n * on the studio timeline (decision D4a); the `endpoints` the UI is\n * handed are the proxy URLs. Full-text log search and alb / ecs serve\n * kinds are still to come.\n */\nexport function createStudioServeManager(config: StudioServeManagerConfig): StudioServeManager {\n const spawnFn = config.spawnFn ?? nodeSpawn;\n const nodeBin = config.nodeBin ?? process.execPath;\n const clock = config.clock ?? Date.now;\n const readyTimeoutMs = config.readyTimeoutMs ?? 120_000;\n const stopGraceMs = config.stopGraceMs ?? 10_000;\n const setTimeoutFn = config.setTimeoutFn ?? setTimeout;\n const clearTimeoutFn = config.clearTimeoutFn ?? clearTimeout;\n const cwd = config.cwd ?? process.cwd();\n const proxyFactory = config.proxyFactory ?? startStudioProxy;\n const captureRequests = config.captureRequests ?? true;\n\n const entries = new Map<string, ServeEntry>();\n\n /** Close every capture proxy fronting `e` (best-effort; idempotent). */\n async function closeProxies(e: ServeEntry): Promise<void> {\n const proxies = e.proxies.splice(0);\n await Promise.all(proxies.map((p) => p.close().catch(() => undefined)));\n }\n\n function publicState(e: ServeEntry): StudioServeState {\n const s: StudioServeState = {\n targetId: e.targetId,\n kind: e.kind,\n status: e.status,\n endpoints: [...e.endpoints],\n startedAt: e.startedAt,\n };\n if (e.pid !== undefined) s.pid = e.pid;\n return s;\n }\n\n function emitServe(e: ServeEntry, message?: string): void {\n const ev: StudioServeEvent = {\n ts: clock(),\n target: e.targetId,\n kind: e.kind,\n status: e.status,\n endpoints: [...e.endpoints],\n };\n if (e.pid !== undefined) ev.pid = e.pid;\n if (message !== undefined) ev.message = message;\n config.bus.emit('serve', ev);\n }\n\n function buildArgs(targetId: string): string[] {\n const args = ['start-api', targetId, '--port', '0', '--host', '127.0.0.1'];\n if (config.app) args.push('--app', config.app);\n if (config.profile) args.push('--profile', config.profile);\n if (config.region) args.push('--region', config.region);\n for (const [k, v] of Object.entries(config.context ?? {})) {\n args.push('-c', `${k}=${v}`);\n }\n return args;\n }\n\n async function start(req: StudioServeRequest): Promise<StudioServeState> {\n if (!SERVE_SUPPORTED.includes(req.kind)) {\n throw new Error(\n `Serving '${req.kind}' targets from studio is not supported yet (API only in this build).`\n );\n }\n const existing = entries.get(req.targetId);\n if (existing && existing.status !== 'stopped' && existing.status !== 'error') {\n throw new Error(`'${req.targetId}' is already running.`);\n }\n\n const startedAt = clock();\n let child: ChildProcessWithoutNullStreams;\n try {\n child = spawnFn(nodeBin, [config.cliEntry, ...buildArgs(req.targetId)], { cwd });\n } catch (err) {\n throw err instanceof Error ? err : new Error(String(err));\n }\n\n const entry: ServeEntry = {\n targetId: req.targetId,\n kind: req.kind,\n status: 'starting',\n endpoints: [],\n startedAt,\n child,\n proxies: [],\n };\n if (child.pid !== undefined) entry.pid = child.pid;\n entries.set(req.targetId, entry);\n emitServe(entry);\n\n return new Promise<StudioServeState>((resolve, reject) => {\n let settled = false;\n child.stdout.setEncoding('utf8');\n child.stderr.setEncoding('utf8');\n\n const timer = setTimeoutFn(() => {\n if (settled) return;\n settled = true;\n entry.status = 'error';\n emitServe(entry, `Timed out after ${readyTimeoutMs}ms waiting for the server to listen.`);\n // Graceful SIGTERM -> SIGKILL (NOT an immediate SIGKILL): `start-api`\n // traps SIGTERM to tear down its RIE containers, so a hard kill on a\n // half-booted child would orphan those containers.\n void stopChild(child, stopGraceMs, setTimeoutFn, clearTimeoutFn);\n void closeProxies(entry);\n entries.delete(req.targetId);\n reject(new Error(`'${req.targetId}' did not start within ${readyTimeoutMs}ms.`));\n }, readyTimeoutMs);\n timer.unref?.();\n\n const becomeRunning = (): void => {\n if (settled) return;\n settled = true;\n clearTimeoutFn(timer);\n entry.status = 'running';\n emitServe(entry);\n resolve(publicState(entry));\n };\n\n // Resolve a child `Server listening on <childUrl>` line into the\n // endpoint the UI is handed: an HTTP endpoint is fronted with a\n // capture proxy (slice C2 / decision D4a) so every request to it\n // lands on the timeline; ws:// (and capture-off) endpoints pass the\n // child URL straight through. The FIRST resolved endpoint flips the\n // serve to running; later ones append + re-emit.\n const onListening = async (childUrl: string): Promise<void> => {\n let endpoint = childUrl;\n if (captureRequests && /^https?:/i.test(childUrl)) {\n try {\n const proxy = await proxyFactory({\n bus: config.bus,\n target: req.targetId,\n kind: req.kind,\n upstream: childUrl,\n });\n entry.proxies.push(proxy);\n endpoint = proxy.url;\n } catch {\n // Proxy failed to bind — fall back to the direct child URL so\n // the serve is still usable (just uncaptured).\n endpoint = childUrl;\n }\n }\n // A stop() may have raced the async proxy startup; if so, drop the\n // now-orphan proxy and do not resurrect the torn-down serve.\n if (entry.stopping || (settled && !entries.has(req.targetId))) {\n await closeProxies(entry);\n return;\n }\n if (!entry.endpoints.includes(endpoint)) entry.endpoints.push(endpoint);\n if (settled) emitServe(entry);\n else becomeRunning();\n };\n\n streamLines(child.stdout, (line) => {\n const m = LISTENING_RE.exec(line);\n if (m?.[1]) void onListening(m[1]);\n emitLog(config.bus, clock, req.targetId, line, 'stdout');\n });\n streamLines(child.stderr, (line) => {\n emitLog(config.bus, clock, req.targetId, line, 'stderr');\n });\n\n child.on('error', (err) => {\n if (settled) {\n // Post-ready spawn error: mark the entry errored for the UI.\n entry.status = 'error';\n emitServe(entry, err.message);\n void closeProxies(entry);\n entries.delete(req.targetId);\n return;\n }\n settled = true;\n clearTimeoutFn(timer);\n entry.status = 'error';\n emitServe(entry, err.message);\n void closeProxies(entry);\n entries.delete(req.targetId);\n reject(err);\n });\n\n child.on('close', (code) => {\n if (!settled) {\n settled = true;\n clearTimeoutFn(timer);\n if (entry.stopping) {\n // A user `stop()` raced the still-starting child: not a boot\n // failure. `stop()` emits the `stopped` event + resolves itself,\n // so here we only reject the pending `start()` (the /api/run POST)\n // with an honest \"cancelled\" message and emit nothing.\n reject(new Error(`'${req.targetId}' was stopped before it finished starting.`));\n return;\n }\n // Exited before ever listening — a boot failure.\n entry.status = 'error';\n const msg = `Server exited before listening (code ${code ?? 'null'}).`;\n emitServe(entry, msg);\n void closeProxies(entry);\n entries.delete(req.targetId);\n reject(new Error(msg));\n return;\n }\n // Exited while running (crash or our own stop()). If still tracked\n // as running, surface it as stopped; stop() removes the entry\n // itself before this fires in the graceful path.\n const tracked = entries.get(req.targetId);\n if (tracked === entry && entry.status === 'running') {\n entry.status = 'stopped';\n emitServe(entry, `Server process exited (code ${code ?? 'null'}).`);\n void closeProxies(entry);\n entries.delete(req.targetId);\n }\n });\n });\n }\n\n async function stop(req: StudioStopRequest): Promise<void> {\n const entry = entries.get(req.targetId);\n if (!entry) {\n throw new Error(`'${req.targetId}' is not running.`);\n }\n // Flag so the child's `close` handler treats this as a user stop, not a\n // boot failure, when the serve was still `starting` (#1).\n entry.stopping = true;\n entries.delete(req.targetId);\n // Tear down the capture proxies and SIGTERM the child concurrently —\n // both are initiated synchronously (stopChild attaches its `close`\n // listener + sends SIGTERM in the same tick) so neither races the\n // child's exit.\n await Promise.all([\n closeProxies(entry),\n stopChild(entry.child, stopGraceMs, setTimeoutFn, clearTimeoutFn),\n ]);\n entry.status = 'stopped';\n emitServe(entry);\n }\n\n function list(): StudioServeState[] {\n return [...entries.values()].map(publicState);\n }\n\n async function stopAll(): Promise<void> {\n const targets = [...entries.keys()];\n // `stop()` itself always resolves for a tracked target (stopChild\n // escalates SIGTERM -> SIGKILL and resolves on close); the only reject\n // path is the \"not running\" race if a sibling stop already evicted the\n // key. The catch keeps one such race from aborting the rest of the\n // shutdown sweep.\n await Promise.all(targets.map((targetId) => stop({ targetId }).catch(() => undefined)));\n }\n\n return { start, stop, list, stopAll };\n}\n\n/** Emit one container log line onto the bus, keyed by the serve target id. */\nfunction emitLog(\n bus: StudioEventBus,\n clock: () => number,\n target: string,\n line: string,\n stream: 'stdout' | 'stderr'\n): void {\n bus.emit('log', { ts: clock(), containerId: target, target, line, stream });\n}\n\n/**\n * Line-buffer a child stream and invoke `onLine` per complete line\n * (trailing newline stripped, blank lines dropped). Flushes any partial\n * final line on stream end.\n */\nfunction streamLines(stream: NodeJS.ReadableStream, onLine: (line: string) => void): void {\n let buf = '';\n stream.on('data', (chunk: string) => {\n buf += chunk;\n let nl = buf.indexOf('\\n');\n while (nl !== -1) {\n const line = buf.slice(0, nl).replace(/\\r$/, '');\n buf = buf.slice(nl + 1);\n if (line.length > 0) onLine(line);\n nl = buf.indexOf('\\n');\n }\n });\n stream.on('end', () => {\n const line = buf.replace(/\\r$/, '');\n if (line.length > 0) onLine(line);\n buf = '';\n });\n}\n\n/**\n * SIGTERM a child and resolve once it exits, escalating to SIGKILL after\n * `graceMs`. Resolves immediately if the child has already exited.\n */\nfunction stopChild(\n child: ChildProcessWithoutNullStreams,\n graceMs: number,\n setTimeoutFn: typeof setTimeout,\n clearTimeoutFn: typeof clearTimeout\n): Promise<void> {\n return new Promise<void>((resolve) => {\n let done = false;\n let kill: ReturnType<typeof setTimeout> | undefined;\n const finish = (): void => {\n if (done) return;\n done = true;\n if (kill) clearTimeoutFn(kill);\n resolve();\n };\n // Attach the `close` listener BEFORE the already-exited check so a child\n // that exits in the window between the check and the listener attach is\n // not missed (which would leave the promise pending forever). #4\n child.once('close', finish);\n if (child.exitCode !== null || child.signalCode !== null) {\n finish();\n return;\n }\n kill = setTimeoutFn(() => {\n if (!done) child.kill('SIGKILL');\n }, graceMs);\n kill.unref?.();\n child.kill('SIGTERM');\n });\n}\n","import { spawn } from 'node:child_process';\nimport { Command, Option } from 'commander';\nimport {\n appOptions,\n commonOptions,\n contextOptions,\n regionOption,\n parseContextOptions,\n} from '../options.js';\nimport { getLogger } from '../../utils/logger.js';\nimport { applyRoleArnIfSet } from '../../utils/role-arn.js';\nimport { withErrorHandling } from '../../utils/error-handler.js';\nimport { Synthesizer, type SynthesisOptions } from '../../synthesis/synthesizer.js';\nimport { resolveApp } from '../config-loader.js';\nimport {\n getEmbedConfig,\n setEmbedConfig,\n type CdkLocalEmbedConfig,\n} from '../../local/embed-config.js';\nimport { listTargets } from '../../local/target-lister.js';\nimport { StudioEventBus, type StudioTargetKind } from '../../local/studio-events.js';\nimport {\n startStudioServer,\n toStudioTargetGroups,\n type RunningStudioServer,\n} from '../../local/studio-server.js';\nimport { createStudioDispatcher, type StudioRunRequest } from '../../local/studio-dispatch.js';\nimport {\n createStudioServeManager,\n type StudioServeManager,\n type StudioStopRequest,\n} from '../../local/studio-serve-manager.js';\n\nconst STUDIO_TARGET_KINDS: readonly StudioTargetKind[] = [\n 'lambda',\n 'api',\n 'alb',\n 'ecs',\n 'agentcore',\n];\n\n/**\n * Validate + narrow the untyped `POST /api/run` body into a\n * {@link StudioRunRequest}. Throws (→ 400 from the server) on a malformed\n * body so a bad UI / curl payload fails loudly rather than spawning an\n * `invoke` for an empty target.\n */\nexport function coerceRunRequest(body: unknown): StudioRunRequest {\n if (typeof body !== 'object' || body === null) {\n throw new Error('Request body must be a JSON object.');\n }\n const { targetId, kind, event } = body as Record<string, unknown>;\n if (typeof targetId !== 'string' || targetId.trim() === '') {\n throw new Error('Request body must include a non-empty \"targetId\" string.');\n }\n if (typeof kind !== 'string' || !STUDIO_TARGET_KINDS.includes(kind as StudioTargetKind)) {\n throw new Error(`Request body \"kind\" must be one of: ${STUDIO_TARGET_KINDS.join(', ')}.`);\n }\n return { targetId, kind: kind as StudioTargetKind, event };\n}\n\n/**\n * Validate + narrow the untyped `POST /api/stop` body into a\n * {@link StudioStopRequest}. Throws (→ 400 from the server) on a missing\n * / empty target id.\n */\nexport function coerceStopRequest(body: unknown): StudioStopRequest {\n if (typeof body !== 'object' || body === null) {\n throw new Error('Request body must be a JSON object.');\n }\n const { targetId } = body as Record<string, unknown>;\n if (typeof targetId !== 'string' || targetId.trim() === '') {\n throw new Error('Request body must include a non-empty \"targetId\" string.');\n }\n return { targetId };\n}\n\nconst DEFAULT_STUDIO_PORT = 9999;\n\n/**\n * Parse + validate the `--studio-port` value. Accepts `0` (OS-assigned)\n * through `65535`. Exported so a unit test can assert the bounds without\n * driving the full command. Throws on anything out of range / non-numeric.\n */\nexport function parseStudioPort(raw: string): number {\n // `Number('')` / `Number(' ')` coerce to 0, which would pass the range\n // check; reject blank input explicitly.\n const port = raw.trim() === '' ? NaN : Number(raw);\n if (!Number.isInteger(port) || port < 0 || port > 65535) {\n throw new Error(`--studio-port must be 0..65535 (got ${raw}).`);\n }\n return port;\n}\n\ninterface LocalStudioOptions {\n app?: string;\n output: string;\n verbose: boolean;\n profile?: string;\n roleArn?: string;\n context?: string[];\n region?: string;\n /** `--studio-port`: preferred listen port (bumps on collision). */\n studioPort: string;\n /** `--no-open`: suppress auto-opening the browser (Commander sets `open`). */\n open: boolean;\n}\n\n/**\n * Factory options for {@link createLocalStudioCommand}.\n */\nexport interface CreateLocalStudioCommandOptions {\n /** Embed-time branding overrides for a host wrapping this factory. */\n embedConfig?: CdkLocalEmbedConfig;\n}\n\nasync function localStudioCommand(options: LocalStudioOptions): Promise<void> {\n const logger = getLogger();\n if (options.verbose) logger.setLevel('debug');\n\n const port = parseStudioPort(options.studioPort);\n\n await applyRoleArnIfSet({\n roleArn: options.roleArn,\n region: undefined,\n profile: options.profile,\n });\n\n const appCmd = resolveApp(options.app);\n if (!appCmd) {\n throw new Error(\n `No CDK app specified. Pass --app, set ${getEmbedConfig().envPrefix}_APP, or add \"app\" to cdk.json.`\n );\n }\n\n logger.info('Synthesizing CDK app...');\n const synthesizer = new Synthesizer();\n const context = parseContextOptions(options.context);\n const synthOpts: SynthesisOptions = {\n app: appCmd,\n output: options.output,\n ...(options.profile && { profile: options.profile }),\n ...(Object.keys(context).length > 0 && { context }),\n };\n const { stacks } = await synthesizer.synthesize(synthOpts);\n\n const listing = listTargets(stacks);\n const targetGroups = toStudioTargetGroups(listing);\n const appLabel = stacks.map((s) => s.stackName).join(', ') || appCmd;\n\n const bus = new StudioEventBus();\n // `process.argv[1]` is the running CLI entry (`dist/cli.js` / the `cdkl`\n // bin); both the invoke dispatcher and the serve manager spawn it again\n // (`cdkl invoke <target>` / `cdkl start-api <target>`) — studio is a\n // control plane over the CLI.\n const cliEntry = process.argv[1] ?? '';\n const childConfig = {\n cliEntry,\n bus,\n cwd: process.cwd(),\n ...(appCmd ? { app: appCmd } : {}),\n ...(options.profile ? { profile: options.profile } : {}),\n ...(options.region ? { region: options.region } : {}),\n ...(Object.keys(context).length > 0 ? { context } : {}),\n };\n const dispatcher = createStudioDispatcher(childConfig);\n const serveManager = createStudioServeManager(childConfig);\n\n const server = await startStudioServer({\n port,\n bus,\n targetGroups,\n appLabel,\n cliName: getEmbedConfig().cliName,\n // `/api/run`: a Lambda is a single-shot invoke; everything else is a\n // long-running serve start (slice C1 supports the `api` kind, the\n // serve manager rejects the rest with a clear message).\n onRun: (body) => {\n const req = coerceRunRequest(body);\n return req.kind === 'lambda' ? dispatcher.run(req) : serveManager.start(req);\n },\n onStop: async (body) => {\n const req = coerceStopRequest(body);\n await serveManager.stop(req);\n return { stopped: req.targetId };\n },\n getRunning: () => ({ running: serveManager.list() }),\n });\n\n const cliName = getEmbedConfig().cliName;\n logger.info(`${cliName} studio is running at ${server.url}`);\n logger.info('Press Ctrl-C to stop.');\n\n // Auto-open the browser only in an interactive terminal (never in CI /\n // piped / integ runs) and unless --no-open was passed.\n if (options.open && process.stdout.isTTY) {\n openBrowser(server.url);\n }\n\n await blockUntilShutdown(server, serveManager, cliName);\n}\n\n/** Best-effort cross-platform browser open. Failures are non-fatal. */\nfunction openBrowser(url: string): void {\n const cmd =\n process.platform === 'darwin' ? 'open' : process.platform === 'win32' ? 'start' : 'xdg-open';\n try {\n const child = spawn(cmd, [url], {\n stdio: 'ignore',\n detached: true,\n shell: process.platform === 'win32',\n });\n child.on('error', () => undefined);\n child.unref();\n } catch {\n // Opening the browser is a convenience; ignore any failure.\n }\n}\n\n/**\n * Block until SIGINT / SIGTERM, then stop every running serve child,\n * close the studio server, and resolve. Mirrors the long-running serve\n * commands' graceful-shutdown contract — the serve children are killed\n * BEFORE the server closes so their RIE containers are torn down rather\n * than orphaned.\n */\nfunction blockUntilShutdown(\n server: RunningStudioServer,\n serveManager: StudioServeManager,\n cliName: string\n): Promise<void> {\n return new Promise<void>((resolveShutdown) => {\n let shuttingDown = false;\n const shutdown = (signal: string): void => {\n if (shuttingDown) return;\n shuttingDown = true;\n getLogger().info(`Received ${signal}; stopping ${cliName} studio...`);\n void serveManager\n .stopAll()\n .catch((err: unknown) => getLogger().warn(`Error stopping serve targets: ${String(err)}`))\n .then(() => server.close())\n .catch((err: unknown) => getLogger().warn(`Error stopping studio server: ${String(err)}`))\n .finally(() => resolveShutdown());\n };\n process.on('SIGINT', () => shutdown('SIGINT'));\n process.on('SIGTERM', () => shutdown('SIGTERM'));\n });\n}\n\nexport function createLocalStudioCommand(opts: CreateLocalStudioCommandOptions = {}): Command {\n setEmbedConfig(opts.embedConfig);\n const cmd = new Command('studio')\n .description(\n \"Open the local studio: a web console that lists the synthesized CDK app's runnable \" +\n 'targets and lets you invoke / serve them from the browser while watching all activity ' +\n 'in one timeline. The interactive counterpart to the headless invoke / start-* commands.'\n )\n .action(\n withErrorHandling(async (options: LocalStudioOptions) => {\n await localStudioCommand(options);\n })\n );\n\n addStudioSpecificOptions(cmd);\n [...commonOptions(), ...appOptions(), ...contextOptions].forEach((opt) => cmd.addOption(opt));\n cmd.addOption(regionOption);\n return cmd;\n}\n\n/**\n * Register the option block `cdkl studio` adds on top of the shared\n * common / app / context option helpers. Kept in a named helper (not\n * inline in {@link createLocalStudioCommand}) so a host CLI embedding\n * this factory inherits new studio flags without a duplicate\n * `.addOption(...)` block, matching every other `add<Cmd>SpecificOptions`\n * extraction. Chainable: returns `cmd`.\n */\nexport function addStudioSpecificOptions(cmd: Command): Command {\n cmd.addOption(\n new Option(\n '--studio-port <port>',\n 'Preferred port for the studio web server (bumps to the next free port on collision)'\n ).default(String(DEFAULT_STUDIO_PORT))\n );\n cmd.addOption(\n new Option('--no-open', 'Do not auto-open the browser when studio starts (TTY only)')\n );\n return cmd;\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cdk-local",
3
- "version": "0.80.0",
3
+ "version": "0.81.1",
4
4
  "description": "Run your CDK-built app locally — standalone, or against the real AWS resources and data it depends on. A CDK-native alternative to sam local.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -1 +0,0 @@
1
- {"version":3,"file":"local-studio-MXuSiMqC.js","names":["nodeSpawn","nodeSpawn"],"sources":["../src/local/studio-events.ts","../src/local/studio-ui.ts","../src/local/studio-server.ts","../src/local/studio-dispatch.ts","../src/local/studio-serve-manager.ts","../src/cli/commands/local-studio.ts"],"sourcesContent":["import { EventEmitter } from 'node:events';\n\n/**\n * The kind of runnable target an invocation ran against. Mirrors the\n * categories `cdkl list` / the studio target list expose.\n */\nexport type StudioTargetKind = 'lambda' | 'api' | 'alb' | 'ecs' | 'agentcore';\n\n/**\n * One request observed by `cdkl studio` — a single-shot invoke or one\n * request to a served target. Emitted twice per request: once at start\n * (status/durationMs/response absent) and once at end (filled in). The\n * UI threads the two by {@link StudioInvocationEvent.id}.\n *\n * Slice A defines the shape; the dispatch layer that emits populated\n * events lands with the invoke / serve slices.\n */\nexport interface StudioInvocationEvent {\n /** Correlation id, unique per request. Stable across the start/end pair. */\n id: string;\n /** Wall-clock epoch ms when the request started. */\n ts: number;\n /** Target id the request ran against (stack-qualified or display path). */\n target: string;\n /** The target's kind, for the UI's per-kind affordances. */\n kind: StudioTargetKind;\n /** Short request label for the timeline row (e.g. `GET /orders`). */\n label: string;\n /** The request payload (Lambda event JSON or the HTTP request shape). */\n request?: unknown;\n /** Response payload, set on the end event. */\n response?: unknown;\n /** HTTP status / invoke outcome code, set on the end event. */\n status?: number;\n /** Wall-clock duration in ms, set on the end event. */\n durationMs?: number;\n /**\n * When this invocation is a re-run of an earlier one (Phase 3), the id\n * of the source invocation. The UI links the new row to its origin.\n */\n reinvokeOf?: string;\n}\n\n/**\n * One container stdout/stderr line, tagged with its emitting container\n * so the detail panel can scope logs at CloudWatch granularity (Lambda\n * per-invocation, ECS per-container).\n */\nexport interface StudioLogEvent {\n /** Wall-clock epoch ms when the line was observed. */\n ts: number;\n /** Container id that emitted the line. */\n containerId: string;\n /** Target id the container backs, for the UI grouping. */\n target: string;\n /** The raw log line (without trailing newline). */\n line: string;\n /** Parsed stream, when known. */\n stream?: 'stdout' | 'stderr';\n}\n\n/**\n * Lifecycle of a long-running served target (`api` / `alb` / `ecs`\n * service) the studio started. Unlike {@link StudioInvocationEvent}\n * (one per request), a serve emits one event per status TRANSITION:\n * `starting` when the child is spawned, `running` once it is listening\n * (with the served endpoints), `stopped` after a clean stop, `error`\n * when it failed to come up or crashed. The UI keys these by\n * {@link StudioServeEvent.target} to drive the per-target `running ●\n * :port [Stop]` affordance.\n */\nexport interface StudioServeEvent {\n /** Wall-clock epoch ms when the status was observed. */\n ts: number;\n /** Target id of the served target (stack-qualified or display path). */\n target: string;\n /** The served target's kind. */\n kind: StudioTargetKind;\n /** Lifecycle status this event reports. */\n status: 'starting' | 'running' | 'stopped' | 'error';\n /**\n * Served endpoint URLs, present on `running` (e.g.\n * `['http://127.0.0.1:51234']`). A single served target can expose\n * several (multiple APIs / WebSocket listeners), so this is a list.\n */\n endpoints?: string[];\n /** Child process id, present from `starting` onward. */\n pid?: number;\n /** Status / error detail (e.g. the failure reason on `error`). */\n message?: string;\n}\n\n/**\n * Map of event name -> listener argument, for the typed wrapper below.\n */\ninterface StudioEventMap {\n invocation: [StudioInvocationEvent];\n log: [StudioLogEvent];\n serve: [StudioServeEvent];\n}\n\n/**\n * In-process event bus that every studio observation flows through. The\n * studio HTTP server subscribes and forwards events to the browser over\n * SSE; the dispatch / log-streaming layers emit onto it.\n *\n * A thin typed wrapper over {@link EventEmitter} so producers and the\n * server agree on the event shapes without `any`. Re-exported from\n * `cdk-local/internal` so a host CLI embedding studio can subscribe.\n */\nexport class StudioEventBus {\n private readonly emitter = new EventEmitter();\n\n constructor() {\n // The bus can accumulate many SSE subscribers across a long session;\n // raise the cap so Node does not warn about a suspected leak.\n this.emitter.setMaxListeners(0);\n }\n\n emit<E extends keyof StudioEventMap>(event: E, ...args: StudioEventMap[E]): void {\n this.emitter.emit(event, ...args);\n }\n\n on<E extends keyof StudioEventMap>(\n event: E,\n listener: (...args: StudioEventMap[E]) => void\n ): this {\n this.emitter.on(event, listener as (...args: unknown[]) => void);\n return this;\n }\n\n off<E extends keyof StudioEventMap>(\n event: E,\n listener: (...args: StudioEventMap[E]) => void\n ): this {\n this.emitter.off(event, listener as (...args: unknown[]) => void);\n return this;\n }\n\n /**\n * Number of listeners currently subscribed to `event`. Exposed so the\n * SSE server's subscribe / unsubscribe symmetry can be asserted (a\n * dropped client must not leak a listener).\n */\n listenerCount<E extends keyof StudioEventMap>(event: E): number {\n return this.emitter.listenerCount(event);\n }\n}\n","/**\n * The studio web UI, embedded as a string so it ships inside the\n * `cdk-local` npm package (decision D9) with no asset-copy build step —\n * `tsdown` bundles this module like any other source file. Served by\n * the studio HTTP server (`startStudioServer`) at `GET /`.\n *\n * 3-pane shell (decision D6), framework-free vanilla JS (decision D7):\n * - left = target list (from `GET /api/targets`); each Lambda has an\n * [Invoke] button, each API a [Start] / [Stop] serve control with a\n * `running ● :port` indicator (slice C1), plus a selected-highlight.\n * - center = the WORKSPACE for the selected target: for a Lambda, an\n * event composer (textarea + Invoke button) with the latest run's\n * Request / Response / Logs shown below; for an API, a Start/Stop\n * control with the served endpoints + streaming logs.\n * - right = the timeline (history) of every invocation; clicking a\n * row loads it back into the workspace.\n *\n * The center workspace is deliberately adjacent to the left target list\n * (short eye-travel: pick a target -> compose right next to it), and is\n * the primary surface — the timeline is secondary history.\n */\n\nconst STUDIO_CSS = `\n * { box-sizing: border-box; }\n body {\n margin: 0; font: 13px/1.5 ui-monospace, SFMono-Regular, Menlo, monospace;\n color: #e6e6e6; background: #1a1a1a; height: 100vh; overflow: hidden;\n }\n header {\n padding: 8px 14px; background: #111; border-bottom: 1px solid #333;\n display: flex; align-items: center; gap: 10px;\n }\n header .brand { font-weight: 700; color: #fff; }\n header .meta { color: #888; font-size: 12px; }\n main {\n display: grid; grid-template-columns: 280px 5px 1fr 5px 320px;\n height: calc(100vh - 38px);\n }\n .pane { overflow: auto; }\n .splitter { background: #2a2a2a; cursor: col-resize; }\n .splitter:hover, .splitter.dragging { background: #4ec97a; }\n .pane h2 {\n margin: 0; padding: 8px 12px; font-size: 11px; text-transform: uppercase;\n letter-spacing: 0.5px; color: #888; background: #151515;\n position: sticky; top: 0; border-bottom: 1px solid #2a2a2a; z-index: 1;\n }\n .group-title { padding: 8px 12px 2px; color: #6aa9ff; font-size: 11px; }\n .target {\n padding: 6px 12px; border-bottom: 1px solid #222; display: flex;\n align-items: center; gap: 8px;\n }\n .target.runnable { cursor: pointer; }\n .target.runnable:hover { background: #202020; }\n .target.sel { background: #2a3550; }\n .target .name { color: #ddd; flex: 1; overflow: hidden; text-overflow: ellipsis; }\n .target .kind { color: #777; font-size: 11px; }\n .target .invoke-btn {\n padding: 2px 10px; font: 11px ui-monospace, Menlo, monospace; font-weight: 700;\n color: #0d1f12; background: #4ec97a; border: 0; border-radius: 3px; cursor: pointer;\n }\n .target .invoke-btn:hover { background: #6fe097; }\n .target.sel .invoke-btn { background: #6fe097; }\n .target .stop-btn {\n padding: 2px 10px; font: 11px ui-monospace, Menlo, monospace; font-weight: 700;\n color: #2a0d0d; background: #e0707a; border: 0; border-radius: 3px; cursor: pointer;\n }\n .target .stop-btn:hover { background: #ec8a92; }\n .target .run-dot { color: #7bd88f; font-size: 11px; white-space: nowrap; }\n .target .run-dot.starting { color: #e0b54e; }\n .empty { padding: 16px 12px; color: #666; }\n .row {\n padding: 5px 12px; border-bottom: 1px solid #222; cursor: pointer;\n display: flex; gap: 8px; white-space: nowrap;\n }\n .row:hover { background: #222; }\n .row.sel { background: #2a3550; }\n .row .ts { color: #777; }\n .row .label { color: #ddd; flex: 1; overflow: hidden; text-overflow: ellipsis; }\n .row .status { color: #7bd88f; }\n .row .status.err { color: #e0707a; }\n #workspace { padding: 0 0 24px; }\n .composer { padding: 10px 12px; border-bottom: 1px solid #2a2a2a; }\n .composer .target-name { color: #fff; font-weight: 700; margin-bottom: 6px; }\n .composer textarea {\n width: 100%; min-height: 130px; resize: vertical; background: #111; color: #ddd;\n border: 1px solid #333; border-radius: 3px; padding: 6px;\n font: 12px/1.5 ui-monospace, SFMono-Regular, Menlo, monospace;\n }\n .composer button {\n margin-top: 8px; padding: 6px 18px; background: #2a7d46; color: #fff;\n border: 0; border-radius: 3px; cursor: pointer; font: inherit; font-weight: 700;\n }\n .composer button:hover { background: #339152; }\n .composer button:disabled { background: #333; color: #888; cursor: default; }\n .composer .err { color: #e0707a; margin-top: 6px; min-height: 18px; }\n .section { padding: 8px 12px; border-bottom: 1px solid #222; }\n .section h3 { margin: 0 0 6px; font-size: 11px; color: #888; text-transform: uppercase; }\n .section h3 .ok { color: #7bd88f; }\n .section h3 .bad { color: #e0707a; }\n .section pre { margin: 0; white-space: pre-wrap; word-break: break-word; color: #cfcfcf; }\n .endpoint { display: block; color: #6aa9ff; text-decoration: none; padding: 2px 0; }\n .endpoint:hover { text-decoration: underline; }\n #conn { font-size: 11px; }\n #conn.up { color: #7bd88f; }\n #conn.down { color: #e0707a; }\n`;\n\nconst STUDIO_SCRIPT = `\n const KIND_LABEL = { lambda: 'Lambda', api: 'API', alb: 'ALB', ecs: 'ECS', agentcore: 'AgentCore' };\n const rowsById = new Map(); // invocationId -> timeline row element\n const invById = new Map(); // invocationId -> latest invocation event\n const logsById = new Map(); // invocationId / serve targetId -> [log lines]\n const targetEls = new Map(); // targetId -> left-pane element\n const serveMeta = new Map(); // serve targetId -> { dot, btnSlot } row controls\n const serveState = new Map(); // serve targetId -> { status, endpoints }\n let active = null; // { id, kind, ta, btn, msg, result }\n let shownInvId = null; // invocation whose result is in the workspace\n let shownServeId = null; // serve target whose workspace is shown\n\n function el(tag, cls, text) {\n const e = document.createElement(tag);\n if (cls) e.className = cls;\n if (text != null) e.textContent = text;\n return e;\n }\n\n async function loadTargets() {\n const pane = document.getElementById('targets');\n try {\n const res = await fetch('/api/targets');\n const data = await res.json();\n pane.querySelectorAll('.group-title,.target,.empty').forEach((n) => n.remove());\n let total = 0;\n for (const group of data.groups) {\n if (!group.entries.length) continue;\n pane.appendChild(el('div', 'group-title', group.title));\n for (const entry of group.entries) {\n total += 1;\n // Lambda targets are single-shot invokes; API targets are\n // long-running serves (slice C1). Other kinds list but are not\n // yet runnable.\n const runnable = group.kind === 'lambda' || group.kind === 'api';\n const t = el('div', runnable ? 'target runnable' : 'target');\n const name = el('span', 'name', entry.id);\n name.title = entry.id; // full path on hover even when truncated\n t.appendChild(name);\n t.appendChild(el('span', 'kind', '(' + (KIND_LABEL[group.kind] || group.kind) + ')'));\n if (group.kind === 'lambda') {\n const btn = el('button', 'invoke-btn', 'Invoke');\n btn.onclick = (e) => { e.stopPropagation(); selectTarget(entry.id, 'lambda'); };\n t.appendChild(btn);\n t.onclick = () => selectTarget(entry.id, 'lambda');\n targetEls.set(entry.id, t);\n } else if (group.kind === 'api') {\n // A serve target: a running-state dot + a Start/Stop button\n // slot, both refreshed by updateServeRow on serve events.\n const dot = el('span', 'run-dot');\n const btnSlot = el('span', 'btn-slot');\n t.appendChild(dot);\n t.appendChild(btnSlot);\n t.onclick = () => selectTarget(entry.id, 'api');\n targetEls.set(entry.id, t);\n serveMeta.set(entry.id, { dot, btnSlot });\n updateServeRow(entry.id);\n }\n pane.appendChild(t);\n }\n }\n if (!total) pane.appendChild(el('div', 'empty', 'No runnable targets found.'));\n } catch (err) {\n pane.appendChild(el('div', 'empty', 'Failed to load targets: ' + err));\n }\n }\n\n // Pull any already-running serves (e.g. after a UI reload) so the rows\n // and workspace reflect them without waiting for a fresh serve event.\n async function loadRunning() {\n try {\n const res = await fetch('/api/running');\n const data = await res.json();\n for (const s of (data.running || [])) {\n serveState.set(s.targetId, { status: s.status, endpoints: s.endpoints || [] });\n updateServeRow(s.targetId);\n }\n } catch (err) {\n /* best-effort; the serve SSE stream still drives live updates */\n }\n }\n\n function firstPort(endpoints) {\n const u = (endpoints || [])[0];\n if (!u) return '';\n const m = /:(\\\\d+)/.exec(u);\n return m ? ':' + m[1] : '';\n }\n\n function updateServeRow(id) {\n const meta = serveMeta.get(id);\n if (!meta) return;\n const st = serveState.get(id);\n const status = st ? st.status : 'stopped';\n const running = status === 'running';\n const starting = status === 'starting';\n meta.dot.textContent = running ? '● ' + firstPort(st.endpoints) : starting ? '○ starting' : '';\n meta.dot.className = 'run-dot' + (starting ? ' starting' : '');\n meta.btnSlot.innerHTML = '';\n const btn = running || starting\n ? el('button', 'stop-btn', 'Stop')\n : el('button', 'invoke-btn', 'Start');\n btn.onclick = (e) => {\n e.stopPropagation();\n if (running || starting) stopServe(id); else startServe(id);\n };\n meta.btnSlot.appendChild(btn);\n // Refresh the workspace if it is showing this serve.\n if (shownServeId === id) renderServeWorkspace(id);\n }\n\n function highlightTarget(id) {\n document.querySelectorAll('.target.sel').forEach((n) => n.classList.remove('sel'));\n const t = targetEls.get(id);\n if (t) t.classList.add('sel');\n }\n\n function selectTarget(id, kind) {\n highlightTarget(id);\n if (kind === 'api') {\n shownServeId = id;\n shownInvId = null;\n active = null;\n renderServeWorkspace(id);\n } else {\n shownServeId = null;\n renderComposer(id, kind, '{}');\n }\n }\n\n async function startServe(id) {\n serveState.set(id, { status: 'starting', endpoints: [] });\n updateServeRow(id);\n try {\n const res = await fetch('/api/run', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ targetId: id, kind: 'api' }),\n });\n const data = await res.json();\n if (!res.ok) {\n // Roll back the optimistic 'starting' on a rejected start.\n serveState.set(id, { status: 'error', endpoints: [] });\n updateServeRow(id);\n if (shownServeId === id) renderServeWorkspace(id, data.error || ('HTTP ' + res.status));\n }\n // On success the serve SSE 'running' event fills in the endpoints.\n } catch (err) {\n serveState.set(id, { status: 'error', endpoints: [] });\n updateServeRow(id);\n if (shownServeId === id) renderServeWorkspace(id, String(err));\n }\n }\n\n async function stopServe(id) {\n try {\n await fetch('/api/stop', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ targetId: id }),\n });\n // The serve SSE 'stopped' event clears the running state.\n } catch (err) {\n /* the stop SSE event (or a later refresh) reconciles state */\n }\n }\n\n function renderServeWorkspace(id, errMsg) {\n const ws = document.getElementById('workspace');\n ws.innerHTML = '';\n const st = serveState.get(id) || { status: 'stopped', endpoints: [] };\n const running = st.status === 'running';\n const starting = st.status === 'starting';\n\n const head = el('div', 'composer');\n head.appendChild(el('div', 'target-name', 'Serve ' + id));\n const btn = running || starting\n ? el('button', null, 'Stop')\n : el('button', null, starting ? 'Starting…' : 'Start');\n btn.onclick = () => { if (running || starting) stopServe(id); else startServe(id); };\n head.appendChild(btn);\n if (errMsg) {\n const m = el('div', 'err', errMsg);\n head.appendChild(m);\n }\n ws.appendChild(head);\n\n const epSec = el('div', 'section');\n epSec.appendChild(el('h3', null, 'Endpoints'));\n if (running && st.endpoints.length) {\n for (const url of st.endpoints) {\n const link = href(url);\n epSec.appendChild(link);\n }\n } else {\n epSec.appendChild(el('pre', null, starting ? '(starting…)' : '(not running)'));\n }\n ws.appendChild(epSec);\n\n const logs = logsById.get(id) || [];\n const logSec = el('div', 'section');\n logSec.appendChild(el('h3', null, 'Logs'));\n logSec.appendChild(el('pre', null, logs.length ? logs.join('\\\\n') : '(none)'));\n ws.appendChild(logSec);\n }\n\n // Build an <a> that opens an http(s) endpoint in a new tab; ws:// URLs\n // are shown as plain text (not navigable in a browser tab).\n function href(url) {\n if (/^https?:/.test(url)) {\n const a = el('a', 'endpoint', url);\n a.href = url;\n a.target = '_blank';\n a.rel = 'noopener';\n return a;\n }\n return el('div', 'endpoint', url);\n }\n\n function renderComposer(id, kind, eventText) {\n const ws = document.getElementById('workspace');\n ws.innerHTML = '';\n\n const composer = el('div', 'composer');\n composer.appendChild(el('div', 'target-name', 'Invoke ' + id));\n const ta = el('textarea');\n ta.value = eventText;\n ta.spellcheck = false;\n composer.appendChild(ta);\n composer.appendChild(document.createElement('br'));\n const btn = el('button', null, 'Invoke');\n const msg = el('div', 'err');\n composer.appendChild(btn);\n composer.appendChild(msg);\n\n const result = el('div', 'result');\n\n ws.appendChild(composer);\n ws.appendChild(result);\n\n active = { id, kind, ta, btn, msg, result };\n btn.onclick = () => runInvoke();\n shownInvId = null;\n ta.focus();\n }\n\n async function runInvoke() {\n if (!active) return;\n const { id, kind, ta, btn, msg, result } = active;\n let event;\n try {\n event = ta.value.trim() === '' ? {} : JSON.parse(ta.value);\n } catch (err) {\n msg.textContent = 'Invalid JSON: ' + err.message;\n return;\n }\n msg.textContent = '';\n btn.disabled = true;\n btn.textContent = 'Invoking...';\n result.innerHTML = '';\n try {\n const res = await fetch('/api/run', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ targetId: id, kind, event }),\n });\n const data = await res.json();\n if (data.invocationId) {\n shownInvId = data.invocationId;\n renderResult(shownInvId);\n }\n if (!res.ok || data.ok === false) {\n msg.textContent = 'Invoke failed: ' + (data.error || ('HTTP ' + res.status));\n }\n } catch (err) {\n msg.textContent = 'Request failed: ' + err;\n } finally {\n btn.disabled = false;\n btn.textContent = 'Invoke';\n }\n }\n\n function fmt(body) {\n return typeof body === 'string' ? body : JSON.stringify(body, null, 2);\n }\n\n function renderResult(invId) {\n if (!active) return;\n const result = active.result;\n result.innerHTML = '';\n const ev = invById.get(invId);\n if (!ev) return;\n\n const reqSec = el('div', 'section');\n reqSec.appendChild(el('h3', null, 'Request'));\n reqSec.appendChild(el('pre', null, ev.request != null ? fmt(ev.request) : '(none)'));\n result.appendChild(reqSec);\n\n const respSec = el('div', 'section');\n const h = el('h3', null, 'Response');\n if (ev.status != null) {\n const cls = ev.status >= 200 && ev.status < 300 ? 'ok' : 'bad';\n const meta = el('span', cls, ' ' + ev.status + (ev.durationMs != null ? ' · ' + ev.durationMs + 'ms' : ''));\n h.appendChild(meta);\n }\n respSec.appendChild(h);\n respSec.appendChild(el('pre', null, ev.response != null ? fmt(ev.response) : '(pending…)'));\n result.appendChild(respSec);\n\n const logs = logsById.get(invId) || [];\n const logSec = el('div', 'section');\n logSec.appendChild(el('h3', null, 'Logs'));\n logSec.appendChild(el('pre', null, logs.length ? logs.join('\\\\n') : '(none)'));\n result.appendChild(logSec);\n }\n\n function addInvocation(ev) {\n invById.set(ev.id, Object.assign(invById.get(ev.id) || {}, ev));\n const timeline = document.getElementById('timeline');\n const placeholder = timeline.querySelector('.empty');\n if (placeholder) placeholder.remove();\n\n let row = rowsById.get(ev.id);\n if (!row) {\n row = el('div', 'row');\n row.appendChild(el('span', 'ts'));\n row.appendChild(el('span', 'label'));\n row.appendChild(el('span', 'status'));\n row.onclick = () => loadInvocation(ev.id);\n rowsById.set(ev.id, row);\n timeline.insertBefore(row, timeline.querySelector('.row')); // newest on top\n }\n const merged = invById.get(ev.id);\n const d = new Date(merged.ts);\n row.querySelector('.ts').textContent = d.toLocaleTimeString();\n row.querySelector('.label').textContent = (merged.target || '') + ' ' + (merged.label || '');\n const statusEl = row.querySelector('.status');\n statusEl.textContent =\n merged.status != null\n ? merged.status + (merged.durationMs != null ? ' ' + merged.durationMs + 'ms' : '')\n : '…';\n statusEl.className = 'status' + (merged.status != null && (merged.status < 200 || merged.status >= 300) ? ' err' : '');\n\n // Live-refresh the workspace result if it is showing this invocation.\n if (shownInvId === ev.id) renderResult(ev.id);\n }\n\n function loadInvocation(id) {\n const ev = invById.get(id);\n if (!ev) return;\n document.querySelectorAll('.row.sel').forEach((n) => n.classList.remove('sel'));\n const row = rowsById.get(id);\n if (row) row.classList.add('sel');\n highlightTarget(ev.target);\n renderComposer(ev.target, ev.kind, ev.request != null ? fmt(ev.request) : '{}');\n shownInvId = id;\n renderResult(id);\n }\n\n function connect() {\n const conn = document.getElementById('conn');\n const es = new EventSource('/api/events');\n es.addEventListener('open', () => { conn.textContent = '● live'; conn.className = 'up'; });\n es.addEventListener('error', () => { conn.textContent = '● disconnected'; conn.className = 'down'; });\n es.addEventListener('invocation', (e) => addInvocation(JSON.parse(e.data)));\n es.addEventListener('serve', (e) => onServeEvent(JSON.parse(e.data)));\n es.addEventListener('log', (e) => {\n const ev = JSON.parse(e.data);\n const arr = logsById.get(ev.containerId) || [];\n arr.push(ev.line);\n logsById.set(ev.containerId, arr);\n if (shownInvId === ev.containerId) renderResult(ev.containerId);\n if (shownServeId === ev.containerId) renderServeWorkspace(ev.containerId);\n });\n }\n\n function onServeEvent(ev) {\n // A 'stopped' / 'error' transition clears the running state; otherwise\n // record the latest status + endpoints for the row + workspace.\n if (ev.status === 'stopped' || ev.status === 'error') {\n serveState.set(ev.target, { status: ev.status, endpoints: [] });\n } else {\n serveState.set(ev.target, { status: ev.status, endpoints: ev.endpoints || [] });\n }\n updateServeRow(ev.target);\n }\n\n function initSplitters() {\n const main = document.querySelector('main');\n let left = 280, right = 320;\n const apply = () => {\n main.style.gridTemplateColumns = left + 'px 5px 1fr 5px ' + right + 'px';\n };\n const wire = (splitterId, onMove) => {\n const s = document.getElementById(splitterId);\n s.addEventListener('mousedown', (e) => {\n e.preventDefault();\n const startX = e.clientX;\n const l0 = left, r0 = right;\n s.classList.add('dragging');\n document.body.style.userSelect = 'none';\n const move = (ev) => { onMove(ev.clientX - startX, l0, r0); apply(); };\n const up = () => {\n s.classList.remove('dragging');\n document.body.style.userSelect = '';\n document.removeEventListener('mousemove', move);\n document.removeEventListener('mouseup', up);\n };\n document.addEventListener('mousemove', move);\n document.addEventListener('mouseup', up);\n });\n };\n const clamp = (v) => Math.max(160, Math.min(760, v));\n wire('split-left', (dx, l0) => { left = clamp(l0 + dx); });\n wire('split-right', (dx, l0, r0) => { right = clamp(r0 - dx); });\n }\n\n loadTargets().then(loadRunning);\n connect();\n initSplitters();\n`;\n\n/**\n * Render the full studio HTML document. `appLabel` is shown in the\n * header (the CDK app / stack context); `cliName` brands the title for\n * host CLIs that rebrand `cdkl`.\n */\nexport function renderStudioHtml(appLabel: string, cliName: string): string {\n const safeApp = escapeHtml(appLabel);\n const safeCli = escapeHtml(cliName);\n return `<!doctype html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\" />\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n<title>${safeCli} studio</title>\n<style>${STUDIO_CSS}</style>\n</head>\n<body>\n<header>\n <span class=\"brand\">${safeCli} studio</span>\n <span class=\"meta\">${safeApp}</span>\n <span id=\"conn\" class=\"down\">● connecting</span>\n</header>\n<main>\n <section class=\"pane\" id=\"targets\"><h2>Targets</h2></section>\n <div class=\"splitter\" id=\"split-left\"></div>\n <section class=\"pane\" id=\"workspace\"><div class=\"empty\">Pick a Lambda to invoke, or an API to serve, on the left.</div></section>\n <div class=\"splitter\" id=\"split-right\"></div>\n <section class=\"pane\" id=\"timeline\"><h2>Timeline</h2><div class=\"empty\">No requests yet.</div></section>\n</main>\n<script>${STUDIO_SCRIPT}</script>\n</body>\n</html>`;\n}\n\n/** Minimal HTML-escape for the few interpolated text values. */\nfunction escapeHtml(s: string): string {\n return s\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;');\n}\n","import { createServer, type IncomingMessage, type Server, type ServerResponse } from 'node:http';\nimport type { AddressInfo } from 'node:net';\nimport {\n StudioEventBus,\n type StudioInvocationEvent,\n type StudioLogEvent,\n type StudioServeEvent,\n} from './studio-events.js';\nimport { renderStudioHtml } from './studio-ui.js';\nimport type { TargetListing } from './target-lister.js';\n\n/** One target as the studio UI consumes it (`GET /api/targets`). */\nexport interface StudioTarget {\n /** Stable target id — the display path when available, else qualified id. */\n id: string;\n /** Stack-qualified `<Stack>:<LogicalId>` for disambiguation. */\n qualifiedId: string;\n /** API surface kind (REST v1 / HTTP v2 / ...), only for `api` entries. */\n surface?: string;\n}\n\n/** A category of targets, grouped by the studio kind that runs them. */\nexport interface StudioTargetGroup {\n /** Studio kind discriminator shared with {@link StudioInvocationEvent}. */\n kind: 'lambda' | 'api' | 'alb' | 'ecs' | 'agentcore';\n /** Human-readable group heading. */\n title: string;\n entries: StudioTarget[];\n}\n\n/**\n * Project a {@link TargetListing} (the same enumeration `cdkl list`\n * prints) into the grouped shape the studio UI renders. ECS services and\n * task definitions are folded into one `ecs` group; everything else maps\n * one category to one group. Exported so a unit test can assert the\n * projection without booting the server.\n */\nexport function toStudioTargetGroups(listing: TargetListing): StudioTargetGroup[] {\n const map = (entries: TargetListing['lambdas']): StudioTarget[] =>\n entries.map((e) => {\n const t: StudioTarget = { id: e.displayPath ?? e.qualifiedId, qualifiedId: e.qualifiedId };\n if (e.kind) t.surface = e.kind;\n return t;\n });\n return [\n { kind: 'lambda', title: 'Lambda Functions', entries: map(listing.lambdas) },\n { kind: 'api', title: 'APIs', entries: map(listing.apis) },\n {\n kind: 'ecs',\n title: 'ECS Services / Tasks',\n entries: [...map(listing.ecsServices), ...map(listing.ecsTaskDefinitions)],\n },\n { kind: 'agentcore', title: 'AgentCore Runtimes', entries: map(listing.agentCoreRuntimes) },\n { kind: 'alb', title: 'Load Balancers', entries: map(listing.loadBalancers) },\n ];\n}\n\n/** Inputs to {@link startStudioServer}. */\nexport interface StudioServerOptions {\n /** Preferred listen port; bumps on collision (decision: collision-safe). */\n port: number;\n /** Listen host. Defaults to `127.0.0.1` (localhost-only). */\n host?: string;\n /** The shared event bus the SSE stream forwards. */\n bus: StudioEventBus;\n /** Target groups to serve at `GET /api/targets`. */\n targetGroups: StudioTargetGroup[];\n /** Header label for the running app / stack context. */\n appLabel: string;\n /** CLI brand name (`cdkl`, or a host rebrand). */\n cliName: string;\n /**\n * Max consecutive ports to try on `EADDRINUSE` before giving up.\n * Defaults to 20.\n */\n maxPortBump?: number;\n /**\n * Handler for `POST /api/run` — runs a target (single-shot Lambda\n * invoke, or start a long-running serve target) and returns the\n * result. When omitted, `/api/run` answers 501 (the observe-only\n * shell). The body is the parsed JSON request; the handler emits its\n * own invocation / serve / log events onto the bus, so the UI's\n * timeline + running state update over SSE independently of the\n * response.\n */\n onRun?: (body: unknown) => Promise<unknown>;\n /**\n * Handler for `POST /api/stop` — stop a running serve target. When\n * omitted, `/api/stop` answers 501. The body identifies the target.\n */\n onStop?: (body: unknown) => Promise<unknown>;\n /**\n * Snapshot of the currently-running serve targets, served at\n * `GET /api/running`. When omitted, the endpoint returns an empty\n * list (the observe-only shell never runs anything).\n */\n getRunning?: () => unknown;\n}\n\n/** A running studio server. */\nexport interface RunningStudioServer {\n /** The URL the UI is served at, e.g. `http://127.0.0.1:9999`. */\n url: string;\n /** The actually-bound port (may differ from the requested one). */\n port: number;\n /** Stop the server and release the port. */\n close: () => Promise<void>;\n}\n\nconst SSE_HEARTBEAT_MS = 15_000;\n\n/**\n * Boot the studio HTTP server: serves the embedded UI at `/`, the target\n * list at `/api/targets`, and a Server-Sent-Events stream of the bus's\n * `invocation` / `log` events at `/api/events`. Localhost-only by\n * default. Resolves once the socket is listening.\n */\nexport async function startStudioServer(\n options: StudioServerOptions\n): Promise<RunningStudioServer> {\n const host = options.host ?? '127.0.0.1';\n const maxBump = options.maxPortBump ?? 20;\n const html = renderStudioHtml(options.appLabel, options.cliName);\n const targetsJson = JSON.stringify({ groups: options.targetGroups });\n\n const server = createServer((req, res) =>\n handleRequest(req, res, options.bus, html, targetsJson, options)\n );\n\n const boundPort = await listenWithBump(server, host, options.port, maxBump);\n\n return {\n url: `http://${host}:${boundPort}`,\n port: boundPort,\n close: () =>\n new Promise<void>((resolveClose, reject) => {\n server.close((err) => (err ? reject(err) : resolveClose()));\n // closeAllConnections exists on Node 18.2+; SSE keeps sockets open\n // so without this `close()` would hang on live EventSource clients.\n server.closeAllConnections?.();\n }),\n };\n}\n\nfunction handleRequest(\n req: IncomingMessage,\n res: ServerResponse,\n bus: StudioEventBus,\n html: string,\n targetsJson: string,\n options: StudioServerOptions\n): void {\n const url = req.url ?? '/';\n const path = url.split('?')[0];\n\n if (req.method === 'GET' && (path === '/' || path === '/index.html')) {\n res.writeHead(200, { 'content-type': 'text/html; charset=utf-8' });\n res.end(html);\n return;\n }\n if (req.method === 'GET' && path === '/api/targets') {\n res.writeHead(200, { 'content-type': 'application/json; charset=utf-8' });\n res.end(targetsJson);\n return;\n }\n if (req.method === 'GET' && path === '/api/running') {\n const running = options.getRunning ? options.getRunning() : { running: [] };\n res.writeHead(200, { 'content-type': 'application/json; charset=utf-8' });\n res.end(JSON.stringify(running));\n return;\n }\n if (req.method === 'GET' && path === '/api/events') {\n serveSse(req, res, bus);\n return;\n }\n if (req.method === 'POST' && path === '/api/run') {\n void handleDispatch(req, res, options.onRun);\n return;\n }\n if (req.method === 'POST' && path === '/api/stop') {\n void handleDispatch(req, res, options.onStop);\n return;\n }\n res.writeHead(404, { 'content-type': 'text/plain; charset=utf-8' });\n res.end('Not found');\n}\n\nconst MAX_RUN_BODY_BYTES = 5 * 1024 * 1024;\n\n/**\n * Reply to a JSON POST endpoint (`/api/run` / `/api/stop`): parse the\n * bounded JSON body and dispatch via `handler`. 501 when no handler is\n * wired (the observe-only shell), 400 on a malformed body, 500 when the\n * handler throws.\n */\nasync function handleDispatch(\n req: IncomingMessage,\n res: ServerResponse,\n handler?: (body: unknown) => Promise<unknown>\n): Promise<void> {\n const sendJson = (statusCode: number, payload: unknown): void => {\n res.writeHead(statusCode, { 'content-type': 'application/json; charset=utf-8' });\n res.end(JSON.stringify(payload));\n };\n\n if (!handler) {\n // The observe-only shell (no dispatcher wired) cannot run targets.\n sendJson(501, { error: 'Running targets is not supported by this studio server.' });\n return;\n }\n\n let body: unknown;\n try {\n body = await readJsonBody(req);\n } catch (err) {\n sendJson(400, { error: err instanceof Error ? err.message : String(err) });\n return;\n }\n\n try {\n const result = await handler(body);\n sendJson(200, result);\n } catch (err) {\n sendJson(500, { error: err instanceof Error ? err.message : String(err) });\n }\n}\n\n/** Read + JSON-parse a request body, bounded to {@link MAX_RUN_BODY_BYTES}. */\nfunction readJsonBody(req: IncomingMessage): Promise<unknown> {\n return new Promise<unknown>((resolveBody, reject) => {\n let raw = '';\n let bytes = 0;\n // Single-flight: once the promise settles (over-limit / end / error) the\n // handlers short-circuit so a late chunk or the destroy-triggered `error`\n // cannot re-settle it.\n let done = false;\n const settle = (fn: () => void): void => {\n if (done) return;\n done = true;\n fn();\n };\n req.setEncoding('utf8');\n req.on('data', (chunk: string) => {\n if (done) return;\n bytes += Buffer.byteLength(chunk);\n if (bytes > MAX_RUN_BODY_BYTES) {\n settle(() => reject(new Error('Request body too large.')));\n req.destroy();\n return;\n }\n raw += chunk;\n });\n req.on('end', () => {\n settle(() => {\n if (raw.trim() === '') {\n resolveBody(undefined);\n return;\n }\n try {\n resolveBody(JSON.parse(raw));\n } catch {\n reject(new Error('Invalid JSON body.'));\n }\n });\n });\n req.on('error', (err) => settle(() => reject(err)));\n });\n}\n\nfunction serveSse(req: IncomingMessage, res: ServerResponse, bus: StudioEventBus): void {\n res.writeHead(200, {\n 'content-type': 'text/event-stream; charset=utf-8',\n 'cache-control': 'no-cache, no-transform',\n connection: 'keep-alive',\n });\n\n let closed = false;\n const heartbeat = setInterval(() => safeWrite(':hb\\n\\n'), SSE_HEARTBEAT_MS);\n heartbeat.unref?.();\n\n const onInvocation = (ev: StudioInvocationEvent): void => {\n safeWrite(`event: invocation\\ndata: ${JSON.stringify(ev)}\\n\\n`);\n };\n const onLog = (ev: StudioLogEvent): void => {\n safeWrite(`event: log\\ndata: ${JSON.stringify(ev)}\\n\\n`);\n };\n const onServe = (ev: StudioServeEvent): void => {\n safeWrite(`event: serve\\ndata: ${JSON.stringify(ev)}\\n\\n`);\n };\n\n // Idempotent teardown: unsubscribe from the bus + stop the heartbeat so\n // a dropped EventSource client never leaks a listener.\n function cleanup(): void {\n if (closed) return;\n closed = true;\n clearInterval(heartbeat);\n bus.off('invocation', onInvocation);\n bus.off('log', onLog);\n bus.off('serve', onServe);\n }\n\n // Guard every write: on shutdown `close()` destroys live SSE sockets\n // (closeAllConnections), and a bus emit / heartbeat tick can race that\n // destroy. Writing to a destroyed socket emits an unhandled `error` on\n // the response — which, with no top-level uncaughtException handler on\n // the studio path, would crash the process on Ctrl-C. Drop the write\n // (and tear down) instead.\n function safeWrite(chunk: string): void {\n if (closed || res.writableEnded || res.destroyed) {\n cleanup();\n return;\n }\n res.write(chunk);\n }\n\n bus.on('invocation', onInvocation);\n bus.on('log', onLog);\n bus.on('serve', onServe);\n\n // `close` fires on client disconnect AND on server-side socket destroy;\n // `error` fires if a write loses the race with the destroy. All three\n // route through the idempotent cleanup so the bus listeners + heartbeat\n // never leak and a write error never propagates as uncaught.\n req.on('close', cleanup);\n res.on('close', cleanup);\n res.on('error', cleanup);\n\n // Open the stream so EventSource fires `open` immediately.\n safeWrite(':ok\\n\\n');\n}\n\n/**\n * Listen on `port`, retrying `port+1`, `port+2`, ... on `EADDRINUSE` up\n * to `maxBump` extra attempts. Resolves with the bound port.\n */\nfunction listenWithBump(\n server: Server,\n host: string,\n port: number,\n maxBump: number\n): Promise<number> {\n return new Promise<number>((resolveListen, reject) => {\n let attempt = 0;\n const tryListen = (p: number): void => {\n const onError = (err: NodeJS.ErrnoException): void => {\n if (err.code === 'EADDRINUSE' && attempt < maxBump) {\n attempt += 1;\n server.removeListener('error', onError);\n tryListen(p + 1);\n return;\n }\n reject(err);\n };\n server.once('error', onError);\n server.listen(p, host, () => {\n server.removeListener('error', onError);\n // Resolve with the ACTUAL bound port from the socket — when the\n // requested port is 0 the OS assigns a free one, which `p` does\n // not reflect.\n resolveListen((server.address() as AddressInfo).port);\n });\n };\n tryListen(port);\n });\n}\n","import { spawn as nodeSpawn, type ChildProcessWithoutNullStreams } from 'node:child_process';\nimport { mkdtempSync, rmSync, writeFileSync } from 'node:fs';\nimport { tmpdir } from 'node:os';\nimport { join } from 'node:path';\nimport { StudioEventBus, type StudioTargetKind } from './studio-events.js';\n\n/** A request to run a target, as the studio UI posts it to `/api/run`. */\nexport interface StudioRunRequest {\n /** Target id the user picked (display path or stack-qualified id). */\n targetId: string;\n /** Target kind. Slice B handles `'lambda'` (single-shot invoke) only. */\n kind: StudioTargetKind;\n /** The event payload to invoke with. */\n event: unknown;\n}\n\n/** The outcome of a single-shot run, returned from `/api/run`. */\nexport interface StudioRunResult {\n /** Correlation id shared with the emitted invocation events. */\n invocationId: string;\n /** Whether the underlying `cdkl invoke` exited 0. */\n ok: boolean;\n /**\n * Synthetic status for the timeline: 200 when the invoke succeeded,\n * 500 when it failed. A direct Lambda invoke has no HTTP status; this\n * is the UI's success/failure signal only.\n */\n status: number;\n /** Parsed Lambda response (JSON when parseable, else the raw string). */\n response?: unknown;\n /** Raw stdout payload from `cdkl invoke`. */\n raw?: string;\n /** Error summary when the invoke failed. */\n error?: string;\n /** Wall-clock duration in ms. */\n durationMs: number;\n}\n\n/** Config for {@link createStudioDispatcher}. */\nexport interface StudioDispatchConfig {\n /** Path to the `cdkl` CLI entry (`dist/cli.js`) — usually `process.argv[1]`. */\n cliEntry: string;\n /** The shared event bus; invocation + log events are emitted onto it. */\n bus: StudioEventBus;\n /** Working directory for the child invoke (defaults to `process.cwd()`). */\n cwd?: string;\n /** `--app` value to thread into the child invoke, if studio was given one. */\n app?: string;\n /** `-c key=value` context overrides to thread through. */\n context?: Record<string, string>;\n /** `--profile` to thread through. */\n profile?: string;\n /** `--region` to thread through. */\n region?: string;\n /** Node binary to spawn (defaults to `process.execPath`; injectable for tests). */\n nodeBin?: string;\n /** Spawn implementation (injectable for tests). */\n spawnFn?: typeof nodeSpawn;\n /** Clock (injectable for tests; defaults to `Date.now`). */\n clock?: () => number;\n /** Invocation-id factory (injectable for tests). */\n idFactory?: () => string;\n}\n\n/** A bound dispatcher exposing the `/api/run` handler. */\nexport interface StudioDispatcher {\n run: (req: StudioRunRequest) => Promise<StudioRunResult>;\n}\n\nlet idCounter = 0;\n\n/**\n * Build the studio run dispatcher. Slice B drives a single-shot Lambda\n * invoke by spawning the SAME `cdkl invoke` the headless command runs —\n * studio is a control plane over the CLI, so re-using the whole command\n * (rather than re-wiring its internals) guarantees byte-for-byte parity\n * and keeps all of `cdkl invoke`'s process-global behavior\n * (`process.exit`, env mutation, stdin) isolated in a child process.\n *\n * The child's stdout is the Lambda response payload; its stderr (status\n * + container logs) is streamed line-by-line onto the bus as `log`\n * events. An `invocation` start event is emitted before spawn and an end\n * event (with response + status + duration) after exit, both keyed by\n * the same correlation id so the UI threads them into one timeline row.\n */\nexport function createStudioDispatcher(config: StudioDispatchConfig): StudioDispatcher {\n const spawnFn = config.spawnFn ?? nodeSpawn;\n const nodeBin = config.nodeBin ?? process.execPath;\n const clock = config.clock ?? Date.now;\n const idFactory =\n config.idFactory ??\n (() => {\n idCounter += 1;\n return `inv-${clock()}-${idCounter}`;\n });\n\n async function run(req: StudioRunRequest): Promise<StudioRunResult> {\n const invocationId = idFactory();\n const startedAt = clock();\n\n if (req.kind !== 'lambda') {\n // Serve targets (API / ALB / ECS) arrive in slice C; reject clearly\n // rather than silently mis-dispatching.\n const error = `Running '${req.kind}' targets from studio is not supported yet (Lambda only).`;\n config.bus.emit('invocation', {\n id: invocationId,\n ts: startedAt,\n target: req.targetId,\n kind: req.kind,\n label: 'invoke',\n request: req.event,\n response: error,\n status: 501,\n durationMs: clock() - startedAt,\n });\n return { invocationId, ok: false, status: 501, error, durationMs: clock() - startedAt };\n }\n\n config.bus.emit('invocation', {\n id: invocationId,\n ts: startedAt,\n target: req.targetId,\n kind: req.kind,\n label: 'invoke',\n request: req.event,\n });\n\n // `dir` is created inside the try so a `writeFileSync` / `JSON.stringify`\n // throw still hits the finally that cleans it up.\n let dir: string | undefined;\n try {\n dir = mkdtempSync(join(tmpdir(), 'cdkl-studio-run-'));\n const eventFile = join(dir, 'event.json');\n writeFileSync(eventFile, JSON.stringify(req.event ?? {}));\n\n const args = ['invoke', req.targetId, '--event', eventFile];\n if (config.app) args.push('--app', config.app);\n if (config.profile) args.push('--profile', config.profile);\n if (config.region) args.push('--region', config.region);\n for (const [k, v] of Object.entries(config.context ?? {})) {\n args.push('-c', `${k}=${v}`);\n }\n\n const { code, stdout, stderr } = await runChild(\n spawnFn,\n nodeBin,\n [config.cliEntry, ...args],\n config.cwd ?? process.cwd(),\n invocationId,\n req.targetId,\n config.bus,\n clock\n );\n\n const durationMs = clock() - startedAt;\n const ok = code === 0;\n const failure = stderr.trim() || `cdkl invoke exited ${code}`;\n\n // `cdkl invoke` interleaves synth progress + streamed container logs on\n // stdout and writes the Lambda response there too. The RIE response is\n // always a single line of valid JSON (the serialized handler return\n // value or error object), whereas progress / container lines\n // (`Synthesizing…`, `Starting container…`, `START/END/REPORT`) are NOT\n // JSON — so the response is the LAST JSON-parseable stdout line, which is\n // robust against a container log line flushing AFTER the response. Every\n // other stdout line is surfaced as a log event. Falls back to the last\n // line when nothing parses (an unusual non-JSON / empty response).\n const stdoutLines = stdout\n .split('\\n')\n .map((l) => l.replace(/\\r$/, '').trimEnd())\n .filter((l) => l.trim().length > 0);\n\n let responseIdx = -1;\n let response: unknown;\n if (ok) {\n for (let i = stdoutLines.length - 1; i >= 0; i -= 1) {\n const parsed = tryParseJson(stdoutLines[i] ?? '');\n if (parsed.ok) {\n responseIdx = i;\n response = parsed.value;\n break;\n }\n }\n if (responseIdx === -1 && stdoutLines.length > 0) {\n responseIdx = stdoutLines.length - 1;\n response = stdoutLines[responseIdx];\n }\n } else {\n response = failure;\n }\n\n // Every stdout line that is not the response line is a log line.\n stdoutLines.forEach((line, i) => {\n if (i === responseIdx) return;\n config.bus.emit('log', {\n ts: clock(),\n containerId: invocationId,\n target: req.targetId,\n line,\n stream: 'stdout',\n });\n });\n\n const raw = responseIdx >= 0 ? (stdoutLines[responseIdx] ?? '') : '';\n const status = ok ? 200 : 500;\n\n config.bus.emit('invocation', {\n id: invocationId,\n ts: startedAt,\n target: req.targetId,\n kind: req.kind,\n label: 'invoke',\n request: req.event,\n response,\n status,\n durationMs,\n });\n\n const result: StudioRunResult = { invocationId, ok, status, durationMs };\n if (raw) result.raw = raw;\n if (response !== undefined) result.response = response;\n if (!ok) result.error = failure;\n return result;\n } finally {\n if (dir) rmSync(dir, { recursive: true, force: true });\n }\n }\n\n return { run };\n}\n\ninterface ChildOutcome {\n code: number | null;\n stdout: string;\n stderr: string;\n}\n\n/**\n * Spawn the child invoke, accumulate stdout, and stream stderr to the\n * bus line-by-line as `log` events. Resolves on process close.\n */\nfunction runChild(\n spawnFn: typeof nodeSpawn,\n nodeBin: string,\n argv: string[],\n cwd: string,\n invocationId: string,\n target: string,\n bus: StudioEventBus,\n clock: () => number\n): Promise<ChildOutcome> {\n return new Promise<ChildOutcome>((resolve, reject) => {\n let child: ChildProcessWithoutNullStreams;\n try {\n child = spawnFn(nodeBin, argv, { cwd });\n } catch (err) {\n reject(err instanceof Error ? err : new Error(String(err)));\n return;\n }\n\n let stdout = '';\n let stderr = '';\n let lineBuf = '';\n\n child.stdout.setEncoding('utf8');\n child.stderr.setEncoding('utf8');\n\n child.stdout.on('data', (chunk: string) => {\n stdout += chunk;\n });\n child.stderr.on('data', (chunk: string) => {\n stderr += chunk;\n lineBuf += chunk;\n let nl = lineBuf.indexOf('\\n');\n while (nl !== -1) {\n const line = lineBuf.slice(0, nl);\n lineBuf = lineBuf.slice(nl + 1);\n if (line.length > 0) {\n bus.emit('log', {\n ts: clock(),\n containerId: invocationId,\n target,\n line,\n stream: 'stderr',\n });\n }\n nl = lineBuf.indexOf('\\n');\n }\n });\n\n child.on('error', (err) => reject(err));\n child.on('close', (code) => {\n if (lineBuf.length > 0) {\n bus.emit('log', {\n ts: clock(),\n containerId: invocationId,\n target,\n line: lineBuf,\n stream: 'stderr',\n });\n }\n resolve({ code, stdout, stderr });\n });\n });\n}\n\n/** Try to JSON-parse `raw`; `ok` distinguishes a parsed value from a failure. */\nfunction tryParseJson(raw: string): { ok: boolean; value: unknown } {\n if (raw === '') return { ok: false, value: undefined };\n try {\n return { ok: true, value: JSON.parse(raw) };\n } catch {\n return { ok: false, value: undefined };\n }\n}\n","import { spawn as nodeSpawn, type ChildProcessWithoutNullStreams } from 'node:child_process';\nimport { StudioEventBus, type StudioServeEvent, type StudioTargetKind } from './studio-events.js';\n\n/** A request to start serving a target, as the studio UI posts it. */\nexport interface StudioServeRequest {\n /** Target id the user picked (display path or stack-qualified id). */\n targetId: string;\n /** Target kind. Slice C1 supports `'api'` (long-running `start-api`) only. */\n kind: StudioTargetKind;\n}\n\n/** A request to stop a running served target. */\nexport interface StudioStopRequest {\n /** Target id of the serve to stop. */\n targetId: string;\n}\n\n/** The public state of one running served target (returned to the UI). */\nexport interface StudioServeState {\n /** Target id of the served target. */\n targetId: string;\n /** The served target's kind. */\n kind: StudioTargetKind;\n /** Lifecycle status. */\n status: 'starting' | 'running' | 'stopped' | 'error';\n /** Served endpoint URLs, populated once running. */\n endpoints: string[];\n /** Child process id. */\n pid?: number;\n /** Wall-clock epoch ms when the serve started. */\n startedAt: number;\n}\n\n/** Config for {@link createStudioServeManager}. */\nexport interface StudioServeManagerConfig {\n /** Path to the `cdkl` CLI entry (`dist/cli.js`) — usually `process.argv[1]`. */\n cliEntry: string;\n /** The shared event bus; serve + log events are emitted onto it. */\n bus: StudioEventBus;\n /** Working directory for the child serve (defaults to `process.cwd()`). */\n cwd?: string;\n /** `--app` value to thread into the child serve, if studio was given one. */\n app?: string;\n /** `-c key=value` context overrides to thread through. */\n context?: Record<string, string>;\n /** `--profile` to thread through. */\n profile?: string;\n /** `--region` to thread through. */\n region?: string;\n /** Node binary to spawn (defaults to `process.execPath`; injectable for tests). */\n nodeBin?: string;\n /** Spawn implementation (injectable for tests). */\n spawnFn?: typeof nodeSpawn;\n /** Clock (injectable for tests; defaults to `Date.now`). */\n clock?: () => number;\n /**\n * Max ms to wait for the first `Server listening on ...` line before\n * giving up and killing the child. Generous by default to cover the\n * first-run base-image pull `start-api` triggers. Defaults to 120_000.\n */\n readyTimeoutMs?: number;\n /**\n * Ms to wait for a SIGTERM'd child to exit before sending SIGKILL.\n * Defaults to 10_000.\n */\n stopGraceMs?: number;\n /** `setTimeout` (injectable for tests). */\n setTimeoutFn?: typeof setTimeout;\n /** `clearTimeout` (injectable for tests). */\n clearTimeoutFn?: typeof clearTimeout;\n}\n\n/** A bound serve manager exposing the start / stop / list surface. */\nexport interface StudioServeManager {\n /** Start serving a target; resolves once it is listening (or rejects). */\n start: (req: StudioServeRequest) => Promise<StudioServeState>;\n /** Stop a running served target; resolves once the child has exited. */\n stop: (req: StudioStopRequest) => Promise<void>;\n /** Snapshot of every running served target. */\n list: () => StudioServeState[];\n /** Stop every running served target (graceful shutdown). */\n stopAll: () => Promise<void>;\n}\n\n/** Kinds the serve manager can start in this build. */\nconst SERVE_SUPPORTED: readonly StudioTargetKind[] = ['api'];\n\n/** `Server listening on <url>` is the stable ready marker `start-api` prints. */\nconst LISTENING_RE = /Server listening on (\\S+)/;\n\ninterface ServeEntry extends StudioServeState {\n child: ChildProcessWithoutNullStreams;\n /**\n * Set by `stop()` when it tears the child down WHILE it is still\n * `starting` (the ready promise has not settled). The child's `close`\n * handler reads it so a user-initiated stop is not misreported as a\n * boot failure (no `error` event, no `start()` reject as a crash).\n */\n stopping?: boolean;\n}\n\n/**\n * Build the studio serve manager. Slice C1 drives a long-running\n * `cdkl start-api <target>` child — studio is a control plane over the\n * CLI (the same pattern as the single-shot invoke dispatcher), so it\n * spawns the SAME headless serve command rather than re-wiring its\n * internals. This preserves byte-for-byte parity and isolates the\n * server's process-global behavior in a child.\n *\n * `start()` spawns the child with `--port 0` (OS-assigned, collision\n * free), streams its stdout/stderr onto the bus as `log` events keyed by\n * the target id, and resolves once the child prints its first\n * `Server listening on <url>` line — emitting a `serve` `running` event\n * with the discovered endpoints. `stop()` SIGTERMs the child (SIGKILL\n * after a grace window) and emits `stopped`.\n *\n * Request capture for traffic to the served port (decision D4a / D5) and\n * full-text log search arrive in slice C2; C1 is lifecycle + log\n * streaming only.\n */\nexport function createStudioServeManager(config: StudioServeManagerConfig): StudioServeManager {\n const spawnFn = config.spawnFn ?? nodeSpawn;\n const nodeBin = config.nodeBin ?? process.execPath;\n const clock = config.clock ?? Date.now;\n const readyTimeoutMs = config.readyTimeoutMs ?? 120_000;\n const stopGraceMs = config.stopGraceMs ?? 10_000;\n const setTimeoutFn = config.setTimeoutFn ?? setTimeout;\n const clearTimeoutFn = config.clearTimeoutFn ?? clearTimeout;\n const cwd = config.cwd ?? process.cwd();\n\n const entries = new Map<string, ServeEntry>();\n\n function publicState(e: ServeEntry): StudioServeState {\n const s: StudioServeState = {\n targetId: e.targetId,\n kind: e.kind,\n status: e.status,\n endpoints: [...e.endpoints],\n startedAt: e.startedAt,\n };\n if (e.pid !== undefined) s.pid = e.pid;\n return s;\n }\n\n function emitServe(e: ServeEntry, message?: string): void {\n const ev: StudioServeEvent = {\n ts: clock(),\n target: e.targetId,\n kind: e.kind,\n status: e.status,\n endpoints: [...e.endpoints],\n };\n if (e.pid !== undefined) ev.pid = e.pid;\n if (message !== undefined) ev.message = message;\n config.bus.emit('serve', ev);\n }\n\n function buildArgs(targetId: string): string[] {\n const args = ['start-api', targetId, '--port', '0', '--host', '127.0.0.1'];\n if (config.app) args.push('--app', config.app);\n if (config.profile) args.push('--profile', config.profile);\n if (config.region) args.push('--region', config.region);\n for (const [k, v] of Object.entries(config.context ?? {})) {\n args.push('-c', `${k}=${v}`);\n }\n return args;\n }\n\n async function start(req: StudioServeRequest): Promise<StudioServeState> {\n if (!SERVE_SUPPORTED.includes(req.kind)) {\n throw new Error(\n `Serving '${req.kind}' targets from studio is not supported yet (API only in this build).`\n );\n }\n const existing = entries.get(req.targetId);\n if (existing && existing.status !== 'stopped' && existing.status !== 'error') {\n throw new Error(`'${req.targetId}' is already running.`);\n }\n\n const startedAt = clock();\n let child: ChildProcessWithoutNullStreams;\n try {\n child = spawnFn(nodeBin, [config.cliEntry, ...buildArgs(req.targetId)], { cwd });\n } catch (err) {\n throw err instanceof Error ? err : new Error(String(err));\n }\n\n const entry: ServeEntry = {\n targetId: req.targetId,\n kind: req.kind,\n status: 'starting',\n endpoints: [],\n startedAt,\n child,\n };\n if (child.pid !== undefined) entry.pid = child.pid;\n entries.set(req.targetId, entry);\n emitServe(entry);\n\n return new Promise<StudioServeState>((resolve, reject) => {\n let settled = false;\n child.stdout.setEncoding('utf8');\n child.stderr.setEncoding('utf8');\n\n const timer = setTimeoutFn(() => {\n if (settled) return;\n settled = true;\n entry.status = 'error';\n emitServe(entry, `Timed out after ${readyTimeoutMs}ms waiting for the server to listen.`);\n // Graceful SIGTERM -> SIGKILL (NOT an immediate SIGKILL): `start-api`\n // traps SIGTERM to tear down its RIE containers, so a hard kill on a\n // half-booted child would orphan those containers.\n void stopChild(child, stopGraceMs, setTimeoutFn, clearTimeoutFn);\n entries.delete(req.targetId);\n reject(new Error(`'${req.targetId}' did not start within ${readyTimeoutMs}ms.`));\n }, readyTimeoutMs);\n timer.unref?.();\n\n const becomeRunning = (): void => {\n if (settled) return;\n settled = true;\n clearTimeoutFn(timer);\n entry.status = 'running';\n emitServe(entry);\n resolve(publicState(entry));\n };\n\n streamLines(child.stdout, (line) => {\n const m = LISTENING_RE.exec(line);\n if (m?.[1]) {\n if (!entry.endpoints.includes(m[1])) entry.endpoints.push(m[1]);\n // First listening line flips the serve to running; later lines\n // (multi-listener apps) append + re-emit so the UI's endpoint\n // list fills in.\n if (settled) emitServe(entry);\n else becomeRunning();\n }\n emitLog(config.bus, clock, req.targetId, line, 'stdout');\n });\n streamLines(child.stderr, (line) => {\n emitLog(config.bus, clock, req.targetId, line, 'stderr');\n });\n\n child.on('error', (err) => {\n if (settled) {\n // Post-ready spawn error: mark the entry errored for the UI.\n entry.status = 'error';\n emitServe(entry, err.message);\n entries.delete(req.targetId);\n return;\n }\n settled = true;\n clearTimeoutFn(timer);\n entry.status = 'error';\n emitServe(entry, err.message);\n entries.delete(req.targetId);\n reject(err);\n });\n\n child.on('close', (code) => {\n if (!settled) {\n settled = true;\n clearTimeoutFn(timer);\n if (entry.stopping) {\n // A user `stop()` raced the still-starting child: not a boot\n // failure. `stop()` emits the `stopped` event + resolves itself,\n // so here we only reject the pending `start()` (the /api/run POST)\n // with an honest \"cancelled\" message and emit nothing.\n reject(new Error(`'${req.targetId}' was stopped before it finished starting.`));\n return;\n }\n // Exited before ever listening — a boot failure.\n entry.status = 'error';\n const msg = `Server exited before listening (code ${code ?? 'null'}).`;\n emitServe(entry, msg);\n entries.delete(req.targetId);\n reject(new Error(msg));\n return;\n }\n // Exited while running (crash or our own stop()). If still tracked\n // as running, surface it as stopped; stop() removes the entry\n // itself before this fires in the graceful path.\n const tracked = entries.get(req.targetId);\n if (tracked === entry && entry.status === 'running') {\n entry.status = 'stopped';\n emitServe(entry, `Server process exited (code ${code ?? 'null'}).`);\n entries.delete(req.targetId);\n }\n });\n });\n }\n\n async function stop(req: StudioStopRequest): Promise<void> {\n const entry = entries.get(req.targetId);\n if (!entry) {\n throw new Error(`'${req.targetId}' is not running.`);\n }\n // Flag so the child's `close` handler treats this as a user stop, not a\n // boot failure, when the serve was still `starting` (#1).\n entry.stopping = true;\n entries.delete(req.targetId);\n await stopChild(entry.child, stopGraceMs, setTimeoutFn, clearTimeoutFn);\n entry.status = 'stopped';\n emitServe(entry);\n }\n\n function list(): StudioServeState[] {\n return [...entries.values()].map(publicState);\n }\n\n async function stopAll(): Promise<void> {\n const targets = [...entries.keys()];\n // `stop()` itself always resolves for a tracked target (stopChild\n // escalates SIGTERM -> SIGKILL and resolves on close); the only reject\n // path is the \"not running\" race if a sibling stop already evicted the\n // key. The catch keeps one such race from aborting the rest of the\n // shutdown sweep.\n await Promise.all(targets.map((targetId) => stop({ targetId }).catch(() => undefined)));\n }\n\n return { start, stop, list, stopAll };\n}\n\n/** Emit one container log line onto the bus, keyed by the serve target id. */\nfunction emitLog(\n bus: StudioEventBus,\n clock: () => number,\n target: string,\n line: string,\n stream: 'stdout' | 'stderr'\n): void {\n bus.emit('log', { ts: clock(), containerId: target, target, line, stream });\n}\n\n/**\n * Line-buffer a child stream and invoke `onLine` per complete line\n * (trailing newline stripped, blank lines dropped). Flushes any partial\n * final line on stream end.\n */\nfunction streamLines(stream: NodeJS.ReadableStream, onLine: (line: string) => void): void {\n let buf = '';\n stream.on('data', (chunk: string) => {\n buf += chunk;\n let nl = buf.indexOf('\\n');\n while (nl !== -1) {\n const line = buf.slice(0, nl).replace(/\\r$/, '');\n buf = buf.slice(nl + 1);\n if (line.length > 0) onLine(line);\n nl = buf.indexOf('\\n');\n }\n });\n stream.on('end', () => {\n const line = buf.replace(/\\r$/, '');\n if (line.length > 0) onLine(line);\n buf = '';\n });\n}\n\n/**\n * SIGTERM a child and resolve once it exits, escalating to SIGKILL after\n * `graceMs`. Resolves immediately if the child has already exited.\n */\nfunction stopChild(\n child: ChildProcessWithoutNullStreams,\n graceMs: number,\n setTimeoutFn: typeof setTimeout,\n clearTimeoutFn: typeof clearTimeout\n): Promise<void> {\n return new Promise<void>((resolve) => {\n let done = false;\n let kill: ReturnType<typeof setTimeout> | undefined;\n const finish = (): void => {\n if (done) return;\n done = true;\n if (kill) clearTimeoutFn(kill);\n resolve();\n };\n // Attach the `close` listener BEFORE the already-exited check so a child\n // that exits in the window between the check and the listener attach is\n // not missed (which would leave the promise pending forever). #4\n child.once('close', finish);\n if (child.exitCode !== null || child.signalCode !== null) {\n finish();\n return;\n }\n kill = setTimeoutFn(() => {\n if (!done) child.kill('SIGKILL');\n }, graceMs);\n kill.unref?.();\n child.kill('SIGTERM');\n });\n}\n","import { spawn } from 'node:child_process';\nimport { Command, Option } from 'commander';\nimport {\n appOptions,\n commonOptions,\n contextOptions,\n regionOption,\n parseContextOptions,\n} from '../options.js';\nimport { getLogger } from '../../utils/logger.js';\nimport { applyRoleArnIfSet } from '../../utils/role-arn.js';\nimport { withErrorHandling } from '../../utils/error-handler.js';\nimport { Synthesizer, type SynthesisOptions } from '../../synthesis/synthesizer.js';\nimport { resolveApp } from '../config-loader.js';\nimport {\n getEmbedConfig,\n setEmbedConfig,\n type CdkLocalEmbedConfig,\n} from '../../local/embed-config.js';\nimport { listTargets } from '../../local/target-lister.js';\nimport { StudioEventBus, type StudioTargetKind } from '../../local/studio-events.js';\nimport {\n startStudioServer,\n toStudioTargetGroups,\n type RunningStudioServer,\n} from '../../local/studio-server.js';\nimport { createStudioDispatcher, type StudioRunRequest } from '../../local/studio-dispatch.js';\nimport {\n createStudioServeManager,\n type StudioServeManager,\n type StudioStopRequest,\n} from '../../local/studio-serve-manager.js';\n\nconst STUDIO_TARGET_KINDS: readonly StudioTargetKind[] = [\n 'lambda',\n 'api',\n 'alb',\n 'ecs',\n 'agentcore',\n];\n\n/**\n * Validate + narrow the untyped `POST /api/run` body into a\n * {@link StudioRunRequest}. Throws (→ 400 from the server) on a malformed\n * body so a bad UI / curl payload fails loudly rather than spawning an\n * `invoke` for an empty target.\n */\nexport function coerceRunRequest(body: unknown): StudioRunRequest {\n if (typeof body !== 'object' || body === null) {\n throw new Error('Request body must be a JSON object.');\n }\n const { targetId, kind, event } = body as Record<string, unknown>;\n if (typeof targetId !== 'string' || targetId.trim() === '') {\n throw new Error('Request body must include a non-empty \"targetId\" string.');\n }\n if (typeof kind !== 'string' || !STUDIO_TARGET_KINDS.includes(kind as StudioTargetKind)) {\n throw new Error(`Request body \"kind\" must be one of: ${STUDIO_TARGET_KINDS.join(', ')}.`);\n }\n return { targetId, kind: kind as StudioTargetKind, event };\n}\n\n/**\n * Validate + narrow the untyped `POST /api/stop` body into a\n * {@link StudioStopRequest}. Throws (→ 400 from the server) on a missing\n * / empty target id.\n */\nexport function coerceStopRequest(body: unknown): StudioStopRequest {\n if (typeof body !== 'object' || body === null) {\n throw new Error('Request body must be a JSON object.');\n }\n const { targetId } = body as Record<string, unknown>;\n if (typeof targetId !== 'string' || targetId.trim() === '') {\n throw new Error('Request body must include a non-empty \"targetId\" string.');\n }\n return { targetId };\n}\n\nconst DEFAULT_STUDIO_PORT = 9999;\n\n/**\n * Parse + validate the `--studio-port` value. Accepts `0` (OS-assigned)\n * through `65535`. Exported so a unit test can assert the bounds without\n * driving the full command. Throws on anything out of range / non-numeric.\n */\nexport function parseStudioPort(raw: string): number {\n // `Number('')` / `Number(' ')` coerce to 0, which would pass the range\n // check; reject blank input explicitly.\n const port = raw.trim() === '' ? NaN : Number(raw);\n if (!Number.isInteger(port) || port < 0 || port > 65535) {\n throw new Error(`--studio-port must be 0..65535 (got ${raw}).`);\n }\n return port;\n}\n\ninterface LocalStudioOptions {\n app?: string;\n output: string;\n verbose: boolean;\n profile?: string;\n roleArn?: string;\n context?: string[];\n region?: string;\n /** `--studio-port`: preferred listen port (bumps on collision). */\n studioPort: string;\n /** `--no-open`: suppress auto-opening the browser (Commander sets `open`). */\n open: boolean;\n}\n\n/**\n * Factory options for {@link createLocalStudioCommand}.\n */\nexport interface CreateLocalStudioCommandOptions {\n /** Embed-time branding overrides for a host wrapping this factory. */\n embedConfig?: CdkLocalEmbedConfig;\n}\n\nasync function localStudioCommand(options: LocalStudioOptions): Promise<void> {\n const logger = getLogger();\n if (options.verbose) logger.setLevel('debug');\n\n const port = parseStudioPort(options.studioPort);\n\n await applyRoleArnIfSet({\n roleArn: options.roleArn,\n region: undefined,\n profile: options.profile,\n });\n\n const appCmd = resolveApp(options.app);\n if (!appCmd) {\n throw new Error(\n `No CDK app specified. Pass --app, set ${getEmbedConfig().envPrefix}_APP, or add \"app\" to cdk.json.`\n );\n }\n\n logger.info('Synthesizing CDK app...');\n const synthesizer = new Synthesizer();\n const context = parseContextOptions(options.context);\n const synthOpts: SynthesisOptions = {\n app: appCmd,\n output: options.output,\n ...(options.profile && { profile: options.profile }),\n ...(Object.keys(context).length > 0 && { context }),\n };\n const { stacks } = await synthesizer.synthesize(synthOpts);\n\n const listing = listTargets(stacks);\n const targetGroups = toStudioTargetGroups(listing);\n const appLabel = stacks.map((s) => s.stackName).join(', ') || appCmd;\n\n const bus = new StudioEventBus();\n // `process.argv[1]` is the running CLI entry (`dist/cli.js` / the `cdkl`\n // bin); both the invoke dispatcher and the serve manager spawn it again\n // (`cdkl invoke <target>` / `cdkl start-api <target>`) — studio is a\n // control plane over the CLI.\n const cliEntry = process.argv[1] ?? '';\n const childConfig = {\n cliEntry,\n bus,\n cwd: process.cwd(),\n ...(appCmd ? { app: appCmd } : {}),\n ...(options.profile ? { profile: options.profile } : {}),\n ...(options.region ? { region: options.region } : {}),\n ...(Object.keys(context).length > 0 ? { context } : {}),\n };\n const dispatcher = createStudioDispatcher(childConfig);\n const serveManager = createStudioServeManager(childConfig);\n\n const server = await startStudioServer({\n port,\n bus,\n targetGroups,\n appLabel,\n cliName: getEmbedConfig().cliName,\n // `/api/run`: a Lambda is a single-shot invoke; everything else is a\n // long-running serve start (slice C1 supports the `api` kind, the\n // serve manager rejects the rest with a clear message).\n onRun: (body) => {\n const req = coerceRunRequest(body);\n return req.kind === 'lambda' ? dispatcher.run(req) : serveManager.start(req);\n },\n onStop: async (body) => {\n const req = coerceStopRequest(body);\n await serveManager.stop(req);\n return { stopped: req.targetId };\n },\n getRunning: () => ({ running: serveManager.list() }),\n });\n\n const cliName = getEmbedConfig().cliName;\n logger.info(`${cliName} studio is running at ${server.url}`);\n logger.info('Press Ctrl-C to stop.');\n\n // Auto-open the browser only in an interactive terminal (never in CI /\n // piped / integ runs) and unless --no-open was passed.\n if (options.open && process.stdout.isTTY) {\n openBrowser(server.url);\n }\n\n await blockUntilShutdown(server, serveManager, cliName);\n}\n\n/** Best-effort cross-platform browser open. Failures are non-fatal. */\nfunction openBrowser(url: string): void {\n const cmd =\n process.platform === 'darwin' ? 'open' : process.platform === 'win32' ? 'start' : 'xdg-open';\n try {\n const child = spawn(cmd, [url], {\n stdio: 'ignore',\n detached: true,\n shell: process.platform === 'win32',\n });\n child.on('error', () => undefined);\n child.unref();\n } catch {\n // Opening the browser is a convenience; ignore any failure.\n }\n}\n\n/**\n * Block until SIGINT / SIGTERM, then stop every running serve child,\n * close the studio server, and resolve. Mirrors the long-running serve\n * commands' graceful-shutdown contract — the serve children are killed\n * BEFORE the server closes so their RIE containers are torn down rather\n * than orphaned.\n */\nfunction blockUntilShutdown(\n server: RunningStudioServer,\n serveManager: StudioServeManager,\n cliName: string\n): Promise<void> {\n return new Promise<void>((resolveShutdown) => {\n let shuttingDown = false;\n const shutdown = (signal: string): void => {\n if (shuttingDown) return;\n shuttingDown = true;\n getLogger().info(`Received ${signal}; stopping ${cliName} studio...`);\n void serveManager\n .stopAll()\n .catch((err: unknown) => getLogger().warn(`Error stopping serve targets: ${String(err)}`))\n .then(() => server.close())\n .catch((err: unknown) => getLogger().warn(`Error stopping studio server: ${String(err)}`))\n .finally(() => resolveShutdown());\n };\n process.on('SIGINT', () => shutdown('SIGINT'));\n process.on('SIGTERM', () => shutdown('SIGTERM'));\n });\n}\n\nexport function createLocalStudioCommand(opts: CreateLocalStudioCommandOptions = {}): Command {\n setEmbedConfig(opts.embedConfig);\n const cmd = new Command('studio')\n .description(\n \"Open the local studio: a web console that lists the synthesized CDK app's runnable \" +\n 'targets and lets you invoke / serve them from the browser while watching all activity ' +\n 'in one timeline. The interactive counterpart to the headless invoke / start-* commands.'\n )\n .action(\n withErrorHandling(async (options: LocalStudioOptions) => {\n await localStudioCommand(options);\n })\n );\n\n addStudioSpecificOptions(cmd);\n [...commonOptions(), ...appOptions(), ...contextOptions].forEach((opt) => cmd.addOption(opt));\n cmd.addOption(regionOption);\n return cmd;\n}\n\n/**\n * Register the option block `cdkl studio` adds on top of the shared\n * common / app / context option helpers. Kept in a named helper (not\n * inline in {@link createLocalStudioCommand}) so a host CLI embedding\n * this factory inherits new studio flags without a duplicate\n * `.addOption(...)` block, matching every other `add<Cmd>SpecificOptions`\n * extraction. Chainable: returns `cmd`.\n */\nexport function addStudioSpecificOptions(cmd: Command): Command {\n cmd.addOption(\n new Option(\n '--studio-port <port>',\n 'Preferred port for the studio web server (bumps to the next free port on collision)'\n ).default(String(DEFAULT_STUDIO_PORT))\n );\n cmd.addOption(\n new Option('--no-open', 'Do not auto-open the browser when studio starts (TTY only)')\n );\n return cmd;\n}\n"],"mappings":"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"}