patchwarden 1.5.0 → 1.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (217) hide show
  1. package/README.en.md +43 -2
  2. package/README.md +43 -2
  3. package/dist/assessments/agentAssessor.d.ts +1 -1
  4. package/dist/assessments/agentAssessor.js +3 -3
  5. package/dist/assessments/assessmentStore.d.ts +1 -1
  6. package/dist/assessments/assessmentStore.js +2 -2
  7. package/dist/assessments/confirmCli.js +5 -4
  8. package/dist/config.d.ts +7 -0
  9. package/dist/config.js +34 -0
  10. package/dist/control/middleware/auth.d.ts +10 -0
  11. package/dist/control/middleware/auth.js +8 -0
  12. package/dist/control/middleware/static.d.ts +3 -0
  13. package/dist/control/middleware/static.js +65 -0
  14. package/dist/control/routes/audit.d.ts +15 -0
  15. package/dist/control/routes/audit.js +277 -0
  16. package/dist/control/routes/evidence.d.ts +4 -0
  17. package/dist/control/routes/evidence.js +96 -0
  18. package/dist/control/routes/lineage.d.ts +3 -0
  19. package/dist/control/routes/lineage.js +71 -0
  20. package/dist/control/routes/policy.d.ts +3 -0
  21. package/dist/control/routes/policy.js +81 -0
  22. package/dist/control/routes/process.d.ts +5 -0
  23. package/dist/control/routes/process.js +200 -0
  24. package/dist/control/routes/sessions.d.ts +22 -0
  25. package/dist/control/routes/sessions.js +224 -0
  26. package/dist/control/routes/status.d.ts +6 -0
  27. package/dist/control/routes/status.js +250 -0
  28. package/dist/control/routes/taskActions.d.ts +21 -0
  29. package/dist/control/routes/taskActions.js +233 -0
  30. package/dist/control/routes/tasks.d.ts +20 -0
  31. package/dist/control/routes/tasks.js +310 -0
  32. package/dist/control/routes/workspace.d.ts +15 -0
  33. package/dist/control/routes/workspace.js +193 -0
  34. package/dist/control/runtime.d.ts +91 -0
  35. package/dist/control/runtime.js +392 -0
  36. package/dist/control/server.d.ts +13 -0
  37. package/dist/control/server.js +479 -0
  38. package/dist/control/shared.d.ts +35 -0
  39. package/dist/control/shared.js +288 -0
  40. package/dist/controlCenter.d.ts +6 -0
  41. package/dist/controlCenter.js +7 -2197
  42. package/dist/direct/directGuards.js +30 -8
  43. package/dist/doctor.d.ts +18 -1
  44. package/dist/doctor.js +579 -348
  45. package/dist/goal/goalReport.d.ts +54 -0
  46. package/dist/goal/goalReport.js +204 -0
  47. package/dist/goal/goalStatus.d.ts +6 -0
  48. package/dist/goal/specKitImport.d.ts +63 -0
  49. package/dist/goal/specKitImport.js +220 -0
  50. package/dist/goal/subgoalSync.js +2 -1
  51. package/dist/httpServer.js +15 -12
  52. package/dist/index.js +7 -4
  53. package/dist/logging.d.ts +7 -1
  54. package/dist/logging.js +8 -0
  55. package/dist/runner/changeCapture.d.ts +3 -3
  56. package/dist/runner/changeCapture.js +63 -39
  57. package/dist/runner/cli.js +7 -6
  58. package/dist/runner/postTaskCleanup.js +26 -2
  59. package/dist/runner/runTask.js +245 -221
  60. package/dist/runner/simpleProcess.js +4 -4
  61. package/dist/runner/watch.js +17 -14
  62. package/dist/security/contentRedaction.d.ts +6 -0
  63. package/dist/security/contentRedaction.js +22 -0
  64. package/dist/smoke-test.js +257 -251
  65. package/dist/test/unit/apply-patch.test.d.ts +1 -0
  66. package/dist/test/unit/apply-patch.test.js +225 -0
  67. package/dist/test/unit/create-task.test.d.ts +1 -0
  68. package/dist/test/unit/create-task.test.js +197 -0
  69. package/dist/test/unit/direct-guards.test.js +124 -9
  70. package/dist/test/unit/evidence-pack.test.js +95 -1
  71. package/dist/test/unit/get-task-status.test.d.ts +1 -0
  72. package/dist/test/unit/get-task-status.test.js +174 -0
  73. package/dist/test/unit/get-task-summary.test.d.ts +1 -0
  74. package/dist/test/unit/get-task-summary.test.js +146 -0
  75. package/dist/test/unit/goal-report.test.d.ts +1 -0
  76. package/dist/test/unit/goal-report.test.js +159 -0
  77. package/dist/test/unit/goal-subgoal-task.test.js +6 -6
  78. package/dist/test/unit/goal-tools-registry.test.js +6 -4
  79. package/dist/test/unit/path-guard.test.js +24 -0
  80. package/dist/test/unit/spec-kit-import.test.d.ts +1 -0
  81. package/dist/test/unit/spec-kit-import.test.js +341 -0
  82. package/dist/test/unit/wait-for-task.test.d.ts +1 -0
  83. package/dist/test/unit/wait-for-task.test.js +144 -0
  84. package/dist/tools/auditTask.d.ts +8 -63
  85. package/dist/tools/auditTask.js +12 -8
  86. package/dist/tools/createDirectSession.d.ts +1 -1
  87. package/dist/tools/createDirectSession.js +2 -2
  88. package/dist/tools/createTask.d.ts +2 -2
  89. package/dist/tools/createTask.js +4 -4
  90. package/dist/tools/dispatch/coreDispatch.d.ts +9 -0
  91. package/dist/tools/dispatch/coreDispatch.js +282 -0
  92. package/dist/tools/dispatch/diagnosticDispatch.d.ts +14 -0
  93. package/dist/tools/dispatch/diagnosticDispatch.js +78 -0
  94. package/dist/tools/dispatch/directDispatch.d.ts +8 -0
  95. package/dist/tools/dispatch/directDispatch.js +115 -0
  96. package/dist/tools/dispatch/goalDispatch.d.ts +8 -0
  97. package/dist/tools/dispatch/goalDispatch.js +91 -0
  98. package/dist/tools/dispatch/releaseDispatch.d.ts +8 -0
  99. package/dist/tools/dispatch/releaseDispatch.js +45 -0
  100. package/dist/tools/dispatch/types.d.ts +23 -0
  101. package/dist/tools/dispatch/types.js +15 -0
  102. package/dist/tools/evidencePack.d.ts +6 -0
  103. package/dist/tools/evidencePack.js +213 -6
  104. package/dist/tools/finalizeDirectSession.d.ts +1 -1
  105. package/dist/tools/finalizeDirectSession.js +3 -3
  106. package/dist/tools/goalSubgoalTask.d.ts +1 -1
  107. package/dist/tools/goalSubgoalTask.js +2 -2
  108. package/dist/tools/healthCheck.js +3 -3
  109. package/dist/tools/registry.d.ts +3 -3
  110. package/dist/tools/registry.js +60 -503
  111. package/dist/tools/retryTask.d.ts +2 -2
  112. package/dist/tools/retryTask.js +2 -2
  113. package/dist/tools/runTaskLoop.js +4 -4
  114. package/dist/tools/safeViews.d.ts +2 -2
  115. package/dist/tools/safeViews.js +2 -2
  116. package/dist/tools/toolRegistry.js +22 -0
  117. package/dist/version.d.ts +1 -1
  118. package/dist/version.js +1 -1
  119. package/docs/CODE_WIKI.md +983 -0
  120. package/docs/agentseal-integration.md +150 -0
  121. package/docs/architecture.md +63 -0
  122. package/docs/assets/patchwarden-oss-demo.gif +0 -0
  123. package/docs/dashboard-overview.md +86 -0
  124. package/docs/demo.md +8 -0
  125. package/docs/direct-session-workflow.md +98 -0
  126. package/docs/evidence-pack-schema.md +215 -0
  127. package/docs/execution-plan-2026-07-09.md +315 -0
  128. package/docs/lineage-evidence-pack-workflow.md +127 -0
  129. package/docs/mcp-inspector-testing.md +200 -0
  130. package/docs/open-source-application.md +162 -0
  131. package/docs/opencode-worker.md +151 -0
  132. package/docs/openhands-worker.md +181 -0
  133. package/docs/release-evidence.md +72 -0
  134. package/docs/roadmap-execution-and-acceptance.md +365 -0
  135. package/docs/spec-kit-integration.md +131 -0
  136. package/docs/task-safe-review-workflow.md +98 -0
  137. package/docs/threat-model.md +79 -0
  138. package/docs/user-feedback.md +40 -0
  139. package/docs/why-patchwarden.md +110 -0
  140. package/package.json +2 -2
  141. package/scripts/checks/control-center-smoke.js +356 -0
  142. package/scripts/checks/lifecycle-smoke.js +29 -23
  143. package/scripts/checks/mcp-smoke.js +3 -1
  144. package/scripts/generate-demo-gif.py +320 -0
  145. package/src/assessments/agentAssessor.ts +3 -3
  146. package/src/assessments/assessmentStore.ts +2 -2
  147. package/src/assessments/confirmCli.ts +5 -4
  148. package/src/config.ts +37 -0
  149. package/src/control/middleware/auth.ts +17 -0
  150. package/src/control/middleware/static.ts +71 -0
  151. package/src/control/routes/audit.ts +321 -0
  152. package/src/control/routes/evidence.ts +107 -0
  153. package/src/control/routes/lineage.ts +92 -0
  154. package/src/control/routes/policy.ts +81 -0
  155. package/src/control/routes/process.ts +204 -0
  156. package/src/control/routes/sessions.ts +251 -0
  157. package/src/control/routes/status.ts +325 -0
  158. package/src/control/routes/taskActions.ts +248 -0
  159. package/src/control/routes/tasks.ts +323 -0
  160. package/src/control/routes/workspace.ts +203 -0
  161. package/src/control/runtime.ts +472 -0
  162. package/src/control/server.ts +471 -0
  163. package/src/control/shared.ts +294 -0
  164. package/src/controlCenter.ts +7 -2347
  165. package/src/direct/directGuards.ts +28 -7
  166. package/src/doctor.ts +741 -481
  167. package/src/goal/goalReport.ts +271 -0
  168. package/src/goal/goalStatus.ts +6 -0
  169. package/src/goal/specKitImport.ts +355 -0
  170. package/src/goal/subgoalSync.ts +4 -2
  171. package/src/httpServer.ts +17 -14
  172. package/src/index.ts +7 -4
  173. package/src/logging.ts +10 -1
  174. package/src/runner/changeCapture.ts +70 -42
  175. package/src/runner/cli.ts +7 -6
  176. package/src/runner/postTaskCleanup.ts +26 -2
  177. package/src/runner/runTask.ts +325 -223
  178. package/src/runner/simpleProcess.ts +4 -4
  179. package/src/runner/watch.ts +17 -14
  180. package/src/security/contentRedaction.ts +29 -0
  181. package/src/smoke-test.ts +252 -250
  182. package/src/test/unit/apply-patch.test.ts +293 -0
  183. package/src/test/unit/create-task.test.ts +255 -0
  184. package/src/test/unit/direct-guards.test.ts +178 -8
  185. package/src/test/unit/evidence-pack.test.ts +110 -1
  186. package/src/test/unit/get-task-status.test.ts +203 -0
  187. package/src/test/unit/get-task-summary.test.ts +173 -0
  188. package/src/test/unit/goal-report.test.ts +189 -0
  189. package/src/test/unit/goal-subgoal-task.test.ts +6 -6
  190. package/src/test/unit/goal-tools-registry.test.ts +7 -5
  191. package/src/test/unit/path-guard.test.ts +30 -0
  192. package/src/test/unit/spec-kit-import.test.ts +429 -0
  193. package/src/test/unit/wait-for-task.test.ts +176 -0
  194. package/src/tools/auditTask.ts +99 -59
  195. package/src/tools/createDirectSession.ts +3 -3
  196. package/src/tools/createTask.ts +7 -7
  197. package/src/tools/dispatch/coreDispatch.ts +374 -0
  198. package/src/tools/dispatch/diagnosticDispatch.ts +101 -0
  199. package/src/tools/dispatch/directDispatch.ts +167 -0
  200. package/src/tools/dispatch/goalDispatch.ts +127 -0
  201. package/src/tools/dispatch/releaseDispatch.ts +65 -0
  202. package/src/tools/dispatch/types.ts +24 -0
  203. package/src/tools/evidencePack.ts +291 -6
  204. package/src/tools/finalizeDirectSession.ts +4 -4
  205. package/src/tools/goalSubgoalTask.ts +2 -2
  206. package/src/tools/healthCheck.ts +3 -3
  207. package/src/tools/registry.ts +68 -628
  208. package/src/tools/retryTask.ts +2 -2
  209. package/src/tools/runTaskLoop.ts +4 -4
  210. package/src/tools/safeViews.ts +2 -2
  211. package/src/tools/toolRegistry.ts +22 -0
  212. package/src/version.ts +1 -1
  213. package/ui/pages/audit.html +192 -3
  214. package/ui/pages/dashboard.html +959 -44
  215. package/ui/pages/direct-sessions.html +505 -53
  216. package/ui/pages/task-detail.html +456 -438
  217. package/ui/pages/tasks.html +598 -61
package/README.en.md CHANGED
@@ -8,7 +8,7 @@
8
8
  [![Node.js](https://img.shields.io/badge/Node.js-%3E%3D18-339933.svg)](https://nodejs.org/)
9
9
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
10
10
 
11
- Current source version: **v1.5.0**. See the
11
+ Current source version: **v1.5.1**. See the
12
12
  [CHANGELOG](CHANGELOG.md), [migration guide](docs/migration-from-safe-bifrost.md), and
13
13
  [release checklist](docs/release-checklist.md). Verify GitHub Release / npm publication separately before release.
14
14
 
@@ -46,6 +46,7 @@ independent verification evidence.
46
46
  - [HTTP MCP mode](#http-mcp-mode)
47
47
  - [Diagnostics and health checks](#diagnostics-and-health-checks)
48
48
  - [Lessons learned and troubleshooting](#lessons-learned-and-troubleshooting)
49
+ - [Ecosystem guides](#ecosystem-guides)
49
50
  - [Security boundaries and local data](#security-boundaries-and-local-data)
50
51
  - [Upgrading and migration](#upgrading-and-migration)
51
52
  - [Development and release verification](#development-and-release-verification)
@@ -845,6 +846,31 @@ It stops only this project's launchers and Watcher plus `tunnel-client.exe`
845
846
  processes that exactly match the selected profile. It does not globally
846
847
  terminate unrelated PatchWarden, OpenCode, or Codex instances.
847
848
 
849
+ ## Dashboard pages and recommended workflow
850
+
851
+ The PatchWarden Dashboard (Control Center) provides these main pages:
852
+
853
+ - **Dashboard**: System overview, including Repo selector, Health Score, service status, Release card, Project Policy, Lineage, Evidence Pack, stale task hints, recent task list, and system status (with Copy diagnostics).
854
+ - **Tasks**: Task list, filterable by repo_path / status / acceptance_status / warning type / agent / date range. Each row offers safe_result / safe_audit / safe_test_summary / safe_diff_summary quick actions.
855
+ - **Task Detail**: Defaults to a safe summary (safe_result / safe_test_summary / safe_diff_summary / safe_audit). Full result / diff / test log live in a collapsed advanced section and load on demand.
856
+ - **Direct Sessions**: Grouped by active / finalized / audited / expired, with safe_direct_summary / safe_finalize_direct_session / safe_audit_direct_session quick actions.
857
+ - **Audit / Warnings**: Aggregated by warning type, showing affected tasks / severity / recommended action.
858
+ - **Workspace**: First-level workspace directories and project list.
859
+ - **Logs**: Tails of Core / Direct / Watcher / Control Center logs.
860
+
861
+ Recommended workflow:
862
+
863
+ 1. Select the target repo at the top of the Dashboard.
864
+ 2. Check the Health Score to confirm system health.
865
+ 3. Click safe_result on a recent task row to view the task summary.
866
+ 4. Open Task Detail for safe-first acceptance; expand the advanced section only when you need the full logs.
867
+ 5. Use Lineage Detail to inspect why a run_task_loop succeeded or failed.
868
+ 6. Export the Evidence Pack after acceptance.
869
+ 7. Use Direct sessions for independent verification, then finalize + audit.
870
+ 8. When something goes wrong, click Copy diagnostics and paste the output to ChatGPT / Codex / opencode for triage.
871
+
872
+ > See [docs/dashboard-overview.md](docs/dashboard-overview.md), [docs/task-safe-review-workflow.md](docs/task-safe-review-workflow.md), [docs/lineage-evidence-pack-workflow.md](docs/lineage-evidence-pack-workflow.md), and [docs/direct-session-workflow.md](docs/direct-session-workflow.md) for details.
873
+
848
874
  ## Lessons learned and troubleshooting
849
875
 
850
876
  ### Quick reference
@@ -1017,6 +1043,17 @@ the Watcher, and retains the existing DPAPI credential handling. In a fresh
1017
1043
  ChatGPT conversation, `health_check` should report `chatgpt_direct`, fourteen
1018
1044
  tools, and `direct_profile_enabled=true`.
1019
1045
 
1046
+ ## Ecosystem guides
1047
+
1048
+ - [Why PatchWarden](docs/why-patchwarden.md)
1049
+ - [Evidence Pack v2 schema](docs/evidence-pack-schema.md)
1050
+ - [Spec Kit integration](docs/spec-kit-integration.md)
1051
+ - [AgentSeal integration](docs/agentseal-integration.md)
1052
+ - [MCP Inspector testing](docs/mcp-inspector-testing.md)
1053
+ - [OpenCode worker integration](docs/opencode-worker.md)
1054
+ - [OpenHands worker integration](docs/openhands-worker.md)
1055
+ - [Threat model](docs/threat-model.md)
1056
+
1020
1057
  ## Security boundaries and local data
1021
1058
 
1022
1059
  Primary protections:
@@ -1122,6 +1159,10 @@ and release-asset checksums independently.
1122
1159
  - [ChatGPT usage guide](docs/chatgpt-usage.md)
1123
1160
  - [Migration guide](docs/migration-from-safe-bifrost.md)
1124
1161
  - [ChatGPT Connector demo](docs/demo.md)
1162
+ - [Dashboard overview](docs/dashboard-overview.md)
1163
+ - [Task safe review workflow](docs/task-safe-review-workflow.md)
1164
+ - [Lineage and Evidence Pack workflow](docs/lineage-evidence-pack-workflow.md)
1165
+ - [Direct session workflow](docs/direct-session-workflow.md)
1125
1166
  - [OpenAI tunnel examples](examples/openai-tunnel/README.md)
1126
1167
  - [ChatGPT test prompt](examples/openai-tunnel/chatgpt-test-prompt.md)
1127
1168
 
@@ -1137,7 +1178,7 @@ and release-asset checksums independently.
1137
1178
  - [x] Release Gate (five-stage pre-release verification)
1138
1179
  - [x] Worktree isolation
1139
1180
  - [x] Multi-agent routing
1140
- - [ ] Local dashboard
1181
+ - [x] Local dashboard
1141
1182
 
1142
1183
  ## License
1143
1184
 
package/README.md CHANGED
@@ -8,7 +8,7 @@
8
8
  [![Node.js](https://img.shields.io/badge/Node.js-%3E%3D18-339933.svg)](https://nodejs.org/)
9
9
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
10
10
 
11
- 当前源码版本:**v1.5.0**。查看
11
+ 当前源码版本:**v1.5.1**。查看
12
12
  [CHANGELOG](CHANGELOG.md)、[迁移指南](docs/migration-from-safe-bifrost.md)和
13
13
  [发布检查清单](docs/release-checklist.md)。GitHub Release / npm 发布状态需要在发布前单独核对。
14
14
 
@@ -44,6 +44,7 @@ PatchWarden 负责把计划保存成工作区内任务,再由预先配置的
44
44
  - [诊断与健康检查](#诊断与健康检查)
45
45
  - [踩坑记录与故障排查](#踩坑记录与故障排查)
46
46
  - [Direct 模式:ChatGPT 直接开发](#direct-模式chatgpt-直接开发)
47
+ - [生态适配](#生态适配)
47
48
  - [安全边界与本地数据](#安全边界与本地数据)
48
49
  - [升级与旧版本迁移](#升级与旧版本迁移)
49
50
  - [开发与发布验证](#开发与发布验证)
@@ -751,6 +752,31 @@ powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%CD%\scripts\control\st
751
752
  `chatgpt_core` / `patchwarden` → `127.0.0.1:8080`,
752
753
  `chatgpt_direct` / `patchwarden-direct` → `127.0.0.1:8081`。
753
754
 
755
+ ## Dashboard 主要页面与推荐工作流
756
+
757
+ PatchWarden Dashboard (Control Center) 提供以下主要页面:
758
+
759
+ - **控制台 (Dashboard)**: 系统总览,包含 Repo selector、Health Score、服务状态、Release 卡片、Project Policy、Lineage、Evidence Pack、Stale 任务提示、最近任务列表、系统状态(含 Copy diagnostics)。
760
+ - **任务面板 (Tasks)**: 任务列表,支持按 repo_path / status / acceptance_status / warning type / agent / date range 过滤,每行提供 safe_result / safe_audit / safe_test_summary / safe_diff_summary 快捷操作。
761
+ - **任务详情 (Task Detail)**: 默认展示 safe 摘要(safe_result / safe_test_summary / safe_diff_summary / safe_audit),完整 result / diff / test log 在折叠的高级区按需加载。
762
+ - **Direct 会话 (Direct Sessions)**: 按 active / finalized / audited / expired 分组,提供 safe_direct_summary / safe_finalize_direct_session / safe_audit_direct_session 快捷操作。
763
+ - **审计日志 / Warnings (Audit)**: 按 warning 类型聚合,显示 affected tasks / severity / recommended action。
764
+ - **工作区 (Workspace)**: workspace 一级目录与项目列表。
765
+ - **日志 (Logs)**: Core / Direct / Watcher / Control Center 日志尾部。
766
+
767
+ 推荐工作流:
768
+
769
+ 1. 在 Dashboard 顶部选择目标 repo。
770
+ 2. 查看 Health Score 确认系统健康。
771
+ 3. 在最近任务列表点击 safe_result 快捷查看任务摘要。
772
+ 4. 打开 Task Detail 进行 safe-first 验收,仅在需要时展开高级区查看完整日志。
773
+ 5. 使用 Lineage Detail 查看 run_task_loop 的成功/失败原因。
774
+ 6. 验收完成后导出 Evidence Pack。
775
+ 7. Direct 会话用于独立验证,完成后 finalize + audit。
776
+ 8. 遇到问题时点击 Copy diagnostics 复制诊断信息发给 ChatGPT / Codex / opencode 排查。
777
+
778
+ > 详细说明见 [docs/dashboard-overview.md](docs/dashboard-overview.md)、[docs/task-safe-review-workflow.md](docs/task-safe-review-workflow.md)、[docs/lineage-evidence-pack-workflow.md](docs/lineage-evidence-pack-workflow.md)、[docs/direct-session-workflow.md](docs/direct-session-workflow.md)。
779
+
754
780
  ## 踩坑记录与故障排查
755
781
 
756
782
  ### 快速对照表
@@ -977,6 +1003,17 @@ Direct 模式在受控 session 内执行,具有以下硬性限制:
977
1003
 
978
1004
  Direct 模式不支持:shell、delete、rename、commit、push、publish、deployment。
979
1005
 
1006
+ ## 生态适配
1007
+
1008
+ - [为什么需要 PatchWarden](docs/why-patchwarden.md)
1009
+ - [Evidence Pack v2 文件结构](docs/evidence-pack-schema.md)
1010
+ - [Spec Kit 集成模式](docs/spec-kit-integration.md)
1011
+ - [AgentSeal 集成模式](docs/agentseal-integration.md)
1012
+ - [MCP Inspector 测试](docs/mcp-inspector-testing.md)
1013
+ - [OpenCode worker 集成](docs/opencode-worker.md)
1014
+ - [OpenHands worker 集成](docs/openhands-worker.md)
1015
+ - [威胁模型](docs/threat-model.md)
1016
+
980
1017
  ## 安全边界与本地数据
981
1018
 
982
1019
  PatchWarden 的主要保护:
@@ -1081,6 +1118,10 @@ Release 和发布资产校验值。
1081
1118
  - [ChatGPT 调用规范](docs/chatgpt-usage.md)
1082
1119
  - [旧版本迁移指南](docs/migration-from-safe-bifrost.md)
1083
1120
  - [ChatGPT Connector 演示](docs/demo.md)
1121
+ - [Dashboard 概览](docs/dashboard-overview.md)
1122
+ - [任务 safe 验收工作流](docs/task-safe-review-workflow.md)
1123
+ - [Lineage 与 Evidence Pack 工作流](docs/lineage-evidence-pack-workflow.md)
1124
+ - [Direct 会话工作流](docs/direct-session-workflow.md)
1084
1125
  - [OpenAI Tunnel 示例](examples/openai-tunnel/README.md)
1085
1126
  - [ChatGPT 测试提示词](examples/openai-tunnel/chatgpt-test-prompt.md)
1086
1127
 
@@ -1096,7 +1137,7 @@ Release 和发布资产校验值。
1096
1137
  - [x] Release Gate(发布前五阶段校验)
1097
1138
  - [x] Worktree 隔离
1098
1139
  - [x] 多 Agent 路由
1099
- - [ ] 本地 Dashboard
1140
+ - [x] 本地 Dashboard
1100
1141
 
1101
1142
  ## License
1102
1143
 
@@ -12,4 +12,4 @@ export interface AgentAssessorInput {
12
12
  maxOutputBytes: number;
13
13
  config: PatchWardenConfig;
14
14
  }
15
- export declare function runAgentAssessment(input: AgentAssessorInput): AgentAssessmentSummary;
15
+ export declare function runAgentAssessment(input: AgentAssessorInput): Promise<AgentAssessmentSummary>;
@@ -4,7 +4,7 @@ import { buildAgentInvocation, buildAssessmentPrompt } from "../runner/agentInvo
4
4
  import { runSimpleProcessSync } from "../runner/simpleProcess.js";
5
5
  import { captureRepoSnapshot, compareSnapshots } from "../runner/changeCapture.js";
6
6
  const ASSESSMENT_MARKER = "===ASSESSMENT_JSON===";
7
- export function runAgentAssessment(input) {
7
+ export async function runAgentAssessment(input) {
8
8
  const logPaths = {
9
9
  stdout: resolve(input.assessmentDir, "agent-assessment-stdout.log"),
10
10
  stderr: resolve(input.assessmentDir, "agent-assessment-stderr.log"),
@@ -33,7 +33,7 @@ export function runAgentAssessment(input) {
33
33
  // ── 2. Before snapshot (repo-scoped) ──
34
34
  let repoBefore;
35
35
  try {
36
- repoBefore = captureRepoSnapshot(input.repoPath);
36
+ repoBefore = await captureRepoSnapshot(input.repoPath);
37
37
  }
38
38
  catch {
39
39
  // If we can't capture before snapshot, conservatively skip agent assessment
@@ -73,7 +73,7 @@ export function runAgentAssessment(input) {
73
73
  let readOnlyViolation = false;
74
74
  let violationFiles = [];
75
75
  try {
76
- const repoAfter = captureRepoSnapshot(input.repoPath);
76
+ const repoAfter = await captureRepoSnapshot(input.repoPath);
77
77
  const changes = compareSnapshots(repoBefore, repoAfter);
78
78
  if (changes.length > 0) {
79
79
  readOnlyViolation = true;
@@ -117,7 +117,7 @@ export declare function validateAssessmentFreshness(assessmentId: string, curren
117
117
  * This is intentionally not registered as an MCP tool: a remote client may
118
118
  * request confirmation, but it cannot grant confirmation to itself.
119
119
  */
120
- export declare function confirmAssessment(assessmentId: string): AssessmentConfirmationResult;
120
+ export declare function confirmAssessment(assessmentId: string): Promise<AssessmentConfirmationResult>;
121
121
  export declare function computeWorkspaceFingerprint(snapshot: RepoSnapshot): string;
122
122
  interface PolicyHashInput {
123
123
  change_policy: ChangePolicy;
@@ -156,7 +156,7 @@ export function validateAssessmentFreshness(assessmentId, currentSnapshot, optio
156
156
  * This is intentionally not registered as an MCP tool: a remote client may
157
157
  * request confirmation, but it cannot grant confirmation to itself.
158
158
  */
159
- export function confirmAssessment(assessmentId) {
159
+ export async function confirmAssessment(assessmentId) {
160
160
  if (!/^assessment_\d{8}_\d{6}_[0-9a-f]{32}$/.test(assessmentId)) {
161
161
  throw new PatchWardenError("assessment_id_invalid", "Local confirmation requires the full assessment_id (32 hexadecimal random characters).", "Copy the complete assessment_id from the assess_only response; assessment_short_id is display-only.");
162
162
  }
@@ -166,7 +166,7 @@ export function confirmAssessment(assessmentId) {
166
166
  ? "Fix the hard-rule finding and run assess_only again."
167
167
  : "This assessment does not require confirmation; execute it with the minimal assessment_id call.");
168
168
  }
169
- const snapshot = captureRepoSnapshot(assessment.resolved_repo_path);
169
+ const snapshot = await captureRepoSnapshot(assessment.resolved_repo_path);
170
170
  const validation = validateAssessmentFreshness(assessmentId, snapshot, {
171
171
  allow_unconfirmed: true,
172
172
  // A standalone CLI has no active MCP profile snapshot. The execute path
@@ -9,18 +9,19 @@
9
9
  import { loadConfig } from "../config.js";
10
10
  import { confirmAssessment } from "./assessmentStore.js";
11
11
  import { errorPayload } from "../errors.js";
12
+ import { logger } from "../logging.js";
12
13
  loadConfig();
13
14
  const assessmentId = process.argv[2];
14
15
  if (!assessmentId || process.argv.length !== 3) {
15
- console.error("Usage: patchwarden-confirm <full_assessment_id>");
16
- console.error("The display-only assessment_short_id is not accepted.");
16
+ logger.info("Usage: patchwarden-confirm <full_assessment_id>");
17
+ logger.info("The display-only assessment_short_id is not accepted.");
17
18
  process.exit(1);
18
19
  }
19
20
  try {
20
- const result = confirmAssessment(assessmentId);
21
+ const result = await confirmAssessment(assessmentId);
21
22
  console.log(JSON.stringify(result, null, 2));
22
23
  }
23
24
  catch (error) {
24
- console.error(JSON.stringify(errorPayload(error), null, 2));
25
+ logger.error("confirm_assessment_failed", { error: errorPayload(error) });
25
26
  process.exit(1);
26
27
  }
package/dist/config.d.ts CHANGED
@@ -17,6 +17,13 @@ export interface PatchWardenConfig {
17
17
  watcherStaleSeconds: number;
18
18
  toolProfile?: "full" | "chatgpt_core" | "chatgpt_direct" | "chatgpt_search";
19
19
  repoAliases?: Record<string, string>;
20
+ httpPort?: number;
21
+ http?: {
22
+ port?: number;
23
+ host?: string;
24
+ ownerTokenEnv?: string;
25
+ };
26
+ enableRunTaskTool?: boolean;
20
27
  enableAgentAssessment?: boolean;
21
28
  agentAssessmentTimeoutSeconds?: number;
22
29
  agentAssessmentMaxOutputBytes?: number;
package/dist/config.js CHANGED
@@ -182,6 +182,40 @@ function normalizeConfig(config) {
182
182
  config.toolProfile !== "chatgpt_search") {
183
183
  throw new Error('toolProfile must be "full", "chatgpt_core", "chatgpt_direct", or "chatgpt_search"');
184
184
  }
185
+ if (config.repoAliases !== undefined) {
186
+ if (typeof config.repoAliases !== "object" || config.repoAliases === null || Array.isArray(config.repoAliases)) {
187
+ throw new Error("repoAliases must be an object mapping alias names to repository paths");
188
+ }
189
+ for (const [alias, target] of Object.entries(config.repoAliases)) {
190
+ if (typeof target !== "string") {
191
+ throw new Error(`repoAliases["${alias}"] must be a string`);
192
+ }
193
+ }
194
+ }
195
+ if (config.httpPort !== undefined) {
196
+ if (typeof config.httpPort !== "number" || !Number.isInteger(config.httpPort) || config.httpPort < 1 || config.httpPort > 65535) {
197
+ throw new Error("httpPort must be an integer from 1 to 65535");
198
+ }
199
+ }
200
+ if (config.http !== undefined) {
201
+ if (typeof config.http !== "object" || config.http === null || Array.isArray(config.http)) {
202
+ throw new Error("http must be an object");
203
+ }
204
+ if (config.http.port !== undefined) {
205
+ if (typeof config.http.port !== "number" || !Number.isInteger(config.http.port) || config.http.port < 1 || config.http.port > 65535) {
206
+ throw new Error("http.port must be an integer from 1 to 65535");
207
+ }
208
+ }
209
+ if (config.http.host !== undefined && typeof config.http.host !== "string") {
210
+ throw new Error("http.host must be a string");
211
+ }
212
+ if (config.http.ownerTokenEnv !== undefined && typeof config.http.ownerTokenEnv !== "string") {
213
+ throw new Error("http.ownerTokenEnv must be a string");
214
+ }
215
+ }
216
+ if (config.enableRunTaskTool !== undefined && typeof config.enableRunTaskTool !== "boolean") {
217
+ throw new Error("enableRunTaskTool must be a boolean");
218
+ }
185
219
  if (config.enableAgentAssessment !== undefined && typeof config.enableAgentAssessment !== "boolean") {
186
220
  throw new Error("enableAgentAssessment must be a boolean");
187
221
  }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Control Center auth middleware — control token validation.
3
+ *
4
+ * POST routes are gated behind an in-memory control token generated at boot.
5
+ * The token is exposed read-only via GET /control-token.json (handled by the
6
+ * server router) so the local dashboard can read it, but every mutating
7
+ * action must pass through `checkControlToken` first.
8
+ */
9
+ import { type IncomingMessage } from "node:http";
10
+ export declare function checkControlToken(req: IncomingMessage): boolean;
@@ -0,0 +1,8 @@
1
+ import { controlToken } from "../shared.js";
2
+ export function checkControlToken(req) {
3
+ const header = req.headers["x-patchwarden-control-token"];
4
+ const provided = Array.isArray(header) ? header[0] : header;
5
+ if (typeof provided !== "string" || provided.length === 0)
6
+ return false;
7
+ return provided === controlToken;
8
+ }
@@ -0,0 +1,3 @@
1
+ import { type ServerResponse } from "node:http";
2
+ export declare function serveStatic(res: ServerResponse, urlPath: string): void;
3
+ export declare function serveFavicon(res: ServerResponse): void;
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Control Center static file middleware.
3
+ *
4
+ * Serves the bundled `ui/` directory (dashboard pages, partials, vendor
5
+ * assets, css) with path-traversal protection, plus the inline SVG favicon.
6
+ * Content types are mapped from `CONTENT_TYPES` in shared.ts.
7
+ */
8
+ import { existsSync, readFileSync, statSync } from "node:fs";
9
+ import { extname, isAbsolute, relative, resolve } from "node:path";
10
+ import { CONTENT_TYPES, CONTROL_CENTER_FAVICON, errorMessage, sendJson, uiRoot, } from "../shared.js";
11
+ export function serveStatic(res, urlPath) {
12
+ let candidate = "";
13
+ try {
14
+ const normalized = urlPath.startsWith("/") ? urlPath.slice(1) : urlPath;
15
+ let decoded;
16
+ try {
17
+ decoded = decodeURIComponent(normalized);
18
+ }
19
+ catch {
20
+ sendJson(res, 400, { error: "Invalid path encoding" });
21
+ return;
22
+ }
23
+ if (decoded.includes("\0")) {
24
+ sendJson(res, 400, { error: "Invalid path" });
25
+ return;
26
+ }
27
+ const segments = decoded.split("/").filter(Boolean);
28
+ if (segments.some((s) => s === "..")) {
29
+ sendJson(res, 403, { error: "Forbidden" });
30
+ return;
31
+ }
32
+ candidate = resolve(uiRoot, ...segments);
33
+ const rel = relative(uiRoot, candidate);
34
+ if (rel === "" || rel.startsWith("..") || isAbsolute(rel)) {
35
+ sendJson(res, 403, { error: "Forbidden" });
36
+ return;
37
+ }
38
+ }
39
+ catch (err) {
40
+ sendJson(res, 500, { error: errorMessage(err) });
41
+ return;
42
+ }
43
+ try {
44
+ if (!existsSync(candidate) || !statSync(candidate).isFile()) {
45
+ sendJson(res, 404, { error: "Not found" });
46
+ return;
47
+ }
48
+ const ext = extname(candidate).toLowerCase();
49
+ const contentType = CONTENT_TYPES[ext] || "application/octet-stream";
50
+ const content = readFileSync(candidate);
51
+ res.writeHead(200, { "Content-Type": contentType });
52
+ res.end(content);
53
+ }
54
+ catch (err) {
55
+ sendJson(res, 500, { error: errorMessage(err) });
56
+ }
57
+ }
58
+ export function serveFavicon(res) {
59
+ res.writeHead(200, {
60
+ "Content-Type": "image/svg+xml; charset=utf-8",
61
+ "Cache-Control": "public, max-age=86400",
62
+ "X-Content-Type-Options": "nosniff",
63
+ });
64
+ res.end(CONTROL_CENTER_FAVICON);
65
+ }
@@ -0,0 +1,15 @@
1
+ import { type ServerResponse } from "node:http";
2
+ export type LogCategory = "core" | "direct" | "watcher" | "control-center";
3
+ export declare function handleLogs(res: ServerResponse, category: LogCategory, tailLines: number): void;
4
+ export declare function handleAudit(res: ServerResponse): void;
5
+ /**
6
+ * Aggregate warnings from multiple sources:
7
+ * - audit.json warnings/fail_checks/possible_false_positives/manual_verification_required
8
+ * - stale task classification (heartbeat stale, collecting_artifacts stale, etc.)
9
+ * - task status=failed_verification
10
+ *
11
+ * Each warning type is grouped into a bucket with affected task IDs. The
12
+ * handler is fault-tolerant: any per-task read failure is skipped, and a
13
+ * top-level failure returns an empty warnings array (never 500).
14
+ */
15
+ export declare function handleWarnings(res: ServerResponse): void;