patchwarden 1.5.1 → 1.6.2

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 (591) hide show
  1. package/PatchWarden.cmd +32 -2
  2. package/README.en.md +158 -26
  3. package/README.md +124 -24
  4. package/dist/assessments/agentAssessor.js +21 -12
  5. package/dist/assessments/assessmentDiagnostics.d.ts +29 -0
  6. package/dist/assessments/assessmentDiagnostics.js +132 -0
  7. package/dist/assessments/assessmentStore.d.ts +39 -0
  8. package/dist/assessments/assessmentStore.js +207 -45
  9. package/dist/assessments/securitySnapshot.d.ts +50 -0
  10. package/dist/assessments/securitySnapshot.js +158 -0
  11. package/dist/config.d.ts +15 -0
  12. package/dist/config.js +66 -13
  13. package/dist/control/fileManager.d.ts +2 -0
  14. package/dist/control/fileManager.js +39 -0
  15. package/dist/control/middleware/auth.d.ts +6 -0
  16. package/dist/control/middleware/auth.js +14 -5
  17. package/dist/control/middleware/static.js +16 -3
  18. package/dist/control/routeTable.d.ts +24 -0
  19. package/dist/control/routeTable.js +263 -0
  20. package/dist/control/routes/audit.js +17 -7
  21. package/dist/control/routes/evidence.js +1 -1
  22. package/dist/control/routes/lineage.js +1 -1
  23. package/dist/control/routes/process.d.ts +7 -0
  24. package/dist/control/routes/process.js +329 -42
  25. package/dist/control/routes/sessions.d.ts +9 -1
  26. package/dist/control/routes/sessions.js +84 -21
  27. package/dist/control/routes/status.d.ts +60 -1
  28. package/dist/control/routes/status.js +94 -15
  29. package/dist/control/routes/taskActions.js +24 -60
  30. package/dist/control/routes/tasks.d.ts +4 -0
  31. package/dist/control/routes/tasks.js +61 -62
  32. package/dist/control/routes/workspace.js +50 -23
  33. package/dist/control/runtime.d.ts +3 -2
  34. package/dist/control/runtime.js +65 -40
  35. package/dist/control/server.js +37 -365
  36. package/dist/control/shared.d.ts +25 -1
  37. package/dist/control/shared.js +64 -11
  38. package/dist/diagnostics/allowedTestCommandSafety.d.ts +1 -0
  39. package/dist/diagnostics/allowedTestCommandSafety.js +11 -0
  40. package/dist/direct/directAudit.js +234 -225
  41. package/dist/direct/directGuards.d.ts +2 -1
  42. package/dist/direct/directGuards.js +42 -29
  43. package/dist/direct/directPatch.d.ts +6 -1
  44. package/dist/direct/directPatch.js +55 -8
  45. package/dist/direct/directSessionStore.d.ts +16 -6
  46. package/dist/direct/directSessionStore.js +128 -35
  47. package/dist/direct/directVerification.js +8 -16
  48. package/dist/doctor.js +65 -57
  49. package/dist/goal/acceptanceEngine.d.ts +1 -1
  50. package/dist/goal/goalProgress.js +75 -69
  51. package/dist/goal/goalReport.js +5 -12
  52. package/dist/goal/goalStatus.d.ts +2 -0
  53. package/dist/goal/goalStatus.js +10 -3
  54. package/dist/goal/goalStore.d.ts +9 -2
  55. package/dist/goal/goalStore.js +152 -45
  56. package/dist/goal/handoffExport.js +6 -6
  57. package/dist/goal/specKitImport.js +97 -96
  58. package/dist/goal/subgoalSync.js +11 -9
  59. package/dist/goal/worktreeManager.d.ts +2 -0
  60. package/dist/goal/worktreeManager.js +209 -136
  61. package/dist/httpServer.js +111 -32
  62. package/dist/logging.js +5 -3
  63. package/dist/policy/projectPolicy.js +5 -2
  64. package/dist/release/releaseGate.d.ts +1 -1
  65. package/dist/release/releaseGate.js +78 -25
  66. package/dist/runner/agentInvocation.d.ts +14 -0
  67. package/dist/runner/agentInvocation.js +96 -4
  68. package/dist/runner/changeCapture.d.ts +13 -5
  69. package/dist/runner/changeCapture.js +243 -61
  70. package/dist/runner/postTaskCleanup.js +48 -19
  71. package/dist/runner/processSecurity.d.ts +56 -0
  72. package/dist/runner/processSecurity.js +373 -0
  73. package/dist/runner/runTask.d.ts +2 -1
  74. package/dist/runner/runTask.js +162 -93
  75. package/dist/runner/simpleProcess.d.ts +3 -0
  76. package/dist/runner/simpleProcess.js +50 -36
  77. package/dist/{taskProgress.d.ts → runner/taskProgress.d.ts} +1 -1
  78. package/dist/{taskProgress.js → runner/taskProgress.js} +2 -2
  79. package/dist/{taskRuntime.d.ts → runner/taskRuntime.d.ts} +3 -1
  80. package/dist/{taskRuntime.js → runner/taskRuntime.js} +3 -2
  81. package/dist/runner/taskStatusStore.d.ts +11 -0
  82. package/dist/runner/taskStatusStore.js +35 -0
  83. package/dist/runner/watch.d.ts +14 -1
  84. package/dist/runner/watch.js +320 -49
  85. package/dist/security/contentRedaction.js +2 -2
  86. package/dist/security/discoveryTokenStore.d.ts +3 -2
  87. package/dist/security/discoveryTokenStore.js +17 -2
  88. package/dist/security/loopbackHost.d.ts +2 -0
  89. package/dist/security/loopbackHost.js +16 -0
  90. package/dist/security/pathGuard.js +32 -17
  91. package/dist/security/planGuard.js +2 -1
  92. package/dist/security/runtimeGuard.js +9 -6
  93. package/dist/security/secretComparison.d.ts +3 -0
  94. package/dist/security/secretComparison.js +11 -0
  95. package/dist/security/sensitiveGuard.d.ts +5 -0
  96. package/dist/security/sensitiveGuard.js +27 -8
  97. package/dist/security/toolInvocationGuard.d.ts +2 -2
  98. package/dist/security/toolInvocationGuard.js +1 -1
  99. package/dist/security/workspaceRootGuard.d.ts +8 -0
  100. package/dist/security/workspaceRootGuard.js +47 -0
  101. package/dist/smoke-test.js +27 -26
  102. package/dist/tools/catalog/index.d.ts +4 -0
  103. package/dist/tools/catalog/index.js +4 -0
  104. package/dist/tools/{toolCatalog.js → catalog/toolCatalog.js} +3 -12
  105. package/dist/tools/{toolRegistry.d.ts → catalog/toolRegistry.d.ts} +3 -3
  106. package/dist/tools/{toolRegistry.js → catalog/toolRegistry.js} +6 -15
  107. package/dist/tools/definitions/toolDefs.d.ts +18 -0
  108. package/dist/tools/definitions/toolDefs.js +1177 -0
  109. package/dist/tools/{auditSession.d.ts → diagnostics/auditSession.d.ts} +1 -1
  110. package/dist/tools/{auditSession.js → diagnostics/auditSession.js} +2 -2
  111. package/dist/tools/{auditTask.d.ts → diagnostics/auditTask.d.ts} +2 -1
  112. package/dist/tools/{auditTask.js → diagnostics/auditTask.js} +134 -36
  113. package/dist/tools/{healthCheck.d.ts → diagnostics/healthCheck.d.ts} +18 -6
  114. package/dist/tools/{healthCheck.js → diagnostics/healthCheck.js} +36 -8
  115. package/dist/tools/diagnostics/index.d.ts +6 -0
  116. package/dist/tools/diagnostics/index.js +6 -0
  117. package/dist/tools/{safeStatus.d.ts → diagnostics/safeStatus.d.ts} +3 -3
  118. package/dist/tools/{safeStatus.js → diagnostics/safeStatus.js} +6 -6
  119. package/dist/tools/{safeViews.d.ts → diagnostics/safeViews.d.ts} +12 -8
  120. package/dist/tools/{safeViews.js → diagnostics/safeViews.js} +23 -13
  121. package/dist/tools/{schemaDriftCheck.d.ts → diagnostics/schemaDriftCheck.d.ts} +1 -1
  122. package/dist/tools/{schemaDriftCheck.js → diagnostics/schemaDriftCheck.js} +2 -2
  123. package/dist/tools/{createDirectSession.js → direct/createDirectSession.js} +6 -6
  124. package/dist/tools/{finalizeDirectSession.d.ts → direct/finalizeDirectSession.d.ts} +1 -1
  125. package/dist/tools/direct/finalizeDirectSession.js +86 -0
  126. package/dist/tools/direct/index.d.ts +3 -0
  127. package/dist/tools/direct/index.js +3 -0
  128. package/dist/tools/{runDirectVerificationBundle.js → direct/runDirectVerificationBundle.js} +2 -2
  129. package/dist/tools/{discoverTools.d.ts → discovery/discoverTools.d.ts} +2 -2
  130. package/dist/tools/{discoverTools.js → discovery/discoverTools.js} +5 -5
  131. package/dist/tools/{explainTool.d.ts → discovery/explainTool.d.ts} +2 -2
  132. package/dist/tools/{explainTool.js → discovery/explainTool.js} +2 -2
  133. package/dist/tools/discovery/index.d.ts +3 -0
  134. package/dist/tools/discovery/index.js +3 -0
  135. package/dist/tools/{invokeDiscoveredTool.d.ts → discovery/invokeDiscoveredTool.d.ts} +2 -2
  136. package/dist/tools/{invokeDiscoveredTool.js → discovery/invokeDiscoveredTool.js} +5 -5
  137. package/dist/tools/dispatch/coreDispatch.js +32 -31
  138. package/dist/tools/dispatch/diagnosticDispatch.d.ts +3 -5
  139. package/dist/tools/dispatch/diagnosticDispatch.js +70 -68
  140. package/dist/tools/dispatch/directDispatch.js +11 -10
  141. package/dist/tools/dispatch/goalDispatch.js +5 -4
  142. package/dist/tools/dispatch/releaseDispatch.js +3 -2
  143. package/dist/tools/dispatch/validation.d.ts +7 -0
  144. package/dist/tools/dispatch/validation.js +71 -0
  145. package/dist/tools/{getPlan.js → goals/getPlan.js} +4 -4
  146. package/dist/tools/{goalSubgoalTask.d.ts → goals/goalSubgoalTask.d.ts} +1 -1
  147. package/dist/tools/goals/goalSubgoalTask.js +118 -0
  148. package/dist/tools/goals/index.d.ts +3 -0
  149. package/dist/tools/goals/index.js +3 -0
  150. package/dist/tools/{savePlan.js → goals/savePlan.js} +28 -8
  151. package/dist/tools/registry.d.ts +7 -11
  152. package/dist/tools/registry.js +17 -1178
  153. package/dist/tools/{checkReleaseGate.d.ts → release/checkReleaseGate.d.ts} +1 -1
  154. package/dist/tools/{checkReleaseGate.js → release/checkReleaseGate.js} +3 -3
  155. package/dist/tools/release/index.d.ts +2 -0
  156. package/dist/tools/release/index.js +2 -0
  157. package/dist/tools/{releaseMode.d.ts → release/releaseMode.d.ts} +2 -2
  158. package/dist/tools/{releaseMode.js → release/releaseMode.js} +46 -26
  159. package/dist/tools/tasks/cancelTask.d.ts +11 -0
  160. package/dist/tools/tasks/cancelTask.js +86 -0
  161. package/dist/tools/{createTask.d.ts → tasks/createTask.d.ts} +12 -4
  162. package/dist/tools/{createTask.js → tasks/createTask.js} +110 -34
  163. package/dist/tools/{diagnoseTask.d.ts → tasks/diagnoseTask.d.ts} +1 -1
  164. package/dist/tools/{diagnoseTask.js → tasks/diagnoseTask.js} +12 -16
  165. package/dist/tools/{evidencePack.js → tasks/evidencePack.js} +34 -22
  166. package/dist/tools/{getTaskFile.js → tasks/getTaskFile.js} +4 -4
  167. package/dist/tools/{getTaskStatus.d.ts → tasks/getTaskStatus.d.ts} +6 -1
  168. package/dist/tools/{getTaskStatus.js → tasks/getTaskStatus.js} +11 -6
  169. package/dist/tools/{getTaskStdoutTail.js → tasks/getTaskStdoutTail.js} +13 -11
  170. package/dist/tools/{getTaskSummary.js → tasks/getTaskSummary.js} +35 -36
  171. package/dist/tools/tasks/index.d.ts +18 -0
  172. package/dist/tools/tasks/index.js +18 -0
  173. package/dist/tools/tasks/killTask.d.ts +1 -0
  174. package/dist/tools/{listTasks.d.ts → tasks/listTasks.d.ts} +1 -1
  175. package/dist/tools/{listTasks.js → tasks/listTasks.js} +5 -4
  176. package/dist/tools/{reconcileTasks.d.ts → tasks/reconcileTasks.d.ts} +3 -2
  177. package/dist/tools/tasks/reconcileTasks.js +496 -0
  178. package/dist/tools/tasks/retryTask.js +60 -0
  179. package/dist/tools/{runTaskLoop.d.ts → tasks/runTaskLoop.d.ts} +6 -6
  180. package/dist/tools/{runTaskLoop.js → tasks/runTaskLoop.js} +63 -41
  181. package/dist/tools/tasks/runVerification.js +38 -0
  182. package/dist/tools/{taskLineage.js → tasks/taskLineage.js} +8 -7
  183. package/dist/tools/{taskOutputs.d.ts → tasks/taskOutputs.d.ts} +2 -2
  184. package/dist/tools/{taskOutputs.js → tasks/taskOutputs.js} +17 -16
  185. package/dist/tools/tasks/taskStates.d.ts +6 -0
  186. package/dist/tools/tasks/taskStates.js +52 -0
  187. package/dist/tools/{waitForTask.js → tasks/waitForTask.js} +3 -11
  188. package/dist/tools/{androidDoctor.js → workspace/androidDoctor.js} +16 -10
  189. package/dist/tools/{applyPatch.d.ts → workspace/applyPatch.d.ts} +1 -1
  190. package/dist/tools/workspace/applyPatch.js +53 -0
  191. package/dist/tools/{discardWorktree.js → workspace/discardWorktree.js} +3 -3
  192. package/dist/tools/workspace/index.d.ts +10 -0
  193. package/dist/tools/workspace/index.js +10 -0
  194. package/dist/tools/workspace/listAgents.d.ts +23 -0
  195. package/dist/tools/workspace/listAgents.js +56 -0
  196. package/dist/tools/{listWorkspace.js → workspace/listWorkspace.js} +3 -3
  197. package/dist/tools/{mergeWorktree.js → workspace/mergeWorktree.js} +3 -3
  198. package/dist/tools/{readWorkspaceFile.js → workspace/readWorkspaceFile.js} +8 -8
  199. package/dist/tools/{recommendAgentForTask.js → workspace/recommendAgentForTask.js} +3 -3
  200. package/dist/tools/{searchWorkspace.js → workspace/searchWorkspace.js} +5 -5
  201. package/dist/tools/{syncFile.d.ts → workspace/syncFile.d.ts} +1 -1
  202. package/dist/tools/workspace/syncFile.js +134 -0
  203. package/dist/utils/atomicFile.d.ts +7 -0
  204. package/dist/utils/atomicFile.js +67 -0
  205. package/dist/utils/boundedFile.d.ts +8 -0
  206. package/dist/utils/boundedFile.js +93 -0
  207. package/dist/utils/lockedJsonFile.d.ts +15 -0
  208. package/dist/utils/lockedJsonFile.js +224 -0
  209. package/dist/utils/platform.d.ts +11 -0
  210. package/dist/utils/platform.js +26 -0
  211. package/dist/utils/stableJson.d.ts +2 -0
  212. package/dist/utils/stableJson.js +12 -0
  213. package/dist/version.d.ts +3 -2
  214. package/dist/version.js +3 -2
  215. package/dist/watcherStatus.d.ts +2 -2
  216. package/dist/watcherStatus.js +18 -0
  217. package/docs/CODE_WIKI.md +808 -143
  218. package/docs/architecture.md +15 -55
  219. package/docs/assets/patchwarden-desktop-onboarding.png +0 -0
  220. package/docs/control-center/README.md +4 -4
  221. package/docs/dashboard-overview.md +4 -4
  222. package/docs/desktop-app.md +175 -0
  223. package/docs/evidence-pack-schema.md +3 -3
  224. package/docs/open-source-application.md +16 -11
  225. package/docs/release-checklist.md +1 -1
  226. package/docs/release-evidence.md +35 -39
  227. package/docs/task-safe-review-workflow.md +5 -5
  228. package/docs/threat-model.md +19 -1
  229. package/examples/config.example.json +4 -2
  230. package/package.json +27 -10
  231. package/scripts/README.md +15 -6
  232. package/scripts/build.js +11 -0
  233. package/scripts/checks/brand-check.js +3 -0
  234. package/scripts/checks/build-output-check.js +37 -0
  235. package/scripts/checks/control-center-smoke.js +102 -10
  236. package/scripts/checks/control-smoke.js +19 -8
  237. package/scripts/checks/http-mcp-smoke.js +82 -4
  238. package/scripts/checks/lifecycle-smoke.js +15 -12
  239. package/scripts/checks/mcp-manifest-check.js +1 -1
  240. package/scripts/checks/mcp-smoke.js +58 -4
  241. package/scripts/checks/package-manifest-check.js +20 -7
  242. package/scripts/checks/unit-tests.js +3 -5
  243. package/scripts/checks/watcher-supervisor-smoke.js +38 -1
  244. package/scripts/control/get-patchwarden-health.ps1 +1 -1
  245. package/scripts/control/manage-patchwarden.ps1 +69 -4
  246. package/scripts/control/provision-patchwarden-tunnel.ps1 +127 -0
  247. package/scripts/control/run-background-supervisor.ps1 +41 -0
  248. package/scripts/control/start-patchwarden-tunnel.ps1 +191 -33
  249. package/scripts/e2e/demo-runtime.mjs +153 -0
  250. package/{PatchWarden-Control-Tray.cmd → scripts/launchers/PatchWarden-Control-Tray.cmd} +3 -3
  251. package/scripts/launchers/PatchWarden-Control.cmd +6 -0
  252. package/{PatchWarden-Desktop.cmd → scripts/launchers/PatchWarden-Desktop.cmd} +2 -2
  253. package/scripts/launchers/Restart-PatchWarden-Control.cmd +6 -0
  254. package/{Stop-PatchWarden.cmd → scripts/launchers/Stop-PatchWarden.cmd} +2 -2
  255. package/scripts/lib/clean-generated-output.js +11 -0
  256. package/scripts/lib/file-discovery.js +12 -0
  257. package/scripts/release/desktop-preflight.js +211 -0
  258. package/scripts/release/pack-clean.js +59 -14
  259. package/src/assessments/agentAssessor.ts +26 -12
  260. package/src/assessments/assessmentDiagnostics.ts +172 -0
  261. package/src/assessments/assessmentStore.ts +288 -51
  262. package/src/assessments/securitySnapshot.ts +231 -0
  263. package/src/config.ts +70 -13
  264. package/src/control/fileManager.ts +40 -0
  265. package/src/control/middleware/auth.ts +22 -4
  266. package/src/control/middleware/static.ts +16 -3
  267. package/src/control/routeTable.ts +301 -0
  268. package/src/control/routes/audit.ts +14 -6
  269. package/src/control/routes/evidence.ts +1 -1
  270. package/src/control/routes/lineage.ts +1 -1
  271. package/src/control/routes/process.ts +341 -40
  272. package/src/control/routes/sessions.ts +88 -25
  273. package/src/control/routes/status.ts +134 -17
  274. package/src/control/routes/taskActions.ts +24 -58
  275. package/src/control/routes/tasks.ts +66 -58
  276. package/src/control/routes/workspace.ts +56 -25
  277. package/src/control/runtime.ts +71 -43
  278. package/src/control/server.ts +37 -342
  279. package/src/control/shared.ts +77 -11
  280. package/src/diagnostics/allowedTestCommandSafety.ts +12 -0
  281. package/src/direct/directAudit.ts +17 -9
  282. package/src/direct/directGuards.ts +72 -44
  283. package/src/direct/directPatch.ts +95 -8
  284. package/src/direct/directSessionStore.ts +213 -55
  285. package/src/direct/directVerification.ts +9 -23
  286. package/src/doctor.ts +76 -64
  287. package/src/goal/acceptanceEngine.ts +1 -1
  288. package/src/goal/goalProgress.ts +17 -13
  289. package/src/goal/goalReport.ts +5 -13
  290. package/src/goal/goalStatus.ts +19 -3
  291. package/src/goal/goalStore.ts +241 -54
  292. package/src/goal/handoffExport.ts +6 -6
  293. package/src/goal/specKitImport.ts +12 -12
  294. package/src/goal/subgoalSync.ts +11 -8
  295. package/src/goal/worktreeManager.ts +139 -22
  296. package/src/httpServer.ts +113 -30
  297. package/src/logging.ts +10 -3
  298. package/src/policy/projectPolicy.ts +5 -2
  299. package/src/release/releaseGate.ts +74 -27
  300. package/src/runner/agentInvocation.ts +126 -4
  301. package/src/runner/changeCapture.ts +291 -57
  302. package/src/runner/postTaskCleanup.ts +41 -17
  303. package/src/runner/processSecurity.ts +433 -0
  304. package/src/runner/runTask.ts +197 -97
  305. package/src/runner/simpleProcess.ts +66 -31
  306. package/src/{taskProgress.ts → runner/taskProgress.ts} +3 -3
  307. package/src/{taskRuntime.ts → runner/taskRuntime.ts} +6 -3
  308. package/src/runner/taskStatusStore.ts +69 -0
  309. package/src/runner/watch.ts +348 -52
  310. package/src/security/contentRedaction.ts +2 -2
  311. package/src/security/discoveryTokenStore.ts +18 -4
  312. package/src/security/loopbackHost.ts +19 -0
  313. package/src/security/pathGuard.ts +35 -16
  314. package/src/security/planGuard.ts +2 -1
  315. package/src/security/runtimeGuard.ts +9 -6
  316. package/src/security/secretComparison.ts +13 -0
  317. package/src/security/sensitiveGuard.ts +27 -8
  318. package/src/security/toolInvocationGuard.ts +2 -2
  319. package/src/security/workspaceRootGuard.ts +53 -0
  320. package/src/smoke-test.ts +27 -26
  321. package/src/tools/catalog/index.ts +4 -0
  322. package/src/tools/{toolCatalog.ts → catalog/toolCatalog.ts} +3 -12
  323. package/src/tools/{toolRegistry.ts → catalog/toolRegistry.ts} +7 -16
  324. package/src/tools/definitions/toolDefs.ts +1264 -0
  325. package/src/tools/{auditSession.ts → diagnostics/auditSession.ts} +2 -2
  326. package/src/tools/{auditTask.ts → diagnostics/auditTask.ts} +136 -39
  327. package/src/tools/{healthCheck.ts → diagnostics/healthCheck.ts} +37 -9
  328. package/src/tools/diagnostics/index.ts +6 -0
  329. package/src/tools/{safeStatus.ts → diagnostics/safeStatus.ts} +7 -7
  330. package/src/tools/{safeViews.ts → diagnostics/safeViews.ts} +27 -17
  331. package/src/tools/{schemaDriftCheck.ts → diagnostics/schemaDriftCheck.ts} +3 -3
  332. package/src/tools/{createDirectSession.ts → direct/createDirectSession.ts} +6 -6
  333. package/src/tools/{finalizeDirectSession.ts → direct/finalizeDirectSession.ts} +7 -5
  334. package/src/tools/direct/index.ts +3 -0
  335. package/src/tools/{runDirectVerificationBundle.ts → direct/runDirectVerificationBundle.ts} +2 -2
  336. package/src/tools/{discoverTools.ts → discovery/discoverTools.ts} +6 -6
  337. package/src/tools/{explainTool.ts → discovery/explainTool.ts} +3 -3
  338. package/src/tools/discovery/index.ts +3 -0
  339. package/src/tools/{invokeDiscoveredTool.ts → discovery/invokeDiscoveredTool.ts} +7 -7
  340. package/src/tools/dispatch/coreDispatch.ts +36 -31
  341. package/src/tools/dispatch/diagnosticDispatch.ts +26 -21
  342. package/src/tools/dispatch/directDispatch.ts +11 -10
  343. package/src/tools/dispatch/goalDispatch.ts +5 -4
  344. package/src/tools/dispatch/releaseDispatch.ts +3 -2
  345. package/src/tools/dispatch/validation.ts +78 -0
  346. package/src/tools/{getPlan.ts → goals/getPlan.ts} +4 -4
  347. package/src/tools/{goalSubgoalTask.ts → goals/goalSubgoalTask.ts} +41 -26
  348. package/src/tools/goals/index.ts +3 -0
  349. package/src/tools/{savePlan.ts → goals/savePlan.ts} +38 -8
  350. package/src/tools/registry.ts +17 -1261
  351. package/src/tools/{checkReleaseGate.ts → release/checkReleaseGate.ts} +3 -3
  352. package/src/tools/release/index.ts +2 -0
  353. package/src/tools/{releaseMode.ts → release/releaseMode.ts} +42 -24
  354. package/src/tools/tasks/cancelTask.ts +110 -0
  355. package/src/tools/{createTask.ts → tasks/createTask.ts} +161 -33
  356. package/src/tools/{diagnoseTask.ts → tasks/diagnoseTask.ts} +12 -16
  357. package/src/tools/{evidencePack.ts → tasks/evidencePack.ts} +36 -22
  358. package/src/tools/{getTaskFile.ts → tasks/getTaskFile.ts} +4 -4
  359. package/src/tools/{getTaskStatus.ts → tasks/getTaskStatus.ts} +13 -6
  360. package/src/tools/{getTaskStdoutTail.ts → tasks/getTaskStdoutTail.ts} +13 -11
  361. package/src/tools/{getTaskSummary.ts → tasks/getTaskSummary.ts} +46 -44
  362. package/src/tools/tasks/index.ts +18 -0
  363. package/src/tools/{listTasks.ts → tasks/listTasks.ts} +6 -5
  364. package/src/tools/{reconcileTasks.ts → tasks/reconcileTasks.ts} +228 -77
  365. package/src/tools/tasks/retryTask.ts +69 -0
  366. package/src/tools/{runTaskLoop.ts → tasks/runTaskLoop.ts} +73 -46
  367. package/src/tools/{runVerification.ts → tasks/runVerification.ts} +6 -3
  368. package/src/tools/{taskLineage.ts → tasks/taskLineage.ts} +8 -7
  369. package/src/tools/{taskOutputs.ts → tasks/taskOutputs.ts} +27 -20
  370. package/src/tools/tasks/taskStates.ts +54 -0
  371. package/src/tools/{waitForTask.ts → tasks/waitForTask.ts} +3 -12
  372. package/src/tools/{androidDoctor.ts → workspace/androidDoctor.ts} +15 -10
  373. package/src/tools/{applyPatch.ts → workspace/applyPatch.ts} +38 -16
  374. package/src/tools/{discardWorktree.ts → workspace/discardWorktree.ts} +3 -3
  375. package/src/tools/workspace/index.ts +10 -0
  376. package/src/tools/workspace/listAgents.ts +75 -0
  377. package/src/tools/{listWorkspace.ts → workspace/listWorkspace.ts} +3 -3
  378. package/src/tools/{mergeWorktree.ts → workspace/mergeWorktree.ts} +3 -3
  379. package/src/tools/{readWorkspaceFile.ts → workspace/readWorkspaceFile.ts} +8 -8
  380. package/src/tools/{recommendAgentForTask.ts → workspace/recommendAgentForTask.ts} +3 -3
  381. package/src/tools/{searchWorkspace.ts → workspace/searchWorkspace.ts} +5 -5
  382. package/src/tools/workspace/syncFile.ts +292 -0
  383. package/src/utils/atomicFile.ts +74 -0
  384. package/src/utils/boundedFile.ts +116 -0
  385. package/src/utils/lockedJsonFile.ts +251 -0
  386. package/src/utils/platform.ts +31 -0
  387. package/src/utils/stableJson.ts +12 -0
  388. package/src/version.ts +3 -2
  389. package/src/watcherStatus.ts +19 -1
  390. package/ui/desktop-bootstrap.js +11 -0
  391. package/ui/desktop-bridge.js +36 -0
  392. package/ui/desktop.css +160 -0
  393. package/ui/getting-started.js +93 -0
  394. package/ui/i18n.js +484 -0
  395. package/ui/log-parser.js +30 -0
  396. package/ui/pages/audit.html +12 -0
  397. package/ui/pages/dashboard.html +94 -147
  398. package/ui/pages/direct-sessions.html +123 -26
  399. package/ui/pages/getting-started.html +43 -0
  400. package/ui/pages/logs.html +134 -22
  401. package/ui/pages/settings.html +101 -0
  402. package/ui/pages/task-detail.html +12 -0
  403. package/ui/pages/tasks.html +54 -8
  404. package/ui/pages/workspace.html +38 -1
  405. package/ui/settings.js +254 -0
  406. package/PatchWarden-Control.cmd +0 -6
  407. package/Restart-PatchWarden-Control.cmd +0 -6
  408. package/dist/test/unit/acceptance-engine.test.d.ts +0 -1
  409. package/dist/test/unit/acceptance-engine.test.js +0 -228
  410. package/dist/test/unit/agent-router.test.d.ts +0 -1
  411. package/dist/test/unit/agent-router.test.js +0 -287
  412. package/dist/test/unit/android-doctor.test.d.ts +0 -1
  413. package/dist/test/unit/android-doctor.test.js +0 -118
  414. package/dist/test/unit/apply-patch.test.d.ts +0 -1
  415. package/dist/test/unit/apply-patch.test.js +0 -225
  416. package/dist/test/unit/audit-checks.test.d.ts +0 -1
  417. package/dist/test/unit/audit-checks.test.js +0 -350
  418. package/dist/test/unit/chinese-path.test.d.ts +0 -1
  419. package/dist/test/unit/chinese-path.test.js +0 -91
  420. package/dist/test/unit/command-guard.test.d.ts +0 -1
  421. package/dist/test/unit/command-guard.test.js +0 -160
  422. package/dist/test/unit/create-task.test.d.ts +0 -1
  423. package/dist/test/unit/create-task.test.js +0 -197
  424. package/dist/test/unit/diagnose-task.test.d.ts +0 -1
  425. package/dist/test/unit/diagnose-task.test.js +0 -457
  426. package/dist/test/unit/direct-guards.test.d.ts +0 -1
  427. package/dist/test/unit/direct-guards.test.js +0 -328
  428. package/dist/test/unit/discovery-token-store.test.d.ts +0 -1
  429. package/dist/test/unit/discovery-token-store.test.js +0 -139
  430. package/dist/test/unit/evidence-pack.test.d.ts +0 -1
  431. package/dist/test/unit/evidence-pack.test.js +0 -224
  432. package/dist/test/unit/get-task-status.test.d.ts +0 -1
  433. package/dist/test/unit/get-task-status.test.js +0 -174
  434. package/dist/test/unit/get-task-summary.test.d.ts +0 -1
  435. package/dist/test/unit/get-task-summary.test.js +0 -146
  436. package/dist/test/unit/goal-graph.test.d.ts +0 -1
  437. package/dist/test/unit/goal-graph.test.js +0 -298
  438. package/dist/test/unit/goal-progress.test.d.ts +0 -1
  439. package/dist/test/unit/goal-progress.test.js +0 -381
  440. package/dist/test/unit/goal-report.test.d.ts +0 -1
  441. package/dist/test/unit/goal-report.test.js +0 -159
  442. package/dist/test/unit/goal-status.test.d.ts +0 -1
  443. package/dist/test/unit/goal-status.test.js +0 -215
  444. package/dist/test/unit/goal-store.test.d.ts +0 -1
  445. package/dist/test/unit/goal-store.test.js +0 -253
  446. package/dist/test/unit/goal-subgoal-task.test.d.ts +0 -1
  447. package/dist/test/unit/goal-subgoal-task.test.js +0 -55
  448. package/dist/test/unit/goal-tools-registry.test.d.ts +0 -1
  449. package/dist/test/unit/goal-tools-registry.test.js +0 -192
  450. package/dist/test/unit/handoff-export.test.d.ts +0 -1
  451. package/dist/test/unit/handoff-export.test.js +0 -263
  452. package/dist/test/unit/invoke-discovered-tool.test.d.ts +0 -1
  453. package/dist/test/unit/invoke-discovered-tool.test.js +0 -167
  454. package/dist/test/unit/logging.test.d.ts +0 -1
  455. package/dist/test/unit/logging.test.js +0 -397
  456. package/dist/test/unit/path-guard.test.d.ts +0 -1
  457. package/dist/test/unit/path-guard.test.js +0 -133
  458. package/dist/test/unit/post-task-cleanup.test.d.ts +0 -1
  459. package/dist/test/unit/post-task-cleanup.test.js +0 -48
  460. package/dist/test/unit/project-policy-release-mode.test.d.ts +0 -1
  461. package/dist/test/unit/project-policy-release-mode.test.js +0 -125
  462. package/dist/test/unit/reconcile-tasks.test.d.ts +0 -1
  463. package/dist/test/unit/reconcile-tasks.test.js +0 -456
  464. package/dist/test/unit/release-gate.test.d.ts +0 -1
  465. package/dist/test/unit/release-gate.test.js +0 -242
  466. package/dist/test/unit/run-task-loop.test.d.ts +0 -1
  467. package/dist/test/unit/run-task-loop.test.js +0 -380
  468. package/dist/test/unit/safe-status.test.d.ts +0 -1
  469. package/dist/test/unit/safe-status.test.js +0 -165
  470. package/dist/test/unit/safe-views.test.d.ts +0 -1
  471. package/dist/test/unit/safe-views.test.js +0 -171
  472. package/dist/test/unit/schema-drift-check.test.d.ts +0 -1
  473. package/dist/test/unit/schema-drift-check.test.js +0 -176
  474. package/dist/test/unit/sensitive-guard.test.d.ts +0 -1
  475. package/dist/test/unit/sensitive-guard.test.js +0 -104
  476. package/dist/test/unit/spec-kit-import.test.d.ts +0 -1
  477. package/dist/test/unit/spec-kit-import.test.js +0 -341
  478. package/dist/test/unit/subgoal-sync.test.d.ts +0 -1
  479. package/dist/test/unit/subgoal-sync.test.js +0 -183
  480. package/dist/test/unit/sync-file.test.d.ts +0 -1
  481. package/dist/test/unit/sync-file.test.js +0 -154
  482. package/dist/test/unit/tool-invocation-guard.test.d.ts +0 -1
  483. package/dist/test/unit/tool-invocation-guard.test.js +0 -432
  484. package/dist/test/unit/tool-usage-stats.test.d.ts +0 -1
  485. package/dist/test/unit/tool-usage-stats.test.js +0 -300
  486. package/dist/test/unit/toolSearch.test.d.ts +0 -1
  487. package/dist/test/unit/toolSearch.test.js +0 -571
  488. package/dist/test/unit/wait-for-task.test.d.ts +0 -1
  489. package/dist/test/unit/wait-for-task.test.js +0 -144
  490. package/dist/test/unit/watcher-status.test.d.ts +0 -1
  491. package/dist/test/unit/watcher-status.test.js +0 -169
  492. package/dist/test/unit/worktree-manager.test.d.ts +0 -1
  493. package/dist/test/unit/worktree-manager.test.js +0 -176
  494. package/dist/tools/applyPatch.js +0 -41
  495. package/dist/tools/cancelTask.d.ts +0 -30
  496. package/dist/tools/cancelTask.js +0 -64
  497. package/dist/tools/finalizeDirectSession.js +0 -84
  498. package/dist/tools/goalSubgoalTask.js +0 -110
  499. package/dist/tools/killTask.d.ts +0 -15
  500. package/dist/tools/listAgents.d.ts +0 -12
  501. package/dist/tools/listAgents.js +0 -42
  502. package/dist/tools/reconcileTasks.js +0 -352
  503. package/dist/tools/retryTask.js +0 -45
  504. package/dist/tools/runVerification.js +0 -36
  505. package/dist/tools/syncFile.js +0 -65
  506. package/docs/control-center/control-center-daily-driver.md +0 -211
  507. package/docs/control-center/control-center-mvp.md +0 -205
  508. package/docs/control-center/control-center-phase2.md +0 -159
  509. package/docs/execution-plan-2026-07-09.md +0 -315
  510. package/docs/release-v0.6.0.md +0 -71
  511. package/docs/release-v0.6.1.md +0 -75
  512. package/docs/release-v0.6.4.md +0 -45
  513. package/docs/roadmap-execution-and-acceptance.md +0 -365
  514. package/src/test/unit/acceptance-engine.test.ts +0 -261
  515. package/src/test/unit/agent-router.test.ts +0 -342
  516. package/src/test/unit/android-doctor.test.ts +0 -158
  517. package/src/test/unit/apply-patch.test.ts +0 -293
  518. package/src/test/unit/audit-checks.test.ts +0 -567
  519. package/src/test/unit/chinese-path.test.ts +0 -106
  520. package/src/test/unit/command-guard.test.ts +0 -221
  521. package/src/test/unit/create-task.test.ts +0 -255
  522. package/src/test/unit/diagnose-task.test.ts +0 -544
  523. package/src/test/unit/direct-guards.test.ts +0 -467
  524. package/src/test/unit/discovery-token-store.test.ts +0 -181
  525. package/src/test/unit/evidence-pack.test.ts +0 -251
  526. package/src/test/unit/get-task-status.test.ts +0 -203
  527. package/src/test/unit/get-task-summary.test.ts +0 -173
  528. package/src/test/unit/goal-graph.test.ts +0 -347
  529. package/src/test/unit/goal-progress.test.ts +0 -538
  530. package/src/test/unit/goal-report.test.ts +0 -189
  531. package/src/test/unit/goal-status.test.ts +0 -270
  532. package/src/test/unit/goal-store.test.ts +0 -318
  533. package/src/test/unit/goal-subgoal-task.test.ts +0 -72
  534. package/src/test/unit/goal-tools-registry.test.ts +0 -245
  535. package/src/test/unit/handoff-export.test.ts +0 -295
  536. package/src/test/unit/invoke-discovered-tool.test.ts +0 -216
  537. package/src/test/unit/logging.test.ts +0 -497
  538. package/src/test/unit/path-guard.test.ts +0 -180
  539. package/src/test/unit/post-task-cleanup.test.ts +0 -53
  540. package/src/test/unit/project-policy-release-mode.test.ts +0 -156
  541. package/src/test/unit/reconcile-tasks.test.ts +0 -551
  542. package/src/test/unit/release-gate.test.ts +0 -314
  543. package/src/test/unit/run-task-loop.test.ts +0 -425
  544. package/src/test/unit/safe-status.test.ts +0 -187
  545. package/src/test/unit/safe-views.test.ts +0 -184
  546. package/src/test/unit/schema-drift-check.test.ts +0 -260
  547. package/src/test/unit/sensitive-guard.test.ts +0 -124
  548. package/src/test/unit/spec-kit-import.test.ts +0 -429
  549. package/src/test/unit/subgoal-sync.test.ts +0 -236
  550. package/src/test/unit/sync-file.test.ts +0 -231
  551. package/src/test/unit/tool-invocation-guard.test.ts +0 -542
  552. package/src/test/unit/tool-usage-stats.test.ts +0 -384
  553. package/src/test/unit/toolSearch.test.ts +0 -756
  554. package/src/test/unit/wait-for-task.test.ts +0 -176
  555. package/src/test/unit/watcher-status.test.ts +0 -190
  556. package/src/test/unit/worktree-manager.test.ts +0 -247
  557. package/src/tools/cancelTask.ts +0 -70
  558. package/src/tools/listAgents.ts +0 -54
  559. package/src/tools/retryTask.ts +0 -53
  560. package/src/tools/syncFile.ts +0 -122
  561. /package/dist/tools/{toolCatalog.d.ts → catalog/toolCatalog.d.ts} +0 -0
  562. /package/dist/tools/{toolSearch.d.ts → catalog/toolSearch.d.ts} +0 -0
  563. /package/dist/tools/{toolSearch.js → catalog/toolSearch.js} +0 -0
  564. /package/dist/tools/{toolUsageStats.d.ts → catalog/toolUsageStats.d.ts} +0 -0
  565. /package/dist/tools/{toolUsageStats.js → catalog/toolUsageStats.js} +0 -0
  566. /package/dist/tools/{createDirectSession.d.ts → direct/createDirectSession.d.ts} +0 -0
  567. /package/dist/tools/{runDirectVerificationBundle.d.ts → direct/runDirectVerificationBundle.d.ts} +0 -0
  568. /package/dist/tools/{getPlan.d.ts → goals/getPlan.d.ts} +0 -0
  569. /package/dist/tools/{savePlan.d.ts → goals/savePlan.d.ts} +0 -0
  570. /package/dist/tools/{evidencePack.d.ts → tasks/evidencePack.d.ts} +0 -0
  571. /package/dist/tools/{getTaskFile.d.ts → tasks/getTaskFile.d.ts} +0 -0
  572. /package/dist/tools/{getTaskProgress.d.ts → tasks/getTaskProgress.d.ts} +0 -0
  573. /package/dist/tools/{getTaskProgress.js → tasks/getTaskProgress.js} +0 -0
  574. /package/dist/tools/{getTaskStdoutTail.d.ts → tasks/getTaskStdoutTail.d.ts} +0 -0
  575. /package/dist/tools/{getTaskSummary.d.ts → tasks/getTaskSummary.d.ts} +0 -0
  576. /package/dist/tools/{killTask.js → tasks/killTask.js} +0 -0
  577. /package/dist/tools/{retryTask.d.ts → tasks/retryTask.d.ts} +0 -0
  578. /package/dist/tools/{runVerification.d.ts → tasks/runVerification.d.ts} +0 -0
  579. /package/dist/tools/{taskLineage.d.ts → tasks/taskLineage.d.ts} +0 -0
  580. /package/dist/tools/{waitForTask.d.ts → tasks/waitForTask.d.ts} +0 -0
  581. /package/dist/tools/{androidDoctor.d.ts → workspace/androidDoctor.d.ts} +0 -0
  582. /package/dist/tools/{discardWorktree.d.ts → workspace/discardWorktree.d.ts} +0 -0
  583. /package/dist/tools/{listWorkspace.d.ts → workspace/listWorkspace.d.ts} +0 -0
  584. /package/dist/tools/{mergeWorktree.d.ts → workspace/mergeWorktree.d.ts} +0 -0
  585. /package/dist/tools/{readWorkspaceFile.d.ts → workspace/readWorkspaceFile.d.ts} +0 -0
  586. /package/dist/tools/{recommendAgentForTask.d.ts → workspace/recommendAgentForTask.d.ts} +0 -0
  587. /package/dist/tools/{searchWorkspace.d.ts → workspace/searchWorkspace.d.ts} +0 -0
  588. /package/src/tools/{toolSearch.ts → catalog/toolSearch.ts} +0 -0
  589. /package/src/tools/{toolUsageStats.ts → catalog/toolUsageStats.ts} +0 -0
  590. /package/src/tools/{getTaskProgress.ts → tasks/getTaskProgress.ts} +0 -0
  591. /package/src/tools/{killTask.ts → tasks/killTask.ts} +0 -0
@@ -95,13 +95,45 @@ 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>
100
+ <script>
101
+ // Keep the log page usable when an older packaged Control Center omits the
102
+ // parser asset. The server serves the canonical parser; this is only a
103
+ // bounded display fallback and never changes log contents.
104
+ if (!window.PatchWardenLogParser || typeof window.PatchWardenLogParser.parseLine !== 'function') {
105
+ window.PatchWardenLogParser = {
106
+ parseLine: function (rawLine, stream) {
107
+ var line = String(rawLine || '');
108
+ var source = String(stream || 'log');
109
+ var entry = null;
110
+ try {
111
+ var parsed = JSON.parse(line);
112
+ if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) entry = parsed;
113
+ } catch (_) {}
114
+ var level = entry ? String(entry.level || entry.severity || '').toLowerCase() : '';
115
+ if (level === 'warning') level = 'warn';
116
+ return {
117
+ raw: line,
118
+ time: entry ? String(entry.time || entry.timestamp || '') : '',
119
+ level: level,
120
+ component: entry ? String(entry.component || entry.scope || entry.source || source) : source,
121
+ summary: entry ? String(entry.summary || entry.msg || entry.message || line) : line,
122
+ detail: entry ? JSON.stringify(entry, null, 2) : line,
123
+ structured: entry !== null
124
+ };
125
+ }
126
+ };
127
+ }
128
+ </script>
99
129
  <style>
100
130
  .no-scrollbar::-webkit-scrollbar { display: none; }
101
131
  .no-scrollbar { -ms-overflow-style: none; scrollbar-width: none; }
102
132
  .pw-spin { animation: pw-spin 1s linear infinite; }
103
133
  @keyframes pw-spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
104
134
  </style>
135
+ <script src="/desktop-bootstrap.js"></script>
136
+ <link rel="stylesheet" href="/desktop.css">
105
137
  </head>
106
138
  <body class="flex h-screen overflow-hidden">
107
139
 
@@ -119,6 +151,10 @@ html.dark {
119
151
 
120
152
  <!-- Navigation -->
121
153
  <nav class="flex-1 flex flex-col gap-1 px-3 pt-4 overflow-y-auto">
154
+ <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);">
155
+ <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>
156
+ <span class="truncate" data-i18n="nav.gettingStarted">开始使用</span>
157
+ </a>
122
158
 
123
159
  <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
160
  <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 +210,10 @@ html.dark {
174
210
  <span class="truncate" data-zh="日志" data-en="Logs">日志</span>
175
211
  </a>
176
212
 
213
+ <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);">
214
+ <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>
215
+ <span class="truncate" data-i18n="nav.settings">设置</span>
216
+ </a>
177
217
  </nav>
178
218
 
179
219
  <!-- Bottom Section -->
@@ -211,6 +251,11 @@ html.dark {
211
251
  <option value="300">tail 300</option>
212
252
  <option value="1000">tail 1000</option>
213
253
  </select>
254
+ <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>
255
+ <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);">
256
+ <option value="">全部级别</option><option value="error">Error</option><option value="warn">Warning</option><option value="info">Info</option>
257
+ </select>
258
+ <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
259
  <!-- Auto-refresh toggle -->
215
260
  <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
261
  <input id="autoRefresh" type="checkbox" style="accent-color: var(--pw-accent);" />
@@ -260,20 +305,20 @@ html.dark {
260
305
  <span id="stdoutMeta" class="text-xs whitespace-nowrap" style="color: var(--pw-text-tertiary); font-family: var(--pw-font-mono);">—</span>
261
306
  </div>
262
307
  <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>
308
+ <div id="stdoutContent" class="hidden log-list"></div>
264
309
  </section>
265
310
 
266
311
  <!-- stderr -->
267
312
  <section class="rounded-lg overflow-hidden" style="background-color: var(--pw-bg-surface); border: 1px solid var(--pw-border);">
268
313
  <div class="flex items-center justify-between px-4 py-3" style="border-bottom: 1px solid var(--pw-border);">
269
314
  <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>
315
+ <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
316
  <h2 class="text-sm font-semibold" style="color: var(--pw-text-primary); font-family: var(--pw-font-sans);">stderr</h2>
272
317
  </div>
273
318
  <span id="stderrMeta" class="text-xs whitespace-nowrap" style="color: var(--pw-text-tertiary); font-family: var(--pw-font-mono);">—</span>
274
319
  </div>
275
320
  <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>
321
+ <div id="stderrContent" class="hidden log-list"></div>
277
322
  </section>
278
323
 
279
324
  </div>
@@ -309,6 +354,19 @@ html.dark {
309
354
  background-color: var(--pw-accent-muted);
310
355
  color: var(--pw-accent);
311
356
  }
357
+ .log-list { max-height: 480px; overflow-y: auto; background: #0d1117; }
358
+ .log-row { border-bottom: 1px solid var(--pw-border-subtle); color: var(--pw-text-secondary); font-family: var(--pw-font-mono); }
359
+ .log-row:last-child { border-bottom: 0; }
360
+ .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; }
361
+ .log-row > summary::-webkit-details-marker { display: none; }
362
+ .log-time,.log-component,.log-summary { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
363
+ .log-level { font-weight: 700; text-transform: uppercase; }
364
+ .log-row[data-level="error"] .log-level { color: var(--pw-state-error); }
365
+ .log-row[data-level="warn"] .log-level { color: var(--pw-state-warning); }
366
+ .log-row[data-level="info"] .log-level { color: var(--pw-state-info); }
367
+ .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; }
368
+ .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); }
369
+ @media (max-width: 900px) { .log-row > summary { grid-template-columns: 92px 52px minmax(0,1fr) auto; }.log-component { display:none; } }
312
370
  </style>
313
371
 
314
372
  <script>lucide.createIcons();</script>
@@ -381,37 +439,57 @@ html.dark {
381
439
  }
382
440
  }
383
441
 
442
+ var lastLogData = null;
443
+ var visibleLogLines = {};
444
+ function parseLogEntries(value, stream) {
445
+ var query = $('logSearch') ? $('logSearch').value.trim().toLowerCase() : '';
446
+ var level = $('levelFilter') ? $('levelFilter').value : '';
447
+ return String(value || '').split(/\r?\n/).filter(Boolean).map(function (line) {
448
+ var parsed = window.PatchWardenLogParser.parseLine(line, stream);
449
+ if (level && parsed.level !== level) return null;
450
+ var searchable = [parsed.time, parsed.level, parsed.component, parsed.summary, line].join(' ').toLowerCase();
451
+ if (query && searchable.indexOf(query) < 0) return null;
452
+ return parsed;
453
+ }).filter(Boolean);
454
+ }
455
+
456
+ function renderLogRows(container, emptyState, value, stream) {
457
+ var entries = parseLogEntries(value, stream);
458
+ if (!entries.length) { hide(container); show(emptyState); return 0; }
459
+ container.innerHTML = entries.map(function (entry, index) {
460
+ var copyId = stream + '-' + index;
461
+ visibleLogLines[copyId] = entry.raw;
462
+ return '<details class="log-row" data-level="' + escapeHtml(entry.level || 'unknown') + '">'
463
+ + '<summary><span class="log-time" title="' + escapeHtml(entry.time || '无时间字段') + '">' + escapeHtml(entry.time || '—') + '</span>'
464
+ + '<span class="log-level">' + escapeHtml(entry.level || 'raw') + '</span>'
465
+ + '<span class="log-component" title="' + escapeHtml(entry.component) + '">' + escapeHtml(entry.component) + '</span>'
466
+ + '<span class="log-summary" title="' + escapeHtml(entry.summary) + '">' + escapeHtml(entry.summary) + '</span>'
467
+ + '<button type="button" class="copy-log-line" data-copy-id="' + copyId + '" title="复制脱敏日志行" aria-label="复制脱敏日志行" data-zh="复制" data-en="Copy">复制</button></summary>'
468
+ + '<pre class="log-detail">' + escapeHtml(entry.detail) + '</pre></details>';
469
+ }).join('');
470
+ show(container); hide(emptyState);
471
+ return entries.length;
472
+ }
473
+
384
474
  function renderLogs(data) {
475
+ lastLogData = data || {};
385
476
  var stdout = (data && data.stdout) || '';
386
477
  var stderr = (data && data.stderr) || '';
478
+ visibleLogLines = {};
387
479
  var reason = (data && data.reason) || null;
388
480
  var tail = (data && data.tail) || currentTail;
389
481
 
390
482
  // stdout
391
483
  var stdoutEl = $('stdoutContent');
392
484
  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 : ''));
485
+ var stdoutCount = renderLogRows(stdoutEl, stdoutEmpty, stdout, 'stdout');
486
+ text($('stdoutMeta'), (data && data.historical_snapshot ? '历史日志 · ' : '') + stdoutCount + ' 行 · tail=' + tail + (reason ? ' · ' + reason : ''));
402
487
 
403
488
  // stderr
404
489
  var stderrEl = $('stderrContent');
405
490
  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 : ''));
491
+ var stderrCount = renderLogRows(stderrEl, stderrEmpty, stderr, 'stderr');
492
+ text($('stderrMeta'), (data && data.historical_snapshot ? '历史日志 · ' : '') + stderrCount + ' 行 · tail=' + tail + (reason ? ' · ' + reason : ''));
415
493
  }
416
494
 
417
495
  function loadLogs() {
@@ -443,6 +521,15 @@ html.dark {
443
521
  });
444
522
  }
445
523
 
524
+ function clearLogView() {
525
+ clearError();
526
+ text($('stdoutMeta'), '已清空当前视图');
527
+ text($('stderrMeta'), '日志文件未删除');
528
+ $('stdoutContent').innerHTML = ''; $('stderrContent').innerHTML = '';
529
+ show($('stdoutEmpty')); show($('stderrEmpty'));
530
+ hide($('stdoutContent')); hide($('stderrContent'));
531
+ }
532
+
446
533
  // ---------- Auto-refresh ----------
447
534
  function startAutoRefresh() {
448
535
  stopAutoRefresh();
@@ -492,11 +579,36 @@ html.dark {
492
579
 
493
580
  var refreshBtn = $('refreshBtn');
494
581
  if (refreshBtn) refreshBtn.addEventListener('click', loadLogs);
582
+ var clearView = $('clearView');
583
+ if (clearView) clearView.addEventListener('click', clearLogView);
584
+ var levelFilter = $('levelFilter');
585
+ if (levelFilter) levelFilter.addEventListener('change', function () { if (lastLogData) renderLogs(lastLogData); });
586
+ var logSearch = $('logSearch');
587
+ if (logSearch) logSearch.addEventListener('input', function () { if (lastLogData) renderLogs(lastLogData); });
588
+ ['stdoutContent', 'stderrContent'].forEach(function (id) {
589
+ var container = $(id);
590
+ if (!container) return;
591
+ container.addEventListener('click', function (event) {
592
+ var button = event.target.closest && event.target.closest('.copy-log-line');
593
+ if (!button) return;
594
+ event.preventDefault();
595
+ event.stopPropagation();
596
+ var line = visibleLogLines[button.getAttribute('data-copy-id')] || '';
597
+ if (navigator.clipboard && navigator.clipboard.writeText) {
598
+ navigator.clipboard.writeText(line).then(function () {
599
+ button.textContent = '已复制';
600
+ setTimeout(function () { button.textContent = '复制'; }, 1200);
601
+ });
602
+ }
603
+ });
604
+ });
495
605
 
496
606
  // ---------- Initial load ----------
497
607
  setCategory(DEFAULT_CATEGORY);
498
608
  loadLogs();
499
609
  })();
500
610
  </script>
611
+ <script src="/i18n.js"></script>
612
+ <script src="/desktop-bridge.js"></script>
501
613
  </body>
502
614
  </html>
@@ -0,0 +1,101 @@
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 data-i18n="settings.pageTitle"></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/tailwindcss-browser.js"></script>
11
+ <script src="/vendor/lucide.js"></script>
12
+ </head>
13
+ <body class="flex h-screen overflow-hidden">
14
+ <aside class="flex flex-col w-60 shrink-0 h-screen" style="background:var(--pw-bg);border-right:1px solid var(--pw-border)">
15
+ <div class="flex items-center gap-3 px-5 py-5" style="border-bottom:1px solid var(--pw-border-subtle)">
16
+ <i data-lucide="shield-check" style="color:var(--pw-accent)"></i><span class="text-lg font-semibold">PatchWarden</span>
17
+ </div>
18
+ <nav class="flex-1 flex flex-col gap-1 px-3 pt-4 overflow-y-auto">
19
+ <a href="/pages/getting-started.html" data-nav-key="getting-started" class="flex items-center gap-3 px-3 py-2.5 text-sm"><i data-lucide="rocket" class="w-4 h-4"></i><span data-i18n="nav.gettingStarted"></span></a>
20
+ <a href="/pages/dashboard.html" data-nav-key="dashboard" class="flex items-center gap-3 px-3 py-2.5 text-sm"><i data-lucide="layout-dashboard" class="w-4 h-4"></i><span data-i18n="nav.dashboard"></span></a>
21
+ <a href="/pages/tasks.html" data-nav-key="tasks" class="flex items-center gap-3 px-3 py-2.5 text-sm"><i data-lucide="list-checks" class="w-4 h-4"></i><span data-i18n="nav.tasks"></span></a>
22
+ <a href="/pages/workspace.html" data-nav-key="workspace" class="flex items-center gap-3 px-3 py-2.5 text-sm"><i data-lucide="folder" class="w-4 h-4"></i><span data-i18n="nav.workspace"></span></a>
23
+ <a href="/pages/audit.html" data-nav-key="audit" class="flex items-center gap-3 px-3 py-2.5 text-sm"><i data-lucide="file-search" class="w-4 h-4"></i><span data-i18n="nav.audit"></span></a>
24
+ <a href="/pages/direct-sessions.html" data-nav-key="direct-sessions" class="flex items-center gap-3 px-3 py-2.5 text-sm"><i data-lucide="route" class="w-4 h-4"></i><span data-i18n="nav.direct"></span></a>
25
+ <a href="/pages/logs.html" data-nav-key="logs" class="flex items-center gap-3 px-3 py-2.5 text-sm"><i data-lucide="scroll-text" class="w-4 h-4"></i><span data-i18n="nav.logs"></span></a>
26
+ <a href="/pages/settings.html" data-nav-key="settings" data-active="true" class="desktop-only-nav flex items-center gap-3 px-3 py-2.5 text-sm"><i data-lucide="settings" class="w-4 h-4"></i><span data-i18n="nav.settings"></span></a>
27
+ </nav>
28
+ </aside>
29
+ <div class="flex flex-col flex-1 min-w-0 h-screen overflow-hidden">
30
+ <header class="flex items-center px-6 py-3" style="border-bottom:1px solid var(--pw-border);background:var(--pw-bg)"><h1 class="text-lg font-semibold" data-i18n="settings.title"></h1></header>
31
+ <main class="flex-1 overflow-y-auto p-6" style="background:var(--pw-bg-elevated)">
32
+ <div class="settings-layout">
33
+ <section>
34
+ <h2 data-i18n="settings.appearance"></h2>
35
+ <div class="setting-row"><div><strong data-i18n="settings.theme"></strong><p data-i18n="settings.themeHelp"></p></div><select id="theme"><option value="system" data-i18n="settings.themeSystem"></option><option value="light" data-i18n="settings.themeLight"></option><option value="dark" data-i18n="settings.themeDark"></option></select></div>
36
+ <div class="setting-row"><div><strong data-i18n="language.label"></strong><p data-i18n="settings.languageHelp"></p></div><select id="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></div>
37
+ <div class="setting-row"><div><strong data-i18n="settings.closeWindow"></strong><p data-i18n="settings.closeWindowHelp"></p></div><select id="closeBehavior"><option value="tray" data-i18n="settings.closeToTray"></option><option value="quit" data-i18n="settings.closeQuit"></option></select></div>
38
+ </section>
39
+ <section>
40
+ <h2 data-i18n="settings.localAgents"></h2>
41
+ <div class="agent-section-toolbar"><p data-i18n="settings.localAgentsHelp"></p><button id="detectAgents"><i data-lucide="scan-search"></i><span data-i18n="settings.detectAgents"></span></button></div>
42
+ <div id="agentSettingsList" class="agent-settings-list"><p class="agent-empty" data-i18n="settings.loadingAgents"></p></div>
43
+ <div class="setting-actions"><button id="saveAgents" class="primary-action"><i data-lucide="save"></i><span data-i18n="settings.saveAgents"></span></button><span id="agentSettingsStatus" class="status-text" aria-live="polite"></span></div>
44
+ </section>
45
+ <section>
46
+ <h2 data-i18n="settings.mcpTunnel"></h2>
47
+ <div class="setting-row">
48
+ <div class="min-w-0"><strong>tunnel-client.exe</strong><p id="tunnelClientPath" class="mono break-path"></p></div>
49
+ <div class="inline-actions"><button id="detectTunnel"><i data-lucide="scan-search"></i><span data-i18n="settings.detect"></span></button><button id="chooseTunnel"><i data-lucide="file-search"></i><span data-i18n="settings.choose"></span></button></div>
50
+ </div>
51
+ <div class="setting-row">
52
+ <div><strong>Direct profile</strong><p data-i18n="settings.directHelp"></p></div>
53
+ <label class="switch-label"><input id="enableDirectProfile" type="checkbox"><span data-i18n="settings.enable"></span></label>
54
+ </div>
55
+ <div class="setting-row">
56
+ <div><strong data-i18n="settings.proxy"></strong><p data-i18n="settings.proxyHelp"></p></div>
57
+ <select id="proxyScope"><option value="shared" data-i18n="settings.proxyShared"></option><option value="separate" data-i18n="settings.proxySeparate"></option></select>
58
+ </div>
59
+ <div class="proxy-grid">
60
+ <div class="proxy-endpoint">
61
+ <strong>Core</strong>
62
+ <select id="coreProxyMode"><option value="environment" data-i18n="settings.proxyEnvironment"></option><option value="none" data-i18n="settings.proxyNone"></option><option value="manual" data-i18n="settings.proxyManual"></option></select>
63
+ <input id="coreProxyUrl" type="url" placeholder="http://127.0.0.1:7890" autocomplete="off">
64
+ </div>
65
+ <div id="directProxyEndpoint" class="proxy-endpoint hidden">
66
+ <strong>Direct</strong>
67
+ <select id="directProxyMode"><option value="environment" data-i18n="settings.proxyEnvironment"></option><option value="none" data-i18n="settings.proxyNone"></option><option value="manual" data-i18n="settings.proxyManual"></option></select>
68
+ <input id="directProxyUrl" type="url" placeholder="socks5://127.0.0.1:1080" autocomplete="off">
69
+ </div>
70
+ </div>
71
+ <div class="setting-actions"><button id="saveRuntime" class="primary-action"><i data-lucide="save"></i><span data-i18n="settings.saveRuntime"></span></button><span id="runtimeStatus" class="status-text" aria-live="polite"></span></div>
72
+ <div class="credential-panel">
73
+ <div class="credential-heading"><div><strong data-i18n="settings.tunnelCredentials"></strong><p data-i18n="settings.runtimeKeyHelp"></p></div><span id="credentialState" class="credential-state"></span></div>
74
+ <div class="credential-grid">
75
+ <label>Profile<select id="tunnelMode"><option value="core">Core</option><option value="direct">Direct</option></select></label>
76
+ <label><span data-i18n="settings.tunnelId"></span><input id="tunnelId" type="text" autocomplete="off" spellcheck="false" placeholder="tun_..."></label>
77
+ <label><span data-i18n="settings.runtimeKey"></span><input id="runtimeKey" type="password" autocomplete="new-password" spellcheck="false"></label>
78
+ </div>
79
+ <div class="setting-actions credential-actions"><button id="provisionTunnel" class="primary-action"><i data-lucide="shield-check"></i><span data-i18n="settings.configureCore"></span></button><button id="revalidateCredential"><i data-lucide="activity"></i><span data-i18n="settings.revalidate"></span></button><button id="forgetCredential"><i data-lucide="key-round"></i><span data-i18n="settings.forgetCredential"></span></button><span id="provisionStatus" class="status-text" aria-live="polite"></span></div>
80
+ </div>
81
+ <details class="install-guide"><summary data-i18n="settings.installSummary"></summary><ol><li data-i18n="settings.installStep1"></li><li data-i18n="settings.installStep2"></li><li data-i18n="settings.installStep3"></li><li data-i18n="settings.installStep4"></li></ol></details>
82
+ </section>
83
+ <section>
84
+ <h2 data-i18n="settings.workspaceDiagnostics"></h2>
85
+ <div class="setting-row"><div class="min-w-0"><strong data-i18n="settings.currentConfig"></strong><p id="configPath" class="mono truncate"></p></div><button id="openConfig" data-i18n-title="settings.openConfig"><i data-lucide="external-link"></i></button></div>
86
+ <div class="setting-row"><div><strong data-i18n="settings.workspace"></strong><p id="workspacePath" class="mono"></p></div><button id="changeWorkspace" data-i18n="settings.change"></button></div>
87
+ <div class="setting-actions"><button id="openLogs"><i data-lucide="folder-open"></i><span data-i18n="settings.openLogs"></span></button><button id="runDoctor"><i data-lucide="stethoscope"></i><span data-i18n="settings.runDoctor"></span></button></div>
88
+ <pre id="doctorOutput" class="hidden"></pre>
89
+ </section>
90
+ </div>
91
+ </main>
92
+ </div>
93
+ <style>
94
+ [data-nav-key]{color:var(--pw-text-secondary);border-radius:6px}[data-nav-key]:hover{background:var(--pw-bg-hover);color:var(--pw-text-primary)}[data-nav-key][data-active="true"]{background:var(--pw-accent-subtle);color:var(--pw-accent);border-left:2px solid var(--pw-accent)}
95
+ .settings-layout{display:grid;gap:18px;max-width:920px}.settings-layout section{background:var(--pw-bg-surface);border:1px solid var(--pw-border);border-radius:7px}.settings-layout h2{margin:0;padding:16px 18px;font-size:14px;border-bottom:1px solid var(--pw-border)}.setting-row{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:20px;align-items:center;padding:16px 18px;border-bottom:1px solid var(--pw-border-subtle)}.setting-row p,.credential-panel p,.agent-section-toolbar p{margin:4px 0 0;color:var(--pw-text-secondary);font-size:12px}.setting-row select,.setting-row button,.setting-actions button,.proxy-endpoint select,.proxy-endpoint input,.credential-grid select,.credential-grid input,.agent-section-toolbar button,.agent-controls select,.agent-controls input,.agent-controls button{min-height:36px;padding:0 12px;color:var(--pw-text-primary);background:var(--pw-bg-elevated);border:1px solid var(--pw-border);border-radius:6px}.setting-row button,.agent-section-toolbar button,.agent-controls button{display:flex;align-items:center;gap:6px}.setting-row svg,.agent-section-toolbar svg,.agent-controls svg{width:16px}.setting-actions{display:flex;align-items:center;gap:10px;padding:16px 18px;flex-wrap:wrap}.setting-actions button{display:flex;align-items:center;gap:8px}.setting-actions svg{width:16px}.inline-actions{display:flex;gap:8px}.switch-label{display:flex;align-items:center;gap:8px}.switch-label input{width:18px;height:18px;accent-color:var(--pw-accent)}.agent-section-toolbar{display:flex;align-items:center;justify-content:space-between;gap:16px;padding:12px 18px;border-bottom:1px solid var(--pw-border-subtle)}.agent-settings-list{display:grid}.agent-setting-row{display:grid;grid-template-columns:minmax(180px,1fr) minmax(280px,1.4fr);align-items:center;gap:18px;padding:14px 18px;border-bottom:1px solid var(--pw-border-subtle)}.agent-identity{display:grid;grid-template-columns:20px minmax(0,1fr);gap:10px;align-items:center}.agent-identity input{width:18px;height:18px;accent-color:var(--pw-accent)}.agent-identity strong,.agent-identity small{display:block}.agent-identity small{margin-top:3px;color:var(--pw-text-secondary);font-size:11px}.agent-controls{display:grid;grid-template-columns:minmax(150px,1fr) minmax(150px,1fr) 38px;gap:8px}.agent-controls button{justify-content:center;padding:0}.agent-empty{padding:18px;color:var(--pw-text-secondary);font-size:12px}.proxy-grid{display:grid;grid-template-columns:1fr 1fr;gap:12px;padding:16px 18px;border-bottom:1px solid var(--pw-border-subtle)}.proxy-endpoint{display:grid;grid-template-columns:auto minmax(150px,auto) minmax(180px,1fr);align-items:center;gap:10px}.proxy-endpoint input:disabled{opacity:.55}.primary-action{background:var(--pw-accent)!important;color:var(--pw-text-inverse)!important}.status-text{font-size:12px;color:var(--pw-text-secondary)}.credential-panel{border-top:1px solid var(--pw-border)}.credential-heading{display:flex;align-items:flex-start;justify-content:space-between;gap:16px;padding:16px 18px}.credential-state{padding:3px 7px;background:var(--pw-bg-hover);border-radius:4px;font-size:11px;white-space:nowrap}.credential-grid{display:grid;grid-template-columns:150px minmax(180px,1fr) minmax(220px,1.4fr);gap:12px;padding:0 18px 16px}.credential-grid label{display:grid;gap:6px;color:var(--pw-text-secondary);font-size:11px}.credential-actions{padding-top:0}.install-guide{margin:0 18px 18px;padding:12px 14px;border:1px solid var(--pw-border-subtle);border-radius:6px;color:var(--pw-text-secondary);font-size:12px}.install-guide summary{cursor:pointer;color:var(--pw-text-primary);font-weight:600}.install-guide ol{margin:10px 0 0 20px;line-height:1.7}.break-path{overflow-wrap:anywhere}.mono{font-family:var(--pw-font-mono)}#doctorOutput{max-height:320px;margin:0 18px 18px;padding:14px;overflow:auto;color:var(--pw-code-text,#dbe8e3);background:var(--pw-code-bg);border-radius:6px;font:11px/1.5 var(--pw-font-mono);white-space:pre-wrap}.hidden{display:none}@media(max-width:760px){.setting-row,.agent-setting-row{grid-template-columns:1fr}.agent-controls{grid-template-columns:1fr}.proxy-grid,.credential-grid{grid-template-columns:1fr}.proxy-endpoint{grid-template-columns:1fr}.inline-actions{justify-content:flex-start}}
96
+ </style>
97
+ <script src="/i18n.js"></script>
98
+ <script src="/desktop-bridge.js"></script>
99
+ <script src="/settings.js"></script>
100
+ </body>
101
+ </html>
@@ -135,6 +135,8 @@ html.dark {
135
135
  .pw-spin { animation: pw-spin 1s linear infinite; }
136
136
  @keyframes pw-spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
137
137
  </style>
138
+ <script src="/desktop-bootstrap.js"></script>
139
+ <link rel="stylesheet" href="/desktop.css">
138
140
  </head>
139
141
  <body class="flex h-screen overflow-hidden">
140
142
 
@@ -152,6 +154,10 @@ html.dark {
152
154
 
153
155
  <!-- Navigation -->
154
156
  <nav class="flex-1 flex flex-col gap-1 px-3 pt-4 overflow-y-auto">
157
+ <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);">
158
+ <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>
159
+ <span class="truncate" data-i18n="nav.gettingStarted">开始使用</span>
160
+ </a>
155
161
 
156
162
  <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);">
157
163
  <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">
@@ -207,6 +213,10 @@ html.dark {
207
213
  <span class="truncate" data-zh="日志" data-en="Logs">日志</span>
208
214
  </a>
209
215
 
216
+ <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);">
217
+ <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>
218
+ <span class="truncate" data-i18n="nav.settings">设置</span>
219
+ </a>
210
220
  </nav>
211
221
 
212
222
  <!-- Bottom Section -->
@@ -1243,5 +1253,7 @@ html.dark {
1243
1253
  </script>
1244
1254
 
1245
1255
  <script>lucide.createIcons();</script>
1256
+ <script src="/i18n.js"></script>
1257
+ <script src="/desktop-bridge.js"></script>
1246
1258
  </body>
1247
1259
  </html>
@@ -140,6 +140,8 @@ html.dark {
140
140
  to { transform: rotate(360deg); }
141
141
  }
142
142
  </style>
143
+ <script src="/desktop-bootstrap.js"></script>
144
+ <link rel="stylesheet" href="/desktop.css">
143
145
  </head>
144
146
  <body class="flex h-screen overflow-hidden">
145
147
 
@@ -157,6 +159,10 @@ html.dark {
157
159
 
158
160
  <!-- Navigation -->
159
161
  <nav class="flex-1 flex flex-col gap-1 px-3 pt-4 overflow-y-auto">
162
+ <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);">
163
+ <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>
164
+ <span class="truncate" data-i18n="nav.gettingStarted">开始使用</span>
165
+ </a>
160
166
 
161
167
  <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);">
162
168
  <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">
@@ -212,6 +218,10 @@ html.dark {
212
218
  <span class="truncate" data-zh="日志" data-en="Logs">日志</span>
213
219
  </a>
214
220
 
221
+ <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);">
222
+ <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>
223
+ <span class="truncate" data-i18n="nav.settings">设置</span>
224
+ </a>
215
225
  </nav>
216
226
 
217
227
  <!-- Bottom Section -->
@@ -442,6 +452,9 @@ html.dark {
442
452
  </tbody>
443
453
  </table>
444
454
  </div>
455
+ <div id="loadMoreWrap" class="hidden px-4 py-3 text-center" style="border-top: 1px solid var(--pw-border-subtle);">
456
+ <button id="loadMoreBtn" class="px-3 py-1.5 rounded-md text-xs font-semibold" type="button" style="background-color: var(--pw-bg-hover); color: var(--pw-text-secondary); border: 1px solid var(--pw-border-subtle); font-family: var(--pw-font-sans);">加载更多</button>
457
+ </div>
445
458
  </div>
446
459
 
447
460
  <!-- ===== SAFE MODAL ===== -->
@@ -558,6 +571,22 @@ html.dark {
558
571
  border-width: 1px;
559
572
  border-style: solid;
560
573
  }
574
+ #taskTableBody td:nth-child(2) {
575
+ display: -webkit-box;
576
+ -webkit-line-clamp: 2;
577
+ -webkit-box-orient: vertical;
578
+ overflow: hidden;
579
+ }
580
+ @media (max-width: 1100px) {
581
+ #tableWrapper { overflow-x: visible; padding: 10px; }
582
+ #tableWrapper table { min-width: 0 !important; display: block; }
583
+ #tableWrapper thead { display: none; }
584
+ #taskTableBody, #taskTableBody tr { display: block; }
585
+ #taskTableBody tr { margin-bottom: 10px; padding: 12px; border: 1px solid var(--pw-border-subtle); border-radius: 6px; }
586
+ #taskTableBody td { display: block; padding: 4px 0 !important; text-align: left !important; max-width: none !important; }
587
+ #taskTableBody td:nth-child(5), #taskTableBody td:nth-child(6), #taskTableBody td:nth-child(7), #taskTableBody td:nth-child(9) { display: none; }
588
+ #taskTableBody td:nth-child(10) > div { display: flex; justify-content: flex-start; flex-wrap: wrap; }
589
+ }
561
590
  </style>
562
591
 
563
592
  <script>
@@ -593,6 +622,8 @@ html.dark {
593
622
  };
594
623
 
595
624
  var allTasks = [];
625
+ var nextCursor = null;
626
+ var totalTasks = 0;
596
627
  // currentFilters holds the active filter values for all dimensions.
597
628
  // repo_path / status / acceptance_status / agent are sent to the backend.
598
629
  // warning_type / date_from / date_to are applied client-side.
@@ -768,6 +799,7 @@ html.dark {
768
799
  hide($('errorState'));
769
800
  hide($('emptyState'));
770
801
  hide($('tableWrapper'));
802
+ hide($('loadMoreWrap'));
771
803
  $('countSummary').textContent = '--';
772
804
  }
773
805
 
@@ -785,6 +817,7 @@ html.dark {
785
817
  hide($('errorState'));
786
818
  show($('emptyState'));
787
819
  hide($('tableWrapper'));
820
+ hide($('loadMoreWrap'));
788
821
  }
789
822
 
790
823
  function showTable() {
@@ -868,8 +901,8 @@ html.dark {
868
901
 
869
902
  // Quick action buttons
870
903
  var quickActions =
871
- '<button class="pw-quick-btn" data-safe-action="safe-result" data-task-id="' + escapeAttr(taskId) + '" title="safe result" type="button"><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/></svg></button>'
872
- + '<button class="pw-quick-btn" data-safe-action="safe-audit" data-task-id="' + escapeAttr(taskId) + '" title="safe audit" type="button"><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 11l3 3L22 4"/><path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"/></svg></button>'
904
+ '<button class="pw-quick-btn" data-safe-action="safe-result" data-task-id="' + escapeAttr(taskId) + '" title="查看安全结果" aria-label="查看安全结果" type="button"><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/></svg></button>'
905
+ + '<button class="pw-quick-btn" data-safe-action="safe-audit" data-task-id="' + escapeAttr(taskId) + '" title="查看安全审计" aria-label="查看安全审计" type="button"><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 11l3 3L22 4"/><path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"/></svg></button>'
873
906
  + '<button class="pw-quick-btn" data-safe-action="safe-test-summary" data-task-id="' + escapeAttr(taskId) + '" title="safe test summary" type="button"><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="22 12 18 12 15 21 9 3 6 12 2 12"/></svg></button>'
874
907
  + '<button class="pw-quick-btn" data-safe-action="safe-diff-summary" data-task-id="' + escapeAttr(taskId) + '" title="safe diff summary" type="button"><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14"/><path d="M12 5l7 7-7 7"/></svg></button>'
875
908
  + '<button class="pw-quick-btn" data-copy-id="' + escapeAttr(taskId) + '" title="复制 task_id" type="button"><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg></button>'
@@ -986,14 +1019,16 @@ html.dark {
986
1019
  showTable();
987
1020
  wireQuickActions();
988
1021
  }
1022
+ var more = $('loadMoreWrap');
1023
+ if (more) nextCursor ? show(more) : hide(more);
989
1024
 
990
1025
  // Count summary
991
1026
  var hasFilters = currentFilters.repo_path || currentFilters.status || currentFilters.acceptance_status ||
992
1027
  currentFilters.agent || currentFilters.warning_type || currentFilters.date_from || currentFilters.date_to;
993
1028
  if (hasFilters) {
994
- $('countSummary').textContent = '显示 ' + filtered.length + ' / ' + allTasks.length + ' 条';
1029
+ $('countSummary').textContent = '显示 ' + filtered.length + ' / ' + totalTasks + ' 条';
995
1030
  } else {
996
- $('countSummary').textContent = ' ' + filtered.length + ' 条';
1031
+ $('countSummary').textContent = '已加载 ' + filtered.length + ' / ' + totalTasks + ' 条';
997
1032
  }
998
1033
  }
999
1034
 
@@ -1031,6 +1066,10 @@ html.dark {
1031
1066
  if (currentFilters.agent) {
1032
1067
  params.push('agent=' + encodeURIComponent(currentFilters.agent));
1033
1068
  }
1069
+ if (currentFilters.warning_type) params.push('warning_type=' + encodeURIComponent(currentFilters.warning_type));
1070
+ if (currentFilters.date_from) params.push('date_from=' + encodeURIComponent(currentFilters.date_from));
1071
+ if (currentFilters.date_to) params.push('date_to=' + encodeURIComponent(currentFilters.date_to));
1072
+ params.push('limit=50');
1034
1073
  return params.length ? ('?' + params.join('&')) : '';
1035
1074
  }
1036
1075
 
@@ -1084,10 +1123,11 @@ html.dark {
1084
1123
  }
1085
1124
  }
1086
1125
 
1087
- function fetchTasks() {
1088
- showLoading();
1126
+ function fetchTasks(cursor, append) {
1127
+ if (!append) showLoading();
1089
1128
  setRefreshing(true);
1090
- var url = API_URL + buildFilterQueryString();
1129
+ var query = buildFilterQueryString();
1130
+ var url = API_URL + query + (query ? '&' : '?') + (cursor ? 'cursor=' + encodeURIComponent(cursor) : '');
1091
1131
  fetch(url, {
1092
1132
  method: 'GET',
1093
1133
  headers: { 'Accept': 'application/json' },
@@ -1102,7 +1142,10 @@ html.dark {
1102
1142
  return res.json();
1103
1143
  })
1104
1144
  .then(function (data) {
1105
- allTasks = (data && Array.isArray(data.tasks)) ? data.tasks : [];
1145
+ var page = (data && Array.isArray(data.tasks)) ? data.tasks : [];
1146
+ allTasks = append ? allTasks.concat(page) : page;
1147
+ nextCursor = data && (data.nextCursor || data.next_cursor) ? (data.nextCursor || data.next_cursor) : null;
1148
+ totalTasks = data && typeof data.total === 'number' ? data.total : allTasks.length;
1106
1149
  renderWatcher(data && data.watcher);
1107
1150
  renderTasks();
1108
1151
  updateFilterStatusText();
@@ -1179,6 +1222,7 @@ html.dark {
1179
1222
  });
1180
1223
 
1181
1224
  $('refreshBtn').addEventListener('click', fetchTasks);
1225
+ $('loadMoreBtn').addEventListener('click', function () { if (nextCursor) fetchTasks(nextCursor, true); });
1182
1226
 
1183
1227
  var retryBtn = $('retryBtn');
1184
1228
  if (retryBtn) {
@@ -1235,5 +1279,7 @@ html.dark {
1235
1279
  </script>
1236
1280
 
1237
1281
  <script>lucide.createIcons();</script>
1282
+ <script src="/i18n.js"></script>
1283
+ <script src="/desktop-bridge.js"></script>
1238
1284
  </body>
1239
1285
  </html>