diff --git a/.doc/archive/design/2026-09-17-personal-engineering-control-center-plan.md b/.doc/archive/design/2026-09-17-personal-engineering-control-center-plan.md index f1722db5..db87f8bb 100644 --- a/.doc/archive/design/2026-09-17-personal-engineering-control-center-plan.md +++ b/.doc/archive/design/2026-09-17-personal-engineering-control-center-plan.md @@ -7,7 +7,7 @@ product_contract_source: ce-plan-bootstrap execution: code --- -> **已废弃(2026-09-17)**:旧版只读工程中控台(`cmd/engineering-dashboard` / `internal/engineeringdashboard`)代码已移除,由独立的可写中控台 Control Center 承接(见 `02-control-center-design.md`)。本文仅作历史证据保留。 +> **已废弃(2026-09-17;2026-09-19 更新)**:旧版只读工程中控台代码已移除。后来短暂合入的 Control Center 也已移出本仓库,进度管理由独立项目承担。本文仅作历史证据保留。 # 个人工程中控台:实时项目看板与离线 HTML 快照 diff --git a/.doc/archive/ops/short-term/2026-09-17-engineering-dashboard.md b/.doc/archive/ops/short-term/2026-09-17-engineering-dashboard.md index 26b8cb87..ea0dff3e 100644 --- a/.doc/archive/ops/short-term/2026-09-17-engineering-dashboard.md +++ b/.doc/archive/ops/short-term/2026-09-17-engineering-dashboard.md @@ -49,7 +49,7 @@ mutex: engineering-dashboard ## 相关链接 -- 设计与实施方案:[../../system-design/project-control-center/01-personal-engineering-control-center-plan.md](../../system-design/project-control-center/01-personal-engineering-control-center-plan.md) +- 设计与实施方案:[../../archive/design/2026-09-17-personal-engineering-control-center-plan.md](../../archive/design/2026-09-17-personal-engineering-control-center-plan.md) - Test report: [../../test-reports/2026-09-17-engineering-dashboard-test-report.md](../../test-reports/2026-09-17-engineering-dashboard-test-report.md) ## 证据 / 备注 diff --git a/.doc/audit/2026-09-17-system-health-check.md b/.doc/audit/2026-09-17-system-health-check.md index 6d42da41..7084ed83 100644 --- a/.doc/audit/2026-09-17-system-health-check.md +++ b/.doc/audit/2026-09-17-system-health-check.md @@ -21,7 +21,7 @@ severity: high | # | 任务 | 位置 | 剩余工作 | |---|------|------|----------| | T1 | partial/deferred 全量收口总控 | `.doc/ops/short-term/2026-09-16-partial-deferred-completion.md`(owner Cline) | W5(SWBOOT-10 真实现 + 真实 PI + UI 二次验收)、W6(FAV shadow→live 放量)、W7(D7 按 ADR 整改,待合规签字)、逐行升 done | -| T2 | Control Center 个人中控台 | `.doc/ops/short-term/2026-09-17-control-center-server.md`(owner Kimi) | 后端 P0–P6 已签;**唯一剩余:前端 SPA 七页人工浏览器走查** | +| T2 | Control Center 个人中控台 | 2026-09-19 已移出本仓库 | 进度管理改由独立项目承担;本仓库不再保留该模块 | | T3 | 纯软件项目自举 W5b | `.doc/ops/long-term/2026-09-14-software-project-bootstrap-implementation.md` | SWBOOT-10 受控执行器(真 patch/merge)未启动,需独立安全评审,当前 fail-closed stub | ### 1.2 已规划未开始 @@ -40,7 +40,7 @@ severity: high |----|--------------| | 纯软件项目自举 | SWBOOT-10 非 stub(W5b)+ 真实 PI 全链路 + UI 二次验收 | | FAV | W6 shadow 已合;live 未放量(staging→artifact→publication 各 ≥7 天观察窗) | -| Control Center | 前端人工走查 PASS 后升 done | +| Control Center | 2026-09-19 已移出本仓库,不再作为本仓库关闭条件 | | 首次加载性能 | U1/U3–U7 | | 文档可靠性试点 | M1 结论「停止扩面」;M2(U5–U12)明确禁止开工,真实 PI/浏览器验收未勾 | @@ -89,7 +89,7 @@ severity: high | ID | 描述 | 证据 | |----|------|------| | R-1 | **聊天会话无归属校验**:`getOrCreateSession` 仅凭客户端传入 `session_id` 返回既有内存会话,随后直接覆盖 `sess.UserID`;非项目会话(ProjectID==0)无任何授权关卡,内存 Messages/Facts 可进入他人 Prompt 与 SSE。缓解:session ID 为纳秒时间戳不易猜 | `internal/server/server.go:394-415`、`handlers.go:181`、`sse.go:187`、`stage_access_helpers.go:251-253` | -| R-2 | **18 个非测试文件 `rows.Next()` 循环后未查 `rows.Err()`**,中途断连静默截断结果集(ctxmgr 历史/窗口/搜索、docgov engine、freedoc、storage/cas、controlcenter tasks、`cmd/document-content-reconcile` 漏检会让 reconcile 漏报文档) | 如 `internal/ctxmgr/chat_history.go`、`internal/docgov/engine.go`、`cmd/document-content-reconcile/main.go:82` | +| R-2 | **18 个非测试文件 `rows.Next()` 循环后未查 `rows.Err()`**,中途断连静默截断结果集(ctxmgr 历史/窗口/搜索、docgov engine、freedoc、storage/cas、`cmd/document-content-reconcile` 漏检会让 reconcile 漏报文档) | 如 `internal/ctxmgr/chat_history.go`、`internal/docgov/engine.go`、`cmd/document-content-reconcile/main.go:82` | | R-3 | 事件 dispatcher `Handlers` map **写持锁、读不持锁**(当前仅启动期注册则良性,动态注册即 race) | `internal/events/worker.go:87,154,234` | | R-4 | `memTurns` 全局幂等表只增不删且跨实例共享,长期运行内存缓增、测试间可能串扰 | `internal/ctxmgr/chat_turns.go:169` | | R-5 | `internal/docrevision/pg_store.go:68-208` 十余处 `_ = json.Unmarshal(...)`,账本 JSONB 损坏时静默零值化 | 同左 | @@ -147,7 +147,7 @@ severity: high |---|------|------| | S-1 | `TestReconcile_LegacyRepair_PG` 从未执行 | 要求隔离 PG 且硬拒绝 5433/5434/5435;canonical-hash 修复链路零自动化验证(gap-matrix 已记测试债) | | S-2 | 两个「永久跳过」 | `taskstream_test.go:189`(功能已删,测试成死代码应删除);`plan_owner_test.go:125`(重定向跳过) | -| S-3 | 无测试的 cmd 入口 | `cmd/server`、`cmd/events`、`cmd/control-center`、`cmd/artifact-vault`、`cmd/dataset`、`cmd/eventmode`、`cmd/recategorize`、`cmd/document-revision-backfill`(仅 bizdev-cli、document-content-reconcile 有);`internal/controlcenter/{config,store,tasks,webui}`、`internal/observability`、`internal/workflow` 无测试文件 | +| S-3 | 无测试的 cmd 入口 | `cmd/server`、`cmd/events`、`cmd/artifact-vault`、`cmd/dataset`、`cmd/eventmode`、`cmd/recategorize`、`cmd/document-revision-backfill`(仅 bizdev-cli、document-content-reconcile 有);`internal/observability`、`internal/workflow` 无测试文件 | | S-4 | **E2E 默认全不跑**:web/e2e 9 个 spec 全部被环境门禁(`RUN_UI_ACCEPTANCE=1`/`DOCREV_E2E_BASE_URL`/`INTENT_DUAL_TAB=1`),默认 `playwright test` 实跑 0 用例;`test:ui-acceptance` script 只覆盖 1 个 spec,其余 8 个无 script 入口 | web/e2e/、web/package.json:11 | | S-5 | 409 信封处理只覆盖部分消费方:projectMembers、freeformDoc、createTaskRun、chatSessions upsert 等 409 只能裸报错(与 FEATURE-MATRIX hardening backlog 一致) | `web/src/api/projectMembers.ts`、`freeformDoc.ts:177`、`taskRuns.ts`、`chatSessions.ts` | | S-6 | 前端 bundle 大 chunk 未分割(mermaid/katex/xlsx/docx 各成 >500 kB chunk,已有 precompress 缓解) | vite build 警告 | diff --git a/.doc/ops/CURRENT.md b/.doc/ops/CURRENT.md index 15681d28..63a932d5 100644 --- a/.doc/ops/CURRENT.md +++ b/.doc/ops/CURRENT.md @@ -6,7 +6,7 @@ ## 焦点 1. **系统体检整改**:Wave 0–7 **done**(Wave 7 编排确认见 [决策记录](./short-term/2026-09-18-wave7-roadmap-orchestration.md)) -2. **已裁决(supersede,done)**:`feature/control-center-server` 取代 `engineering-dashboard`,门禁已过,随本分支合入本地 `dev`。见 [任务](./short-term/2026-09-17-control-center-server.md)、[报告](../test-reports/2026-09-18-control-center-merge-gate-test-report.md) +2. **Control Center 已移出本仓库**:项目进度由独立统一管理系统承担;本仓库恢复为智能体系统。见 [任务](./short-term/2026-09-19-remove-control-center.md) 3. **rdev 模板深度 / document-author-only**:已合入 `origin/dev`;未晋升 test/main 4. **红线**:禁止整支 merge FAV;不默认升 `MULTI_INTENT_PLAN_TIER=full`;`SEMANTIC_SUMMARY_REPLACE` 不随 W2 开启;FAV live 观察窗证据驱动;`FAV_DEGRADED_READ=false`;项目 143 只读;SWBOOT-10 保持 fail-closed stub;D7 待合规签字 @@ -14,7 +14,8 @@ | owner | task | branch | worktree | mutex | status | |-------|------|--------|----------|-------|--------| -| Cursor-Auto | 2026-09-17-control-center-server | feature/control-center-server | - | controlcenter | done | +| Cursor-Auto | 2026-09-19-remove-control-center | fix/remove-control-center | - | controlcenter | done | +| Cursor-Auto | 2026-09-17-control-center-server | feature/control-center-server | - | controlcenter | cancelled | | Kimi | 2026-09-18-user-manual | feature/user-manual | .worktrees/feature-user-manual | docs | done | | Cursor-Auto | 2026-09-18-wave7-roadmap-orchestration | docs/wave7-roadmap-orchestration | - | roadmap-orchestration | done | | Cursor-Auto | 2026-09-18-wave6-doc-drift | docs/wave6-doc-drift | - | doc-drift | done | @@ -143,7 +144,7 @@ | holder | phase | since | scope | queue | |--------|-------|-------|-------|-------| -| - | idle | 2026-09-19 | feature/control-center-server 取代 engineering-dashboard,门禁(单测 / 真实 LLM / 七页走查)PASS 后合入本地 dev | - | +| - | idle | 2026-09-19 | fix/remove-control-center 已删除 Control Center;合入后 dev 健康检查见测试报告 | - | **phase**: `idle` | `merging` | `smoke` | `e2e` diff --git a/.doc/ops/short-term/2026-09-17-control-center-server.md b/.doc/ops/short-term/2026-09-17-control-center-server.md deleted file mode 100644 index 113ba507..00000000 --- a/.doc/ops/short-term/2026-09-17-control-center-server.md +++ /dev/null @@ -1,76 +0,0 @@ ---- -title: "个人项目中控台 Control Center:独立 Go 服务 + 8 阶段流水线 + 向量文档库 + agent 协作 API" -status: done -created: 2026-09-17 -updated: 2026-09-19(合并门禁:单测、真实 LLM 审查、七页走查) -owner: Kimi -branch: feature/control-center-server -worktree: "-" -mutex: controlcenter ---- - -# 个人项目中控台 Control Center - -## 目标 - -交付独立于产品系统的个人项目管理中控台 Go 服务:任务/提案 8 阶段流水线(提案→分析→方案制定→方案评审→实施→测试→评价→确认)、agent 认领/进度/产物/决策上报 API(agent 禁止自动推进阶段)、`.doc` 文档 + 阶段产物入 pgvector 向量库支持模糊查询、阶段级模型指定、外部工具阶段提交、每日定时仓库审查与 findings 展示、Vite+React SPA 前端。 - -## 范围 - -- `cmd/control-center/` + `internal/controlcenter/{config,server,store,pipeline,tasks,docs,importer,audit,llm,embed}` -- `deploy/control-center/`(独立 pgvector 容器 + goose 迁移) -- `control-center/web/`(Vite+React SPA,embed 进二进制) -- 复用系统 `.env`:EMBEDDING_* / MINIMAX_* / CURSOR_*(仅读配置,不耦合产品代码路径) -- 设计文档、agent 接入规范、FEATURE-MATRIX 行、验收报告 - -## 非目标 - -- 不接产品 sysadmin、不动 internal/server 路由 -- 不做多仓库、不做远端 CI 集成 -- 旧 `internal/engineeringdashboard`:用户已授权移除(2026-09-17 执行:代码删除,设计文档归档 `.doc/archive/design/`,验收报告保留为历史证据) -- 不做 agent 自动推进阶段(任何推进必须人工批准) - -## 步骤 - -1. [x] P0 骨架:cmd/internal/compose/goose/healthz/token 鉴权 -2. [x] P1 流水线核心:8 阶段状态机、门禁、decision 上报/裁决、阶段产物、claim/lease/事件流、阶段指令包、SSE -3. [x] P2 文档向量库:扫描入库、切片、embedding、hybrid 检索、阶段产物向量化 -4. [x] P3 前端 SPA:总览/阶段看板/任务详情 stepper/决策收件箱/文档搜索/设置 -5. [x] P4 .doc 导入同步 + agent CLI 子命令 + 外部提交入口 -6. [x] P5 定时审查:调度器、确定性检查、LLM chat client、findings 转任务 -7. [x] P6 文档收口 + 验收报告 - -## 验收标准 - -- [x] `go vet ./...` / `go test -short ./...` / `go build ./cmd/control-center` 全绿 -- [x] 状态机单测:agent 直接推进被拒、decision 缺建议拒收、lease 过期孤儿恢复 -- [x] `.doc` 全量入库后语义搜索命中已知文档 -- [x] curl 冒烟:建任务→agent claim→产物→advance-request→人工批准→下一阶段 全链路 -- [x] 前端 `vite build` 通过并 embed 可用 - -## 相关链接 - -- 设计: [.doc/system-design/project-control-center/02-control-center-design.md](../../system-design/project-control-center/02-control-center-design.md) -- Agent 接入规范: [.doc/system-design/project-control-center/agent-integration.md](../../system-design/project-control-center/agent-integration.md) -- Checklist: -- Handoff: -- Test report: [.doc/test-reports/2026-09-17-control-center-test-report.md](../../test-reports/2026-09-17-control-center-test-report.md) - -## 证据 / 备注 - -计划经用户批准(存储=独立 pgvector 容器;前端=Vite+React SPA embed;旧台共存后弃用;审查 LLM=薄 OpenAI 兼容客户端)。 - -commit 列表(cf805a7..HEAD): - -```text -07a8e12 feat(control-center): P5 scheduled repo audits — deterministic checks, LLM review, findings UI -f79956c feat(control-center): P4 .doc importer, agent CLI, task doc export -b0095de feat(control-center): P3 web SPA — overview, stage kanban, task pipeline, decisions, docs search -63f1fa5 feat(control-center): P2 document vector store — ingestion, chunking, embedding, hybrid search -b11cc64 feat(control-center): P1 pipeline core — stage machine, decisions, artifacts, leases, SSE -2be43ef feat(control-center): P0 skeleton — standalone server, pgvector compose, goose schema, token auth -``` - -P6 冒烟发现并修复:`GET /api/v1/tasks?q=` 500(ListTasks Query 分支占位符 bug),修复 + 回归测试 `TestTaskListQueryFilter`,详见测试报告 §5.5。 - -**status: done**(2026-09-19)。合并门禁已过:`config`/`store`/`tasks` 单测、真实 MiniMax 审查 findings 落库(run 195,`CC-GATE-LIVE`)、七页浏览器走查。证据见 [.doc/test-reports/2026-09-18-control-center-merge-gate-test-report.md](../../test-reports/2026-09-18-control-center-merge-gate-test-report.md)。 diff --git a/.doc/ops/short-term/2026-09-18-user-manual.md b/.doc/ops/short-term/2026-09-18-user-manual.md index 134faca6..66947c5e 100644 --- a/.doc/ops/short-term/2026-09-18-user-manual.md +++ b/.doc/ops/short-term/2026-09-18-user-manual.md @@ -26,7 +26,7 @@ mutex: docs - 不合入 dev、不 push(用户未要求) - 不改动任何产品代码 -- 不覆盖 control-center/web(空壳)与 engineering-dashboard(工程内部) +- 不覆盖产品手册范围外的工程内部工具(`engineering-dashboard` 当时为工程内部,其后已移出本仓库) ## 步骤 diff --git a/.doc/ops/short-term/2026-09-18-wave7-roadmap-orchestration.md b/.doc/ops/short-term/2026-09-18-wave7-roadmap-orchestration.md index 079301fc..d34e6a26 100644 --- a/.doc/ops/short-term/2026-09-18-wave7-roadmap-orchestration.md +++ b/.doc/ops/short-term/2026-09-18-wave7-roadmap-orchestration.md @@ -77,6 +77,7 @@ mutex: roadmap-orchestration - **关闭条件**:FEATURE-MATRIX 该行**已经 done**(通过 engineering-dashboard)。若要转向「复活/合并 Kimi 实现」,需先有独立决策与重新规划,不属于「仅剩一次前端走查」。 - **是否需用户授权**:**是**。授权对象是「两套实现如何取舍」,不是「批准做前端走查」。本波只读编排确认,不能代为废弃或合并该分支。 - **建议下一触发点**:请用户明确二选一:(a) 维持现状——`engineering-dashboard` 为唯一 canonical 实现,`feature/control-center-server` 由用户决定归档或保留(推荐:已 done 且范围经批准);或 (b) 要求评估合并该分支的增量能力(SPA / pgvector / 定期审计)。触发后再开对应短期任务。本波只记录事实与建议,**不合并、不删除**该分支。 +- **后续裁决(2026-09-19)**:用户决定进度管理由独立项目承担。本仓库已删除 Control Center 代码与文档,不恢复 `engineering-dashboard`。见 [移除任务](./2026-09-19-remove-control-center.md)。 ### T4:首次加载性能 U1/U3–U7 @@ -93,7 +94,7 @@ mutex: roadmap-orchestration ## 新发现(需用户后续裁决,不阻塞本波收口) -- **Control Center 双实现冲突**:`feature/control-center-server`(Kimi,P0–P6 SPA)未合入 `dev`。相对 `dev@a88ed85` 落后 136 commits(merge-base `adb48c8`),自身 11 commits,其中 `b5bacee` 删除已合入的 `engineering-dashboard`。建议维持 `engineering-dashboard` 为唯一 canonical 实现;是否归档或评估合并该分支,待用户裁决。本波不删除、不合并该分支。 +- **Control Center 双实现冲突**:当时未合入。2026-09-19 用户裁决为移出本仓库,代码与专属文档已删除,不恢复旧工程中控台。见 [移除任务](./2026-09-19-remove-control-center.md)。 ## 证据 / 备注 diff --git a/.doc/ops/short-term/2026-09-19-remove-control-center.md b/.doc/ops/short-term/2026-09-19-remove-control-center.md new file mode 100644 index 00000000..785e78b6 --- /dev/null +++ b/.doc/ops/short-term/2026-09-19-remove-control-center.md @@ -0,0 +1,53 @@ +--- +title: "移除 Control Center,本仓库回到智能体系统" +status: done +created: 2026-09-19 +updated: 2026-09-19 +owner: Cursor-Auto +branch: fix/remove-control-center +worktree: "-" +mutex: controlcenter +--- + +# 移除 Control Center + +## 目标 + +项目进度管理已由独立项目承担。从本仓库删除 Control Center 的代码、部署、规范和专属文档,不恢复旧 `engineering-dashboard`。 + +## 范围 + +- 删除 `cmd/control-center`、`internal/controlcenter`、`control-center/web`、`deploy/control-center`、`scripts/control-center.sh` +- 删除专属设计、任务、测试报告与截图 +- 更新 AGENTS、FEATURE-MATRIX、CURRENT、模块边界与断链引用 +- 停掉 `control-center-db` 与 `127.0.0.1:8790` + +## 非目标 + +- 不恢复 `engineering-dashboard` +- 不 push +- 不改产品 `internal/server`、产品前端或三环境 compose + +## 步骤 + +1. [x] 从本地 dev 开 `fix/remove-control-center` +2. [x] 删除代码与文档并更新引用 +3. [x] 停运行时 +4. [x] `go vet` / `go test -short` / 产品前端 build / archtest +5. [ ] 合入本地 dev 并做健康检查 + +## 验收标准 + +- [x] 仓库内不再有 Control Center 源码或专属文档 +- [x] `go vet ./...`、`go test -short ./...`、`go test ./internal/archtest/`、`web` 的 `npm run build` 通过 +- [ ] 本地 dev 健康检查 200 + +## 相关链接 + +- Checklist: +- Handoff: +- Test report: [../../test-reports/2026-09-19-remove-control-center-test-report.md](../../test-reports/2026-09-19-remove-control-center-test-report.md) + +## 证据 / 备注 + +用户确认:只移除 Control Center,副本已在别处实现,合入本地 dev。 diff --git a/.doc/remediation/2026-09-17-system-health-remediation-plan.md b/.doc/remediation/2026-09-17-system-health-remediation-plan.md index 84388e6e..cc4044bb 100644 --- a/.doc/remediation/2026-09-17-system-health-remediation-plan.md +++ b/.doc/remediation/2026-09-17-system-health-remediation-plan.md @@ -34,7 +34,7 @@ updated: 2026-09-18 | **Wave 4:隐患加固(可与 Wave 0–3 并行认领,mutex 不冲突时)** | R-1:`getOrCreateSession` 增 owner 绑定校验(session 创建时记录 owner,后续请求校验,不匹配 403)。R-2:18 处 `rows.Next()` 循环补 `rows.Err()` 检查。R-3:dispatcher Handlers 读路径加 RLock(或注释冻结「仅启动期注册」并加 archtest 约束)。R-4:`memTurns` 加 TTL/容量淘汰。R-5:pg_store JSONB 反序列化错误改为显式报错。R-6:后台 ticker 纳入 Shutdown;vault Drain 错误上报告警。R-7/R-8:SSE 重连 401/404 fail-fast、逐帧容错;登出 abort 在途流 | **done**(2026-09-18):[test-report](../test-reports/2026-09-18-wave4-hardening-r1-r8-test-report.md);Memory+PG 跨用户负例 PASS;`go test -short` / `npm test` 全绿 | | **Wave 5:死代码与机制收口(需逐项决策,不擅自删)** | X-1:删除 `SEMANTIC_FALLBACK_ENABLED`。X-2:`SEMANTIC_SUMMARY_REPLACE` 维持未接线并封存注释。X-3:00048 三表 deferred → SWBOOT-10/W5b。X-4:system_settings 6 键保留并注释二期。X-5:删除 DocumentNotificationList、WorkflowPrerequisiteBanner、SettingsModal、activateWorkflow、saveMessages 及级联 patchNotification | **done**(2026-09-18):[决策](../ops/short-term/2026-09-18-wave5-deadcode-decisions.md) · [test-report](../test-reports/2026-09-18-wave5-deadcode-decisions-test-report.md) | | **Wave 6:文档漂移修复** | D-1:PU8 四份契约文件状态改 done 并归档。D-2:modular-monolith ADR 状态更新为 accepted。D-3:两个 INDEX 末行已在本审计修正(复核即可)。D-4:handoff 回标已被覆盖。D-5:backlog D3/D7 同步最新状态。D-6:PROJECT.md §9「剩余 blocker」段与 gap-matrix 对齐或删除 | **done**(2026-09-18):[short-term](../ops/short-term/2026-09-18-wave6-doc-drift.md) · [test-report](../test-reports/2026-09-18-wave6-doc-drift-test-report.md);D-3 INDEX 复核 PASS、零改动 | -| **Wave 7:既有 roadmap 收口(不改优先级,仅确认编排)** | T1/W5:SWBOOT-10 安全评审后实施(保持 fail-closed 直至授权)。T1/W6:FAV live 放量 staging→artifact→publication,每级 ≥7 天观察窗,`FAV_DEGRADED_READ=false` 不动。T1/W7:D7 待合规签字后按 ADR 整改。T2:Control Center 前端 SPA 七页人工走查。T4:首次加载性能 U1/U3–U7。T5/T6/T8:backlog 类按收益触发 | **done**(2026-09-18):[决策记录](../ops/short-term/2026-09-18-wave7-roadmap-orchestration.md)。六项编排确认已记录;T2 发现审计结论过期(canonical 实现为 engineering-dashboard,已 done;`feature/control-center-server` 未合入,相对 `dev@a88ed85` 落后 136 commits,待用户裁决);FAV/D7/tier 升级红线未动 | +| **Wave 7:既有 roadmap 收口(不改优先级,仅确认编排)** | T1/W5:SWBOOT-10 安全评审后实施(保持 fail-closed 直至授权)。T1/W6:FAV live 放量 staging→artifact→publication,每级 ≥7 天观察窗,`FAV_DEGRADED_READ=false` 不动。T1/W7:D7 待合规签字后按 ADR 整改。T4:首次加载性能 U1/U3–U7。T5/T6/T8:backlog 类按收益触发。T2(Control Center)已于 2026-09-19 移出本仓库 | **done**(2026-09-18):[决策记录](../ops/short-term/2026-09-18-wave7-roadmap-orchestration.md)。六项编排确认已记录;T2 后来由用户裁决为独立项目,本仓库不再保留 Control Center;FAV/D7/tier 升级红线未动 | ## 验收标准 diff --git a/.doc/status/FEATURE-MATRIX.md b/.doc/status/FEATURE-MATRIX.md index e81befba..ccda9cc3 100644 --- a/.doc/status/FEATURE-MATRIX.md +++ b/.doc/status/FEATURE-MATRIX.md @@ -69,8 +69,7 @@ | 可靠事件核心 | PG dispatcher、notify/advance/projection live、生产闭包 hook 退出、Phase2 选择性解耦已合入 | done | non_blocker | [E0A–E2](../test-reports/2026-09-15-event-e0a-production-scope-contract-test-report.md) 等;[复盘](../ops/research/2026-09-15-track-e-retrospective.md);[shared live](../test-reports/2026-09-15-event-notify-shared-live-test-report.md);[phase2 closeout](../test-reports/2026-09-15-phase2-closeout-test-report.md) | **before**:与 FAV off 混成 partial → **after**:FAV 独立行。选择性后续解耦按收益触发 | `architecture-docs` / `events` | | Final Artifact Vault(FAV) | 不可变终态制品库;`FAV_DEGRADED_READ=false` | partial | deferred_enhancement | [Track E 复盘](../ops/research/2026-09-15-track-e-retrospective.md);[U7 报告](../test-reports/2026-09-16-fav-u7-incremental-test-report.md);[W6 shadow](../test-reports/2026-09-17-w6-fav-subscriber-shadow-test-report.md);[short-term](../ops/short-term/2026-09-17-w6-fav-subscriber-shadow.md) | **after W6**:subscriber `ObserveShadow` 只计算 intended outcome,不写副作用。仍**禁止**整支 merge;`FAV_DEGRADED_READ` 默认 false;**live 未放量**(staging→artifact→publication 各 ≥7 天观察窗) | `events` | | 系统管理台 | 全屏系统级管理视图(总览/KB/特性开关/系统设置/工作流/角色/用户/文档 8 分组);`/api/v1/admin/system/*`;`system_settings` 白名单可写;吸收下线 AdminSettingsModal | done | non_blocker | [short-term](../ops/short-term/2026-09-17-system-admin-console.md);[测试报告](../test-reports/2026-09-17-system-admin-console-test-report.md);commit `f1404d5`/`0eaca61`、merge `dc7c31e`(dev);dev 冒烟 schema 49 + 四端点 + 写读回环 PASS | 二期:Playwright 实跑、细粒度 `system:*` 权限、设置审计日志、KB 运维动作、白名单项接消费方 | `web` + `sysadmin` | -| 个人工程中控台(旧,已移除) | 原独立本地只读 CLI/`serve`/`export`;四轴状态;`.doc`/Git/Docker/`/ready` 聚合 | removed | 已被 Control Center 取代 | [设计(已归档)](../archive/design/2026-09-17-personal-engineering-control-center-plan.md);[报告](../test-reports/2026-09-17-engineering-dashboard-test-report.md) | 2026-09-17 代码移除(`cmd/engineering-dashboard`、`internal/engineeringdashboard`、`scripts/engineering-dashboard.sh`);能力由下一行 Control Center 承接 | `engineering-dashboard`(已删) | -| 个人中控台 Control Center | 独立 Go 服务(loopback,独立 pgvector 库):8 阶段流水线 + agent 协作 API(禁自动推进)+ 决策上报 + 租约/SSE + `.doc` 向量检索 + 导入导出 + 每日审查 + React SPA | done | deferred_enhancement(个人工具,非产品门禁) | [设计](../system-design/project-control-center/02-control-center-design.md);[agent 接入规范](../system-design/project-control-center/agent-integration.md);[任务](../ops/short-term/2026-09-17-control-center-server.md);[报告](../test-reports/2026-09-18-control-center-merge-gate-test-report.md) | engineering-dashboard 已下线,由 control-center 承接一期。合并门禁:核心包单测、真实 MiniMax 审查落库、SPA 七页走查均 PASS | `controlcenter` | +| 个人工程中控台(已移出) | 原本地只读看板与后来的 Control Center 都不再属于本仓库 | removed | 进度管理改由独立项目承担 | [历史设计](../archive/design/2026-09-17-personal-engineering-control-center-plan.md);[历史报告](../test-reports/2026-09-17-engineering-dashboard-test-report.md) | 2026-09-19 删除 `cmd/control-center`、`internal/controlcenter`、`control-center/web`、`deploy/control-center`。不恢复 `engineering-dashboard` | — | --- diff --git a/.doc/system-design/README.md b/.doc/system-design/README.md index 939bdd00..0ef1fddd 100644 --- a/.doc/system-design/README.md +++ b/.doc/system-design/README.md @@ -138,18 +138,9 @@ --- -### 8. 个人中控台(Control Center) +### 8. 个人中控台(已移出本仓库) -| 项 | 链接 | -|-----|------| -| 设计与架构 SSOT | [project-control-center/02-control-center-design.md](project-control-center/02-control-center-design.md) | -| Agent 接入规范 | [project-control-center/agent-integration.md](project-control-center/agent-integration.md) | -| 任务入口 | [../ops/short-term/2026-09-17-control-center-server.md](../ops/short-term/2026-09-17-control-center-server.md) | -| 合并门禁报告 | [../test-reports/2026-09-18-control-center-merge-gate-test-report.md](../test-reports/2026-09-18-control-center-merge-gate-test-report.md) | -| 一句话总结 | 独立于产品系统的个人项目管理中控台:8 阶段流水线(agent 禁自动推进)+ 决策上报 + 向量文档库 + 每日审查 + React SPA | -| 旧版(已移除) | 只读工程中控台设计已归档:[../archive/design/2026-09-17-personal-engineering-control-center-plan.md](../archive/design/2026-09-17-personal-engineering-control-center-plan.md);任务:[../archive/ops/short-term/2026-09-17-engineering-dashboard.md](../archive/ops/short-term/2026-09-17-engineering-dashboard.md) | - -**核心边界**:个人工具、不接产品后台 sysadmin;agent 只能上报与 advance-request,阶段推进与模糊决策必须人工。 +项目开发进度由独立的统一管理系统负责,不再是本仓库功能。本仓库只保留智能体系统。旧只读工程中控台的历史设计仍在 [../archive/design/2026-09-17-personal-engineering-control-center-plan.md](../archive/design/2026-09-17-personal-engineering-control-center-plan.md),代码不恢复。 --- diff --git a/.doc/system-design/module-boundaries.yaml b/.doc/system-design/module-boundaries.yaml index a650805a..9fba27c8 100644 --- a/.doc/system-design/module-boundaries.yaml +++ b/.doc/system-design/module-boundaries.yaml @@ -248,33 +248,6 @@ imports: - app/uow sysadmin: - config - controlcenter/config: [] - controlcenter/embed: [] - controlcenter/llm: [] - controlcenter/audit: - - controlcenter/llm - - controlcenter/store - controlcenter/docs: - - controlcenter/embed - - controlcenter/store - controlcenter/importer: - - controlcenter/pipeline - - controlcenter/store - controlcenter/pipeline: [] - controlcenter/store: [] - controlcenter/tasks: - - controlcenter/pipeline - - controlcenter/store - controlcenter/server: - - controlcenter/audit - - controlcenter/config - - controlcenter/docs - - controlcenter/embed - - controlcenter/pipeline - - controlcenter/store - - controlcenter/tasks - - controlcenter/webui - controlcenter/webui: [] table_owners: document_records: diff --git a/.doc/system-design/project-control-center/02-control-center-design.md b/.doc/system-design/project-control-center/02-control-center-design.md deleted file mode 100644 index 7279d9e6..00000000 --- a/.doc/system-design/project-control-center/02-control-center-design.md +++ /dev/null @@ -1,248 +0,0 @@ -# Control Center 设计文档(架构 SSOT) - -> 模块:`cmd/control-center` + `internal/controlcenter/*` + `deploy/control-center/` + `control-center/web/` -> 前置阅读:[01-personal-engineering-control-center-plan.md](./01-personal-engineering-control-center-plan.md)(一期只读 CLI);本文档覆盖二期服务端。 -> Agent 操作手册见 [agent-integration.md](./agent-integration.md)。 - -## 1. 定位与红线 - -- **个人中控台**:单用户的个人项目控制平面,管理本仓库的任务/提案流水线、文档检索、定时审查。不是产品功能。 -- **独立于产品系统**:不接产品 sysadmin、不动 `internal/server` 路由、不复用产品数据库。数据库是独立 pgvector 容器(`control-center-db`,默认端口 5440)。 -- **Loopback-first**:默认监听 `127.0.0.1:8790`,鉴权仅 token(console/agent 两类 bearer),无 TLS、无多租户。 -- **Agent 禁止推进阶段**:agent 侧 API 没有任何 advance 路由;阶段推进只能由 console 人工(或显式 external-submit)完成。 -- **复用系统 `.env` 仅限配置读取**:`EMBEDDING_*` / `MINIMAX_*` / `CURSOR_*` 通过 `CC_*` 覆盖或回退借用,不 import 产品代码路径。 - -## 2. 总体架构 - -```text - ┌──────────────────────────── 仓库主机 ────────────────────────────┐ - │ │ - human ──HTTP──▶│ control-center serve (127.0.0.1:8790) │ - (browser SPA) │ ├─ webui (embed control-center/web/dist) │ - │ ├─ server (console API / agent API / SSE broker / lease reaper)│ - agent ──HTTP──▶│ ├─ tasks (domain logic: transitions/decisions/lease/…) │ - (CLI/curl) │ ├─ pipeline (纯状态机规则, 无 IO) │ - │ ├─ docs (Syncer: .doc 扫描/切片/embedding 入库) │ - │ ├─ importer(.doc/ops 任务文档 → cc_tasks 周期导入) │ - │ ├─ audit (每日审查: 确定性检查 + LLM review) │ - │ ├─ embed (OpenAI 兼容 /embeddings 客户端, bge-m3 1024 维) │ - │ └─ llm (OpenAI 兼容 /chat/completions 客户端) │ - │ │ pgxpool │ HTTP │ - └──────────┼──────────────────────────────────┼───────────────────┘ - ▼ ▼ - ┌────────────────────┐ EMBEDDING_ENDPOINT_URL (bge-m3) - │ control-center-db │ MINIMAX_ENDPOINT_URL (审查 LLM) - │ pgvector/pg16:5440 │ - └────────────────────┘ -``` - -- 单二进制 `cmd/control-center`:`serve`(迁移+服务+周期任务)、`migrate`、`register-agent`、`import-docs`、10 个 agent CLI 子命令。 -- `serve` 启动时:goose 迁移 → 连接池 → doc Syncer 周期同步(`CC_DOC_SYNC_SECONDS`,默认 60s)→ importer 周期导入 → 审查调度器(`CC_AUDIT_DAILY_AT`)→ 租约回收器(60s 一轮)。 -- 前端 SPA 构建产物 embed 进二进制(`internal/controlcenter/webui`,`go:embed dist`),`GET /` 与非 `/api/` 路径直接服务,不带鉴权;`/api/*` 一律要求 bearer token。 - -## 3. 目录结构 - -```text -cmd/control-center/ main.go(子命令分发 + serve/migrate/register-agent/import-docs) - agentcli.go(agent 侧薄 HTTP 客户端 CLI) -internal/controlcenter/ - config/ CC_* 环境变量加载 + 系统 .env 回退借用 - store/ pgxpool 连接 + goose 迁移执行 - pipeline/ 纯状态机:阶段顺序、三轴枚举、门禁规则、deliverable 文案、DecisionRules - tasks/ 领域逻辑(SQL 全部在此):tasks/decisions/events/plans/settings/ - transitions/overview/docgen - server/ HTTP 适配层:路由、鉴权中间件、SSE broker、lease reaper、各 handler - docs/ .doc 扫描同步 + markdown 切片 - embed/ OpenAI 兼容 embeddings 客户端(Dim=1024)+ 测试 stub - importer/ .doc/ops/{short-term,long-term}+backlog 任务文档导入 cc_tasks - audit/ 每日审查:确定性检查、LLM review、findings 生命周期 - llm/ OpenAI 兼容 chat 客户端(审查专用) - webui/ embed SPA dist(dist 提交入库) -deploy/control-center/ docker-compose.yml(pgvector pg16,172.32.0.0/24 固定子网) - goose/00001_init.sql -control-center/web/ Vite + React + TS SPA 源码(build 输出到 internal/controlcenter/webui/dist) -``` - -## 4. DB schema(goose 00001_init.sql,独立库 control_center) - -| 表 | 用途 | 关键约束 | -|----|------|----------| -| `cc_plans` | 计划(short/long/backlog 三 horizon) | `slug` UNIQUE;`horizon/status` CHECK | -| `cc_agents` | agent 注册 | `name`/`token_hash` UNIQUE;仅存 SHA-256 hash | -| `cc_tasks` | 任务/提案 | `slug` UNIQUE;`kind∈{task,proposal}`;`status/stage/stage_state` CHECK(枚举见 §5);`testing_required` 默认 true;`lease_expires_at`/`heartbeat_at` 可空 | -| `cc_task_events` | 事件流 | `phase` CHECK 9 值;`task_id` ON DELETE CASCADE | -| `cc_task_reports` | agent 报告 | `kind∈{result,test-report,handoff}` | -| `cc_stage_artifacts` | 阶段产物 | `source_type∈{agent,human,external}`;`(task_id,stage)` 索引 | -| `cc_stage_transitions` | 阶段流转账本 | 记录 from/to/actor/approved_by/note | -| `cc_stage_settings` | 阶段级模型/执行者 | `scope∈{global,task}`;`executor∈{agent,human,external}`;`UNIQUE(scope,task_id,stage)` | -| `cc_decisions` | 决策上报 | `suggestions` jsonb;`status∈{open,resolved}`;open 部分索引 | -| `cc_documents` | .doc 文档登记 | `path` UNIQUE;`sha256` 变更检测 | -| `cc_doc_chunks` | 切片 + 向量 | `chunk_hash` UNIQUE(artifact 前缀 `artifact:`);`embedding vector(1024)` 可空;`tsv` 生成列;HNSW(cosine) + GIN(tsv);`CHECK(doc_id IS NOT NULL OR artifact_id IS NOT NULL)` | -| `cc_audit_runs` | 审查运行 | `status∈{running,completed,failed}` | -| `cc_audit_findings` | 审查发现 | `severity∈{low,medium,high,critical}`;`kind∈{bug,gap,unfinished,doc-drift}`;`status∈{open,converted,dismissed}` | -| `cc_snapshots` | 快照 | revision + payload jsonb | - -## 5. 八阶段流水线状态机 - -### 5.1 阶段顺序(`pipeline.Stages`,固定) - -```text -proposal → analysis → design → review → implementation → testing → evaluation → confirmation - │ - testing_required=false 时跳过 testing ┘ -``` - -`confirmation` 人工 advance 后任务 `status=done`(不再进入下一阶段)。 - -### 5.2 三轴模型(`cc_tasks`) - -| 轴 | 取值 | 含义 | -|----|------|------| -| `status` | `planned / in_progress / blocked_pending_decision / done / cancelled` | 任务生命周期 | -| `stage` | 8 阶段之一 | 当前所处阶段 | -| `stage_state` | `idle / assigned / in_progress / awaiting_review / changes_requested / approved` | 当前阶段内的流转状态 | - -### 5.3 stage_state 状态图与门禁铁律 - -```text - assign(console) claim(agent, 2h lease) - idle ───────────────▶ assigned ───────────────▶ in_progress - ▲ ▲ ▲ │ │ - │ │ └── release / 租约过期回收 │ │ event/artifact/decision… - │ send-back(console) │ │ │ advance-request(agent) - │ │ ▼ │ - │ changes_requested ◀── awaiting_review ◀─┘ - │ │ │ - └──── 重新 claim 回到 in_progress │ advance(console, 人工) - ▼ - 下一阶段 idle(已指派则 assigned) - confirmation 阶段 advance → status=done -``` - -铁律(`pipeline.CanAgentTransition`):**agent 唯一允许的 stage_state 迁移是 `in_progress → awaiting_review`**(advance-request)。没有 agent 路由可以移动 `stage`;console advance 仅允许从 `awaiting_review` 发起;send-back 从 `awaiting_review → changes_requested`;external-submit 带 `advance:true` 时允许从 `in_progress/assigned/awaiting_review` 推进(视为外部人工动作,actor=external,approved_by=user-external)。所有流转写入 `cc_stage_transitions` 账本。 - -### 5.4 阶段级设置(cc_stage_settings) - -- `scope=global` 为每阶段默认 `{model, executor}`;`scope=task` 覆盖指定任务。 -- 解析顺序:task 覆盖优先,global 兜底(`tasks.ResolveStageSetting`)。 -- 通过 agent instruction 包的 `model`/`executor` 字段下发;`executor=human|external` 表示该阶段不由 agent 执行。 - -## 6. API 全量清单 - -鉴权:`/healthz` 与非 `/api/` 路径公开;`/api/v1/agent/*` 校验 agent token(`cc_agents.token_hash`,SHA-256);其余 `/api/*` 校验 console token(`CC_CONSOLE_TOKEN`,常量时间比较)。错误体统一 `{"error": "..."}`;ErrNotFound→404、ErrConflict/ErrBadState→409、ErrBadInput→400。 - -### 6.1 Agent API(bearer = agent token) - -| 方法 路径 | 用途 | 关键字段 / 行为 | -|---|---|---| -| GET `/api/v1/agent/tasks/next` | 取当前 agent 最高优先可认领任务 | 仅 `assigned_agent=self` 且 `stage_state∈{assigned,changes_requested}` 且未终态;无可认领 → 204 | -| GET `/api/v1/agent/tasks/{id}/instruction` | 阶段指令包 | `{task, stage, stage_state, deliverable, model, executor, decision_rules, recent_events(20), prior_artifacts}` | -| POST `/api/v1/agent/tasks/{id}/claim` | 认领 | 获 2h 租约;stage_state→in_progress;冲突(他人租约/他人指派)→409 | -| POST `/api/v1/agent/tasks/{id}/heartbeat` | 心跳续租 | 仅当前租约持有者可续 2h;否则 409 | -| POST `/api/v1/agent/tasks/{id}/events` | 上报事件 | `phase`(9 枚举之一,非法 400)+ `message` + 可选 `payload`;写库并广播 SSE | -| POST `/api/v1/agent/tasks/{id}/artifacts` | 提交阶段产物 | `kind/title/content_md[/stage]`(stage 缺省=当前阶段);入库后异步切片向量化 | -| POST `/api/v1/agent/tasks/{id}/advance-request` | 交回人工评审 | 仅 `in_progress` 且本人;stage_state→awaiting_review,租约释放;`summary`+`artifact_ids` 记入事件 | -| POST `/api/v1/agent/tasks/{id}/decisions` | 决策上报 | `question` 必填、`suggestions≥1` 且每条 `option/rationale/impact` 非空,否则 400;成功后任务→`blocked_pending_decision` | -| POST `/api/v1/agent/tasks/{id}/release` | 主动释放租约 | stage_state 回 assigned | -| POST `/api/v1/agent/reports` | 提交报告 | `task_id/kind∈{result,test-report,handoff}/content_md[/links]` | - -### 6.2 Console API(bearer = console token) - -| 方法 路径 | 用途 | 关键字段 / 行为 | -|---|---|---| -| POST `/api/v1/plans` | 建计划 | `slug/title/horizon/summary_md/doc_path` | -| GET `/api/v1/plans` | 计划列表 | — | -| PATCH `/api/v1/plans/{id}` | 改计划 | PlanPatch | -| POST `/api/v1/tasks` | 建任务 | `slug/title[/kind=task|proposal][/plan_id][/priority][/testing_required 默认 true][/instruction_md][/doc_path][/mutex]` | -| GET `/api/v1/tasks` | 任务列表 | query: `status/stage/plan_id/q`;按 priority desc, created_at asc | -| GET `/api/v1/tasks/{id}` | 任务详情 | — | -| PATCH `/api/v1/tasks/{id}` | 改任务 | 可选字段 patch;`assign=` 指派(idle→assigned,planned→in_progress) | -| POST `/api/v1/tasks/{id}/stages/advance` | 人工推进 | 仅 `awaiting_review`;confirmation 推进 → `done:true` | -| POST `/api/v1/tasks/{id}/stages/send-back` | 打回 | 仅 `awaiting_review` → changes_requested;`note` | -| POST `/api/v1/tasks/{id}/stages/{stage}/external-submit` | 外部阶段提交 | stage 必须是当前阶段(否则 409);`advance:true` 时按外部人工推进 | -| GET `/api/v1/tasks/{id}/pipeline` | 流水线视图 | 8 阶段各自的 artifacts/transitions/decisions | -| POST `/api/v1/tasks/{id}/export-doc` | 导出 .doc 任务文档 | 写 `.doc/ops/short-term/YYYY-MM-DD-.md`;slug 须匹配 `^[a-z0-9-]+$`;已存在 → 409 | -| GET `/api/v1/decisions` | 决策收件箱 | query `status=open|resolved|all` | -| POST `/api/v1/decisions/{id}/resolve` | 裁决 | `resolution` 或 `suggestion_index` 二选一(都缺 400);裁决后任务 unblock 回 in_progress | -| GET `/api/v1/stage-settings` | 阶段设置 | query `task_id` 时合并 task 覆盖 | -| PUT `/api/v1/stage-settings` | 写阶段设置 | 单条 `{stage,model,executor}` 或批量 `settings` map;executor 非法 400 | -| GET `/api/v1/overview` | 总览 | task_counts/open_decisions/awaiting_review/open_findings/agents(心跳 >5min 记 stale) | -| GET `/api/v1/stream` | SSE 事件流 | 见 §9 | -| GET `/api/v1/docs` | 文档列表 | query `taxonomy/q` | -| GET `/api/v1/docs/search` | 文档检索 | 见 §7 | -| GET `/api/v1/docs/{id}` | 文档详情 | — | -| GET `/api/v1/audits/runs` | 审查运行列表 | query `limit`(默认 20,上限 200) | -| GET `/api/v1/audits/findings` | findings 列表 | query `status/severity/limit`(默认 100,上限 500) | -| POST `/api/v1/audits/run` | 手动触发审查 | 后台执行,202 返回 `run_id`;已有运行中 → 409 | -| POST `/api/v1/audits/findings/{id}/convert` | finding 转任务 | 生成 `kind=proposal` 任务(slug `audit-`);非 open → 409 | -| POST `/api/v1/audits/findings/{id}/dismiss` | 忽略 finding | 非 open → 409;不存在 → 404 | - -## 7. 文档向量管线 - -- **入库**:`docs.Syncer` 周期(默认 60s)walk `/.doc/**/*.md`(跳过 `assets/` 目录);按文件 sha256 差异做增删改;front matter 解析标题/taxonomy(taxonomy=.doc 下第一级目录)。 -- **切片**:按 ATX 标题切节;节超 1800 估算 token(CJK=2,ASCII≈len/4)按段落递归二分,单巨段按 rune 硬切;标题行保留在 chunk 正文内。`chunk_hash = sha256(owner:source:seq:content)`,未变化 chunk 不重嵌。 -- **Embedding**:`embed.HTTPClient`,OpenAI 兼容 `/embeddings`,批 ≤16,5xx/网络错误重试一次;模型 bge-m3,维度 1024,写入 `cc_doc_chunks.embedding vector(1024)`(HNSW cosine)。失败降级:chunk 以 NULL embedding 入库,同步不失败。 -- **产物向量化**:agent/external 提交 artifact 后后台(2min 超时)按同一切片规则入库,`chunk_hash` 加 `artifact:` 前缀,`doc_id` 为 NULL。 -- **检索** `GET /api/v1/docs/search?q=&mode=hybrid|semantic|keyword&limit=`(默认 20,上限 100): - - semantic:`1 - (embedding <=> q)` 余弦相似度 top-N; - - keyword:`tsv @@ plainto_tsquery('simple')` 的 ts_rank + content ILIKE(+0.5) + heading ILIKE(+0.3); - - hybrid(默认):两路各自按 max 归一后 0.6·向量 + 0.4·关键词融合排序; - - embedding 不可用(未配置或调用失败)→ 自动降级 keyword-only,响应 `degraded:true`。 - - 结果条目:`{score, snippet(≤300 rune), heading, source:{type:doc|artifact, …}, doc_id, artifact_id}`。 - -## 8. 导入 / 导出与 .doc 的双向关系 - -**原则:git 仓库中的 `.doc` 文档是可审计真相来源;DB 是索引与协作层。** - -- **导入(.doc → DB)**:`importer` 扫描 `.doc/ops/short-term`、`.doc/ops/long-term`、`.doc/backlog`,每个 md 文件 upsert 一条 cc_tasks(slug=文件名去 .md),并确保 short-term/long-term/backlog 三个 plan 存在。front matter `status/owner/branch/worktree/mutex` 映射入库;`blocked` 映射为 `blocked_pending_decision`;未知 status 回落 planned。覆盖规则:title/owner/branch/worktree/mutex/instruction_md/doc_path 跟随文档;status 仅在任务 planned/in_progress 时跟随;**stage/stage_state/priority/assignment/终态 status 导入器永不覆盖**。同名文件多目录出现时按 planDirs 顺序取先者。`control-center import-docs` 可手动执行;serve 内按 `CC_DOC_SYNC_SECONDS` 周期执行。 -- **导出(DB → .doc)**:`POST /api/v1/tasks/{id}/export-doc` 按 `.doc/ops/templates/short-term-task.md` 渲染任务文档写入 `.doc/ops/short-term/`;文件已存在 409(绝不覆盖);导出后回写 `doc_path` 并记 system 事件;随后周期性 docs sync 会把它当普通 .doc 文件入库向量化。 - -## 9. SSE 与租约语义 - -- **SSE**:`GET /api/v1/stream`(console token)。每条 `cc_task_events` 写入经 broker 广播为 `data: `;连接后先发 `: connected`,每 30s 发 `: ping` 保活。事件唯一写路径是 `server.appendEvent` / agent events 路由。 -- **租约**:`claim` 获得 2h 租约(`tasks.LeaseDuration`),`heartbeat` 续租(仅持有者),`release` 主动释放回 assigned。lease reaper 每 60s 扫描 `stage_state=in_progress` 且租约过期的任务,回收为 assigned 并记 system 事件("lease expired, task unclaimed")——孤儿任务自动回到可认领池。 -- 总览中的 agent 活跃度:最后心跳距今 >5min(`StaleHeartbeatAfter`)记 stale,与 2h 租约是两个不同阈值(前者是 UI 活性提示,后者是强制回收)。 - -## 10. 审查子系统 - -- **调度**:`CC_AUDIT_ENABLED=true`(默认)时按 `CC_AUDIT_DAILY_AT`(默认 `03:30`,本地时间,分钟级 ticker)每日触发;也可 `POST /api/v1/audits/run` 手动触发(202 后台执行,30min 超时)。并发由 PG advisory lock(key `0x6363_6175_6469_74`)+ `running` 行检查保证,重复触发返回 409。 -- **确定性检查(必跑,纯 SQL + git)**: - 1. `awaiting_review` 超 24h(medium/unfinished); - 2. `in_progress` 心跳超 30min 或租约过期(high/unfinished); - 3. `blocked_pending_decision` 超 48h(high/gap); - 4. `planned` 超 7 天未动(low/gap); - 5. 任务声明的 branch 在本地仓库不存在(`git show-ref --verify`,medium/doc-drift;非法 ref 名跳过)。 -- **LLM review(可选,降级不失败)**:上下文 = 上次 completed run 以来的 `git log --oneline --stat`(≤200 行,无历史 run 则取 24h)+ 未完成任务(≤50)+ 最新事件(≤100)+ 已有 open findings(≤50);system prompt 要求只输出 JSON 数组 `{severity,kind,title,detail,refs}`;解析容忍 ```json 围栏与首尾散文(取第一个 `[` 到最后一个 `]`),非法 severity/kind 回落 medium/gap,无 title 丢弃。LLM 未配置/失败只记入 run summary,run 仍 completed。 -- **去重**:`INSERT … WHERE NOT EXISTS (open finding with same title)`,跨 run 同名 open finding 抑制插入。 -- **处置**:finding `open → converted`(一键生成 proposal 任务)或 `open → dismissed`;转换把 detail_md + refs 写入任务 instruction_md。 - -## 11. 配置项清单 - -`CC_*` 优先;标注「借用」的 key 在未设 `CC_*` 时从系统 `.env`(`CC_ENV_FILE`,默认 `/.env`)或进程环境读取。只列 key 名,不含值。 - -| Key | 默认 | 说明 | -|-----|------|------| -| `CC_HTTP_ADDR` | `127.0.0.1:8790` | 监听地址;设为 `0.0.0.0:8790` 或 `scripts/control-center.sh serve-lan` 对外开放 | -| `CC_DB_DSN` | `postgres://cc:cc@127.0.0.1:5440/control_center?sslmode=disable` | 独立库 DSN | -| `CC_CONSOLE_TOKEN` | 空(loopback 时生成临时 token 并打印告警) | console bearer token;**绑定非 loopback 地址时强制要求显式设置固定 token,否则拒绝启动** | -| `CC_REPO_ROOT` | `.` | 仓库根(.doc 扫描/git 审查基准) | -| `CC_ENV_FILE` | `/.env` | 借用配置来源 | -| `CC_MIGRATIONS_DIR` | `/deploy/control-center/goose` | goose 迁移目录 | -| `CC_EMBEDDING_ENDPOINT_URL` / 借用 `EMBEDDING_ENDPOINT_URL` | 空(空则检索降级 keyword-only) | embeddings 端点 | -| `CC_EMBEDDING_MODEL` / 借用 `EMBEDDING_MODEL` | `bge-m3` | embedding 模型 | -| `CC_EMBEDDING_API_KEY` / 借用 `EMBEDDING_API_KEY` | 空 | embeddings key | -| `CC_LLM_ENDPOINT_URL` / 借用 `MINIMAX_ENDPOINT_URL` | 空 | 审查 LLM 端点 | -| `CC_LLM_MODEL` / 借用 `MINIMAX_MODEL_ID` | 空 | 审查 LLM 模型 | -| `CC_LLM_API_KEY` / 借用 `MINIMAX_API_KEY` | 空 | 审查 LLM key | -| `CC_CURSOR_MODEL` / 借用 `CURSOR_MODEL` | 空 | 预留的 Cursor 模型名 | -| `CC_AUDIT_ENABLED` | `true` | 每日审查开关 | -| `CC_AUDIT_DAILY_AT` | `03:30` | 每日审查时刻(本地 HH:MM,非法值回落 03:30) | -| `CC_DOC_SYNC_SECONDS` | `60` | .doc 同步 + 任务导入周期(秒) | -| `CC_SERVER` | `http://127.0.0.1:8790` | agent CLI 默认 server | -| `CC_AGENT_TOKEN` | 空 | agent CLI 默认 token | - -compose 侧:`CC_DB_USER/CC_DB_PASSWORD/CC_DB_NAME/CC_DB_PORT`(默认 cc/cc/control_center/5440)。 - -## 12. 前端 SPA - -`control-center/web`(Vite 7 + React + TS):页面 Overview / Board(阶段看板)/ TaskDetail(stepper + 事件流)/ Decisions / Docs(搜索)/ Audit / Settings;console token 存 localStorage(`cc_console_token`),fetch 包装器统一注入 bearer。`npm run build` 输出到 `internal/controlcenter/webui/dist`(提交入库,`go:embed`),因此 Go 二进制自包含,无需单独部署前端。 diff --git a/.doc/system-design/project-control-center/agent-integration.md b/.doc/system-design/project-control-center/agent-integration.md deleted file mode 100644 index 17750a1c..00000000 --- a/.doc/system-design/project-control-center/agent-integration.md +++ /dev/null @@ -1,154 +0,0 @@ -# Control Center Agent 接入规范 - -> 面向在本仓库工作的编码 agent(Cursor / Claude Code / Kimi / Codex / PI 等)。 -> 架构细节见 [02-control-center-design.md](./02-control-center-design.md);本文是操作手册。 -> **本规范与仓库 AGENTS.md 同时生效**:分支纪律、文档同步红线、集成槽位等不因接入中控台而豁免。 - -## 1. 认证与接入 - -1. 让人类(或已持 console token 的运维流程)注册 agent,拿到一次性明文 token: - - ```bash - go run ./cmd/control-center register-agent -name [-kind kimi|cursor|pi|generic] - # 输出: agent "" registered. token (shown once): - ``` - - 重复执行同名即轮换 token。服务端只存 SHA-256 hash(`cc_agents.token_hash`)。 - -2. 配置环境变量(agent CLI 与 curl 通用): - - ```bash - export CC_SERVER=http://127.0.0.1:8790 # 默认即此值 - export CC_AGENT_TOKEN= - ``` - -3. 所有 agent 请求带 `Authorization: Bearer $CC_AGENT_TOKEN`,路径前缀 `/api/v1/agent/`。 - -## 2. 何时接入 - -- **会话启动时**:完成 AGENTS.md 启动协议(读 AGENTS.md / PROJECT.md / CURRENT 等)后,执行一次 `next` 查看是否有指派给自己的任务;有则优先按本规范接续工作。 -- **手头工作完成或被打回修改后**:再次 `next`,**同一任务可能已进入下一阶段**——stage 变了就必须重新 `instruction` 拿新指令包,不得沿用上一阶段的指令。 -- **空闲轮询**:长期驻留的 agent 以 5–15min 节奏 `next`;无任务(204)即静默等待,不要刷屏 event。 -- **人类口头指派的临时工作**:若未走中控台,完成后主动提醒人类「此工作未入流水线」,由人类决定是否补录任务。 - -## 3. 标准工作循环 - -```text -next ──▶ instruction ──▶ claim ──▶ ┌ event (start/plan/progress/…) ─┐ - │ artifact (阶段产物, 可多次) │ - │ heartbeat (<2h 节奏续租) │ - └────────────循环───────────────┘ - │ - 完成本阶段工作 │ 需要人类拍板 - ▼ ▼ - advance-request decision 上报 - (交回人工评审, (任务转 blocked_pending_decision, - 租约释放,等人工) 停止推进,等 resolve) -``` - -1. **next**:发现分配给自己、可认领(`assigned` 或 `changes_requested`)的最高优先任务。204/`{"task":null}` 表示无事可做。 -2. **instruction -task ID**:拿阶段指令包——`deliverable`(本阶段交付要求)、`decision_rules`(决策规则原文)、`model`/`executor`(阶段级配置)、`prior_artifacts`(前序阶段产物)、`recent_events`。**动手前必读**: - - `executor` 非 `agent`(human/external)时,该阶段不由你执行——直接放弃认领,回 `next`; - - `model` 指定了本阶段使用的模型时,按指定模型执行;自身无法满足(如模型不可用)时上报 decision,不要静默换模型; - - 指令包内容不清晰、有歧义、缺关键约束时,**先 decision 上报再动手**,禁止带着猜测开工。 -3. **claim -task ID**:认领,获 2h 租约,任务转 `in_progress`。409 表示他人持有租约或已指派他人——放弃,不要抢。 -4. 工作中(节奏规范见 §8): - - **event**:记录进度(`phase=start/plan/progress/report/done`);**卡住时 `phase=blocked` 并写清原因**。 - - **artifact**:每个阶段交付物按 §7 的 kind 提交 markdown 全文(`-file` 或 `-content`)。 - - **heartbeat**:节奏必须 **< 2h**(租约时长)。租约过期会被回收为 `assigned`(系统事件留痕),你的工作上下文对他人重新可见。建议每 15–30min 一次。 -5. **advance-request -task ID -summary "…"**:本阶段完成后交回人工评审。summary 写「做了什么 + 产物索引 + 自评是否满足交付要求」。此后**停止动该任务**,等 console 人工 advance(进入下一阶段)或 send-back(打回 `changes_requested`,你可重新 claim 修改)。 -6. 中途放弃:`release -task ID` 主动释放租约,并在 message 里留下进度说明方便他人接手。 - -## 4. 铁律(违反即 409/400 或人工打回) - -1. **禁止自动推进阶段**:agent API 没有任何 advance 路由;你唯一能做的时间线动作是 `advance-request`(`in_progress → awaiting_review`)。任何「替我点一下推进」的尝试都违反设计。 -2. **决策必须上报**:方案取舍、范围变更、触碰红线风险等需要人类拍板的问题,调 `decision` 上报,**禁止自行猜测关键决策**。质量要求见 §6。 - - 上报成功后任务自动转 `blocked_pending_decision`,**停止推进该任务**,等人类在 console resolve(裁决后任务自动回 `in_progress`)。 -3. **blocked 即停**:无法继续时先 `event -phase blocked -message <原因>`;需要决策的阻塞走第 2 条。 -4. **仓库红线并行生效**:一律在 `feature/*` / `fix/*` 分支提交、文档按 AGENTS.md taxonomy 落盘、合入 dev 须占集成槽位。流水线阶段流转不替代这些规矩(例如 implementation 阶段的代码仍走正常分支与提交流程)。 -5. 心跳断了别假设租约还在:claim/heartbeat 返回 409 就按提示放弃或重新认领。 - -## 5. 阶段行为细则 - -每阶段的「进入条件 → 该做什么 → 退出条件」: - -| 阶段 | 进入条件 | 你该做什么 | 退出(advance-request)条件 | -|------|----------|-----------|---------------------------| -| proposal | 任务被指派给你 | 读 instruction + 关联文档(可用 `/api/v1/docs/search` 检索历史材料);把人类的一句话意图展开为结构化提案 | proposal artifact 含问题陈述/目标/范围/候选方案对比;未决取舍已全部 decision 上报 | -| analysis | 人类批准提案 | 读代码与 `.doc` 取证,结论必须有文件路径/行号级证据 | analysis artifact 含现状证据/约束/风险清单 | -| design | 人类批准分析 | 产出方案:模块划分、接口、数据结构、迁移步骤;与既有规范核对 | design artifact 完整到「另一个 agent 只看它能实现」 | -| review | 人类批准方案 | 自检:对照 AGENTS/PROJECT/regulations 与任务验收标准逐条核对;找自己方案的漏洞 | review artifact 含自检清单与每项结论 | -| implementation | 人类批准评审 | 按方案在 feature 分支编码,分批原子提交;同步更新任务文档 | implementation artifact 含变更文件清单与提交 hash;`go vet`/`go test -short` 绿 | -| testing | 人类批准实施(任务 testing_required=true 才有此阶段) | 按 PROJECT.md 命令跑测试;失败先修再报 | test-report artifact + `report -kind test-report` 双写 | -| evaluation | 人类批准测试 | 对照 proposal 阶段的目标逐条验收,诚实列出未达成项 | evaluation artifact 含达成/未达成/遗留清单 | -| confirmation | 人类批准评价 | 整理交付物索引、handoff、回链证据 | 材料齐后 advance-request,**等人类最终确认**——任务 done 由人类完成 | - -被打回(`changes_requested`)时:重新 claim → 读打回 note(transitions 里)→ 修正 → 重新 advance-request。 - -## 6. 决策上报质量规范 - -**必须上报的情形**(不限于):需求歧义或自相矛盾;多种合理技术路线且影响架构;需要越权(动主干分支、删数据、改他人 mutex 模块);范围膨胀(发现任务实际工作量远超描述);规范之间冲突。 - -**写法要求**: - -- `question`:一句话说清要人类拍板什么,带任务上下文。 -- `context_md`:证据(文件路径、行号、命令输出摘要),让人类不看代码也能判断。 -- `suggestions`:至少 1 条,**推荐项放第一条**;每条三段—— - - `option`:具体可执行的选项(不是「方案一」这种空话); - - `rationale`:为什么推荐/为什么不推荐,基于证据; - - `impact`:选它的代价、风险、对下游阶段的影响。 -- 没有倾向时必须明说「无推荐,各选项权衡如下」,禁止隐藏自己的判断。 - -## 7. 各阶段交付物规范(`pipeline.DeliverableSpec`,instruction 包原文下发) - -| 阶段 | 交付要求 | artifact kind | -|------|----------|---------------| -| proposal | 问题陈述、目标、范围边界、候选方案对比;需要拍板的取舍用 decisions 上报 | `proposal` | -| analysis | 现状/代码证据、约束、风险清单;发现阻塞问题立即上报 decision,不得自行猜测 | `analysis` | -| design | 模块划分、接口、数据结构、迁移方案 | `design` | -| review | 自检清单、与既有规范(AGENTS/PROJECT/regulations)一致性核对 | `review` | -| implementation | 代码改动说明、提交计划、变更文件清单;实际编码在仓库分支上进行 | `implementation` | -| testing | 测试命令、结果、覆盖率与失败分析;**另需** `report -kind test-report` | `test-report` | -| evaluation | 对照 proposal 目标验收,列出达成/未达成项与遗留问题 | `evaluation` | -| confirmation | 交付物索引、handoff 说明、回链证据;等待人类最终确认后任务 done | (材料随 advance-request 摘要/artifact) | - -产物质量底线:markdown 格式;给出文件路径/命令/commit 等可核验证据;如实标注未完成项。产物提交后服务端异步切片入向量库(2min 超时,失败只记日志),可被 `/api/v1/docs/search` 检索。 - -## 8. 事件与心跳节奏 - -- `start`:claim 成功后第一条事件,写你对本阶段任务的一句话理解(防止理解偏差到交付才暴露)。 -- `plan`:动手前输出工作计划要点。 -- `progress`:里程碑粒度(完成一个子步骤/遇到关键发现),不要每分钟刷屏。 -- `blocked`:一卡住就发,写明阻塞原因与需要什么。 -- heartbeat:每 15–30min;长时间无输出(如下载/构建)也要保持心跳。 - -## 9. CLI 子命令速查(`cmd/control-center/agentcli.go`) - -公共 flag:`-server`(默认 `$CC_SERVER` 或 `http://127.0.0.1:8790`)、`-token`(默认 `$CC_AGENT_TOKEN`);`-task` 接受任务 uuid(用 `next` 发现 id)。二进制可用 `go run ./cmd/control-center ` 代替。 - -| 子命令 | flag | 等价 curl | -|--------|------|-----------| -| `next` | — | `curl -H "Authorization: Bearer $CC_AGENT_TOKEN" $CC_SERVER/api/v1/agent/tasks/next` | -| `instruction` | `-task ID` `[-md]`(-md 输出可读排版,默认 JSON) | `curl -H … $CC_SERVER/api/v1/agent/tasks/$ID/instruction` | -| `claim` | `-task ID` | `curl -X POST -H … $CC_SERVER/api/v1/agent/tasks/$ID/claim` | -| `heartbeat` | `-task ID` | `curl -X POST -H … $CC_SERVER/api/v1/agent/tasks/$ID/heartbeat` | -| `release` | `-task ID` | `curl -X POST -H … $CC_SERVER/api/v1/agent/tasks/$ID/release` | -| `event` | `-task ID -phase P -message M [-payload '']`(phase ∈ start/plan/progress/blocked/report/done/advance-request/decision/system) | `curl -X POST -H … -H 'Content-Type: application/json' -d '{"phase":"progress","message":"…"}' $CC_SERVER/api/v1/agent/tasks/$ID/events` | -| `artifact` | `-task ID -kind K -title T [-file F \| -content MD]` | `curl -X POST -H … -d '{"kind":"analysis","title":"…","content_md":"…"}' $CC_SERVER/api/v1/agent/tasks/$ID/artifacts` | -| `advance-request` | `-task ID -summary S [-artifacts id1,id2]` | `curl -X POST -H … -d '{"summary":"…","artifact_ids":["…"]}' $CC_SERVER/api/v1/agent/tasks/$ID/advance-request` | -| `decision` | `-task ID -question Q -context MD -suggestion "选项||理由||影响"`(分隔符为两个 ASCII 竖线 `||`;`-suggestion` 可重复,至少一次) | `curl -X POST -H … -d '{"question":"…","context_md":"…","suggestions":[{"option":"…","rationale":"…","impact":"…"}]}' $CC_SERVER/api/v1/agent/tasks/$ID/decisions` | -| `report` | `-task ID -kind result\|test-report\|handoff [-file F \| -content MD]` | `curl -X POST -H … -d '{"task_id":"…","kind":"test-report","content_md":"…"}' $CC_SERVER/api/v1/agent/reports` | - -CLI 输出为紧凑 JSON;失败时把服务端 `error` 字段打到 stderr 并以非零码退出。 - -## 10. 错误语义与处理 - -| 状态码 | 含义 | agent 处理 | -|--------|------|------------| -| 400 | 请求不合规:非法 phase、decision 缺建议/三段不全、JSON 体坏等 | 修正请求体重试;不要原样重发 | -| 401 | token 缺失或非法 | 检查 `CC_AGENT_TOKEN`;仍失败则让人类用 `register-agent -name <你>` 轮换 | -| 404 | 任务/资源不存在 | 重新 `next` 发现任务;可能已被取消 | -| 409 | 状态冲突:他人租约/指派、非 `in_progress` 调 advance-request、任务已终态等 | **放弃当前动作**,回 `next` 重新认领;禁止强行重试 | -| 500 | 服务端内部错误 | 记 `event -phase blocked`,把错误原文写进 message,等人类处理 | - -204(`next` 无可认领任务)与 201/200 均为正常响应,不是错误。 diff --git a/.doc/test-reports/2026-09-17-control-center-test-report.md b/.doc/test-reports/2026-09-17-control-center-test-report.md deleted file mode 100644 index aa49a774..00000000 --- a/.doc/test-reports/2026-09-17-control-center-test-report.md +++ /dev/null @@ -1,156 +0,0 @@ -# 个人中控台 Control Center(P0–P6)测试报告 - -- 日期:2026-09-17 -- 分支:`feature/control-center-server` -- 范围:P0 骨架 → P5 定时审查 全部后端能力 + P6 文档收口;任务文档 [.doc/ops/short-term/2026-09-17-control-center-server.md](../ops/short-term/2026-09-17-control-center-server.md) -- 关联设计:[02-control-center-design.md](../system-design/project-control-center/02-control-center-design.md)、[agent-integration.md](../system-design/project-control-center/agent-integration.md) - -## 一、 测试背景 (Background) - -个人中控台 Control Center 是独立于产品系统的单用户项目控制平面:8 阶段任务流水线、agent 协作 API(禁止自动推进阶段)、`.doc` 向量文档库、每日仓库审查、React SPA。P0–P5 以 6 个 commit(`2be43ef`→`07a8e12`)完成于本分支,本报告为 P6 交付前的全量验收。 - -## 二、 测试目标 (Test Objectives) - -逐条核对任务文档验收标准: - -1. `go vet ./...` / `go test -short ./...` / `go build ./cmd/control-center` 全绿; -2. 状态机单测:agent 直接推进被拒、decision 缺建议拒收、lease 过期孤儿恢复; -3. `.doc` 全量入库后语义搜索命中已知文档; -4. curl 冒烟:建任务→agent claim→产物→advance-request→人工批准→下一阶段 全链路; -5. 前端 `vite build` 通过并 embed 可用。 - -补充验证:token 鉴权负例、agent 无 advance 路由负例、overview/pipeline/audits 端点、DB 集成测试(真实 pgvector 容器)。 - -## 三、 测试方案与环境 (Test Plan & Environment) - -- OS:Linux 7.0.0-31-generic x86_64;Go 1.27.1;Node v26.8.2(vite 7.3.6) -- 数据库:真实容器 `control-center-db`(pgvector/pgvector:pg16,127.0.0.1:5440,Up healthy),goose 版本 1 -- Embedding:真实 bge-m3 端点(借用系统 `.env` 的 `EMBEDDING_*`),hybrid 检索 `degraded:false` -- 服务实例:`/tmp/control-center-bin serve`,`CC_HTTP_ADDR=127.0.0.1:8799`(避开默认 8790)、`CC_CONSOLE_TOKEN=p6-smoke-token`、`CC_AUDIT_ENABLED=false`、`CC_DOC_SYNC_SECONDS=3600` -- 分层:全仓 vet/单测 → controlcenter 包 DB 集成测试(`TEST_CC_DB_DSN`)→ 前端构建 → 二进制构建 → 真实 HTTP 冒烟 - -## 四、 预期结果 (Expected Results) - -- 四条构建/测试命令退出码 0,无 FAIL; -- 状态机负例:agent advance 路由不存在(404/405);decision 缺建议 400;advance-request 二次提交 409;租约冲突 409; -- 搜索对「中控台 流水线 阶段」命中系统设计文档,`degraded:false`; -- 冒烟全链路 stage 从 proposal 推进到 analysis(`testing_required=false` 时跳 testing,链路中已验证),任务账本有 transitions 记录; -- SPA `GET /` 200 text/html。 - -## 五、 实际测试结果与判定 (Actual Results & Evaluation) - -### 5.1 自动化套件 - -**`go vet ./...`** — 无任何输出,退出码 0。**PASS** - -**`go test -short ./...`** — 全仓无 FAIL 行,退出码 0(ok 行全部通过)。**PASS** - -**`TEST_CC_DB_DSN=postgres://cc:cc@127.0.0.1:5440/control_center?sslmode=disable go test -count=1 ./internal/controlcenter/...`** — 真实输出: - -```text -ok chat-agent-system/internal/controlcenter/audit 0.237s -? chat-agent-system/internal/controlcenter/config [no test files] -ok chat-agent-system/internal/controlcenter/docs 0.205s -ok chat-agent-system/internal/controlcenter/embed 0.004s -ok chat-agent-system/internal/controlcenter/importer 0.017s -ok chat-agent-system/internal/controlcenter/llm 0.004s -ok chat-agent-system/internal/controlcenter/pipeline 0.002s -ok chat-agent-system/internal/controlcenter/server 0.766s -? chat-agent-system/internal/controlcenter/store [no test files] -? chat-agent-system/internal/controlcenter/tasks [no test files] -? chat-agent-system/internal/controlcenter/webui [no test files] -``` - -**PASS**(含状态机门禁:agent advance 路由缺失、decision 三段校验 400、advance-request 二次 409、claim 冲突 409、testing 跳过、confirmation 推进即 done;lease 过期孤儿恢复由 `internal/controlcenter/audit`/`server` 集成用例与 reaper 单测覆盖)。 - -**`cd control-center/web && npm run build`** — `tsc && vite build` 通过: - -```text -vite v7.3.6 building client environment for production... -✓ 42 modules transformed. -../../internal/controlcenter/webui/dist/index.html 0.40 kB -../../internal/controlcenter/webui/dist/assets/index-DyNgdwvP.css 9.33 kB -../../internal/controlcenter/webui/dist/assets/index-Dj6oWpHL.js 303.55 kB -✓ built in 595ms -``` - -**PASS**(产物 embed 进 `internal/controlcenter/webui/dist`,与已提交内容一致,无 diff)。 - -**`go build -o /tmp/control-center-bin ./cmd/control-center`** — 成功。**PASS** - -### 5.2 真实服务冒烟(curl) - -启动日志: - -```text -2026/09/17 12:51:09 goose: no migrations to run. current version: 1 -2026/09/17 12:51:09 INFO control-center listening addr=127.0.0.1:8799 -2026/09/17 12:51:09 INFO doc task import done added=0 updated=0 skipped=47 -``` - -| # | 用例 | 实际输出(节选) | 判定 | -|---|------|------------------|------| -| 1 | `GET /healthz` | `{"status":"ok"}` HTTP 200 | PASS | -| 2 | `GET /`(SPA) | HTTP 200 `text/html; charset=utf-8` | PASS | -| 3 | `GET /api/v1/overview` 无 token | `{"error":"missing bearer token"}` HTTP 401 | PASS | -| 4 | `GET /api/v1/overview` console token | HTTP 200,`task_counts` 7 组、`open_decisions:1`、`awaiting_review:24`、`open_findings:18`、`agents` 含心跳活性 | PASS | -| 5 | `GET /api/v1/docs/search?q=中控台 流水线 阶段&mode=hybrid&limit=3` | `degraded:false count:3`;top1 score=0.6,heading `### 8. 个人工程中控台`,source `.doc/system-design/README.md`(系统设计文档索引) | PASS(语义命中已知文档) | -| 6 | `GET /api/v1/tasks?stage=proposal` → `GET /api/v1/tasks/{id}/pipeline` | `stage:proposal stage_state:assigned stages:8`;每阶段 artifacts/transitions/decisions 分组完整 | PASS | -| 7 | `GET /api/v1/audits/runs?limit=3` | HTTP 200,3 条 completed 运行(含 findings 计数与 summary) | PASS | - -### 5.3 冒烟全链路(建任务→claim→产物→advance-request→人工批准) - -```text -POST /api/v1/tasks {"slug":"p6-acceptance-smoke","testing_required":false} → 201 id=be3b1f57-… -register-agent -name p6-smoke-agent → agent "p6-smoke-agent" registered. token (shown once): … -PATCH /api/v1/tasks/{id} {"assign":"p6-smoke-agent"} → after assign: proposal assigned p6-smoke-agent -POST /api/v1/agent/tasks/{id}/claim → after claim: in_progress lease: 2026-09-17T14:52:09Z(2h) -POST /api/v1/agent/tasks/{id}/artifacts → 201 {"id":"3688fda9-…"} -POST /api/v1/agent/tasks/{id}/advance-request → after advance-request: awaiting_review -POST /api/v1/tasks/{id}/stages/advance {"note":"P6 冒烟批准"} → after human advance: analysis assigned done:false -``` - -**PASS**:proposal → analysis 由人工批准完成,agent 全程未触碰 stage。 - -### 5.4 负例 - -| 用例 | 实际输出 | 判定 | -|------|----------|------| -| agent 调 `POST /api/v1/agent/tasks/{id}/stages/advance` | HTTP 405(路由不存在;单测同时断言 404/405 且任务状态不变) | PASS | -| decision 缺建议:`{"question":"没有建议的问题"}` | `{"error":"bad input: at least one suggestion is required"}` HTTP 400 | PASS | - -### 5.5 冒烟中发现的缺陷与修复(本报告周期内闭环) - -- **缺陷**:`GET /api/v1/tasks?q=` 返回 500(`SQLSTATE 42601 syntax error at or near "$"`)。根因:`internal/controlcenter/tasks/tasks.go` `ListTasks` 的 Query 分支 format 串含两个 `$%d` 占位但只传一个参数,第二个被 fmt 渲染为 `%!d(MISSING)`。 -- **修复**:改为显式索引 `$%[1]d`(同一参数复用两次);新增回归测试 `TestTaskListQueryFilter`(`internal/controlcenter/server/server_test.go`)。 -- **复验**:修复后 `go vet` 通过、`./internal/controlcenter/...` 全部 ok、新测试 PASS;真实服务 `tasks?q=p6-acceptance-smoke` 返回 HTTP 200 且精确命中 1 条。 - -### 5.6 性能与耗时 - -controlcenter 包 DB 集成测试全量 ≈1.5s;前端构建 595ms;冒烟单请求均为毫秒级(本地 loopback)。 - -## 六、 自动化测试套件汇总 (Test Suite Execution) - -- `go vet ./...`:0 问题 -- `go test -short ./...`:全仓 PASS(无 FAIL 行) -- `go test -count=1 ./internal/controlcenter/...`(真实 pgvector):7 个有测试的包全部 ok,0 FAIL -- `npm run build`(control-center/web):PASS,产物 hash 与入库一致 -- 冒烟 7 端点 + 全链路 + 2 负例:全部 PASS - -## 七、 测试结论与后续优化建议 (Conclusion & Next Steps) - -**结论:后端 P0–P5 验收标准全部通过,达到合入标准。** 唯一冒烟缺陷(tasks `?q=` 500)已在周期内修复并附回归测试。 - -验收标准逐条: - -| 验收标准 | 结果 | -|----------|------| -| `go vet` / `go test -short` / `go build ./cmd/control-center` 全绿 | ✅ 通过 | -| 状态机单测:agent 推进被拒 / decision 缺建议拒收 / lease 过期恢复 | ✅ 通过(单测 + 冒烟负例) | -| `.doc` 全量入库后语义搜索命中已知文档 | ✅ 通过(hybrid,`degraded:false`,top1 命中系统设计索引) | -| curl 冒烟全链路(建任务→claim→产物→advance-request→人工批准→下一阶段) | ✅ 通过(proposal→analysis) | -| 前端 `vite build` 通过并 embed 可用 | ✅ 通过(`GET /` 200 text/html) | - -**未完成(如实标注)**:前端 SPA 人工走查(浏览器实机操作 Overview/Board/TaskDetail/Decisions/Docs/Audit/Settings 七页)未执行——本报告仅覆盖构建与 embed 服务可达。FEATURE-MATRIX 对应行保持 `partial`,走查通过后升 `done`。 - -后续建议:SPA 走查后补截图证据;审查子系统 LLM review 当前冒烟环境未配置 `MINIMAX_*`(输出 "LLM 未接入,仅确定性检查",属设计内降级),建议在用足配置的环境跑一次每日审查全链路;旧 `internal/engineeringdashboard` 弃用评估另立任务。 diff --git a/.doc/test-reports/2026-09-17-engineering-dashboard-test-report.md b/.doc/test-reports/2026-09-17-engineering-dashboard-test-report.md index 7f787ec5..4fd49747 100644 --- a/.doc/test-reports/2026-09-17-engineering-dashboard-test-report.md +++ b/.doc/test-reports/2026-09-17-engineering-dashboard-test-report.md @@ -2,7 +2,7 @@ ## 一、 测试背景 (Background) -仓库需要独立于产品后台的本地只读工程中控台,避免把文档 `done` 误读为已合并或已部署。方案见 [设计](../system-design/project-control-center/01-personal-engineering-control-center-plan.md)。本报告验收一期 `serve` / `export` 实现。 +仓库需要独立于产品后台的本地只读工程中控台,避免把文档 `done` 误读为已合并或已部署。方案见 [设计](../archive/design/2026-09-17-personal-engineering-control-center-plan.md)。本报告验收一期 `serve` / `export` 实现。 ## 二、 测试目标 (Test Objectives) @@ -106,4 +106,4 @@ git diff --check ## 相关链接 - 任务: [../archive/ops/short-term/2026-09-17-engineering-dashboard.md](../archive/ops/short-term/2026-09-17-engineering-dashboard.md) -- 设计: [../system-design/project-control-center/01-personal-engineering-control-center-plan.md](../system-design/project-control-center/01-personal-engineering-control-center-plan.md) +- 设计: [../archive/design/2026-09-17-personal-engineering-control-center-plan.md](../archive/design/2026-09-17-personal-engineering-control-center-plan.md) diff --git a/.doc/test-reports/2026-09-18-control-center-merge-gate-test-report.md b/.doc/test-reports/2026-09-18-control-center-merge-gate-test-report.md deleted file mode 100644 index ae648a13..00000000 --- a/.doc/test-reports/2026-09-18-control-center-merge-gate-test-report.md +++ /dev/null @@ -1,123 +0,0 @@ -# Control Center 合并门禁 测试报告 - -## 一、 测试背景 (Background) - -`feature/control-center-server` 要取代已合入 `dev` 的 `engineering-dashboard`。合并前缺三项证据:核心包单测、真实 LLM 审查、前端七页走查。本报告记录这三项以及端口加固、全量回归和 curl 全链路。 - -## 二、 测试目标 (Test Objectives) - -- `config` / `store` / `tasks` 有可重复的测试,且 PASS。 -- `CC_AUDIT_ENABLED=true` 时,真实 MiniMax 返回能被解析,findings 写入 `cc_audit_findings`,并在 `/api/v1/audits/runs` 可查。 -- Overview / Board / TaskDetail / Decisions / Docs / Audit / Settings 可打开,无页面 JS 错误,关键交互可用。 -- 数据库端口只绑定 `127.0.0.1`。`go vet`、`go build`、`go test -short`、`TEST_CC_DB_DSN` 集成测试、两边前端 build、`internal/archtest` 全绿。 -- curl 全链路:建任务 → claim → 产物 → advance-request → 人工批准 → 下一阶段。 - -## 三、 测试方案与环境 (Test Plan & Environment) - -- OS:Linux;Go 模块 `chat-agent-system`;数据库容器 `control-center-db`(pgvector/pg16),DSN `postgres://cc:cc@127.0.0.1:5440/control_center?sslmode=disable`。 -- 服务:`CC_HTTP_ADDR=127.0.0.1:8790`,`CC_AUDIT_ENABLED=true`。LLM 借用仓库 `.env` 的 `MINIMAX_*`(模型 `MiniMax-M2.7-highspeed`,端点 `https://api.minimaxi.com/v1`)。密钥不入库。 -- 前端走查:IDE 浏览器操作七页;截图由本机 headless Chromium 写入证据目录(无 pageerror / console.error)。 -- 证据目录:`.doc/test-reports/assets/2026-09-18-control-center-merge-gate/`。 - -## 四、 预期结果 (Expected Results) - -- `pick()`:`CC_*` 优先于 `.env` 借用键,再回退缺省(`EmbeddingModel=bge-m3`,`HTTPAddr=127.0.0.1:8790`)。 -- 租约:未指派不可 claim;他人 claim/heartbeat/release 冲突;过期租约被 reap 后原 agent 可再次 claim。 -- 审查:模型输出(含 ``)能解析出至少一条 finding 并落库。 -- 七页可加载;文档搜索 `流水线` 返回 hybrid 结果;看板可按标题过滤。 -- 冒烟任务从 `proposal` 经人工批准进入 `analysis`;agent 调用 `stages/advance` 得到 405。 - -## 五、 实际测试结果与判定 (Actual Results & Evaluation) - -### 5.1 核心包单测 - -| 包 | 命令 | 结果 | 耗时 | -|----|------|------|------| -| config | `go test -count=1 ./internal/controlcenter/config/` | PASS | 0.002s | -| store | 同上,带 `TEST_CC_DB_DSN` | PASS | 0.011s | -| tasks | 同上 | PASS | 0.019s | - -判定:PASS。覆盖 CC 优先、借用 `.env`、缺省回退;`Migrate` + `SELECT count(*) FROM cc_tasks`;claim/heartbeat/release 冲突与过期回收。 - -### 5.2 真实 LLM 审查 - -手动审查 run 194(生产路径 `POST /api/v1/audits/run`): - -```text -status=completed -summary=确定性 findings: 25;LLM findings: 0 -started_at=2026-09-19T03:00:17Z finished_at=2026-09-19T03:00:38Z -``` - -模型已接通(无“未配置/降级”备注),但返回空数组。随后用同一 `llm.HTTPClient.Chat` + `parseFindingsJSON` 强制一条 finding。首次失败:MiniMax 把推理放在 `` 里,其中的方括号让 JSON 截取失败(`invalid character 'C' after top-level value`)。修复后复跑: - -```text -run_id=195 status=completed summary=live LLM gate: 1 parsed finding -title=CC-GATE-LIVE: Real model response parsed but no downstream action triggered -severity=medium kind=gap status=open -refs=["internal/controlcenter/audit"] -``` - -`GET /api/v1/audits/runs?limit=3` 含 195 与 194。审计页可见该 finding。判定:PASS。摘录见 `llm-audit.json`(无密钥)。 - -### 5.3 七页走查 - -| 页 | 观察 | 判定 | -|----|------|------| -| 总览 `/#/` | 待裁决 1、待审 27、Open Findings 51;阶段矩阵与 agent 心跳 | PASS | -| 看板 `/#/board` | proposal 216 张卡;搜索 `Smoke` 命中 | PASS | -| 任务详情 | `Smoke Task`,指派框、批准/退回、Transitions | PASS | -| 决策收件箱 | 待裁决项,采纳建议 1/2,提交裁决 | PASS | -| 文档库 | 搜索「流水线」→ `搜索结果 · hybrid · 20 条` | PASS | -| 审计 | run 195 与 `CC-GATE-LIVE` finding | PASS | -| 设置 | 八阶段 model/executor,保存按钮,Agent 接入说明 | PASS | - -`spa-console.txt`:`no pageerror / console.error`。截图:`01-overview.png` … `07-settings.png`,以及 `02b-board-filter.png`、`05b-docs-search.png`。 - -### 5.4 端口与冒烟 - -`docker compose up -d --force-recreate` 后: - -```text -LISTEN 127.0.0.1:5440 -``` - -不再监听 `0.0.0.0:5440`。 - -curl 全链路(`curl-smoke.txt`,token 已替换): - -```text -GET /healthz -> 200 {"status":"ok"} -GET / -> 200 text/html -GET /api/v1/overview 无 token -> 401 missing bearer token -POST /api/v1/tasks -> 201 stage=proposal state=idle -PATCH assign -> stage_state=assigned -POST claim -> stage_state=in_progress lease=+2h -POST artifacts -> 201 -POST advance-request -> stage_state=awaiting_review(stage 仍为 proposal) -POST stages/advance -> 200 -after advance stage=analysis state=assigned -POST agent stages/advance -> 405 -``` - -判定:PASS。 - -## 六、 自动化测试套件汇总 (Test Suite Execution) - -日志:`verify.txt`。 - -| 命令 | 退出码 | -|------|--------| -| `go vet ./...` | 0 | -| `go build ./...` | 0 | -| `go test -short -count=1 ./...` | 0(含 `internal/archtest`) | -| `TEST_CC_DB_DSN=... go test -count=1 ./internal/controlcenter/...` | 0 | -| `go test -count=1 ./internal/archtest/` | 0 | -| `control-center/web` `npm run build` | 0 | -| `web` `npm run build` | 0 | - -## 七、 测试结论与后续优化建议 (Conclusion & Next Steps) - -达到合入本地 `dev` 的标准。`engineering-dashboard` 代码已删除,个人中控台由 Control Center 承接,FEATURE-MATRIX 行状态为 `done`,mutex 为 `controlcenter`。 - -后续不在本次范围:不接入 `env-service.sh` 三环境,不改 CI,不重新设计 `serve-lan`,不 push 远端。 diff --git a/.doc/test-reports/2026-09-18-wave7-roadmap-orchestration-test-report.md b/.doc/test-reports/2026-09-18-wave7-roadmap-orchestration-test-report.md index d22243ec..9e6a47ad 100644 --- a/.doc/test-reports/2026-09-18-wave7-roadmap-orchestration-test-report.md +++ b/.doc/test-reports/2026-09-18-wave7-roadmap-orchestration-test-report.md @@ -62,4 +62,6 @@ ## 七、 测试结论与后续优化建议 (Conclusion & Next Steps) -达到本波「编排确认」合并标准:文档已落盘,能力状态未伪装完成,红线未动。后续触发点仍是用户授权(SWBOOT-10 / FAV live / D7 签字 / Control Center 取舍)或收益认领(T4 U1、T5、T6、T8),不在本波实施。未 push。 +达到本波「编排确认」合并标准:文档已落盘,能力状态未伪装完成,红线未动。后续触发点仍是用户授权(SWBOOT-10 / FAV live / D7 签字)或收益认领(T4 U1、T5、T6、T8),不在本波实施。未 push。 + +2026-09-19 补充:Control Center 取舍已关闭,模块已移出本仓库。本报告 §5.3 只记录当时未合并的事实。 diff --git a/.doc/test-reports/2026-09-19-remove-control-center-test-report.md b/.doc/test-reports/2026-09-19-remove-control-center-test-report.md new file mode 100644 index 00000000..7a643418 --- /dev/null +++ b/.doc/test-reports/2026-09-19-remove-control-center-test-report.md @@ -0,0 +1,56 @@ +# 移除 Control Center 测试报告 + +## 一、 测试背景 (Background) + +项目进度管理已由独立项目承担。本仓库曾合入 Control Center,需要删除其代码、部署、规范和专属文档,并确认产品智能体系统仍能编译、测试和启动。不恢复已下线的 `engineering-dashboard`。 + +## 二、 测试目标 (Test Objectives) + +- 仓库内不再存在 Control Center 源码、compose、脚本和专属文档。 +- `go vet`、`go build`、`go test -short`、`internal/archtest`、产品前端 `npm run build` 全部通过。 +- 本地 `dev` 容器重建后健康检查为 200。 + +## 三、 测试方案与环境 (Test Plan & Environment) + +- OS:Linux。分支 `fix/remove-control-center`,基线为本地 `dev@2641045`。 +- 层级:删除后的编译与短测;模块边界契约测试;产品前端构建。容器冒烟在合入本地 `dev` 之后执行。 +- 证据:`.doc/test-reports/assets/2026-09-19-remove-control-center/verify.txt`。 + +## 四、 预期结果 (Expected Results) + +- `cmd/control-center`、`internal/controlcenter`、`control-center/`、`deploy/control-center` 不存在。 +- 上述命令退出码均为 0。 +- `GET http://127.0.0.1:8080/health` 返回 `env=dev`、`schema_version=52`。 + +## 五、 实际测试结果与判定 (Actual Results & Evaluation) + +### 5.1 源码与运行时 + +- **输入**:删除 Control Center 目录后检查路径与端口。 +- **实际输出**:源码目录已不在索引中。`control-center` 进程已停。`docker rm -f control-center-db` 与 `docker volume rm control-center_cc-db-data` 成功。 +- **判定**:PASS。 +- **耗时**:容器删除 < 1s。 + +### 5.2 编译与测试 + +- **输入**:`go vet ./...`、`go build ./...`、`go test -short -count=1 ./...`、`go test -count=1 ./internal/archtest/`、`cd web && npm run build`。 +- **实际输出**(摘自 `verify.txt`): + +```text +vet=0 +build=0 +short=0 +arch=0 +web=0 +``` + +- **判定**:PASS。 +- **耗时**:整段验证约 33s。 + +## 六、 自动化测试套件汇总 (Test Suite Execution) + +`go test -short -count=1 ./...` 退出码 0,无 FAIL。`internal/archtest` 单独再跑一次,退出码 0。完整输出见 `verify.txt`。 + +## 七、 测试结论与后续优化建议 (Conclusion & Next Steps) + +达到合入本地 `dev` 的标准。本仓库不再包含 Control Center。产品 `dev` 重建与健康检查在合入后补记于同一证据目录。不 push。 diff --git a/.doc/test-reports/assets/2026-09-18-control-center-merge-gate/01-overview.png b/.doc/test-reports/assets/2026-09-18-control-center-merge-gate/01-overview.png deleted file mode 100644 index 6053e7b7..00000000 Binary files a/.doc/test-reports/assets/2026-09-18-control-center-merge-gate/01-overview.png and /dev/null differ diff --git a/.doc/test-reports/assets/2026-09-18-control-center-merge-gate/02-board.png b/.doc/test-reports/assets/2026-09-18-control-center-merge-gate/02-board.png deleted file mode 100644 index 6175dede..00000000 Binary files a/.doc/test-reports/assets/2026-09-18-control-center-merge-gate/02-board.png and /dev/null differ diff --git a/.doc/test-reports/assets/2026-09-18-control-center-merge-gate/02b-board-filter.png b/.doc/test-reports/assets/2026-09-18-control-center-merge-gate/02b-board-filter.png deleted file mode 100644 index ada4f38b..00000000 Binary files a/.doc/test-reports/assets/2026-09-18-control-center-merge-gate/02b-board-filter.png and /dev/null differ diff --git a/.doc/test-reports/assets/2026-09-18-control-center-merge-gate/03-task-detail.png b/.doc/test-reports/assets/2026-09-18-control-center-merge-gate/03-task-detail.png deleted file mode 100644 index f57318d2..00000000 Binary files a/.doc/test-reports/assets/2026-09-18-control-center-merge-gate/03-task-detail.png and /dev/null differ diff --git a/.doc/test-reports/assets/2026-09-18-control-center-merge-gate/04-decisions.png b/.doc/test-reports/assets/2026-09-18-control-center-merge-gate/04-decisions.png deleted file mode 100644 index 1d1330ad..00000000 Binary files a/.doc/test-reports/assets/2026-09-18-control-center-merge-gate/04-decisions.png and /dev/null differ diff --git a/.doc/test-reports/assets/2026-09-18-control-center-merge-gate/05-docs.png b/.doc/test-reports/assets/2026-09-18-control-center-merge-gate/05-docs.png deleted file mode 100644 index 6618c8f0..00000000 Binary files a/.doc/test-reports/assets/2026-09-18-control-center-merge-gate/05-docs.png and /dev/null differ diff --git a/.doc/test-reports/assets/2026-09-18-control-center-merge-gate/05b-docs-search.png b/.doc/test-reports/assets/2026-09-18-control-center-merge-gate/05b-docs-search.png deleted file mode 100644 index f0a90fa2..00000000 Binary files a/.doc/test-reports/assets/2026-09-18-control-center-merge-gate/05b-docs-search.png and /dev/null differ diff --git a/.doc/test-reports/assets/2026-09-18-control-center-merge-gate/06-audit.png b/.doc/test-reports/assets/2026-09-18-control-center-merge-gate/06-audit.png deleted file mode 100644 index cf036643..00000000 Binary files a/.doc/test-reports/assets/2026-09-18-control-center-merge-gate/06-audit.png and /dev/null differ diff --git a/.doc/test-reports/assets/2026-09-18-control-center-merge-gate/07-settings.png b/.doc/test-reports/assets/2026-09-18-control-center-merge-gate/07-settings.png deleted file mode 100644 index 434e5960..00000000 Binary files a/.doc/test-reports/assets/2026-09-18-control-center-merge-gate/07-settings.png and /dev/null differ diff --git a/.doc/test-reports/assets/2026-09-18-control-center-merge-gate/curl-smoke.txt b/.doc/test-reports/assets/2026-09-18-control-center-merge-gate/curl-smoke.txt deleted file mode 100644 index 88bca9c5..00000000 --- a/.doc/test-reports/assets/2026-09-18-control-center-merge-gate/curl-smoke.txt +++ /dev/null @@ -1,52 +0,0 @@ -GET /healthz -> 200 application/json; charset=utf-8 -{"status":"ok"} - - -GET / -> 200 text/html; charset=utf-8 - - - - - - Control Center - - - - -
- - 401 application/json; charset=utf-8 -{"error":"missing bearer token"} - - -POST /api/v1/tasks -> 201 application/json; charset=utf-8 -{"id":"e0e58306-7580-4675-9dfc-55107f91cc38","slug":"merge-gate-1789787895","title":"merge gate smoke","kind":"task","status":"planned","stage":"proposal","stage_state":"idle","testing_required":false,"priority":0,"owner":"","agent_kind":"","mutex":"","branch":"","worktree":"","instruction_md":"","doc_path":"","assigned_agent":"","created_at":"2026-09-19T03:18:15.95345Z","updated_at":"2026-09-19T0… - -PATCH /api/v1/tasks/e0e58306-7580-4675-9dfc-55107f91cc38 -> 200 application/json; charset=utf-8 -{"id":"e0e58306-7580-4675-9dfc-55107f91cc38","slug":"merge-gate-1789787895","title":"merge gate smoke","kind":"task","status":"in_progress","stage":"proposal","stage_state":"assigned","testing_required":false,"priority":0,"owner":"","agent_kind":"","mutex":"","branch":"","worktree":"","instruction_md":"","doc_path":"","assigned_agent":"merge-gate-agent","created_at":"2026-09-19T03:18:15.95345Z","u… - -POST /api/v1/agent/tasks/e0e58306-7580-4675-9dfc-55107f91cc38/claim -> 200 application/json; charset=utf-8 -{"id":"e0e58306-7580-4675-9dfc-55107f91cc38","slug":"merge-gate-1789787895","title":"merge gate smoke","kind":"task","status":"in_progress","stage":"proposal","stage_state":"in_progress","testing_required":false,"priority":0,"owner":"","agent_kind":"","mutex":"","branch":"","worktree":"","instruction_md":"","doc_path":"","assigned_agent":"merge-gate-agent","lease_expires_at":"2026-09-19T05:18:15.9… - -claim stage=proposal state=in_progress lease=2026-09-19T05:18:15.95774Z - -POST /api/v1/agent/tasks/e0e58306-7580-4675-9dfc-55107f91cc38/artifacts -> 201 application/json; charset=utf-8 -{"id":"81272e26-6089-49c9-b8fd-8f43e67c5392"} - - -POST /api/v1/agent/tasks/e0e58306-7580-4675-9dfc-55107f91cc38/advance-request -> 200 application/json; charset=utf-8 -{"id":"e0e58306-7580-4675-9dfc-55107f91cc38","slug":"merge-gate-1789787895","title":"merge gate smoke","kind":"task","status":"in_progress","stage":"proposal","stage_state":"awaiting_review","testing_required":false,"priority":0,"owner":"","agent_kind":"","mutex":"","branch":"","worktree":"","instruction_md":"","doc_path":"","assigned_agent":"merge-gate-agent","heartbeat_at":"2026-09-19T03:18:15.9… - -POST /api/v1/tasks/e0e58306-7580-4675-9dfc-55107f91cc38/stages/advance -> 200 application/json; charset=utf-8 -{"done":false,"task":{"id":"e0e58306-7580-4675-9dfc-55107f91cc38","slug":"merge-gate-1789787895","title":"merge gate smoke","kind":"task","status":"in_progress","stage":"analysis","stage_state":"assigned","testing_required":false,"priority":0,"owner":"","agent_kind":"","mutex":"","branch":"","worktree":"","instruction_md":"","doc_path":"","assigned_agent":"merge-gate-agent","heartbeat_at":"2026-09… - -GET /api/v1/tasks/e0e58306-7580-4675-9dfc-55107f91cc38 -> 200 application/json; charset=utf-8 -{"id":"e0e58306-7580-4675-9dfc-55107f91cc38","slug":"merge-gate-1789787895","title":"merge gate smoke","kind":"task","status":"in_progress","stage":"analysis","stage_state":"assigned","testing_required":false,"priority":0,"owner":"","agent_kind":"","mutex":"","branch":"","worktree":"","instruction_md":"","doc_path":"","assigned_agent":"merge-gate-agent","heartbeat_at":"2026-09-19T03:18:15.95774Z",… - -after advance stage=analysis state=assigned status=in_progress - -POST /api/v1/agent/tasks/e0e58306-7580-4675-9dfc-55107f91cc38/stages/advance -> 405 text/plain; charset=utf-8 -Method Not Allowed - diff --git a/.doc/test-reports/assets/2026-09-18-control-center-merge-gate/llm-audit.json b/.doc/test-reports/assets/2026-09-18-control-center-merge-gate/llm-audit.json deleted file mode 100644 index 8a09bf70..00000000 --- a/.doc/test-reports/assets/2026-09-18-control-center-merge-gate/llm-audit.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "manual_run_194": { - "id": 194, - "kind": "manual", - "status": "completed", - "summary": "确定性 findings: 25;LLM findings: 0", - "error": "", - "started_at": "2026-09-19T03:00:17.249224Z", - "finished_at": "2026-09-19T03:00:38.880135Z", - "findings": 25 - }, - "live_run": { - "id": 195, - "kind": "manual", - "status": "completed", - "summary": "live LLM gate: 1 parsed finding", - "error": "", - "started_at": "2026-09-19T03:03:49.443754Z", - "finished_at": "2026-09-19T03:03:49.447922Z", - "findings": 1 - }, - "live_finding": { - "id": "63634acb-e56a-44f6-8f71-add399f71134", - "run_id": 195, - "severity": "medium", - "kind": "gap", - "title": "CC-GATE-LIVE: Real model response parsed but no downstream action triggered", - "detail_md": "The system receives a real gate‑live model response and successfully parses it, yet no downstream action (e.g., logging, notification, or status update) is initiated, leaving the parsed data unused.", - "refs": [ - "internal/controlcenter/audit" - ], - "status": "open", - "created_at": "2026-09-19T03:03:49.447144Z", - "run_started_at": "2026-09-19T03:03:49.443754Z" - }, - "model": "MiniMax-M2.7-highspeed", - "endpoint": "https://api.minimaxi.com/v1", - "excerpt": "\nThe user says: \"这是一次门禁探测,不是真实缺陷清单。请只输出恰好一条 finding:severity=medium,kind=gap,title 必须以 CC-GATE-LIVE 开头,detail 说明这是真实模型返回且已被解析,refs 包含 internal/controlcenter/audit.\"\n\nWe must output exactly one finding in JSON array with one object: severity \"medium\", kind \"gap\", title starting with \"CC-GATE-LIVE\". The detail should say it's a real model return and has been parsed. The refs should include \"internal/controlcenter/audit\". That is the entire response.\n\nWe must obey the instruction to output only the JSON array, no extra text.\n\nWe need to think about whether the user is requesting us to produce content that is a 'finding' about some bug detection. The user is asking for a finding (a …" -} \ No newline at end of file diff --git a/.doc/test-reports/assets/2026-09-18-control-center-merge-gate/spa-console.txt b/.doc/test-reports/assets/2026-09-18-control-center-merge-gate/spa-console.txt deleted file mode 100644 index 1e6df973..00000000 --- a/.doc/test-reports/assets/2026-09-18-control-center-merge-gate/spa-console.txt +++ /dev/null @@ -1 +0,0 @@ -no pageerror / console.error \ No newline at end of file diff --git a/.doc/test-reports/assets/2026-09-18-control-center-merge-gate/spa-notes.txt b/.doc/test-reports/assets/2026-09-18-control-center-merge-gate/spa-notes.txt deleted file mode 100644 index 34f35387..00000000 --- a/.doc/test-reports/assets/2026-09-18-control-center-merge-gate/spa-notes.txt +++ /dev/null @@ -1,2 +0,0 @@ -board filter contains Smoke: true -console errors: 0 diff --git a/.doc/test-reports/assets/2026-09-18-control-center-merge-gate/verify.txt b/.doc/test-reports/assets/2026-09-19-remove-control-center/verify.txt similarity index 81% rename from .doc/test-reports/assets/2026-09-18-control-center-merge-gate/verify.txt rename to .doc/test-reports/assets/2026-09-19-remove-control-center/verify.txt index 50d78d07..0a439d82 100644 --- a/.doc/test-reports/assets/2026-09-18-control-center-merge-gate/verify.txt +++ b/.doc/test-reports/assets/2026-09-19-remove-control-center/verify.txt @@ -1,118 +1,78 @@ -=== go vet === -vet_exit=0 -=== go build === -build_exit=0 -=== go test -short === +=== vet === +vet=0 +=== build === +build=0 +=== short === ? chat-agent-system/cmd/artifact-vault [no test files] -ok chat-agent-system/cmd/bizdev-cli 2.328s -? chat-agent-system/cmd/control-center [no test files] +ok chat-agent-system/cmd/bizdev-cli 2.260s ? chat-agent-system/cmd/dataset [no test files] -ok chat-agent-system/cmd/document-content-reconcile 0.007s +ok chat-agent-system/cmd/document-content-reconcile 0.003s ? chat-agent-system/cmd/document-revision-backfill [no test files] ? chat-agent-system/cmd/eventmode [no test files] ? chat-agent-system/cmd/events [no test files] ? chat-agent-system/cmd/recategorize [no test files] ? chat-agent-system/cmd/server [no test files] -ok chat-agent-system/internal/agent 5.940s -ok chat-agent-system/internal/app 0.031s +ok chat-agent-system/internal/agent 3.580s +ok chat-agent-system/internal/app 0.032s ok chat-agent-system/internal/app/chatflow 0.005s -ok chat-agent-system/internal/app/docadvance 0.013s -ok chat-agent-system/internal/app/docflow 0.009s -ok chat-agent-system/internal/app/docnotify 0.011s -ok chat-agent-system/internal/app/docproject 0.007s -ok chat-agent-system/internal/app/fav 0.009s -ok chat-agent-system/internal/app/intentflow 0.028s -ok chat-agent-system/internal/app/stageflow 0.012s +ok chat-agent-system/internal/app/docadvance 0.006s +ok chat-agent-system/internal/app/docflow 0.006s +ok chat-agent-system/internal/app/docnotify 0.003s +ok chat-agent-system/internal/app/docproject 0.003s +ok chat-agent-system/internal/app/fav 0.003s +ok chat-agent-system/internal/app/intentflow 0.020s +ok chat-agent-system/internal/app/stageflow 0.007s ok chat-agent-system/internal/app/uow 0.003s -ok chat-agent-system/internal/archtest 0.290s -ok chat-agent-system/internal/artifactvault 0.018s -ok chat-agent-system/internal/auth 0.998s -ok chat-agent-system/internal/authz 0.017s -ok chat-agent-system/internal/bizdev 0.226s -ok chat-agent-system/internal/chat 0.038s -ok chat-agent-system/internal/config 0.014s -ok chat-agent-system/internal/controlcenter/audit 0.005s -ok chat-agent-system/internal/controlcenter/config 0.003s -ok chat-agent-system/internal/controlcenter/docs 0.282s -ok chat-agent-system/internal/controlcenter/embed 0.008s -ok chat-agent-system/internal/controlcenter/importer 0.008s -ok chat-agent-system/internal/controlcenter/llm 0.013s -ok chat-agent-system/internal/controlcenter/pipeline 0.002s -ok chat-agent-system/internal/controlcenter/server 0.005s -ok chat-agent-system/internal/controlcenter/store 0.004s -ok chat-agent-system/internal/controlcenter/tasks 0.003s -? chat-agent-system/internal/controlcenter/webui [no test files] +ok chat-agent-system/internal/archtest 0.194s +ok chat-agent-system/internal/artifactvault 0.005s +ok chat-agent-system/internal/auth 0.989s +ok chat-agent-system/internal/authz 0.003s +ok chat-agent-system/internal/bizdev 0.166s +ok chat-agent-system/internal/chat 0.014s +ok chat-agent-system/internal/config 0.004s ok chat-agent-system/internal/coordinator 0.011s -ok chat-agent-system/internal/ctxmgr 0.083s -ok chat-agent-system/internal/dataset 0.005s -ok chat-agent-system/internal/db 0.005s +ok chat-agent-system/internal/ctxmgr 0.088s +ok chat-agent-system/internal/dataset 0.004s +ok chat-agent-system/internal/db 0.004s ok chat-agent-system/internal/doccontent 0.002s ok chat-agent-system/internal/docduty 0.004s -ok chat-agent-system/internal/docgov 0.023s +ok chat-agent-system/internal/docgov 0.025s ok chat-agent-system/internal/docquery 0.004s ok chat-agent-system/internal/docrevision 0.008s -ok chat-agent-system/internal/events 0.006s -ok chat-agent-system/internal/freedoc 0.024s +ok chat-agent-system/internal/events 0.005s +ok chat-agent-system/internal/freedoc 0.025s ok chat-agent-system/internal/fsutil 0.002s -ok chat-agent-system/internal/httpx 0.003s -ok chat-agent-system/internal/intent 0.023s +ok chat-agent-system/internal/httpx 0.004s +ok chat-agent-system/internal/intent 0.022s ok chat-agent-system/internal/kb 0.009s ok chat-agent-system/internal/maturity 0.002s -ok chat-agent-system/internal/notification 0.073s +ok chat-agent-system/internal/notification 0.085s ? chat-agent-system/internal/observability [no test files] ok chat-agent-system/internal/projectcontext 0.004s -ok chat-agent-system/internal/projectinstance 0.005s -ok chat-agent-system/internal/projectworkflow 0.002s -ok chat-agent-system/internal/quote 0.007s -ok chat-agent-system/internal/replyexport 0.002s -ok chat-agent-system/internal/server 14.119s -ok chat-agent-system/internal/squad 0.029s -ok chat-agent-system/internal/storage 0.012s -ok chat-agent-system/internal/stream 0.002s +ok chat-agent-system/internal/projectinstance 0.004s +ok chat-agent-system/internal/projectworkflow 0.003s +ok chat-agent-system/internal/quote 0.008s +ok chat-agent-system/internal/replyexport 0.003s +ok chat-agent-system/internal/server 14.027s +ok chat-agent-system/internal/squad 0.033s +ok chat-agent-system/internal/storage 0.015s +ok chat-agent-system/internal/stream 0.003s ok chat-agent-system/internal/stream/transport 0.002s -ok chat-agent-system/internal/swboot 0.001s +ok chat-agent-system/internal/swboot 0.002s ok chat-agent-system/internal/sysadmin 0.003s ok chat-agent-system/internal/taskrun 0.192s -ok chat-agent-system/internal/template 2.799s +ok chat-agent-system/internal/template 0.817s ok chat-agent-system/internal/upload 0.002s ? chat-agent-system/internal/workflow [no test files] -ok chat-agent-system/internal/workflow/runtime 0.002s +ok chat-agent-system/internal/workflow/runtime 0.003s ok chat-agent-system/internal/workflowdef 0.004s -ok chat-agent-system/internal/workspace 0.002s +ok chat-agent-system/internal/workspace 0.003s ? chat-agent-system/templates [no test files] -short_exit=0 -=== controlcenter integration === -ok chat-agent-system/internal/controlcenter/audit 0.249s -ok chat-agent-system/internal/controlcenter/config 0.002s -ok chat-agent-system/internal/controlcenter/docs 0.203s -ok chat-agent-system/internal/controlcenter/embed 0.004s -ok chat-agent-system/internal/controlcenter/importer 0.015s -ok chat-agent-system/internal/controlcenter/llm 0.004s -ok chat-agent-system/internal/controlcenter/pipeline 0.002s -ok chat-agent-system/internal/controlcenter/server 1.107s -ok chat-agent-system/internal/controlcenter/store 0.010s -ok chat-agent-system/internal/controlcenter/tasks 0.016s -? chat-agent-system/internal/controlcenter/webui [no test files] -cc_exit=0 +short=0 === archtest === -ok chat-agent-system/internal/archtest 0.161s -arch_exit=0 -=== control-center web build === - -> control-center-web@0.3.0 build -> tsc && vite build - -vite v7.3.6 building client environment for production... -transforming... -✓ 42 modules transformed. -rendering chunks... -computing gzip size... -../../internal/controlcenter/webui/dist/index.html 0.40 kB │ gzip: 0.27 kB -../../internal/controlcenter/webui/dist/assets/index-DyNgdwvP.css 9.33 kB │ gzip: 2.30 kB -../../internal/controlcenter/webui/dist/assets/index-Dj6oWpHL.js 303.55 kB │ gzip: 93.80 kB -✓ built in 557ms -ccweb_exit=0 -=== product web build === +ok chat-agent-system/internal/archtest 0.154s +arch=0 +=== web === > shuimu-workbench@1.0.0 build > tsc && vite build && node scripts/precompress-dist.mjs @@ -248,7 +208,7 @@ dist/assets/index-DhyNmxJE.js 2,829.14 kB │ gzip: 805. - Using dynamic import() to code-split the application - Use build.rollupOptions.output.manualChunks to improve chunking: https://rollupjs.org/configuration-options/#output-manualchunks - Adjust chunk size limit for this warning via build.chunkSizeWarningLimit. -✓ built in 7.59s +✓ built in 7.55s precompress: 49 candidate(s) under /home/weichen/projects/Agent-System/web/dist br assets/abnfDiagram-VCTEODGH-DMcEZewO.js 1774 → 798 (−55.0%) gzip assets/abnfDiagram-VCTEODGH-DMcEZewO.js 1774 → 872 (−50.8%) @@ -349,5 +309,4 @@ precompress: 49 candidate(s) under /home/weichen/projects/Agent-System/web/dist br fonts/OFL.txt 4474 → 1437 (−67.9%) gzip fonts/OFL.txt 4474 → 1980 (−55.7%) precompress: wrote 49 .br and 49 .gz -web_exit=0 -DONE +web=0 diff --git a/.gitignore b/.gitignore index c9eccef9..18e7ab92 100644 --- a/.gitignore +++ b/.gitignore @@ -55,8 +55,6 @@ node_modules/ *.o *.a /server -# Control Center SPA embed output is committed (go build works without node) -!internal/controlcenter/webui/dist/ # Regulations Knowledge Base Data .regulations/ diff --git a/AGENTS.md b/AGENTS.md index 76e85b1d..0fdaafa6 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -254,10 +254,6 @@ audit(问题)→ remediation(方案)→ ops/short-term(任务) 均转发至本文件启动链。 -### 13.1 个人中控台(Control Center) - -本仓库有独立运行的个人项目中控台(loopback `127.0.0.1:8790`,独立于产品系统):agent 在本仓库工作可通过它认领任务、上报进度/产物/决策。**铁律:agent 禁止自行推进阶段,只能 advance-request 交回人工。**接入步骤(注册 token、工作循环、CLI 速查)见 [agent-integration.md](./.doc/system-design/project-control-center/agent-integration.md)。 - --- ## 14. 模板索引 diff --git a/cmd/control-center/agentcli.go b/cmd/control-center/agentcli.go deleted file mode 100644 index 4551e12e..00000000 --- a/cmd/control-center/agentcli.go +++ /dev/null @@ -1,401 +0,0 @@ -package main - -import ( - "bytes" - "encoding/json" - "flag" - "fmt" - "io" - "net/http" - "os" - "strings" - "time" -) - -// Thin agent-side HTTP client over the Control Center agent API. Output is -// compact JSON on stdout; failures print the server's error field to stderr -// and exit non-zero. - -type agentClient struct { - base string - token string - hc *http.Client -} - -func newAgentClient(server, token string) (*agentClient, error) { - if token == "" { - return nil, fmt.Errorf("agent token required: pass -token or set CC_AGENT_TOKEN") - } - return &agentClient{ - base: strings.TrimRight(server, "/"), - token: token, - hc: &http.Client{Timeout: 30 * time.Second}, - }, nil -} - -// do performs one request and returns the decoded body. Server error -// responses are unwrapped into an error carrying the server's error field. -func (c *agentClient) do(method, path string, body any) (json.RawMessage, error) { - var rdr io.Reader - if body != nil { - raw, err := json.Marshal(body) - if err != nil { - return nil, err - } - rdr = bytes.NewReader(raw) - } - req, err := http.NewRequest(method, c.base+path, rdr) - if err != nil { - return nil, err - } - req.Header.Set("Authorization", "Bearer "+c.token) - if body != nil { - req.Header.Set("Content-Type", "application/json") - } - resp, err := c.hc.Do(req) - if err != nil { - return nil, err - } - defer resp.Body.Close() - raw, err := io.ReadAll(resp.Body) - if err != nil { - return nil, err - } - if resp.StatusCode == http.StatusNoContent { - return nil, nil - } - if resp.StatusCode >= 400 { - var e struct { - Error string `json:"error"` - } - if json.Unmarshal(raw, &e) == nil && e.Error != "" { - return nil, fmt.Errorf("%s %s: %s (%d)", method, path, e.Error, resp.StatusCode) - } - return nil, fmt.Errorf("%s %s: HTTP %d: %s", method, path, resp.StatusCode, strings.TrimSpace(string(raw))) - } - return json.RawMessage(raw), nil -} - -func printJSON(raw json.RawMessage) { - if len(raw) == 0 { - fmt.Println("null") - return - } - // Re-encode compactly so output is stable regardless of server framing. - var v any - if err := json.Unmarshal(raw, &v); err != nil { - fmt.Println(string(raw)) - return - } - out, _ := json.Marshal(v) - fmt.Println(string(out)) -} - -func envDefault(key, def string) string { - if v := os.Getenv(key); v != "" { - return v - } - return def -} - -// commonFlags registers -server and -token on fs and returns a client -// factory. -func commonFlags(fs *flag.FlagSet) func() (*agentClient, error) { - server := fs.String("server", envDefault("CC_SERVER", "http://127.0.0.1:8790"), "control-center base URL") - token := fs.String("token", envDefault("CC_AGENT_TOKEN", ""), "agent token (or CC_AGENT_TOKEN)") - return func() (*agentClient, error) { return newAgentClient(*server, *token) } -} - -func readContent(file, content string) (string, error) { - if file != "" { - raw, err := os.ReadFile(file) - if err != nil { - return "", err - } - return string(raw), nil - } - if content == "" { - return "", fmt.Errorf("one of -file or -content is required") - } - return content, nil -} - -func cmdAgentNext(args []string) error { - fs := flag.NewFlagSet("next", flag.ContinueOnError) - mk := commonFlags(fs) - if err := fs.Parse(args); err != nil { - return err - } - c, err := mk() - if err != nil { - return err - } - raw, err := c.do("GET", "/api/v1/agent/tasks/next", nil) - if err != nil { - return err - } - if len(raw) == 0 { - fmt.Println(`{"task":null}`) - return nil - } - var t struct { - ID string `json:"id"` - Slug string `json:"slug"` - Title string `json:"title"` - Stage string `json:"stage"` - } - if err := json.Unmarshal(raw, &t); err != nil { - return err - } - out, _ := json.Marshal(map[string]string{ - "id": t.ID, "slug": t.Slug, "title": t.Title, "stage": t.Stage, - }) - fmt.Println(string(out)) - return nil -} - -func cmdAgentInstruction(args []string) error { - fs := flag.NewFlagSet("instruction", flag.ContinueOnError) - mk := commonFlags(fs) - task := fs.String("task", "", "task id (uuid; use `next` to discover)") - asMD := fs.Bool("md", false, "render a readable summary instead of raw JSON") - if err := fs.Parse(args); err != nil { - return err - } - if *task == "" { - return fmt.Errorf("-task is required") - } - c, err := mk() - if err != nil { - return err - } - raw, err := c.do("GET", "/api/v1/agent/tasks/"+*task+"/instruction", nil) - if err != nil { - return err - } - if !*asMD { - printJSON(raw) - return nil - } - var pkg struct { - Task struct { - Slug string `json:"slug"` - Title string `json:"title"` - InstructionMD string `json:"instruction_md"` - } `json:"task"` - Stage string `json:"stage"` - StageState string `json:"stage_state"` - Deliverable string `json:"deliverable"` - DecisionRules string `json:"decision_rules"` - } - if err := json.Unmarshal(raw, &pkg); err != nil { - return err - } - fmt.Printf("# %s (%s)\nstage: %s [%s]\n\n## 交付要求\n%s\n\n## 决策规则\n%s\n\n## 任务说明\n%s\n", - pkg.Task.Title, pkg.Task.Slug, pkg.Stage, pkg.StageState, - pkg.Deliverable, pkg.DecisionRules, pkg.Task.InstructionMD) - return nil -} - -// simpleTaskAction covers claim / heartbeat / release: POST with no body. -func simpleTaskAction(name string) func([]string) error { - return func(args []string) error { - fs := flag.NewFlagSet(name, flag.ContinueOnError) - mk := commonFlags(fs) - task := fs.String("task", "", "task id (uuid)") - if err := fs.Parse(args); err != nil { - return err - } - if *task == "" { - return fmt.Errorf("-task is required") - } - c, err := mk() - if err != nil { - return err - } - raw, err := c.do("POST", "/api/v1/agent/tasks/"+*task+"/"+name, nil) - if err != nil { - return err - } - printJSON(raw) - return nil - } -} - -func cmdAgentEvent(args []string) error { - fs := flag.NewFlagSet("event", flag.ContinueOnError) - mk := commonFlags(fs) - task := fs.String("task", "", "task id (uuid)") - phase := fs.String("phase", "", "event phase: start|plan|progress|blocked|report|done|advance-request|decision|system") - message := fs.String("message", "", "event message") - payload := fs.String("payload", "", "optional JSON payload") - if err := fs.Parse(args); err != nil { - return err - } - if *task == "" || *phase == "" { - return fmt.Errorf("-task and -phase are required") - } - body := map[string]any{"phase": *phase, "message": *message} - if *payload != "" { - var p any - if err := json.Unmarshal([]byte(*payload), &p); err != nil { - return fmt.Errorf("-payload must be valid JSON: %w", err) - } - body["payload"] = json.RawMessage(*payload) - } - c, err := mk() - if err != nil { - return err - } - raw, err := c.do("POST", "/api/v1/agent/tasks/"+*task+"/events", body) - if err != nil { - return err - } - printJSON(raw) - return nil -} - -func cmdAgentArtifact(args []string) error { - fs := flag.NewFlagSet("artifact", flag.ContinueOnError) - mk := commonFlags(fs) - task := fs.String("task", "", "task id (uuid)") - kind := fs.String("kind", "note", "artifact kind, e.g. proposal|analysis|design|implementation|test-report|evaluation") - title := fs.String("title", "", "artifact title") - file := fs.String("file", "", "read content_md from file") - content := fs.String("content", "", "inline content_md") - if err := fs.Parse(args); err != nil { - return err - } - if *task == "" { - return fmt.Errorf("-task is required") - } - md, err := readContent(*file, *content) - if err != nil { - return err - } - c, err := mk() - if err != nil { - return err - } - raw, err := c.do("POST", "/api/v1/agent/tasks/"+*task+"/artifacts", map[string]string{ - "kind": *kind, "title": *title, "content_md": md, - }) - if err != nil { - return err - } - printJSON(raw) - return nil -} - -func cmdAgentAdvanceRequest(args []string) error { - fs := flag.NewFlagSet("advance-request", flag.ContinueOnError) - mk := commonFlags(fs) - task := fs.String("task", "", "task id (uuid)") - summary := fs.String("summary", "", "handoff summary for human review") - artifacts := fs.String("artifacts", "", "comma-separated artifact ids") - if err := fs.Parse(args); err != nil { - return err - } - if *task == "" { - return fmt.Errorf("-task is required") - } - ids := []string{} - if *artifacts != "" { - for _, id := range strings.Split(*artifacts, ",") { - if id = strings.TrimSpace(id); id != "" { - ids = append(ids, id) - } - } - } - c, err := mk() - if err != nil { - return err - } - raw, err := c.do("POST", "/api/v1/agent/tasks/"+*task+"/advance-request", map[string]any{ - "summary": *summary, "artifact_ids": ids, - }) - if err != nil { - return err - } - printJSON(raw) - return nil -} - -// suggestions collects repeated -suggestion "选项||理由||影响" flags. -type suggestions []map[string]string - -func (s *suggestions) String() string { return fmt.Sprint([]map[string]string(*s)) } -func (s *suggestions) Set(v string) error { - parts := strings.SplitN(v, "||", 3) - if len(parts) != 3 { - return fmt.Errorf("suggestion must be \"选项||理由||影响\"") - } - *s = append(*s, map[string]string{ - "option": strings.TrimSpace(parts[0]), - "rationale": strings.TrimSpace(parts[1]), - "impact": strings.TrimSpace(parts[2]), - }) - return nil -} - -func cmdAgentDecision(args []string) error { - fs := flag.NewFlagSet("decision", flag.ContinueOnError) - mk := commonFlags(fs) - task := fs.String("task", "", "task id (uuid)") - question := fs.String("question", "", "question for the human (required)") - contextMD := fs.String("context", "", "markdown context") - var suggs suggestions - fs.Var(&suggs, "suggestion", `"选项||理由||影响"; repeatable, at least one required`) - if err := fs.Parse(args); err != nil { - return err - } - if *task == "" || *question == "" { - return fmt.Errorf("-task and -question are required") - } - if len(suggs) == 0 { - return fmt.Errorf("at least one -suggestion is required") - } - c, err := mk() - if err != nil { - return err - } - raw, err := c.do("POST", "/api/v1/agent/tasks/"+*task+"/decisions", map[string]any{ - "question": *question, "context_md": *contextMD, "suggestions": suggs, - }) - if err != nil { - return err - } - printJSON(raw) - return nil -} - -func cmdAgentReport(args []string) error { - fs := flag.NewFlagSet("report", flag.ContinueOnError) - mk := commonFlags(fs) - task := fs.String("task", "", "task id (uuid)") - kind := fs.String("kind", "", "result|test-report|handoff") - file := fs.String("file", "", "read content_md from file") - content := fs.String("content", "", "inline content_md") - if err := fs.Parse(args); err != nil { - return err - } - if *task == "" || *kind == "" { - return fmt.Errorf("-task and -kind are required") - } - md, err := readContent(*file, *content) - if err != nil { - return err - } - c, err := mk() - if err != nil { - return err - } - raw, err := c.do("POST", "/api/v1/agent/reports", map[string]string{ - "task_id": *task, "kind": *kind, "content_md": md, - }) - if err != nil { - return err - } - printJSON(raw) - return nil -} diff --git a/cmd/control-center/main.go b/cmd/control-center/main.go deleted file mode 100644 index e6934cac..00000000 --- a/cmd/control-center/main.go +++ /dev/null @@ -1,270 +0,0 @@ -// Control Center: personal project control plane, independent of the product -// system. Subcommands: serve, migrate, register-agent, import-docs, plus the -// agent CLI (next / instruction / claim / heartbeat / release / event / -// artifact / advance-request / decision / report). -package main - -import ( - "context" - "crypto/rand" - "encoding/hex" - "flag" - "fmt" - "log/slog" - "net" - "net/http" - "os" - "os/signal" - "syscall" - "time" - - "chat-agent-system/internal/controlcenter/audit" - "chat-agent-system/internal/controlcenter/config" - "chat-agent-system/internal/controlcenter/docs" - "chat-agent-system/internal/controlcenter/embed" - "chat-agent-system/internal/controlcenter/importer" - "chat-agent-system/internal/controlcenter/llm" - "chat-agent-system/internal/controlcenter/server" - "chat-agent-system/internal/controlcenter/store" -) - -func main() { - if len(os.Args) < 2 { - usage() - os.Exit(2) - } - var err error - switch os.Args[1] { - case "serve": - err = cmdServe(os.Args[2:]) - case "migrate": - err = cmdMigrate(os.Args[2:]) - case "register-agent": - err = cmdRegisterAgent(os.Args[2:]) - case "import-docs": - err = cmdImportDocs(os.Args[2:]) - case "next": - err = cmdAgentNext(os.Args[2:]) - case "instruction": - err = cmdAgentInstruction(os.Args[2:]) - case "claim": - err = simpleTaskAction("claim")(os.Args[2:]) - case "heartbeat": - err = simpleTaskAction("heartbeat")(os.Args[2:]) - case "release": - err = simpleTaskAction("release")(os.Args[2:]) - case "event": - err = cmdAgentEvent(os.Args[2:]) - case "artifact": - err = cmdAgentArtifact(os.Args[2:]) - case "advance-request": - err = cmdAgentAdvanceRequest(os.Args[2:]) - case "decision": - err = cmdAgentDecision(os.Args[2:]) - case "report": - err = cmdAgentReport(os.Args[2:]) - default: - usage() - os.Exit(2) - } - if err != nil { - slog.Error("control-center", "err", err) - os.Exit(1) - } -} - -func usage() { - fmt.Fprintln(os.Stderr, `control-center — personal project control plane - -usage: - control-center serve run HTTP server (CC_HTTP_ADDR, default 127.0.0.1:8790) - control-center migrate apply goose migrations only - control-center register-agent -name X [-kind K] [-token T] create/rotate an agent token - control-center import-docs import .doc task docs into cc_tasks (also runs periodically in serve) - -agent CLI (common flags: -server, default $CC_SERVER or http://127.0.0.1:8790; - -token, default $CC_AGENT_TOKEN; -task takes a task uuid — use 'next' to discover ids): - next print next claimable task (slug/title/id/stage) - instruction -task ID [-md] print the instruction package (JSON, or readable with -md) - claim|heartbeat|release -task ID lease lifecycle - event -task ID -phase P -message M [-payload ''] - artifact -task ID -kind K -title T [-file F | -content MD] - advance-request -task ID -summary S [-artifacts id1,id2] - decision -task ID -question Q -context MD -suggestion "选项||理由||影响" [-suggestion ...] - report -task ID -kind result|test-report|handoff [-file F | -content MD]`) -} - -func cmdServe(args []string) error { - fs := flag.NewFlagSet("serve", flag.ContinueOnError) - if err := fs.Parse(args); err != nil { - return err - } - cfg, err := config.Load() - if err != nil { - return err - } - ctx := context.Background() - if err := store.Migrate(ctx, cfg.DBDSN, cfg.MigrationsDir); err != nil { - return err - } - st, err := store.Connect(ctx, cfg.DBDSN) - if err != nil { - return err - } - defer st.Close() - - loopback := isLoopbackAddr(cfg.HTTPAddr) - if cfg.ConsoleToken == "" { - if !loopback { - return fmt.Errorf("refusing to bind %s without a fixed console token: set CC_CONSOLE_TOKEN", cfg.HTTPAddr) - } - cfg.ConsoleToken = randomToken() - slog.Warn("CC_CONSOLE_TOKEN not set; generated ephemeral console token") - } - if !loopback { - slog.Warn("control-center is reachable from the network; all /api routes require tokens") - } - srv := server.New(cfg, st) - - var emb *embed.HTTPClient - if cfg.EmbeddingEndpoint != "" { - emb = embed.NewHTTP(cfg.EmbeddingEndpoint, cfg.EmbeddingAPIKey, cfg.EmbeddingModel, embed.Dim) - } else { - slog.Warn("embedding endpoint not configured; search runs keyword-only") - } - syncer := docs.New(st, cfg.RepoRoot, emb) - var embIface embed.Embedder - if emb != nil { - embIface = emb - } - srv.SetDocs(embIface, syncer) - - chat := llm.NewHTTP(cfg.LLMEndpoint, cfg.LLMAPIKey, cfg.LLMModel) - if !chat.Configured() { - slog.Warn("LLM endpoint/model not configured; audits run deterministic checks only") - } - auditRunner := audit.New(st, cfg.RepoRoot, chat) - srv.SetAudit(auditRunner) - - syncCtx, stopSync := context.WithCancel(ctx) - defer stopSync() - syncer.StartPeriodic(syncCtx, time.Duration(cfg.DocSyncSecond)*time.Second) - importer.New(st, cfg.RepoRoot).StartPeriodic(syncCtx, time.Duration(cfg.DocSyncSecond)*time.Second) - if cfg.AuditEnabled { - auditRunner.StartScheduler(syncCtx, cfg.AuditDailyAt) - } - - reapCtx, stopReaper := context.WithCancel(ctx) - defer stopReaper() - srv.StartLeaseReaper(reapCtx) - - httpSrv := &http.Server{Addr: cfg.HTTPAddr, Handler: srv.Handler(), ReadHeaderTimeout: 10 * time.Second} - go func() { - sig := make(chan os.Signal, 1) - signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM) - <-sig - stopSync() - stopReaper() - shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second) - defer cancel() - _ = httpSrv.Shutdown(shutdownCtx) - }() - slog.Info("control-center listening", "addr", cfg.HTTPAddr) - fmt.Printf("console token: %s\n", cfg.ConsoleToken) - if err := httpSrv.ListenAndServe(); err != nil && err != http.ErrServerClosed { - return err - } - return nil -} - -func cmdMigrate(args []string) error { - fs := flag.NewFlagSet("migrate", flag.ContinueOnError) - if err := fs.Parse(args); err != nil { - return err - } - cfg, err := config.Load() - if err != nil { - return err - } - return store.Migrate(context.Background(), cfg.DBDSN, cfg.MigrationsDir) -} - -func cmdImportDocs(args []string) error { - fs := flag.NewFlagSet("import-docs", flag.ContinueOnError) - if err := fs.Parse(args); err != nil { - return err - } - cfg, err := config.Load() - if err != nil { - return err - } - ctx := context.Background() - st, err := store.Connect(ctx, cfg.DBDSN) - if err != nil { - return err - } - defer st.Close() - stats, err := importer.New(st, cfg.RepoRoot).Import(ctx) - if err != nil { - return err - } - fmt.Printf("import-docs: added=%d updated=%d skipped=%d\n", stats.Added, stats.Updated, stats.Skipped) - return nil -} - -func cmdRegisterAgent(args []string) error { - fs := flag.NewFlagSet("register-agent", flag.ContinueOnError) - name := fs.String("name", "", "agent name (required)") - kind := fs.String("kind", "generic", "agent kind, e.g. cursor / kimi / pi") - token := fs.String("token", "", "plaintext token; generated when empty") - if err := fs.Parse(args); err != nil { - return err - } - if *name == "" { - return fmt.Errorf("-name is required") - } - cfg, err := config.Load() - if err != nil { - return err - } - ctx := context.Background() - if err := store.Migrate(ctx, cfg.DBDSN, cfg.MigrationsDir); err != nil { - return err - } - st, err := store.Connect(ctx, cfg.DBDSN) - if err != nil { - return err - } - defer st.Close() - if *token == "" { - *token = randomToken() - } - if err := server.RegisterAgent(ctx, st, *name, *kind, *token); err != nil { - return err - } - fmt.Printf("agent %q registered. token (shown once): %s\n", *name, *token) - return nil -} - -func randomToken() string { - b := make([]byte, 24) - if _, err := rand.Read(b); err != nil { - panic(err) - } - return hex.EncodeToString(b) -} - -// isLoopbackAddr reports whether addr ("host:port") binds only to loopback. -func isLoopbackAddr(addr string) bool { - host, _, err := net.SplitHostPort(addr) - if err != nil { - host = addr - } - if host == "" { - return false // ":8790" listens on all interfaces - } - if ip := net.ParseIP(host); ip != nil { - return ip.IsLoopback() - } - return host == "localhost" -} diff --git a/control-center/web/index.html b/control-center/web/index.html deleted file mode 100644 index 10011c1a..00000000 --- a/control-center/web/index.html +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - Control Center - - -
- - - diff --git a/control-center/web/package-lock.json b/control-center/web/package-lock.json deleted file mode 100644 index 7b480141..00000000 --- a/control-center/web/package-lock.json +++ /dev/null @@ -1,1896 +0,0 @@ -{ - "name": "control-center-web", - "version": "0.3.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "control-center-web", - "version": "0.3.0", - "dependencies": { - "marked": "^18.0.13", - "react": "^19.2.0", - "react-dom": "^19.2.0" - }, - "devDependencies": { - "@types/react": "^19.2.7", - "@types/react-dom": "^19.2.3", - "@vitejs/plugin-react": "^5.1.0", - "typescript": "~5.9.3", - "vite": "^7.1.14" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", - "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.29.7", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", - "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", - "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.29.7", - "@babel/generator": "^7.29.7", - "@babel/helper-compilation-targets": "^7.29.7", - "@babel/helper-module-transforms": "^7.29.7", - "@babel/helpers": "^7.29.7", - "@babel/parser": "^7.29.7", - "@babel/template": "^7.29.7", - "@babel/traverse": "^7.29.7", - "@babel/types": "^7.29.7", - "@jridgewell/remapping": "^2.3.5", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/generator": { - "version": "7.29.8", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.8.tgz", - "integrity": "sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.29.8", - "@babel/types": "^7.29.8", - "@jridgewell/gen-mapping": "^0.3.12", - "@jridgewell/trace-mapping": "^0.3.28", - "jsesc": "^3.0.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", - "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.29.7", - "@babel/helper-validator-option": "^7.29.7", - "browserslist": "^4.24.0", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-globals": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", - "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", - "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.29.7", - "@babel/types": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", - "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.29.7", - "@babel/helper-validator-identifier": "^7.29.7", - "@babel/traverse": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz", - "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", - "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", - "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", - "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz", - "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.29.7", - "@babel/types": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.29.8", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.8.tgz", - "integrity": "sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.29.8" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx-self": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.29.7.tgz", - "integrity": "sha512-TL0hMc9xzy86VD31nUiwzd5otRAcyEPcsegCxolO0PvcXuH1v0kECe/UIznYFihpkvU5wg/jk4v0TTEFfm53fw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx-source": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.29.7.tgz", - "integrity": "sha512-06IyK09H3wi4cGbhDBwp5gUGo0IKtnYa8tyTiephirPCK6fbobVGiXMMI5zLQ4aKEYP3wZ3ArU44o+8KMrSG/Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/template": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", - "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.29.7", - "@babel/parser": "^7.29.7", - "@babel/types": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.29.8", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.8.tgz", - "integrity": "sha512-I5z7H3bf/41ktsNVLtpN0wAa336HkqIHQ5BuPLEhTkt1jVSyZpeNKIzTgEWmlxjdg81R0IgUCcaE+Ok3NvrfZg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.29.7", - "@babel/generator": "^7.29.8", - "@babel/helper-globals": "^7.29.7", - "@babel/parser": "^7.29.8", - "@babel/template": "^7.29.7", - "@babel/types": "^7.29.8", - "debug": "^4.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/types": { - "version": "7.29.8", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz", - "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.29.7", - "@babel/helper-validator-identifier": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz", - "integrity": "sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.2.tgz", - "integrity": "sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.2.tgz", - "integrity": "sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.2.tgz", - "integrity": "sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.2.tgz", - "integrity": "sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.2.tgz", - "integrity": "sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.2.tgz", - "integrity": "sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.2.tgz", - "integrity": "sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.2.tgz", - "integrity": "sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.2.tgz", - "integrity": "sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.2.tgz", - "integrity": "sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.2.tgz", - "integrity": "sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.2.tgz", - "integrity": "sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.2.tgz", - "integrity": "sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.2.tgz", - "integrity": "sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.2.tgz", - "integrity": "sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.2.tgz", - "integrity": "sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.2.tgz", - "integrity": "sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.2.tgz", - "integrity": "sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.2.tgz", - "integrity": "sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.2.tgz", - "integrity": "sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openharmony-arm64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.2.tgz", - "integrity": "sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.2.tgz", - "integrity": "sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.2.tgz", - "integrity": "sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.2.tgz", - "integrity": "sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.2.tgz", - "integrity": "sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.13", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", - "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/remapping": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", - "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz", - "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.31", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", - "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@napi-rs/lzma-linux-x64-gnu": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-x64-gnu/-/lzma-linux-x64-gnu-1.5.1.tgz", - "integrity": "sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ==", - "cpu": [ - "x64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^22.20 || ^24.12 || >=25" - } - }, - "node_modules/@rolldown/pluginutils": { - "version": "1.0.0-rc.3", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.3.tgz", - "integrity": "sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.63.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.63.3.tgz", - "integrity": "sha512-w3Jnvi1ocaVm/c7yVPpfB98XeSRBMyzp6njL5MVVbGyXjpmUkN+s6Hp4t0PqhGCCaI1ZHMKXt/w0lA1RCaLVcw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.63.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.63.3.tgz", - "integrity": "sha512-uI/ESiaIbbRYAEhzy8PCUWDp1hB0bjAqM06mW9flOoNO4Q8DQpeoREhBR5Hegfl+wpXiguyJv6XSPzEN7OxyHQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.63.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.63.3.tgz", - "integrity": "sha512-oxhrd1jmXLwWZ83eQYDXxuqRdkqkzrjR3JobKeuUyfdNZo11FuQIvqEOZhyIT7OBHxXoGslDDjN0cQcM6T0TqQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.63.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.63.3.tgz", - "integrity": "sha512-7/YiIMghVE8DrxKvNdorAaJVdriOFgOIpdStnPx8ppx5zfTwC3jBCSEAIzB7JD5404m65THl6H93UTTVUvypmg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.63.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.63.3.tgz", - "integrity": "sha512-GXFZRRoMAytaI5z6N3Zhfw0WL18Q0M8r95D5hlC4GqE/lGk8pbSJNUBoOWDfbm6dTciqHj2nU87tI5f6XhQiOg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.63.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.63.3.tgz", - "integrity": "sha512-77W+8X3ddYgPxUpB8nZFQs2Mq+wc4HVlcSRtApXLjYBcnPMkttrSnU8VwKQjeWYhMsITHFs5cWBQ8vz1Q+5RHQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.63.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.63.3.tgz", - "integrity": "sha512-FVkwK+iUC+mq+GipVK46rRVticfAPtvPUNlqlGXUDxdVk/UGjQiiiUVPUrEXdSpU2ufU0XxLGyTqDtBidDOVmg==", - "cpu": [ - "arm" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.63.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.63.3.tgz", - "integrity": "sha512-+aGU1t3398yQOVj1Bz8o3e+KtswxAPvO+mtxtNdfXYMkXIHu7XhhkCD7/DEH9q8tF8uhDnMWvfpUKI8y1sZJsg==", - "cpu": [ - "arm" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.63.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.63.3.tgz", - "integrity": "sha512-cR0kjpRXR2KJ2oQK8E2KTPtphs+b9hZ8IhTZubNryt/RsqgdOZBQ2Zq0q5UedtiIi0rs3jVhJh55RE1ZHUVGUA==", - "cpu": [ - "arm64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.63.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.63.3.tgz", - "integrity": "sha512-y1RYi4Q3/9ByVWSSt9kX2ustE0B7kFYbJ6zZdVZVyqopZs3yhCTwRfrjIX4vezUJInma/Gs6BOFDJg7yZmJ0IQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.63.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.63.3.tgz", - "integrity": "sha512-DNhEA5viIj3Z5bZLE4z4oV8N5ozWqDwyt7T6KG7VdLDJ0nW+rNOYlphBl4/3HQkK75qipPLsVOfStHHOwN9WSg==", - "cpu": [ - "loong64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loong64-musl": { - "version": "4.63.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.63.3.tgz", - "integrity": "sha512-17gQCqrIpXBX2Cmi9/TygnVOqGbzsba/iaqcYSL8FY7lNugg+7AiYNs5c5nKWD+NRQha36Sa0CqkJqH4XVHwnQ==", - "cpu": [ - "loong64" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.63.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.63.3.tgz", - "integrity": "sha512-6LwVnZRIyINpdku/yOcI8Tm9YqLmhHK5emmlOOnW9tO0SYEm1FmKPcsSAGp0NBlqR2P04xaND4jvN6sTHqhq8A==", - "cpu": [ - "ppc64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-ppc64-musl": { - "version": "4.63.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.63.3.tgz", - "integrity": "sha512-xMUqkTXlEUtI/p5AAukMwBRr1enU3efsTeF+bskeFfk8t1C9rcC8sLREcZXmTfAXEbvRdJVSonVJez3TMlbR3w==", - "cpu": [ - "ppc64" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.63.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.63.3.tgz", - "integrity": "sha512-S3E94co9F9WRRqEaUoQZ38K1gCz6KiM+nL7/3ijq7fDGF3OznjS5TasgYITlvl27GQKtu4lOAOsr5MFwkijvOA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.63.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.63.3.tgz", - "integrity": "sha512-1QtRDwG42x5BJI3s9mxu5rEjDnfbSnk20HQ9/ylTAYnSwYwxMVb+Vgu34wzzTQ7ogqBybebgQNUDAvZVQ38DbA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.63.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.63.3.tgz", - "integrity": "sha512-BQhejF6ZXOpxbngiNTP12GCGQeaDVL2QXGeBVViKIYzFHM5RKxTxwUMB1fr1BeNFphFMpnRqC5QSXFSa4z6UQw==", - "cpu": [ - "s390x" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.63.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.63.3.tgz", - "integrity": "sha512-SXagRwnI2Wlwlitllu59UK/nGVbD1CKPcNqDplHwIC4BqJcpXFjD32d1R/RbuISa95HdQrZM3/7v4bKiowFaLA==", - "cpu": [ - "x64" - ], - "dev": true, - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.63.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.63.3.tgz", - "integrity": "sha512-2IPozoEALRCziGqE8O9KMK60PMu5TS1huv4fwoeCexj+WjmcwFtX9CTOVbfXCUqcELAubEwRFPYlzb/WvwY2HQ==", - "cpu": [ - "x64" - ], - "dev": true, - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-openbsd-x64": { - "version": "4.63.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.63.3.tgz", - "integrity": "sha512-AoxqosUHT9IX54hFn2TiN6A7d6ZKTtE6pd2bqWtqkkNJ6HJGaU6FRouGX8L1O7R/ZwsnCnpQrHzb4pDEx+UHRQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ] - }, - "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.63.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.63.3.tgz", - "integrity": "sha512-d+CaftKgmkFBzCwezMqqy1d0QNNYugqLCMcYVQWBy5SS2YfeMP8Q8ripkgx9O8IyBXXLHrJ+aaCV4U96usv6Yg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ] - }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.63.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.63.3.tgz", - "integrity": "sha512-xXlDF6nR1eOuXbdDy5Hl5fmtY7teUDevF/k0O7IPoZe4Tpmdv+lgdE5JRsnhQtt37ql9P0VF2kAN9a0OCZdo+Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.63.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.63.3.tgz", - "integrity": "sha512-YtXAgLN+JP7Ay6qG3eWhc7IHMQPzLc8r3uvhAvlJIoCz/4Q32+Bl9Fmnywidh8v1GOIMmymjovfqY9ETAtysvA==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.63.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.63.3.tgz", - "integrity": "sha512-WuWtSJRNo549vzcfZyEgfqb6zeSgn1F+UE5kQ+BCjzz0W4MGCjntUHkZVc1VRuAM7+ULaSyhiPxD1spyewFvkQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.63.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.63.3.tgz", - "integrity": "sha512-+lIKX7O0+IGe7WuhATaAMMeT7B76vfhXH/l9wLQL+nvyhbw2ohYCKIdWL56JfDu75CWt5oKRP4QFH/jkMtBquA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@types/babel__core": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", - "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "node_modules/@types/babel__generator": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", - "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", - "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__traverse": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", - "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.28.2" - } - }, - "node_modules/@types/estree": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", - "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/react": { - "version": "19.3.0", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.3.0.tgz", - "integrity": "sha512-N0rFCuH9YoxG9/m61l9MfpJKfmLOVU0em7ipIz6TRgSSkvReLB9vL85GB+yr8Bs5leqpvg96JSwF4ZS1s4viQg==", - "dev": true, - "license": "MIT", - "dependencies": { - "csstype": "^3.2.2" - } - }, - "node_modules/@types/react-dom": { - "version": "19.3.0", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.3.0.tgz", - "integrity": "sha512-ZI7bU42mZXXKHn/qNLEw2IrbiINU7X5+vfgdixBHkCNpYWXjKgfQ/P+uyGb5CjOLB9UcnTeg3rylQtV2hym44Q==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "@types/react": "^19.3.0" - } - }, - "node_modules/@vitejs/plugin-react": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.2.0.tgz", - "integrity": "sha512-YmKkfhOAi3wsB1PhJq5Scj3GXMn3WvtQ/JC0xoopuHoXSdmtdStOpFrYaT1kie2YgFBcIe64ROzMYRjCrYOdYw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.29.0", - "@babel/plugin-transform-react-jsx-self": "^7.27.1", - "@babel/plugin-transform-react-jsx-source": "^7.27.1", - "@rolldown/pluginutils": "1.0.0-rc.3", - "@types/babel__core": "^7.20.5", - "react-refresh": "^0.18.0" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "peerDependencies": { - "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/baseline-browser-mapping": { - "version": "2.11.24", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.24.tgz", - "integrity": "sha512-hYrgxie335U08WqICoGqKRzV1HFXv6zdxwJE4ekCb80CM9a0SVVsN4QPwT67RraRo+9h8IATk6uxHJw7QSkdOg==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "baseline-browser-mapping": "dist/cli.cjs" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/browserslist": { - "version": "4.29.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.29.0.tgz", - "integrity": "sha512-3GSvyjvDI4Dur1Meg2BekJquu5uF+9R9a1+5M1Mde192eZoXbeXjzgOsgqPS2V8D5wrrip0gR5Hf/GhWQ9ZzaA==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "baseline-browser-mapping": "^2.11.23", - "caniuse-lite": "^1.0.30001810", - "electron-to-chromium": "^1.5.427", - "node-releases": "^2.0.55", - "update-browserslist-db": "^1.3.3" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001810", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001810.tgz", - "integrity": "sha512-TITQPUkaz+aVk5GL6NhOdwk1aEaNTSDPsGFWrTuhKGtjTF70jL/Oht2W4c6rXUe5fu7Ie19VIahAXHIIiWWNeg==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" - }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true, - "license": "MIT" - }, - "node_modules/csstype": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", - "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/electron-to-chromium": { - "version": "1.5.430", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.430.tgz", - "integrity": "sha512-e1QEj72Y4zd8RlNZVmoTg+iCOSVwpk05IOiiQwdrkwCSVlZfPthevErhE+nckGd2YbsXfp1SkisznhGVIXP2NQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/esbuild": { - "version": "0.28.2", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz", - "integrity": "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.28.2", - "@esbuild/android-arm": "0.28.2", - "@esbuild/android-arm64": "0.28.2", - "@esbuild/android-x64": "0.28.2", - "@esbuild/darwin-arm64": "0.28.2", - "@esbuild/darwin-x64": "0.28.2", - "@esbuild/freebsd-arm64": "0.28.2", - "@esbuild/freebsd-x64": "0.28.2", - "@esbuild/linux-arm": "0.28.2", - "@esbuild/linux-arm64": "0.28.2", - "@esbuild/linux-ia32": "0.28.2", - "@esbuild/linux-loong64": "0.28.2", - "@esbuild/linux-mips64el": "0.28.2", - "@esbuild/linux-ppc64": "0.28.2", - "@esbuild/linux-riscv64": "0.28.2", - "@esbuild/linux-s390x": "0.28.2", - "@esbuild/linux-x64": "0.28.2", - "@esbuild/netbsd-arm64": "0.28.2", - "@esbuild/netbsd-x64": "0.28.2", - "@esbuild/openbsd-arm64": "0.28.2", - "@esbuild/openbsd-x64": "0.28.2", - "@esbuild/openharmony-arm64": "0.28.2", - "@esbuild/sunos-x64": "0.28.2", - "@esbuild/win32-arm64": "0.28.2", - "@esbuild/win32-ia32": "0.28.2", - "@esbuild/win32-x64": "0.28.2" - } - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/jsesc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "dev": true, - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "license": "MIT", - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/marked": { - "version": "18.0.13", - "resolved": "https://registry.npmjs.org/marked/-/marked-18.0.13.tgz", - "integrity": "sha512-xTxVzZsBFwunP6HDmtBkabUQEYArnP7/rMDGmPj9SlrKlQ4i8MdYVow+nJL0eOqwpUqhzBoTBRADGN6uYwPyOw==", - "license": "MIT", - "bin": { - "marked": "bin/marked.js" - }, - "engines": { - "node": ">= 20" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/nanoid": { - "version": "3.3.19", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.19.tgz", - "integrity": "sha512-Y2tUNy4ouw6tq5oDSKeQYGOyhkUBhNOcGV/02KC+6kd9eDGqdZd++mjMiIDilrBYvjEnCYvVtsuHCuP+okSfug==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/node-releases": { - "version": "2.0.56", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.56.tgz", - "integrity": "sha512-x0InOIyzgdk+eyaWaRJFH5snEtiImgBgblZ2CyPrLmqqcuMQkEvcDPHbzqbD8eDsSeJbVOjn+crzyzHaM4D+/A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", - "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/postcss": { - "version": "8.5.28", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.28.tgz", - "integrity": "sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.18", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/react": { - "version": "19.3.0", - "resolved": "https://registry.npmjs.org/react/-/react-19.3.0.tgz", - "integrity": "sha512-E8LUcbtBWt20bbl2YoHfx4ZDBdxVTfOKtCZn9cDSJ4l6/nuoApcpIBcj47t2wZoVX8g2ZHuMHbiShgCR1T5Sog==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-dom": { - "version": "19.3.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.3.0.tgz", - "integrity": "sha512-JDk8dgif51OjFoDE70+OT9ICyYr+69HlmihNwp1+Nsfbna3t5sIiCa9ZJktDmQ4/1b/rn26hIAR2uYXDMr5r0Q==", - "license": "MIT", - "dependencies": { - "scheduler": "^0.28.0" - }, - "peerDependencies": { - "react": "^19.3.0" - } - }, - "node_modules/react-refresh": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.18.0.tgz", - "integrity": "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/rollup": { - "version": "4.63.3", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.63.3.tgz", - "integrity": "sha512-1i2XreiAoMMXuPGD6Msj2xWrMMkHojNRKivInxGQcg7/1KuPuYlfUutLyh4drnOxUTHX9cHI4wFoat8D/NKaBw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "1.0.9" - }, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "@napi-rs/lzma-linux-x64-gnu": "1.5.1", - "@rollup/rollup-android-arm-eabi": "4.63.3", - "@rollup/rollup-android-arm64": "4.63.3", - "@rollup/rollup-darwin-arm64": "4.63.3", - "@rollup/rollup-darwin-x64": "4.63.3", - "@rollup/rollup-freebsd-arm64": "4.63.3", - "@rollup/rollup-freebsd-x64": "4.63.3", - "@rollup/rollup-linux-arm-gnueabihf": "4.63.3", - "@rollup/rollup-linux-arm-musleabihf": "4.63.3", - "@rollup/rollup-linux-arm64-gnu": "4.63.3", - "@rollup/rollup-linux-arm64-musl": "4.63.3", - "@rollup/rollup-linux-loong64-gnu": "4.63.3", - "@rollup/rollup-linux-loong64-musl": "4.63.3", - "@rollup/rollup-linux-ppc64-gnu": "4.63.3", - "@rollup/rollup-linux-ppc64-musl": "4.63.3", - "@rollup/rollup-linux-riscv64-gnu": "4.63.3", - "@rollup/rollup-linux-riscv64-musl": "4.63.3", - "@rollup/rollup-linux-s390x-gnu": "4.63.3", - "@rollup/rollup-linux-x64-gnu": "4.63.3", - "@rollup/rollup-linux-x64-musl": "4.63.3", - "@rollup/rollup-openbsd-x64": "4.63.3", - "@rollup/rollup-openharmony-arm64": "4.63.3", - "@rollup/rollup-win32-arm64-msvc": "4.63.3", - "@rollup/rollup-win32-ia32-msvc": "4.63.3", - "@rollup/rollup-win32-x64-gnu": "4.63.3", - "@rollup/rollup-win32-x64-msvc": "4.63.3", - "fsevents": "~2.3.2" - } - }, - "node_modules/scheduler": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.28.0.tgz", - "integrity": "sha512-juorfCmIkIw8tT+p5BXSm6PJjQF/ycEYmKyzURCIt/RaZIhL+PulbQ9Yu2z1HdOJDdqDTlxA1+xKBmHXJsczAw==", - "license": "MIT" - }, - "node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/tinyglobby": { - "version": "0.2.17", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", - "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", - "dev": true, - "license": "MIT", - "dependencies": { - "fdir": "^6.5.0", - "picomatch": "^4.0.4" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" - } - }, - "node_modules/typescript": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/update-browserslist-db": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.3.3.tgz", - "integrity": "sha512-pJ2sYawQS0R/WI928Gj5GlPhTGzbMelq0+4INtSYNDV9ErKJcX6xjGWkoG/VnB3dpUm00zALaqkrUD77pO5TDQ==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/vite": { - "version": "7.3.6", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.6.tgz", - "integrity": "sha512-4XP60spRGjSZFf1qYH+dJIkK2znL3zQfl9KkOV9MkkRR/3Dls0dxaBsQPTloEc5BLXWPL9vsOxopxyKoMmDueg==", - "dev": true, - "license": "MIT", - "dependencies": { - "esbuild": "^0.27.0 || ^0.28.0", - "fdir": "^6.5.0", - "picomatch": "^4.0.3", - "postcss": "^8.5.6", - "rollup": "^4.43.0", - "tinyglobby": "^0.2.15" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^20.19.0 || >=22.12.0", - "jiti": ">=1.21.0", - "less": "^4.0.0", - "lightningcss": "^1.21.0", - "sass": "^1.70.0", - "sass-embedded": "^1.70.0", - "stylus": ">=0.54.8", - "sugarss": "^5.0.0", - "terser": "^5.16.0", - "tsx": "^4.8.1", - "yaml": "^2.4.2" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "jiti": { - "optional": true - }, - "less": { - "optional": true - }, - "lightningcss": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - }, - "tsx": { - "optional": true - }, - "yaml": { - "optional": true - } - } - }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true, - "license": "ISC" - } - } -} diff --git a/control-center/web/package.json b/control-center/web/package.json deleted file mode 100644 index 56d2a9d1..00000000 --- a/control-center/web/package.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "name": "control-center-web", - "private": true, - "version": "0.3.0", - "type": "module", - "scripts": { - "dev": "vite", - "build": "tsc && vite build", - "preview": "vite preview" - }, - "dependencies": { - "marked": "^18.0.13", - "react": "^19.2.0", - "react-dom": "^19.2.0" - }, - "devDependencies": { - "@types/react": "^19.2.7", - "@types/react-dom": "^19.2.3", - "@vitejs/plugin-react": "^5.1.0", - "typescript": "~5.9.3", - "vite": "^7.1.14" - } -} diff --git a/control-center/web/src/App.tsx b/control-center/web/src/App.tsx deleted file mode 100644 index 26bb1359..00000000 --- a/control-center/web/src/App.tsx +++ /dev/null @@ -1,116 +0,0 @@ -import { useEffect, useState, type FormEvent } from 'react' -import { getToken, setToken, onUnauthorized } from './api' -import { useHashPath, nav } from './router' -import Overview from './pages/Overview' -import Board from './pages/Board' -import TaskDetail from './pages/TaskDetail' -import Decisions from './pages/Decisions' -import Docs from './pages/Docs' -import Settings from './pages/Settings' -import Audit from './pages/Audit' - -const NAV: { path: string; label: string }[] = [ - { path: '/', label: '总览' }, - { path: '/board', label: '看板' }, - { path: '/decisions', label: '决策收件箱' }, - { path: '/docs', label: '文档库' }, - { path: '/settings', label: '设置' }, - { path: '/audit', label: '审计' }, -] - -function Login({ onLogin }: { onLogin: () => void }) { - const [value, setValue] = useState('') - const submit = (e: FormEvent) => { - e.preventDefault() - if (!value.trim()) return - setToken(value.trim()) - onLogin() - } - return ( -
-
-

Control Center

-
- - setValue(e.target.value)} - placeholder="CC_CONSOLE_TOKEN" - /> - - token 存于 localStorage,401 时自动清除。 -
-
-
- ) -} - -function Page({ path }: { path: string }) { - const taskMatch = path.match(/^\/tasks\/([^/]+)$/) - if (taskMatch) return - switch (path) { - case '/': - return - case '/board': - return - case '/decisions': - return - case '/docs': - return - case '/settings': - return - case '/audit': - return - default: - return - } -} - -export default function App() { - const [authed, setAuthed] = useState(() => getToken() !== '') - const path = useHashPath() - - useEffect(() => { - onUnauthorized(() => setAuthed(false)) - }, []) - - if (!authed) { - return setAuthed(true)} /> - } - - const active = path.startsWith('/tasks/') || path === '/board' ? '/board' : path - return ( -
- -
- -
-
- ) -} diff --git a/control-center/web/src/api.ts b/control-center/web/src/api.ts deleted file mode 100644 index 8bbd733f..00000000 --- a/control-center/web/src/api.ts +++ /dev/null @@ -1,79 +0,0 @@ -// Token-aware fetch wrapper for the console API. The token lives in -// localStorage; a 401 clears it and bounces the app back to the login screen. - -const TOKEN_KEY = 'cc_console_token' - -let unauthorizedHandler: () => void = () => {} - -export function getToken(): string { - return localStorage.getItem(TOKEN_KEY) ?? '' -} - -export function setToken(token: string) { - if (token) { - localStorage.setItem(TOKEN_KEY, token) - } else { - localStorage.removeItem(TOKEN_KEY) - } -} - -export function onUnauthorized(fn: () => void) { - unauthorizedHandler = fn -} - -export class ApiError extends Error { - status: number - constructor(status: number, message: string) { - super(message) - this.status = status - } -} - -async function request(method: string, path: string, body?: unknown): Promise { - const headers: Record = { - Authorization: `Bearer ${getToken()}`, - } - if (body !== undefined) { - headers['Content-Type'] = 'application/json' - } - const resp = await fetch(`/api/v1${path}`, { - method, - headers, - body: body === undefined ? undefined : JSON.stringify(body), - }) - if (resp.status === 401) { - setToken('') - unauthorizedHandler() - throw new ApiError(401, 'unauthorized') - } - if (!resp.ok) { - let msg = `http ${resp.status}` - try { - const data = (await resp.json()) as { error?: string } - if (data.error) msg = data.error - } catch { - // keep generic message - } - throw new ApiError(resp.status, msg) - } - if (resp.status === 204) { - return undefined as T - } - return (await resp.json()) as T -} - -export const api = { - get: (path: string) => request('GET', path), - post: (path: string, body?: unknown) => request('POST', path, body), - patch: (path: string, body?: unknown) => request('PATCH', path, body), - put: (path: string, body?: unknown) => request('PUT', path, body), -} - -export function qs(params: Record): string { - const sp = new URLSearchParams() - for (const [k, v] of Object.entries(params)) { - if (v) sp.set(k, v) - } - const s = sp.toString() - return s ? `?${s}` : '' -} diff --git a/control-center/web/src/app.css b/control-center/web/src/app.css deleted file mode 100644 index 9bcc77d7..00000000 --- a/control-center/web/src/app.css +++ /dev/null @@ -1,306 +0,0 @@ -:root { - --bg: #0d1117; - --bg-panel: #161b22; - --bg-hover: #1c2330; - --border: #2b3442; - --fg: #d6dde6; - --fg-dim: #8b97a7; - --accent: #4f9cf9; - --green: #3fb950; - --red: #f85149; - --yellow: #d29922; - --purple: #bc8cff; - --cyan: #39c5cf; - font-size: 14px; -} - -* { box-sizing: border-box; } - -body { - margin: 0; - background: var(--bg); - color: var(--fg); - font-family: "SF Mono", "Cascadia Code", Menlo, Consolas, "PingFang SC", "Microsoft YaHei", monospace; -} - -a { color: var(--accent); text-decoration: none; } -a:hover { text-decoration: underline; } - -button { - background: var(--bg-hover); - color: var(--fg); - border: 1px solid var(--border); - border-radius: 4px; - padding: 4px 10px; - font: inherit; - cursor: pointer; -} -button:hover:not(:disabled) { border-color: var(--accent); color: var(--accent); } -button:disabled { opacity: 0.4; cursor: not-allowed; } -button.primary { background: var(--accent); border-color: var(--accent); color: #0d1117; font-weight: 600; } -button.primary:hover:not(:disabled) { color: #0d1117; filter: brightness(1.15); } -button.danger { border-color: var(--red); color: var(--red); } - -input, select, textarea { - background: var(--bg); - color: var(--fg); - border: 1px solid var(--border); - border-radius: 4px; - padding: 4px 8px; - font: inherit; -} -input:focus, select:focus, textarea:focus { outline: none; border-color: var(--accent); } -textarea { width: 100%; resize: vertical; } - -/* layout */ -.layout { display: flex; min-height: 100vh; } -.sidebar { - width: 180px; - flex: 0 0 180px; - background: var(--bg-panel); - border-right: 1px solid var(--border); - padding: 12px 8px; - position: sticky; - top: 0; - height: 100vh; -} -.sidebar h1 { - font-size: 13px; - letter-spacing: 1px; - color: var(--fg-dim); - margin: 4px 8px 14px; - text-transform: uppercase; -} -.sidebar a { - display: block; - padding: 6px 10px; - border-radius: 4px; - color: var(--fg); - margin-bottom: 2px; -} -.sidebar a:hover { background: var(--bg-hover); text-decoration: none; } -.sidebar a.active { background: var(--bg-hover); color: var(--accent); border-left: 2px solid var(--accent); } -.main { flex: 1; padding: 16px 20px; min-width: 0; } - -.page-head { display: flex; align-items: center; gap: 12px; margin-bottom: 14px; flex-wrap: wrap; } -.page-head h2 { margin: 0; font-size: 16px; } -.page-head .spacer { flex: 1; } - -.panel { - background: var(--bg-panel); - border: 1px solid var(--border); - border-radius: 6px; - padding: 12px; - margin-bottom: 14px; -} -.panel h3 { margin: 0 0 10px; font-size: 13px; color: var(--fg-dim); text-transform: uppercase; letter-spacing: 1px; } - -.error-box { - background: rgba(248, 81, 73, 0.1); - border: 1px solid var(--red); - border-radius: 4px; - color: var(--red); - padding: 8px 12px; - margin-bottom: 12px; - white-space: pre-wrap; -} -.dim { color: var(--fg-dim); } -.mono-sm { font-size: 12px; } - -/* login */ -.login-wrap { display: flex; align-items: center; justify-content: center; min-height: 100vh; } -.login-box { width: 380px; } -.login-box form { display: flex; flex-direction: column; gap: 10px; } - -/* badges */ -.badge { - display: inline-block; - padding: 1px 7px; - border-radius: 10px; - font-size: 11px; - border: 1px solid var(--border); - color: var(--fg-dim); - white-space: nowrap; -} -.badge.st-idle { color: var(--fg-dim); } -.badge.st-assigned { color: var(--cyan); border-color: var(--cyan); } -.badge.st-in_progress { color: var(--accent); border-color: var(--accent); } -.badge.st-awaiting_review { color: var(--yellow); border-color: var(--yellow); } -.badge.st-changes_requested { color: var(--red); border-color: var(--red); } -.badge.st-approved { color: var(--green); border-color: var(--green); } -.badge.status-blocked_pending_decision { color: var(--purple); border-color: var(--purple); } -.badge.status-done { color: var(--green); border-color: var(--green); } -.badge.status-in_progress { color: var(--accent); border-color: var(--accent); } -.badge.status-cancelled { color: var(--fg-dim); } -.badge.sev-low { color: var(--fg-dim); } -.badge.sev-medium { color: var(--accent); border-color: var(--accent); } -.badge.sev-high { color: var(--yellow); border-color: var(--yellow); } -.badge.sev-critical { color: var(--red); border-color: var(--red); } - -/* audit */ -.finding-card { border: 1px solid var(--border); border-radius: 6px; padding: 10px 12px; margin-bottom: 10px; background: var(--bg-panel); } -.finding-card.sev-high, .finding-card.sev-critical { border-left: 3px solid var(--yellow); } -.finding-card.sev-critical { border-left-color: var(--red); } -.finding-card .f-head { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; cursor: pointer; } -.finding-card .f-title { font-weight: 600; } -.finding-card .f-body { margin-top: 8px; border-top: 1px solid var(--border); padding-top: 8px; } -.finding-card .f-refs { font-size: 12px; color: var(--fg-dim); margin-top: 6px; } -.finding-card .f-actions { display: flex; gap: 8px; margin-top: 8px; } -.runs-table { border-collapse: collapse; width: 100%; } -.runs-table th, .runs-table td { border: 1px solid var(--border); padding: 5px 8px; text-align: left; font-size: 12px; } -.runs-table th { color: var(--fg-dim); font-weight: 500; background: var(--bg-hover); } -.runs-table td .summary { color: var(--fg-dim); } - -/* overview */ -.stat-cards { display: flex; gap: 12px; flex-wrap: wrap; margin-bottom: 14px; } -.stat-card { - background: var(--bg-panel); - border: 1px solid var(--border); - border-radius: 6px; - padding: 10px 16px; - min-width: 130px; - cursor: pointer; -} -.stat-card .num { font-size: 26px; font-weight: 700; } -.stat-card .lbl { color: var(--fg-dim); font-size: 12px; } -.stat-card.warn .num { color: var(--yellow); } -.stat-card.crit .num { color: var(--red); } -.stat-card.ok .num { color: var(--green); } - -.matrix { border-collapse: collapse; width: 100%; } -.matrix th, .matrix td { - border: 1px solid var(--border); - padding: 4px 8px; - text-align: center; - font-size: 12px; -} -.matrix th { color: var(--fg-dim); font-weight: 500; background: var(--bg-hover); } -.matrix td:first-child, .matrix th:first-child { text-align: left; } -.matrix td.hot { color: var(--yellow); font-weight: 700; } - -.agent-row { display: flex; gap: 10px; align-items: center; padding: 3px 0; } -.dot { width: 8px; height: 8px; border-radius: 50%; display: inline-block; } -.dot.live { background: var(--green); } -.dot.stale { background: var(--red); } - -.event-feed { max-height: 300px; overflow-y: auto; font-size: 12px; } -.event-feed .ev { padding: 3px 0; border-bottom: 1px solid var(--border); display: flex; gap: 8px; } -.event-feed .ev .ts { color: var(--fg-dim); flex: 0 0 auto; } -.event-feed .ev .phase { color: var(--purple); flex: 0 0 auto; } - -/* kanban */ -.board { display: flex; gap: 10px; overflow-x: auto; padding-bottom: 8px; align-items: flex-start; } -.lane { - flex: 0 0 240px; - background: var(--bg-panel); - border: 1px solid var(--border); - border-radius: 6px; - padding: 8px; -} -.lane h4 { - margin: 0 0 8px; - font-size: 12px; - text-transform: uppercase; - letter-spacing: 1px; - color: var(--fg-dim); - display: flex; - justify-content: space-between; -} -.card { - background: var(--bg-hover); - border: 1px solid var(--border); - border-radius: 4px; - padding: 8px; - margin-bottom: 8px; - cursor: pointer; -} -.card:hover { border-color: var(--accent); } -.card .title { font-weight: 600; margin-bottom: 4px; } -.card .meta { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; font-size: 11px; color: var(--fg-dim); } - -/* stepper */ -.stepper { display: flex; gap: 4px; flex-wrap: wrap; margin-bottom: 14px; } -.step { - flex: 1; - min-width: 90px; - text-align: center; - padding: 6px 4px; - border: 1px solid var(--border); - border-radius: 4px; - font-size: 12px; - color: var(--fg-dim); - cursor: pointer; - background: var(--bg-panel); -} -.step.done { color: var(--green); border-color: var(--green); } -.step.current { color: var(--accent); border-color: var(--accent); background: rgba(79, 156, 249, 0.08); font-weight: 700; } -.step .check { margin-right: 4px; } - -.stage-panel { border: 1px solid var(--border); border-radius: 6px; margin-bottom: 8px; background: var(--bg-panel); } -.stage-panel > header { - padding: 8px 12px; - cursor: pointer; - display: flex; - gap: 10px; - align-items: center; -} -.stage-panel > header:hover { background: var(--bg-hover); } -.stage-panel > header .counts { margin-left: auto; font-size: 11px; color: var(--fg-dim); } -.stage-panel .body { border-top: 1px solid var(--border); padding: 10px 12px; } - -.op-row { display: flex; gap: 8px; align-items: flex-end; flex-wrap: wrap; margin-bottom: 10px; } -.op-row label { display: flex; flex-direction: column; gap: 3px; font-size: 11px; color: var(--fg-dim); } - -/* markdown */ -.md { font-size: 13px; line-height: 1.55; } -.md h2, .md h3, .md h4, .md h5 { margin: 10px 0 6px; } -.md p { margin: 6px 0; } -.md-code { - background: var(--bg); - border: 1px solid var(--border); - border-radius: 4px; - padding: 8px; - overflow-x: auto; - font-size: 12px; -} -.md code { background: var(--bg); border-radius: 3px; padding: 1px 4px; } -.md pre.md-code code { padding: 0; background: transparent; } -.md blockquote { border-left: 3px solid var(--border); margin: 6px 0; padding: 2px 10px; color: var(--fg-dim); } -.md-table { border-collapse: collapse; } -.md-table th, .md-table td { border: 1px solid var(--border); padding: 3px 8px; font-size: 12px; } -.md-image { color: var(--fg-dim); } - -/* decisions */ -.decision-card { border: 1px solid var(--yellow); border-radius: 6px; padding: 12px; margin-bottom: 12px; background: var(--bg-panel); } -.decision-card.resolved { border-color: var(--border); } -.suggestion { - border: 1px solid var(--border); - border-radius: 4px; - padding: 8px; - margin: 6px 0; - background: var(--bg-hover); -} -.suggestion .opt { font-weight: 600; margin-bottom: 4px; } -.suggestion .why { font-size: 12px; color: var(--fg-dim); } - -/* docs */ -.docs-layout { display: flex; gap: 14px; align-items: flex-start; } -.docs-side { flex: 0 0 260px; max-height: 75vh; overflow-y: auto; } -.docs-main { flex: 1; min-width: 0; } -.tax-group { margin-bottom: 8px; } -.tax-group > .tax-name { color: var(--accent); cursor: pointer; font-size: 12px; text-transform: uppercase; letter-spacing: 1px; } -.doc-item { padding: 3px 0 3px 10px; cursor: pointer; font-size: 12px; } -.doc-item:hover { color: var(--accent); } -.doc-item.active { color: var(--accent); } -.hit { border: 1px solid var(--border); border-radius: 4px; padding: 8px; margin-bottom: 8px; background: var(--bg-panel); } -.hit .snippet { font-size: 12px; color: var(--fg-dim); margin-top: 4px; white-space: pre-wrap; } -.hit .score { color: var(--green); font-size: 11px; } - -/* settings table */ -.settings-table { border-collapse: collapse; width: 100%; } -.settings-table th, .settings-table td { border: 1px solid var(--border); padding: 6px 8px; text-align: left; } -.settings-table th { color: var(--fg-dim); font-weight: 500; background: var(--bg-hover); } - -.filters { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; } - -details.fold > summary { cursor: pointer; color: var(--fg-dim); margin-bottom: 8px; } diff --git a/control-center/web/src/main.tsx b/control-center/web/src/main.tsx deleted file mode 100644 index 62cc9565..00000000 --- a/control-center/web/src/main.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import { createRoot } from 'react-dom/client' -import App from './App' -import './app.css' - -createRoot(document.getElementById('root')!).render() diff --git a/control-center/web/src/markdown.tsx b/control-center/web/src/markdown.tsx deleted file mode 100644 index ad4cb200..00000000 --- a/control-center/web/src/markdown.tsx +++ /dev/null @@ -1,119 +0,0 @@ -import type { ReactNode } from 'react' -import { lexer, type Token, type Tokens } from 'marked' - -// Renders markdown to React elements (never raw HTML), so agent-authored -// artifact content cannot inject markup. Uses marked only as a parser. - -function safeHref(href: string): string | undefined { - const h = href.trim() - if (/^(https?:|mailto:)/i.test(h) || h.startsWith('/') || h.startsWith('#')) { - return h - } - return undefined -} - -function inline(tokens: Token[] | undefined): ReactNode[] { - if (!tokens) return [] - return tokens.map((t, i) => { - switch (t.type) { - case 'strong': - return {inline((t as Tokens.Strong).tokens)} - case 'em': - return {inline((t as Tokens.Em).tokens)} - case 'del': - return {inline((t as Tokens.Del).tokens)} - case 'codespan': - return {(t as Tokens.Codespan).text} - case 'link': { - const lt = t as Tokens.Link - const href = safeHref(lt.href) - if (!href) return {lt.text} - return ( - - {lt.tokens ? inline(lt.tokens) : lt.text} - - ) - } - case 'image': { - const it = t as Tokens.Image - return [img: {it.text || it.href}] - } - case 'br': - return
- case 'escape': - return {(t as Tokens.Escape).text} - case 'html': - return {(t as Tokens.HTML).text} - case 'text': { - const tt = t as Tokens.Text - return {tt.tokens ? inline(tt.tokens) : tt.text} - } - default: - return {'raw' in t ? String(t.raw ?? '') : ''} - } - }) -} - -function block(tokens: Token[]): ReactNode[] { - return tokens.map((t, i) => { - switch (t.type) { - case 'heading': { - const h = t as Tokens.Heading - const content = inline(h.tokens) - switch (h.depth) { - case 1: return

{content}

- case 2: return

{content}

- case 3: return

{content}

- default: return
{content}
- } - } - case 'paragraph': - return

{inline((t as Tokens.Paragraph).tokens)}

- case 'code': { - const c = t as Tokens.Code - return ( -
-            {c.text}
-          
- ) - } - case 'blockquote': - return
{block((t as Tokens.Blockquote).tokens)}
- case 'list': { - const l = t as Tokens.List - const items = l.items.map((item, j) =>
  • {block(item.tokens)}
  • ) - return l.ordered ?
      {items}
    :
      {items}
    - } - case 'table': { - const tb = t as Tokens.Table - return ( - - - {tb.header.map((cell, j) => )} - - - {tb.rows.map((row, r) => ( - {row.map((cell, c) => )} - ))} - -
    {inline(cell.tokens)}
    {inline(cell.tokens)}
    - ) - } - case 'hr': - return
    - case 'space': - return null - case 'html': - return
    {(t as Tokens.HTML).text}
    - case 'text': - return

    {inline([(t as Tokens.Text)])}

    - default: - return
    {'raw' in t ? String(t.raw ?? '') : ''}
    - } - }) -} - -export function Markdown({ text }: { text: string }) { - if (!text.trim()) return null - return
    {block(lexer(text))}
    -} diff --git a/control-center/web/src/pages/Audit.tsx b/control-center/web/src/pages/Audit.tsx deleted file mode 100644 index 5638d860..00000000 --- a/control-center/web/src/pages/Audit.tsx +++ /dev/null @@ -1,219 +0,0 @@ -import { useCallback, useEffect, useState } from 'react' -import { api } from '../api' -import { nav } from '../router' -import { Markdown } from '../markdown' -import { fmtTime } from '../util' -import type { AuditFinding, AuditRun } from '../types' - -type FindingStatus = 'open' | 'converted' | 'dismissed' - -function runDuration(r: AuditRun): string { - if (!r.finished_at) return '-' - const ms = new Date(r.finished_at).getTime() - new Date(r.started_at).getTime() - if (Number.isNaN(ms)) return '-' - return `${(ms / 1000).toFixed(1)}s` -} - -function FindingCard({ f, onChanged }: { f: AuditFinding; onChanged: () => void }) { - const [open, setOpen] = useState(false) - const [busy, setBusy] = useState(false) - const [err, setErr] = useState('') - - const act = async (action: 'convert' | 'dismiss') => { - setBusy(true) - setErr('') - try { - await api.post(`/audits/findings/${f.id}/${action}`) - onChanged() - } catch (ex) { - setErr((ex as Error).message) - setBusy(false) - } - } - - return ( -
    -
    setOpen(!open)}> - {f.severity} - {f.kind} - {f.title} - {fmtTime(f.created_at)} - {open ? '▾' : '▸'} -
    - {open && ( -
    - - {f.refs.length > 0 && ( -
    refs: {f.refs.join(' · ')}
    - )} -
    - run #{f.run_id} · {fmtTime(f.run_started_at)} - {f.task_id && ( - <> - {' · 已转任务 '} - { - e.preventDefault() - nav(`/tasks/${f.task_id}`) - }} - > - {f.task_id.slice(0, 8)} - - - )} -
    - {err &&
    {err}
    } - {f.status === 'open' && ( -
    e.stopPropagation()}> - - -
    - )} -
    - )} -
    - ) -} - -export default function Audit() { - const [runs, setRuns] = useState([]) - const [findings, setFindings] = useState([]) - const [tab, setTab] = useState('open') - const [severity, setSeverity] = useState('') - const [err, setErr] = useState('') - const [running, setRunning] = useState(false) - - const loadRuns = useCallback(() => { - api - .get('/audits/runs?limit=20') - .then(setRuns) - .catch(() => {}) - }, []) - - const loadFindings = useCallback(() => { - api - .get(`/audits/findings?status=${tab}${severity ? `&severity=${severity}` : ''}&limit=100`) - .then((f) => { - setFindings(f) - setErr('') - }) - .catch((e: Error) => { - if (e.message !== 'unauthorized') setErr(e.message) - }) - }, [tab, severity]) - - const refresh = useCallback(() => { - loadRuns() - loadFindings() - }, [loadRuns, loadFindings]) - - useEffect(() => { - refresh() - }, [refresh]) - - // While a run is in flight, poll until it finishes. - useEffect(() => { - if (!runs.some((r) => r.status === 'running')) return - const timer = setInterval(refresh, 3000) - return () => clearInterval(timer) - }, [runs, refresh]) - - const triggerRun = async () => { - setRunning(true) - setErr('') - try { - await api.post('/audits/run') - loadRuns() - } catch (ex) { - setErr((ex as Error).message) - } finally { - setRunning(false) - } - } - - const isRunning = runs.some((r) => r.status === 'running') - - return ( -
    -
    -

    审计

    - {findings.length} {tab} -
    - - -
    - {err &&
    {err}
    } - -
    -

    运行历史

    - {runs.length === 0 ? ( -
    暂无审查记录。点击「立即运行」触发一次审查。
    - ) : ( - - - - - - - - - - - - - - {runs.map((r) => ( - - - - - - - - - - ))} - -
    #kind状态开始耗时findingssummary
    {r.id}{r.kind} - - {r.status} - - {fmtTime(r.started_at)}{runDuration(r)}{r.findings} - {r.error || r.summary} -
    - )} -
    - -
    -

    Findings

    -
    - {(['open', 'converted', 'dismissed'] as FindingStatus[]).map((s) => ( - - ))} - severity: - -
    - {findings.length === 0 ? ( -
    当前过滤条件下没有 findings。
    - ) : ( - findings.map((f) => ) - )} -
    -
    - ) -} diff --git a/control-center/web/src/pages/Board.tsx b/control-center/web/src/pages/Board.tsx deleted file mode 100644 index 9b14bfab..00000000 --- a/control-center/web/src/pages/Board.tsx +++ /dev/null @@ -1,170 +0,0 @@ -import { useCallback, useEffect, useState, type FormEvent } from 'react' -import { api, qs } from '../api' -import { nav } from '../router' -import { STAGES, TASK_STATUSES, type Plan, type Task } from '../types' - -function NewTaskForm({ plans, onCreated, onClose }: { plans: Plan[]; onCreated: () => void; onClose: () => void }) { - const [slug, setSlug] = useState('') - const [title, setTitle] = useState('') - const [kind, setKind] = useState('task') - const [planID, setPlanID] = useState('') - const [testingRequired, setTestingRequired] = useState(true) - const [priority, setPriority] = useState(0) - const [instructionMD, setInstructionMD] = useState('') - const [err, setErr] = useState('') - const [busy, setBusy] = useState(false) - - const submit = async (e: FormEvent) => { - e.preventDefault() - setBusy(true) - setErr('') - try { - await api.post('/tasks', { - slug: slug.trim(), - title: title.trim(), - kind, - plan_id: planID || undefined, - testing_required: testingRequired, - priority, - instruction_md: instructionMD, - }) - onCreated() - } catch (ex) { - setErr((ex as Error).message) - setBusy(false) - } - } - - return ( -
    -

    新建任务

    - {err &&
    {err}
    } -
    -
    - - - - - - -
    - -