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.
- package/PatchWarden.cmd +32 -2
- package/README.en.md +194 -26
- package/README.md +160 -22
- package/dist/assessments/agentAssessor.d.ts +1 -1
- package/dist/assessments/agentAssessor.js +24 -15
- package/dist/assessments/assessmentStore.d.ts +1 -1
- package/dist/assessments/assessmentStore.js +44 -36
- package/dist/assessments/confirmCli.js +5 -4
- package/dist/config.d.ts +22 -0
- package/dist/config.js +98 -11
- package/dist/control/fileManager.d.ts +2 -0
- package/dist/control/fileManager.js +39 -0
- package/dist/control/middleware/auth.d.ts +16 -0
- package/dist/control/middleware/auth.js +17 -0
- package/dist/control/middleware/static.d.ts +3 -0
- package/dist/control/middleware/static.js +78 -0
- package/dist/control/routeTable.d.ts +24 -0
- package/dist/control/routeTable.js +263 -0
- package/dist/control/routes/audit.d.ts +15 -0
- package/dist/control/routes/audit.js +287 -0
- package/dist/control/routes/evidence.d.ts +4 -0
- package/dist/control/routes/evidence.js +96 -0
- package/dist/control/routes/lineage.d.ts +3 -0
- package/dist/control/routes/lineage.js +71 -0
- package/dist/control/routes/policy.d.ts +3 -0
- package/dist/control/routes/policy.js +81 -0
- package/dist/control/routes/process.d.ts +12 -0
- package/dist/control/routes/process.js +487 -0
- package/dist/control/routes/sessions.d.ts +30 -0
- package/dist/control/routes/sessions.js +287 -0
- package/dist/control/routes/status.d.ts +51 -0
- package/dist/control/routes/status.js +307 -0
- package/dist/control/routes/taskActions.d.ts +21 -0
- package/dist/control/routes/taskActions.js +197 -0
- package/dist/control/routes/tasks.d.ts +24 -0
- package/dist/control/routes/tasks.js +309 -0
- package/dist/control/routes/workspace.d.ts +15 -0
- package/dist/control/routes/workspace.js +220 -0
- package/dist/control/runtime.d.ts +92 -0
- package/dist/control/runtime.js +425 -0
- package/dist/control/server.d.ts +13 -0
- package/dist/control/server.js +150 -0
- package/dist/control/shared.d.ts +59 -0
- package/dist/control/shared.js +341 -0
- package/dist/controlCenter.d.ts +6 -0
- package/dist/controlCenter.js +7 -2197
- package/dist/direct/directAudit.js +234 -225
- package/dist/direct/directGuards.d.ts +2 -1
- package/dist/direct/directGuards.js +72 -37
- package/dist/direct/directPatch.d.ts +6 -1
- package/dist/direct/directPatch.js +55 -8
- package/dist/direct/directSessionStore.d.ts +16 -6
- package/dist/direct/directSessionStore.js +127 -35
- package/dist/direct/directVerification.js +8 -16
- package/dist/doctor.d.ts +18 -1
- package/dist/doctor.js +614 -373
- package/dist/goal/acceptanceEngine.d.ts +1 -1
- package/dist/goal/goalProgress.js +75 -69
- package/dist/goal/goalReport.d.ts +54 -0
- package/dist/goal/goalReport.js +197 -0
- package/dist/goal/goalStatus.d.ts +8 -0
- package/dist/goal/goalStatus.js +10 -3
- package/dist/goal/goalStore.d.ts +9 -2
- package/dist/goal/goalStore.js +152 -45
- package/dist/goal/handoffExport.js +6 -6
- package/dist/goal/specKitImport.d.ts +63 -0
- package/dist/goal/specKitImport.js +221 -0
- package/dist/goal/subgoalSync.js +13 -10
- package/dist/goal/worktreeManager.d.ts +2 -0
- package/dist/goal/worktreeManager.js +209 -136
- package/dist/httpServer.js +126 -44
- package/dist/index.js +7 -4
- package/dist/logging.d.ts +7 -1
- package/dist/logging.js +13 -3
- package/dist/policy/projectPolicy.js +5 -2
- package/dist/release/releaseGate.d.ts +1 -1
- package/dist/release/releaseGate.js +78 -25
- package/dist/runner/agentInvocation.d.ts +14 -0
- package/dist/runner/agentInvocation.js +96 -4
- package/dist/runner/changeCapture.d.ts +14 -6
- package/dist/runner/changeCapture.js +287 -81
- package/dist/runner/cli.js +7 -6
- package/dist/runner/postTaskCleanup.js +60 -7
- package/dist/runner/processSecurity.d.ts +56 -0
- package/dist/runner/processSecurity.js +373 -0
- package/dist/runner/runTask.d.ts +1 -1
- package/dist/runner/runTask.js +339 -287
- package/dist/runner/simpleProcess.d.ts +3 -0
- package/dist/runner/simpleProcess.js +52 -38
- package/dist/{taskProgress.d.ts → runner/taskProgress.d.ts} +1 -1
- package/dist/{taskProgress.js → runner/taskProgress.js} +2 -2
- package/dist/{taskRuntime.d.ts → runner/taskRuntime.d.ts} +1 -1
- package/dist/{taskRuntime.js → runner/taskRuntime.js} +3 -2
- package/dist/runner/taskStatusStore.d.ts +11 -0
- package/dist/runner/taskStatusStore.js +24 -0
- package/dist/runner/watch.d.ts +14 -1
- package/dist/runner/watch.js +265 -54
- package/dist/security/contentRedaction.d.ts +6 -0
- package/dist/security/contentRedaction.js +24 -2
- package/dist/security/discoveryTokenStore.d.ts +3 -2
- package/dist/security/discoveryTokenStore.js +17 -2
- package/dist/security/loopbackHost.d.ts +2 -0
- package/dist/security/loopbackHost.js +16 -0
- package/dist/security/pathGuard.js +32 -17
- package/dist/security/planGuard.js +2 -1
- package/dist/security/runtimeGuard.js +9 -6
- package/dist/security/secretComparison.d.ts +3 -0
- package/dist/security/secretComparison.js +11 -0
- package/dist/security/sensitiveGuard.d.ts +5 -0
- package/dist/security/sensitiveGuard.js +27 -8
- package/dist/security/toolInvocationGuard.d.ts +2 -2
- package/dist/security/toolInvocationGuard.js +1 -1
- package/dist/security/workspaceRootGuard.d.ts +8 -0
- package/dist/security/workspaceRootGuard.js +47 -0
- package/dist/smoke-test.js +280 -273
- package/dist/tools/catalog/index.d.ts +4 -0
- package/dist/tools/catalog/index.js +4 -0
- package/dist/tools/{toolCatalog.js → catalog/toolCatalog.js} +3 -12
- package/dist/tools/{toolRegistry.d.ts → catalog/toolRegistry.d.ts} +3 -3
- package/dist/tools/{toolRegistry.js → catalog/toolRegistry.js} +28 -15
- package/dist/tools/definitions/toolDefs.d.ts +18 -0
- package/dist/tools/definitions/toolDefs.js +1177 -0
- package/dist/tools/{auditSession.d.ts → diagnostics/auditSession.d.ts} +1 -1
- package/dist/tools/{auditSession.js → diagnostics/auditSession.js} +2 -2
- package/dist/tools/{auditTask.d.ts → diagnostics/auditTask.d.ts} +8 -63
- package/dist/tools/{auditTask.js → diagnostics/auditTask.js} +111 -37
- package/dist/tools/{healthCheck.d.ts → diagnostics/healthCheck.d.ts} +6 -6
- package/dist/tools/{healthCheck.js → diagnostics/healthCheck.js} +10 -10
- package/dist/tools/diagnostics/index.d.ts +6 -0
- package/dist/tools/diagnostics/index.js +6 -0
- package/dist/tools/{safeStatus.d.ts → diagnostics/safeStatus.d.ts} +3 -3
- package/dist/tools/{safeStatus.js → diagnostics/safeStatus.js} +6 -6
- package/dist/tools/{safeViews.d.ts → diagnostics/safeViews.d.ts} +14 -10
- package/dist/tools/{safeViews.js → diagnostics/safeViews.js} +25 -15
- package/dist/tools/{schemaDriftCheck.d.ts → diagnostics/schemaDriftCheck.d.ts} +1 -1
- package/dist/tools/{schemaDriftCheck.js → diagnostics/schemaDriftCheck.js} +2 -2
- package/dist/tools/{createDirectSession.d.ts → direct/createDirectSession.d.ts} +1 -1
- package/dist/tools/{createDirectSession.js → direct/createDirectSession.js} +8 -8
- package/dist/tools/{finalizeDirectSession.d.ts → direct/finalizeDirectSession.d.ts} +2 -2
- package/dist/tools/direct/finalizeDirectSession.js +86 -0
- package/dist/tools/direct/index.d.ts +3 -0
- package/dist/tools/direct/index.js +3 -0
- package/dist/tools/{runDirectVerificationBundle.js → direct/runDirectVerificationBundle.js} +2 -2
- package/dist/tools/{discoverTools.d.ts → discovery/discoverTools.d.ts} +2 -2
- package/dist/tools/{discoverTools.js → discovery/discoverTools.js} +5 -5
- package/dist/tools/{explainTool.d.ts → discovery/explainTool.d.ts} +2 -2
- package/dist/tools/{explainTool.js → discovery/explainTool.js} +2 -2
- package/dist/tools/discovery/index.d.ts +3 -0
- package/dist/tools/discovery/index.js +3 -0
- package/dist/tools/{invokeDiscoveredTool.d.ts → discovery/invokeDiscoveredTool.d.ts} +2 -2
- package/dist/tools/{invokeDiscoveredTool.js → discovery/invokeDiscoveredTool.js} +5 -5
- package/dist/tools/dispatch/coreDispatch.d.ts +9 -0
- package/dist/tools/dispatch/coreDispatch.js +283 -0
- package/dist/tools/dispatch/diagnosticDispatch.d.ts +12 -0
- package/dist/tools/dispatch/diagnosticDispatch.js +80 -0
- package/dist/tools/dispatch/directDispatch.d.ts +8 -0
- package/dist/tools/dispatch/directDispatch.js +116 -0
- package/dist/tools/dispatch/goalDispatch.d.ts +8 -0
- package/dist/tools/dispatch/goalDispatch.js +92 -0
- package/dist/tools/dispatch/releaseDispatch.d.ts +8 -0
- package/dist/tools/dispatch/releaseDispatch.js +46 -0
- package/dist/tools/dispatch/types.d.ts +23 -0
- package/dist/tools/dispatch/types.js +15 -0
- package/dist/tools/dispatch/validation.d.ts +7 -0
- package/dist/tools/dispatch/validation.js +71 -0
- package/dist/tools/{getPlan.js → goals/getPlan.js} +4 -4
- package/dist/tools/{goalSubgoalTask.d.ts → goals/goalSubgoalTask.d.ts} +2 -2
- package/dist/tools/goals/goalSubgoalTask.js +118 -0
- package/dist/tools/goals/index.d.ts +3 -0
- package/dist/tools/goals/index.js +3 -0
- package/dist/tools/{savePlan.js → goals/savePlan.js} +28 -8
- package/dist/tools/registry.d.ts +10 -14
- package/dist/tools/registry.js +47 -1651
- package/dist/tools/{checkReleaseGate.d.ts → release/checkReleaseGate.d.ts} +1 -1
- package/dist/tools/{checkReleaseGate.js → release/checkReleaseGate.js} +3 -3
- package/dist/tools/release/index.d.ts +2 -0
- package/dist/tools/release/index.js +2 -0
- package/dist/tools/{releaseMode.d.ts → release/releaseMode.d.ts} +2 -2
- package/dist/tools/{releaseMode.js → release/releaseMode.js} +46 -26
- package/dist/tools/tasks/cancelTask.d.ts +11 -0
- package/dist/tools/tasks/cancelTask.js +85 -0
- package/dist/tools/{createTask.d.ts → tasks/createTask.d.ts} +12 -4
- package/dist/tools/{createTask.js → tasks/createTask.js} +46 -31
- package/dist/tools/{diagnoseTask.d.ts → tasks/diagnoseTask.d.ts} +1 -1
- package/dist/tools/{diagnoseTask.js → tasks/diagnoseTask.js} +8 -8
- package/dist/tools/{evidencePack.d.ts → tasks/evidencePack.d.ts} +6 -0
- package/dist/tools/tasks/evidencePack.js +387 -0
- package/dist/tools/{getTaskFile.js → tasks/getTaskFile.js} +4 -4
- package/dist/tools/{getTaskStatus.d.ts → tasks/getTaskStatus.d.ts} +6 -1
- package/dist/tools/{getTaskStatus.js → tasks/getTaskStatus.js} +5 -5
- package/dist/tools/{getTaskStdoutTail.js → tasks/getTaskStdoutTail.js} +13 -11
- package/dist/tools/{getTaskSummary.js → tasks/getTaskSummary.js} +33 -26
- package/dist/tools/tasks/index.d.ts +18 -0
- package/dist/tools/tasks/index.js +18 -0
- package/dist/tools/tasks/killTask.d.ts +1 -0
- package/dist/tools/{listTasks.d.ts → tasks/listTasks.d.ts} +1 -1
- package/dist/tools/{listTasks.js → tasks/listTasks.js} +3 -3
- package/dist/tools/{reconcileTasks.d.ts → tasks/reconcileTasks.d.ts} +1 -1
- package/dist/tools/{reconcileTasks.js → tasks/reconcileTasks.js} +64 -66
- package/dist/tools/{retryTask.d.ts → tasks/retryTask.d.ts} +2 -2
- package/dist/tools/tasks/retryTask.js +60 -0
- package/dist/tools/{runTaskLoop.d.ts → tasks/runTaskLoop.d.ts} +6 -6
- package/dist/tools/{runTaskLoop.js → tasks/runTaskLoop.js} +59 -31
- package/dist/tools/tasks/runVerification.js +38 -0
- package/dist/tools/{taskLineage.js → tasks/taskLineage.js} +8 -7
- package/dist/tools/{taskOutputs.js → tasks/taskOutputs.js} +17 -16
- package/dist/tools/{androidDoctor.js → workspace/androidDoctor.js} +16 -10
- package/dist/tools/{applyPatch.d.ts → workspace/applyPatch.d.ts} +1 -1
- package/dist/tools/workspace/applyPatch.js +53 -0
- package/dist/tools/{discardWorktree.js → workspace/discardWorktree.js} +3 -3
- package/dist/tools/workspace/index.d.ts +10 -0
- package/dist/tools/workspace/index.js +10 -0
- package/dist/tools/{listAgents.d.ts → workspace/listAgents.d.ts} +5 -0
- package/dist/tools/{listAgents.js → workspace/listAgents.js} +8 -4
- package/dist/tools/{listWorkspace.js → workspace/listWorkspace.js} +3 -3
- package/dist/tools/{mergeWorktree.js → workspace/mergeWorktree.js} +3 -3
- package/dist/tools/{readWorkspaceFile.js → workspace/readWorkspaceFile.js} +8 -8
- package/dist/tools/{recommendAgentForTask.js → workspace/recommendAgentForTask.js} +3 -3
- package/dist/tools/{searchWorkspace.js → workspace/searchWorkspace.js} +5 -5
- package/dist/tools/{syncFile.d.ts → workspace/syncFile.d.ts} +1 -1
- package/dist/tools/workspace/syncFile.js +134 -0
- package/dist/utils/atomicFile.d.ts +7 -0
- package/dist/utils/atomicFile.js +48 -0
- package/dist/utils/boundedFile.d.ts +8 -0
- package/dist/utils/boundedFile.js +93 -0
- package/dist/utils/lockedJsonFile.d.ts +15 -0
- package/dist/utils/lockedJsonFile.js +224 -0
- package/dist/utils/platform.d.ts +11 -0
- package/dist/utils/platform.js +26 -0
- package/dist/utils/stableJson.d.ts +2 -0
- package/dist/utils/stableJson.js +12 -0
- package/dist/version.d.ts +3 -2
- package/dist/version.js +3 -2
- package/dist/watcherStatus.d.ts +2 -2
- package/dist/watcherStatus.js +18 -0
- package/docs/CODE_WIKI.md +1648 -0
- package/docs/agentseal-integration.md +150 -0
- package/docs/architecture.md +23 -0
- package/docs/assets/patchwarden-desktop-onboarding.png +0 -0
- package/docs/assets/patchwarden-oss-demo.gif +0 -0
- package/docs/control-center/README.md +4 -4
- package/docs/dashboard-overview.md +86 -0
- package/docs/demo.md +8 -0
- package/docs/desktop-app.md +175 -0
- package/docs/direct-session-workflow.md +98 -0
- package/docs/evidence-pack-schema.md +215 -0
- package/docs/lineage-evidence-pack-workflow.md +127 -0
- package/docs/mcp-inspector-testing.md +200 -0
- package/docs/open-source-application.md +168 -0
- package/docs/opencode-worker.md +151 -0
- package/docs/openhands-worker.md +181 -0
- package/docs/release-checklist.md +1 -1
- package/docs/release-evidence.md +82 -0
- package/docs/spec-kit-integration.md +131 -0
- package/docs/task-safe-review-workflow.md +98 -0
- package/docs/threat-model.md +97 -0
- package/docs/user-feedback.md +40 -0
- package/docs/why-patchwarden.md +110 -0
- package/examples/config.example.json +4 -2
- package/package.json +28 -11
- package/scripts/README.md +15 -6
- package/scripts/build.js +11 -0
- package/scripts/checks/brand-check.js +3 -0
- package/scripts/checks/build-output-check.js +37 -0
- package/scripts/checks/control-center-smoke.js +447 -6
- package/scripts/checks/control-smoke.js +19 -8
- package/scripts/checks/http-mcp-smoke.js +82 -4
- package/scripts/checks/lifecycle-smoke.js +39 -33
- package/scripts/checks/mcp-manifest-check.js +1 -1
- package/scripts/checks/mcp-smoke.js +30 -4
- package/scripts/checks/package-manifest-check.js +20 -7
- package/scripts/checks/unit-tests.js +3 -5
- package/scripts/checks/watcher-supervisor-smoke.js +9 -0
- package/scripts/control/get-patchwarden-health.ps1 +1 -1
- package/scripts/control/manage-patchwarden.ps1 +69 -4
- package/scripts/control/provision-patchwarden-tunnel.ps1 +127 -0
- package/scripts/control/run-background-supervisor.ps1 +41 -0
- package/scripts/control/start-patchwarden-tunnel.ps1 +126 -32
- package/scripts/generate-demo-gif.py +320 -0
- package/{PatchWarden-Control-Tray.cmd → scripts/launchers/PatchWarden-Control-Tray.cmd} +3 -3
- package/scripts/launchers/PatchWarden-Control.cmd +6 -0
- package/{PatchWarden-Desktop.cmd → scripts/launchers/PatchWarden-Desktop.cmd} +2 -2
- package/scripts/launchers/Restart-PatchWarden-Control.cmd +6 -0
- package/{Stop-PatchWarden.cmd → scripts/launchers/Stop-PatchWarden.cmd} +2 -2
- package/scripts/lib/clean-generated-output.js +11 -0
- package/scripts/lib/file-discovery.js +12 -0
- package/scripts/release/desktop-preflight.js +211 -0
- package/scripts/release/pack-clean.js +59 -14
- package/src/assessments/agentAssessor.ts +29 -15
- package/src/assessments/assessmentStore.ts +55 -43
- package/src/assessments/confirmCli.ts +5 -4
- package/src/config.ts +105 -11
- package/src/control/fileManager.ts +40 -0
- package/src/control/middleware/auth.ts +35 -0
- package/src/control/middleware/static.ts +84 -0
- package/src/control/routeTable.ts +301 -0
- package/src/control/routes/audit.ts +329 -0
- package/src/control/routes/evidence.ts +107 -0
- package/src/control/routes/lineage.ts +92 -0
- package/src/control/routes/policy.ts +81 -0
- package/src/control/routes/process.ts +505 -0
- package/src/control/routes/sessions.ts +314 -0
- package/src/control/routes/status.ts +405 -0
- package/src/control/routes/taskActions.ts +214 -0
- package/src/control/routes/tasks.ts +331 -0
- package/src/control/routes/workspace.ts +234 -0
- package/src/control/runtime.ts +508 -0
- package/src/control/server.ts +165 -0
- package/src/control/shared.ts +360 -0
- package/src/controlCenter.ts +7 -2347
- package/src/direct/directAudit.ts +17 -9
- package/src/direct/directGuards.ts +100 -51
- package/src/direct/directPatch.ts +95 -8
- package/src/direct/directSessionStore.ts +211 -55
- package/src/direct/directVerification.ts +9 -23
- package/src/doctor.ts +766 -492
- package/src/goal/acceptanceEngine.ts +1 -1
- package/src/goal/goalProgress.ts +17 -13
- package/src/goal/goalReport.ts +263 -0
- package/src/goal/goalStatus.ts +25 -3
- package/src/goal/goalStore.ts +241 -54
- package/src/goal/handoffExport.ts +6 -6
- package/src/goal/specKitImport.ts +355 -0
- package/src/goal/subgoalSync.ts +15 -10
- package/src/goal/worktreeManager.ts +139 -22
- package/src/httpServer.ts +130 -44
- package/src/index.ts +7 -4
- package/src/logging.ts +20 -4
- package/src/policy/projectPolicy.ts +5 -2
- package/src/release/releaseGate.ts +74 -27
- package/src/runner/agentInvocation.ts +126 -4
- package/src/runner/changeCapture.ts +342 -80
- package/src/runner/cli.ts +7 -6
- package/src/runner/postTaskCleanup.ts +55 -7
- package/src/runner/processSecurity.ts +433 -0
- package/src/runner/runTask.ts +441 -292
- package/src/runner/simpleProcess.ts +68 -33
- package/src/{taskProgress.ts → runner/taskProgress.ts} +3 -3
- package/src/{taskRuntime.ts → runner/taskRuntime.ts} +4 -3
- package/src/runner/taskStatusStore.ts +54 -0
- package/src/runner/watch.ts +290 -57
- package/src/security/contentRedaction.ts +31 -2
- package/src/security/discoveryTokenStore.ts +18 -4
- package/src/security/loopbackHost.ts +19 -0
- package/src/security/pathGuard.ts +35 -16
- package/src/security/planGuard.ts +2 -1
- package/src/security/runtimeGuard.ts +9 -6
- package/src/security/secretComparison.ts +13 -0
- package/src/security/sensitiveGuard.ts +27 -8
- package/src/security/toolInvocationGuard.ts +2 -2
- package/src/security/workspaceRootGuard.ts +53 -0
- package/src/smoke-test.ts +276 -273
- package/src/tools/catalog/index.ts +4 -0
- package/src/tools/{toolCatalog.ts → catalog/toolCatalog.ts} +3 -12
- package/src/tools/{toolRegistry.ts → catalog/toolRegistry.ts} +29 -16
- package/src/tools/definitions/toolDefs.ts +1264 -0
- package/src/tools/{auditSession.ts → diagnostics/auditSession.ts} +2 -2
- package/src/tools/{auditTask.ts → diagnostics/auditTask.ts} +196 -89
- package/src/tools/{healthCheck.ts → diagnostics/healthCheck.ts} +11 -11
- package/src/tools/diagnostics/index.ts +6 -0
- package/src/tools/{safeStatus.ts → diagnostics/safeStatus.ts} +7 -7
- package/src/tools/{safeViews.ts → diagnostics/safeViews.ts} +29 -19
- package/src/tools/{schemaDriftCheck.ts → diagnostics/schemaDriftCheck.ts} +3 -3
- package/src/tools/{createDirectSession.ts → direct/createDirectSession.ts} +9 -9
- package/src/tools/{finalizeDirectSession.ts → direct/finalizeDirectSession.ts} +11 -9
- package/src/tools/direct/index.ts +3 -0
- package/src/tools/{runDirectVerificationBundle.ts → direct/runDirectVerificationBundle.ts} +2 -2
- package/src/tools/{discoverTools.ts → discovery/discoverTools.ts} +6 -6
- package/src/tools/{explainTool.ts → discovery/explainTool.ts} +3 -3
- package/src/tools/discovery/index.ts +3 -0
- package/src/tools/{invokeDiscoveredTool.ts → discovery/invokeDiscoveredTool.ts} +7 -7
- package/src/tools/dispatch/coreDispatch.ts +379 -0
- package/src/tools/dispatch/diagnosticDispatch.ts +106 -0
- package/src/tools/dispatch/directDispatch.ts +168 -0
- package/src/tools/dispatch/goalDispatch.ts +128 -0
- package/src/tools/dispatch/releaseDispatch.ts +66 -0
- package/src/tools/dispatch/types.ts +24 -0
- package/src/tools/dispatch/validation.ts +78 -0
- package/src/tools/{getPlan.ts → goals/getPlan.ts} +4 -4
- package/src/tools/{goalSubgoalTask.ts → goals/goalSubgoalTask.ts} +43 -28
- package/src/tools/goals/index.ts +3 -0
- package/src/tools/{savePlan.ts → goals/savePlan.ts} +38 -8
- package/src/tools/registry.ts +54 -1858
- package/src/tools/{checkReleaseGate.ts → release/checkReleaseGate.ts} +3 -3
- package/src/tools/release/index.ts +2 -0
- package/src/tools/{releaseMode.ts → release/releaseMode.ts} +42 -24
- package/src/tools/tasks/cancelTask.ts +109 -0
- package/src/tools/{createTask.ts → tasks/createTask.ts} +56 -33
- package/src/tools/{diagnoseTask.ts → tasks/diagnoseTask.ts} +8 -8
- package/src/tools/tasks/evidencePack.ts +504 -0
- package/src/tools/{getTaskFile.ts → tasks/getTaskFile.ts} +4 -4
- package/src/tools/{getTaskStatus.ts → tasks/getTaskStatus.ts} +7 -5
- package/src/tools/{getTaskStdoutTail.ts → tasks/getTaskStdoutTail.ts} +13 -11
- package/src/tools/{getTaskSummary.ts → tasks/getTaskSummary.ts} +44 -33
- package/src/tools/tasks/index.ts +18 -0
- package/src/tools/{listTasks.ts → tasks/listTasks.ts} +4 -4
- package/src/tools/{reconcileTasks.ts → tasks/reconcileTasks.ts} +68 -66
- package/src/tools/tasks/retryTask.ts +69 -0
- package/src/tools/{runTaskLoop.ts → tasks/runTaskLoop.ts} +70 -35
- package/src/tools/{runVerification.ts → tasks/runVerification.ts} +6 -3
- package/src/tools/{taskLineage.ts → tasks/taskLineage.ts} +8 -7
- package/src/tools/{taskOutputs.ts → tasks/taskOutputs.ts} +27 -20
- package/src/tools/{androidDoctor.ts → workspace/androidDoctor.ts} +15 -10
- package/src/tools/{applyPatch.ts → workspace/applyPatch.ts} +38 -16
- package/src/tools/{discardWorktree.ts → workspace/discardWorktree.ts} +3 -3
- package/src/tools/workspace/index.ts +10 -0
- package/src/tools/{listAgents.ts → workspace/listAgents.ts} +11 -4
- package/src/tools/{listWorkspace.ts → workspace/listWorkspace.ts} +3 -3
- package/src/tools/{mergeWorktree.ts → workspace/mergeWorktree.ts} +3 -3
- package/src/tools/{readWorkspaceFile.ts → workspace/readWorkspaceFile.ts} +8 -8
- package/src/tools/{recommendAgentForTask.ts → workspace/recommendAgentForTask.ts} +3 -3
- package/src/tools/{searchWorkspace.ts → workspace/searchWorkspace.ts} +5 -5
- package/src/tools/workspace/syncFile.ts +292 -0
- package/src/utils/atomicFile.ts +58 -0
- package/src/utils/boundedFile.ts +116 -0
- package/src/utils/lockedJsonFile.ts +251 -0
- package/src/utils/platform.ts +31 -0
- package/src/utils/stableJson.ts +12 -0
- package/src/version.ts +3 -2
- package/src/watcherStatus.ts +19 -1
- package/ui/desktop-bootstrap.js +11 -0
- package/ui/desktop-bridge.js +36 -0
- package/ui/desktop.css +160 -0
- package/ui/getting-started.js +93 -0
- package/ui/i18n.js +484 -0
- package/ui/log-parser.js +30 -0
- package/ui/pages/audit.html +204 -3
- package/ui/pages/dashboard.html +937 -79
- package/ui/pages/direct-sessions.html +609 -60
- package/ui/pages/getting-started.html +43 -0
- package/ui/pages/logs.html +105 -22
- package/ui/pages/settings.html +101 -0
- package/ui/pages/task-detail.html +468 -438
- package/ui/pages/tasks.html +648 -65
- package/ui/pages/workspace.html +38 -1
- package/ui/settings.js +245 -0
- package/PatchWarden-Control.cmd +0 -6
- package/Restart-PatchWarden-Control.cmd +0 -6
- package/dist/test/unit/acceptance-engine.test.d.ts +0 -1
- package/dist/test/unit/acceptance-engine.test.js +0 -228
- package/dist/test/unit/agent-router.test.d.ts +0 -1
- package/dist/test/unit/agent-router.test.js +0 -287
- package/dist/test/unit/android-doctor.test.d.ts +0 -1
- package/dist/test/unit/android-doctor.test.js +0 -118
- package/dist/test/unit/audit-checks.test.d.ts +0 -1
- package/dist/test/unit/audit-checks.test.js +0 -350
- package/dist/test/unit/chinese-path.test.d.ts +0 -1
- package/dist/test/unit/chinese-path.test.js +0 -91
- package/dist/test/unit/command-guard.test.d.ts +0 -1
- package/dist/test/unit/command-guard.test.js +0 -160
- package/dist/test/unit/diagnose-task.test.d.ts +0 -1
- package/dist/test/unit/diagnose-task.test.js +0 -457
- package/dist/test/unit/direct-guards.test.d.ts +0 -1
- package/dist/test/unit/direct-guards.test.js +0 -213
- package/dist/test/unit/discovery-token-store.test.d.ts +0 -1
- package/dist/test/unit/discovery-token-store.test.js +0 -139
- package/dist/test/unit/evidence-pack.test.d.ts +0 -1
- package/dist/test/unit/evidence-pack.test.js +0 -130
- package/dist/test/unit/goal-graph.test.d.ts +0 -1
- package/dist/test/unit/goal-graph.test.js +0 -298
- package/dist/test/unit/goal-progress.test.d.ts +0 -1
- package/dist/test/unit/goal-progress.test.js +0 -381
- package/dist/test/unit/goal-status.test.d.ts +0 -1
- package/dist/test/unit/goal-status.test.js +0 -215
- package/dist/test/unit/goal-store.test.d.ts +0 -1
- package/dist/test/unit/goal-store.test.js +0 -253
- package/dist/test/unit/goal-subgoal-task.test.d.ts +0 -1
- package/dist/test/unit/goal-subgoal-task.test.js +0 -55
- package/dist/test/unit/goal-tools-registry.test.d.ts +0 -1
- package/dist/test/unit/goal-tools-registry.test.js +0 -190
- package/dist/test/unit/handoff-export.test.d.ts +0 -1
- package/dist/test/unit/handoff-export.test.js +0 -263
- package/dist/test/unit/invoke-discovered-tool.test.d.ts +0 -1
- package/dist/test/unit/invoke-discovered-tool.test.js +0 -167
- package/dist/test/unit/logging.test.d.ts +0 -1
- package/dist/test/unit/logging.test.js +0 -397
- package/dist/test/unit/path-guard.test.d.ts +0 -1
- package/dist/test/unit/path-guard.test.js +0 -109
- package/dist/test/unit/post-task-cleanup.test.d.ts +0 -1
- package/dist/test/unit/post-task-cleanup.test.js +0 -48
- package/dist/test/unit/project-policy-release-mode.test.d.ts +0 -1
- package/dist/test/unit/project-policy-release-mode.test.js +0 -125
- package/dist/test/unit/reconcile-tasks.test.d.ts +0 -1
- package/dist/test/unit/reconcile-tasks.test.js +0 -456
- package/dist/test/unit/release-gate.test.d.ts +0 -1
- package/dist/test/unit/release-gate.test.js +0 -242
- package/dist/test/unit/run-task-loop.test.d.ts +0 -1
- package/dist/test/unit/run-task-loop.test.js +0 -380
- package/dist/test/unit/safe-status.test.d.ts +0 -1
- package/dist/test/unit/safe-status.test.js +0 -165
- package/dist/test/unit/safe-views.test.d.ts +0 -1
- package/dist/test/unit/safe-views.test.js +0 -171
- package/dist/test/unit/schema-drift-check.test.d.ts +0 -1
- package/dist/test/unit/schema-drift-check.test.js +0 -176
- package/dist/test/unit/sensitive-guard.test.d.ts +0 -1
- package/dist/test/unit/sensitive-guard.test.js +0 -104
- package/dist/test/unit/subgoal-sync.test.d.ts +0 -1
- package/dist/test/unit/subgoal-sync.test.js +0 -183
- package/dist/test/unit/sync-file.test.d.ts +0 -1
- package/dist/test/unit/sync-file.test.js +0 -154
- package/dist/test/unit/tool-invocation-guard.test.d.ts +0 -1
- package/dist/test/unit/tool-invocation-guard.test.js +0 -432
- package/dist/test/unit/tool-usage-stats.test.d.ts +0 -1
- package/dist/test/unit/tool-usage-stats.test.js +0 -300
- package/dist/test/unit/toolSearch.test.d.ts +0 -1
- package/dist/test/unit/toolSearch.test.js +0 -571
- package/dist/test/unit/watcher-status.test.d.ts +0 -1
- package/dist/test/unit/watcher-status.test.js +0 -169
- package/dist/test/unit/worktree-manager.test.d.ts +0 -1
- package/dist/test/unit/worktree-manager.test.js +0 -176
- package/dist/tools/applyPatch.js +0 -41
- package/dist/tools/cancelTask.d.ts +0 -30
- package/dist/tools/cancelTask.js +0 -64
- package/dist/tools/evidencePack.js +0 -168
- package/dist/tools/finalizeDirectSession.js +0 -84
- package/dist/tools/goalSubgoalTask.js +0 -110
- package/dist/tools/killTask.d.ts +0 -15
- package/dist/tools/retryTask.js +0 -45
- package/dist/tools/runVerification.js +0 -36
- package/dist/tools/syncFile.js +0 -65
- package/docs/control-center/control-center-daily-driver.md +0 -211
- package/docs/control-center/control-center-mvp.md +0 -205
- package/docs/control-center/control-center-phase2.md +0 -159
- package/docs/release-v0.6.0.md +0 -71
- package/docs/release-v0.6.1.md +0 -75
- package/docs/release-v0.6.4.md +0 -45
- package/src/test/unit/acceptance-engine.test.ts +0 -261
- package/src/test/unit/agent-router.test.ts +0 -342
- package/src/test/unit/android-doctor.test.ts +0 -158
- package/src/test/unit/audit-checks.test.ts +0 -567
- package/src/test/unit/chinese-path.test.ts +0 -106
- package/src/test/unit/command-guard.test.ts +0 -221
- package/src/test/unit/diagnose-task.test.ts +0 -544
- package/src/test/unit/direct-guards.test.ts +0 -297
- package/src/test/unit/discovery-token-store.test.ts +0 -181
- package/src/test/unit/evidence-pack.test.ts +0 -142
- package/src/test/unit/goal-graph.test.ts +0 -347
- package/src/test/unit/goal-progress.test.ts +0 -538
- package/src/test/unit/goal-status.test.ts +0 -270
- package/src/test/unit/goal-store.test.ts +0 -318
- package/src/test/unit/goal-subgoal-task.test.ts +0 -72
- package/src/test/unit/goal-tools-registry.test.ts +0 -243
- package/src/test/unit/handoff-export.test.ts +0 -295
- package/src/test/unit/invoke-discovered-tool.test.ts +0 -216
- package/src/test/unit/logging.test.ts +0 -497
- package/src/test/unit/path-guard.test.ts +0 -150
- package/src/test/unit/post-task-cleanup.test.ts +0 -53
- package/src/test/unit/project-policy-release-mode.test.ts +0 -156
- package/src/test/unit/reconcile-tasks.test.ts +0 -551
- package/src/test/unit/release-gate.test.ts +0 -314
- package/src/test/unit/run-task-loop.test.ts +0 -425
- package/src/test/unit/safe-status.test.ts +0 -187
- package/src/test/unit/safe-views.test.ts +0 -184
- package/src/test/unit/schema-drift-check.test.ts +0 -260
- package/src/test/unit/sensitive-guard.test.ts +0 -124
- package/src/test/unit/subgoal-sync.test.ts +0 -236
- package/src/test/unit/sync-file.test.ts +0 -231
- package/src/test/unit/tool-invocation-guard.test.ts +0 -542
- package/src/test/unit/tool-usage-stats.test.ts +0 -384
- package/src/test/unit/toolSearch.test.ts +0 -756
- package/src/test/unit/watcher-status.test.ts +0 -190
- package/src/test/unit/worktree-manager.test.ts +0 -247
- package/src/tools/cancelTask.ts +0 -70
- package/src/tools/evidencePack.ts +0 -205
- package/src/tools/retryTask.ts +0 -53
- package/src/tools/syncFile.ts +0 -122
- /package/dist/tools/{toolCatalog.d.ts → catalog/toolCatalog.d.ts} +0 -0
- /package/dist/tools/{toolSearch.d.ts → catalog/toolSearch.d.ts} +0 -0
- /package/dist/tools/{toolSearch.js → catalog/toolSearch.js} +0 -0
- /package/dist/tools/{toolUsageStats.d.ts → catalog/toolUsageStats.d.ts} +0 -0
- /package/dist/tools/{toolUsageStats.js → catalog/toolUsageStats.js} +0 -0
- /package/dist/tools/{runDirectVerificationBundle.d.ts → direct/runDirectVerificationBundle.d.ts} +0 -0
- /package/dist/tools/{getPlan.d.ts → goals/getPlan.d.ts} +0 -0
- /package/dist/tools/{savePlan.d.ts → goals/savePlan.d.ts} +0 -0
- /package/dist/tools/{getTaskFile.d.ts → tasks/getTaskFile.d.ts} +0 -0
- /package/dist/tools/{getTaskProgress.d.ts → tasks/getTaskProgress.d.ts} +0 -0
- /package/dist/tools/{getTaskProgress.js → tasks/getTaskProgress.js} +0 -0
- /package/dist/tools/{getTaskStdoutTail.d.ts → tasks/getTaskStdoutTail.d.ts} +0 -0
- /package/dist/tools/{getTaskSummary.d.ts → tasks/getTaskSummary.d.ts} +0 -0
- /package/dist/tools/{killTask.js → tasks/killTask.js} +0 -0
- /package/dist/tools/{runVerification.d.ts → tasks/runVerification.d.ts} +0 -0
- /package/dist/tools/{taskLineage.d.ts → tasks/taskLineage.d.ts} +0 -0
- /package/dist/tools/{taskOutputs.d.ts → tasks/taskOutputs.d.ts} +0 -0
- /package/dist/tools/{waitForTask.d.ts → tasks/waitForTask.d.ts} +0 -0
- /package/dist/tools/{waitForTask.js → tasks/waitForTask.js} +0 -0
- /package/dist/tools/{androidDoctor.d.ts → workspace/androidDoctor.d.ts} +0 -0
- /package/dist/tools/{discardWorktree.d.ts → workspace/discardWorktree.d.ts} +0 -0
- /package/dist/tools/{listWorkspace.d.ts → workspace/listWorkspace.d.ts} +0 -0
- /package/dist/tools/{mergeWorktree.d.ts → workspace/mergeWorktree.d.ts} +0 -0
- /package/dist/tools/{readWorkspaceFile.d.ts → workspace/readWorkspaceFile.d.ts} +0 -0
- /package/dist/tools/{recommendAgentForTask.d.ts → workspace/recommendAgentForTask.d.ts} +0 -0
- /package/dist/tools/{searchWorkspace.d.ts → workspace/searchWorkspace.d.ts} +0 -0
- /package/src/tools/{toolSearch.ts → catalog/toolSearch.ts} +0 -0
- /package/src/tools/{toolUsageStats.ts → catalog/toolUsageStats.ts} +0 -0
- /package/src/tools/{getTaskProgress.ts → tasks/getTaskProgress.ts} +0 -0
- /package/src/tools/{killTask.ts → tasks/killTask.ts} +0 -0
- /package/src/tools/{waitForTask.ts → tasks/waitForTask.ts} +0 -0
package/dist/config.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { readFileSync, existsSync } from "node:fs";
|
|
2
|
-
import { isAbsolute, relative, resolve, sep } from "node:path";
|
|
2
|
+
import { basename, isAbsolute, relative, resolve, sep } from "node:path";
|
|
3
|
+
import { validateWorkspaceRoot } from "./security/workspaceRootGuard.js";
|
|
3
4
|
// ── Defaults ──────────────────────────────────────────────────────
|
|
4
5
|
const DEFAULT_CONFIG = {
|
|
5
6
|
workspaceRoot: process.cwd(),
|
|
@@ -7,16 +8,7 @@ const DEFAULT_CONFIG = {
|
|
|
7
8
|
tasksDir: ".patchwarden/tasks",
|
|
8
9
|
assessmentsDir: ".patchwarden/assessments",
|
|
9
10
|
assessmentTtlSeconds: 3600,
|
|
10
|
-
agents: {
|
|
11
|
-
codex: {
|
|
12
|
-
command: "codex",
|
|
13
|
-
args: ["exec", "--cd", "{repo}", "{prompt}"],
|
|
14
|
-
},
|
|
15
|
-
opencode: {
|
|
16
|
-
command: "opencode",
|
|
17
|
-
args: ["run", "{prompt}"],
|
|
18
|
-
},
|
|
19
|
-
},
|
|
11
|
+
agents: {},
|
|
20
12
|
allowedTestCommands: [
|
|
21
13
|
"npm test",
|
|
22
14
|
"npm run test",
|
|
@@ -123,6 +115,11 @@ function normalizeConfig(config) {
|
|
|
123
115
|
if (!config.workspaceRoot || typeof config.workspaceRoot !== "string") {
|
|
124
116
|
throw new Error("workspaceRoot must be a non-empty string");
|
|
125
117
|
}
|
|
118
|
+
const workspaceValidation = validateWorkspaceRoot(config.workspaceRoot);
|
|
119
|
+
if (!workspaceValidation.ok) {
|
|
120
|
+
throw new Error(`Invalid workspaceRoot: ${workspaceValidation.reason} (${workspaceValidation.path})`);
|
|
121
|
+
}
|
|
122
|
+
config.workspaceRoot = workspaceValidation.path;
|
|
126
123
|
if (!config.plansDir || typeof config.plansDir !== "string") {
|
|
127
124
|
throw new Error("plansDir must be a non-empty string");
|
|
128
125
|
}
|
|
@@ -138,6 +135,21 @@ function normalizeConfig(config) {
|
|
|
138
135
|
if (!config.agents || typeof config.agents !== "object") {
|
|
139
136
|
throw new Error("agents must be an object");
|
|
140
137
|
}
|
|
138
|
+
const agents = {};
|
|
139
|
+
for (const [agentName, agent] of Object.entries(config.agents)) {
|
|
140
|
+
if (!agent || typeof agent !== "object" || typeof agent.command !== "string" || !Array.isArray(agent.args)) {
|
|
141
|
+
throw new Error(`agents["${agentName}"] must define command and args`);
|
|
142
|
+
}
|
|
143
|
+
const envAllowlist = agent.envAllowlist ?? [];
|
|
144
|
+
if (!Array.isArray(envAllowlist) || envAllowlist.some((name) => typeof name !== "string" || !/^[A-Za-z_][A-Za-z0-9_]*$/.test(name))) {
|
|
145
|
+
throw new Error(`agents["${agentName}"].envAllowlist must contain only environment variable names`);
|
|
146
|
+
}
|
|
147
|
+
const blockedNames = new Set(["CONTROL_PLANE_API_KEY", "PATCHWARDEN_OWNER_TOKEN", config.http?.ownerTokenEnv?.toUpperCase()]);
|
|
148
|
+
const blocked = envAllowlist.find((name) => blockedNames.has(name.toUpperCase()));
|
|
149
|
+
if (blocked)
|
|
150
|
+
throw new Error(`agents["${agentName}"].envAllowlist cannot include reserved variable "${blocked}"`);
|
|
151
|
+
agents[agentName] = { ...agent, args: [...agent.args], envAllowlist: [...new Set(envAllowlist)] };
|
|
152
|
+
}
|
|
141
153
|
if (!Array.isArray(config.allowedTestCommands)) {
|
|
142
154
|
throw new Error("allowedTestCommands must be an array");
|
|
143
155
|
}
|
|
@@ -182,6 +194,42 @@ function normalizeConfig(config) {
|
|
|
182
194
|
config.toolProfile !== "chatgpt_search") {
|
|
183
195
|
throw new Error('toolProfile must be "full", "chatgpt_core", "chatgpt_direct", or "chatgpt_search"');
|
|
184
196
|
}
|
|
197
|
+
if (config.repoAliases !== undefined) {
|
|
198
|
+
if (typeof config.repoAliases !== "object" || config.repoAliases === null || Array.isArray(config.repoAliases)) {
|
|
199
|
+
throw new Error("repoAliases must be an object mapping alias names to repository paths");
|
|
200
|
+
}
|
|
201
|
+
for (const [alias, target] of Object.entries(config.repoAliases)) {
|
|
202
|
+
if (typeof target !== "string") {
|
|
203
|
+
throw new Error(`repoAliases["${alias}"] must be a string`);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
if (config.httpPort !== undefined) {
|
|
208
|
+
if (typeof config.httpPort !== "number" || !Number.isInteger(config.httpPort) || config.httpPort < 1 || config.httpPort > 65535) {
|
|
209
|
+
throw new Error("httpPort must be an integer from 1 to 65535");
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
if (config.http !== undefined) {
|
|
213
|
+
if (typeof config.http !== "object" || config.http === null || Array.isArray(config.http)) {
|
|
214
|
+
throw new Error("http must be an object");
|
|
215
|
+
}
|
|
216
|
+
if (config.http.port !== undefined) {
|
|
217
|
+
if (typeof config.http.port !== "number" || !Number.isInteger(config.http.port) || config.http.port < 1 || config.http.port > 65535) {
|
|
218
|
+
throw new Error("http.port must be an integer from 1 to 65535");
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
if (config.http.host !== undefined && typeof config.http.host !== "string") {
|
|
222
|
+
throw new Error("http.host must be a string");
|
|
223
|
+
}
|
|
224
|
+
if (config.http.ownerTokenEnv !== undefined
|
|
225
|
+
&& (typeof config.http.ownerTokenEnv !== "string"
|
|
226
|
+
|| !/^[A-Za-z_][A-Za-z0-9_]*$/.test(config.http.ownerTokenEnv))) {
|
|
227
|
+
throw new Error("http.ownerTokenEnv must be a valid environment variable name");
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
if (config.enableRunTaskTool !== undefined && typeof config.enableRunTaskTool !== "boolean") {
|
|
231
|
+
throw new Error("enableRunTaskTool must be a boolean");
|
|
232
|
+
}
|
|
185
233
|
if (config.enableAgentAssessment !== undefined && typeof config.enableAgentAssessment !== "boolean") {
|
|
186
234
|
throw new Error("enableAgentAssessment must be a boolean");
|
|
187
235
|
}
|
|
@@ -201,6 +249,44 @@ function normalizeConfig(config) {
|
|
|
201
249
|
if (config.enableDirectProfile !== undefined && typeof config.enableDirectProfile !== "boolean") {
|
|
202
250
|
throw new Error("enableDirectProfile must be a boolean");
|
|
203
251
|
}
|
|
252
|
+
if (config.tunnelClientPath !== undefined) {
|
|
253
|
+
if (typeof config.tunnelClientPath !== "string" || !isAbsolute(config.tunnelClientPath)) {
|
|
254
|
+
throw new Error("tunnelClientPath must be an absolute path");
|
|
255
|
+
}
|
|
256
|
+
if (basename(config.tunnelClientPath).toLowerCase() !== "tunnel-client.exe") {
|
|
257
|
+
throw new Error("tunnelClientPath must point to tunnel-client.exe");
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
if (config.tunnelProxy !== undefined) {
|
|
261
|
+
if (!config.tunnelProxy || typeof config.tunnelProxy !== "object" || !["shared", "separate"].includes(config.tunnelProxy.scope)) {
|
|
262
|
+
throw new Error("tunnelProxy.scope must be shared or separate");
|
|
263
|
+
}
|
|
264
|
+
for (const key of ["core", "direct"]) {
|
|
265
|
+
const endpoint = config.tunnelProxy[key];
|
|
266
|
+
if (!endpoint || typeof endpoint !== "object" || !["environment", "none", "manual"].includes(endpoint.mode)) {
|
|
267
|
+
throw new Error(`tunnelProxy.${key}.mode must be environment, none, or manual`);
|
|
268
|
+
}
|
|
269
|
+
if (endpoint.mode === "manual") {
|
|
270
|
+
if (typeof endpoint.url !== "string")
|
|
271
|
+
throw new Error(`tunnelProxy.${key}.url is required in manual mode`);
|
|
272
|
+
let parsed;
|
|
273
|
+
try {
|
|
274
|
+
parsed = new URL(endpoint.url);
|
|
275
|
+
}
|
|
276
|
+
catch {
|
|
277
|
+
throw new Error(`tunnelProxy.${key}.url must be a valid URL`);
|
|
278
|
+
}
|
|
279
|
+
if (!["http:", "https:", "socks5:"].includes(parsed.protocol)) {
|
|
280
|
+
throw new Error(`tunnelProxy.${key}.url must use http, https, or socks5`);
|
|
281
|
+
}
|
|
282
|
+
if (parsed.username || parsed.password)
|
|
283
|
+
throw new Error(`tunnelProxy.${key}.url must not contain credentials`);
|
|
284
|
+
}
|
|
285
|
+
else if (endpoint.url !== undefined) {
|
|
286
|
+
throw new Error(`tunnelProxy.${key}.url is only allowed in manual mode`);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
204
290
|
if (!config.directSessionsDir || typeof config.directSessionsDir !== "string") {
|
|
205
291
|
throw new Error("directSessionsDir must be a non-empty string");
|
|
206
292
|
}
|
|
@@ -242,6 +328,7 @@ function normalizeConfig(config) {
|
|
|
242
328
|
}
|
|
243
329
|
return {
|
|
244
330
|
...config,
|
|
331
|
+
agents,
|
|
245
332
|
workspaceRoot: resolve(config.workspaceRoot),
|
|
246
333
|
allowedTestCommands: [...new Set(config.allowedTestCommands.map((command) => command.trim()))],
|
|
247
334
|
repoAllowedTestCommands,
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { buildChildEnvironment, resolveTrustedExecutable } from "../runner/processSecurity.js";
|
|
4
|
+
const FILE_MANAGER_ENVIRONMENT = [
|
|
5
|
+
"DISPLAY",
|
|
6
|
+
"WAYLAND_DISPLAY",
|
|
7
|
+
"DBUS_SESSION_BUS_ADDRESS",
|
|
8
|
+
"XDG_RUNTIME_DIR",
|
|
9
|
+
];
|
|
10
|
+
/** Launch a trusted platform file manager without inheriting ambient secrets. */
|
|
11
|
+
export function launchFileManager(target, cwd) {
|
|
12
|
+
try {
|
|
13
|
+
const env = buildChildEnvironment({ cwd, allowedNames: FILE_MANAGER_ENVIRONMENT });
|
|
14
|
+
const requested = fileManagerCommand(env);
|
|
15
|
+
const command = resolveTrustedExecutable(requested, cwd, { pathValue: env.PATH });
|
|
16
|
+
const child = spawn(command, [target], {
|
|
17
|
+
cwd,
|
|
18
|
+
detached: true,
|
|
19
|
+
env,
|
|
20
|
+
stdio: "ignore",
|
|
21
|
+
windowsHide: true,
|
|
22
|
+
});
|
|
23
|
+
child.on("error", () => { });
|
|
24
|
+
child.unref();
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function fileManagerCommand(env) {
|
|
32
|
+
if (process.platform === "win32") {
|
|
33
|
+
const windowsRoot = env.SystemRoot || env.WINDIR;
|
|
34
|
+
return windowsRoot ? join(windowsRoot, "explorer.exe") : "explorer.exe";
|
|
35
|
+
}
|
|
36
|
+
if (process.platform === "darwin")
|
|
37
|
+
return "open";
|
|
38
|
+
return "xdg-open";
|
|
39
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Control Center auth middleware — control token validation.
|
|
3
|
+
*
|
|
4
|
+
* POST routes are gated behind an in-memory control token generated at boot.
|
|
5
|
+
* The token is exposed read-only via GET /control-token.json (handled by the
|
|
6
|
+
* server router) so the local dashboard can read it, but every mutating
|
|
7
|
+
* action must pass through `checkControlToken` first.
|
|
8
|
+
*/
|
|
9
|
+
import { type IncomingMessage } from "node:http";
|
|
10
|
+
export declare function checkControlToken(req: IncomingMessage): boolean;
|
|
11
|
+
/**
|
|
12
|
+
* Reject non-loopback Host headers before exposing the browser-readable
|
|
13
|
+
* control token. Binding to 127.0.0.1 alone does not prevent DNS rebinding.
|
|
14
|
+
*/
|
|
15
|
+
export declare function isTrustedControlHost(req: IncomingMessage, expectedPort: number): boolean;
|
|
16
|
+
export declare function isTrustedControlHostHeader(value: string | string[] | undefined, expectedPort: number): boolean;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { firstHeaderValue, timingSafeStringEqual } from "../../security/secretComparison.js";
|
|
2
|
+
import { isTrustedLoopbackHostHeader } from "../../security/loopbackHost.js";
|
|
3
|
+
import { controlToken } from "../shared.js";
|
|
4
|
+
export function checkControlToken(req) {
|
|
5
|
+
const provided = firstHeaderValue(req.headers["x-patchwarden-control-token"]);
|
|
6
|
+
return provided.length > 0 && timingSafeStringEqual(provided, controlToken);
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Reject non-loopback Host headers before exposing the browser-readable
|
|
10
|
+
* control token. Binding to 127.0.0.1 alone does not prevent DNS rebinding.
|
|
11
|
+
*/
|
|
12
|
+
export function isTrustedControlHost(req, expectedPort) {
|
|
13
|
+
return isTrustedControlHostHeader(req.headers.host, expectedPort);
|
|
14
|
+
}
|
|
15
|
+
export function isTrustedControlHostHeader(value, expectedPort) {
|
|
16
|
+
return isTrustedLoopbackHostHeader(value, expectedPort);
|
|
17
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Control Center static file middleware.
|
|
3
|
+
*
|
|
4
|
+
* Serves the bundled `ui/` directory (dashboard pages, partials, vendor
|
|
5
|
+
* assets, css) with path-traversal protection, plus the inline SVG favicon.
|
|
6
|
+
* Content types are mapped from `CONTENT_TYPES` in shared.ts.
|
|
7
|
+
*/
|
|
8
|
+
import { existsSync, readFileSync, realpathSync, statSync } from "node:fs";
|
|
9
|
+
import { extname, isAbsolute, relative, resolve } from "node:path";
|
|
10
|
+
import { CONTENT_TYPES, CONTROL_CENTER_FAVICON, errorMessage, sendJson, uiRoot, } from "../shared.js";
|
|
11
|
+
export function serveStatic(res, urlPath) {
|
|
12
|
+
let candidate = "";
|
|
13
|
+
try {
|
|
14
|
+
const normalized = urlPath.startsWith("/") ? urlPath.slice(1) : urlPath;
|
|
15
|
+
let decoded;
|
|
16
|
+
try {
|
|
17
|
+
decoded = decodeURIComponent(normalized);
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
sendJson(res, 400, { error: "Invalid path encoding" });
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
if (decoded.includes("\0")) {
|
|
24
|
+
sendJson(res, 400, { error: "Invalid path" });
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const segments = decoded.split("/").filter(Boolean);
|
|
28
|
+
if (segments.some((s) => s === "..")) {
|
|
29
|
+
sendJson(res, 403, { error: "Forbidden" });
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
candidate = resolve(uiRoot, ...segments);
|
|
33
|
+
const rel = relative(uiRoot, candidate);
|
|
34
|
+
if (rel === "" || rel.startsWith("..") || isAbsolute(rel)) {
|
|
35
|
+
sendJson(res, 403, { error: "Forbidden" });
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
catch (err) {
|
|
40
|
+
sendJson(res, 500, { error: errorMessage(err) });
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
try {
|
|
44
|
+
if (!existsSync(candidate) || !statSync(candidate).isFile()) {
|
|
45
|
+
sendJson(res, 404, { error: "Not found" });
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
// Resolve links before reading. Lexical `..` checks alone do not stop a
|
|
49
|
+
// symlink or junction inside `ui/` from pointing outside the bundle.
|
|
50
|
+
const realRoot = realpathSync(uiRoot);
|
|
51
|
+
const realCandidate = realpathSync(candidate);
|
|
52
|
+
const realRelative = relative(realRoot, realCandidate);
|
|
53
|
+
if (realRelative === "" || realRelative.startsWith("..") || isAbsolute(realRelative)) {
|
|
54
|
+
sendJson(res, 403, { error: "Forbidden" });
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
if (!statSync(realCandidate).isFile()) {
|
|
58
|
+
sendJson(res, 404, { error: "Not found" });
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
const ext = extname(realCandidate).toLowerCase();
|
|
62
|
+
const contentType = CONTENT_TYPES[ext] || "application/octet-stream";
|
|
63
|
+
const content = readFileSync(realCandidate);
|
|
64
|
+
res.writeHead(200, { "Content-Type": contentType });
|
|
65
|
+
res.end(content);
|
|
66
|
+
}
|
|
67
|
+
catch (err) {
|
|
68
|
+
sendJson(res, 500, { error: errorMessage(err) });
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
export function serveFavicon(res) {
|
|
72
|
+
res.writeHead(200, {
|
|
73
|
+
"Content-Type": "image/svg+xml; charset=utf-8",
|
|
74
|
+
"Cache-Control": "public, max-age=86400",
|
|
75
|
+
"X-Content-Type-Options": "nosniff",
|
|
76
|
+
});
|
|
77
|
+
res.end(CONTROL_CENTER_FAVICON);
|
|
78
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PatchWarden Control Center — declarative route table.
|
|
3
|
+
*
|
|
4
|
+
* Exports `buildRoutes(parsedUrl)` which returns the full `Route[]` array
|
|
5
|
+
* consumed by `server.ts`'s `handleRequest`. Routes that need query-string
|
|
6
|
+
* parameters (e.g. `/api/tasks`, `/api/logs/:category`) close over `parsedUrl`
|
|
7
|
+
* via the `query` helper, so each request gets its own self-contained table
|
|
8
|
+
* without module-scoped request state. This keeps concurrent requests safe.
|
|
9
|
+
*
|
|
10
|
+
* Ordering rules:
|
|
11
|
+
* - GET routes first, POST routes second.
|
|
12
|
+
* - Within each method, more specific patterns precede generic `:id`
|
|
13
|
+
* patterns (e.g. `/api/tasks/stale` before `/api/tasks/:id`).
|
|
14
|
+
* - Static asset routes (`/`, `/favicon.ico`, CSS/JS, `/pages/`,
|
|
15
|
+
* `/partials/`, `/vendor/`) and `/control-token.json` are NOT in this
|
|
16
|
+
* table — they remain priority branches in `server.ts`.
|
|
17
|
+
*/
|
|
18
|
+
import { type Route } from "./shared.js";
|
|
19
|
+
/**
|
|
20
|
+
* Build the per-request `Route[]` table. `parsedUrl` is captured by closures
|
|
21
|
+
* so handlers that need query parameters can read `searchParams` without any
|
|
22
|
+
* module-scoped state.
|
|
23
|
+
*/
|
|
24
|
+
export declare function buildRoutes(parsedUrl: URL): Route[];
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PatchWarden Control Center — declarative route table.
|
|
3
|
+
*
|
|
4
|
+
* Exports `buildRoutes(parsedUrl)` which returns the full `Route[]` array
|
|
5
|
+
* consumed by `server.ts`'s `handleRequest`. Routes that need query-string
|
|
6
|
+
* parameters (e.g. `/api/tasks`, `/api/logs/:category`) close over `parsedUrl`
|
|
7
|
+
* via the `query` helper, so each request gets its own self-contained table
|
|
8
|
+
* without module-scoped request state. This keeps concurrent requests safe.
|
|
9
|
+
*
|
|
10
|
+
* Ordering rules:
|
|
11
|
+
* - GET routes first, POST routes second.
|
|
12
|
+
* - Within each method, more specific patterns precede generic `:id`
|
|
13
|
+
* patterns (e.g. `/api/tasks/stale` before `/api/tasks/:id`).
|
|
14
|
+
* - Static asset routes (`/`, `/favicon.ico`, CSS/JS, `/pages/`,
|
|
15
|
+
* `/partials/`, `/vendor/`) and `/control-token.json` are NOT in this
|
|
16
|
+
* table — they remain priority branches in `server.ts`.
|
|
17
|
+
*/
|
|
18
|
+
import { decodeParam, resolveTailParam, sendJson } from "./shared.js";
|
|
19
|
+
import { isValidTaskId } from "./runtime.js";
|
|
20
|
+
import { handleTasks, handleStaleTasks, handleTaskDetail, handleTaskSafeResult, handleTaskSafeAudit, handleTaskSafeTestSummary, handleTaskSafeDiffSummary, } from "./routes/tasks.js";
|
|
21
|
+
import { handleReconcile, handleTaskAudit, handleOpenTaskFolder, handleHideStale, } from "./routes/taskActions.js";
|
|
22
|
+
import { handleDirectSessions, handleDirectSessionDetail, handleDirectSessionSafeSummary, handleDirectSessionFinalize, handleDirectSessionAudit, handleDirectSessionHide, } from "./routes/sessions.js";
|
|
23
|
+
import { handleLineages, handleLineageDetail } from "./routes/lineage.js";
|
|
24
|
+
import { handleEvidencePacks, handleEvidencePackDetail, handleEvidencePackExport, } from "./routes/evidence.js";
|
|
25
|
+
import { handleProjectPolicy, handleReleaseStatus } from "./routes/policy.js";
|
|
26
|
+
import { handleWorkspace, handleWorkspaceRepos, handleWorkspaceRepoStatus, } from "./routes/workspace.js";
|
|
27
|
+
import { handleManageAction, handleOpenLogsFolder } from "./routes/process.js";
|
|
28
|
+
import { handleStatus, handleControlCenterStatus, handleEvents, handleTunnelUiUrl, handleDiagnostics, } from "./routes/status.js";
|
|
29
|
+
import { handleLogs, handleAudit, handleWarnings } from "./routes/audit.js";
|
|
30
|
+
const KNOWN_LOG_CATEGORIES = new Set([
|
|
31
|
+
"core",
|
|
32
|
+
"direct",
|
|
33
|
+
"watcher",
|
|
34
|
+
"control-center",
|
|
35
|
+
]);
|
|
36
|
+
/**
|
|
37
|
+
* Build the per-request `Route[]` table. `parsedUrl` is captured by closures
|
|
38
|
+
* so handlers that need query parameters can read `searchParams` without any
|
|
39
|
+
* module-scoped state.
|
|
40
|
+
*/
|
|
41
|
+
export function buildRoutes(parsedUrl) {
|
|
42
|
+
const query = parsedUrl.searchParams;
|
|
43
|
+
// ── GET routes (no token required) ──────────────────────────────
|
|
44
|
+
const getRoutes = [
|
|
45
|
+
{ method: "GET", pattern: /^\/api\/status$/, handler: (res) => handleStatus(res) },
|
|
46
|
+
{
|
|
47
|
+
method: "GET",
|
|
48
|
+
pattern: /^\/api\/tasks$/,
|
|
49
|
+
handler: (res) => handleTasks(res, {
|
|
50
|
+
repo_path: query.get("repo_path") || undefined,
|
|
51
|
+
status: query.get("status") || undefined,
|
|
52
|
+
acceptance_status: query.get("acceptance_status") || undefined,
|
|
53
|
+
agent: query.get("agent") || undefined,
|
|
54
|
+
warning_type: query.get("warning_type") || undefined,
|
|
55
|
+
date_from: query.get("date_from") || undefined,
|
|
56
|
+
date_to: query.get("date_to") || undefined,
|
|
57
|
+
limit: query.get("limit") ? Number(query.get("limit")) : undefined,
|
|
58
|
+
cursor: query.get("cursor") || undefined,
|
|
59
|
+
}),
|
|
60
|
+
},
|
|
61
|
+
// /api/tasks/stale MUST precede /api/tasks/:id (stale matches the :id pattern).
|
|
62
|
+
{ method: "GET", pattern: /^\/api\/tasks\/stale$/, handler: (res) => handleStaleTasks(res) },
|
|
63
|
+
{ method: "GET", pattern: /^\/api\/lineages$/, handler: (res) => handleLineages(res) },
|
|
64
|
+
{
|
|
65
|
+
method: "GET",
|
|
66
|
+
pattern: /^\/api\/lineages\/([^/]+)$/,
|
|
67
|
+
handler: (res, p) => handleLineageDetail(res, decodeParam(p, 0)),
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
method: "GET",
|
|
71
|
+
pattern: /^\/api\/project-policy$/,
|
|
72
|
+
handler: (res) => handleProjectPolicy(res, query.get("repo_path") || "."),
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
method: "GET",
|
|
76
|
+
pattern: /^\/api\/release\/status$/,
|
|
77
|
+
handler: (res) => handleReleaseStatus(res, query.get("repo_path") || "."),
|
|
78
|
+
},
|
|
79
|
+
{ method: "GET", pattern: /^\/api\/evidence-packs$/, handler: (res) => handleEvidencePacks(res) },
|
|
80
|
+
{
|
|
81
|
+
method: "GET",
|
|
82
|
+
pattern: /^\/api\/evidence-packs\/([^/]+)$/,
|
|
83
|
+
handler: (res, p) => handleEvidencePackDetail(res, decodeParam(p, 0)),
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
method: "GET",
|
|
87
|
+
pattern: /^\/api\/tasks\/([^/]+)$/,
|
|
88
|
+
handler: (res, p) => {
|
|
89
|
+
const taskId = decodeParam(p, 0);
|
|
90
|
+
if (!isValidTaskId(taskId)) {
|
|
91
|
+
sendJson(res, 400, { error: "Invalid task id" });
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
handleTaskDetail(res, taskId);
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
// Safe, bounded views for task artifacts (no full stdout/stderr/diff).
|
|
98
|
+
{
|
|
99
|
+
method: "GET",
|
|
100
|
+
pattern: /^\/api\/tasks\/([^/]+)\/safe-result$/,
|
|
101
|
+
handler: (res, p) => handleTaskSafeResult(res, decodeParam(p, 0)),
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
method: "GET",
|
|
105
|
+
pattern: /^\/api\/tasks\/([^/]+)\/safe-audit$/,
|
|
106
|
+
handler: (res, p) => handleTaskSafeAudit(res, decodeParam(p, 0)),
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
method: "GET",
|
|
110
|
+
pattern: /^\/api\/tasks\/([^/]+)\/safe-test-summary$/,
|
|
111
|
+
handler: (res, p) => handleTaskSafeTestSummary(res, decodeParam(p, 0)),
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
method: "GET",
|
|
115
|
+
pattern: /^\/api\/tasks\/([^/]+)\/safe-diff-summary$/,
|
|
116
|
+
handler: (res, p) => handleTaskSafeDiffSummary(res, decodeParam(p, 0)),
|
|
117
|
+
},
|
|
118
|
+
// /api/logs/<category>?tail=<100|300|1000>
|
|
119
|
+
{
|
|
120
|
+
method: "GET",
|
|
121
|
+
pattern: /^\/api\/logs\/([a-z-]+)$/,
|
|
122
|
+
handler: (res, p) => {
|
|
123
|
+
const rawCat = p[0] || "";
|
|
124
|
+
const category = KNOWN_LOG_CATEGORIES.has(rawCat)
|
|
125
|
+
? rawCat
|
|
126
|
+
: null;
|
|
127
|
+
if (!category) {
|
|
128
|
+
sendJson(res, 404, { error: "Unknown log category" });
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
const tail = resolveTailParam(query.get("tail"));
|
|
132
|
+
handleLogs(res, category, tail);
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
{ method: "GET", pattern: /^\/api\/workspace$/, handler: (res) => handleWorkspace(res) },
|
|
136
|
+
// /api/workspace/repos MUST precede /api/workspace/:repo+/status (no segment conflict,
|
|
137
|
+
// but preserved for ordering clarity).
|
|
138
|
+
{ method: "GET", pattern: /^\/api\/workspace\/repos$/, handler: (res) => handleWorkspaceRepos(res) },
|
|
139
|
+
{
|
|
140
|
+
method: "GET",
|
|
141
|
+
pattern: /^\/api\/workspace\/([^/]+(?:\/[^/]+)*)\/status$/,
|
|
142
|
+
handler: (res, p) => {
|
|
143
|
+
const raw = p[0] || "";
|
|
144
|
+
let repoParam;
|
|
145
|
+
try {
|
|
146
|
+
repoParam = decodeURIComponent(raw);
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
sendJson(res, 400, { error: "Invalid repo path encoding" });
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
handleWorkspaceRepoStatus(res, repoParam);
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
method: "GET",
|
|
157
|
+
pattern: /^\/api\/direct-sessions$/,
|
|
158
|
+
handler: (res) => handleDirectSessions(res, {
|
|
159
|
+
state: (query.get("state") || undefined),
|
|
160
|
+
repo_path: query.get("repo_path") || undefined,
|
|
161
|
+
date_from: query.get("date_from") || undefined,
|
|
162
|
+
date_to: query.get("date_to") || undefined,
|
|
163
|
+
limit: query.get("limit") ? Number(query.get("limit")) : undefined,
|
|
164
|
+
cursor: query.get("cursor") || undefined,
|
|
165
|
+
}),
|
|
166
|
+
},
|
|
167
|
+
// /api/direct-sessions/:id/summary MUST precede /api/direct-sessions/:id.
|
|
168
|
+
{
|
|
169
|
+
method: "GET",
|
|
170
|
+
pattern: /^\/api\/direct-sessions\/([^/]+)\/summary$/,
|
|
171
|
+
handler: (res, p) => handleDirectSessionSafeSummary(res, decodeParam(p, 0)),
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
method: "GET",
|
|
175
|
+
pattern: /^\/api\/direct-sessions\/([^/]+)$/,
|
|
176
|
+
handler: (res, p) => handleDirectSessionDetail(res, decodeParam(p, 0)),
|
|
177
|
+
},
|
|
178
|
+
{ method: "GET", pattern: /^\/api\/audit$/, handler: (res) => handleAudit(res) },
|
|
179
|
+
{ method: "GET", pattern: /^\/api\/warnings$/, handler: (res) => handleWarnings(res) },
|
|
180
|
+
{ method: "GET", pattern: /^\/api\/diagnostics$/, handler: (res) => handleDiagnostics(res) },
|
|
181
|
+
{ method: "GET", pattern: /^\/api\/tunnel-ui-url$/, handler: (res) => handleTunnelUiUrl(res) },
|
|
182
|
+
{
|
|
183
|
+
method: "GET",
|
|
184
|
+
pattern: /^\/api\/events$/,
|
|
185
|
+
handler: (res) => {
|
|
186
|
+
const limitParam = query.get("limit");
|
|
187
|
+
let limit = 100;
|
|
188
|
+
if (limitParam !== null) {
|
|
189
|
+
const n = parseInt(limitParam, 10);
|
|
190
|
+
if (Number.isFinite(n) && n > 0 && n <= 1000)
|
|
191
|
+
limit = n;
|
|
192
|
+
}
|
|
193
|
+
handleEvents(res, limit);
|
|
194
|
+
},
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
method: "GET",
|
|
198
|
+
pattern: /^\/api\/control-center-status$/,
|
|
199
|
+
handler: (res) => handleControlCenterStatus(res),
|
|
200
|
+
},
|
|
201
|
+
];
|
|
202
|
+
// ── POST routes (requiresToken: true) ──────────────────────────
|
|
203
|
+
const postRoutes = [
|
|
204
|
+
{ method: "POST", pattern: /^\/api\/start-all$/, requiresToken: true, handler: (res) => handleManageAction(res, "start", "all") },
|
|
205
|
+
{ method: "POST", pattern: /^\/api\/stop-all$/, requiresToken: true, handler: (res) => handleManageAction(res, "stop", "all") },
|
|
206
|
+
{ method: "POST", pattern: /^\/api\/restart-all$/, requiresToken: true, handler: (res) => handleManageAction(res, "restart", "all") },
|
|
207
|
+
{ method: "POST", pattern: /^\/api\/core\/start$/, requiresToken: true, handler: (res) => handleManageAction(res, "start", "core") },
|
|
208
|
+
{ method: "POST", pattern: /^\/api\/core\/stop$/, requiresToken: true, handler: (res) => handleManageAction(res, "stop", "core") },
|
|
209
|
+
{ method: "POST", pattern: /^\/api\/direct\/start$/, requiresToken: true, handler: (res) => handleManageAction(res, "start", "direct") },
|
|
210
|
+
{ method: "POST", pattern: /^\/api\/direct\/stop$/, requiresToken: true, handler: (res) => handleManageAction(res, "stop", "direct") },
|
|
211
|
+
{ method: "POST", pattern: /^\/api\/open-logs-folder$/, requiresToken: true, handler: (res) => handleOpenLogsFolder(res) },
|
|
212
|
+
// /api/direct-sessions/:id/finalize MUST precede any generic /api/direct-sessions/:id pattern.
|
|
213
|
+
{
|
|
214
|
+
method: "POST",
|
|
215
|
+
pattern: /^\/api\/direct-sessions\/([^/]+)\/finalize$/,
|
|
216
|
+
requiresToken: true,
|
|
217
|
+
handler: (res, p) => handleDirectSessionFinalize(res, decodeParam(p, 0)),
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
method: "POST",
|
|
221
|
+
pattern: /^\/api\/direct-sessions\/([^/]+)\/audit$/,
|
|
222
|
+
requiresToken: true,
|
|
223
|
+
handler: (res, p) => handleDirectSessionAudit(res, decodeParam(p, 0)),
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
method: "POST",
|
|
227
|
+
pattern: /^\/api\/direct-sessions\/([^/]+)\/hide$/,
|
|
228
|
+
requiresToken: true,
|
|
229
|
+
handler: (res, p) => handleDirectSessionHide(res, decodeParam(p, 0)),
|
|
230
|
+
},
|
|
231
|
+
{
|
|
232
|
+
method: "POST",
|
|
233
|
+
pattern: /^\/api\/tasks\/([^/]+)\/reconcile$/,
|
|
234
|
+
requiresToken: true,
|
|
235
|
+
handler: (res, p) => handleReconcile(res, decodeParam(p, 0)),
|
|
236
|
+
},
|
|
237
|
+
{
|
|
238
|
+
method: "POST",
|
|
239
|
+
pattern: /^\/api\/tasks\/([^/]+)\/audit$/,
|
|
240
|
+
requiresToken: true,
|
|
241
|
+
handler: (res, p) => handleTaskAudit(res, decodeParam(p, 0)),
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
method: "POST",
|
|
245
|
+
pattern: /^\/api\/tasks\/([^/]+)\/open-folder$/,
|
|
246
|
+
requiresToken: true,
|
|
247
|
+
handler: (res, p) => handleOpenTaskFolder(res, decodeParam(p, 0)),
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
method: "POST",
|
|
251
|
+
pattern: /^\/api\/tasks\/([^/]+)\/hide-stale$/,
|
|
252
|
+
requiresToken: true,
|
|
253
|
+
handler: (res, p) => handleHideStale(res, decodeParam(p, 0)),
|
|
254
|
+
},
|
|
255
|
+
{
|
|
256
|
+
method: "POST",
|
|
257
|
+
pattern: /^\/api\/evidence-packs\/([^/]+)\/export$/,
|
|
258
|
+
requiresToken: true,
|
|
259
|
+
handler: (res, p) => handleEvidencePackExport(res, decodeParam(p, 0)),
|
|
260
|
+
},
|
|
261
|
+
];
|
|
262
|
+
return [...getRoutes, ...postRoutes];
|
|
263
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type ServerResponse } from "node:http";
|
|
2
|
+
export type LogCategory = "core" | "direct" | "watcher" | "control-center";
|
|
3
|
+
export declare function handleLogs(res: ServerResponse, category: LogCategory, tailLines: number): void;
|
|
4
|
+
export declare function handleAudit(res: ServerResponse): void;
|
|
5
|
+
/**
|
|
6
|
+
* Aggregate warnings from multiple sources:
|
|
7
|
+
* - audit.json warnings/fail_checks/possible_false_positives/manual_verification_required
|
|
8
|
+
* - stale task classification (heartbeat stale, collecting_artifacts stale, etc.)
|
|
9
|
+
* - task status=failed_verification
|
|
10
|
+
*
|
|
11
|
+
* Each warning type is grouped into a bucket with affected task IDs. The
|
|
12
|
+
* handler is fault-tolerant: any per-task read failure is skipped, and a
|
|
13
|
+
* top-level failure returns an empty warnings array (never 500).
|
|
14
|
+
*/
|
|
15
|
+
export declare function handleWarnings(res: ServerResponse): void;
|