# oakctl 工具

oakctl 是一款命令行工具，允许您与 OAK4 相机进行交互。它可用于创建、部署和管理运行 oak-agent 服务且位于本地网络中的应用程序和设备。

请注意，oak-agent 已预装在 OAK4 设备上（它内置于 [Luxonis OS](https://docs.luxonis.com/software-v3/sw-stack/luxonis-os.md) 中），因此您可以立即开始使用 oakctl。

## 安装

#### Linux/MacOS

在 64 位系统上，运行：

```bash
bash -c "$(curl -fsSL https://oakctl-releases.luxonis.com/oakctl-installer.sh)"
```

#### Windows

下载并使用 [Windows 安装程序](https://oakctl-releases.luxonis.com/data/latest/windows_x86_64/oakctl.msi) 进行安装。

## 更新

您之后可以通过以下方式更新 oakctl 工具：

```bash
oakctl self-update
```

> **Windows 更新问题**
> 如果您在 Windows 上更新 oakctl 版本时遇到问题，请从
> [此处](https://oakctl-releases.luxonis.com/data/latest/windows_x86_64/oakctl.msi)
> 下载安装程序。

## USB 网络共享

> **版本说明**
> USB 网络共享需要
> `oakctl v0.22.0`
> 及更高版本以及
> `Luxonis OS 1.32.0`
> 及更高版本。

如果您的 OAK4 通过 USB 连接并且需要访问互联网，您可以使用 oakctl usbd 将主机的互联网连接共享给设备。这对于 oakctl app run、oakctl app build、与 hub
协作、更新设备操作系统以及任何其他需要互联网连接的操作都非常有用。

启用后，oakctl-usbd 后台服务会监控支持的 OAK USB 以太网接口，并自动配置主机侧网络，包括接口寻址、IP 转发和 NAT。在 Linux 上，它还会安装一个 udev 规则，以防止 NetworkManager 和
systemd-networkd 接管 OAK4 USB 以太网接口。

oakctl usbd 是可选功能，默认情况下处于禁用状态。

### 启用 oakctl usbd

```bash
oakctl usbd enable
```

此命令会安装服务（如果需要）、启用并启动它。该命令需要提升的权限，因此 oakctl 可能会提示输入管理员或 root 权限。

您可以检查服务当前是否已启用：

```bash
oakctl usbd status
```

### 禁用 oakctl usbd

```bash
oakctl usbd disable
```

此命令会停止服务并禁用它。

## 使用方法

### 连接到设备

如果网络中只有一台设备可用，您可以通过以下命令连接到它：

```bash
oakctl connect
```

如果您有多台设备，或者设备与您的 PC 不在同一网络中，您可以指定设备的 IPv4 地址来连接：

```bash
oakctl connect 10.0.1.24
```

可选地，您可以使用 --with-password 标志存储设备的密码。这样可以避免每次会话过期时都输入密码。

### 查找设备

oakctl 允许您与安装了 oak-agent 的设备进行交互。您可以通过以下命令列出所有可用的设备（在您的网络上找到的）：

```bash
oakctl list
+---+---------------+---------------+------------+----------------+---------------+-------+
| # | 序列号         | 设备          | 连接方式    | 操作系统         | Agent 版本    | 设置  |
+=========================================================================================+
| 1 | 1623452492    | Luxonis, Inc. | 10.0.1.24  | OS RVC4 1.18.0 | 0.13.7        | 正常  |
|   |               |  OAK4-D R7    |            |                |               |       |
+---+---------------+---------------+------------+----------------+---------------+-------+
```

### 应用程序开发

您可以通过以下命令运行应用程序：

```bash
oakctl app run /app/path/to/source
```

默认情况下，oakctl app run 使用提供的应用程序目录中的 oakapp.toml。使用 --oakapp-file 选择备用配置文件（例如 oakapp.dev.toml 或
oakapp.prod.toml），只要该文件位于同一应用程序目录树内即可。

#### 可选标志：

| 标志 | 描述 |
| --- | --- |
| `-b, --detach` | 在分离模式下运行应用程序。您可以使用 `oakctl app stop <APP_ID>` 停止它。 |
| `-d, --device [<DEVICE>]` | 使用 IP、`IP:PORT`、索引或来自 `oakctl list` 的序列号指定设备。 |
| `-i, --invalidate` | 使缓存失效并强制重建。 |
| `-e, --env <KEY=VALUE>` | 将运行时环境变量导出到应用程序（可多次提供）。 |
| `--env-file <FILE>` | 从 `.env` 文件导出运行时环境变量。 |
| `--oakapp-file [<PATH>]` | 从提供的应用程序目录树内部使用备用的 `oakapp.toml` 文件（例如开发/生产变体）。 |
| `--preserve-line-endings` | 在上传文件到设备之前，禁用自动将 CRLF 转换为 LF。 |

#### 示例应用

我们准备了[示例应用](https://github.com/luxonis/oak-examples/tree/main/neural-networks)，您可以在 OAK4
设备上运行。下面我们将使用[人体姿态检测](https://github.com/luxonis/oak-examples/tree/main/neural-networks/pose-estimation/human-pose)示例，该示例还包含了
[oakapp.toml](#oakapp.toml)：

```bash
git clone https://github.com/luxonis/oak-examples.git
cd oak-examples
# Run an app
oakctl app run ./neural-networks/pose-estimation/human-pose # or some other example
```

#### 环境变量

您可以使用 -e, --env 标志或 --env-file 标志向应用传递环境变量。这些变量将在应用的运行时环境中可用，并可用于配置应用而无需更改代码。

```bash
oakctl app run ./app/path --env KEY1=VALUE1 --env KEY2=VALUE2
```

某些应用可能需要在构建时设置特定的环境变量（例如 API 密钥、模型下载链接等）。在这种情况下，您可以在 oakapp.toml 文件中使用 build_env 字段设置这些变量，或者在构建时使用 --build-arg 标志传递它们：

```bash
oakctl app build ./app/path --build-arg KEY1=VALUE1 --build-arg KEY2=VALUE
```

每个应用默认设置了一些环境变量：

| 变量 | 描述 |
| --- | --- |
| `OAKAGENT_APP_ID` | 唯一的应用 ID。 |
| `OAKAGENT_CONTAINER_ID` | 容器 ID（如果应用已更新，可能与 App ID 不同）。 |
| `OAKAGENT_APP_IDENTIFIER` | 来自 `oakapp.toml` 的应用标识符。 |
| `OAKAGENT_APP_VERSION` | 来自 `oakapp.toml` 的应用版本。 |
| `DEPTHAI_ZOO_MODELS_PATH` | DepthAI Zoo 模型目录的路径；根据 `oakapp.toml` 中的 `depthai_models` 字段设置；建议不要手动修改。 |
| `DEPTHAI_ZOO_CACHE_PATH` | DepthAI Zoo 缓存目录的路径；用于缓存下载的模型；建议不要手动修改。 |
| `DEPTHAI_ZOO_INTERNET_CHECK` | 启用模型下载的互联网连接检查；根据 `oakapp.toml` 中的 `depthai_models` 字段设置；建议不要手动修改。 |
| `DEPTHAI_DEVICE_NAME_LIST` | 在所有 RVC4 设备上设置为 `127.0.0.1`，以确保独立应用只使用它们正在运行的设备，即使同一网络上有多个设备。 |
| `OAKAPP_STATIC_FRONTEND_PATH` | 应用的静态前端文件路径。 |
| `OAKAPP_STATIC_FRONTEND_PORT` | 用于提供静态前端文件的端口。其设置范围为 9000-9128，以避免与代理或其他应用使用的端口冲突。 |
| `OAKAGENT_STORAGE_APP` | 应用特定的持久化存储路径。 |
| `OAKAGENT_STORAGE_SHARED` | 在同一标识符的应用之间可访问的共享存储路径。 |
| `OAKAGENT_STORAGE_GLOBAL` | 设备上的全局存储路径。 |

> **持久化存储**
> 持久化存储路径用于存储在应用重启和更新后应保留的数据。这些路径被挂载到应用的容器中。您可以通过
> `oakapp.toml`
> 配置（
> `storage_dir_name`
> ）选择存储目标在容器中的目录名称。
> - `OAKAGENT_STORAGE_APP`
> 是每个应用独有的存储路径，不与其他应用共享。它用于存储不应被其他应用访问的应用特定数据。
> - `OAKAGENT_STORAGE_SHARED`
> 是所有具有相同标识符的应用之间共享的存储路径。它用于存储不同安装的同一应用应可访问的数据。
> - `OAKAGENT_STORAGE_GLOBAL`
> 是不绑定到任何特定应用的全局存储路径。它用于存储无论安装或运行哪些应用都应可访问的数据。

### 应用管理

您可以在设备上安装多个应用。如果您正在开发一个应用，您可以在团队内与其他成员分享编译后的应用。

请确保一次只运行一个 DepthAI 应用。

大多数应用管理命令都有可选的标志 -d, --device <DEVICE>，用于指定运行命令的设备（IP 地址或来自 oakctl list 的索引）。

#### 列出已安装的应用

如果您设置设备并在 Hub 中采纳，您可能已经安装了一个应用（com.luxonis.default）。

```bash
$ oakctl app list
+---+--------+---------------------------+---------+-------------------+
| E | App Id | Identifier                | Status  | Frontend URL      |
+====================================================================================================+
| * | f8da5  | com.luxonis.default:1.3.8 | running | 10.0.1.24:9000    |
+---+--------+---------------------------+---------+-------------------+
```

第一列（E）如果应用已启用，则会显示星号。

任何应用都可以处于以下状态之一：installing、building、ready、starting、running、stopping、stopped、deleting、deleted、unknown。

Ready 表示应用已安装并准备运行，但自代理启动后尚未启动；而 stopped 表示应用已安装但未运行，已被用户停止。

> **注意**
> 为了更好的可读性，列表中的应用 ID 会进行截断。如果有多个应用具有相同的前缀，截断长度会增加，直到所有应用 ID 唯一。你还可以使用
> `--long`
> 标志来显示完整的应用 ID。 所有应用控制命令都接受完整和截断的应用 ID，只要它们是唯一的。

#### 应用控制流程

控制应用的基本命令是 start、stop 和 delete。首先，启动应用：

```bash
$ oakctl app start 612a745c-8d68-4264-a3d6-d06d14275ef5
Starting app 612a745c-8d68-4264-a3d6-d06d14275ef5 ...
  App has been started!
```

你也可以使用唯一的应用 ID 前缀：

```bash
$ oakctl app start 612a745c
```

现在，读取应用日志——此命令将读取应用日志并在接收到时打印到控制台：

```bash
$ oakctl app logs <app-id>
Reading logs ...
  Press Ctrl+C to stop reading logs.
  App output:
    [2025-02-12T14:37:54Z] Hello cli 1 / Wed Feb 12 14:37:54 UTC 2025
```

可选地，你可以使用 -n 标志指定要读取的前几行数：

```bash
oakctl app logs <app-id> -n 10
```

如果你只想要历史日志而不附加到实时输出，请使用：

```bash
oakctl app logs <app-id> --no-follow
```

现在，你可以停止应用：

```bash
$ oakctl app stop 612a745c-8d68-4264-a3d6-d06d14275ef5
Stopping app 612a745c-8d68-4264-a3d6-d06d14275ef5 ...
  App has been stopped!
```

以下是删除应用的方法：

```bash
$ oakctl app delete 612a745c-8d68-4264-a3d6-d06d14275ef5
Deleting app 612a745c-8d68-4264-a3d6-d06d14275ef5 ...
  App has been deleted!
```

任何应用都可以启用或禁用。如果应用已启用，它将在设备启动时自动启动。

```bash
oakctl app enable <app-id>
```

你也可以使用 disable 命令禁用应用：

```bash
oakctl app disable <app-id>
```

要交互式地连接到设备上已运行的应用，请使用 exec 命令：

```bash
oakctl app exec <app-id> -i /bin/bash
```

这将在应用的容器内打开一个交互式的 bash shell，允许你检查文件、进程和环境变量。

> **注意**
> 在容器内修改文件或保存数据时要小心，因为当容器停止或删除时，更改可能会丢失。 更好的方法是使用挂载将主机的持久化文件夹挂载到容器中（参见
> [`oakapp.toml` 配置](https://docs.luxonis.com/software-v3/oak-apps/configuration.md)
> ）或使用由 OAK 代理管理的持久化存储（参见
> `OAKAGENT_STORAGE_APP`
> 、
> `OAKAGENT_STORAGE_SHARED`
> 、
> `OAKAGENT_STORAGE_GLOBAL`
> 环境变量）。

#### 安装应用

应用可以在 Hub 的 [OAK4 应用](https://hub.luxonis.com/control/apps/team-apps?tab=oak4) 部分找到。
这里有与团队共享的应用、与你团队共享的应用或公共应用。默认情况下，新安装的应用会停止所有其他应用，并已启用且正在运行。你可以使用 --enable=false 或 --keep-others 标志来修改此行为。

应用的安装通过以下命令完成：

```bash
$ oakctl app install com-example-emotion_recognition_1-0-1.oakapp
Sending data to agent ...
  Data sent successfully!

✔ Oakapp package built successfully.                                                                                                         
Installed app has ID: ddb8a884-d9e1-4779-a88b-c30d5d1e265f

Start the app:  oakctl app start ddb8a884-d9e1-4779-a88b-c30d5d1e265f
```

可以从 *.oakapp 文件、Hub（通过标识符）或 URL 安装应用。

如果要从 Hub 安装应用，请执行以下操作：

```bash
# 通过标识符安装应用
oakctl app install -i com.example.app
```

如果应用在互联网上某个地方，也可以通过 URL 安装：

```bash
# 通过 URL 安装应用
oakctl app install -u https://example.com/app.oakapp
```

#### 更新应用

应用更新可以使用 oakctl app install 命令执行。默认情况下，oakctl app install 会尝试更新具有相同标识符的现有应用。使用 --force-new 标志可以安装一个新实例而不是更新（这对于自托管 OAK
代理很有用；Luxonis OS 不允许同时运行多个 DepthAI 应用）。

如果多个应用共享相同的标识符，请使用 --update <APP_ID> 标志指定要更新的应用。

更新过程遵循以下步骤：

 1. 从 Hub、URL 或本地 *.oakapp 文件下载新版本的应用（取决于来源：-i、-u 或文件路径）。在此步骤中，当前版本保持运行。
 2. 一旦新版本下载并验证成功，当前版本停止，新版本启动。任何先前的回滚版本随后被移除。

为了获得更多控制，您可以手动执行更新：

 1. 暂存新版本：oakctl app install --manual-update 下载并准备新版本，而无需停止当前版本。使用 oakctl app list 验证准备就绪。
 2. 提交更新：oakctl app update <APP_ID> --commit 停止当前版本并启动新版本。
 3. 如有需要，回滚：oakctl app update <APP_ID> --rollback 如果出现问题，恢复到先前版本。
 4. 更新完成时确认：oakctl app update <APP_ID> --finalize 一旦确认新版本稳定，移除为回滚保留的旧版本。

#### 构建并发布应用

您可以构建自己的应用。该过程将生成 *.oakapp 文件，可安装到其他设备上：

```bash
oakctl app build ./folder/app/src
```

默认情况下，oakctl app build 使用提供的应用目录中的 oakapp.toml 文件。 使用 --oakapp-file 选择备用配置文件（例如 oakapp.dev.toml 或
oakapp.prod.toml），只要该文件位于相同的应用目录树内即可。

可选标志：

 * -d, --device [<DEVICE>] - 使用 IP、IP:PORT、索引或 oakctl list 中的序列号指定设备
 * -k, --keep - 构建后保留容器（如果您希望构建后立即使用 oakctl app start 启动它，则很有用）
 * -p, --publish - 构建后将应用发布到 Hub（该应用将出现在 Hub 的 [OAK4 Apps](https://hub.luxonis.com/control/apps/team-apps?tab=oak4) 中）
 * -O, --optimized - 以优化模式构建应用（体积更小，构建时间更长）
 * -U, --update-description - 如果应用已发布，则更新 Hub 上的应用描述
 * -N, --no-download - 构建后不将 *.oakapp 文件下载到主机
 * -o, --output [<PATH>] - 构建的 *.oakapp 文件应下载到的目录路径
 * --oakapp-file [<PATH>] - 从提供的应用目录树中使用备用的 oakapp.toml 文件
 * --preserve-line-endings - 禁用将文件上传到设备前的自动 CRLF 转 LF 转换

如果您发布应用，则需要在 oakapp.toml 文件中指定应用的标识符和唯一版本。

### Hub 连接

对于某些操作，您需要将设备连接到 Hub。您可以使用以下命令执行此操作：

```bash
oakctl hub login
```

登录后，您可以使用以下命令检查连接状态：

```bash
oakctl hub status
```

要更改活动团队，请再次运行 oakctl hub login 并在 Web 登录流程中选择团队。 oakctl hub switch-team 是 oakctl hub login 的别名。

使用以下命令注销：

```bash
oakctl hub logout
```

登录后，您可以发布构建好的 *.oakapp 包：

```bash
oakctl hub publish <oakapp-file>
```

您还可以运行需要 Hub 令牌才能下载模型等的本地脚本：

```bash
oakctl hub run-script <script-file>
```

### 设备管理

#### 设备信息

设备信息命令将显示设备信息，例如操作系统版本、代理版本、架构、序列号和型号。

```bash
$ oakctl device info
Device Info:
  OS:                   Luxonis OS RVC4 1.4.0
  Agent Version:        2.0.11 (rvc4)
  Architecture:         linux/arm64
  Serial Number:        2713799968
  Model:                Luxonis, Inc. KalamaP OAK4-D R1
```

使用 --check_internet 标志也可检查互联网连接。

#### 设备更新

可以使用以下命令更新设备 - CHANNEL 的可能值为 stable 和 beta：

```bash
oakctl device update --channel=[CHANNEL]
```

此命令将检查最新的可用版本，如果版本不同，则更新设备。

您可以使用 --select 标志选择要更新到的特定版本：

```bash
oakctl device update --select --channel=[CHANNEL]
```

或者，您可以使用 --url 或 --path 标志直接提供更新 URL 或本地文件路径：

```bash
oakctl device update --url=[URL]
oakctl device update --path=[PATH]
```

更新命令仅在设备连接到互联网时有效。 如果您的设备未连接，可以使用 --local 标志将文件下载到本地计算机，然后在下载完成后将其传输到设备：

```bash
oakctl device update --local
```

--local 标志也可以与 --url 或其他标志结合使用。

#### 设备重启

您可以使用 oakctl 重启设备：

```bash
oakctl device reboot
```

此命令将重启设备并等待其重新上线。

#### 设备解锁

新设备默认禁用 SSH 访问。您可以使用以下命令启用它：

```bash
oakctl device unlock
```

此命令将提示您设置密码。

#### 设备刷写

您可以使用以下命令（仅限 USB）将新 Luxonis OS 镜像刷写到设备：

```bash
oakctl device flash [<path-to-tar.xz>]
```

*.tar.xz
镜像路径是可选的；如果未指定，将下载并刷写最新的稳定版本镜像。有关刷写和操作系统更新的详细说明，请参见[更新操作系统页面](https://docs.luxonis.com/software-v3/sw-stack/luxonis-os.md)。

## oak-agent

oak-agent 是在 OAK4 设备上运行并管理容器的服务。它负责：

 * 管理应用，运行容器
 * 与 oakctl 工具通信
 * 与 Hub（云平台）通信

### 容器

oak-agent 使用 runc 作为轻量级容器运行时，这也是 Docker/Podman 使用的。我们选择 runc 是因为它轻量级，并能完全控制容器生命周期。

## oakctl 和 oak-agent 的发布说明

### oakctl v0.23.0（2026年5月29日）

#### 新增

 * 为 oakctl 添加了分析功能。可通过 OAKCTL_ANALYTICS=0 环境变量禁用分析。

#### 变更

 * 移除了 oakctl app install --update 的弃用提示。

### oakctl v0.22.0（2026年5月21日）

#### 新增

 * oakctl app examples list 用于显示可用的 OAK 应用示例，oakctl app examples clone <path> 用于在本地下载 oak-examples 仓库。
 * 通过 oakctl usbd 支持 USB 互联网共享。
   * 新增 oakctl-usbd 后台服务，用于通过 USB 与 OAK 设备共享主机的互联网连接。
   * 新增 oakctl usbd <status|enable|disable> 用于管理 oakctl-usbd 服务。

#### 变更

 * 移除了 oakctl list、oakctl connect、oakctl setup、oakctl setup-b64 和 oakctl start-setup 的弃用通知。
 * 使 oakctl list 和 oakctl connect 在帮助输出中再次可见。

### agent v0.19.1，oakctl v0.20.0（2026年5月5日）

#### 修复

 * oakctl adb 有时会以退出码 255 退出，即使 adb 命令成功执行。
 * oakctl 在运行 self-update 时不再显示新版本可用提示。

#### 新增

 * oakctl device update --url/--path 用于从 URL 或本地文件更新 RVC 设备操作系统。
 * oakctl device update --local 用于在本地下载操作系统更新。
 * oakctl device connection get|unset 用于在使用 oakctl connect 后检查或清除当前设备连接。

#### 变更

 * oakctl CLI 一致性：
   * oakctl list 移至 oakctl device list（旧命令已隐藏并显示弃用通知）。
   * oakctl connect 移至 oakctl device connection set（旧命令已隐藏并显示弃用通知）。
   * oakctl start-setup 移至 oakctl device setup start（旧命令已隐藏并显示弃用通知）。
   * oakctl setup 移至 oakctl device setup apply（旧命令已隐藏并显示弃用通知）。
   * oakctl device flash 更名为 oakctl device reflash（旧命令已隐藏并显示弃用通知）。
   * oakctl app delete 更名为 oakctl app uninstall（旧命令已隐藏并显示弃用通知）。
   * oakctl hub publish 移至 oakctl app publish（旧命令已隐藏并显示弃用通知）。
   * oakctl app config <app_id> set|get 重构为 oakctl app config set|get <app_id>。
   * oakctl setup-b64 移至 oakctl device setup apply-b64（旧命令已隐藏并显示弃用通知）。
 * 扩展了所有 oakctl 的 --help 输出。帮助现在包含示例命令和核心概念的简短说明。

### agent v0.18.6，oakctl v0.18.6（2026年4月8日）

#### 新增

 * oakctl app install 现在在安装后删除不必要的文件以节省存储空间。
 * oakctl app prune-storage 现在也移除安装和下载文件。
 * oakctl app delete 现在也删除格式错误的应用。
 * oakctl device info 现在显示：
   * 日期
   * 网络信息
   * 用于下载构建应用时的 HTTP API 端口
 * oakctl app run 在构建步骤中命令失败时不再重建整个应用；而是保留成功构建的层。
 * 新增 oakapp.toml 定义：
   * 添加了 shell 字段，用于指定 RUN 命令、build_steps 和 entrypoint 的默认 shell（默认值：["/bin/sh", "-c"]）。
   * 命令现在可以以 shell 形式（字符串）或 exec 形式（字符串数组）编写。
   * 移除了基于 shlex 的解析，因为它不支持重定向和管道等结构。
   * 添加了可选的 arg 部分用于构建时环境变量，以及 oakctl app build 和 oakctl app run 的 --build-arg 标志。
   * 添加了可选的 additional_build_mounts 部分，用于仅在构建阶段使用的挂载。
 * 所有 Hub 服务端点现在可以通过 API 配置。
   * 在 oakctl 中使用 OAKCTL_HUB_API_URL。
   * 在 oak-agent 的 setup-utility 配置中使用 api=。

#### 变更

 * 将 oakctl 中选择 Hub 服务环境的环境变量从 ENV 重命名为 OAKCTL_ENV。

### agent v0.18.3, oakctl v0.18.2（2026年2月17日）

#### 新增

 * 在 oakctl app build 和 oakctl app run 命令中报告镜像下载进度。
 * 自托管 agent 的 GPU 支持，启用后允许 oak 应用使用主机的 Nvidia GPU。
 * oakctl app [build|run] --oakapp-file [PATH] 参数，用于指定 oakapp.toml 文件的自定义路径。
 * oakctl app logs --no-follow 参数，避免附加到日志流。
 * 通过 Luxonis Hub 的远程命令执行。
 * 用新的子命令 oakctl setup-b64 替换了 oakctl setup 的 --*-b64 参数。
 * 拒绝 oakapp.toml 挂载字段中的相对路径：required_mounts、required_devices、optional_mounts、optional_devices、additional_mounts。
 * oakctl device update 支持自托管 oak-agent。

#### 修复

 * 修复在尝试使用具有许多层（约30层）的基础镜像时 oakctl app build 出现的 Failed to mount overlayfs 错误。
 * 修复 jetson 上的 oakctl adb 问题。
 * 为 oakctl self-update 添加了 QDL udev 规则的安装。
   * 修复了 oakctl device flash 过程中某些情况下出现的 qdl: Failed to open device 错误。
 * 修复在编辑 oakapp.toml 后第二次运行应用时容器中缺少 /etc/resolv.conf 的问题。

### agent v0.17.4, oakctl v0.17.4（2026年1月15日）

#### 新增

 * oakctl app build --output [PATH] 参数，用于指定构建后下载 *.oakapp 文件的位置。
 * oakctl setup 命令的 Base64 编码参数，确保 Hub 生成的设置命令中特殊字符的跨 OS 兼容转义。
 * 每天自动检查一次新的 oakctl 版本。
 * oakctl app prune-storage 命令，用于删除不必要的应用文件。
 * oakctl app list --long 标志，显示完整应用 ID。
 * oak-agent 测试 WebRTC 连接。
 * oakctl device info 和 oakctl list 显示采纳状态。
 * Welcome ACK 恢复出厂设置支持。
 * oakctl app install 默认更新现有应用。
   * 可通过 --force-new 标志修改为安装为新应用。
   * 可通过 --update 标志修改为更新特定现有应用。
 * oakctl app install --env / --env-file 在应用安装时设置环境变量。
 * 多平台自托管 Docker 镜像。

#### 变更

 * oakctl app list 现在默认将应用 ID 截断为 3 个字符显示；当存在冲突时自动增加截断长度。
 * oak-agent 保存新的应用构建元数据。
 * 自托管 WebRTC 终端打开主机 shell 而非容器 shell。
 * oakctl app <ACTION> <app-id> 命令现在接受完整和截断的应用 ID。

#### 修复

 * 修复如果在单次读取中接收到多条消息，OTA 更新状态反序列化失败的问题。
 * Windows 上的 oakctl 自动将 CRLF 转换为 LF。
 * 自托管 oak-agent/oakctl 修复：
   * 修复 oakctl device info --check_internet
   * 修复 oakctl device reboot
   * 修复 oakctl setup 命令（在某些设备如 RPi 上失败）
   * 缺少 ARM 设备的 adb 二进制文件（错误地包含了 x86 二进制文件）
 * 修复使用 OAKCTL_HUB_TOKEN 发布应用上传的问题。
 * 修复 macOS ARM 和 Windows 的 oakctl 构建未签名的问题。
 * 修复 oak-agent 的构建缓存失效问题。
 * 修复 oakctl 未正确保存和/或读取 sn-data.json 的问题。

#### 弃用

 * 环境变量 OAKCTL_HUB_LOGIN_TOKEN 重命名为 OAKCTL_HUB_TOKEN。
 * oakctl hub switch-team 现在是 oakctl hub login 的别名（团队通过 Web 登录流程选择）。
 * --enabled 标志的短选项从 -e 改为 -E。

### oakctl v0.16.5（2025年12月15日）

#### 新增

 * oakctl device update 新增 --yes 标志用于非交互模式。
 * oakctl device update 新增 --check_internet 标志，用于在更新前检查网络连接。
 * 启用 oakctl viewer 命令以打开 Viewer。
 * oakctl app build 新增 --no-download 标志，用于跳过将构建好的 *.oakapp 文件下载到主机；适用于测试。
 * oakctl app start 新增 --enable 和 --disable-others 标志。

#### 变更

 * oakctl hub login 现在使用基于 Web 的身份验证流程。
 * 为集成到 Viewer 进行了小幅改进。
