Skip to content

[P2] 「更新」按钮用前端硬编码的 npm 清单判断,已漏掉 OpenClaw,且未安装也照样显示 #61

Description

@yujiezhang-ops

AgentManageRow 用一份写在前端的 npm Agent 清单决定要不要显示「更新」按钮。这份清单是 agents.lock.json 之外的第二份真源,已经漂移了——OpenClaw 上周合入后没人同步它。同一个按钮还不检查 Agent 是否已安装。

两份真源已经分叉

AgentManageRow.tsx:12-18

const npmAgents = new Set(["codex", "claude-code", "opencode", "kilo-cli"]);
const npmPackages: Record<string, string> = {
  codex: "@openai/codex",
  "claude-code": "@anthropic-ai/claude-code",
  opencode: "opencode-ai",
  "kilo-cli": "@kilocode/cli",
};

对着 lock 核了一遍:

Agent lock 里的 manager npmAgents
codex npm
claude-code npm
opencode npm
kilo-cli npm
openclaw npm ❌ 漏了
aider uv 不适用

OpenClaw 是 #53 加进来的可安装 npm Agent,但在管理页拿不到「更新」按钮,因为它不在这个 Set 里。npmPackages 同样缺它,所以详情区(AgentManageRow.tsx:263)也不显示包名。

后端根本不需要前端告诉它这些。 internal/app/update.go:26-32 自己从 lock 读,并且自己校验:

manifest, err := catalog.LoadEmbedded()
agent, ok := manifest.Agents[strings.TrimSpace(agentID)]
if !ok || agent.Package == nil || agent.Package.Manager != "npm" {
    return AgentUpdateResult{}, oneerrors.New(oneerrors.InvalidRequest, "Agent is not npm-managed: "+agentID)
}
args := []string{npm, "update", "-g", agent.Package.Name}

包名也是从 lock 取的。所以前端这两个常量是纯粹的重复,且是会烂掉的那种重复:新增 npm Agent 时没有任何机制提醒你改它,CI 也不会红。

AgentCatalogItem 里已经有 configModeprotocol 这类字段从后端投影过来,多带一个"是否 npm 托管"和包名并不算扩张 DTO。

未安装也显示「更新」

AgentManageRow.tsx:214 的条件只有 npmAgents.has(agentId)

{npmAgents.has(agentId) ? (
  <button ... onClick={() => void update()} title={t("执行 npm update")}>

同一个组件里 :137 明明算过安装状态,「启动」按钮也用了它(:229canLaunch),但「更新」没用:

const canLaunch = status.installed;

所以一个装都没装的 Agent,界面上会给你一个「更新」按钮。点下去后端不会报错——npm update -g <没装过的包> 是空操作,会成功返回——于是任务中心显示「更新完成」,而那个 Agent 依然没装。这比报错更糟,因为它给了一个成功的假象。

后端也没有这道判断:update.go 只检查 manager == "npm" 和 npm 可用,不检查 Agent 是否已安装。

建议

  1. 让后端把「是否 npm 托管」和包名投影到 AgentCatalogItem,前端删掉 npmAgentsnpmPackages。这样新增 Agent 只需要改 lock 一处。
  2. 「更新」按钮的显示条件加上 status.installed,与同组件里「启动」按钮的做法对齐。
  3. UpdateAgent 里补一道未安装的前置检查,返回明确错误,而不是让 npm update -g 静默成功。第 2 条只挡住 UI,任务中心恢复的卡片和 CLI 仍可能打进来。

相关

#60 是同一类问题:界面按钮的可用性由一个与实际状态无关的条件决定。#60 是入口语义不看安装状态,这个是操作按钮不看安装状态,另外多了一层硬编码清单漂移。

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions