patchwarden 1.5.0 → 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 (597) hide show
  1. package/PatchWarden.cmd +32 -2
  2. package/README.en.md +194 -26
  3. package/README.md +160 -22
  4. package/dist/assessments/agentAssessor.d.ts +1 -1
  5. package/dist/assessments/agentAssessor.js +24 -15
  6. package/dist/assessments/assessmentStore.d.ts +1 -1
  7. package/dist/assessments/assessmentStore.js +44 -36
  8. package/dist/assessments/confirmCli.js +5 -4
  9. package/dist/config.d.ts +22 -0
  10. package/dist/config.js +98 -11
  11. package/dist/control/fileManager.d.ts +2 -0
  12. package/dist/control/fileManager.js +39 -0
  13. package/dist/control/middleware/auth.d.ts +16 -0
  14. package/dist/control/middleware/auth.js +17 -0
  15. package/dist/control/middleware/static.d.ts +3 -0
  16. package/dist/control/middleware/static.js +78 -0
  17. package/dist/control/routeTable.d.ts +24 -0
  18. package/dist/control/routeTable.js +263 -0
  19. package/dist/control/routes/audit.d.ts +15 -0
  20. package/dist/control/routes/audit.js +287 -0
  21. package/dist/control/routes/evidence.d.ts +4 -0
  22. package/dist/control/routes/evidence.js +96 -0
  23. package/dist/control/routes/lineage.d.ts +3 -0
  24. package/dist/control/routes/lineage.js +71 -0
  25. package/dist/control/routes/policy.d.ts +3 -0
  26. package/dist/control/routes/policy.js +81 -0
  27. package/dist/control/routes/process.d.ts +12 -0
  28. package/dist/control/routes/process.js +487 -0
  29. package/dist/control/routes/sessions.d.ts +30 -0
  30. package/dist/control/routes/sessions.js +287 -0
  31. package/dist/control/routes/status.d.ts +51 -0
  32. package/dist/control/routes/status.js +307 -0
  33. package/dist/control/routes/taskActions.d.ts +21 -0
  34. package/dist/control/routes/taskActions.js +197 -0
  35. package/dist/control/routes/tasks.d.ts +24 -0
  36. package/dist/control/routes/tasks.js +309 -0
  37. package/dist/control/routes/workspace.d.ts +15 -0
  38. package/dist/control/routes/workspace.js +220 -0
  39. package/dist/control/runtime.d.ts +92 -0
  40. package/dist/control/runtime.js +425 -0
  41. package/dist/control/server.d.ts +13 -0
  42. package/dist/control/server.js +150 -0
  43. package/dist/control/shared.d.ts +59 -0
  44. package/dist/control/shared.js +341 -0
  45. package/dist/controlCenter.d.ts +6 -0
  46. package/dist/controlCenter.js +7 -2197
  47. package/dist/direct/directAudit.js +234 -225
  48. package/dist/direct/directGuards.d.ts +2 -1
  49. package/dist/direct/directGuards.js +72 -37
  50. package/dist/direct/directPatch.d.ts +6 -1
  51. package/dist/direct/directPatch.js +55 -8
  52. package/dist/direct/directSessionStore.d.ts +16 -6
  53. package/dist/direct/directSessionStore.js +127 -35
  54. package/dist/direct/directVerification.js +8 -16
  55. package/dist/doctor.d.ts +18 -1
  56. package/dist/doctor.js +614 -373
  57. package/dist/goal/acceptanceEngine.d.ts +1 -1
  58. package/dist/goal/goalProgress.js +75 -69
  59. package/dist/goal/goalReport.d.ts +54 -0
  60. package/dist/goal/goalReport.js +197 -0
  61. package/dist/goal/goalStatus.d.ts +8 -0
  62. package/dist/goal/goalStatus.js +10 -3
  63. package/dist/goal/goalStore.d.ts +9 -2
  64. package/dist/goal/goalStore.js +152 -45
  65. package/dist/goal/handoffExport.js +6 -6
  66. package/dist/goal/specKitImport.d.ts +63 -0
  67. package/dist/goal/specKitImport.js +221 -0
  68. package/dist/goal/subgoalSync.js +13 -10
  69. package/dist/goal/worktreeManager.d.ts +2 -0
  70. package/dist/goal/worktreeManager.js +209 -136
  71. package/dist/httpServer.js +126 -44
  72. package/dist/index.js +7 -4
  73. package/dist/logging.d.ts +7 -1
  74. package/dist/logging.js +13 -3
  75. package/dist/policy/projectPolicy.js +5 -2
  76. package/dist/release/releaseGate.d.ts +1 -1
  77. package/dist/release/releaseGate.js +78 -25
  78. package/dist/runner/agentInvocation.d.ts +14 -0
  79. package/dist/runner/agentInvocation.js +96 -4
  80. package/dist/runner/changeCapture.d.ts +14 -6
  81. package/dist/runner/changeCapture.js +287 -81
  82. package/dist/runner/cli.js +7 -6
  83. package/dist/runner/postTaskCleanup.js +60 -7
  84. package/dist/runner/processSecurity.d.ts +56 -0
  85. package/dist/runner/processSecurity.js +373 -0
  86. package/dist/runner/runTask.d.ts +1 -1
  87. package/dist/runner/runTask.js +339 -287
  88. package/dist/runner/simpleProcess.d.ts +3 -0
  89. package/dist/runner/simpleProcess.js +52 -38
  90. package/dist/{taskProgress.d.ts → runner/taskProgress.d.ts} +1 -1
  91. package/dist/{taskProgress.js → runner/taskProgress.js} +2 -2
  92. package/dist/{taskRuntime.d.ts → runner/taskRuntime.d.ts} +1 -1
  93. package/dist/{taskRuntime.js → runner/taskRuntime.js} +3 -2
  94. package/dist/runner/taskStatusStore.d.ts +11 -0
  95. package/dist/runner/taskStatusStore.js +24 -0
  96. package/dist/runner/watch.d.ts +14 -1
  97. package/dist/runner/watch.js +265 -54
  98. package/dist/security/contentRedaction.d.ts +6 -0
  99. package/dist/security/contentRedaction.js +24 -2
  100. package/dist/security/discoveryTokenStore.d.ts +3 -2
  101. package/dist/security/discoveryTokenStore.js +17 -2
  102. package/dist/security/loopbackHost.d.ts +2 -0
  103. package/dist/security/loopbackHost.js +16 -0
  104. package/dist/security/pathGuard.js +32 -17
  105. package/dist/security/planGuard.js +2 -1
  106. package/dist/security/runtimeGuard.js +9 -6
  107. package/dist/security/secretComparison.d.ts +3 -0
  108. package/dist/security/secretComparison.js +11 -0
  109. package/dist/security/sensitiveGuard.d.ts +5 -0
  110. package/dist/security/sensitiveGuard.js +27 -8
  111. package/dist/security/toolInvocationGuard.d.ts +2 -2
  112. package/dist/security/toolInvocationGuard.js +1 -1
  113. package/dist/security/workspaceRootGuard.d.ts +8 -0
  114. package/dist/security/workspaceRootGuard.js +47 -0
  115. package/dist/smoke-test.js +280 -273
  116. package/dist/tools/catalog/index.d.ts +4 -0
  117. package/dist/tools/catalog/index.js +4 -0
  118. package/dist/tools/{toolCatalog.js → catalog/toolCatalog.js} +3 -12
  119. package/dist/tools/{toolRegistry.d.ts → catalog/toolRegistry.d.ts} +3 -3
  120. package/dist/tools/{toolRegistry.js → catalog/toolRegistry.js} +28 -15
  121. package/dist/tools/definitions/toolDefs.d.ts +18 -0
  122. package/dist/tools/definitions/toolDefs.js +1177 -0
  123. package/dist/tools/{auditSession.d.ts → diagnostics/auditSession.d.ts} +1 -1
  124. package/dist/tools/{auditSession.js → diagnostics/auditSession.js} +2 -2
  125. package/dist/tools/{auditTask.d.ts → diagnostics/auditTask.d.ts} +8 -63
  126. package/dist/tools/{auditTask.js → diagnostics/auditTask.js} +111 -37
  127. package/dist/tools/{healthCheck.d.ts → diagnostics/healthCheck.d.ts} +6 -6
  128. package/dist/tools/{healthCheck.js → diagnostics/healthCheck.js} +10 -10
  129. package/dist/tools/diagnostics/index.d.ts +6 -0
  130. package/dist/tools/diagnostics/index.js +6 -0
  131. package/dist/tools/{safeStatus.d.ts → diagnostics/safeStatus.d.ts} +3 -3
  132. package/dist/tools/{safeStatus.js → diagnostics/safeStatus.js} +6 -6
  133. package/dist/tools/{safeViews.d.ts → diagnostics/safeViews.d.ts} +14 -10
  134. package/dist/tools/{safeViews.js → diagnostics/safeViews.js} +25 -15
  135. package/dist/tools/{schemaDriftCheck.d.ts → diagnostics/schemaDriftCheck.d.ts} +1 -1
  136. package/dist/tools/{schemaDriftCheck.js → diagnostics/schemaDriftCheck.js} +2 -2
  137. package/dist/tools/{createDirectSession.d.ts → direct/createDirectSession.d.ts} +1 -1
  138. package/dist/tools/{createDirectSession.js → direct/createDirectSession.js} +8 -8
  139. package/dist/tools/{finalizeDirectSession.d.ts → direct/finalizeDirectSession.d.ts} +2 -2
  140. package/dist/tools/direct/finalizeDirectSession.js +86 -0
  141. package/dist/tools/direct/index.d.ts +3 -0
  142. package/dist/tools/direct/index.js +3 -0
  143. package/dist/tools/{runDirectVerificationBundle.js → direct/runDirectVerificationBundle.js} +2 -2
  144. package/dist/tools/{discoverTools.d.ts → discovery/discoverTools.d.ts} +2 -2
  145. package/dist/tools/{discoverTools.js → discovery/discoverTools.js} +5 -5
  146. package/dist/tools/{explainTool.d.ts → discovery/explainTool.d.ts} +2 -2
  147. package/dist/tools/{explainTool.js → discovery/explainTool.js} +2 -2
  148. package/dist/tools/discovery/index.d.ts +3 -0
  149. package/dist/tools/discovery/index.js +3 -0
  150. package/dist/tools/{invokeDiscoveredTool.d.ts → discovery/invokeDiscoveredTool.d.ts} +2 -2
  151. package/dist/tools/{invokeDiscoveredTool.js → discovery/invokeDiscoveredTool.js} +5 -5
  152. package/dist/tools/dispatch/coreDispatch.d.ts +9 -0
  153. package/dist/tools/dispatch/coreDispatch.js +283 -0
  154. package/dist/tools/dispatch/diagnosticDispatch.d.ts +12 -0
  155. package/dist/tools/dispatch/diagnosticDispatch.js +80 -0
  156. package/dist/tools/dispatch/directDispatch.d.ts +8 -0
  157. package/dist/tools/dispatch/directDispatch.js +116 -0
  158. package/dist/tools/dispatch/goalDispatch.d.ts +8 -0
  159. package/dist/tools/dispatch/goalDispatch.js +92 -0
  160. package/dist/tools/dispatch/releaseDispatch.d.ts +8 -0
  161. package/dist/tools/dispatch/releaseDispatch.js +46 -0
  162. package/dist/tools/dispatch/types.d.ts +23 -0
  163. package/dist/tools/dispatch/types.js +15 -0
  164. package/dist/tools/dispatch/validation.d.ts +7 -0
  165. package/dist/tools/dispatch/validation.js +71 -0
  166. package/dist/tools/{getPlan.js → goals/getPlan.js} +4 -4
  167. package/dist/tools/{goalSubgoalTask.d.ts → goals/goalSubgoalTask.d.ts} +2 -2
  168. package/dist/tools/goals/goalSubgoalTask.js +118 -0
  169. package/dist/tools/goals/index.d.ts +3 -0
  170. package/dist/tools/goals/index.js +3 -0
  171. package/dist/tools/{savePlan.js → goals/savePlan.js} +28 -8
  172. package/dist/tools/registry.d.ts +10 -14
  173. package/dist/tools/registry.js +47 -1651
  174. package/dist/tools/{checkReleaseGate.d.ts → release/checkReleaseGate.d.ts} +1 -1
  175. package/dist/tools/{checkReleaseGate.js → release/checkReleaseGate.js} +3 -3
  176. package/dist/tools/release/index.d.ts +2 -0
  177. package/dist/tools/release/index.js +2 -0
  178. package/dist/tools/{releaseMode.d.ts → release/releaseMode.d.ts} +2 -2
  179. package/dist/tools/{releaseMode.js → release/releaseMode.js} +46 -26
  180. package/dist/tools/tasks/cancelTask.d.ts +11 -0
  181. package/dist/tools/tasks/cancelTask.js +85 -0
  182. package/dist/tools/{createTask.d.ts → tasks/createTask.d.ts} +12 -4
  183. package/dist/tools/{createTask.js → tasks/createTask.js} +46 -31
  184. package/dist/tools/{diagnoseTask.d.ts → tasks/diagnoseTask.d.ts} +1 -1
  185. package/dist/tools/{diagnoseTask.js → tasks/diagnoseTask.js} +8 -8
  186. package/dist/tools/{evidencePack.d.ts → tasks/evidencePack.d.ts} +6 -0
  187. package/dist/tools/tasks/evidencePack.js +387 -0
  188. package/dist/tools/{getTaskFile.js → tasks/getTaskFile.js} +4 -4
  189. package/dist/tools/{getTaskStatus.d.ts → tasks/getTaskStatus.d.ts} +6 -1
  190. package/dist/tools/{getTaskStatus.js → tasks/getTaskStatus.js} +5 -5
  191. package/dist/tools/{getTaskStdoutTail.js → tasks/getTaskStdoutTail.js} +13 -11
  192. package/dist/tools/{getTaskSummary.js → tasks/getTaskSummary.js} +33 -26
  193. package/dist/tools/tasks/index.d.ts +18 -0
  194. package/dist/tools/tasks/index.js +18 -0
  195. package/dist/tools/tasks/killTask.d.ts +1 -0
  196. package/dist/tools/{listTasks.d.ts → tasks/listTasks.d.ts} +1 -1
  197. package/dist/tools/{listTasks.js → tasks/listTasks.js} +3 -3
  198. package/dist/tools/{reconcileTasks.d.ts → tasks/reconcileTasks.d.ts} +1 -1
  199. package/dist/tools/{reconcileTasks.js → tasks/reconcileTasks.js} +64 -66
  200. package/dist/tools/{retryTask.d.ts → tasks/retryTask.d.ts} +2 -2
  201. package/dist/tools/tasks/retryTask.js +60 -0
  202. package/dist/tools/{runTaskLoop.d.ts → tasks/runTaskLoop.d.ts} +6 -6
  203. package/dist/tools/{runTaskLoop.js → tasks/runTaskLoop.js} +59 -31
  204. package/dist/tools/tasks/runVerification.js +38 -0
  205. package/dist/tools/{taskLineage.js → tasks/taskLineage.js} +8 -7
  206. package/dist/tools/{taskOutputs.js → tasks/taskOutputs.js} +17 -16
  207. package/dist/tools/{androidDoctor.js → workspace/androidDoctor.js} +16 -10
  208. package/dist/tools/{applyPatch.d.ts → workspace/applyPatch.d.ts} +1 -1
  209. package/dist/tools/workspace/applyPatch.js +53 -0
  210. package/dist/tools/{discardWorktree.js → workspace/discardWorktree.js} +3 -3
  211. package/dist/tools/workspace/index.d.ts +10 -0
  212. package/dist/tools/workspace/index.js +10 -0
  213. package/dist/tools/{listAgents.d.ts → workspace/listAgents.d.ts} +5 -0
  214. package/dist/tools/{listAgents.js → workspace/listAgents.js} +8 -4
  215. package/dist/tools/{listWorkspace.js → workspace/listWorkspace.js} +3 -3
  216. package/dist/tools/{mergeWorktree.js → workspace/mergeWorktree.js} +3 -3
  217. package/dist/tools/{readWorkspaceFile.js → workspace/readWorkspaceFile.js} +8 -8
  218. package/dist/tools/{recommendAgentForTask.js → workspace/recommendAgentForTask.js} +3 -3
  219. package/dist/tools/{searchWorkspace.js → workspace/searchWorkspace.js} +5 -5
  220. package/dist/tools/{syncFile.d.ts → workspace/syncFile.d.ts} +1 -1
  221. package/dist/tools/workspace/syncFile.js +134 -0
  222. package/dist/utils/atomicFile.d.ts +7 -0
  223. package/dist/utils/atomicFile.js +48 -0
  224. package/dist/utils/boundedFile.d.ts +8 -0
  225. package/dist/utils/boundedFile.js +93 -0
  226. package/dist/utils/lockedJsonFile.d.ts +15 -0
  227. package/dist/utils/lockedJsonFile.js +224 -0
  228. package/dist/utils/platform.d.ts +11 -0
  229. package/dist/utils/platform.js +26 -0
  230. package/dist/utils/stableJson.d.ts +2 -0
  231. package/dist/utils/stableJson.js +12 -0
  232. package/dist/version.d.ts +3 -2
  233. package/dist/version.js +3 -2
  234. package/dist/watcherStatus.d.ts +2 -2
  235. package/dist/watcherStatus.js +18 -0
  236. package/docs/CODE_WIKI.md +1648 -0
  237. package/docs/agentseal-integration.md +150 -0
  238. package/docs/architecture.md +23 -0
  239. package/docs/assets/patchwarden-desktop-onboarding.png +0 -0
  240. package/docs/assets/patchwarden-oss-demo.gif +0 -0
  241. package/docs/control-center/README.md +4 -4
  242. package/docs/dashboard-overview.md +86 -0
  243. package/docs/demo.md +8 -0
  244. package/docs/desktop-app.md +175 -0
  245. package/docs/direct-session-workflow.md +98 -0
  246. package/docs/evidence-pack-schema.md +215 -0
  247. package/docs/lineage-evidence-pack-workflow.md +127 -0
  248. package/docs/mcp-inspector-testing.md +200 -0
  249. package/docs/open-source-application.md +168 -0
  250. package/docs/opencode-worker.md +151 -0
  251. package/docs/openhands-worker.md +181 -0
  252. package/docs/release-checklist.md +1 -1
  253. package/docs/release-evidence.md +82 -0
  254. package/docs/spec-kit-integration.md +131 -0
  255. package/docs/task-safe-review-workflow.md +98 -0
  256. package/docs/threat-model.md +97 -0
  257. package/docs/user-feedback.md +40 -0
  258. package/docs/why-patchwarden.md +110 -0
  259. package/examples/config.example.json +4 -2
  260. package/package.json +28 -11
  261. package/scripts/README.md +15 -6
  262. package/scripts/build.js +11 -0
  263. package/scripts/checks/brand-check.js +3 -0
  264. package/scripts/checks/build-output-check.js +37 -0
  265. package/scripts/checks/control-center-smoke.js +447 -6
  266. package/scripts/checks/control-smoke.js +19 -8
  267. package/scripts/checks/http-mcp-smoke.js +82 -4
  268. package/scripts/checks/lifecycle-smoke.js +39 -33
  269. package/scripts/checks/mcp-manifest-check.js +1 -1
  270. package/scripts/checks/mcp-smoke.js +30 -4
  271. package/scripts/checks/package-manifest-check.js +20 -7
  272. package/scripts/checks/unit-tests.js +3 -5
  273. package/scripts/checks/watcher-supervisor-smoke.js +9 -0
  274. package/scripts/control/get-patchwarden-health.ps1 +1 -1
  275. package/scripts/control/manage-patchwarden.ps1 +69 -4
  276. package/scripts/control/provision-patchwarden-tunnel.ps1 +127 -0
  277. package/scripts/control/run-background-supervisor.ps1 +41 -0
  278. package/scripts/control/start-patchwarden-tunnel.ps1 +126 -32
  279. package/scripts/generate-demo-gif.py +320 -0
  280. package/{PatchWarden-Control-Tray.cmd → scripts/launchers/PatchWarden-Control-Tray.cmd} +3 -3
  281. package/scripts/launchers/PatchWarden-Control.cmd +6 -0
  282. package/{PatchWarden-Desktop.cmd → scripts/launchers/PatchWarden-Desktop.cmd} +2 -2
  283. package/scripts/launchers/Restart-PatchWarden-Control.cmd +6 -0
  284. package/{Stop-PatchWarden.cmd → scripts/launchers/Stop-PatchWarden.cmd} +2 -2
  285. package/scripts/lib/clean-generated-output.js +11 -0
  286. package/scripts/lib/file-discovery.js +12 -0
  287. package/scripts/release/desktop-preflight.js +211 -0
  288. package/scripts/release/pack-clean.js +59 -14
  289. package/src/assessments/agentAssessor.ts +29 -15
  290. package/src/assessments/assessmentStore.ts +55 -43
  291. package/src/assessments/confirmCli.ts +5 -4
  292. package/src/config.ts +105 -11
  293. package/src/control/fileManager.ts +40 -0
  294. package/src/control/middleware/auth.ts +35 -0
  295. package/src/control/middleware/static.ts +84 -0
  296. package/src/control/routeTable.ts +301 -0
  297. package/src/control/routes/audit.ts +329 -0
  298. package/src/control/routes/evidence.ts +107 -0
  299. package/src/control/routes/lineage.ts +92 -0
  300. package/src/control/routes/policy.ts +81 -0
  301. package/src/control/routes/process.ts +505 -0
  302. package/src/control/routes/sessions.ts +314 -0
  303. package/src/control/routes/status.ts +405 -0
  304. package/src/control/routes/taskActions.ts +214 -0
  305. package/src/control/routes/tasks.ts +331 -0
  306. package/src/control/routes/workspace.ts +234 -0
  307. package/src/control/runtime.ts +508 -0
  308. package/src/control/server.ts +165 -0
  309. package/src/control/shared.ts +360 -0
  310. package/src/controlCenter.ts +7 -2347
  311. package/src/direct/directAudit.ts +17 -9
  312. package/src/direct/directGuards.ts +100 -51
  313. package/src/direct/directPatch.ts +95 -8
  314. package/src/direct/directSessionStore.ts +211 -55
  315. package/src/direct/directVerification.ts +9 -23
  316. package/src/doctor.ts +766 -492
  317. package/src/goal/acceptanceEngine.ts +1 -1
  318. package/src/goal/goalProgress.ts +17 -13
  319. package/src/goal/goalReport.ts +263 -0
  320. package/src/goal/goalStatus.ts +25 -3
  321. package/src/goal/goalStore.ts +241 -54
  322. package/src/goal/handoffExport.ts +6 -6
  323. package/src/goal/specKitImport.ts +355 -0
  324. package/src/goal/subgoalSync.ts +15 -10
  325. package/src/goal/worktreeManager.ts +139 -22
  326. package/src/httpServer.ts +130 -44
  327. package/src/index.ts +7 -4
  328. package/src/logging.ts +20 -4
  329. package/src/policy/projectPolicy.ts +5 -2
  330. package/src/release/releaseGate.ts +74 -27
  331. package/src/runner/agentInvocation.ts +126 -4
  332. package/src/runner/changeCapture.ts +342 -80
  333. package/src/runner/cli.ts +7 -6
  334. package/src/runner/postTaskCleanup.ts +55 -7
  335. package/src/runner/processSecurity.ts +433 -0
  336. package/src/runner/runTask.ts +441 -292
  337. package/src/runner/simpleProcess.ts +68 -33
  338. package/src/{taskProgress.ts → runner/taskProgress.ts} +3 -3
  339. package/src/{taskRuntime.ts → runner/taskRuntime.ts} +4 -3
  340. package/src/runner/taskStatusStore.ts +54 -0
  341. package/src/runner/watch.ts +290 -57
  342. package/src/security/contentRedaction.ts +31 -2
  343. package/src/security/discoveryTokenStore.ts +18 -4
  344. package/src/security/loopbackHost.ts +19 -0
  345. package/src/security/pathGuard.ts +35 -16
  346. package/src/security/planGuard.ts +2 -1
  347. package/src/security/runtimeGuard.ts +9 -6
  348. package/src/security/secretComparison.ts +13 -0
  349. package/src/security/sensitiveGuard.ts +27 -8
  350. package/src/security/toolInvocationGuard.ts +2 -2
  351. package/src/security/workspaceRootGuard.ts +53 -0
  352. package/src/smoke-test.ts +276 -273
  353. package/src/tools/catalog/index.ts +4 -0
  354. package/src/tools/{toolCatalog.ts → catalog/toolCatalog.ts} +3 -12
  355. package/src/tools/{toolRegistry.ts → catalog/toolRegistry.ts} +29 -16
  356. package/src/tools/definitions/toolDefs.ts +1264 -0
  357. package/src/tools/{auditSession.ts → diagnostics/auditSession.ts} +2 -2
  358. package/src/tools/{auditTask.ts → diagnostics/auditTask.ts} +196 -89
  359. package/src/tools/{healthCheck.ts → diagnostics/healthCheck.ts} +11 -11
  360. package/src/tools/diagnostics/index.ts +6 -0
  361. package/src/tools/{safeStatus.ts → diagnostics/safeStatus.ts} +7 -7
  362. package/src/tools/{safeViews.ts → diagnostics/safeViews.ts} +29 -19
  363. package/src/tools/{schemaDriftCheck.ts → diagnostics/schemaDriftCheck.ts} +3 -3
  364. package/src/tools/{createDirectSession.ts → direct/createDirectSession.ts} +9 -9
  365. package/src/tools/{finalizeDirectSession.ts → direct/finalizeDirectSession.ts} +11 -9
  366. package/src/tools/direct/index.ts +3 -0
  367. package/src/tools/{runDirectVerificationBundle.ts → direct/runDirectVerificationBundle.ts} +2 -2
  368. package/src/tools/{discoverTools.ts → discovery/discoverTools.ts} +6 -6
  369. package/src/tools/{explainTool.ts → discovery/explainTool.ts} +3 -3
  370. package/src/tools/discovery/index.ts +3 -0
  371. package/src/tools/{invokeDiscoveredTool.ts → discovery/invokeDiscoveredTool.ts} +7 -7
  372. package/src/tools/dispatch/coreDispatch.ts +379 -0
  373. package/src/tools/dispatch/diagnosticDispatch.ts +106 -0
  374. package/src/tools/dispatch/directDispatch.ts +168 -0
  375. package/src/tools/dispatch/goalDispatch.ts +128 -0
  376. package/src/tools/dispatch/releaseDispatch.ts +66 -0
  377. package/src/tools/dispatch/types.ts +24 -0
  378. package/src/tools/dispatch/validation.ts +78 -0
  379. package/src/tools/{getPlan.ts → goals/getPlan.ts} +4 -4
  380. package/src/tools/{goalSubgoalTask.ts → goals/goalSubgoalTask.ts} +43 -28
  381. package/src/tools/goals/index.ts +3 -0
  382. package/src/tools/{savePlan.ts → goals/savePlan.ts} +38 -8
  383. package/src/tools/registry.ts +54 -1858
  384. package/src/tools/{checkReleaseGate.ts → release/checkReleaseGate.ts} +3 -3
  385. package/src/tools/release/index.ts +2 -0
  386. package/src/tools/{releaseMode.ts → release/releaseMode.ts} +42 -24
  387. package/src/tools/tasks/cancelTask.ts +109 -0
  388. package/src/tools/{createTask.ts → tasks/createTask.ts} +56 -33
  389. package/src/tools/{diagnoseTask.ts → tasks/diagnoseTask.ts} +8 -8
  390. package/src/tools/tasks/evidencePack.ts +504 -0
  391. package/src/tools/{getTaskFile.ts → tasks/getTaskFile.ts} +4 -4
  392. package/src/tools/{getTaskStatus.ts → tasks/getTaskStatus.ts} +7 -5
  393. package/src/tools/{getTaskStdoutTail.ts → tasks/getTaskStdoutTail.ts} +13 -11
  394. package/src/tools/{getTaskSummary.ts → tasks/getTaskSummary.ts} +44 -33
  395. package/src/tools/tasks/index.ts +18 -0
  396. package/src/tools/{listTasks.ts → tasks/listTasks.ts} +4 -4
  397. package/src/tools/{reconcileTasks.ts → tasks/reconcileTasks.ts} +68 -66
  398. package/src/tools/tasks/retryTask.ts +69 -0
  399. package/src/tools/{runTaskLoop.ts → tasks/runTaskLoop.ts} +70 -35
  400. package/src/tools/{runVerification.ts → tasks/runVerification.ts} +6 -3
  401. package/src/tools/{taskLineage.ts → tasks/taskLineage.ts} +8 -7
  402. package/src/tools/{taskOutputs.ts → tasks/taskOutputs.ts} +27 -20
  403. package/src/tools/{androidDoctor.ts → workspace/androidDoctor.ts} +15 -10
  404. package/src/tools/{applyPatch.ts → workspace/applyPatch.ts} +38 -16
  405. package/src/tools/{discardWorktree.ts → workspace/discardWorktree.ts} +3 -3
  406. package/src/tools/workspace/index.ts +10 -0
  407. package/src/tools/{listAgents.ts → workspace/listAgents.ts} +11 -4
  408. package/src/tools/{listWorkspace.ts → workspace/listWorkspace.ts} +3 -3
  409. package/src/tools/{mergeWorktree.ts → workspace/mergeWorktree.ts} +3 -3
  410. package/src/tools/{readWorkspaceFile.ts → workspace/readWorkspaceFile.ts} +8 -8
  411. package/src/tools/{recommendAgentForTask.ts → workspace/recommendAgentForTask.ts} +3 -3
  412. package/src/tools/{searchWorkspace.ts → workspace/searchWorkspace.ts} +5 -5
  413. package/src/tools/workspace/syncFile.ts +292 -0
  414. package/src/utils/atomicFile.ts +58 -0
  415. package/src/utils/boundedFile.ts +116 -0
  416. package/src/utils/lockedJsonFile.ts +251 -0
  417. package/src/utils/platform.ts +31 -0
  418. package/src/utils/stableJson.ts +12 -0
  419. package/src/version.ts +3 -2
  420. package/src/watcherStatus.ts +19 -1
  421. package/ui/desktop-bootstrap.js +11 -0
  422. package/ui/desktop-bridge.js +36 -0
  423. package/ui/desktop.css +160 -0
  424. package/ui/getting-started.js +93 -0
  425. package/ui/i18n.js +484 -0
  426. package/ui/log-parser.js +30 -0
  427. package/ui/pages/audit.html +204 -3
  428. package/ui/pages/dashboard.html +937 -79
  429. package/ui/pages/direct-sessions.html +609 -60
  430. package/ui/pages/getting-started.html +43 -0
  431. package/ui/pages/logs.html +105 -22
  432. package/ui/pages/settings.html +101 -0
  433. package/ui/pages/task-detail.html +468 -438
  434. package/ui/pages/tasks.html +648 -65
  435. package/ui/pages/workspace.html +38 -1
  436. package/ui/settings.js +245 -0
  437. package/PatchWarden-Control.cmd +0 -6
  438. package/Restart-PatchWarden-Control.cmd +0 -6
  439. package/dist/test/unit/acceptance-engine.test.d.ts +0 -1
  440. package/dist/test/unit/acceptance-engine.test.js +0 -228
  441. package/dist/test/unit/agent-router.test.d.ts +0 -1
  442. package/dist/test/unit/agent-router.test.js +0 -287
  443. package/dist/test/unit/android-doctor.test.d.ts +0 -1
  444. package/dist/test/unit/android-doctor.test.js +0 -118
  445. package/dist/test/unit/audit-checks.test.d.ts +0 -1
  446. package/dist/test/unit/audit-checks.test.js +0 -350
  447. package/dist/test/unit/chinese-path.test.d.ts +0 -1
  448. package/dist/test/unit/chinese-path.test.js +0 -91
  449. package/dist/test/unit/command-guard.test.d.ts +0 -1
  450. package/dist/test/unit/command-guard.test.js +0 -160
  451. package/dist/test/unit/diagnose-task.test.d.ts +0 -1
  452. package/dist/test/unit/diagnose-task.test.js +0 -457
  453. package/dist/test/unit/direct-guards.test.d.ts +0 -1
  454. package/dist/test/unit/direct-guards.test.js +0 -213
  455. package/dist/test/unit/discovery-token-store.test.d.ts +0 -1
  456. package/dist/test/unit/discovery-token-store.test.js +0 -139
  457. package/dist/test/unit/evidence-pack.test.d.ts +0 -1
  458. package/dist/test/unit/evidence-pack.test.js +0 -130
  459. package/dist/test/unit/goal-graph.test.d.ts +0 -1
  460. package/dist/test/unit/goal-graph.test.js +0 -298
  461. package/dist/test/unit/goal-progress.test.d.ts +0 -1
  462. package/dist/test/unit/goal-progress.test.js +0 -381
  463. package/dist/test/unit/goal-status.test.d.ts +0 -1
  464. package/dist/test/unit/goal-status.test.js +0 -215
  465. package/dist/test/unit/goal-store.test.d.ts +0 -1
  466. package/dist/test/unit/goal-store.test.js +0 -253
  467. package/dist/test/unit/goal-subgoal-task.test.d.ts +0 -1
  468. package/dist/test/unit/goal-subgoal-task.test.js +0 -55
  469. package/dist/test/unit/goal-tools-registry.test.d.ts +0 -1
  470. package/dist/test/unit/goal-tools-registry.test.js +0 -190
  471. package/dist/test/unit/handoff-export.test.d.ts +0 -1
  472. package/dist/test/unit/handoff-export.test.js +0 -263
  473. package/dist/test/unit/invoke-discovered-tool.test.d.ts +0 -1
  474. package/dist/test/unit/invoke-discovered-tool.test.js +0 -167
  475. package/dist/test/unit/logging.test.d.ts +0 -1
  476. package/dist/test/unit/logging.test.js +0 -397
  477. package/dist/test/unit/path-guard.test.d.ts +0 -1
  478. package/dist/test/unit/path-guard.test.js +0 -109
  479. package/dist/test/unit/post-task-cleanup.test.d.ts +0 -1
  480. package/dist/test/unit/post-task-cleanup.test.js +0 -48
  481. package/dist/test/unit/project-policy-release-mode.test.d.ts +0 -1
  482. package/dist/test/unit/project-policy-release-mode.test.js +0 -125
  483. package/dist/test/unit/reconcile-tasks.test.d.ts +0 -1
  484. package/dist/test/unit/reconcile-tasks.test.js +0 -456
  485. package/dist/test/unit/release-gate.test.d.ts +0 -1
  486. package/dist/test/unit/release-gate.test.js +0 -242
  487. package/dist/test/unit/run-task-loop.test.d.ts +0 -1
  488. package/dist/test/unit/run-task-loop.test.js +0 -380
  489. package/dist/test/unit/safe-status.test.d.ts +0 -1
  490. package/dist/test/unit/safe-status.test.js +0 -165
  491. package/dist/test/unit/safe-views.test.d.ts +0 -1
  492. package/dist/test/unit/safe-views.test.js +0 -171
  493. package/dist/test/unit/schema-drift-check.test.d.ts +0 -1
  494. package/dist/test/unit/schema-drift-check.test.js +0 -176
  495. package/dist/test/unit/sensitive-guard.test.d.ts +0 -1
  496. package/dist/test/unit/sensitive-guard.test.js +0 -104
  497. package/dist/test/unit/subgoal-sync.test.d.ts +0 -1
  498. package/dist/test/unit/subgoal-sync.test.js +0 -183
  499. package/dist/test/unit/sync-file.test.d.ts +0 -1
  500. package/dist/test/unit/sync-file.test.js +0 -154
  501. package/dist/test/unit/tool-invocation-guard.test.d.ts +0 -1
  502. package/dist/test/unit/tool-invocation-guard.test.js +0 -432
  503. package/dist/test/unit/tool-usage-stats.test.d.ts +0 -1
  504. package/dist/test/unit/tool-usage-stats.test.js +0 -300
  505. package/dist/test/unit/toolSearch.test.d.ts +0 -1
  506. package/dist/test/unit/toolSearch.test.js +0 -571
  507. package/dist/test/unit/watcher-status.test.d.ts +0 -1
  508. package/dist/test/unit/watcher-status.test.js +0 -169
  509. package/dist/test/unit/worktree-manager.test.d.ts +0 -1
  510. package/dist/test/unit/worktree-manager.test.js +0 -176
  511. package/dist/tools/applyPatch.js +0 -41
  512. package/dist/tools/cancelTask.d.ts +0 -30
  513. package/dist/tools/cancelTask.js +0 -64
  514. package/dist/tools/evidencePack.js +0 -168
  515. package/dist/tools/finalizeDirectSession.js +0 -84
  516. package/dist/tools/goalSubgoalTask.js +0 -110
  517. package/dist/tools/killTask.d.ts +0 -15
  518. package/dist/tools/retryTask.js +0 -45
  519. package/dist/tools/runVerification.js +0 -36
  520. package/dist/tools/syncFile.js +0 -65
  521. package/docs/control-center/control-center-daily-driver.md +0 -211
  522. package/docs/control-center/control-center-mvp.md +0 -205
  523. package/docs/control-center/control-center-phase2.md +0 -159
  524. package/docs/release-v0.6.0.md +0 -71
  525. package/docs/release-v0.6.1.md +0 -75
  526. package/docs/release-v0.6.4.md +0 -45
  527. package/src/test/unit/acceptance-engine.test.ts +0 -261
  528. package/src/test/unit/agent-router.test.ts +0 -342
  529. package/src/test/unit/android-doctor.test.ts +0 -158
  530. package/src/test/unit/audit-checks.test.ts +0 -567
  531. package/src/test/unit/chinese-path.test.ts +0 -106
  532. package/src/test/unit/command-guard.test.ts +0 -221
  533. package/src/test/unit/diagnose-task.test.ts +0 -544
  534. package/src/test/unit/direct-guards.test.ts +0 -297
  535. package/src/test/unit/discovery-token-store.test.ts +0 -181
  536. package/src/test/unit/evidence-pack.test.ts +0 -142
  537. package/src/test/unit/goal-graph.test.ts +0 -347
  538. package/src/test/unit/goal-progress.test.ts +0 -538
  539. package/src/test/unit/goal-status.test.ts +0 -270
  540. package/src/test/unit/goal-store.test.ts +0 -318
  541. package/src/test/unit/goal-subgoal-task.test.ts +0 -72
  542. package/src/test/unit/goal-tools-registry.test.ts +0 -243
  543. package/src/test/unit/handoff-export.test.ts +0 -295
  544. package/src/test/unit/invoke-discovered-tool.test.ts +0 -216
  545. package/src/test/unit/logging.test.ts +0 -497
  546. package/src/test/unit/path-guard.test.ts +0 -150
  547. package/src/test/unit/post-task-cleanup.test.ts +0 -53
  548. package/src/test/unit/project-policy-release-mode.test.ts +0 -156
  549. package/src/test/unit/reconcile-tasks.test.ts +0 -551
  550. package/src/test/unit/release-gate.test.ts +0 -314
  551. package/src/test/unit/run-task-loop.test.ts +0 -425
  552. package/src/test/unit/safe-status.test.ts +0 -187
  553. package/src/test/unit/safe-views.test.ts +0 -184
  554. package/src/test/unit/schema-drift-check.test.ts +0 -260
  555. package/src/test/unit/sensitive-guard.test.ts +0 -124
  556. package/src/test/unit/subgoal-sync.test.ts +0 -236
  557. package/src/test/unit/sync-file.test.ts +0 -231
  558. package/src/test/unit/tool-invocation-guard.test.ts +0 -542
  559. package/src/test/unit/tool-usage-stats.test.ts +0 -384
  560. package/src/test/unit/toolSearch.test.ts +0 -756
  561. package/src/test/unit/watcher-status.test.ts +0 -190
  562. package/src/test/unit/worktree-manager.test.ts +0 -247
  563. package/src/tools/cancelTask.ts +0 -70
  564. package/src/tools/evidencePack.ts +0 -205
  565. package/src/tools/retryTask.ts +0 -53
  566. package/src/tools/syncFile.ts +0 -122
  567. /package/dist/tools/{toolCatalog.d.ts → catalog/toolCatalog.d.ts} +0 -0
  568. /package/dist/tools/{toolSearch.d.ts → catalog/toolSearch.d.ts} +0 -0
  569. /package/dist/tools/{toolSearch.js → catalog/toolSearch.js} +0 -0
  570. /package/dist/tools/{toolUsageStats.d.ts → catalog/toolUsageStats.d.ts} +0 -0
  571. /package/dist/tools/{toolUsageStats.js → catalog/toolUsageStats.js} +0 -0
  572. /package/dist/tools/{runDirectVerificationBundle.d.ts → direct/runDirectVerificationBundle.d.ts} +0 -0
  573. /package/dist/tools/{getPlan.d.ts → goals/getPlan.d.ts} +0 -0
  574. /package/dist/tools/{savePlan.d.ts → goals/savePlan.d.ts} +0 -0
  575. /package/dist/tools/{getTaskFile.d.ts → tasks/getTaskFile.d.ts} +0 -0
  576. /package/dist/tools/{getTaskProgress.d.ts → tasks/getTaskProgress.d.ts} +0 -0
  577. /package/dist/tools/{getTaskProgress.js → tasks/getTaskProgress.js} +0 -0
  578. /package/dist/tools/{getTaskStdoutTail.d.ts → tasks/getTaskStdoutTail.d.ts} +0 -0
  579. /package/dist/tools/{getTaskSummary.d.ts → tasks/getTaskSummary.d.ts} +0 -0
  580. /package/dist/tools/{killTask.js → tasks/killTask.js} +0 -0
  581. /package/dist/tools/{runVerification.d.ts → tasks/runVerification.d.ts} +0 -0
  582. /package/dist/tools/{taskLineage.d.ts → tasks/taskLineage.d.ts} +0 -0
  583. /package/dist/tools/{taskOutputs.d.ts → tasks/taskOutputs.d.ts} +0 -0
  584. /package/dist/tools/{waitForTask.d.ts → tasks/waitForTask.d.ts} +0 -0
  585. /package/dist/tools/{waitForTask.js → tasks/waitForTask.js} +0 -0
  586. /package/dist/tools/{androidDoctor.d.ts → workspace/androidDoctor.d.ts} +0 -0
  587. /package/dist/tools/{discardWorktree.d.ts → workspace/discardWorktree.d.ts} +0 -0
  588. /package/dist/tools/{listWorkspace.d.ts → workspace/listWorkspace.d.ts} +0 -0
  589. /package/dist/tools/{mergeWorktree.d.ts → workspace/mergeWorktree.d.ts} +0 -0
  590. /package/dist/tools/{readWorkspaceFile.d.ts → workspace/readWorkspaceFile.d.ts} +0 -0
  591. /package/dist/tools/{recommendAgentForTask.d.ts → workspace/recommendAgentForTask.d.ts} +0 -0
  592. /package/dist/tools/{searchWorkspace.d.ts → workspace/searchWorkspace.d.ts} +0 -0
  593. /package/src/tools/{toolSearch.ts → catalog/toolSearch.ts} +0 -0
  594. /package/src/tools/{toolUsageStats.ts → catalog/toolUsageStats.ts} +0 -0
  595. /package/src/tools/{getTaskProgress.ts → tasks/getTaskProgress.ts} +0 -0
  596. /package/src/tools/{killTask.ts → tasks/killTask.ts} +0 -0
  597. /package/src/tools/{waitForTask.ts → tasks/waitForTask.ts} +0 -0
@@ -0,0 +1,425 @@
1
+ /**
2
+ * PatchWarden Control Center — runtime state readers and domain helpers.
3
+ *
4
+ * Provides health probing, runtime file readers (tunnel status, tool manifest,
5
+ * watcher status), stale-task classification, the control-center status/event
6
+ * timeline, hidden-id UI state, and small shared helpers (verdict parsing,
7
+ * file mtime, id validation). These are pure helpers consumed by the route
8
+ * modules; they hold no HTTP-layer concerns beyond reusing `sendJson`.
9
+ */
10
+ import { appendFileSync, existsSync, mkdirSync, readFileSync, statSync, unlinkSync } from "node:fs";
11
+ import { join } from "node:path";
12
+ import { get as httpGet } from "node:http";
13
+ import { listTasks } from "../tools/tasks/listTasks.js";
14
+ import { listAgents } from "../tools/workspace/listAgents.js";
15
+ import { readWatcherStatus } from "../watcherStatus.js";
16
+ import { resolveWorkspaceRoot } from "../config.js";
17
+ import { PATCHWARDEN_VERSION } from "../version.js";
18
+ import { isValidDirectSessionId as isValidStoredDirectSessionId } from "../direct/directSessionStore.js";
19
+ import { atomicWriteFileSync, atomicWriteJsonFileSync } from "../utils/atomicFile.js";
20
+ import { withFileLockSync } from "../utils/lockedJsonFile.js";
21
+ import { logger } from "../logging.js";
22
+ import { config, controlCenterEventsPath, controlCenterStatusPath, errorMessage, getControlCenterLogDir, getRuntimeRoot, host, MAX_EVENT_LINES, port, readJsonFileSafe, } from "./shared.js";
23
+ export function probeHealthStatus(targetUrl) {
24
+ return new Promise((resolve) => {
25
+ const controller = new AbortController();
26
+ let settled = false;
27
+ const finish = (result) => {
28
+ if (settled)
29
+ return;
30
+ settled = true;
31
+ clearTimeout(timer);
32
+ resolve(result);
33
+ };
34
+ const timer = setTimeout(() => {
35
+ try {
36
+ controller.abort();
37
+ }
38
+ catch { /* ignore */ }
39
+ finish({ available: false, status: null, reason: "timeout after 2000ms" });
40
+ }, 2000);
41
+ try {
42
+ const req = httpGet(targetUrl, { signal: controller.signal }, (resp) => {
43
+ resp.resume();
44
+ const status = resp.statusCode ?? 0;
45
+ // Only 2xx counts as available. A 404 from an unrelated service (or a
46
+ // 4xx/5xx from the real service) must NOT be treated as healthy.
47
+ if (status >= 200 && status < 300) {
48
+ finish({ available: true, status, reason: null });
49
+ }
50
+ else {
51
+ finish({ available: false, status, reason: `unexpected status ${status}` });
52
+ }
53
+ });
54
+ req.on("error", (err) => {
55
+ finish({ available: false, status: null, reason: err.message });
56
+ });
57
+ }
58
+ catch (err) {
59
+ finish({ available: false, status: null, reason: errorMessage(err) });
60
+ }
61
+ });
62
+ }
63
+ export async function probeRuntimeHealth(baseUrl) {
64
+ const [h, r] = await Promise.all([
65
+ probeHealthStatus(`${baseUrl}/healthz`),
66
+ probeHealthStatus(`${baseUrl}/readyz`),
67
+ ]);
68
+ if (h.available && r.available && h.status !== null && r.status !== null) {
69
+ return { available: true, reason: null, healthz: { status: h.status }, readyz: { status: r.status } };
70
+ }
71
+ const failed = !h.available ? h : r;
72
+ return { available: false, reason: failed.reason ?? "unavailable", healthz: null, readyz: null };
73
+ }
74
+ // ── Runtime file readers ──────────────────────────────────────────
75
+ export function readTunnelStatus(direct) {
76
+ const filePath = join(getRuntimeRoot(direct), "tunnel-status.json");
77
+ if (!existsSync(filePath))
78
+ return { observed: false };
79
+ try {
80
+ const data = readJsonFileSafe(filePath);
81
+ if (data === null)
82
+ return { observed: true, error: "invalid JSON" };
83
+ return { observed: true, ...data };
84
+ }
85
+ catch (err) {
86
+ return { observed: true, error: errorMessage(err) };
87
+ }
88
+ }
89
+ export function readToolManifest(direct) {
90
+ const empty = {
91
+ tool_profile: null,
92
+ tool_count: null,
93
+ schema_epoch: null,
94
+ tool_manifest_sha256: null,
95
+ tool_names: null,
96
+ };
97
+ const filePath = join(getRuntimeRoot(direct), "tool-manifest.json");
98
+ if (!existsSync(filePath))
99
+ return empty;
100
+ const data = readJsonFileSafe(filePath);
101
+ if (!data)
102
+ return empty;
103
+ return {
104
+ tool_profile: typeof data.tool_profile === "string" ? data.tool_profile : null,
105
+ tool_count: typeof data.tool_count === "number" ? data.tool_count : null,
106
+ schema_epoch: typeof data.schema_epoch === "string" ? data.schema_epoch : null,
107
+ tool_manifest_sha256: typeof data.tool_manifest_sha256 === "string" ? data.tool_manifest_sha256 : null,
108
+ tool_names: Array.isArray(data.tool_names) ? data.tool_names : null,
109
+ };
110
+ }
111
+ export function readTunnelUrl(direct) {
112
+ const filePath = join(getRuntimeRoot(direct), "tunnel-health-url.txt");
113
+ if (!existsSync(filePath))
114
+ return { url: null, reason: "tunnel-health-url.txt not found" };
115
+ try {
116
+ const content = readFileSync(filePath, "utf-8").trim();
117
+ if (!content)
118
+ return { url: null, reason: "tunnel-health-url.txt is empty" };
119
+ return { url: content, reason: null };
120
+ }
121
+ catch (err) {
122
+ return { url: null, reason: errorMessage(err) };
123
+ }
124
+ }
125
+ // ── Safe wrappers around reusable modules ─────────────────────────
126
+ export function readWatcherStatusSafe() {
127
+ try {
128
+ return readWatcherStatus(config);
129
+ }
130
+ catch (err) {
131
+ return {
132
+ status: "unreadable",
133
+ available: false,
134
+ stale_after_seconds: config.watcherStaleSeconds,
135
+ last_heartbeat_at: null,
136
+ heartbeat_age_seconds: null,
137
+ heartbeat_pid: null,
138
+ instance_id: null,
139
+ launcher_pid: null,
140
+ reason: errorMessage(err),
141
+ activity: null,
142
+ };
143
+ }
144
+ }
145
+ export function listAgentsSafe() {
146
+ try {
147
+ return listAgents().agents;
148
+ }
149
+ catch {
150
+ return [];
151
+ }
152
+ }
153
+ export function resolveWorkspaceRootSafe() {
154
+ try {
155
+ return resolveWorkspaceRoot(config);
156
+ }
157
+ catch {
158
+ return null;
159
+ }
160
+ }
161
+ export const TERMINAL_TASK_STATUSES = new Set([
162
+ "done",
163
+ "done_by_agent",
164
+ "failed",
165
+ "failed_verification",
166
+ "failed_scope_violation",
167
+ "failed_policy_violation",
168
+ "canceled",
169
+ "timeout",
170
+ ]);
171
+ /**
172
+ * Classify a task as stale based on Phase 2 rules:
173
+ * - status=running but last_heartbeat_at exceeds threshold
174
+ * - phase=collecting_artifacts exceeds threshold
175
+ * - current_command=null AND watcher currently healthy
176
+ * - task last_heartbeat_at significantly earlier than current watcher heartbeat
177
+ *
178
+ * Only pending/running tasks can be stale; terminal tasks are never stale.
179
+ */
180
+ export function classifyStaleTask(task, watcher, nowMs = Date.now()) {
181
+ const reasons = [];
182
+ if (TERMINAL_TASK_STATUSES.has(task.status)) {
183
+ return { is_stale: false, stale_reasons: reasons };
184
+ }
185
+ // Only pending/running are candidates for staleness.
186
+ if (task.status !== "pending" && task.status !== "running") {
187
+ return { is_stale: false, stale_reasons: reasons };
188
+ }
189
+ const staleThresholdMs = config.watcherStaleSeconds * 1000;
190
+ const hbMs = Date.parse(task.last_heartbeat_at || "");
191
+ const heartbeatAgeMs = Number.isFinite(hbMs) ? Math.max(0, nowMs - hbMs) : null;
192
+ // Rule 1: running with stale heartbeat
193
+ if (task.status === "running" && heartbeatAgeMs !== null && heartbeatAgeMs > staleThresholdMs) {
194
+ reasons.push("heartbeat_stale");
195
+ }
196
+ // Rule 2: collecting_artifacts phase exceeds threshold
197
+ if (task.phase === "collecting_artifacts" && heartbeatAgeMs !== null && heartbeatAgeMs > staleThresholdMs) {
198
+ reasons.push("collecting_artifacts_stale");
199
+ }
200
+ // Rule 3: running with no current_command while watcher is healthy
201
+ if (task.status === "running" &&
202
+ (task.current_command === null || task.current_command === "") &&
203
+ watcher.status === "healthy") {
204
+ reasons.push("running_no_command_watcher_healthy");
205
+ }
206
+ // Rule 4: task heartbeat significantly earlier than watcher heartbeat
207
+ if (heartbeatAgeMs !== null && watcher.last_heartbeat_at) {
208
+ const watcherHbMs = Date.parse(watcher.last_heartbeat_at);
209
+ if (Number.isFinite(watcherHbMs)) {
210
+ const gapMs = watcherHbMs - hbMs;
211
+ // Task heartbeat is "significantly earlier" than watcher heartbeat when
212
+ // the task has not heartbeat for at least 2x the stale threshold while
213
+ // the watcher is alive.
214
+ if (gapMs > staleThresholdMs * 2 && watcher.status === "healthy") {
215
+ reasons.push("heartbeat_far_behind_watcher");
216
+ }
217
+ }
218
+ }
219
+ return { is_stale: reasons.length > 0, stale_reasons: reasons };
220
+ }
221
+ export function augmentTaskWithStale(task, watcher, nowMs = Date.now()) {
222
+ const cls = classifyStaleTask(task, watcher, nowMs);
223
+ return { ...task, is_stale: cls.is_stale, stale_reasons: cls.stale_reasons };
224
+ }
225
+ export function listTasksForStatus() {
226
+ try {
227
+ const result = listTasks({ limit: 100 });
228
+ const watcher = result.watcher;
229
+ const now = Date.now();
230
+ let active = 0;
231
+ let stale = 0;
232
+ const staleTaskIds = [];
233
+ const augmented = result.tasks.map((t) => {
234
+ const a = augmentTaskWithStale(t, watcher, now);
235
+ if (t.status === "pending" || t.status === "running")
236
+ active++;
237
+ if (a.is_stale) {
238
+ stale++;
239
+ staleTaskIds.push(t.task_id);
240
+ }
241
+ return a;
242
+ });
243
+ return { tasks: augmented, total: result.total, active, stale, stale_task_ids: staleTaskIds, reason: null };
244
+ }
245
+ catch (err) {
246
+ return { tasks: [], total: 0, active: 0, stale: 0, stale_task_ids: [], reason: errorMessage(err) };
247
+ }
248
+ }
249
+ export function writeStatusFile() {
250
+ const status = {
251
+ pid: process.pid,
252
+ port,
253
+ started_at: new Date().toISOString(),
254
+ url: `http://${host}:${port}/`,
255
+ version: PATCHWARDEN_VERSION,
256
+ };
257
+ try {
258
+ mkdirSync(getControlCenterLogDir(), { recursive: true });
259
+ atomicWriteJsonFileSync(controlCenterStatusPath, status);
260
+ }
261
+ catch (err) {
262
+ logger.error("[control-center] Failed to write status file", { error: errorMessage(err) });
263
+ }
264
+ }
265
+ export function removeStatusFile() {
266
+ try {
267
+ if (existsSync(controlCenterStatusPath)) {
268
+ unlinkSync(controlCenterStatusPath);
269
+ }
270
+ }
271
+ catch (err) {
272
+ logger.error("[control-center] Failed to remove status file", { error: errorMessage(err) });
273
+ }
274
+ }
275
+ /**
276
+ * Append a single event line to the JSONL timeline. Best-effort: a write
277
+ * failure is logged but never crashes the server. Trims the file to
278
+ * MAX_EVENT_LINES lazily when it grows past 1.5x the cap, so we don't pay the
279
+ * trim cost on every event.
280
+ */
281
+ export function recordEvent(type, payload) {
282
+ const event = {
283
+ timestamp: new Date().toISOString(),
284
+ type,
285
+ payload,
286
+ };
287
+ try {
288
+ mkdirSync(getControlCenterLogDir(), { recursive: true });
289
+ withFileLockSync(controlCenterEventsPath, () => {
290
+ appendFileSync(controlCenterEventsPath, JSON.stringify(event) + "\n", "utf-8");
291
+ const stat = statSync(controlCenterEventsPath);
292
+ if (stat.size > 512 * 1024)
293
+ trimEventsFileUnlocked();
294
+ });
295
+ }
296
+ catch (err) {
297
+ logger.error("[control-center] Failed to write event", { error: errorMessage(err) });
298
+ }
299
+ }
300
+ function trimEventsFileUnlocked() {
301
+ try {
302
+ if (!existsSync(controlCenterEventsPath))
303
+ return;
304
+ const raw = readFileSync(controlCenterEventsPath, "utf-8");
305
+ const lines = raw.split(/\r?\n/).filter((l) => l.length > 0);
306
+ if (lines.length <= MAX_EVENT_LINES)
307
+ return;
308
+ const trimmed = lines.slice(lines.length - MAX_EVENT_LINES);
309
+ atomicWriteFileSync(controlCenterEventsPath, trimmed.join("\n") + "\n");
310
+ }
311
+ catch (err) {
312
+ logger.error("[control-center] Failed to trim events file", { error: errorMessage(err) });
313
+ }
314
+ }
315
+ export function readEvents(limit) {
316
+ if (!existsSync(controlCenterEventsPath))
317
+ return [];
318
+ try {
319
+ const raw = readFileSync(controlCenterEventsPath, "utf-8");
320
+ const lines = raw.split(/\r?\n/).filter((l) => l.length > 0);
321
+ const sliced = lines.slice(Math.max(0, lines.length - limit));
322
+ const events = [];
323
+ for (const line of sliced) {
324
+ try {
325
+ events.push(JSON.parse(line));
326
+ }
327
+ catch {
328
+ /* skip malformed line */
329
+ }
330
+ }
331
+ return events;
332
+ }
333
+ catch {
334
+ return [];
335
+ }
336
+ }
337
+ // ── Hidden stale task IDs (local UI state) ────────────────────────
338
+ export function readHiddenStaleIds() {
339
+ const p = join(getControlCenterLogDir(), "hidden-stale-ids.json");
340
+ if (!existsSync(p))
341
+ return [];
342
+ return readStoredStringArray(p);
343
+ }
344
+ export function reconstructTaskEntry(taskId, statusData, runtimeData = {}, watcher = readWatcherStatusSafe()) {
345
+ const validAcceptance = new Set(["pending", "accepted", "rejected", "needs_fix", "blocked"]);
346
+ const taskStatus = String(statusData.status || "pending");
347
+ const acceptanceStatus = taskStatus === "done_by_agent"
348
+ ? (typeof statusData.acceptance_status === "string" && validAcceptance.has(statusData.acceptance_status)
349
+ ? statusData.acceptance_status
350
+ : "pending")
351
+ : null;
352
+ return {
353
+ task_id: taskId,
354
+ plan_id: String(statusData.plan_id || ""),
355
+ title: "",
356
+ agent: String(statusData.agent || ""),
357
+ status: taskStatus,
358
+ phase: String(runtimeData.phase || statusData.phase || "queued"),
359
+ acceptance_status: acceptanceStatus,
360
+ created_at: String(statusData.created_at || ""),
361
+ updated_at: String(statusData.updated_at || ""),
362
+ workspace_root: String(statusData.workspace_root || config.workspaceRoot),
363
+ repo_path: String(statusData.repo_path || "."),
364
+ resolved_repo_path: String(statusData.resolved_repo_path || statusData.repo_path || config.workspaceRoot),
365
+ test_command: String(statusData.test_command || ""),
366
+ verify_commands: Array.isArray(statusData.verify_commands)
367
+ ? statusData.verify_commands.filter((command) => typeof command === "string")
368
+ : [],
369
+ error: statusData.error ? String(statusData.error) : null,
370
+ last_heartbeat_at: String(runtimeData.last_heartbeat_at || statusData.last_heartbeat_at || statusData.updated_at || ""),
371
+ current_command: runtimeData.current_command === undefined ? null : String(runtimeData.current_command || "") || null,
372
+ timeout_seconds: Number(statusData.timeout_seconds) || config.defaultTaskTimeoutSeconds,
373
+ pending_reason: null,
374
+ watcher_status: watcher.status,
375
+ };
376
+ }
377
+ export function writeHiddenStaleIds(ids) {
378
+ const dir = getControlCenterLogDir();
379
+ if (!existsSync(dir))
380
+ mkdirSync(dir, { recursive: true });
381
+ atomicWriteJsonFileSync(join(dir, "hidden-stale-ids.json"), normalizeStoredIds(ids));
382
+ }
383
+ // ── Hidden direct session IDs (local UI state) ────────────────────
384
+ export function readHiddenDirectSessionIds() {
385
+ const p = join(getControlCenterLogDir(), "hidden-direct-session-ids.json");
386
+ if (!existsSync(p))
387
+ return [];
388
+ return readStoredStringArray(p);
389
+ }
390
+ export function writeHiddenDirectSessionIds(ids) {
391
+ const dir = getControlCenterLogDir();
392
+ if (!existsSync(dir))
393
+ mkdirSync(dir, { recursive: true });
394
+ atomicWriteJsonFileSync(join(dir, "hidden-direct-session-ids.json"), normalizeStoredIds(ids));
395
+ }
396
+ export function isValidDirectSessionId(sessionId) {
397
+ return isValidStoredDirectSessionId(sessionId);
398
+ }
399
+ export function isValidTaskId(taskId) {
400
+ return /^[A-Za-z0-9][A-Za-z0-9_-]{0,159}$/.test(taskId);
401
+ }
402
+ function readStoredStringArray(path) {
403
+ const value = readJsonFileSafe(path);
404
+ if (!Array.isArray(value))
405
+ return [];
406
+ return normalizeStoredIds(value);
407
+ }
408
+ function normalizeStoredIds(value) {
409
+ return [...new Set(value.filter((entry) => typeof entry === "string" && entry.length > 0))];
410
+ }
411
+ // ── Small shared helpers ──────────────────────────────────────────
412
+ export function parseReviewVerdict(content) {
413
+ // independent-review.md format: "**Verdict**: PASS" (case-insensitive)
414
+ const m = content.match(/\*\*Verdict\*\*\s*:\s*([A-Za-z]+)/);
415
+ return m ? m[1].toLowerCase() : null;
416
+ }
417
+ export function fileMtimeIso(filePath) {
418
+ try {
419
+ const m = statSync(filePath).mtime;
420
+ return m ? m.toISOString() : null;
421
+ }
422
+ catch {
423
+ return null;
424
+ }
425
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * PatchWarden Control Center — HTTP server creation, routing, and lifecycle.
3
+ *
4
+ * This module wires together the route handlers (routes/*.ts), middleware
5
+ * (auth/static), and the shared/runtime helpers. It owns the `handleRequest`
6
+ * router that dispatches every GET/POST route, the POST control-token gate,
7
+ * and the server bootstrap/shutdown lifecycle (status file, activity events,
8
+ * SIGINT/SIGTERM).
9
+ *
10
+ * Entry point: `controlCenter.ts` imports `startServer()` from here.
11
+ */
12
+ import { type Server } from "node:http";
13
+ export declare function startServer(): Server;
@@ -0,0 +1,150 @@
1
+ /**
2
+ * PatchWarden Control Center — HTTP server creation, routing, and lifecycle.
3
+ *
4
+ * This module wires together the route handlers (routes/*.ts), middleware
5
+ * (auth/static), and the shared/runtime helpers. It owns the `handleRequest`
6
+ * router that dispatches every GET/POST route, the POST control-token gate,
7
+ * and the server bootstrap/shutdown lifecycle (status file, activity events,
8
+ * SIGINT/SIGTERM).
9
+ *
10
+ * Entry point: `controlCenter.ts` imports `startServer()` from here.
11
+ */
12
+ import { createServer } from "node:http";
13
+ import { PATCHWARDEN_VERSION, TOOL_SCHEMA_EPOCH } from "../version.js";
14
+ import { logger } from "../logging.js";
15
+ import { config, controlToken, errorMessage, host, PAGE_ALIASES, port, readBody, sendJson, } from "./shared.js";
16
+ import { recordEvent, removeStatusFile, writeStatusFile } from "./runtime.js";
17
+ import { checkControlToken, isTrustedControlHost } from "./middleware/auth.js";
18
+ import { serveFavicon, serveStatic } from "./middleware/static.js";
19
+ import { buildRoutes } from "./routeTable.js";
20
+ // ── Request router ────────────────────────────────────────────────
21
+ async function handleRequest(req, res) {
22
+ res.setHeader("Content-Security-Policy", "frame-ancestors 'none'");
23
+ res.setHeader("Cross-Origin-Resource-Policy", "same-origin");
24
+ res.setHeader("Referrer-Policy", "no-referrer");
25
+ res.setHeader("X-Content-Type-Options", "nosniff");
26
+ res.setHeader("X-Frame-Options", "DENY");
27
+ if (!isTrustedControlHost(req, port)) {
28
+ sendJson(res, 421, { error: "Untrusted Host header" });
29
+ return;
30
+ }
31
+ const parsedUrl = new URL(req.url || "/", `http://${host}:${port}`);
32
+ const pathname = parsedUrl.pathname;
33
+ const method = (req.method || "GET").toUpperCase();
34
+ // ── Static resource priority branches (not in routeTable.ts) ──
35
+ if (method === "GET" && pathname === "/") {
36
+ serveStatic(res, "pages/dashboard.html");
37
+ return;
38
+ }
39
+ const pageAlias = PAGE_ALIASES[pathname];
40
+ if (method === "GET" && pageAlias) {
41
+ serveStatic(res, pageAlias);
42
+ return;
43
+ }
44
+ if (method === "GET" && pathname === "/control-token.json") {
45
+ sendJson(res, 200, { token: controlToken });
46
+ return;
47
+ }
48
+ if (method === "GET" && pathname === "/favicon.ico") {
49
+ serveFavicon(res);
50
+ return;
51
+ }
52
+ if (method === "GET" &&
53
+ (pathname === "/colors_and_type.css" ||
54
+ pathname === "/desktop.css" ||
55
+ pathname === "/desktop-bridge.js" ||
56
+ pathname === "/i18n.js" ||
57
+ pathname === "/getting-started.js" ||
58
+ pathname === "/settings.js" ||
59
+ pathname.startsWith("/pages/") ||
60
+ pathname.startsWith("/partials/") ||
61
+ pathname.startsWith("/vendor/"))) {
62
+ serveStatic(res, pathname);
63
+ return;
64
+ }
65
+ // ── POST routes: drain body + verify control token before dispatch ──
66
+ // All POST routes in the table declare `requiresToken: true`; the body is
67
+ // drained up-front so HTTP keep-alive connections stay clean even when the
68
+ // route is unknown (preserving the original handleRequest behavior).
69
+ if (method === "POST") {
70
+ await readBody(req);
71
+ if (!checkControlToken(req)) {
72
+ sendJson(res, 403, { error: "Missing or invalid control token" });
73
+ return;
74
+ }
75
+ }
76
+ // ── Declarative route table dispatch ──
77
+ // Filter by method → match pattern (in declaration order, so specific
78
+ // patterns win over generic `:id`) → call handler with capture-group slice.
79
+ const routes = buildRoutes(parsedUrl);
80
+ for (const route of routes) {
81
+ if (route.method !== method)
82
+ continue;
83
+ const match = pathname.match(route.pattern);
84
+ if (!match)
85
+ continue;
86
+ await route.handler(res, match.slice(1));
87
+ return;
88
+ }
89
+ sendJson(res, 404, { error: "Not found" });
90
+ }
91
+ // ── Server bootstrap ──────────────────────────────────────────────
92
+ let server = null;
93
+ export function startServer() {
94
+ server = createServer((req, res) => {
95
+ handleRequest(req, res).catch((err) => {
96
+ if (!res.headersSent) {
97
+ sendJson(res, 500, { error: errorMessage(err) });
98
+ }
99
+ else {
100
+ try {
101
+ res.end();
102
+ }
103
+ catch { /* ignore */ }
104
+ }
105
+ });
106
+ });
107
+ server.on("error", (err) => {
108
+ logger.fatal("[control-center] Server error", { error: errorMessage(err) });
109
+ process.exit(1);
110
+ });
111
+ server.listen(port, host, () => {
112
+ const addr = server.address();
113
+ const formatted = addr && typeof addr === "object" ? `http://${addr.address}:${addr.port}/` : `http://${host}:${port}/`;
114
+ logger.info(`[control-center] PatchWarden v${PATCHWARDEN_VERSION} (schema epoch ${TOOL_SCHEMA_EPOCH})`);
115
+ logger.info(`[control-center] Workspace: ${config.workspaceRoot}`);
116
+ logger.info(`[control-center] Listening: ${formatted}`);
117
+ logger.info(`[control-center] Bound to 127.0.0.1 only — not exposed to network`);
118
+ // Persist status file so the launcher can detect a running instance and
119
+ // open the browser without spawning a second server.
120
+ writeStatusFile();
121
+ recordEvent("control_center.started", {
122
+ pid: process.pid,
123
+ port,
124
+ url: formatted,
125
+ version: PATCHWARDEN_VERSION,
126
+ });
127
+ });
128
+ process.on("SIGINT", shutdown);
129
+ process.on("SIGTERM", shutdown);
130
+ return server;
131
+ }
132
+ function shutdown() {
133
+ logger.info("[control-center] Shutting down...");
134
+ recordEvent("control_center.stopped", { pid: process.pid });
135
+ removeStatusFile();
136
+ if (server) {
137
+ server.close(() => {
138
+ try {
139
+ process.exit(0);
140
+ }
141
+ catch { /* ignore */ }
142
+ });
143
+ }
144
+ setTimeout(() => {
145
+ try {
146
+ process.exit(0);
147
+ }
148
+ catch { /* ignore */ }
149
+ }, 3000).unref();
150
+ }
@@ -0,0 +1,59 @@
1
+ import { delimiter, extname, isAbsolute, join, relative, resolve } from "node:path";
2
+ import { type IncomingMessage, type ServerResponse } from "node:http";
3
+ import { type PatchWardenConfig } from "../config.js";
4
+ export declare const projectRoot: string;
5
+ export declare const uiRoot: string;
6
+ export declare const manageScriptPath: string;
7
+ export declare const activeConfigPath: string;
8
+ export declare const configIdentitySha256: string;
9
+ export declare const PAGE_ALIASES: Record<string, string>;
10
+ export declare function createFallbackConfig(): PatchWardenConfig;
11
+ export declare let config: PatchWardenConfig;
12
+ export declare const controlToken: `${string}-${string}-${string}-${string}-${string}`;
13
+ export declare function resolvePort(): number;
14
+ export declare const port: number;
15
+ export declare const host = "127.0.0.1";
16
+ export declare const CORE_BASE_URL: string;
17
+ export declare const DIRECT_BASE_URL: string;
18
+ export declare const CONTROL_CENTER_FAVICON = "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 64 64\">\n <rect width=\"64\" height=\"64\" rx=\"14\" fill=\"#0a0e14\"/>\n <path d=\"M32 52s17-8 17-27V14L32 8 15 14v11c0 19 17 27 17 27z\" fill=\"#111820\" stroke=\"#2dd4a8\" stroke-width=\"4\" stroke-linejoin=\"round\"/>\n <path d=\"M24 32l6 6 12-14\" fill=\"none\" stroke=\"#2dd4a8\" stroke-width=\"5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n</svg>";
19
+ export declare function errorMessage(err: unknown): string;
20
+ export declare function getRuntimeRoot(direct: boolean): string;
21
+ export declare function getControlCenterLogDir(): string;
22
+ export declare const controlCenterStatusPath: string;
23
+ export declare const controlCenterEventsPath: string;
24
+ export declare const MAX_EVENT_LINES = 2000;
25
+ export declare const ALLOWED_LOG_TAILS: Set<number>;
26
+ export declare function resolveTailParam(value: string | null): number;
27
+ export declare const CONTENT_TYPES: Record<string, string>;
28
+ export declare function sendJson(res: ServerResponse, status: number, body: unknown): void;
29
+ export declare function readBody(req: IncomingMessage): Promise<unknown | null>;
30
+ export declare function readJsonFileSafe<T = unknown>(filePath: string): T | null;
31
+ export declare function isPathInside(root: string, target: string): boolean;
32
+ export declare function readJsonFileSafeUnder<T = unknown>(root: string, relPath: string): T | null;
33
+ export declare function readTextFileSafe(filePath: string): string | null;
34
+ /** Resolve a Control Center artifact path and reject symlink/junction escapes. */
35
+ export declare function guardControlPath(path: string, allowedPrefix: string): string | null;
36
+ export declare function readFileTail(filePath: string, lines?: number): string;
37
+ export declare function findLatestLog(dir: string, pattern: RegExp): string | null;
38
+ /**
39
+ * Declarative route descriptor consumed by `routeTable.ts` and dispatched by
40
+ * `server.ts`'s `handleRequest`. The handler receives the slice of path
41
+ * capture groups `[match[1], match[2], ...]` so wrappers can decode them via
42
+ * `decodeParam`. POST routes default to `requiresToken: true`.
43
+ */
44
+ export interface Route {
45
+ method: "GET" | "POST";
46
+ pattern: RegExp;
47
+ handler: (res: ServerResponse, params: string[]) => Promise<void> | void;
48
+ requiresToken?: boolean;
49
+ }
50
+ /**
51
+ * Safely decode a path capture group, falling back to the raw value if the
52
+ * percent-encoding is malformed. Accepts the raw `RegExpMatchArray` or the
53
+ * sliced `string[]` params passed to a Route handler — both are readable by
54
+ * numeric index. Note: under `lib: es2024+` `RegExpMatchArray` is a tuple
55
+ * `[string, ...string[]]`, so we widen the parameter to `readonly string[]` to
56
+ * accept both shapes without forcing callers to cast.
57
+ */
58
+ export declare function decodeParam(match: readonly string[] | RegExpMatchArray | null, index: number): string;
59
+ export { delimiter, extname, join, resolve, isAbsolute, relative };