|
5 | 5 | EvaluateCommand, |
6 | 6 | type EvaluationReferenceInput, |
7 | 7 | InvokeAgentRuntimeCommand, |
| 8 | + InvokeAgentRuntimeCommandCommand, |
8 | 9 | StopRuntimeSessionCommand, |
9 | 10 | } from '@aws-sdk/client-bedrock-agentcore'; |
10 | 11 | import type { HttpRequest } from '@smithy/protocol-http'; |
@@ -326,9 +327,7 @@ export async function invokeAgentRuntimeStreaming(options: InvokeAgentRuntimeOpt |
326 | 327 | buffer += decoded; |
327 | 328 | fullResponse += decoded; |
328 | 329 |
|
329 | | - // Process complete lines from the buffer |
330 | 330 | const lines = buffer.split('\n'); |
331 | | - // Keep the last incomplete line in the buffer |
332 | 331 | buffer = lines.pop() ?? ''; |
333 | 332 |
|
334 | 333 | for (const line of lines) { |
@@ -824,8 +823,9 @@ export async function invokeA2ARuntime(options: A2AInvokeOptions, message: strin |
824 | 823 | } |
825 | 824 |
|
826 | 825 | /** Wrap a single string value as an AsyncGenerator for StreamingInvokeResult compatibility. */ |
| 826 | +// eslint-disable-next-line @typescript-eslint/require-await |
827 | 827 | async function* singleValueStream(value: string): AsyncGenerator<string, void, unknown> { |
828 | | - yield await Promise.resolve(value); |
| 828 | + yield value; |
829 | 829 | } |
830 | 830 |
|
831 | 831 | /** Extract text content from A2A JSON-RPC response. Supports both kind:'text' and type:'text' part formats. */ |
@@ -904,3 +904,89 @@ export async function stopRuntimeSession(options: StopRuntimeSessionOptions): Pr |
904 | 904 | statusCode: response.statusCode, |
905 | 905 | }; |
906 | 906 | } |
| 907 | + |
| 908 | +// --------------------------------------------------------------------------- |
| 909 | +// Execute Bash: Run shell commands in runtime containers |
| 910 | +// --------------------------------------------------------------------------- |
| 911 | + |
| 912 | +export interface ExecuteBashOptions { |
| 913 | + region: string; |
| 914 | + runtimeArn: string; |
| 915 | + command: string; |
| 916 | + sessionId?: string; |
| 917 | + timeout?: number; |
| 918 | + /** Custom headers to forward to the agent runtime */ |
| 919 | + headers?: Record<string, string>; |
| 920 | + /** Bearer token for CUSTOM_JWT auth — not yet supported for exec, will throw */ |
| 921 | + bearerToken?: string; |
| 922 | +} |
| 923 | + |
| 924 | +export interface ExecuteBashStreamEvent { |
| 925 | + type: 'start' | 'stdout' | 'stderr' | 'stop'; |
| 926 | + data?: string; |
| 927 | + exitCode?: number; |
| 928 | + status?: string; |
| 929 | +} |
| 930 | + |
| 931 | +export interface ExecuteBashResult { |
| 932 | + stream: AsyncGenerator<ExecuteBashStreamEvent, void, unknown>; |
| 933 | + sessionId: string | undefined; |
| 934 | +} |
| 935 | + |
| 936 | +/** |
| 937 | + * Execute a shell command in a running AgentCore Runtime container. |
| 938 | + * Returns a streaming result with stdout/stderr events and exit code. |
| 939 | + */ |
| 940 | +export async function executeBashCommand(options: ExecuteBashOptions): Promise<ExecuteBashResult> { |
| 941 | + if (options.bearerToken) { |
| 942 | + throw new Error('Bearer token auth for exec is not yet supported. Use SigV4 credentials.'); |
| 943 | + } |
| 944 | + |
| 945 | + const client = createAgentCoreClient(options.region, options.headers); |
| 946 | + |
| 947 | + const command = new InvokeAgentRuntimeCommandCommand({ |
| 948 | + agentRuntimeArn: options.runtimeArn, |
| 949 | + runtimeSessionId: options.sessionId, |
| 950 | + body: { |
| 951 | + command: options.command, |
| 952 | + ...(options.timeout != null ? { timeout: options.timeout } : {}), |
| 953 | + }, |
| 954 | + }); |
| 955 | + |
| 956 | + const response = await client.send(command); |
| 957 | + const sessionId = response.runtimeSessionId; |
| 958 | + |
| 959 | + async function* streamEvents(): AsyncGenerator<ExecuteBashStreamEvent, void, unknown> { |
| 960 | + if (!response.stream) { |
| 961 | + throw new Error('No stream in response from AgentCore Runtime'); |
| 962 | + } |
| 963 | + for await (const event of response.stream) { |
| 964 | + // SDK types for InvokeAgentRuntimeCommandCommand stream events are not yet published — cast needed until SDK stabilizes |
| 965 | + const chunk = (event as unknown as Record<string, unknown>).chunk as Record<string, unknown> | undefined; |
| 966 | + if (!chunk || typeof chunk !== 'object') continue; |
| 967 | + |
| 968 | + if (chunk.contentStart !== undefined) { |
| 969 | + yield { type: 'start' }; |
| 970 | + } |
| 971 | + const delta = chunk.contentDelta as { stdout?: string; stderr?: string } | undefined; |
| 972 | + if (delta) { |
| 973 | + if (delta.stdout) { |
| 974 | + yield { type: 'stdout', data: delta.stdout }; |
| 975 | + } |
| 976 | + if (delta.stderr) { |
| 977 | + yield { type: 'stderr', data: delta.stderr }; |
| 978 | + } |
| 979 | + } |
| 980 | + const stop = chunk.contentStop as { exitCode?: number; status?: string } | undefined; |
| 981 | + if (stop) { |
| 982 | + yield { |
| 983 | + type: 'stop', |
| 984 | + exitCode: stop.exitCode, |
| 985 | + status: stop.status, |
| 986 | + }; |
| 987 | + } |
| 988 | + } |
| 989 | + } |
| 990 | + |
| 991 | + return { stream: streamEvents(), sessionId }; |
| 992 | +} |
0 commit comments