patchwarden 1.1.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 (258) hide show
  1. package/README.en.md +83 -8
  2. package/README.md +78 -10
  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 -2001
  42. package/dist/direct/directGuards.js +30 -8
  43. package/dist/direct/directSessionStore.d.ts +2 -0
  44. package/dist/direct/directVerification.js +7 -0
  45. package/dist/doctor.d.ts +18 -1
  46. package/dist/doctor.js +579 -348
  47. package/dist/goal/goalReport.d.ts +54 -0
  48. package/dist/goal/goalReport.js +204 -0
  49. package/dist/goal/goalStatus.d.ts +6 -0
  50. package/dist/goal/specKitImport.d.ts +63 -0
  51. package/dist/goal/specKitImport.js +220 -0
  52. package/dist/goal/subgoalSync.js +2 -1
  53. package/dist/httpServer.js +15 -12
  54. package/dist/index.js +7 -4
  55. package/dist/logging.d.ts +7 -1
  56. package/dist/logging.js +8 -0
  57. package/dist/policy/projectPolicy.d.ts +55 -0
  58. package/dist/policy/projectPolicy.js +286 -0
  59. package/dist/runner/changeCapture.d.ts +3 -3
  60. package/dist/runner/changeCapture.js +63 -39
  61. package/dist/runner/cli.js +7 -6
  62. package/dist/runner/postTaskCleanup.js +26 -2
  63. package/dist/runner/runTask.js +245 -221
  64. package/dist/runner/simpleProcess.js +4 -4
  65. package/dist/runner/watch.js +17 -14
  66. package/dist/security/contentRedaction.d.ts +6 -0
  67. package/dist/security/contentRedaction.js +22 -0
  68. package/dist/smoke-test.js +261 -255
  69. package/dist/test/unit/apply-patch.test.d.ts +1 -0
  70. package/dist/test/unit/apply-patch.test.js +225 -0
  71. package/dist/test/unit/create-task.test.d.ts +1 -0
  72. package/dist/test/unit/create-task.test.js +197 -0
  73. package/dist/test/unit/direct-guards.test.js +124 -9
  74. package/dist/test/unit/evidence-pack.test.d.ts +1 -0
  75. package/dist/test/unit/evidence-pack.test.js +224 -0
  76. package/dist/test/unit/get-task-status.test.d.ts +1 -0
  77. package/dist/test/unit/get-task-status.test.js +174 -0
  78. package/dist/test/unit/get-task-summary.test.d.ts +1 -0
  79. package/dist/test/unit/get-task-summary.test.js +146 -0
  80. package/dist/test/unit/goal-report.test.d.ts +1 -0
  81. package/dist/test/unit/goal-report.test.js +159 -0
  82. package/dist/test/unit/goal-subgoal-task.test.js +6 -6
  83. package/dist/test/unit/goal-tools-registry.test.js +6 -4
  84. package/dist/test/unit/path-guard.test.js +24 -0
  85. package/dist/test/unit/project-policy-release-mode.test.d.ts +1 -0
  86. package/dist/test/unit/project-policy-release-mode.test.js +125 -0
  87. package/dist/test/unit/run-task-loop.test.d.ts +1 -0
  88. package/dist/test/unit/run-task-loop.test.js +380 -0
  89. package/dist/test/unit/schema-drift-check.test.js +10 -9
  90. package/dist/test/unit/spec-kit-import.test.d.ts +1 -0
  91. package/dist/test/unit/spec-kit-import.test.js +341 -0
  92. package/dist/test/unit/wait-for-task.test.d.ts +1 -0
  93. package/dist/test/unit/wait-for-task.test.js +144 -0
  94. package/dist/tools/auditTask.d.ts +8 -63
  95. package/dist/tools/auditTask.js +12 -8
  96. package/dist/tools/createDirectSession.d.ts +1 -1
  97. package/dist/tools/createDirectSession.js +2 -2
  98. package/dist/tools/createTask.d.ts +2 -2
  99. package/dist/tools/createTask.js +4 -4
  100. package/dist/tools/dispatch/coreDispatch.d.ts +9 -0
  101. package/dist/tools/dispatch/coreDispatch.js +282 -0
  102. package/dist/tools/dispatch/diagnosticDispatch.d.ts +14 -0
  103. package/dist/tools/dispatch/diagnosticDispatch.js +78 -0
  104. package/dist/tools/dispatch/directDispatch.d.ts +8 -0
  105. package/dist/tools/dispatch/directDispatch.js +115 -0
  106. package/dist/tools/dispatch/goalDispatch.d.ts +8 -0
  107. package/dist/tools/dispatch/goalDispatch.js +91 -0
  108. package/dist/tools/dispatch/releaseDispatch.d.ts +8 -0
  109. package/dist/tools/dispatch/releaseDispatch.js +45 -0
  110. package/dist/tools/dispatch/types.d.ts +23 -0
  111. package/dist/tools/dispatch/types.js +15 -0
  112. package/dist/tools/evidencePack.d.ts +45 -0
  113. package/dist/tools/evidencePack.js +375 -0
  114. package/dist/tools/finalizeDirectSession.d.ts +1 -1
  115. package/dist/tools/finalizeDirectSession.js +3 -3
  116. package/dist/tools/goalSubgoalTask.d.ts +1 -1
  117. package/dist/tools/goalSubgoalTask.js +2 -2
  118. package/dist/tools/healthCheck.js +3 -3
  119. package/dist/tools/recommendAgentForTask.d.ts +19 -0
  120. package/dist/tools/recommendAgentForTask.js +56 -0
  121. package/dist/tools/registry.d.ts +3 -3
  122. package/dist/tools/registry.js +333 -402
  123. package/dist/tools/releaseMode.d.ts +50 -0
  124. package/dist/tools/releaseMode.js +370 -0
  125. package/dist/tools/retryTask.d.ts +2 -2
  126. package/dist/tools/retryTask.js +2 -2
  127. package/dist/tools/runDirectVerificationBundle.d.ts +26 -0
  128. package/dist/tools/runDirectVerificationBundle.js +64 -0
  129. package/dist/tools/runTaskLoop.d.ts +57 -0
  130. package/dist/tools/runTaskLoop.js +417 -0
  131. package/dist/tools/runVerification.d.ts +4 -0
  132. package/dist/tools/runVerification.js +4 -0
  133. package/dist/tools/safeViews.d.ts +8 -2
  134. package/dist/tools/safeViews.js +4 -2
  135. package/dist/tools/taskLineage.d.ts +91 -0
  136. package/dist/tools/taskLineage.js +175 -0
  137. package/dist/tools/toolCatalog.d.ts +2 -2
  138. package/dist/tools/toolCatalog.js +6 -0
  139. package/dist/tools/toolRegistry.js +132 -0
  140. package/dist/version.d.ts +2 -2
  141. package/dist/version.js +2 -2
  142. package/docs/CODE_WIKI.md +983 -0
  143. package/docs/agentseal-integration.md +150 -0
  144. package/docs/architecture.md +63 -0
  145. package/docs/assets/patchwarden-oss-demo.gif +0 -0
  146. package/docs/chatgpt-usage.md +31 -0
  147. package/docs/control-center/README.md +9 -0
  148. package/docs/dashboard-overview.md +86 -0
  149. package/docs/demo.md +8 -0
  150. package/docs/direct-session-workflow.md +98 -0
  151. package/docs/evidence-pack-schema.md +215 -0
  152. package/docs/execution-plan-2026-07-09.md +315 -0
  153. package/docs/lineage-evidence-pack-workflow.md +127 -0
  154. package/docs/mcp-inspector-testing.md +200 -0
  155. package/docs/open-source-application.md +162 -0
  156. package/docs/opencode-worker.md +151 -0
  157. package/docs/openhands-worker.md +181 -0
  158. package/docs/release-evidence.md +72 -0
  159. package/docs/roadmap-execution-and-acceptance.md +365 -0
  160. package/docs/spec-kit-integration.md +131 -0
  161. package/docs/task-safe-review-workflow.md +98 -0
  162. package/docs/threat-model.md +79 -0
  163. package/docs/user-feedback.md +40 -0
  164. package/docs/why-patchwarden.md +110 -0
  165. package/package.json +3 -3
  166. package/scripts/checks/control-center-smoke.js +443 -0
  167. package/scripts/checks/control-smoke.js +2 -2
  168. package/scripts/checks/lifecycle-smoke.js +29 -23
  169. package/scripts/checks/mcp-manifest-check.js +12 -0
  170. package/scripts/checks/mcp-smoke.js +33 -7
  171. package/scripts/checks/watcher-supervisor-smoke.js +1 -1
  172. package/scripts/generate-demo-gif.py +320 -0
  173. package/src/assessments/agentAssessor.ts +3 -3
  174. package/src/assessments/assessmentStore.ts +2 -2
  175. package/src/assessments/confirmCli.ts +5 -4
  176. package/src/config.ts +37 -0
  177. package/src/control/middleware/auth.ts +17 -0
  178. package/src/control/middleware/static.ts +71 -0
  179. package/src/control/routes/audit.ts +321 -0
  180. package/src/control/routes/evidence.ts +107 -0
  181. package/src/control/routes/lineage.ts +92 -0
  182. package/src/control/routes/policy.ts +81 -0
  183. package/src/control/routes/process.ts +204 -0
  184. package/src/control/routes/sessions.ts +251 -0
  185. package/src/control/routes/status.ts +325 -0
  186. package/src/control/routes/taskActions.ts +248 -0
  187. package/src/control/routes/tasks.ts +323 -0
  188. package/src/control/routes/workspace.ts +203 -0
  189. package/src/control/runtime.ts +472 -0
  190. package/src/control/server.ts +471 -0
  191. package/src/control/shared.ts +294 -0
  192. package/src/controlCenter.ts +7 -2150
  193. package/src/direct/directGuards.ts +28 -7
  194. package/src/direct/directSessionStore.ts +2 -0
  195. package/src/direct/directVerification.ts +7 -0
  196. package/src/doctor.ts +741 -481
  197. package/src/goal/goalReport.ts +271 -0
  198. package/src/goal/goalStatus.ts +6 -0
  199. package/src/goal/specKitImport.ts +355 -0
  200. package/src/goal/subgoalSync.ts +4 -2
  201. package/src/httpServer.ts +17 -14
  202. package/src/index.ts +7 -4
  203. package/src/logging.ts +10 -1
  204. package/src/policy/projectPolicy.ts +344 -0
  205. package/src/runner/changeCapture.ts +70 -42
  206. package/src/runner/cli.ts +7 -6
  207. package/src/runner/postTaskCleanup.ts +26 -2
  208. package/src/runner/runTask.ts +325 -223
  209. package/src/runner/simpleProcess.ts +4 -4
  210. package/src/runner/watch.ts +17 -14
  211. package/src/security/contentRedaction.ts +29 -0
  212. package/src/smoke-test.ts +254 -252
  213. package/src/test/unit/apply-patch.test.ts +293 -0
  214. package/src/test/unit/create-task.test.ts +255 -0
  215. package/src/test/unit/direct-guards.test.ts +178 -8
  216. package/src/test/unit/evidence-pack.test.ts +251 -0
  217. package/src/test/unit/get-task-status.test.ts +203 -0
  218. package/src/test/unit/get-task-summary.test.ts +173 -0
  219. package/src/test/unit/goal-report.test.ts +189 -0
  220. package/src/test/unit/goal-subgoal-task.test.ts +6 -6
  221. package/src/test/unit/goal-tools-registry.test.ts +7 -5
  222. package/src/test/unit/path-guard.test.ts +30 -0
  223. package/src/test/unit/project-policy-release-mode.test.ts +156 -0
  224. package/src/test/unit/run-task-loop.test.ts +425 -0
  225. package/src/test/unit/schema-drift-check.test.ts +11 -9
  226. package/src/test/unit/spec-kit-import.test.ts +429 -0
  227. package/src/test/unit/wait-for-task.test.ts +176 -0
  228. package/src/tools/auditTask.ts +99 -59
  229. package/src/tools/createDirectSession.ts +3 -3
  230. package/src/tools/createTask.ts +7 -7
  231. package/src/tools/dispatch/coreDispatch.ts +374 -0
  232. package/src/tools/dispatch/diagnosticDispatch.ts +101 -0
  233. package/src/tools/dispatch/directDispatch.ts +167 -0
  234. package/src/tools/dispatch/goalDispatch.ts +127 -0
  235. package/src/tools/dispatch/releaseDispatch.ts +65 -0
  236. package/src/tools/dispatch/types.ts +24 -0
  237. package/src/tools/evidencePack.ts +490 -0
  238. package/src/tools/finalizeDirectSession.ts +4 -4
  239. package/src/tools/goalSubgoalTask.ts +2 -2
  240. package/src/tools/healthCheck.ts +3 -3
  241. package/src/tools/listWorkspace.ts +71 -71
  242. package/src/tools/recommendAgentForTask.ts +79 -0
  243. package/src/tools/registry.ts +352 -509
  244. package/src/tools/releaseMode.ts +450 -0
  245. package/src/tools/retryTask.ts +2 -2
  246. package/src/tools/runDirectVerificationBundle.ts +98 -0
  247. package/src/tools/runTaskLoop.ts +526 -0
  248. package/src/tools/runVerification.ts +8 -0
  249. package/src/tools/safeViews.ts +4 -2
  250. package/src/tools/taskLineage.ts +300 -0
  251. package/src/tools/toolCatalog.ts +6 -0
  252. package/src/tools/toolRegistry.ts +132 -0
  253. package/src/version.ts +2 -2
  254. package/ui/pages/audit.html +192 -3
  255. package/ui/pages/dashboard.html +1070 -14
  256. package/ui/pages/direct-sessions.html +505 -53
  257. package/ui/pages/task-detail.html +456 -438
  258. 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.1.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)
@@ -493,7 +494,7 @@ PatchWarden.cmd start core
493
494
  The launcher:
494
495
 
495
496
  - Builds `dist/index.js` if it is missing.
496
- - Verifies v1.1.0, the fixed 21-tool `chatgpt_core` catalog, and its schema
497
+ - Verifies v1.5.0, the fixed 26-tool `chatgpt_core` catalog, and its schema
497
498
  manifest.
498
499
  - Reads or prompts for the Tunnel ID.
499
500
  - Reads or prompts for the runtime API key.
@@ -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
@@ -930,10 +956,12 @@ names.
930
956
 
931
957
  ## MCP tools and profiles
932
958
 
933
- `chatgpt_core` is the fixed 21-tool profile used by the ChatGPT tunnel:
959
+ `chatgpt_core` is the fixed 26-tool profile used by the ChatGPT tunnel:
934
960
 
935
961
  `health_check`, `list_agents`, `list_workspace`,
936
- `read_workspace_file`, `save_plan`, `create_task`,
962
+ `read_workspace_file`, `save_plan`, `create_task`, `run_task_loop`,
963
+ `recommend_agent_for_task`, `get_task_lineage`, `export_task_evidence_pack`,
964
+ `get_project_policy`,
937
965
  `wait_for_task`, `get_task_summary`, `get_diff`, `get_result`,
938
966
  `get_result_json`, `get_test_log`, `get_task_status`, `list_tasks`,
939
967
  `cancel_task`, `audit_task`, `safe_status`, `safe_result`, `safe_audit`, `safe_test_summary`, and `safe_diff_summary`.
@@ -942,6 +970,38 @@ names.
942
970
  ChatGPT should request `view: "compact"` first; terminal `wait_for_task`
943
971
  responses also embed compact acceptance evidence only.
944
972
 
973
+ `run_task_loop` is the v1.2 safe orchestration entrypoint. It only composes
974
+ existing `create_task`, `wait_for_task`, safe summary, and `audit_task`
975
+ behavior; it does not bypass the Watcher, command allowlist, workspace
976
+ confinement, or local confirmation boundaries. `get_task_lineage` reads the
977
+ bounded `.patchwarden/lineages/<lineage_id>/` summary without returning full
978
+ logs or diffs.
979
+
980
+ `get_project_policy` is the v1.3 read-only policy entrypoint. It returns the
981
+ bounded effective `.patchwarden/project-policy.json` policy and release
982
+ readiness summary without expanding command permissions. The v1.3 release mode
983
+ tools, `release_check`, `release_prepare`, `release_verify`, and
984
+ `release_cleanup`, are full-profile only and never perform publish, push, tag,
985
+ or GitHub Release writes. The Control Center dashboard also shows bounded
986
+ lineage, policy, and release status summaries without full logs, diffs, or
987
+ secret-bearing content.
988
+
989
+ v1.4 adds Direct-assisted loop verification. `run_task_loop` can opt into
990
+ `direct_verify=true`; after the guarded task and audit succeed, PatchWarden
991
+ creates a Direct session, runs only allowlisted verification commands, safe
992
+ finalizes, safe audits, and records bounded Direct evidence in lineage. It does
993
+ not call Direct patching tools, publish, push, tag, create releases, restart
994
+ watchers, or return full stdout/stderr/diffs.
995
+
996
+ v1.5 adds worktree-assisted loop isolation, bounded agent routing, and evidence
997
+ pack export. `run_task_loop(isolation_mode="worktree")` creates an isolated git
998
+ worktree for the task and records the worktree id, path, branch, and next action
999
+ in lineage. It does not auto-merge or auto-delete the worktree. `agent="auto"`
1000
+ uses `recommend_agent_for_task` to select a configured agent before task
1001
+ creation. `export_task_evidence_pack` writes bounded `evidence.json` and
1002
+ `EVIDENCE.md` files under `.patchwarden/evidence-packs/<lineage_id>/` without
1003
+ stdout/stderr tails, full diffs, verification logs, or sensitive file content.
1004
+
945
1005
  `full` additionally provides:
946
1006
 
947
1007
  - `get_plan`
@@ -956,9 +1016,9 @@ to `full`.
956
1016
 
957
1017
  ### ChatGPT Direct mode
958
1018
 
959
- Direct mode exposes thirteen guarded tools so ChatGPT can create an editing
1019
+ Direct mode exposes fourteen guarded tools so ChatGPT can create an editing
960
1020
  session, read and search source files, apply hash-bound JSON patches, run
961
- exactly allowlisted verification commands, finalize the evidence, and audit
1021
+ exactly allowlisted verification commands or a bounded verification bundle, finalize the evidence, and audit
962
1022
  the result without a local execution agent.
963
1023
 
964
1024
  Enable it in the trusted local configuration while keeping the ordinary Core
@@ -980,9 +1040,20 @@ On first use, provide the `tunnel-client.exe` path and a Tunnel ID dedicated
980
1040
  to the Direct Connector. The launcher uses the `patchwarden-direct` profile,
981
1041
  stores runtime state under `%LOCALAPPDATA%\patchwarden\runtime-direct`, skips
982
1042
  the Watcher, and retains the existing DPAPI credential handling. In a fresh
983
- ChatGPT conversation, `health_check` should report `chatgpt_direct`, thirteen
1043
+ ChatGPT conversation, `health_check` should report `chatgpt_direct`, fourteen
984
1044
  tools, and `direct_profile_enabled=true`.
985
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
+
986
1057
  ## Security boundaries and local data
987
1058
 
988
1059
  Primary protections:
@@ -1088,6 +1159,10 @@ and release-asset checksums independently.
1088
1159
  - [ChatGPT usage guide](docs/chatgpt-usage.md)
1089
1160
  - [Migration guide](docs/migration-from-safe-bifrost.md)
1090
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)
1091
1166
  - [OpenAI tunnel examples](examples/openai-tunnel/README.md)
1092
1167
  - [ChatGPT test prompt](examples/openai-tunnel/chatgpt-test-prompt.md)
1093
1168
 
@@ -1103,7 +1178,7 @@ and release-asset checksums independently.
1103
1178
  - [x] Release Gate (five-stage pre-release verification)
1104
1179
  - [x] Worktree isolation
1105
1180
  - [x] Multi-agent routing
1106
- - [ ] Local dashboard
1181
+ - [x] Local dashboard
1107
1182
 
1108
1183
  ## License
1109
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.1.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
  - [开发与发布验证](#开发与发布验证)
@@ -399,7 +400,7 @@ PatchWarden.cmd start core
399
400
  启动器会:
400
401
 
401
402
  - 检查 `dist/index.js`,缺失时自动构建。
402
- - 校验 `chatgpt_core` 的版本、21 个核心工具和 Schema Manifest。
403
+ - 校验 `chatgpt_core` 的版本、26 个核心工具和 Schema Manifest。
403
404
  - 读取或提示输入 Tunnel ID。
404
405
  - 读取或提示输入运行时 API Key。
405
406
  - 使用 Windows DPAPI 保存凭据到 `%APPDATA%\patchwarden`。
@@ -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
  ### 快速对照表
@@ -836,10 +862,11 @@ PatchWarden v0.4.0 不自动读取旧 CLI、旧环境变量、旧 Header、旧
836
862
 
837
863
  ## MCP 工具与 Profile
838
864
 
839
- `chatgpt_core` 是固定的 21 工具 Profile,适合 ChatGPT Tunnel:
865
+ `chatgpt_core` 是固定的 26 工具 Profile,适合 ChatGPT Tunnel:
840
866
 
841
867
  `health_check`、`list_agents`、`list_workspace`、
842
- `read_workspace_file`、`save_plan`、`create_task`、
868
+ `read_workspace_file`、`save_plan`、`create_task`、`run_task_loop`、`recommend_agent_for_task`、
869
+ `get_task_lineage`、`export_task_evidence_pack`、`get_project_policy`、
843
870
  `wait_for_task`、`get_task_summary`、`get_diff`、`get_result`、
844
871
  `get_result_json`、`get_test_log`、`get_task_status`、`list_tasks`、
845
872
  `cancel_task`、`audit_task`、`safe_status`、`safe_result`、`safe_audit`、`safe_test_summary`、`safe_diff_summary`。
@@ -847,7 +874,33 @@ PatchWarden v0.4.0 不自动读取旧 CLI、旧环境变量、旧 Header、旧
847
874
  `get_task_summary` 默认保留兼容的 `standard` 视图;ChatGPT 应优先使用
848
875
  `view: "compact"`,终态 `wait_for_task` 也只内嵌 compact 验收证据。
849
876
 
850
- `full` 提供完整 54 工具本地开发目录,包含核心工具、管理工具和 Direct
877
+ `run_task_loop` v1.2 的安全编排入口:它只组合现有 `create_task`、`wait_for_task`、safe summary 和
878
+ `audit_task`,不会绕过 Watcher、命令白名单、workspace confinement 或确认边界。`get_task_lineage`
879
+ 读取 `.patchwarden/lineages/<lineage_id>/` 中的有界链路摘要,不返回完整日志或 diff。
880
+
881
+ v1.4 adds Direct-assisted loop verification. `run_task_loop(direct_verify=true)`
882
+ will create a Direct session only after the watcher-driven task and normal audit
883
+ succeed, run allowlisted Direct verification commands, safe-finalize, safe-audit,
884
+ and write bounded Direct evidence into lineage. It does not call Direct patching
885
+ tools, publish, push, tag, create releases, restart live services, or return full
886
+ stdout/stderr/diffs.
887
+
888
+ v1.5 adds worktree-assisted loop isolation, bounded agent routing, and evidence
889
+ pack export. `run_task_loop(isolation_mode="worktree")` creates an isolated git
890
+ worktree for the task and records the worktree id, path, branch, and next action
891
+ in lineage. It does not auto-merge or auto-delete the worktree. `agent="auto"`
892
+ uses `recommend_agent_for_task` to select a configured agent before task
893
+ creation. `export_task_evidence_pack` writes bounded `evidence.json` and
894
+ `EVIDENCE.md` files under `.patchwarden/evidence-packs/<lineage_id>/` without
895
+ stdout/stderr tails, full diffs, verification logs, or sensitive file content.
896
+
897
+ `get_project_policy` 是 v1.3 的只读策略入口,返回 `.patchwarden/project-policy.json`
898
+ 的有界 effective policy 与 release readiness,不会扩大命令白名单。v1.3 的
899
+ `release_check`、`release_prepare`、`release_verify`、`release_cleanup` 仅在 `full`
900
+ Profile 中可见,不执行 publish、push、tag 或 GitHub Release 写操作。Control Center
901
+ Dashboard 会展示 lineage、policy 和 release status 的有界摘要,不返回完整日志、diff 或密钥内容。
902
+
903
+ `full` 提供完整 64 工具本地开发目录,包含核心工具、管理工具和 Direct
851
904
  工具。除 `chatgpt_core` 外,常用额外管理工具包括:
852
905
 
853
906
  - `get_plan`
@@ -857,15 +910,15 @@ PatchWarden v0.4.0 不自动读取旧 CLI、旧环境变量、旧 Header、旧
857
910
  - `get_task_stdout_tail`
858
911
  - `get_task_log_tail`
859
912
 
860
- `chatgpt_direct` 是 v0.6.0 新增的 Direct 直接开发 Profile,v1.1 包含 13 个工具:
913
+ `chatgpt_direct` 是 v0.6.0 新增的 Direct 直接开发 Profile,v1.4 包含 14 个工具:
861
914
 
862
915
  `health_check`、`list_workspace`、`create_direct_session`、
863
916
  `search_workspace`、`read_workspace_file`、`apply_patch`、
864
- `run_verification`、`finalize_direct_session`、`audit_session`、`safe_direct_summary`、`safe_finalize_direct_session`、`safe_audit_direct_session`、`sync_file`。
917
+ `run_verification`、`run_direct_verification_bundle`、`finalize_direct_session`、`audit_session`、`safe_direct_summary`、`safe_finalize_direct_session`、`safe_audit_direct_session`、`sync_file`。
865
918
 
866
919
  `chatgpt_direct` 默认关闭,需要通过 `enableDirectProfile: true` 或
867
920
  `PATCHWARDEN_TOOL_PROFILE=chatgpt_direct` 显式启用。`chatgpt_core` 保持
868
- 21 工具清单。
921
+ 26 工具清单。
869
922
 
870
923
  Tunnel 包装脚本会强制使用 `chatgpt_core`;普通本地开发默认使用 `full`。
871
924
 
@@ -920,7 +973,7 @@ Tunnel ID。启动器使用 `patchwarden-direct` Profile、独立的
920
973
  Tunnel API Key 仍通过现有 Windows DPAPI 缓存处理,不会写入仓库。
921
974
 
922
975
  连接 ChatGPT 后新建对话并先调用 `health_check`。预期
923
- `tool_profile=chatgpt_direct`、`tool_count=13`、
976
+ `tool_profile=chatgpt_direct`、`tool_count=14`、
924
977
  `direct_profile_enabled=true`。已有对话可能缓存旧工具清单,需要重新连接
925
978
  Connector 后再新建对话。
926
979
 
@@ -950,6 +1003,17 @@ Direct 模式在受控 session 内执行,具有以下硬性限制:
950
1003
 
951
1004
  Direct 模式不支持:shell、delete、rename、commit、push、publish、deployment。
952
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
+
953
1017
  ## 安全边界与本地数据
954
1018
 
955
1019
  PatchWarden 的主要保护:
@@ -1054,6 +1118,10 @@ Release 和发布资产校验值。
1054
1118
  - [ChatGPT 调用规范](docs/chatgpt-usage.md)
1055
1119
  - [旧版本迁移指南](docs/migration-from-safe-bifrost.md)
1056
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)
1057
1125
  - [OpenAI Tunnel 示例](examples/openai-tunnel/README.md)
1058
1126
  - [ChatGPT 测试提示词](examples/openai-tunnel/chatgpt-test-prompt.md)
1059
1127
 
@@ -1069,7 +1137,7 @@ Release 和发布资产校验值。
1069
1137
  - [x] Release Gate(发布前五阶段校验)
1070
1138
  - [x] Worktree 隔离
1071
1139
  - [x] 多 Agent 路由
1072
- - [ ] 本地 Dashboard
1140
+ - [x] 本地 Dashboard
1073
1141
 
1074
1142
  ## License
1075
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;