Files
codex-app-server/cloud/console-react/src/api/chv.ts
T
host 4fa94edd9b feat(chv): 架构/可维护性/前端只读侧推进——生产实验分离、配置片段化、VM 只读展示
架构:WarmPool 从 cloud/chv/pool.py 移到 experimental_pool.py,
生产残留清理彻底分文件到 cloud/chv/orphans.py(facade 直接读
registry 文件清理,不构造池、不碰 prewarm/acquire/release);
cloud/chv 与 cloud/sandbox 不再导出 WarmPool,旧路径一律
ImportError(CHV 未合并、无生产用户,兼容保留已无保留理由);
worker 改走 orphans facade,不再持有 _warm_pool;#215 对齐从
接口形状推进到 REPLACEMENT_POINTS 替换清单(R1/R2 可机械替换、
R3 永不替换,#215 模块当前不存在由测试钉住)。

可维护性:build_chv_config 拆成 base/ssh/network/quota/disk/
timeout 六片段(组装只做 dict 合并,键冲突即报错;38 字段集
与取值不变,单项非法报错文案逐字一致);ADR-12 模块职责图
与禁 import 清单(AST 钉住);cloud/chv/ 公开 API 全量类型
标注、无 Any 逃逸,并修正三处旧标注错误;死代码扫一遍
(删实验池已无调用的 _quarantine 委托与未用 _LOGGER)。

前端(只读侧,不做控制按钮):chv.ts 新增纯逻辑谓词与文案
(执行器精确匹配、附件守卫、VM 状态文案、列表徽标);列表
行新增 VM 已报告死亡徽标;详情对全部 CHV 任务显示 VM 行
(已报告死亡/未报告死亡,不猜 live);WorkersSection 与成本
卡沿用已有数据,不新开池管理/成本页;服务端未新增第二个
生命周期字段(持久化事实只有三项,chv_vm_dead 已是完整派生),
只把派生抽成 _chv_vm_dead_from_row 并补 goal 回落形态测试。
2026-11-08 09:44:34 +00:00

106 lines
4.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type { Worker } from "./types";
import { workerAgeText } from "./client";
// CHV 可用性纯逻辑:不发请求、不拼 URL,只从 ctx 已有的
// /workers 数据推断。非管理员的 /workers 只返回聚合数
// (workers_total/online/offline),没有按执行器的分项,
// 此时 CHV 在线数是未知,绝不能把未知写成 0,也绝不能
// 把聚合在线数当成 CHV 在线数。
export function isWorkerAlive(w: Worker): boolean {
if (w.alive !== undefined && w.alive !== null) return !!w.alive;
const info = workerAgeText(w);
return info.age !== null && info.age <= 120;
}
export function countChvWorkers(workers: Worker[]): { online: number; total: number } {
const chv = workers.filter((w) => w.executor === "cloud-hypervisor");
return { online: chv.filter(isWorkerAlive).length, total: chv.length };
}
export type ChvAvailability =
| { state: "known"; online: number; total: number }
| { state: "unknown"; reason: "not-loaded" | "aggregate-only" | "load-failed" };
export function chvAvailability(
workers: Worker[] | null,
workersSummary: string,
workersError: string,
): ChvAvailability {
if (workers) return { state: "known", ...countChvWorkers(workers) };
if (workersError) return { state: "unknown", reason: "load-failed" };
if (workersSummary.includes("无 worker 注册")) return { state: "known", online: 0, total: 0 };
if (workersSummary.includes("完整 worker 清单仅全局管理员可见")) {
return { state: "unknown", reason: "aggregate-only" };
}
return { state: "unknown", reason: "not-loaded" };
}
// 执行器选择谓词:派发下拉与列表筛选都以字符串相等为准,
// 不做大小写/前缀宽松匹配(后端白名单是精确值,见 server
// POST /tasks 的 executor 白名单;宽松匹配会把非法值放进
// “CHV 任务”分支,造成前端提示与后端拒绝不一致)。
export function isChvExecutor(executor: string | undefined | null): boolean {
return executor === "cloud-hypervisor";
}
// 附件守卫谓词:CHV + 带附件 → 后端建任务即拒绝(server
// POST /tasks 的附件绑定分支,错误文案“附件不支持
// cloud-hypervisor 执行器”)。本函数只作前端提示口径,
// 绝不代替后端白名单:前端照常提交,后端拒绝为准。
export function chvAttachmentBlocked(
executor: string | undefined | null,
hasAttachments: boolean,
): boolean {
return isChvExecutor(executor) && hasAttachments;
}
// VM 只读状态文案:输入只有后端已返回的两个字段,输出
// 三种情形之一,绝不编造第四种:
// - chv_vm_dead 为 true → 已报告死亡(来源:worker 终局
// 写回的 interrupted_reason,见 server _chv_vm_dead_from_row);
// - CHV 任务但无该字段 → 未报告死亡,且明写“后端无 live
// 状态,不代表 VM 活着”(server 没有 VM 登记表);
// - 非 CHV 任务 → 空串(不显示 VM 行)。
export function chvVmStatusText(task: {
executor?: string | null;
chv_vm_dead?: boolean;
}): string {
// 死亡文案同样先过执行器门:后端只对 CHV 任务派生该字段,
// 万一某天非 CHV 行误带该字段,前端也不把 docker 任务
// 写成“VM 死亡”。
if (task.chv_vm_dead && isChvExecutor(task.executor)) {
return "VM 中途死亡,任务未完成、断点可续跑(后端只读字段 chv_vm_dead)";
}
if (isChvExecutor(task.executor)) {
return "未报告 VM 死亡(后端无 live 状态字段,不代表 VM 活着)";
}
return "";
}
// 列表行徽标:只有已报告死亡时才出现短徽标;未报告时
// 列表行不写 VM 状态(详情里才写“未报告”长文案,避免
// 列表行被未知状态刷屏)。
export function chvVmBadge(task: {
executor?: string | null;
chv_vm_dead?: boolean;
}): string {
return task.chv_vm_dead && isChvExecutor(task.executor)
? "VM 已报告死亡"
: "";
}
// 排队原因提示:只在“已知零 CHV worker 在线 + CHV 任务排队”时
// 给出,文案明确写“可能原因”,且不编造后端未返回的
// VM 状态/IP/快照。未知时返回空串(不提示、不猜测)。
export function chvQueueHint(
executor: string | undefined | null,
status: string | undefined | null,
availability: ChvAvailability,
): string {
if (executor !== "cloud-hypervisor" || status !== "queued") return "";
if (availability.state === "known" && availability.online === 0) {
return "可能原因:当前无 cloud-hypervisor worker 在线,任务在排队(基于 /workers 推断;VM 状态/IP/快照等后端未返回字段不展示)。请先确认 KVM 宿主上的 CHV worker 已起,worker 上线后可认领。";
}
return "";
}