

本文属于机器翻译版本。若本译文内容与英语原文存在差异，则一律以英文原文为准。

# 交互式外壳（终端）
<a name="harness-command-shell"></a>

该`InvokeAgentRuntimeCommandShell`操作将在正在 AgentCore 运行的Harness会话中打开一个持久的交互式终端 WebSocket。API 使用运行时会话 ID 来标识此线束会话。与一次性命令执行不同，交互式 shell 会跨输入维护其环境变量、工作目录、命令历史记录和正在运行的进程。

当你需要在命令之间保留 shell 状态时，使用交互式 shell 进行调试、迭代开发或长时间运行的进程。

对于 Sigv4 和 SigV4 的预签名请求，调用者需要`bedrock-agentcore:InvokeAgentRuntimeCommandShell`获得该工具的许可。 Bearer-token 相反，请求必须满足线束入站授权者的要求。

**重要**  
启动时， AgentCore CLI 命令和高级别 AgentCore SDK shell 助手不支持线束目标。直接通过 `InvokeAgentRuntimeCommandShell` WebSocket API 连接到安全带外壳，如本页所示。

## 工作原理
<a name="harness-command-shell-how-it-works"></a>

 `InvokeAgentRuntimeCommandShell`在运行您的线束会话的 microVM 中建立与交互式 shell 进程的 WebSocket 连接。该连接使用二进制帧来双向传输终端输入和输出。

如果线束使用默认环境，则外壳将在托管线束环境中运行。如果安全带使用自定义容器，则外壳将在该容器中运行。Harness 交互式终端目前仅支持 Bash。

该连接支持：


| 功能 | 说明 | 
| --- | --- | 
| 持续状态 | 环境变量、工作目录、命令历史记录和正在运行的进程保存在同一 shell 中。 | 
| 重新连接 | 提供相同的运行时会话 ID 并重新`shellId`连接到已分离的 shell。该服务最多可重放 256 KB 的缓冲输出。 | 
| 多个并发外壳 | 在一个安全带会话中打开最多 10 个活跃的 shell 会话。分离的炮弹不计入此限制。每个 shell 都有一个独立的终端进程并共享会话文件系统。 | 

## 先决条件
<a name="harness-command-shell-prerequisites"></a>
+ 安全带处于`READY`状态
+ 所选身份验证方法的授权：Sigv4 `bedrock-agentcore:InvokeAgentRuntimeCommandShell` 的 IAM 权限，或线束入站授权者接受的持有者令牌
+ 支持二进制帧以及请求标头或预签名 URL 的 WebSocket 客户端
+ 对于自定义容器，Bash 安装在。`/bin/bash`目前不支持其他 shell`dash`，包括`sh`和。

有关完整工具权限模型的更多信息，请参阅[来电者必需的 IAM 权限](harness-security.md#harness-iam-permissions)。

## 通过 WebSocket API 进行连接
<a name="harness-command-shell-connect"></a>

打开 WebSocket 与以下 URL 的连接：

```
wss://bedrock-agentcore.<region>.amazonaws.com/runtimes/<url-encoded-harness-arn>/ws/shells
```

使用安全工具 ARN，而不是其托管运行时的 ARN。 URL-encode 将其放入路径之前的完整安全带 ARN。安全带 ARN 的格式如下：

```
arn:aws:bedrock-agentcore:<region>:<accountId>:harness/<harnessId>
```

在 WebSocket 升级请求中发送以下值：


| 值 | 必需 | 说明 | 
| --- | --- | --- | 
| 身份验证 | 是 | 使用签名版本 4 (Sigv4) 对请求进行签名，使用 Sigv4 预签名 URL，或者在标头中发送由线束入站授权方配置的不记名令牌。`Authorization`Sigv4 的服务名称是。`bedrock-agentcore` | 
|  `X-Amzn-Bedrock-AgentCore-Runtime-Session-Id`标题或查询参数 | 否 | 以现有安全带会话为目标或使用提供的 ID 创建一个。该值必须至少包含 33 个字符。如果省略它，则该服务会创建一个运行时会话 ID 并在响应中返回。 | 
|  `Sec-WebSocket-Protocol`  | 否 | 对于 SigV4 连接，可以选择提供标识命令行`v1.command.agentcore.aws.dev`管理程序二进制协议。 Bearer-token 连接改用此表后面描述的身份验证子协议。 | 
|  `shellId`查询参数 | 否 | 重新连接到现有的外壳。将其与创建 shell 相同的运行时会话 ID 一起使用。 | 

对于无法设置`Authorization`标头的浏览器客户端，base64url 对持有者令牌进行编码，然后同时提供两者以及哨兵。`base64UrlBearerAuthorization.<encoded-token>` `base64UrlBearerAuthorization` `Sec-WebSocket-Protocol`也不要在持有者令牌连接`v1.command.agentcore.aws.dev`上提供。

`101 Switching Protocols`响应会返回和`X-Amzn-Bedrock-AgentCore-Shell-Id`标头中的运行时会话 ID `X-Amzn-Bedrock-AgentCore-Runtime-Session-Id` 和外壳 ID。无法读取 WebSocket 升级响应标头的客户端可以从第一`STATUS`帧读取 shell ID。框架不包含运行时会话 ID，因此这些客户端在连接之前必须生成并保留运行时会话 ID。创建预签名 URL 时，请在签名 URL 之前将运行时会话 ID 作为查询参数包括在内。

 WebSocket 升级超时仅在服务返回之前适用`101 Switching Protocols`。升级后，Shell 和自定义容器的初始化可以继续。等待初始`STATUS`帧后再将 shell 视为就绪，在等待时应用单独的应用程序超时，将关闭代码`1011`作为 shell 初始化或服务器故障处理。

以下 Python 示例使用通用 WebSocket 客户端，并且`botocore`仅用于签署升级请求。它不使用 AgentCore SDK 外壳助手。

```
pip install "boto3>=1.34" "websockets>=15"
```

```
import asyncio
import json
import urllib.parse
import uuid

import botocore.session
import websockets
from botocore.auth import SigV4Auth
from botocore.awsrequest import AWSRequest


REGION = "us-west-2"
HARNESS_ARN = (
    "arn:aws:bedrock-agentcore:us-west-2:123456789012:"
    "harness/MyHarness-a1b2c3d4e5"
)
SESSION_ID = str(uuid.uuid4())  # 36 characters
SUBPROTOCOL = "v1.command.agentcore.aws.dev"


def signed_headers(url):
    request = AWSRequest(
        method="GET",
        url=url,
        headers={
            "X-Amzn-Bedrock-AgentCore-Runtime-Session-Id": SESSION_ID,
        },
    )
    credentials = botocore.session.Session().get_credentials()
    SigV4Auth(credentials, "bedrock-agentcore", REGION).add_auth(request)
    return dict(request.headers)


async def main():
    encoded_arn = urllib.parse.quote(HARNESS_ARN, safe="")
    host = f"bedrock-agentcore.{REGION}.amazonaws.com"
    path = f"/runtimes/{encoded_arn}/ws/shells"
    https_url = f"https://{host}{path}"
    websocket_url = f"wss://{host}{path}"

    async with websockets.connect(
        websocket_url,
        additional_headers=signed_headers(https_url),
        subprotocols=[SUBPROTOCOL],
        open_timeout=330,
    ) as websocket:
        shell_id = websocket.response.headers.get(
            "X-Amzn-Bedrock-AgentCore-Shell-Id"
        )
        print(f"Connected to shell {shell_id}")

        # Channel 0x00 carries terminal input.
        await websocket.send(b"\x00echo 'Hello from my harness'\n")
        await websocket.send(b"\x00exit\n")

        async for frame in websocket:
            if isinstance(frame, str) or not frame:
                continue

            channel, payload = frame[0], frame[1:]
            if channel == 0x01:  # PTY output (command stdout and stderr)
                print(payload.decode("utf-8", errors="replace"), end="")
            elif channel == 0x02:  # Service or platform diagnostics
                print(payload.decode("utf-8", errors="replace"), end="")
            elif channel == 0x03:  # STATUS
                status = json.loads(payload)
                shell_id = status.get("metadata", {}).get("shellId", shell_id)
                print(f"\nStatus: {status}")


asyncio.run(main())
```

## 二进制帧协议
<a name="harness-command-shell-protocol"></a>

每条应用程序级消息都是二进制 WebSocket 帧。第一个字节标识频道，其余字节是有效载荷。


| 频道 | 方向 | 有效载荷 | 
| --- | --- | --- | 
|  `0x00`-标准输入 | 客户到服务 | 原始终端输入。 | 
|  `0x01`-STDOUT | 为客户提供服务 | 未处理的 PTY 输出。PTY 在这个频道上结合了命令 stdout 和 stderr。 | 
|  `0x02`-STDERR | 为客户提供服务 | 服务或平台诊断。命令 stderr 是 `0x01` PTY 输出的一部分。 | 
|  `0x03`-状态 | 为客户提供服务 | UTF-8 包含连接元数据或外壳退出状态的 JSON。 | 
|  `0x04`-调整大小 | 客户到服务 | UTF-8 表单中的 JSON `{"width":120,"height":40}`。每个维度必须介于 1 到 1,000 之间。 | 
|  `0x05`-心跳 | 两个方向 | 可选的应用程序心跳。该服务会回显有效载荷。 WebSocket 还支持 ping 帧。 | 
|  `0xFF`-关闭 | 两个方向 | 结束外壳会话。 | 

文本框架不是协议的一部分。该服务会忽略孤立的文本框架，并在连续五个文本框架`1003`后关闭与代码的连接。

第一`STATUS`帧确认连接，并包含类似于以下内容的元数据：

```
{
  "kind": "Status",
  "apiVersion": "v1",
  "metadata": {
    "shellId": "my-shell",
    "reconnected": false
  },
  "status": "Success"
}
```

当 shell 以非零状态退出时，最后一`STATUS`帧会在中包含`ExitCode`原因。`details.causes`

## 重新连接到外壳
<a name="harness-command-shell-reconnect"></a>

存储运行时会话 ID 和外壳 ID。要重新连接，请使用相同的运行时会话 ID 标头并将 URL-encoded 外壳 ID 添加为查询参数：

```
wss://bedrock-agentcore.<region>.amazonaws.com/runtimes/<url-encoded-harness-arn>/ws/shells?shellId=<shell-id>
```

断开连接后，shell 进程将继续运行。重新连接时，该服务最多可重放 256 KB 的缓冲输出。`metadata.bytesDropped`如果分离的 shell 产生的输出超过了重播缓冲区所能保留的输出量，则连接`STATUS`帧将设置`metadata.reconnected`为并包含。`true`

打开另一个具有相同 shell ID 的连接会替换现有连接。被替换的客户端会收到关闭码`4000`。

## 常见使用案例
<a name="harness-command-shell-use-cases"></a>

交互式调试  
在与线束代理相同的环境中检查已安装的软件包、文件、进程和环境配置。

自定义环境验证  
对于带有自定义容器的线束，请从该容器内部验证已安装的依赖项和文件系统安装情况。

编码代理终端访问权限  
为需要创建文件、运行测试、检查输出和迭代的编码代理提供持久的执行环境。

Long-running 进程  
启动进程，与分离，稍后重新连接以检查进度或提供输入。 WebSocket

## 安全注意事项
<a name="harness-command-shell-security"></a>

**重要**  
可以打开 Harness shell 的主体可以交互式命令访问线束会话。仅限`bedrock-agentcore:InvokeAgentRuntimeCommandShell`于受信任的委托人和特定的线束 ARN。

shell 可以在选定的环境或已安装的文件系统上访问客户提供的数据、文件和机密。它还可以连接到会话可以到达的网络目的地。shell 环境使用许可名单，不注入 AgentCore 平台或工作负载凭证。shell 保留在会话的隔离式 microVM 中，无法访问其他客户的工作负载。

AgentCore 将请求 ID 和连接元数据发送到 Amazon Logs 中的 harness 运行时 CloudWatch 日志组。该服务不记录终端的输入或输出。

 AWS CloudTrail 记录 `InvokeAgentRuntimeCommandShell` API 调用，包括呼叫者身份、时间戳、源 IP 地址、已解析的运行时资源和响应状态。 CloudTrail 不记录终端输入或输出。

如需了解更多 Harness 安全指南，请参阅[安全性和访问控制](harness-security.md)。

## 错误处理
<a name="harness-command-shell-errors"></a>

 WebSocket 升级可能会返回以下错误：

 **ValidationException**   
请求参数无效。验证运行时会话 ID 是否包含至少 33 个字符，外壳 ID 是否有效，并且该功能在目标区域中可用。在请求路径中使用 URL-encoded 安全带 ARN；直接使用托管运行时 ARN 被拒绝。

 **AccessDeniedException**   
来电者没有使用安全带的`bedrock-agentcore:InvokeAgentRuntimeCommandShell`权限。

 **ResourceNotFoundException**   
指定的安全带不存在。验证 URL 是否包含 URL-encoded 线束 ARN。

 **ServiceQuotaExceededException (429)**   
最多已打开 10 个活跃炮弹。结束活动外壳或将其分离，然后重试。

 **RuntimeClientError (424)**   
无法达到运行时间。

 **ThrottlingException**   
该请求超出了 API 速率限制。使用指数回退进行重试。

 **RetryableConflictException (409)**   
该服务正在配置或删除目标会话。使用短的指数回退重试。

连接后，以下关闭代码指示连接结束的原因：


| 代码 | 含义 | 客户操作 | 
| --- | --- | --- | 
|  `1000`  | 炮弹干净利落地退出。 | 视为正常终止。 | 
|  `1003`  | 客户端向仅限二进制协议连续发送了五个文本框架。 | 切换到二进制帧。不要以相同的行为自动重新连接。 | 
|  `1006`  | 由于网络故障或服务中断，连接在没有关闭帧的情况下结束。 | 使用存储的外壳 ID 重新连接。 | 
|  `1008`  | 一小时的连接持续时间已过期，帧速超过每秒 250 帧，或者写入缓冲区溢出。 | 在持续时间到期后重新连接。对于速率或缓冲区违规，请在重新连接之前退出。 | 
|  `1009`  | 帧有效载荷超过 64 KB。 | 将有效负载分成小于 64 KB 的帧，然后重新连接。 | 
|  `1011`  | 服务器出现意外故障。 | 使用指数回退进行重试。 | 
|  `4000`  | 另一台使用相同外壳 ID 连接的客户端。 | 不要自动重新连接。通知用户有另一台客户端连接到 shell。 | 

## 最佳实践
<a name="harness-command-shell-best-practices"></a>
+ 为每个逻辑终端生成并保留一个唯一的 shell ID。
+ 保留运行时会话 ID 和外壳 ID。这两个值都是重新连接所必需的。
+ 立即读取输出，这样连接写入缓冲区就不会被填满。
+ 将较大的输入分割成小于 64 KB 的二进制帧。
+ 如果 shell 必须保持更长的可用时间，则在一小时连接限制之前或之后重新连接。
+ 当不再需要外壳时，明确结束外壳。分离的炮弹不计入 10 个主动炮弹的上限。该服务最多保留 20 个活动和已分离的 shell 条目，并在必要时驱逐最旧的分离外壳。
+ 当连接可以启动冷安全带会话时，使用至少 330 秒的升级超时。升级后，在等待初始`STATUS`帧时单独应用超时。

## 限额和限制
<a name="harness-command-shell-limits"></a>


| 限制 | 值 | 说明 | 
| --- | --- | --- | 
| 最大帧有效载荷 | 64 KB | 较大的帧会导致代码接近尾声`1009`。 | 
| 帧率 | 每秒 250 帧 | 超过限制会导致代码关闭`1008`。 | 
| 最大连接持续时间 | 1 小时 | 使用相同的运行时会话 ID 和 shell ID 重新连接以继续。 | 
| 每个安全带会话并发活动外壳 | 10 | 当 10 个 shell 处于活动状态时，新连接将被拒绝。分离的炮弹不计入此限制。 | 
| 保留了活动和已分离的 shell 条目 | 20 | 达到限制后，该服务会在创建另一个 shell 之前驱逐最旧的分离外壳。 | 
| 重新连接缓冲区 | 256 KB | 重新连接后重播的最大输出。 | 

有关完整的服务限制，请参阅 [Amazon Bedrock AgentCore 配额](bedrock-agentcore-limits.md)。

## 相关主题
<a name="_related_topics"></a>
+  [环境和文件系统](harness-environment.md)-配置默认环境或自定义容器
+  [安全性和访问控制](harness-security.md)-控制对线束及其执行环境的访问权限
+  [可观察性和成本控制](harness-operations.md)-监控安全带活动和成本
+  [ AgentCore 运行时的交互式外壳](runtime-get-started-command-shell.md) 