# @coda-console/host 中文文档

规范地址：https://npmcn.com/package/%40coda-console/host/

版本：2\.0\.1
npm 元数据更新时间：2026-11-08T06:02:52.236Z
中文译文更新时间：2026-11-10T07:54:05.705Z
软件包许可：MIT

本文由上游 README 翻译，可能存在偏差，请结合原文核对 API、命令与配置。版权与许可归原作者所有。

原始 npm 页面：https://www.npmjs.com/package/%40coda-console/host
原文对照：https://npmcn.com/package/%40coda-console/host/?lang=original
项目主页：https://github.com/coda-console/coda-plugins#readme
源码仓库：https://github.com/coda-console/coda-plugins

## 关键信息（来自 npm 元数据）

- 包体积（解压后）：93.9 kB，27 个文件
- 模块格式：ES Module
- TypeScript 类型：未声明
- 运行依赖：0 个
- 首次发布：2026-10-27T05:36:39.674Z
- 最新版本发布：2026-11-08T06:02:51.883Z
- 近 30 天下载量：2257（2026-10-10 至 2026-11-08，npm 官方下载统计）
- 已发布版本数：13（近 12 个月 13 个）

## 中文简介

本机运行器与 Xcode、模拟器的排队名额

## 安装

```sh
npm install @coda-console/host
```

也可以使用：`pnpm add @coda-console/host`、`yarn add @coda-console/host`、`bun add @coda-console/host`

## README

# 宿主资源

## 运行器与执行环境

从 2.0.0 起，host 在清单里声明实现 `coda/executor@1` 与 `coda/run@1`，把自己这台机器的执行环境发布为一条 `coda/executor@1` 记录（ID `host`）。别的机器或 Coda 对这条记录调用 `execute`，输入照协议包 executor@1；host 在本机执行任务，并只写这次调用开出的 `coda/run@1` 运行：进行中是 `working` 带进展，结束写 `completed`、`failed`（带退出码、输出末尾、摘要与步骤输出）或 `canceled`。终态之后不再改。取消是对运行调用 `cancel`，交给 host 停下原 worker。

2.0.1：启动时先读上次运行留下的 `coda/executor@1` 记录，按它的 revision 接管；此前插件宿主重启后写这条记录会被 Coda 以 conflict 拒绝，host 模块载入失败。

配置意图、能力和名额在本机持有。`host.enabled` 控制是否接活，默认 true；`host.capacity` 配置名额数；`host.labels` 是逗号分隔的自定义标签。系统／CPU、Node、Xcode、可用模拟器 runtime 与图形会话由本机探测，每分钟刷新，不启动模拟器。容量和占用订阅 `concurrency-groups`，包括旧流程与 run-held，公布值不构成预留。停止接活不终止已接收的任务。

资源只接受 `{slots:1}` 或空对象；`exclusiveResources: ['slots']` 表示独占。host 将 slots 映射到本机组 host，开跑前通过同一名额账本最终准入，账本返回稳定名额序号。其他资源量不能模拟为多次申请。

**执行**：`execute` 的输入照协议包 executor@1：`source`（`git/snapshot@1`，或 `git/source@1` 加完整 commit）、`requirements.labels`、`resources`、`context`、`task`、`env`、`timeoutMs`、`cancelGraceMs`。源码经 Coda 的引用解析取得鉴权到达，不拼机器 ID 或 `/forward`；快照的 source ID 在其持有者范围内解释。代理鉴权头只传给本次 Git 子进程，不进入对象、日志、持久记录或 `.git/config`。

固定目录为 `<插件状态>/workspaces/<来源工作副本身份及 instance 的散列>/<账本名额序号>`，不同来源工作副本、不同名额隔离；快照或提交改变不会丢掉同源缓存。fetch 后校验 commit，执行 `reset --hard` 和 `clean -fd`，保留被忽略缓存。cwd 必须在该工作根内，拒绝路径穿越和符号链接逃逸。没有源码的任务使用每次尝试的隔离目录。源码获取不改变发布／部署的准入政策。

**三段 hook 与恢复**：`prepare_job / run_script_step / cleanup_job` 使用原 JSON envelope，prepare 的 state 即使伴随失败也会收尾；命令退出码原样返回，cleanup 失败记日志。声明变量与执行器变量分开，取消先停止当前进程组、在收尾期限内 cleanup，确认完成后才释放名额。每次尝试在启动前持久记账；独立 worker 继续运行，不随插件宿主重启而重复启动。重启后跟随已有 worker；worker 不可达且无最终记录时在运行进展里写 `result_unknown`，保留名额，绝不把丢失响应当作安全重试。取消通过带随机 token 的 loopback 控制通道交给原 worker，不对可能已被复用的 PID 盲发信号。

运行本身是这次执行的持久结果引用，日志与结果留在插件状态的 `attempts/<运行键>/` 下；结果保留有界 outputs、summary 和日志末尾。产物字节传输留给 artifacts 插件。

**验证**：纯规则、Git 目录、hook、取消竞态与重启记录在 `test/plugins/host/`；公共形状直接用 host 安装的协议包和同步的 provider 校验器测试。真实 Coda 存储／节点镜像／资源账本装配留 Coda 仓库验证。包含 host-sim 的完整插件测试经会话的 `workflow_run` → `run-held` 运行，不启动真实模拟器。

## 现有持锁执行与模拟器工具

这个后端实例的 Apple 模拟器、Xcode 构建和项目要求排队的命令共用一组名额，会话里的这类命令经这个插件排队与执行。名额是流程引擎的[并发组](https://github.com/coda-console/coda/blob/main/docs/workflows.md#%E5%B9%B6%E5%8F%91%E7%BB%84) `host`， 每个[后端实例](https://github.com/coda-console/coda/blob/main/docs/deployment.md#%E5%90%8E%E7%AB%AF%E5%AE%9E%E4%BE%8B)各有一组，同一台 Mac 上的两个实例互不相干；插件只说它装的是什么、能同时进几个，Coda 内核不认识它。

从 Coda 的商店安装（npm 包 `@coda-console/host`，分类「机器与资源」），不随 Coda 默认装上；没装时流程步骤写 `coda:group="host"` 照样排队，容量按 1， 会话里也没有 `host-sim`。用法给 Agent 的部分在 Skill `host-slots`（`skills/host-slots/SKILL.md`）。

**范围**：插件的说明让会话的 Agent 把三类命令交给持锁执行：Xcode 构建、用 Apple 模拟器的命令、会调用 `host-sim` 的项目脚本。 别的命令（其他项目的构建、测试、静态检查）直接执行，不因为是构建就排队；项目要别的命令也排队，就在项目说明（如 `AGENTS.md`）里写明， 或让脚本经 `host-sim` 自己排队（见下文）。流程步骤占不占名额由流程自己用 `coda:group="host"` 声明。

**入口**：会话的 Agent 用 `workflow_run` 调用能力 `run-held`（持锁执行），`inputs` 给 `command`（要执行的命令，由 `$SHELL -c` 执行）、 `cwd`（在哪个目录执行，绝对路径），用模拟器时 `what` 为 `模拟器`（默认 `构建`）。能力写了 `coda:inline` 与 `coda:subagents="false"`： 结束得快就随这次调用返回结果，否则结果之后送回会话（见 [Agent 工具](https://github.com/coda-console/coda/blob/main/docs/workflows.md#agent-%E5%B7%A5%E5%85%B7)）；原生子 Agent 发起时拒绝， 提示把这一步交回主 Agent 或用 `spawn` 派 Coda 子会话（见[流程属性](https://github.com/coda-console/coda/blob/main/docs/workflows.md#%E6%B5%81%E7%A8%8B%E5%B1%9E%E6%80%A7)）。从命令行（`coda-run`）发起同样拒绝：命令行说不出是会话里谁在要。

`host-sim` 是这个插件放在会话 `PATH` 上的[命令](https://github.com/coda-console/coda/blob/main/docs/plugins.md#%E5%91%BD%E4%BB%A4)（`scripts/host-sim.mjs`），只在已占着的名额里用：持锁执行的命令里（`HOST_SIM_LEASE`）， 或占着组 `host` 的流程步骤里（`CODA_GROUPS`）。在会话里不在名额中调用 `host-sim run --kind build|simulator -- <命令>` 时拒绝（退出码 2）， 提示改由 Agent 用 `workflow_run` 调用 `run-held` 执行这条命令；所以经 `host-sim` 自己排队的项目脚本（`scripts/check.sh` 的编译范围、iOS 构建与测试入口） 在会话里整条交给 `run-held` 执行，脚本本身不必改写。别的 `--kind`（测试机的设备名）交给接在本插件扩展点 `kinds` 上的插件（[测试机](https://npmcn.com/package/%40coda-console/testbed/README.md)）。

**总时限**：`what=模拟器` 默认 60 分钟，其他 `run-held` 默认 30 分钟；从取得名额、进入执行步骤开始计时，不含排队。 长批次在 `inputs` 显式给 `timeoutMinutes`，如 `{ "command": "bash scripts/ios-ui-test.sh", "cwd": "/path/to/project", "what": "模拟器", "timeoutMinutes": 120 }`。 `timeoutMinutes=0`（默认）按种类取值；显式值须大于 0、最多 1440 分钟。用引擎的中断计时器取消整组进程，保留 5 分钟清理宽限，之后才释放名额。

**无输出上限**：`idleMinutes` 默认 10 分钟，从命令启动起，stdout 或 stderr 每次有新输出就重新计时；连续到限无输出即失败，先向整组进程发送 TERM（连同命令另起进程组的子进程，如 `swift build` 的编译器），最多留 5 分钟收尾。可在 `inputs` 设 `idleMinutes: 30` 调高，或 `idleMinutes: 0` 关闭（总时限仍生效）。失败结果与会话通知区分「执行超过总时限」「N 分钟无输出」「命令非零退出（退出码 N）」；输出 `failure_reason` 记录原因。run-held 因无输出或总时限失败、执行器超时时，若 macOS 上检测到 `SecurityAgent` 进程，失败原因会追加钥匙串授权框提示，见 [host-slots](https://npmcn.com/package/%40coda-console/host/skills/host-slots/SKILL.md#%E6%97%A0%E4%BA%BA%E5%80%BC%E5%AE%88%E6%9E%84%E5%BB%BA)。

**过程**（`.coda/capabilities/run-held.bpmn`）：

1.  「等名额」：`hold-group` 为「占用名额」这一段排进 `host`，容量读设置 `host.capacity`；状态行写「排队等构建名额 · 排第 N 位」或「排队等模拟器名额 · 排第 N 位」， 队伍一动就改，会话底部显示这项等待。
2.  「执行」：在 `cwd` 里、带项目的环境（同流程步骤，不含插件与执行器自己的变量）执行命令（`scripts/run-held.mjs`），附上 `HOST_SIM_LEASE=coda-run:<种类>`： 命令里的项目脚本再调用 `host-sim run` 时直接执行，`host-sim check` 通过，`host-sim bind <udid>` 把它启动的模拟器登记给这一步。 `cwd` 不是存在的目录的绝对路径时不执行，退出码记为 2。 命令结束（含被取消）时关机并删除登记的模拟器，命令的退出码记为输出 `exit_code`；命令失败这一步也失败，Agent 收到的结果带着输出的末尾。 被取消时整组进程收到 TERM，这一步的脚本以 `exec` 运行，执行器等它删完模拟器才结束；收尾超过取消时限被强杀而留下的模拟器，由下面的定时清点删除。
3.  离开「占用名额」这一段才放开名额，排在后面的随即进入。

**执行与中断**：命令由 Coda 的步骤执行器运行，不随 Agent 的调用结束：调用先返回了，命令照常执行，结果送回会话； 要停下它用 `workflow_cancel`。命令不经过会话 Agent 自带的沙箱。

**定时清点**（`.coda/workflows/clean-simulators.bpmn`「清点模拟器」，`scripts/clean-simulators.mjs`）：`host-sim bind <udid>` 除了登记给这一步， 还把 UDID 和登记运行 ID 写进本实例插件状态的 `simulator-registrations/`，每条登记独立保存，清理不会覆盖同时新增的登记。 持锁命令通过 `HOST_SIM_RUN_ID` 传递运行 ID；普通流程步骤读 `GITHUB_RUN_ID`。 按设置 `host.cleanSchedule` 的周期清点（整个实例只跑一次，`coda:scope="machine"`），**不申请名额、不进入 host 队列**。 清理脚本通过本插件模块的带进程令牌的本机只读通道查询；模块每次经公开 `coda.api("workflow-runs/<id>")` 读取运行终态，不缓存清理资格。 运行须为成功、失败或已取消才删除；运行与子运行是否收尾完成、何时发布终态，由 Coda 运行模块判断。插件不读取宿主数据库或复刻名额收尾逻辑。 运行中、取消收尾中、查不到运行、宿主接口不可用的都跳过。旧版 `simulators` 纯 UDID 登记没有归属，保留并记录原因，待人工确认闲置后处理。 删除直接交给 `simctl delete`，不强制关机；使用中或删不掉的跳过、记日志并保留登记，下次再试，已经不在的算删完。不为删除与其他进程使用的罕见交错加锁。 没经 `host-sim bind` 登记的模拟器不删：用户自己建的，同一用户另一个后端实例的。 只有这个实例自己的正式后端（`CODA_ARBITER`，[插件进程的环境](https://github.com/coda-console/coda/blob/main/docs/plugins.md#%E6%8F%92%E4%BB%B6%E8%BF%9B%E7%A8%8B%E7%9A%84%E7%8E%AF%E5%A2%83)）真正删除，测试或开发起的后端跑这条流程什么也不删。

**会话之外**：终端里没有这个命令；从会话以外的地方调用插件的 `host-sim` 不排队，也不执行，提示只能在 Coda 的会话或流程步骤里用； 没有占着组的流程步骤里调用同样拒绝，提示写 `coda:group="host"`。

**资源**「构建名额」（类型 `slots`）：从 `CODA_GROUPS_STATE` 读组 `host`，一行：读数「空闲」画空闲名额（空几个、共几个）， 接着每个占着的写运行标题、所属会话和占了多久（借用中、正在收尾、占满另外注明），每个排着的写排第几、等了多久，合计至多 8 个读数。打开或刷新资源页时读，不在后台采集。 容量在「设置 › 插件」里改。

**设置**：`host.capacity`「名额数」，1–8，默认 1；`host.cleanSchedule`「清点模拟器的周期」，ISO 8601 重复间隔，默认 `R/P1D`（每天一次）。 插件以 `contributes.groups` 声明组 `host` 的容量就是 `host.capacity`，所以项目流程的步骤写 `coda:group="host"` 也按这个容量排；步骤里看到 `CODA_GROUPS` 含 `host` 时，`host-sim` 同样视作已占着名额（`run` 直接执行、`check` 通过，`bind` 只记进本实例的 `simulator-registrations/`，由定时清点删除）。`run` 直接执行的命令带上 `HOST_SIM_LEASE=coda-group:host`，所以只看有没有 `HOST_SIM_LEASE` 的项目脚本在步骤里多走一次就照常往下，不会反复调用自己。项目脚本判断自己在不在名额里应问 `host-sim check`（Skill `host-slots`）：测试机上执行的命令也带 `HOST_SIM_LEASE`，占的却不是构建名额。已占着名额只说构建与模拟器，测试机的设备名另由[测试机插件](https://npmcn.com/package/%40coda-console/testbed/README.md)借用：在构建名额里用设备名调用，同样交给测试机插件，只借给正在做那台设备任务的会话。

**扩展点** `kinds`：`host-sim` 遇到 `build`、`simulator` 之外的 `--kind`，把整条命令交给接在这里的第一个命令（按插件 ID 排），参数原样附在后面； 那条命令以接上它的插件的 `CODA_PLUGIN_PATH`、`CODA_PLUGIN_STATE` 运行，退出码就是 `host-sim` 的。没有插件接上时拒绝，提示装测试机插件。

-   装上后对这台机器的所有项目生效（[安装、停用与卸载](https://github.com/coda-console/coda/blob/main/docs/plugins.md#%E5%AE%89%E8%A3%85%E5%81%9C%E7%94%A8%E4%B8%8E%E5%8D%B8%E8%BD%BD)），可按 Tag 限定或在项目里停用。

## 常见问题

### @coda-console/host 是什么？

本机运行器与 Xcode、模拟器的排队名额

### 如何安装 @coda-console/host？

在项目目录运行 npm install @coda-console/host，或使用 pnpm add / yarn add / bun add。

### @coda-console/host 支持 TypeScript 吗？

该包的 package\.json 没有声明类型入口。需要类型提示时，可以在 npm 上查找社区维护的 @types/coda-console\_\_host 是否存在。

### @coda-console/host 支持 ESM 还是 CommonJS？

以 ES Module 形式发布，推荐使用 import 引入。在 CommonJS 项目中，Node\.js 20\.19\+ / 22\.12\+ 可以直接 require\(\) 不含顶层 await 的 ES Module，更早的版本需要改用动态 import\(\)。

### @coda-console/host 可以商用吗？

@coda-console/host 使用 MIT 许可。宽松许可：允许商业使用、修改和再分发，通常只需在分发时保留原版权与许可声明。以上为概括，不构成法律意见，请以许可原文为准。

### @coda-console/host 的下载量有多少？

根据 npm 官方下载统计，@coda-console/host 在 2026/10/10 – 2026/11/08 这 30 天内被下载 2,257 次，日均约 75 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入，适合横向比较热度，不能直接等同于使用人数。

### @coda-console/host 还在维护吗？

最新版本 2\.0\.1 发布于 2026/11/08（2 天前）；至今共发布 13 个版本，其中过去 12 个月发布了 13 个；首次发布于 2026/10/27。发布频率只是参考信号：功能稳定的工具可能很少需要更新，建议结合源码仓库的 issue 和提交记录判断。

