From decf59c1b405a36dc30ca7e70b1ca2e6798a668b Mon Sep 17 00:00:00 2001 From: AskaEth Date: Sun, 4 Oct 2026 14:21:41 +0800 Subject: [PATCH] =?UTF-8?q?feat(gateway,admin-web):=20Skill=20=E8=BF=90?= =?UTF-8?q?=E8=A1=8C=E6=97=B6=E5=AE=89=E8=A3=85=20+=20backthink=20?= =?UTF-8?q?=E6=89=93=E6=89=B0=E5=8E=9F=E5=9B=A0=20+=20=E5=BB=BA=E6=8F=90?= =?UTF-8?q?=E9=86=92=E5=85=A5=E5=8F=A3=EF=BC=88P0-19/P0-20/P0-22=EF=BC=8C?= =?UTF-8?q?=E5=AE=A1=E8=AE=A1=20=C2=A74.3=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - P0-19:POST /admin/plugins/install 支持 `kind:"skill"`(此前一律 400)——yaml 或 path 二选一、 name 一致性校验、显式错误码(bad_request/name_mismatch/skill_parse_failed/name_conflict)、 装后以 kind:"skill" 出现在 GET /admin/plugins;**文件来源解析错误绝不回显文件内容**(变异验证实测确认 该泄露向量真实存在:yaml 解码器会带出出错值前几字符 ⇒ 只回显路径 + 通用原因,细节进日志) - P0-20:AdminBackthinkItem 补 route/detail/bypassed(DB 早已存,零迁移),面板 Backthink 展示 - P0-22:新增 POST /admin/automations/reminders(owner-only + 审计),at/message 必填、RFC3339 校验、 409 already_exists;过去时间不作 400(下次轮询立即触发) - 顺手修真缺陷:新建 id 原用 time.Now().UnixNano(),Windows 时钟粒度粗到毫秒 ⇒ 并行创建撞 id 得假 409; 改用 crypto/rand 128 位 hex(仓库既有 skills/store.go 范式) - 复核:gateway 包 build/vet/测试绿 + 前端 tsc 0 错;新增 12 用例(含 8 条非法输入显式码); **4 条变异反证**均已回滚 - 未做(登记):WS 帧侧的 route/detail 未在前端展示(`lib/wsParse.ts` 3 行,需放行);skill 未落 skills.Store(重启后消失,注入点在 cmd/cyrene);提醒无删除入口(CancelReminder 已存在、无路由) --- frontend/web/src/admin/modules/Backthink.tsx | 23 +- frontend/web/src/lib/types/agent.ts | 7 + internal/services/gateway/admin.go | 13 +- .../gateway/admin_backthink_push_test.go | 102 ++++++ internal/services/gateway/admin_plugins.go | 63 ++-- .../services/gateway/admin_plugins_skill.go | 200 ++++++++++++ .../gateway/admin_plugins_skill_test.go | 296 ++++++++++++++++++ internal/services/gateway/admin_query.go | 3 + .../gateway/admin_write_iot_automations.go | 83 +++++ .../gateway/admin_write_reminder_test.go | 185 +++++++++++ internal/services/gateway/rbac_viewer_test.go | 1 + internal/services/gateway/routes.go | 4 + 12 files changed, 956 insertions(+), 24 deletions(-) create mode 100644 internal/services/gateway/admin_backthink_push_test.go create mode 100644 internal/services/gateway/admin_plugins_skill.go create mode 100644 internal/services/gateway/admin_plugins_skill_test.go create mode 100644 internal/services/gateway/admin_write_reminder_test.go diff --git a/frontend/web/src/admin/modules/Backthink.tsx b/frontend/web/src/admin/modules/Backthink.tsx index 85c81bc..236acf7 100644 --- a/frontend/web/src/admin/modules/Backthink.tsx +++ b/frontend/web/src/admin/modules/Backthink.tsx @@ -10,6 +10,22 @@ import { adminBackthink } from "../../lib/api"; import { useAsyncData } from "../../lib/useAsyncData"; import { BACKTHINK_SESSION_ID } from "../../store/sessionCache"; +/** 推送路由的中文标签(后端 PushResult.Route 的取值域)。未知取值原样显示, + * 绝不猜一个更好听的词。 */ +const PUSH_ROUTE_LABEL: Record = { + active_client: "活跃客户端", + im_bridge: "IM 桥", + rate_limited: "限频拦截", + none: "无投递目标", +}; + +/** 推送路由的色调:真的送出去了才用 ok,被限频拦住是 warn。 */ +function pushRouteTone(route: string): "ok" | "warn" | "neutral" { + if (route === "active_client" || route === "im_bridge") return "ok"; + if (route === "rate_limited") return "warn"; + return "neutral"; +} + function BackthinkRow({ item }: { item: BackthinkItem }): React.ReactNode { const markPushed = useAppStore((s) => s.markBackthinkPushed); // 裁决 16.4.2:这条思考的链路图取自**后台思考会话**(`sess-backthink`,design/19 §1 @@ -26,6 +42,8 @@ function BackthinkRow({ item }: { item: BackthinkItem }): React.ReactNode { {item.trigger} {item.shouldNotify ? "需要打扰" : "静默"} {item.pushed ? "已推送" : "未推送"} + {item.route ? {PUSH_ROUTE_LABEL[item.route] ?? item.route} : null} + {item.bypassed ? 事件直发 : null}