patchwarden 1.5.1 → 1.6.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 (573) hide show
  1. package/PatchWarden.cmd +32 -2
  2. package/README.en.md +152 -25
  3. package/README.md +120 -23
  4. package/dist/assessments/agentAssessor.js +21 -12
  5. package/dist/assessments/assessmentStore.js +42 -34
  6. package/dist/config.d.ts +15 -0
  7. package/dist/config.js +66 -13
  8. package/dist/control/fileManager.d.ts +2 -0
  9. package/dist/control/fileManager.js +39 -0
  10. package/dist/control/middleware/auth.d.ts +6 -0
  11. package/dist/control/middleware/auth.js +14 -5
  12. package/dist/control/middleware/static.js +16 -3
  13. package/dist/control/routeTable.d.ts +24 -0
  14. package/dist/control/routeTable.js +263 -0
  15. package/dist/control/routes/audit.js +17 -7
  16. package/dist/control/routes/evidence.js +1 -1
  17. package/dist/control/routes/lineage.js +1 -1
  18. package/dist/control/routes/process.d.ts +7 -0
  19. package/dist/control/routes/process.js +329 -42
  20. package/dist/control/routes/sessions.d.ts +9 -1
  21. package/dist/control/routes/sessions.js +84 -21
  22. package/dist/control/routes/status.d.ts +46 -1
  23. package/dist/control/routes/status.js +72 -15
  24. package/dist/control/routes/taskActions.js +24 -60
  25. package/dist/control/routes/tasks.d.ts +4 -0
  26. package/dist/control/routes/tasks.js +61 -62
  27. package/dist/control/routes/workspace.js +50 -23
  28. package/dist/control/runtime.d.ts +3 -2
  29. package/dist/control/runtime.js +63 -30
  30. package/dist/control/server.js +36 -365
  31. package/dist/control/shared.d.ts +25 -1
  32. package/dist/control/shared.js +64 -11
  33. package/dist/direct/directAudit.js +234 -225
  34. package/dist/direct/directGuards.d.ts +2 -1
  35. package/dist/direct/directGuards.js +42 -29
  36. package/dist/direct/directPatch.d.ts +6 -1
  37. package/dist/direct/directPatch.js +55 -8
  38. package/dist/direct/directSessionStore.d.ts +16 -6
  39. package/dist/direct/directSessionStore.js +127 -35
  40. package/dist/direct/directVerification.js +8 -16
  41. package/dist/doctor.js +62 -52
  42. package/dist/goal/acceptanceEngine.d.ts +1 -1
  43. package/dist/goal/goalProgress.js +75 -69
  44. package/dist/goal/goalReport.js +5 -12
  45. package/dist/goal/goalStatus.d.ts +2 -0
  46. package/dist/goal/goalStatus.js +10 -3
  47. package/dist/goal/goalStore.d.ts +9 -2
  48. package/dist/goal/goalStore.js +152 -45
  49. package/dist/goal/handoffExport.js +6 -6
  50. package/dist/goal/specKitImport.js +97 -96
  51. package/dist/goal/subgoalSync.js +11 -9
  52. package/dist/goal/worktreeManager.d.ts +2 -0
  53. package/dist/goal/worktreeManager.js +209 -136
  54. package/dist/httpServer.js +111 -32
  55. package/dist/logging.js +5 -3
  56. package/dist/policy/projectPolicy.js +5 -2
  57. package/dist/release/releaseGate.d.ts +1 -1
  58. package/dist/release/releaseGate.js +78 -25
  59. package/dist/runner/agentInvocation.d.ts +14 -0
  60. package/dist/runner/agentInvocation.js +96 -4
  61. package/dist/runner/changeCapture.d.ts +13 -5
  62. package/dist/runner/changeCapture.js +243 -61
  63. package/dist/runner/postTaskCleanup.js +48 -19
  64. package/dist/runner/processSecurity.d.ts +56 -0
  65. package/dist/runner/processSecurity.js +373 -0
  66. package/dist/runner/runTask.d.ts +1 -1
  67. package/dist/runner/runTask.js +115 -87
  68. package/dist/runner/simpleProcess.d.ts +3 -0
  69. package/dist/runner/simpleProcess.js +50 -36
  70. package/dist/{taskProgress.d.ts → runner/taskProgress.d.ts} +1 -1
  71. package/dist/{taskProgress.js → runner/taskProgress.js} +2 -2
  72. package/dist/{taskRuntime.d.ts → runner/taskRuntime.d.ts} +1 -1
  73. package/dist/{taskRuntime.js → runner/taskRuntime.js} +3 -2
  74. package/dist/runner/taskStatusStore.d.ts +11 -0
  75. package/dist/runner/taskStatusStore.js +24 -0
  76. package/dist/runner/watch.d.ts +14 -1
  77. package/dist/runner/watch.js +256 -48
  78. package/dist/security/contentRedaction.js +2 -2
  79. package/dist/security/discoveryTokenStore.d.ts +3 -2
  80. package/dist/security/discoveryTokenStore.js +17 -2
  81. package/dist/security/loopbackHost.d.ts +2 -0
  82. package/dist/security/loopbackHost.js +16 -0
  83. package/dist/security/pathGuard.js +32 -17
  84. package/dist/security/planGuard.js +2 -1
  85. package/dist/security/runtimeGuard.js +9 -6
  86. package/dist/security/secretComparison.d.ts +3 -0
  87. package/dist/security/secretComparison.js +11 -0
  88. package/dist/security/sensitiveGuard.d.ts +5 -0
  89. package/dist/security/sensitiveGuard.js +27 -8
  90. package/dist/security/toolInvocationGuard.d.ts +2 -2
  91. package/dist/security/toolInvocationGuard.js +1 -1
  92. package/dist/security/workspaceRootGuard.d.ts +8 -0
  93. package/dist/security/workspaceRootGuard.js +47 -0
  94. package/dist/smoke-test.js +24 -23
  95. package/dist/tools/catalog/index.d.ts +4 -0
  96. package/dist/tools/catalog/index.js +4 -0
  97. package/dist/tools/{toolCatalog.js → catalog/toolCatalog.js} +3 -12
  98. package/dist/tools/{toolRegistry.d.ts → catalog/toolRegistry.d.ts} +3 -3
  99. package/dist/tools/{toolRegistry.js → catalog/toolRegistry.js} +6 -15
  100. package/dist/tools/definitions/toolDefs.d.ts +18 -0
  101. package/dist/tools/definitions/toolDefs.js +1177 -0
  102. package/dist/tools/{auditSession.d.ts → diagnostics/auditSession.d.ts} +1 -1
  103. package/dist/tools/{auditSession.js → diagnostics/auditSession.js} +2 -2
  104. package/dist/tools/{auditTask.d.ts → diagnostics/auditTask.d.ts} +1 -1
  105. package/dist/tools/{auditTask.js → diagnostics/auditTask.js} +99 -29
  106. package/dist/tools/{healthCheck.d.ts → diagnostics/healthCheck.d.ts} +6 -6
  107. package/dist/tools/{healthCheck.js → diagnostics/healthCheck.js} +7 -7
  108. package/dist/tools/diagnostics/index.d.ts +6 -0
  109. package/dist/tools/diagnostics/index.js +6 -0
  110. package/dist/tools/{safeStatus.d.ts → diagnostics/safeStatus.d.ts} +3 -3
  111. package/dist/tools/{safeStatus.js → diagnostics/safeStatus.js} +6 -6
  112. package/dist/tools/{safeViews.d.ts → diagnostics/safeViews.d.ts} +12 -8
  113. package/dist/tools/{safeViews.js → diagnostics/safeViews.js} +23 -13
  114. package/dist/tools/{schemaDriftCheck.d.ts → diagnostics/schemaDriftCheck.d.ts} +1 -1
  115. package/dist/tools/{schemaDriftCheck.js → diagnostics/schemaDriftCheck.js} +2 -2
  116. package/dist/tools/{createDirectSession.js → direct/createDirectSession.js} +6 -6
  117. package/dist/tools/{finalizeDirectSession.d.ts → direct/finalizeDirectSession.d.ts} +1 -1
  118. package/dist/tools/direct/finalizeDirectSession.js +86 -0
  119. package/dist/tools/direct/index.d.ts +3 -0
  120. package/dist/tools/direct/index.js +3 -0
  121. package/dist/tools/{runDirectVerificationBundle.js → direct/runDirectVerificationBundle.js} +2 -2
  122. package/dist/tools/{discoverTools.d.ts → discovery/discoverTools.d.ts} +2 -2
  123. package/dist/tools/{discoverTools.js → discovery/discoverTools.js} +5 -5
  124. package/dist/tools/{explainTool.d.ts → discovery/explainTool.d.ts} +2 -2
  125. package/dist/tools/{explainTool.js → discovery/explainTool.js} +2 -2
  126. package/dist/tools/discovery/index.d.ts +3 -0
  127. package/dist/tools/discovery/index.js +3 -0
  128. package/dist/tools/{invokeDiscoveredTool.d.ts → discovery/invokeDiscoveredTool.d.ts} +2 -2
  129. package/dist/tools/{invokeDiscoveredTool.js → discovery/invokeDiscoveredTool.js} +5 -5
  130. package/dist/tools/dispatch/coreDispatch.js +32 -31
  131. package/dist/tools/dispatch/diagnosticDispatch.d.ts +3 -5
  132. package/dist/tools/dispatch/diagnosticDispatch.js +70 -68
  133. package/dist/tools/dispatch/directDispatch.js +11 -10
  134. package/dist/tools/dispatch/goalDispatch.js +5 -4
  135. package/dist/tools/dispatch/releaseDispatch.js +3 -2
  136. package/dist/tools/dispatch/validation.d.ts +7 -0
  137. package/dist/tools/dispatch/validation.js +71 -0
  138. package/dist/tools/{getPlan.js → goals/getPlan.js} +4 -4
  139. package/dist/tools/{goalSubgoalTask.d.ts → goals/goalSubgoalTask.d.ts} +1 -1
  140. package/dist/tools/goals/goalSubgoalTask.js +118 -0
  141. package/dist/tools/goals/index.d.ts +3 -0
  142. package/dist/tools/goals/index.js +3 -0
  143. package/dist/tools/{savePlan.js → goals/savePlan.js} +28 -8
  144. package/dist/tools/registry.d.ts +7 -11
  145. package/dist/tools/registry.js +17 -1178
  146. package/dist/tools/{checkReleaseGate.d.ts → release/checkReleaseGate.d.ts} +1 -1
  147. package/dist/tools/{checkReleaseGate.js → release/checkReleaseGate.js} +3 -3
  148. package/dist/tools/release/index.d.ts +2 -0
  149. package/dist/tools/release/index.js +2 -0
  150. package/dist/tools/{releaseMode.d.ts → release/releaseMode.d.ts} +2 -2
  151. package/dist/tools/{releaseMode.js → release/releaseMode.js} +46 -26
  152. package/dist/tools/tasks/cancelTask.d.ts +11 -0
  153. package/dist/tools/tasks/cancelTask.js +85 -0
  154. package/dist/tools/{createTask.d.ts → tasks/createTask.d.ts} +10 -2
  155. package/dist/tools/{createTask.js → tasks/createTask.js} +42 -27
  156. package/dist/tools/{diagnoseTask.d.ts → tasks/diagnoseTask.d.ts} +1 -1
  157. package/dist/tools/{diagnoseTask.js → tasks/diagnoseTask.js} +8 -8
  158. package/dist/tools/{evidencePack.js → tasks/evidencePack.js} +34 -22
  159. package/dist/tools/{getTaskFile.js → tasks/getTaskFile.js} +4 -4
  160. package/dist/tools/{getTaskStatus.d.ts → tasks/getTaskStatus.d.ts} +6 -1
  161. package/dist/tools/{getTaskStatus.js → tasks/getTaskStatus.js} +5 -5
  162. package/dist/tools/{getTaskStdoutTail.js → tasks/getTaskStdoutTail.js} +13 -11
  163. package/dist/tools/{getTaskSummary.js → tasks/getTaskSummary.js} +33 -26
  164. package/dist/tools/tasks/index.d.ts +18 -0
  165. package/dist/tools/tasks/index.js +18 -0
  166. package/dist/tools/tasks/killTask.d.ts +1 -0
  167. package/dist/tools/{listTasks.d.ts → tasks/listTasks.d.ts} +1 -1
  168. package/dist/tools/{listTasks.js → tasks/listTasks.js} +3 -3
  169. package/dist/tools/{reconcileTasks.d.ts → tasks/reconcileTasks.d.ts} +1 -1
  170. package/dist/tools/{reconcileTasks.js → tasks/reconcileTasks.js} +64 -66
  171. package/dist/tools/tasks/retryTask.js +60 -0
  172. package/dist/tools/{runTaskLoop.d.ts → tasks/runTaskLoop.d.ts} +6 -6
  173. package/dist/tools/{runTaskLoop.js → tasks/runTaskLoop.js} +58 -30
  174. package/dist/tools/tasks/runVerification.js +38 -0
  175. package/dist/tools/{taskLineage.js → tasks/taskLineage.js} +8 -7
  176. package/dist/tools/{taskOutputs.js → tasks/taskOutputs.js} +17 -16
  177. package/dist/tools/{androidDoctor.js → workspace/androidDoctor.js} +16 -10
  178. package/dist/tools/{applyPatch.d.ts → workspace/applyPatch.d.ts} +1 -1
  179. package/dist/tools/workspace/applyPatch.js +53 -0
  180. package/dist/tools/{discardWorktree.js → workspace/discardWorktree.js} +3 -3
  181. package/dist/tools/workspace/index.d.ts +10 -0
  182. package/dist/tools/workspace/index.js +10 -0
  183. package/dist/tools/{listAgents.d.ts → workspace/listAgents.d.ts} +5 -0
  184. package/dist/tools/{listAgents.js → workspace/listAgents.js} +8 -4
  185. package/dist/tools/{listWorkspace.js → workspace/listWorkspace.js} +3 -3
  186. package/dist/tools/{mergeWorktree.js → workspace/mergeWorktree.js} +3 -3
  187. package/dist/tools/{readWorkspaceFile.js → workspace/readWorkspaceFile.js} +8 -8
  188. package/dist/tools/{recommendAgentForTask.js → workspace/recommendAgentForTask.js} +3 -3
  189. package/dist/tools/{searchWorkspace.js → workspace/searchWorkspace.js} +5 -5
  190. package/dist/tools/{syncFile.d.ts → workspace/syncFile.d.ts} +1 -1
  191. package/dist/tools/workspace/syncFile.js +134 -0
  192. package/dist/utils/atomicFile.d.ts +7 -0
  193. package/dist/utils/atomicFile.js +48 -0
  194. package/dist/utils/boundedFile.d.ts +8 -0
  195. package/dist/utils/boundedFile.js +93 -0
  196. package/dist/utils/lockedJsonFile.d.ts +15 -0
  197. package/dist/utils/lockedJsonFile.js +224 -0
  198. package/dist/utils/platform.d.ts +11 -0
  199. package/dist/utils/platform.js +26 -0
  200. package/dist/utils/stableJson.d.ts +2 -0
  201. package/dist/utils/stableJson.js +12 -0
  202. package/dist/version.d.ts +3 -2
  203. package/dist/version.js +3 -2
  204. package/dist/watcherStatus.d.ts +2 -2
  205. package/dist/watcherStatus.js +18 -0
  206. package/docs/CODE_WIKI.md +808 -143
  207. package/docs/architecture.md +15 -55
  208. package/docs/assets/patchwarden-desktop-onboarding.png +0 -0
  209. package/docs/control-center/README.md +4 -4
  210. package/docs/dashboard-overview.md +4 -4
  211. package/docs/desktop-app.md +175 -0
  212. package/docs/evidence-pack-schema.md +3 -3
  213. package/docs/open-source-application.md +17 -11
  214. package/docs/release-checklist.md +1 -1
  215. package/docs/release-evidence.md +39 -29
  216. package/docs/task-safe-review-workflow.md +5 -5
  217. package/docs/threat-model.md +19 -1
  218. package/examples/config.example.json +4 -2
  219. package/package.json +27 -10
  220. package/scripts/README.md +15 -6
  221. package/scripts/build.js +11 -0
  222. package/scripts/checks/brand-check.js +3 -0
  223. package/scripts/checks/build-output-check.js +37 -0
  224. package/scripts/checks/control-center-smoke.js +91 -6
  225. package/scripts/checks/control-smoke.js +19 -8
  226. package/scripts/checks/http-mcp-smoke.js +82 -4
  227. package/scripts/checks/lifecycle-smoke.js +10 -10
  228. package/scripts/checks/mcp-manifest-check.js +1 -1
  229. package/scripts/checks/mcp-smoke.js +27 -3
  230. package/scripts/checks/package-manifest-check.js +20 -7
  231. package/scripts/checks/unit-tests.js +3 -5
  232. package/scripts/checks/watcher-supervisor-smoke.js +9 -0
  233. package/scripts/control/get-patchwarden-health.ps1 +1 -1
  234. package/scripts/control/manage-patchwarden.ps1 +69 -4
  235. package/scripts/control/provision-patchwarden-tunnel.ps1 +127 -0
  236. package/scripts/control/run-background-supervisor.ps1 +41 -0
  237. package/scripts/control/start-patchwarden-tunnel.ps1 +126 -32
  238. package/{PatchWarden-Control-Tray.cmd → scripts/launchers/PatchWarden-Control-Tray.cmd} +3 -3
  239. package/scripts/launchers/PatchWarden-Control.cmd +6 -0
  240. package/{PatchWarden-Desktop.cmd → scripts/launchers/PatchWarden-Desktop.cmd} +2 -2
  241. package/scripts/launchers/Restart-PatchWarden-Control.cmd +6 -0
  242. package/{Stop-PatchWarden.cmd → scripts/launchers/Stop-PatchWarden.cmd} +2 -2
  243. package/scripts/lib/clean-generated-output.js +11 -0
  244. package/scripts/lib/file-discovery.js +12 -0
  245. package/scripts/release/desktop-preflight.js +211 -0
  246. package/scripts/release/pack-clean.js +59 -14
  247. package/src/assessments/agentAssessor.ts +26 -12
  248. package/src/assessments/assessmentStore.ts +52 -40
  249. package/src/config.ts +70 -13
  250. package/src/control/fileManager.ts +40 -0
  251. package/src/control/middleware/auth.ts +22 -4
  252. package/src/control/middleware/static.ts +16 -3
  253. package/src/control/routeTable.ts +301 -0
  254. package/src/control/routes/audit.ts +14 -6
  255. package/src/control/routes/evidence.ts +1 -1
  256. package/src/control/routes/lineage.ts +1 -1
  257. package/src/control/routes/process.ts +341 -40
  258. package/src/control/routes/sessions.ts +88 -25
  259. package/src/control/routes/status.ts +97 -17
  260. package/src/control/routes/taskActions.ts +24 -58
  261. package/src/control/routes/tasks.ts +66 -58
  262. package/src/control/routes/workspace.ts +56 -25
  263. package/src/control/runtime.ts +69 -33
  264. package/src/control/server.ts +36 -342
  265. package/src/control/shared.ts +77 -11
  266. package/src/direct/directAudit.ts +17 -9
  267. package/src/direct/directGuards.ts +72 -44
  268. package/src/direct/directPatch.ts +95 -8
  269. package/src/direct/directSessionStore.ts +211 -55
  270. package/src/direct/directVerification.ts +9 -23
  271. package/src/doctor.ts +73 -59
  272. package/src/goal/acceptanceEngine.ts +1 -1
  273. package/src/goal/goalProgress.ts +17 -13
  274. package/src/goal/goalReport.ts +5 -13
  275. package/src/goal/goalStatus.ts +19 -3
  276. package/src/goal/goalStore.ts +241 -54
  277. package/src/goal/handoffExport.ts +6 -6
  278. package/src/goal/specKitImport.ts +12 -12
  279. package/src/goal/subgoalSync.ts +11 -8
  280. package/src/goal/worktreeManager.ts +139 -22
  281. package/src/httpServer.ts +113 -30
  282. package/src/logging.ts +10 -3
  283. package/src/policy/projectPolicy.ts +5 -2
  284. package/src/release/releaseGate.ts +74 -27
  285. package/src/runner/agentInvocation.ts +126 -4
  286. package/src/runner/changeCapture.ts +291 -57
  287. package/src/runner/postTaskCleanup.ts +41 -17
  288. package/src/runner/processSecurity.ts +433 -0
  289. package/src/runner/runTask.ts +138 -91
  290. package/src/runner/simpleProcess.ts +66 -31
  291. package/src/{taskProgress.ts → runner/taskProgress.ts} +3 -3
  292. package/src/{taskRuntime.ts → runner/taskRuntime.ts} +4 -3
  293. package/src/runner/taskStatusStore.ts +54 -0
  294. package/src/runner/watch.ts +281 -51
  295. package/src/security/contentRedaction.ts +2 -2
  296. package/src/security/discoveryTokenStore.ts +18 -4
  297. package/src/security/loopbackHost.ts +19 -0
  298. package/src/security/pathGuard.ts +35 -16
  299. package/src/security/planGuard.ts +2 -1
  300. package/src/security/runtimeGuard.ts +9 -6
  301. package/src/security/secretComparison.ts +13 -0
  302. package/src/security/sensitiveGuard.ts +27 -8
  303. package/src/security/toolInvocationGuard.ts +2 -2
  304. package/src/security/workspaceRootGuard.ts +53 -0
  305. package/src/smoke-test.ts +25 -24
  306. package/src/tools/catalog/index.ts +4 -0
  307. package/src/tools/{toolCatalog.ts → catalog/toolCatalog.ts} +3 -12
  308. package/src/tools/{toolRegistry.ts → catalog/toolRegistry.ts} +7 -16
  309. package/src/tools/definitions/toolDefs.ts +1264 -0
  310. package/src/tools/{auditSession.ts → diagnostics/auditSession.ts} +2 -2
  311. package/src/tools/{auditTask.ts → diagnostics/auditTask.ts} +99 -32
  312. package/src/tools/{healthCheck.ts → diagnostics/healthCheck.ts} +8 -8
  313. package/src/tools/diagnostics/index.ts +6 -0
  314. package/src/tools/{safeStatus.ts → diagnostics/safeStatus.ts} +7 -7
  315. package/src/tools/{safeViews.ts → diagnostics/safeViews.ts} +27 -17
  316. package/src/tools/{schemaDriftCheck.ts → diagnostics/schemaDriftCheck.ts} +3 -3
  317. package/src/tools/{createDirectSession.ts → direct/createDirectSession.ts} +6 -6
  318. package/src/tools/{finalizeDirectSession.ts → direct/finalizeDirectSession.ts} +7 -5
  319. package/src/tools/direct/index.ts +3 -0
  320. package/src/tools/{runDirectVerificationBundle.ts → direct/runDirectVerificationBundle.ts} +2 -2
  321. package/src/tools/{discoverTools.ts → discovery/discoverTools.ts} +6 -6
  322. package/src/tools/{explainTool.ts → discovery/explainTool.ts} +3 -3
  323. package/src/tools/discovery/index.ts +3 -0
  324. package/src/tools/{invokeDiscoveredTool.ts → discovery/invokeDiscoveredTool.ts} +7 -7
  325. package/src/tools/dispatch/coreDispatch.ts +36 -31
  326. package/src/tools/dispatch/diagnosticDispatch.ts +26 -21
  327. package/src/tools/dispatch/directDispatch.ts +11 -10
  328. package/src/tools/dispatch/goalDispatch.ts +5 -4
  329. package/src/tools/dispatch/releaseDispatch.ts +3 -2
  330. package/src/tools/dispatch/validation.ts +78 -0
  331. package/src/tools/{getPlan.ts → goals/getPlan.ts} +4 -4
  332. package/src/tools/{goalSubgoalTask.ts → goals/goalSubgoalTask.ts} +41 -26
  333. package/src/tools/goals/index.ts +3 -0
  334. package/src/tools/{savePlan.ts → goals/savePlan.ts} +38 -8
  335. package/src/tools/registry.ts +17 -1261
  336. package/src/tools/{checkReleaseGate.ts → release/checkReleaseGate.ts} +3 -3
  337. package/src/tools/release/index.ts +2 -0
  338. package/src/tools/{releaseMode.ts → release/releaseMode.ts} +42 -24
  339. package/src/tools/tasks/cancelTask.ts +109 -0
  340. package/src/tools/{createTask.ts → tasks/createTask.ts} +49 -26
  341. package/src/tools/{diagnoseTask.ts → tasks/diagnoseTask.ts} +8 -8
  342. package/src/tools/{evidencePack.ts → tasks/evidencePack.ts} +36 -22
  343. package/src/tools/{getTaskFile.ts → tasks/getTaskFile.ts} +4 -4
  344. package/src/tools/{getTaskStatus.ts → tasks/getTaskStatus.ts} +7 -5
  345. package/src/tools/{getTaskStdoutTail.ts → tasks/getTaskStdoutTail.ts} +13 -11
  346. package/src/tools/{getTaskSummary.ts → tasks/getTaskSummary.ts} +44 -33
  347. package/src/tools/tasks/index.ts +18 -0
  348. package/src/tools/{listTasks.ts → tasks/listTasks.ts} +4 -4
  349. package/src/tools/{reconcileTasks.ts → tasks/reconcileTasks.ts} +68 -66
  350. package/src/tools/tasks/retryTask.ts +69 -0
  351. package/src/tools/{runTaskLoop.ts → tasks/runTaskLoop.ts} +69 -34
  352. package/src/tools/{runVerification.ts → tasks/runVerification.ts} +6 -3
  353. package/src/tools/{taskLineage.ts → tasks/taskLineage.ts} +8 -7
  354. package/src/tools/{taskOutputs.ts → tasks/taskOutputs.ts} +27 -20
  355. package/src/tools/{androidDoctor.ts → workspace/androidDoctor.ts} +15 -10
  356. package/src/tools/{applyPatch.ts → workspace/applyPatch.ts} +38 -16
  357. package/src/tools/{discardWorktree.ts → workspace/discardWorktree.ts} +3 -3
  358. package/src/tools/workspace/index.ts +10 -0
  359. package/src/tools/{listAgents.ts → workspace/listAgents.ts} +11 -4
  360. package/src/tools/{listWorkspace.ts → workspace/listWorkspace.ts} +3 -3
  361. package/src/tools/{mergeWorktree.ts → workspace/mergeWorktree.ts} +3 -3
  362. package/src/tools/{readWorkspaceFile.ts → workspace/readWorkspaceFile.ts} +8 -8
  363. package/src/tools/{recommendAgentForTask.ts → workspace/recommendAgentForTask.ts} +3 -3
  364. package/src/tools/{searchWorkspace.ts → workspace/searchWorkspace.ts} +5 -5
  365. package/src/tools/workspace/syncFile.ts +292 -0
  366. package/src/utils/atomicFile.ts +58 -0
  367. package/src/utils/boundedFile.ts +116 -0
  368. package/src/utils/lockedJsonFile.ts +251 -0
  369. package/src/utils/platform.ts +31 -0
  370. package/src/utils/stableJson.ts +12 -0
  371. package/src/version.ts +3 -2
  372. package/src/watcherStatus.ts +19 -1
  373. package/ui/desktop-bootstrap.js +11 -0
  374. package/ui/desktop-bridge.js +36 -0
  375. package/ui/desktop.css +160 -0
  376. package/ui/getting-started.js +93 -0
  377. package/ui/i18n.js +484 -0
  378. package/ui/log-parser.js +30 -0
  379. package/ui/pages/audit.html +12 -0
  380. package/ui/pages/dashboard.html +90 -147
  381. package/ui/pages/direct-sessions.html +123 -26
  382. package/ui/pages/getting-started.html +43 -0
  383. package/ui/pages/logs.html +105 -22
  384. package/ui/pages/settings.html +101 -0
  385. package/ui/pages/task-detail.html +12 -0
  386. package/ui/pages/tasks.html +54 -8
  387. package/ui/pages/workspace.html +38 -1
  388. package/ui/settings.js +245 -0
  389. package/PatchWarden-Control.cmd +0 -6
  390. package/Restart-PatchWarden-Control.cmd +0 -6
  391. package/dist/test/unit/acceptance-engine.test.d.ts +0 -1
  392. package/dist/test/unit/acceptance-engine.test.js +0 -228
  393. package/dist/test/unit/agent-router.test.d.ts +0 -1
  394. package/dist/test/unit/agent-router.test.js +0 -287
  395. package/dist/test/unit/android-doctor.test.d.ts +0 -1
  396. package/dist/test/unit/android-doctor.test.js +0 -118
  397. package/dist/test/unit/apply-patch.test.d.ts +0 -1
  398. package/dist/test/unit/apply-patch.test.js +0 -225
  399. package/dist/test/unit/audit-checks.test.d.ts +0 -1
  400. package/dist/test/unit/audit-checks.test.js +0 -350
  401. package/dist/test/unit/chinese-path.test.d.ts +0 -1
  402. package/dist/test/unit/chinese-path.test.js +0 -91
  403. package/dist/test/unit/command-guard.test.d.ts +0 -1
  404. package/dist/test/unit/command-guard.test.js +0 -160
  405. package/dist/test/unit/create-task.test.d.ts +0 -1
  406. package/dist/test/unit/create-task.test.js +0 -197
  407. package/dist/test/unit/diagnose-task.test.d.ts +0 -1
  408. package/dist/test/unit/diagnose-task.test.js +0 -457
  409. package/dist/test/unit/direct-guards.test.d.ts +0 -1
  410. package/dist/test/unit/direct-guards.test.js +0 -328
  411. package/dist/test/unit/discovery-token-store.test.d.ts +0 -1
  412. package/dist/test/unit/discovery-token-store.test.js +0 -139
  413. package/dist/test/unit/evidence-pack.test.d.ts +0 -1
  414. package/dist/test/unit/evidence-pack.test.js +0 -224
  415. package/dist/test/unit/get-task-status.test.d.ts +0 -1
  416. package/dist/test/unit/get-task-status.test.js +0 -174
  417. package/dist/test/unit/get-task-summary.test.d.ts +0 -1
  418. package/dist/test/unit/get-task-summary.test.js +0 -146
  419. package/dist/test/unit/goal-graph.test.d.ts +0 -1
  420. package/dist/test/unit/goal-graph.test.js +0 -298
  421. package/dist/test/unit/goal-progress.test.d.ts +0 -1
  422. package/dist/test/unit/goal-progress.test.js +0 -381
  423. package/dist/test/unit/goal-report.test.d.ts +0 -1
  424. package/dist/test/unit/goal-report.test.js +0 -159
  425. package/dist/test/unit/goal-status.test.d.ts +0 -1
  426. package/dist/test/unit/goal-status.test.js +0 -215
  427. package/dist/test/unit/goal-store.test.d.ts +0 -1
  428. package/dist/test/unit/goal-store.test.js +0 -253
  429. package/dist/test/unit/goal-subgoal-task.test.d.ts +0 -1
  430. package/dist/test/unit/goal-subgoal-task.test.js +0 -55
  431. package/dist/test/unit/goal-tools-registry.test.d.ts +0 -1
  432. package/dist/test/unit/goal-tools-registry.test.js +0 -192
  433. package/dist/test/unit/handoff-export.test.d.ts +0 -1
  434. package/dist/test/unit/handoff-export.test.js +0 -263
  435. package/dist/test/unit/invoke-discovered-tool.test.d.ts +0 -1
  436. package/dist/test/unit/invoke-discovered-tool.test.js +0 -167
  437. package/dist/test/unit/logging.test.d.ts +0 -1
  438. package/dist/test/unit/logging.test.js +0 -397
  439. package/dist/test/unit/path-guard.test.d.ts +0 -1
  440. package/dist/test/unit/path-guard.test.js +0 -133
  441. package/dist/test/unit/post-task-cleanup.test.d.ts +0 -1
  442. package/dist/test/unit/post-task-cleanup.test.js +0 -48
  443. package/dist/test/unit/project-policy-release-mode.test.d.ts +0 -1
  444. package/dist/test/unit/project-policy-release-mode.test.js +0 -125
  445. package/dist/test/unit/reconcile-tasks.test.d.ts +0 -1
  446. package/dist/test/unit/reconcile-tasks.test.js +0 -456
  447. package/dist/test/unit/release-gate.test.d.ts +0 -1
  448. package/dist/test/unit/release-gate.test.js +0 -242
  449. package/dist/test/unit/run-task-loop.test.d.ts +0 -1
  450. package/dist/test/unit/run-task-loop.test.js +0 -380
  451. package/dist/test/unit/safe-status.test.d.ts +0 -1
  452. package/dist/test/unit/safe-status.test.js +0 -165
  453. package/dist/test/unit/safe-views.test.d.ts +0 -1
  454. package/dist/test/unit/safe-views.test.js +0 -171
  455. package/dist/test/unit/schema-drift-check.test.d.ts +0 -1
  456. package/dist/test/unit/schema-drift-check.test.js +0 -176
  457. package/dist/test/unit/sensitive-guard.test.d.ts +0 -1
  458. package/dist/test/unit/sensitive-guard.test.js +0 -104
  459. package/dist/test/unit/spec-kit-import.test.d.ts +0 -1
  460. package/dist/test/unit/spec-kit-import.test.js +0 -341
  461. package/dist/test/unit/subgoal-sync.test.d.ts +0 -1
  462. package/dist/test/unit/subgoal-sync.test.js +0 -183
  463. package/dist/test/unit/sync-file.test.d.ts +0 -1
  464. package/dist/test/unit/sync-file.test.js +0 -154
  465. package/dist/test/unit/tool-invocation-guard.test.d.ts +0 -1
  466. package/dist/test/unit/tool-invocation-guard.test.js +0 -432
  467. package/dist/test/unit/tool-usage-stats.test.d.ts +0 -1
  468. package/dist/test/unit/tool-usage-stats.test.js +0 -300
  469. package/dist/test/unit/toolSearch.test.d.ts +0 -1
  470. package/dist/test/unit/toolSearch.test.js +0 -571
  471. package/dist/test/unit/wait-for-task.test.d.ts +0 -1
  472. package/dist/test/unit/wait-for-task.test.js +0 -144
  473. package/dist/test/unit/watcher-status.test.d.ts +0 -1
  474. package/dist/test/unit/watcher-status.test.js +0 -169
  475. package/dist/test/unit/worktree-manager.test.d.ts +0 -1
  476. package/dist/test/unit/worktree-manager.test.js +0 -176
  477. package/dist/tools/applyPatch.js +0 -41
  478. package/dist/tools/cancelTask.d.ts +0 -30
  479. package/dist/tools/cancelTask.js +0 -64
  480. package/dist/tools/finalizeDirectSession.js +0 -84
  481. package/dist/tools/goalSubgoalTask.js +0 -110
  482. package/dist/tools/killTask.d.ts +0 -15
  483. package/dist/tools/retryTask.js +0 -45
  484. package/dist/tools/runVerification.js +0 -36
  485. package/dist/tools/syncFile.js +0 -65
  486. package/docs/control-center/control-center-daily-driver.md +0 -211
  487. package/docs/control-center/control-center-mvp.md +0 -205
  488. package/docs/control-center/control-center-phase2.md +0 -159
  489. package/docs/execution-plan-2026-07-09.md +0 -315
  490. package/docs/release-v0.6.0.md +0 -71
  491. package/docs/release-v0.6.1.md +0 -75
  492. package/docs/release-v0.6.4.md +0 -45
  493. package/docs/roadmap-execution-and-acceptance.md +0 -365
  494. package/src/test/unit/acceptance-engine.test.ts +0 -261
  495. package/src/test/unit/agent-router.test.ts +0 -342
  496. package/src/test/unit/android-doctor.test.ts +0 -158
  497. package/src/test/unit/apply-patch.test.ts +0 -293
  498. package/src/test/unit/audit-checks.test.ts +0 -567
  499. package/src/test/unit/chinese-path.test.ts +0 -106
  500. package/src/test/unit/command-guard.test.ts +0 -221
  501. package/src/test/unit/create-task.test.ts +0 -255
  502. package/src/test/unit/diagnose-task.test.ts +0 -544
  503. package/src/test/unit/direct-guards.test.ts +0 -467
  504. package/src/test/unit/discovery-token-store.test.ts +0 -181
  505. package/src/test/unit/evidence-pack.test.ts +0 -251
  506. package/src/test/unit/get-task-status.test.ts +0 -203
  507. package/src/test/unit/get-task-summary.test.ts +0 -173
  508. package/src/test/unit/goal-graph.test.ts +0 -347
  509. package/src/test/unit/goal-progress.test.ts +0 -538
  510. package/src/test/unit/goal-report.test.ts +0 -189
  511. package/src/test/unit/goal-status.test.ts +0 -270
  512. package/src/test/unit/goal-store.test.ts +0 -318
  513. package/src/test/unit/goal-subgoal-task.test.ts +0 -72
  514. package/src/test/unit/goal-tools-registry.test.ts +0 -245
  515. package/src/test/unit/handoff-export.test.ts +0 -295
  516. package/src/test/unit/invoke-discovered-tool.test.ts +0 -216
  517. package/src/test/unit/logging.test.ts +0 -497
  518. package/src/test/unit/path-guard.test.ts +0 -180
  519. package/src/test/unit/post-task-cleanup.test.ts +0 -53
  520. package/src/test/unit/project-policy-release-mode.test.ts +0 -156
  521. package/src/test/unit/reconcile-tasks.test.ts +0 -551
  522. package/src/test/unit/release-gate.test.ts +0 -314
  523. package/src/test/unit/run-task-loop.test.ts +0 -425
  524. package/src/test/unit/safe-status.test.ts +0 -187
  525. package/src/test/unit/safe-views.test.ts +0 -184
  526. package/src/test/unit/schema-drift-check.test.ts +0 -260
  527. package/src/test/unit/sensitive-guard.test.ts +0 -124
  528. package/src/test/unit/spec-kit-import.test.ts +0 -429
  529. package/src/test/unit/subgoal-sync.test.ts +0 -236
  530. package/src/test/unit/sync-file.test.ts +0 -231
  531. package/src/test/unit/tool-invocation-guard.test.ts +0 -542
  532. package/src/test/unit/tool-usage-stats.test.ts +0 -384
  533. package/src/test/unit/toolSearch.test.ts +0 -756
  534. package/src/test/unit/wait-for-task.test.ts +0 -176
  535. package/src/test/unit/watcher-status.test.ts +0 -190
  536. package/src/test/unit/worktree-manager.test.ts +0 -247
  537. package/src/tools/cancelTask.ts +0 -70
  538. package/src/tools/retryTask.ts +0 -53
  539. package/src/tools/syncFile.ts +0 -122
  540. /package/dist/tools/{toolCatalog.d.ts → catalog/toolCatalog.d.ts} +0 -0
  541. /package/dist/tools/{toolSearch.d.ts → catalog/toolSearch.d.ts} +0 -0
  542. /package/dist/tools/{toolSearch.js → catalog/toolSearch.js} +0 -0
  543. /package/dist/tools/{toolUsageStats.d.ts → catalog/toolUsageStats.d.ts} +0 -0
  544. /package/dist/tools/{toolUsageStats.js → catalog/toolUsageStats.js} +0 -0
  545. /package/dist/tools/{createDirectSession.d.ts → direct/createDirectSession.d.ts} +0 -0
  546. /package/dist/tools/{runDirectVerificationBundle.d.ts → direct/runDirectVerificationBundle.d.ts} +0 -0
  547. /package/dist/tools/{getPlan.d.ts → goals/getPlan.d.ts} +0 -0
  548. /package/dist/tools/{savePlan.d.ts → goals/savePlan.d.ts} +0 -0
  549. /package/dist/tools/{evidencePack.d.ts → tasks/evidencePack.d.ts} +0 -0
  550. /package/dist/tools/{getTaskFile.d.ts → tasks/getTaskFile.d.ts} +0 -0
  551. /package/dist/tools/{getTaskProgress.d.ts → tasks/getTaskProgress.d.ts} +0 -0
  552. /package/dist/tools/{getTaskProgress.js → tasks/getTaskProgress.js} +0 -0
  553. /package/dist/tools/{getTaskStdoutTail.d.ts → tasks/getTaskStdoutTail.d.ts} +0 -0
  554. /package/dist/tools/{getTaskSummary.d.ts → tasks/getTaskSummary.d.ts} +0 -0
  555. /package/dist/tools/{killTask.js → tasks/killTask.js} +0 -0
  556. /package/dist/tools/{retryTask.d.ts → tasks/retryTask.d.ts} +0 -0
  557. /package/dist/tools/{runVerification.d.ts → tasks/runVerification.d.ts} +0 -0
  558. /package/dist/tools/{taskLineage.d.ts → tasks/taskLineage.d.ts} +0 -0
  559. /package/dist/tools/{taskOutputs.d.ts → tasks/taskOutputs.d.ts} +0 -0
  560. /package/dist/tools/{waitForTask.d.ts → tasks/waitForTask.d.ts} +0 -0
  561. /package/dist/tools/{waitForTask.js → tasks/waitForTask.js} +0 -0
  562. /package/dist/tools/{androidDoctor.d.ts → workspace/androidDoctor.d.ts} +0 -0
  563. /package/dist/tools/{discardWorktree.d.ts → workspace/discardWorktree.d.ts} +0 -0
  564. /package/dist/tools/{listWorkspace.d.ts → workspace/listWorkspace.d.ts} +0 -0
  565. /package/dist/tools/{mergeWorktree.d.ts → workspace/mergeWorktree.d.ts} +0 -0
  566. /package/dist/tools/{readWorkspaceFile.d.ts → workspace/readWorkspaceFile.d.ts} +0 -0
  567. /package/dist/tools/{recommendAgentForTask.d.ts → workspace/recommendAgentForTask.d.ts} +0 -0
  568. /package/dist/tools/{searchWorkspace.d.ts → workspace/searchWorkspace.d.ts} +0 -0
  569. /package/src/tools/{toolSearch.ts → catalog/toolSearch.ts} +0 -0
  570. /package/src/tools/{toolUsageStats.ts → catalog/toolUsageStats.ts} +0 -0
  571. /package/src/tools/{getTaskProgress.ts → tasks/getTaskProgress.ts} +0 -0
  572. /package/src/tools/{killTask.ts → tasks/killTask.ts} +0 -0
  573. /package/src/tools/{waitForTask.ts → tasks/waitForTask.ts} +0 -0
@@ -124,7 +124,7 @@ html.dark {
124
124
  .pw-toast.pw-toast-info { border-left: 3px solid var(--pw-state-info); }
125
125
  .pw-toast .pw-toast-title { font-weight: 600; margin-bottom: 2px; }
126
126
  .pw-toast .pw-toast-msg { color: var(--pw-text-secondary); word-break: break-word; }
127
- .pw-toast .pw-toast-close { margin-left: auto; cursor: pointer; color: var(--pw-text-tertiary); font-size: 14px; line-height: 1; padding: 0 2px; }
127
+ .pw-toast .pw-toast-close { margin-left: auto; cursor: pointer; color: var(--pw-text-tertiary); background: transparent; border: 0; font-size: 14px; line-height: 1; padding: 0 2px; }
128
128
  .pw-toast .pw-toast-close:hover { color: var(--pw-text-primary); }
129
129
  @keyframes pw-toast-in {
130
130
  from { opacity: 0; transform: translateX(20px); }
@@ -172,6 +172,8 @@ html.dark {
172
172
  background-color: var(--pw-accent-muted);
173
173
  }
174
174
  </style>
175
+ <script src="/desktop-bootstrap.js"></script>
176
+ <link rel="stylesheet" href="/desktop.css">
175
177
  </head>
176
178
  <body class="flex h-screen overflow-hidden">
177
179
 
@@ -189,6 +191,10 @@ html.dark {
189
191
 
190
192
  <!-- Navigation -->
191
193
  <nav class="flex-1 flex flex-col gap-1 px-3 pt-4 overflow-y-auto">
194
+ <a href="/pages/getting-started.html" data-nav-key="getting-started" class="desktop-only-nav flex items-center gap-3 px-3 py-2.5 rounded-lg text-sm font-medium transition-colors truncate" style="color: var(--pw-text-secondary); font-family: var(--pw-font-sans);">
195
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="shrink-0"><path d="M4.5 16.5c-1.5 1.26-2 5-2 5s3.74-.5 5-2c.71-.84.7-2.13-.09-2.91a2.18 2.18 0 0 0-2.91-.09z"/><path d="m12 15-3-3a22 22 0 0 1 2-3.95A12.87 12.87 0 0 1 22 2c0 2.72-.78 7.5-6.05 11a22.35 22.35 0 0 1-3.95 2z"/></svg>
196
+ <span class="truncate" data-i18n="nav.gettingStarted">开始使用</span>
197
+ </a>
192
198
 
193
199
  <a href="/pages/dashboard.html" data-nav-key="dashboard" data-dom-id="nav-dashboard" class="flex items-center gap-3 px-3 py-2.5 rounded-lg text-sm font-medium transition-colors truncate" style="color: var(--pw-text-secondary); font-family: var(--pw-font-sans);">
194
200
  <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="shrink-0">
@@ -244,6 +250,10 @@ html.dark {
244
250
  <span class="truncate" data-zh="日志" data-en="Logs">日志</span>
245
251
  </a>
246
252
 
253
+ <a href="/pages/settings.html" data-nav-key="settings" class="desktop-only-nav flex items-center gap-3 px-3 py-2.5 rounded-lg text-sm font-medium transition-colors truncate" style="color: var(--pw-text-secondary); font-family: var(--pw-font-sans);">
254
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="shrink-0"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.7 1.7 0 0 0 .3 1.9l.1.1-2.8 2.8-.1-.1a1.7 1.7 0 0 0-1.9-.3 1.7 1.7 0 0 0-1 1.6v.2h-4V21a1.7 1.7 0 0 0-1-1.6 1.7 1.7 0 0 0-1.9.3l-.1.1L4.2 17l.1-.1a1.7 1.7 0 0 0 .3-1.9A1.7 1.7 0 0 0 3 14H2.8v-4H3a1.7 1.7 0 0 0 1.6-1 1.7 1.7 0 0 0-.3-1.9L4.2 7 7 4.2l.1.1A1.7 1.7 0 0 0 9 4.6 1.7 1.7 0 0 0 10 3V2.8h4V3a1.7 1.7 0 0 0 1 1.6 1.7 1.7 0 0 0 1.9-.3l.1-.1L19.8 7l-.1.1a1.7 1.7 0 0 0-.3 1.9A1.7 1.7 0 0 0 21 10h.2v4H21a1.7 1.7 0 0 0-1.6 1z"/></svg>
255
+ <span class="truncate" data-i18n="nav.settings">设置</span>
256
+ </a>
247
257
  </nav>
248
258
 
249
259
  <!-- Bottom Section -->
@@ -268,6 +278,12 @@ html.dark {
268
278
  Direct 会话
269
279
  </h1>
270
280
  <div class="flex items-center gap-4 shrink-0">
281
+ <select id="session-state" class="px-2 py-1.5 rounded-md text-xs font-medium" style="background-color: var(--pw-bg-surface); color: var(--pw-text-secondary); border: 1px solid var(--pw-border-subtle); font-family: var(--pw-font-sans);">
282
+ <option value="active">进行中</option>
283
+ <option value="archive">历史记录</option>
284
+ <option value="audited">仅审计完成</option>
285
+ <option value="expired">仅已过期</option>
286
+ </select>
271
287
  <span id="lastUpdated" class="text-xs whitespace-nowrap hidden" style="color: var(--pw-text-tertiary); font-family: var(--pw-font-mono);"></span>
272
288
  <button id="refreshBtn" class="flex items-center gap-2 px-4 py-1.5 rounded-lg text-xs font-semibold whitespace-nowrap transition-colors" style="background-color: var(--pw-bg-surface); color: var(--pw-text-secondary); border: 1px solid var(--pw-border-subtle); font-family: var(--pw-font-sans);">
273
289
  <svg id="refreshIcon" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
@@ -286,6 +302,18 @@ html.dark {
286
302
  <!-- Page Content Area -->
287
303
  <main class="flex-1 overflow-y-auto p-6" style="background-color: var(--pw-bg-elevated);">
288
304
 
305
+ <div class="mb-4 flex flex-wrap items-end gap-3 rounded-lg px-4 py-3" style="background-color: var(--pw-bg-surface); border: 1px solid var(--pw-border);">
306
+ <label class="grid gap-1 text-xs" style="color: var(--pw-text-tertiary);"><span data-zh="项目" data-en="Project">项目</span><select id="session-repo" aria-label="Direct 会话项目" class="px-2 py-1.5 rounded-md text-xs" style="min-width:180px;background-color:var(--pw-bg-elevated);color:var(--pw-text-primary);border:1px solid var(--pw-border-subtle);"><option value="">全部项目</option></select></label>
307
+ <label class="grid gap-1 text-xs" style="color: var(--pw-text-tertiary);"><span data-zh="开始日期" data-en="From date">开始日期</span><input id="session-date-from" type="date" aria-label="Direct 会话开始日期" class="px-2 py-1.5 rounded-md text-xs" style="background-color:var(--pw-bg-elevated);color:var(--pw-text-primary);border:1px solid var(--pw-border-subtle);"></label>
308
+ <label class="grid gap-1 text-xs" style="color: var(--pw-text-tertiary);"><span data-zh="结束日期" data-en="To date">结束日期</span><input id="session-date-to" type="date" aria-label="Direct 会话结束日期" class="px-2 py-1.5 rounded-md text-xs" style="background-color:var(--pw-bg-elevated);color:var(--pw-text-primary);border:1px solid var(--pw-border-subtle);"></label>
309
+ </div>
310
+
311
+ <section id="direct-disabled" class="hidden rounded-lg p-6 mb-4" style="background-color: var(--pw-bg-surface); border: 1px solid var(--pw-border);">
312
+ <h2 class="text-base font-semibold" style="color:var(--pw-text-primary);" data-zh="Direct 是可选高级能力" data-en="Direct is an optional advanced capability">Direct 是可选高级能力</h2>
313
+ <p class="mt-2 text-sm" style="color:var(--pw-text-secondary);" data-zh="未启用不会影响 Core、Tunnel 或普通任务。需要受控的直接编辑会话时,可在设置中启用。" data-en="Leaving Direct disabled does not affect Core, Tunnel, or regular tasks. Enable it in Settings when you need controlled direct editing sessions.">未启用不会影响 Core、Tunnel 或普通任务。需要受控的直接编辑会话时,可在设置中启用。</p>
314
+ <a href="/pages/settings.html" class="inline-flex mt-4 px-3 py-2 rounded-md text-sm font-semibold" style="background-color:var(--pw-accent);color:var(--pw-text-inverse);" data-zh="前往设置" data-en="Open Settings">前往设置</a>
315
+ </section>
316
+
289
317
  <!-- ===== Error Banner ===== -->
290
318
  <div id="error-banner" class="hidden mb-5 rounded-lg px-4 py-3 flex items-start gap-3" style="background-color: var(--pw-state-error-bg); border: 1px solid var(--pw-state-error);">
291
319
  <svg class="shrink-0 mt-0.5" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="var(--pw-state-error)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
@@ -327,6 +355,7 @@ html.dark {
327
355
 
328
356
  <!-- ===== Sessions List ===== -->
329
357
  <div id="sessions-list" class="hidden flex flex-col gap-3"></div>
358
+ <div id="sessions-more-wrap" class="hidden mt-4 text-center"><button id="sessions-more" type="button" class="px-4 py-2 rounded-md text-sm font-medium" style="background-color:var(--pw-bg-surface);color:var(--pw-text-primary);border:1px solid var(--pw-border);" data-zh="加载更多" data-en="Load more">加载更多</button></div>
330
359
 
331
360
  <!-- ===== Session Detail ===== -->
332
361
  <div id="session-detail" class="hidden flex flex-col gap-6"></div>
@@ -339,7 +368,7 @@ html.dark {
339
368
  <div class="rounded-lg w-full max-w-2xl max-h-[80vh] flex flex-col" style="background-color: var(--pw-bg-surface); border: 1px solid var(--pw-border); box-shadow: var(--pw-shadow-modal);">
340
369
  <div class="flex items-center justify-between px-4 py-3" style="border-bottom: 1px solid var(--pw-border);">
341
370
  <h3 id="safe-modal-title" class="text-sm font-semibold truncate" style="color: var(--pw-text-primary);">—</h3>
342
- <button id="safe-modal-close" class="text-lg leading-none" style="color: var(--pw-text-tertiary);" type="button">×</button>
371
+ <button id="safe-modal-close" class="text-lg leading-none" style="color: var(--pw-text-tertiary);" type="button" title="关闭" aria-label="关闭结果面板">×</button>
343
372
  </div>
344
373
  <div id="safe-modal-body" class="flex-1 overflow-auto px-4 py-3 text-xs" style="color: var(--pw-text-primary); font-family: var(--pw-font-mono); white-space: pre-wrap; word-break: break-all;"></div>
345
374
  </div>
@@ -373,6 +402,8 @@ html.dark {
373
402
 
374
403
  var API_URL = '/api/direct-sessions';
375
404
  var controlToken = null;
405
+ var loadedSessions = [];
406
+ var nextCursor = null;
376
407
 
377
408
  function $(id) { return document.getElementById(id); }
378
409
  function hide(el) { if (el) el.classList.add('hidden'); }
@@ -432,6 +463,7 @@ html.dark {
432
463
 
433
464
  function showLoading() {
434
465
  show($('loading-state'));
466
+ hide($('direct-disabled'));
435
467
  hide($('empty-state'));
436
468
  hide($('sessions-list'));
437
469
  hide($('session-detail'));
@@ -441,6 +473,8 @@ html.dark {
441
473
  show($('empty-state'));
442
474
  hide($('sessions-list'));
443
475
  hide($('session-detail'));
476
+ hide($('direct-disabled'));
477
+ hide($('sessions-more-wrap'));
444
478
  var rEl = $('empty-reason');
445
479
  if (rEl) rEl.textContent = reason || '';
446
480
  }
@@ -449,12 +483,24 @@ html.dark {
449
483
  hide($('empty-state'));
450
484
  show($('sessions-list'));
451
485
  hide($('session-detail'));
486
+ hide($('direct-disabled'));
452
487
  }
453
488
  function showDetail() {
454
489
  hide($('loading-state'));
455
490
  hide($('empty-state'));
456
491
  hide($('sessions-list'));
457
492
  show($('session-detail'));
493
+ hide($('direct-disabled'));
494
+ hide($('sessions-more-wrap'));
495
+ }
496
+
497
+ function showDirectDisabled() {
498
+ hide($('loading-state'));
499
+ hide($('empty-state'));
500
+ hide($('sessions-list'));
501
+ hide($('session-detail'));
502
+ hide($('sessions-more-wrap'));
503
+ show($('direct-disabled'));
458
504
  }
459
505
 
460
506
  function verdictColor(v) {
@@ -489,7 +535,7 @@ html.dark {
489
535
  + '<div class="pw-toast-title">' + escapeHtml(title || '') + '</div>'
490
536
  + (msg ? '<div class="pw-toast-msg">' + escapeHtml(msg) + '</div>' : '')
491
537
  + '</div>'
492
- + '<span class="pw-toast-close" title="关闭">×</span>';
538
+ + '<button type="button" class="pw-toast-close" title="关闭" aria-label="关闭通知">×</button>';
493
539
  var closeBtn = el.querySelector('.pw-toast-close');
494
540
  if (closeBtn) closeBtn.addEventListener('click', function () { dismissToast(el); });
495
541
  container.appendChild(el);
@@ -650,7 +696,7 @@ html.dark {
650
696
  var c = COLOR_VARS[meta.color] || COLOR_VARS.muted;
651
697
  return '<div class="flex items-center gap-2 mt-2 mb-1 px-1">'
652
698
  + '<span class="w-2 h-2 rounded-full" style="background-color: ' + c.fg + ';"></span>'
653
- + '<h2 class="text-sm font-semibold" style="color: var(--pw-text-primary); font-family: var(--pw-font-sans);">' + escapeHtml(meta.zh) + '</h2>'
699
+ + '<h2 class="text-sm font-semibold" data-zh="' + escapeHtml(meta.zh) + '" data-en="' + escapeHtml(meta.en) + '" style="color: var(--pw-text-primary); font-family: var(--pw-font-sans);">' + escapeHtml(meta.zh) + '</h2>'
654
700
  + '<span class="text-xs" style="color: var(--pw-text-tertiary);">(' + count + ')</span>'
655
701
  + '</div>';
656
702
  }
@@ -662,7 +708,7 @@ html.dark {
662
708
  '<summary class="flex items-center gap-2 cursor-pointer px-3 py-2" style="color: var(--pw-text-primary); font-family: var(--pw-font-sans;">'
663
709
  + '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="shrink-0"><polyline points="6 9 12 15 18 9"/></svg>'
664
710
  + '<span class="w-2 h-2 rounded-full" style="background-color: ' + c.fg + ';"></span>'
665
- + '<span class="text-sm font-semibold">' + escapeHtml(meta.zh) + '</span>'
711
+ + '<span class="text-sm font-semibold" data-zh="' + escapeHtml(meta.zh) + '" data-en="' + escapeHtml(meta.en) + '">' + escapeHtml(meta.zh) + '</span>'
666
712
  + '<span class="text-xs" style="color: var(--pw-text-tertiary);">(' + sessions.length + ')</span>'
667
713
  + '<span class="ml-auto text-xs" style="color: var(--pw-text-tertiary);">点击展开 / 折叠</span>'
668
714
  + '</summary>';
@@ -690,11 +736,11 @@ html.dark {
690
736
  var decision = s.audit_decision || null;
691
737
  var decisionBadge = decision
692
738
  ? badgeHtml(decision, verdictColor(decision))
693
- : '<span class="text-xs px-2 py-0.5 rounded-md" style="color: var(--pw-text-tertiary); background-color: var(--pw-bg-hover);">not audited</span>';
739
+ : '<span class="text-xs px-2 py-0.5 rounded-md" style="color: var(--pw-text-tertiary); background-color: var(--pw-bg-hover);">未审计</span>';
694
740
  var stateBadge = finalized
695
- ? badgeHtml('finalized', 'success')
696
- : (isExpired ? badgeHtml('expired', 'warning') : badgeHtml('open', 'warning'));
697
- var auditedBadge = audited ? badgeHtml('audited', 'success') : '';
741
+ ? badgeHtml('已完成', 'success')
742
+ : (isExpired ? badgeHtml('已过期', 'warning') : badgeHtml('进行中', 'warning'));
743
+ var auditedBadge = audited ? badgeHtml('已审计', 'success') : '';
698
744
  var titleText = s.title || s.repo_path || sid;
699
745
 
700
746
  // Verification status summary
@@ -706,21 +752,21 @@ html.dark {
706
752
  // Quick action buttons
707
753
  var actions = '';
708
754
  // safe_direct_summary
709
- actions += quickBtnHtml('summary', 'safe_direct_summary', sid, '查看安全摘要', false, false);
755
+ actions += quickBtnHtml('summary', '查看摘要', 'View summary', sid, '查看脱敏后的安全摘要', 'View the redacted safe summary', false, false);
710
756
  // run_direct_verification_bundle — informational button (links to tasks page)
711
- actions += quickBtnHtml('verify-bundle', 'verify', sid, 'run_direct_verification_bundle:通过任务页创建验证任务(该操作需在任务页发起)', false, false);
757
+ actions += quickBtnHtml('verify-bundle', '创建验证任务', 'Create verification task', sid, '前往任务页创建受控验证任务', 'Open Tasks to create a controlled verification task', false, false);
712
758
  // safe_finalize_direct_session (disabled if already finalized)
713
- actions += quickBtnHtml('finalize', 'safe_finalize_direct_session', sid, 'Finalize 会话', finalized, true);
759
+ actions += quickBtnHtml('finalize', '完成会话', 'Finalize session', sid, '完成并生成会话摘要', 'Finalize and generate the session summary', finalized, true);
714
760
  // safe_audit_direct_session (disabled if already audited)
715
- actions += quickBtnHtml('audit', 'safe_audit_direct_session', sid, '审计会话', audited, true);
761
+ actions += quickBtnHtml('audit', '运行审计', 'Run audit', sid, '运行安全审计', 'Run the security audit', audited, true);
716
762
  // copy session_id
717
- actions += quickBtnHtml('copy', 'copy session_id', sid, '复制 session_id', false, false);
763
+ actions += quickBtnHtml('copy', '复制 ID', 'Copy ID', sid, '复制会话 ID', 'Copy session ID', false, false);
718
764
  // hide (expired only)
719
765
  if (isExpired) {
720
- actions += quickBtnHtml('hide', 'hide', sid, '从列表隐藏', false, false, true);
766
+ actions += quickBtnHtml('hide', '隐藏记录', 'Hide record', sid, '仅从列表隐藏,不删除证据', 'Hide from the list without deleting evidence', false, false, true);
721
767
  }
722
768
 
723
- return '<div class="rounded-lg p-4 transition-colors session-card" data-session-id="' + escapeHtml(sid) + '" '
769
+ return '<div class="rounded-lg p-4 transition-colors session-card" role="button" tabindex="0" aria-label="查看 Direct 会话 ' + escapeHtml(shortId(sid)) + '" data-session-id="' + escapeHtml(sid) + '" '
724
770
  + 'onmouseover="this.style.backgroundColor=\'var(--pw-bg-hover)\'" '
725
771
  + 'onmouseout="this.style.backgroundColor=\'var(--pw-bg-surface)\'" '
726
772
  + 'style="background-color: var(--pw-bg-surface); border: 1px solid var(--pw-border); cursor: pointer;">'
@@ -765,13 +811,13 @@ html.dark {
765
811
  + '</div>';
766
812
  }
767
813
 
768
- function quickBtnHtml(action, label, sid, title, disabled, accent, danger) {
814
+ function quickBtnHtml(action, labelZh, labelEn, sid, titleZh, titleEn, disabled, accent, danger) {
769
815
  var cls = 'pw-quick-btn';
770
816
  if (accent) cls += ' pw-quick-accent';
771
817
  if (danger) cls += ' pw-quick-danger';
772
818
  var disAttr = disabled ? ' disabled' : '';
773
- return '<button type="button" class="' + cls + '" data-action="' + escapeHtml(action) + '" data-session-id="' + escapeHtml(sid) + '" title="' + escapeHtml(title + ' (' + label + ')') + '"' + disAttr + '>'
774
- + escapeHtml(label)
819
+ return '<button type="button" class="' + cls + '" data-action="' + escapeHtml(action) + '" data-session-id="' + escapeHtml(sid) + '" title="' + escapeHtml(titleZh) + '" aria-label="' + escapeHtml(titleZh) + '" data-zh="' + escapeHtml(labelZh) + '" data-en="' + escapeHtml(labelEn) + '"' + disAttr + '>'
820
+ + escapeHtml(labelZh)
775
821
  + '</button>';
776
822
  }
777
823
 
@@ -803,6 +849,13 @@ html.dark {
803
849
  var sid2 = ev.currentTarget.getAttribute('data-session-id');
804
850
  if (sid2) loadSessionDetail(sid2);
805
851
  });
852
+ card.addEventListener('keydown', function (ev) {
853
+ if (ev.key !== 'Enter' && ev.key !== ' ') return;
854
+ if (ev.target.closest('.pw-quick-btn') || ev.target.closest('summary')) return;
855
+ ev.preventDefault();
856
+ var sid2 = ev.currentTarget.getAttribute('data-session-id');
857
+ if (sid2) loadSessionDetail(sid2);
858
+ });
806
859
  }
807
860
  }
808
861
 
@@ -1009,11 +1062,29 @@ html.dark {
1009
1062
  }
1010
1063
 
1011
1064
  // ---------- Network ----------
1012
- function loadSessions() {
1065
+ function updateRepoOptions(data) {
1066
+ var select = $('session-repo');
1067
+ if (!select) return;
1068
+ var selected = select.value;
1069
+ var repos = data && data.filters && data.filters.options && Array.isArray(data.filters.options.repos) ? data.filters.options.repos : [];
1070
+ select.innerHTML = '<option value="">全部项目</option>' + repos.map(function (repo) { return '<option value="' + escapeHtml(repo) + '">' + escapeHtml(repo) + '</option>'; }).join('');
1071
+ select.value = repos.indexOf(selected) >= 0 ? selected : '';
1072
+ }
1073
+
1074
+ function loadSessions(cursor, append) {
1013
1075
  clearError();
1014
- showLoading();
1076
+ if (!append) showLoading();
1015
1077
  setRefreshing(true);
1016
- fetch(API_URL, { method: 'GET', headers: { 'Accept': 'application/json' }, cache: 'no-store' })
1078
+ var state = $('session-state') ? $('session-state').value : 'active';
1079
+ var params = ['state=' + encodeURIComponent(state), 'limit=50'];
1080
+ var repo = $('session-repo') ? $('session-repo').value : '';
1081
+ var dateFrom = $('session-date-from') ? $('session-date-from').value : '';
1082
+ var dateTo = $('session-date-to') ? $('session-date-to').value : '';
1083
+ if (repo) params.push('repo_path=' + encodeURIComponent(repo));
1084
+ if (dateFrom) params.push('date_from=' + encodeURIComponent(dateFrom));
1085
+ if (dateTo) params.push('date_to=' + encodeURIComponent(dateTo));
1086
+ if (cursor) params.push('cursor=' + encodeURIComponent(cursor));
1087
+ fetch(API_URL + '?' + params.join('&'), { method: 'GET', headers: { 'Accept': 'application/json' }, cache: 'no-store' })
1017
1088
  .then(function (res) {
1018
1089
  if (!res.ok) {
1019
1090
  return res.text().then(function (body) {
@@ -1024,7 +1095,23 @@ html.dark {
1024
1095
  })
1025
1096
  .then(function (data) {
1026
1097
  data = data || {};
1027
- renderSessions(data.sessions || []);
1098
+ updateRepoOptions(data);
1099
+ if (data.direct_profile_enabled === false) {
1100
+ loadedSessions = append ? loadedSessions.concat(data.sessions || []) : (data.sessions || []);
1101
+ nextCursor = data.nextCursor || data.next_cursor || null;
1102
+ if (loadedSessions.length) {
1103
+ renderSessions(loadedSessions);
1104
+ show($('direct-disabled'));
1105
+ nextCursor ? show($('sessions-more-wrap')) : hide($('sessions-more-wrap'));
1106
+ } else {
1107
+ showDirectDisabled();
1108
+ }
1109
+ } else {
1110
+ loadedSessions = append ? loadedSessions.concat(data.sessions || []) : (data.sessions || []);
1111
+ nextCursor = data.nextCursor || data.next_cursor || null;
1112
+ renderSessions(loadedSessions);
1113
+ nextCursor ? show($('sessions-more-wrap')) : hide($('sessions-more-wrap'));
1114
+ }
1028
1115
  setLastUpdated();
1029
1116
  })
1030
1117
  .catch(function (err) {
@@ -1080,9 +1167,17 @@ html.dark {
1080
1167
 
1081
1168
  // ---------- Event wiring ----------
1082
1169
  var refreshBtn = $('refreshBtn');
1083
- if (refreshBtn) refreshBtn.addEventListener('click', loadSessions);
1170
+ if (refreshBtn) refreshBtn.addEventListener('click', function () { loadSessions(null, false); });
1084
1171
  var retryBtn = $('retry-btn');
1085
- if (retryBtn) retryBtn.addEventListener('click', loadSessions);
1172
+ if (retryBtn) retryBtn.addEventListener('click', function () { loadSessions(null, false); });
1173
+ var stateSelect = $('session-state');
1174
+ if (stateSelect) stateSelect.addEventListener('change', function () { loadSessions(null, false); });
1175
+ ['session-repo', 'session-date-from', 'session-date-to'].forEach(function (id) {
1176
+ var input = $(id);
1177
+ if (input) input.addEventListener('change', function () { loadSessions(null, false); });
1178
+ });
1179
+ var moreBtn = $('sessions-more');
1180
+ if (moreBtn) moreBtn.addEventListener('click', function () { if (nextCursor) loadSessions(nextCursor, true); });
1086
1181
 
1087
1182
  var modalClose = $('safe-modal-close');
1088
1183
  if (modalClose) modalClose.addEventListener('click', closeSafeModal);
@@ -1097,8 +1192,10 @@ html.dark {
1097
1192
  });
1098
1193
 
1099
1194
  // Initial load
1100
- loadSessions();
1195
+ loadSessions(null, false);
1101
1196
  })();
1102
1197
  </script>
1198
+ <script src="/i18n.js"></script>
1199
+ <script src="/desktop-bridge.js"></script>
1103
1200
  </body>
1104
1201
  </html>
@@ -0,0 +1,43 @@
1
+ <!doctype html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width,initial-scale=1">
6
+ <title>PatchWarden</title>
7
+ <link rel="stylesheet" href="/colors_and_type.css">
8
+ <script src="/desktop-bootstrap.js"></script>
9
+ <link rel="stylesheet" href="/desktop.css">
10
+ <script src="/vendor/lucide.js"></script>
11
+ </head>
12
+ <body class="pw-app-shell">
13
+ <aside class="pw-sidebar">
14
+ <div class="pw-brand"><i data-lucide="shield-check"></i><strong>PatchWarden</strong></div>
15
+ <nav>
16
+ <a href="/pages/getting-started.html" data-nav-key="getting-started" data-active="true"><i data-lucide="rocket"></i><span data-i18n="nav.gettingStarted"></span></a>
17
+ <a href="/pages/dashboard.html" data-nav-key="dashboard"><i data-lucide="layout-dashboard"></i><span data-i18n="nav.dashboard"></span></a>
18
+ <a href="/pages/tasks.html" data-nav-key="tasks"><i data-lucide="list-checks"></i><span data-i18n="nav.tasks"></span></a>
19
+ <a href="/pages/workspace.html" data-nav-key="workspace"><i data-lucide="folder"></i><span data-i18n="nav.workspace"></span></a>
20
+ <a href="/pages/audit.html" data-nav-key="audit"><i data-lucide="file-search"></i><span data-i18n="nav.audit"></span></a>
21
+ <a href="/pages/direct-sessions.html" data-nav-key="direct-sessions"><i data-lucide="route"></i><span data-i18n="nav.direct"></span></a>
22
+ <a href="/pages/logs.html" data-nav-key="logs"><i data-lucide="scroll-text"></i><span data-i18n="nav.logs"></span></a>
23
+ <a href="/pages/settings.html" data-nav-key="settings" class="desktop-only-nav"><i data-lucide="settings"></i><span data-i18n="nav.settings"></span></a>
24
+ </nav>
25
+ </aside>
26
+ <div class="pw-page">
27
+ <header class="pw-page-header"><div><h1 data-i18n="home.title"></h1><p data-i18n="home.subtitle"></p></div><select id="language" aria-label="Language"><option value="system" data-i18n="language.system"></option><option value="zh-CN" data-i18n="language.zh"></option><option value="en" data-i18n="language.en"></option></select></header>
28
+ <main class="getting-started-main">
29
+ <div class="readiness-list">
30
+ <section class="readiness-item" data-check="workspace"><span class="check-index">1</span><div><div class="check-heading"><h2 data-i18n="home.workspace"></h2><span class="check-status" data-status>…</span></div><p data-message data-i18n="home.checking"></p><p class="reason-detail" data-reason></p></div><a class="primary-command" href="/pages/settings.html" data-i18n="home.openSettings"></a></section>
31
+ <section class="readiness-item" data-check="core"><span class="check-index">2</span><div><div class="check-heading"><h2 data-i18n="home.core"></h2><span class="check-status" data-status>…</span></div><p data-message data-i18n="home.checking"></p><p class="reason-detail" data-reason></p></div><button class="primary-command" id="startCore" data-i18n="home.startCore"></button></section>
32
+ <section class="readiness-item" data-check="tunnel"><span class="check-index">3</span><div><div class="check-heading"><h2 data-i18n="home.platform"></h2><span class="check-status" data-status>…</span></div><p data-message data-i18n="home.checking"></p><p class="reason-detail" data-reason></p></div><a class="primary-command" href="https://platform.openai.com/settings/organization/tunnels" target="_blank" rel="noreferrer" data-i18n="home.openPlatform"></a></section>
33
+ <section class="readiness-item" data-check="chatgpt"><span class="check-index">4</span><div><div class="check-heading"><h2 data-i18n="home.chatgpt"></h2><span class="check-status manual" data-status data-i18n="home.manual"></span></div><p data-message data-i18n="home.chatgptManual"></p><p class="reason-detail" data-reason></p></div><button class="primary-command" id="copyPrompt" data-i18n="home.copyPrompt"></button></section>
34
+ </div>
35
+ <section class="guide-band"><h2 data-i18n="home.docs"></h2><div class="guide-links"><a href="https://developers.openai.com/api/docs/guides/secure-mcp-tunnels" target="_blank" rel="noreferrer" data-i18n="home.secureGuide"></a><a href="https://chatgpt.com/#settings/Plugins" target="_blank" rel="noreferrer" data-i18n="home.pluginsGuide"></a><a href="https://github.com/jiezeng2004-design/PatchWarden/blob/main/README.md" target="_blank" rel="noreferrer" data-i18n="home.readmeZh"></a><a href="https://github.com/jiezeng2004-design/PatchWarden/blob/main/README.en.md" target="_blank" rel="noreferrer" data-i18n="home.readmeEn"></a></div></section>
36
+ <div id="toast" class="pw-toast" role="status" aria-live="polite"></div>
37
+ </main>
38
+ </div>
39
+ <script src="/i18n.js"></script>
40
+ <script src="/desktop-bridge.js"></script>
41
+ <script src="/getting-started.js"></script>
42
+ </body>
43
+ </html>
@@ -95,13 +95,16 @@ html.dark {
95
95
  }
96
96
  </style>
97
97
  <script src="/vendor/tailwindcss-browser.js"></script>
98
- <script src="/vendor/lucide.js"></script>
98
+ <script src="/vendor/lucide.js"></script>
99
+ <script src="/log-parser.js"></script>
99
100
  <style>
100
101
  .no-scrollbar::-webkit-scrollbar { display: none; }
101
102
  .no-scrollbar { -ms-overflow-style: none; scrollbar-width: none; }
102
103
  .pw-spin { animation: pw-spin 1s linear infinite; }
103
104
  @keyframes pw-spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
104
105
  </style>
106
+ <script src="/desktop-bootstrap.js"></script>
107
+ <link rel="stylesheet" href="/desktop.css">
105
108
  </head>
106
109
  <body class="flex h-screen overflow-hidden">
107
110
 
@@ -119,6 +122,10 @@ html.dark {
119
122
 
120
123
  <!-- Navigation -->
121
124
  <nav class="flex-1 flex flex-col gap-1 px-3 pt-4 overflow-y-auto">
125
+ <a href="/pages/getting-started.html" data-nav-key="getting-started" class="desktop-only-nav flex items-center gap-3 px-3 py-2.5 rounded-lg text-sm font-medium transition-colors truncate" style="color: var(--pw-text-secondary); font-family: var(--pw-font-sans);">
126
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="shrink-0"><path d="M4.5 16.5c-1.5 1.26-2 5-2 5s3.74-.5 5-2c.71-.84.7-2.13-.09-2.91a2.18 2.18 0 0 0-2.91-.09z"/><path d="m12 15-3-3a22 22 0 0 1 2-3.95A12.87 12.87 0 0 1 22 2c0 2.72-.78 7.5-6.05 11a22.35 22.35 0 0 1-3.95 2z"/></svg>
127
+ <span class="truncate" data-i18n="nav.gettingStarted">开始使用</span>
128
+ </a>
122
129
 
123
130
  <a href="/pages/dashboard.html" data-nav-key="dashboard" data-dom-id="nav-dashboard" class="flex items-center gap-3 px-3 py-2.5 rounded-lg text-sm font-medium transition-colors truncate" style="color: var(--pw-text-secondary); font-family: var(--pw-font-sans);">
124
131
  <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="shrink-0">
@@ -174,6 +181,10 @@ html.dark {
174
181
  <span class="truncate" data-zh="日志" data-en="Logs">日志</span>
175
182
  </a>
176
183
 
184
+ <a href="/pages/settings.html" data-nav-key="settings" class="desktop-only-nav flex items-center gap-3 px-3 py-2.5 rounded-lg text-sm font-medium transition-colors truncate" style="color: var(--pw-text-secondary); font-family: var(--pw-font-sans);">
185
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="shrink-0"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.7 1.7 0 0 0 .3 1.9l.1.1-2.8 2.8-.1-.1a1.7 1.7 0 0 0-1.9-.3 1.7 1.7 0 0 0-1 1.6v.2h-4V21a1.7 1.7 0 0 0-1-1.6 1.7 1.7 0 0 0-1.9.3l-.1.1L4.2 17l.1-.1a1.7 1.7 0 0 0 .3-1.9A1.7 1.7 0 0 0 3 14H2.8v-4H3a1.7 1.7 0 0 0 1.6-1 1.7 1.7 0 0 0-.3-1.9L4.2 7 7 4.2l.1.1A1.7 1.7 0 0 0 9 4.6 1.7 1.7 0 0 0 10 3V2.8h4V3a1.7 1.7 0 0 0 1 1.6 1.7 1.7 0 0 0 1.9-.3l.1-.1L19.8 7l-.1.1a1.7 1.7 0 0 0-.3 1.9A1.7 1.7 0 0 0 21 10h.2v4H21a1.7 1.7 0 0 0-1.6 1z"/></svg>
186
+ <span class="truncate" data-i18n="nav.settings">设置</span>
187
+ </a>
177
188
  </nav>
178
189
 
179
190
  <!-- Bottom Section -->
@@ -211,6 +222,11 @@ html.dark {
211
222
  <option value="300">tail 300</option>
212
223
  <option value="1000">tail 1000</option>
213
224
  </select>
225
+ <button id="clearView" class="px-2.5 py-1 rounded-md text-xs font-medium" type="button" title="仅清空当前显示,不删除日志文件" style="background-color: var(--pw-bg-surface); color: var(--pw-text-secondary); border: 1px solid var(--pw-border-subtle); font-family: var(--pw-font-sans);">清空视图</button>
226
+ <select id="levelFilter" aria-label="日志级别" class="px-2 py-1 rounded-md text-xs font-medium" style="background-color: var(--pw-bg-surface); color: var(--pw-text-secondary); border: 1px solid var(--pw-border-subtle);">
227
+ <option value="">全部级别</option><option value="error">Error</option><option value="warn">Warning</option><option value="info">Info</option>
228
+ </select>
229
+ <input id="logSearch" type="search" aria-label="搜索日志" placeholder="搜索日志" class="px-2 py-1 rounded-md text-xs outline-none" style="width: 140px; background-color: var(--pw-bg-surface); color: var(--pw-text-primary); border: 1px solid var(--pw-border-subtle);">
214
230
  <!-- Auto-refresh toggle -->
215
231
  <label class="flex items-center gap-1.5 text-xs cursor-pointer select-none" style="color: var(--pw-text-secondary); font-family: var(--pw-font-sans);">
216
232
  <input id="autoRefresh" type="checkbox" style="accent-color: var(--pw-accent);" />
@@ -260,20 +276,20 @@ html.dark {
260
276
  <span id="stdoutMeta" class="text-xs whitespace-nowrap" style="color: var(--pw-text-tertiary); font-family: var(--pw-font-mono);">—</span>
261
277
  </div>
262
278
  <div id="stdoutEmpty" class="px-4 py-8 text-center text-sm" style="color: var(--pw-text-tertiary); font-family: var(--pw-font-sans);" data-zh="暂无 stdout 日志" data-en="No stdout log">暂无 stdout 日志</div>
263
- <pre id="stdoutContent" class="hidden px-4 py-3 text-xs leading-relaxed overflow-x-auto whitespace-pre-wrap break-all" style="background-color: #0d1117; color: var(--pw-text-secondary); font-family: var(--pw-font-mono); max-height: 480px; overflow-y: auto;"></pre>
279
+ <div id="stdoutContent" class="hidden log-list"></div>
264
280
  </section>
265
281
 
266
282
  <!-- stderr -->
267
283
  <section class="rounded-lg overflow-hidden" style="background-color: var(--pw-bg-surface); border: 1px solid var(--pw-border);">
268
284
  <div class="flex items-center justify-between px-4 py-3" style="border-bottom: 1px solid var(--pw-border);">
269
285
  <div class="flex items-center gap-2">
270
- <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="var(--pw-state-error)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>
286
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="var(--pw-text-secondary)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="4 17 10 11 4 5"/><line x1="12" y1="19" x2="20" y2="19"/></svg>
271
287
  <h2 class="text-sm font-semibold" style="color: var(--pw-text-primary); font-family: var(--pw-font-sans);">stderr</h2>
272
288
  </div>
273
289
  <span id="stderrMeta" class="text-xs whitespace-nowrap" style="color: var(--pw-text-tertiary); font-family: var(--pw-font-mono);">—</span>
274
290
  </div>
275
291
  <div id="stderrEmpty" class="px-4 py-8 text-center text-sm" style="color: var(--pw-text-tertiary); font-family: var(--pw-font-sans);" data-zh="暂无 stderr 日志" data-en="No stderr log">暂无 stderr 日志</div>
276
- <pre id="stderrContent" class="hidden px-4 py-3 text-xs leading-relaxed overflow-x-auto whitespace-pre-wrap break-all" style="background-color: #0d1117; color: var(--pw-state-error); font-family: var(--pw-font-mono); max-height: 480px; overflow-y: auto;"></pre>
292
+ <div id="stderrContent" class="hidden log-list"></div>
277
293
  </section>
278
294
 
279
295
  </div>
@@ -309,6 +325,19 @@ html.dark {
309
325
  background-color: var(--pw-accent-muted);
310
326
  color: var(--pw-accent);
311
327
  }
328
+ .log-list { max-height: 480px; overflow-y: auto; background: #0d1117; }
329
+ .log-row { border-bottom: 1px solid var(--pw-border-subtle); color: var(--pw-text-secondary); font-family: var(--pw-font-mono); }
330
+ .log-row:last-child { border-bottom: 0; }
331
+ .log-row > summary { display: grid; grid-template-columns: minmax(132px,auto) 64px minmax(90px,140px) minmax(180px,1fr) auto; gap: 10px; align-items: center; padding: 9px 12px; cursor: pointer; list-style: none; }
332
+ .log-row > summary::-webkit-details-marker { display: none; }
333
+ .log-time,.log-component,.log-summary { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
334
+ .log-level { font-weight: 700; text-transform: uppercase; }
335
+ .log-row[data-level="error"] .log-level { color: var(--pw-state-error); }
336
+ .log-row[data-level="warn"] .log-level { color: var(--pw-state-warning); }
337
+ .log-row[data-level="info"] .log-level { color: var(--pw-state-info); }
338
+ .log-detail { margin: 0; padding: 10px 12px; color: var(--pw-text-tertiary); background: var(--pw-code-bg); white-space: pre-wrap; word-break: break-all; }
339
+ .copy-log-line { padding: 3px 7px; border: 1px solid var(--pw-border-subtle); border-radius: 4px; color: var(--pw-text-secondary); background: var(--pw-bg-hover); font: 11px var(--pw-font-sans); }
340
+ @media (max-width: 900px) { .log-row > summary { grid-template-columns: 92px 52px minmax(0,1fr) auto; }.log-component { display:none; } }
312
341
  </style>
313
342
 
314
343
  <script>lucide.createIcons();</script>
@@ -381,37 +410,57 @@ html.dark {
381
410
  }
382
411
  }
383
412
 
413
+ var lastLogData = null;
414
+ var visibleLogLines = {};
415
+ function parseLogEntries(value, stream) {
416
+ var query = $('logSearch') ? $('logSearch').value.trim().toLowerCase() : '';
417
+ var level = $('levelFilter') ? $('levelFilter').value : '';
418
+ return String(value || '').split(/\r?\n/).filter(Boolean).map(function (line) {
419
+ var parsed = window.PatchWardenLogParser.parseLine(line, stream);
420
+ if (level && parsed.level !== level) return null;
421
+ var searchable = [parsed.time, parsed.level, parsed.component, parsed.summary, line].join(' ').toLowerCase();
422
+ if (query && searchable.indexOf(query) < 0) return null;
423
+ return parsed;
424
+ }).filter(Boolean);
425
+ }
426
+
427
+ function renderLogRows(container, emptyState, value, stream) {
428
+ var entries = parseLogEntries(value, stream);
429
+ if (!entries.length) { hide(container); show(emptyState); return 0; }
430
+ container.innerHTML = entries.map(function (entry, index) {
431
+ var copyId = stream + '-' + index;
432
+ visibleLogLines[copyId] = entry.raw;
433
+ return '<details class="log-row" data-level="' + escapeHtml(entry.level || 'unknown') + '">'
434
+ + '<summary><span class="log-time" title="' + escapeHtml(entry.time || '无时间字段') + '">' + escapeHtml(entry.time || '—') + '</span>'
435
+ + '<span class="log-level">' + escapeHtml(entry.level || 'raw') + '</span>'
436
+ + '<span class="log-component" title="' + escapeHtml(entry.component) + '">' + escapeHtml(entry.component) + '</span>'
437
+ + '<span class="log-summary" title="' + escapeHtml(entry.summary) + '">' + escapeHtml(entry.summary) + '</span>'
438
+ + '<button type="button" class="copy-log-line" data-copy-id="' + copyId + '" title="复制脱敏日志行" aria-label="复制脱敏日志行" data-zh="复制" data-en="Copy">复制</button></summary>'
439
+ + '<pre class="log-detail">' + escapeHtml(entry.detail) + '</pre></details>';
440
+ }).join('');
441
+ show(container); hide(emptyState);
442
+ return entries.length;
443
+ }
444
+
384
445
  function renderLogs(data) {
446
+ lastLogData = data || {};
385
447
  var stdout = (data && data.stdout) || '';
386
448
  var stderr = (data && data.stderr) || '';
449
+ visibleLogLines = {};
387
450
  var reason = (data && data.reason) || null;
388
451
  var tail = (data && data.tail) || currentTail;
389
452
 
390
453
  // stdout
391
454
  var stdoutEl = $('stdoutContent');
392
455
  var stdoutEmpty = $('stdoutEmpty');
393
- if (stdout) {
394
- text(stdoutEl, stdout);
395
- show(stdoutEl);
396
- hide(stdoutEmpty);
397
- } else {
398
- hide(stdoutEl);
399
- show(stdoutEmpty);
400
- }
401
- text($('stdoutMeta'), 'tail=' + tail + (reason ? ' · ' + reason : ''));
456
+ var stdoutCount = renderLogRows(stdoutEl, stdoutEmpty, stdout, 'stdout');
457
+ text($('stdoutMeta'), (data && data.historical_snapshot ? '历史日志 · ' : '') + stdoutCount + ' 行 · tail=' + tail + (reason ? ' · ' + reason : ''));
402
458
 
403
459
  // stderr
404
460
  var stderrEl = $('stderrContent');
405
461
  var stderrEmpty = $('stderrEmpty');
406
- if (stderr) {
407
- text(stderrEl, stderr);
408
- show(stderrEl);
409
- hide(stderrEmpty);
410
- } else {
411
- hide(stderrEl);
412
- show(stderrEmpty);
413
- }
414
- text($('stderrMeta'), 'tail=' + tail + (reason ? ' · ' + reason : ''));
462
+ var stderrCount = renderLogRows(stderrEl, stderrEmpty, stderr, 'stderr');
463
+ text($('stderrMeta'), (data && data.historical_snapshot ? '历史日志 · ' : '') + stderrCount + ' 行 · tail=' + tail + (reason ? ' · ' + reason : ''));
415
464
  }
416
465
 
417
466
  function loadLogs() {
@@ -443,6 +492,15 @@ html.dark {
443
492
  });
444
493
  }
445
494
 
495
+ function clearLogView() {
496
+ clearError();
497
+ text($('stdoutMeta'), '已清空当前视图');
498
+ text($('stderrMeta'), '日志文件未删除');
499
+ $('stdoutContent').innerHTML = ''; $('stderrContent').innerHTML = '';
500
+ show($('stdoutEmpty')); show($('stderrEmpty'));
501
+ hide($('stdoutContent')); hide($('stderrContent'));
502
+ }
503
+
446
504
  // ---------- Auto-refresh ----------
447
505
  function startAutoRefresh() {
448
506
  stopAutoRefresh();
@@ -492,11 +550,36 @@ html.dark {
492
550
 
493
551
  var refreshBtn = $('refreshBtn');
494
552
  if (refreshBtn) refreshBtn.addEventListener('click', loadLogs);
553
+ var clearView = $('clearView');
554
+ if (clearView) clearView.addEventListener('click', clearLogView);
555
+ var levelFilter = $('levelFilter');
556
+ if (levelFilter) levelFilter.addEventListener('change', function () { if (lastLogData) renderLogs(lastLogData); });
557
+ var logSearch = $('logSearch');
558
+ if (logSearch) logSearch.addEventListener('input', function () { if (lastLogData) renderLogs(lastLogData); });
559
+ ['stdoutContent', 'stderrContent'].forEach(function (id) {
560
+ var container = $(id);
561
+ if (!container) return;
562
+ container.addEventListener('click', function (event) {
563
+ var button = event.target.closest && event.target.closest('.copy-log-line');
564
+ if (!button) return;
565
+ event.preventDefault();
566
+ event.stopPropagation();
567
+ var line = visibleLogLines[button.getAttribute('data-copy-id')] || '';
568
+ if (navigator.clipboard && navigator.clipboard.writeText) {
569
+ navigator.clipboard.writeText(line).then(function () {
570
+ button.textContent = '已复制';
571
+ setTimeout(function () { button.textContent = '复制'; }, 1200);
572
+ });
573
+ }
574
+ });
575
+ });
495
576
 
496
577
  // ---------- Initial load ----------
497
578
  setCategory(DEFAULT_CATEGORY);
498
579
  loadLogs();
499
580
  })();
500
581
  </script>
582
+ <script src="/i18n.js"></script>
583
+ <script src="/desktop-bridge.js"></script>
501
584
  </body>
502
585
  </html>