node 控制能力升级:缺 provider/allocation 子命令、窗口与运行态不可见、输出不可机读 #3

Open
opened 2026-09-27 02:04:14 +08:00 by xm · 5 comments
Owner

背景

近两天做平台问题定位/验证时,凡涉及「受管运维动作」几乎都绕不开 CLI 的能力缺口,最后只能手写 REST 调用与直连只读数据库。可以用,但不好用:同一件事在 CLI 里没有入口,或入口给了错误/不完整的信息。以下全部为实测(NAS 节点,2026-09-26/27)。

缺口清单(含实测证据)

1. 没有受管运维子命令:provider / allocation(租约)

  • 顶层命令只有 capability|task|workflow|template|resource|resources|runtime|node,无 provider、无租约/allocation。
  • 实测:yuvu provider list → error: usage: yuvu <capability|task|workflow|template|resource|resources|runtime|node> ...
  • 后果:运维动作只能手写 REST —— PATCH /api/v1/providers/{id}(base_url / unpin_base_url)、POST /api/v1/task-allocations/{leaseID}/release(释放卡死租约)、provider control/restart。这些正是排查 execution target registration cannot change... 一类故障的必需动作。
  • 期望:yuvu provider list|get|patch(含 --pin-base-url / --unpin-base-url)、yuvu provider restart、yuvu allocation list|show|release|renew。

2. 看不到 capability 并发窗口与排队

  • 窗口是 capability 级(max_concurrency,opencode 原为 1、现已调到 3)。CLI 无任何入口查看「谁占着窗口、有多少在 ready 排队」。
  • 后果:排查「任务被 max_inflight_reached 拒绝」只能直连 DB 查 task_invocations / 并发字段。
  • 期望:yuvu capability windows [-capability X](running/limit/ready 列表 + 占用者 task id)。

3. node deployment show 缺运行态,只有声明 tag

  • show core -node nas -format json 给出 image: docker.lib.zuixue.site/yuvu/core:latest(tag,非 digest)、plan_hash、resolved_plan(env/labels/mounts),但没有:解析后的镜像 digest、容器运行态(running/exited、started_at、health)、实例数。
  • upgrade --dry-run 的 plan 里 previous_image == next_image == ...:latest → 无法判断本次部署是否真的会换构建。
  • 后果:"线上跑的是哪个构建"必须查注册表/DB/docker inspect 侧推(本次排查 #9/#17/#13 都靠这种方式)。
  • 期望:新增 resolved_image_digest、runtime_state{status,started_at,health}、instances[];dry-run plan 输出解析后的 digest 并标注 no change。

4. node status / node instance-info 参数语义与报错不可诊断

  • 实测:yuvu node status -node nas → error: node status requires at least one instance id;yuvu node instance-info -node nas 同。
  • 但 node deployment show core 用的是 deployment id,两处一个要 instance id 一个要 deployment id,命名未区分。
  • 期望:帮助里给用法示例;错误信息附 例: yuvu node status core;统一 --deployment/--instance 参数名。

5. node logs 是裸 envelope,无 tail/follow

  • 实测输出形如 { "action": "INSTANCE_ACTION_LOGS", "extensions": {...} },需手工剥壳;-format json|yaml 存在但内容仍是 envelope。
  • 且对 provider_adapter 报 provider instance is not managed by agent(见 yuvuAdapter 侧问题)。
  • 期望:-tail N / -since / -follow / -stderr,默认直接给人看日志文本。

6. --output 与 --format 命名分裂 + 状态值大小写不一致

  • task run|get:-output full|id|status|runtime|allocation(选字段)与 -format yaml|json(选格式)并存;node * 只有 -format。
  • task ... -output status 返回大写枚举 TASK_STATUS_SUCCEEDED。我方轮询脚本按小写匹配导致永不退出(空转到超时)。
  • 期望:统一 -format json|yaml,字段选择改 -fields;状态值统一小写或文档明确枚举表。

7. 凭据/前置条件缺失时错误不可诊断

  • 未导出节点凭据时:yuvu node deployment show core -node nas → unknown node target "nas"(实际原因:缺 YUVU_NODE_* 等 env)。
  • yuvu node self plan → request violates whitelist: image "" is not allowed(实际原因:节点自身不在 deployment 列表)。
  • 期望:区分「未配置的目标」与「凭据无效」,并列出缺失的变量名;whitelist 报错附被判定的实际值。

8. 错误没有分类/退出码约定

  • 统一 error: <文本>,脚本无法区分 404 / 凭据 / whitelist / 超时(remote runtime unavailable 曾以 exit 125 出现)。
  • 期望:error_class + 稳定退出码表。

期望的总目标

让「受管运维」在 CLI 内闭环:看得到(窗口/运行态/digest/日志)→ 改得动(provider pin、租约释放、受管重启)→ 机读稳定(-format json + 错误分类 + 退出码),不再需要手写 REST 和直连数据库。

最小复现(逐条)

export PATH="/home/zx/yuvu/workspaces/bin:$PATH"; source /tmp/node-env.sh
yuvu provider list                                   # 1) 无此子命令
yuvu node deployment show core -node nas -format json | head -c 200   # 3) 只有 tag,无 digest/运行态
yuvu node status -node nas                           # 4) requires at least one instance id
yuvu node logs core | head -3                        # 5) 裸 action envelope
yuvu task get <id> -output status                    # 6) 返回 TASK_STATUS_SUCCEEDED(大写)
yuvu node deployment show core -node missing         # 7) unknown node target "missing"(无缺失变量提示)

关联

  • 本仓 yuvuCli#1(node exec 够不到 runtime 容器)
  • yuvuCore#16(task cancel 返回 internal_bug)、yuvuCore#18(base_url pin 被部署覆盖)
  • yuvuAdapter#15(provider_adapter 无容器日志/冒烟覆盖)
## 背景 近两天做平台问题定位/验证时,凡涉及「受管运维动作」几乎都绕不开 CLI 的能力缺口,最后只能手写 REST 调用与直连只读数据库。**可以用,但不好用**:同一件事在 CLI 里没有入口,或入口给了错误/不完整的信息。以下全部为实测(NAS 节点,2026-09-26/27)。 ## 缺口清单(含实测证据) **1. 没有受管运维子命令:provider / allocation(租约)** - 顶层命令只有 `capability|task|workflow|template|resource|resources|runtime|node`,**无 `provider`、无租约/allocation**。 - 实测:`yuvu provider list` → `error: usage: yuvu <capability|task|workflow|template|resource|resources|runtime|node> ...` - 后果:运维动作只能手写 REST —— `PATCH /api/v1/providers/{id}`(base_url / `unpin_base_url`)、`POST /api/v1/task-allocations/{leaseID}/release`(释放卡死租约)、provider `control/restart`。这些正是排查 `execution target registration cannot change...` 一类故障的必需动作。 - 期望:`yuvu provider list|get|patch`(含 `--pin-base-url` / `--unpin-base-url`)、`yuvu provider restart`、`yuvu allocation list|show|release|renew`。 **2. 看不到 capability 并发窗口与排队** - 窗口是 capability 级(`max_concurrency`,opencode 原为 1、现已调到 3)。CLI 无任何入口查看「谁占着窗口、有多少在 ready 排队」。 - 后果:排查「任务被 `max_inflight_reached` 拒绝」只能直连 DB 查 `task_invocations` / 并发字段。 - 期望:`yuvu capability windows [-capability X]`(running/limit/ready 列表 + 占用者 task id)。 **3. `node deployment show` 缺运行态,只有声明 tag** - `show core -node nas -format json` 给出 `image: docker.lib.zuixue.site/yuvu/core:latest`(tag,非 digest)、`plan_hash`、`resolved_plan`(env/labels/mounts),但**没有**:解析后的镜像 digest、容器运行态(running/exited、started_at、health)、实例数。 - `upgrade --dry-run` 的 plan 里 `previous_image == next_image == ...:latest` → 无法判断本次部署是否真的会换构建。 - 后果:"线上跑的是哪个构建"必须查注册表/DB/`docker inspect` 侧推(本次排查 `#9/#17/#13` 都靠这种方式)。 - 期望:新增 `resolved_image_digest`、`runtime_state{status,started_at,health}`、`instances[]`;dry-run plan 输出解析后的 digest 并标注 `no change`。 **4. `node status` / `node instance-info` 参数语义与报错不可诊断** - 实测:`yuvu node status -node nas` → `error: node status requires at least one instance id`;`yuvu node instance-info -node nas` 同。 - 但 `node deployment show core` 用的是 **deployment id**,两处一个要 instance id 一个要 deployment id,命名未区分。 - 期望:帮助里给用法示例;错误信息附 `例: yuvu node status core`;统一 `--deployment/--instance` 参数名。 **5. `node logs` 是裸 envelope,无 tail/follow** - 实测输出形如 `{ "action": "INSTANCE_ACTION_LOGS", "extensions": {...} }`,需手工剥壳;`-format json|yaml` 存在但内容仍是 envelope。 - 且对 provider_adapter 报 `provider instance is not managed by agent`(见 yuvuAdapter 侧问题)。 - 期望:`-tail N` / `-since` / `-follow` / `-stderr`,默认直接给人看日志文本。 **6. `--output` 与 `--format` 命名分裂 + 状态值大小写不一致** - `task run|get`:`-output full|id|status|runtime|allocation`(**选字段**)与 `-format yaml|json`(**选格式**)并存;`node *` 只有 `-format`。 - `task ... -output status` 返回**大写枚举** `TASK_STATUS_SUCCEEDED`。我方轮询脚本按小写匹配导致永不退出(空转到超时)。 - 期望:统一 `-format json|yaml`,字段选择改 `-fields`;状态值统一小写或文档明确枚举表。 **7. 凭据/前置条件缺失时错误不可诊断** - 未导出节点凭据时:`yuvu node deployment show core -node nas` → `unknown node target "nas"`(实际原因:缺 `YUVU_NODE_*` 等 env)。 - `yuvu node self plan` → `request violates whitelist: image "" is not allowed`(实际原因:节点自身不在 deployment 列表)。 - 期望:区分「未配置的目标」与「凭据无效」,并列出缺失的变量名;whitelist 报错附被判定的实际值。 **8. 错误没有分类/退出码约定** - 统一 `error: <文本>`,脚本无法区分 404 / 凭据 / whitelist / 超时(`remote runtime unavailable` 曾以 exit 125 出现)。 - 期望:`error_class` + 稳定退出码表。 ## 期望的总目标 让「受管运维」在 CLI 内闭环:**看得到**(窗口/运行态/digest/日志)→ **改得动**(provider pin、租约释放、受管重启)→ **机读稳定**(`-format json` + 错误分类 + 退出码),不再需要手写 REST 和直连数据库。 ## 最小复现(逐条) ```bash export PATH="/home/zx/yuvu/workspaces/bin:$PATH"; source /tmp/node-env.sh yuvu provider list # 1) 无此子命令 yuvu node deployment show core -node nas -format json | head -c 200 # 3) 只有 tag,无 digest/运行态 yuvu node status -node nas # 4) requires at least one instance id yuvu node logs core | head -3 # 5) 裸 action envelope yuvu task get <id> -output status # 6) 返回 TASK_STATUS_SUCCEEDED(大写) yuvu node deployment show core -node missing # 7) unknown node target "missing"(无缺失变量提示) ``` ## 关联 - 本仓 yuvuCli#1(`node exec` 够不到 runtime 容器) - yuvuCore#16(task cancel 返回 `internal_bug`)、yuvuCore#18(base_url pin 被部署覆盖) - yuvuAdapter#15(provider_adapter 无容器日志/冒烟覆盖)
Author
Owner

补充:泄漏面比正文更广(2026-09-27 03:4x 追加实测)

正文只写了 yuvu node deployment show -format json 的 resolved_plan.env。刚又发现同一个泄漏也出现在 yuvu node deployment upgrade <dep> --dry-run 的 compose_preview:

$ yuvu node deployment upgrade core -node nas --dry-run
plan:
    changes:
        - none
    compose_preview: |-
        ...
                environment:
                    YUVU_MAA_ALLOCATION_SIGNING_KEY: <64 字符明文,与 show 中同一值>
                    YUVU_MAEND_ALLOCATION_SIGNING_KEY: <64 字符明文>
                    YUVU_CORE_ARTIFACT_S3_SECRET_KEY: <redacted>
                    YUVU_MAA_RPC_TOKEN: <redacted>

即:同一份 env 里遮蔽策略不一致(*_SIGNING_KEY 明文,*_SECRET_KEY / *_RPC_TOKEN 遮蔽),且所有会把 plan/compose 渲染出来的命令都带着它(至少 show 与 upgrade --dry-run)。

处置建议不变,但优先级提高:这是"只要跑一条只读命令就能拿到 Adapter V3 allocation 签名密钥"的暴露面,且签名密钥可用于伪造 allocation。若短时间内无法改代码,建议先把 YUVU_*_ALLOCATION_SIGNING_KEY 轮换一次(轮换后旧的泄漏值失效),再修遮蔽逻辑。

(本条不复制任何取值,只给形状与长度。)

## 补充:泄漏面比正文更广(2026-09-27 03:4x 追加实测) 正文只写了 `yuvu node deployment show -format json` 的 `resolved_plan.env`。刚又发现**同一个泄漏也出现在 `yuvu node deployment upgrade <dep> --dry-run` 的 `compose_preview`**: ``` $ yuvu node deployment upgrade core -node nas --dry-run plan: changes: - none compose_preview: |- ... environment: YUVU_MAA_ALLOCATION_SIGNING_KEY: <64 字符明文,与 show 中同一值> YUVU_MAEND_ALLOCATION_SIGNING_KEY: <64 字符明文> YUVU_CORE_ARTIFACT_S3_SECRET_KEY: <redacted> YUVU_MAA_RPC_TOKEN: <redacted> ``` 即:**同一份 env 里遮蔽策略不一致**(`*_SIGNING_KEY` 明文,`*_SECRET_KEY` / `*_RPC_TOKEN` 遮蔽),且**所有会把 plan/compose 渲染出来的命令都带着它**(至少 `show` 与 `upgrade --dry-run`)。 处置建议不变,但优先级提高:这是"只要跑一条只读命令就能拿到 Adapter V3 allocation 签名密钥"的暴露面,且签名密钥可用于伪造 allocation。若短时间内无法改代码,建议**先把 `YUVU_*_ALLOCATION_SIGNING_KEY` 轮换一次**(轮换后旧的泄漏值失效),再修遮蔽逻辑。 (本条不复制任何取值,只给形状与长度。)
xm added the
P2
label 2026-09-30 23:41:24 +08:00
Author
Owner

✅ 复验通过并已合入 main(合并提交 e591cfe,分支 fix/cli-3 tip 8d03ee6 保留)

门禁:lane worktree 与合并树各跑一次 make check,两次 rc=0(internal/cli 全部用例 ok)。

反向验证(变异必红 → 还原 sha256 一致 → 复绿)

  • 变异:taskStatusText() 从「小写 + 去 TASK_STATUS_ 前缀」改回直接 status.String()
  • 结果:--- FAIL: TestTaskStatusOutputIsLowercase → status_output_test.go:18: expected lowercase status, got "TASK_STATUS_SUCCEEDED\n"
  • 还原:sha256(internal/cli/task_output.go) 与变异前一致,工作树干净,复绿

(另一次尝试性变异「从 runtimeStateValue 删掉 running 键」没有让 TestNodeDeploymentShowIncludesRuntimeState 变红 → 该用例对 running 键的断言偏弱,建议后续补强。)

请验证这几点(真机)

  1. yuvu task output --output status 输出小写 succeeded,shell 脚本可直接比较;
  2. yuvu node deployment show <id> 输出稳定形状的 runtime_state(status/health/running/instance_id,+started_at)与 resolved_image_digest;runtime report 拉取失败时降级为 runtime_state.status=unavailable 且错误文本已脱敏,命令本身不整体失败;
  3. yuvu node deployment show --instance/--deployment 别名、--fields、allocation list|show|lease renew、capability 并发窗口在真机可用且报错可操作。
✅ 复验通过并已合入 `main`(合并提交 `e591cfe`,分支 `fix/cli-3` tip `8d03ee6` 保留) **门禁**:lane worktree 与合并树各跑一次 `make check`,两次 rc=0(`internal/cli` 全部用例 ok)。 **反向验证(变异必红 → 还原 sha256 一致 → 复绿)** - 变异:`taskStatusText()` 从「小写 + 去 `TASK_STATUS_` 前缀」改回直接 `status.String()` - 结果:`--- FAIL: TestTaskStatusOutputIsLowercase` → `status_output_test.go:18: expected lowercase status, got "TASK_STATUS_SUCCEEDED\n"` - 还原:`sha256(internal/cli/task_output.go)` 与变异前一致,工作树干净,复绿 (另一次尝试性变异「从 `runtimeStateValue` 删掉 `running` 键」**没有**让 `TestNodeDeploymentShowIncludesRuntimeState` 变红 → 该用例对 `running` 键的断言偏弱,建议后续补强。) **请验证这几点**(真机) 1. `yuvu task output --output status` 输出小写 `succeeded`,shell 脚本可直接比较; 2. `yuvu node deployment show <id>` 输出稳定形状的 `runtime_state`(status/health/running/instance_id,+started_at)与 `resolved_image_digest`;runtime report 拉取失败时降级为 `runtime_state.status=unavailable` 且错误文本已脱敏,命令本身不整体失败; 3. `yuvu node deployment show --instance/--deployment` 别名、`--fields`、`allocation list|show|lease renew`、capability 并发窗口在真机可用且报错可操作。
Author
Owner

独立复验 + 合并完成(issue 保持 open,等真机验收)

分支 fix/cli-3 tip f15c29f → 已合并 main = eb003ff(与 #1 的 ac7130a 同一条 main 序列,合并树门禁 rc=0)。

改动两处:① taskWatchEventSummary/taskWatchSummary 统一走 taskStatusText()(状态文本小写);② 新增「runtime_state 精确键集合」断言(2a3311c),使 digest/state 字段被改动的实现必红。

门禁

$ make check   -> rc=0(vendor snapshot / gofmt+vet / publish scope / go test ./... 全 ok)

反向验证

# task_observe.go: "status="+taskStatusText(event.GetStatus())  ->  "status="+event.GetStatus().String()
--- FAIL: TestTaskWatchStatusTextIsLowercase
    task_observe_test.go:167: watch event summary should use lowercase status,
        got "task_status sequence=1 status=TASK_STATUS_RUNNING"

还原后 sha256sum internal/cli/task_observe.go = b0351f41911f118c5637ef0ac2783c8bf83108ddcf0c950e33a49fba98bedf2a == git show HEAD: 同值 ⇒ 复绿 rc=0。

请验证这几点(真机)

  1. yuvu task watch/task get 的状态文本一律小写(running/succeeded…),脚本消费不必再大小写兼容;
  2. yuvu node deployment show <name> -format json 的 runtime_state 键集合稳定(新增/改名键会使测试红);
  3. 与 yuvuCli#1 的 node runtime exec 联合使用(会话→运行时容器→状态可观测)。
## 独立复验 + 合并完成(issue 保持 open,等真机验收) **分支** `fix/cli-3` tip `f15c29f` → 已合并 `main` = **`eb003ff`**(与 #1 的 `ac7130a` 同一条 main 序列,合并树门禁 rc=0)。 改动两处:① `taskWatchEventSummary`/`taskWatchSummary` 统一走 `taskStatusText()`(状态文本小写);② 新增「runtime_state 精确键集合」断言(`2a3311c`),使 digest/state 字段被改动的实现必红。 ### 门禁 ``` $ make check -> rc=0(vendor snapshot / gofmt+vet / publish scope / go test ./... 全 ok) ``` ### 反向验证 ``` # task_observe.go: "status="+taskStatusText(event.GetStatus()) -> "status="+event.GetStatus().String() --- FAIL: TestTaskWatchStatusTextIsLowercase task_observe_test.go:167: watch event summary should use lowercase status, got "task_status sequence=1 status=TASK_STATUS_RUNNING" ``` 还原后 `sha256sum internal/cli/task_observe.go` = `b0351f41911f118c5637ef0ac2783c8bf83108ddcf0c950e33a49fba98bedf2a` == `git show HEAD:` 同值 ⇒ 复绿 rc=0。 ### 请验证这几点(真机) 1. `yuvu task watch`/`task get` 的状态文本一律小写(`running`/`succeeded`…),脚本消费不必再大小写兼容; 2. `yuvu node deployment show <name> -format json` 的 `runtime_state` 键集合稳定(新增/改名键会使测试红); 3. 与 yuvuCli#1 的 `node runtime exec` 联合使用(会话→运行时容器→状态可观测)。
Author
Owner

补充新发现(2026-10-03):上述 CLI 运行态缺口在最新 main/已合入提交上仍需真机验收。对 MAA workflow 1a056746-5dca-51d7-aa9c-0b8a7cde19e6 实测,已成功任务仍返回 runtime.state/profile/session_id/provider_runtime_id/node_code 为 <nil>,而 selected_instance_id、selected_node_code、provider_trace_id 存在。请把 yuvuCore#41 作为跨仓关联,优先验证 execution identity 的统一投影。

补充新发现(2026-10-03):上述 CLI 运行态缺口在最新 main/已合入提交上仍需真机验收。对 MAA workflow `1a056746-5dca-51d7-aa9c-0b8a7cde19e6` 实测,已成功任务仍返回 runtime.state/profile/session_id/provider_runtime_id/node_code 为 `<nil>`,而 selected_instance_id、selected_node_code、provider_trace_id 存在。请把 `yuvuCore#41` 作为跨仓关联,优先验证 execution identity 的统一投影。
Author
Owner

当前 CLI 已补 provider/allocation、窗口/运行态、日志 tail/follow 与 help 等运维入口;go test ./... 通过,状态文本/运行态结构也有回归约束。但 issue 中错误分类/退出码统一和 NAS 全链路验收仍未完成;本轮记录为部分实现,保持 needs-fix/open,下一步补齐稳定错误契约后再验收。

当前 CLI 已补 provider/allocation、窗口/运行态、日志 tail/follow 与 help 等运维入口;go test ./... 通过,状态文本/运行态结构也有回归约束。但 issue 中错误分类/退出码统一和 NAS 全链路验收仍未完成;本轮记录为部分实现,保持 needs-fix/open,下一步补齐稳定错误契约后再验收。
Sign in to join this conversation.
No description provided.