patchwarden 1.5.1 → 1.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/PatchWarden.cmd +32 -2
- package/README.en.md +152 -25
- package/README.md +120 -23
- package/dist/assessments/agentAssessor.js +21 -12
- package/dist/assessments/assessmentStore.js +42 -34
- package/dist/config.d.ts +15 -0
- package/dist/config.js +66 -13
- package/dist/control/fileManager.d.ts +2 -0
- package/dist/control/fileManager.js +39 -0
- package/dist/control/middleware/auth.d.ts +6 -0
- package/dist/control/middleware/auth.js +14 -5
- package/dist/control/middleware/static.js +16 -3
- package/dist/control/routeTable.d.ts +24 -0
- package/dist/control/routeTable.js +263 -0
- package/dist/control/routes/audit.js +17 -7
- package/dist/control/routes/evidence.js +1 -1
- package/dist/control/routes/lineage.js +1 -1
- package/dist/control/routes/process.d.ts +7 -0
- package/dist/control/routes/process.js +329 -42
- package/dist/control/routes/sessions.d.ts +9 -1
- package/dist/control/routes/sessions.js +84 -21
- package/dist/control/routes/status.d.ts +46 -1
- package/dist/control/routes/status.js +72 -15
- package/dist/control/routes/taskActions.js +24 -60
- package/dist/control/routes/tasks.d.ts +4 -0
- package/dist/control/routes/tasks.js +61 -62
- package/dist/control/routes/workspace.js +50 -23
- package/dist/control/runtime.d.ts +3 -2
- package/dist/control/runtime.js +63 -30
- package/dist/control/server.js +36 -365
- package/dist/control/shared.d.ts +25 -1
- package/dist/control/shared.js +64 -11
- package/dist/direct/directAudit.js +234 -225
- package/dist/direct/directGuards.d.ts +2 -1
- package/dist/direct/directGuards.js +42 -29
- 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.js +62 -52
- package/dist/goal/acceptanceEngine.d.ts +1 -1
- package/dist/goal/goalProgress.js +75 -69
- package/dist/goal/goalReport.js +5 -12
- package/dist/goal/goalStatus.d.ts +2 -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.js +97 -96
- package/dist/goal/subgoalSync.js +11 -9
- package/dist/goal/worktreeManager.d.ts +2 -0
- package/dist/goal/worktreeManager.js +209 -136
- package/dist/httpServer.js +111 -32
- package/dist/logging.js +5 -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 +13 -5
- package/dist/runner/changeCapture.js +243 -61
- package/dist/runner/postTaskCleanup.js +48 -19
- 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 +115 -87
- package/dist/runner/simpleProcess.d.ts +3 -0
- package/dist/runner/simpleProcess.js +50 -36
- 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 +256 -48
- package/dist/security/contentRedaction.js +2 -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 +24 -23
- 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} +6 -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} +1 -1
- package/dist/tools/{auditTask.js → diagnostics/auditTask.js} +99 -29
- package/dist/tools/{healthCheck.d.ts → diagnostics/healthCheck.d.ts} +6 -6
- package/dist/tools/{healthCheck.js → diagnostics/healthCheck.js} +7 -7
- 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} +12 -8
- package/dist/tools/{safeViews.js → diagnostics/safeViews.js} +23 -13
- 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.js → direct/createDirectSession.js} +6 -6
- package/dist/tools/{finalizeDirectSession.d.ts → direct/finalizeDirectSession.d.ts} +1 -1
- 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.js +32 -31
- package/dist/tools/dispatch/diagnosticDispatch.d.ts +3 -5
- package/dist/tools/dispatch/diagnosticDispatch.js +70 -68
- package/dist/tools/dispatch/directDispatch.js +11 -10
- package/dist/tools/dispatch/goalDispatch.js +5 -4
- package/dist/tools/dispatch/releaseDispatch.js +3 -2
- 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} +1 -1
- 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 +7 -11
- package/dist/tools/registry.js +17 -1178
- 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} +10 -2
- package/dist/tools/{createTask.js → tasks/createTask.js} +42 -27
- 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.js → tasks/evidencePack.js} +34 -22
- 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/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} +58 -30
- 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 +808 -143
- package/docs/architecture.md +15 -55
- package/docs/assets/patchwarden-desktop-onboarding.png +0 -0
- package/docs/control-center/README.md +4 -4
- package/docs/dashboard-overview.md +4 -4
- package/docs/desktop-app.md +175 -0
- package/docs/evidence-pack-schema.md +3 -3
- package/docs/open-source-application.md +17 -11
- package/docs/release-checklist.md +1 -1
- package/docs/release-evidence.md +39 -29
- package/docs/task-safe-review-workflow.md +5 -5
- package/docs/threat-model.md +19 -1
- package/examples/config.example.json +4 -2
- package/package.json +27 -10
- 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 +91 -6
- package/scripts/checks/control-smoke.js +19 -8
- package/scripts/checks/http-mcp-smoke.js +82 -4
- package/scripts/checks/lifecycle-smoke.js +10 -10
- package/scripts/checks/mcp-manifest-check.js +1 -1
- package/scripts/checks/mcp-smoke.js +27 -3
- 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/{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 +26 -12
- package/src/assessments/assessmentStore.ts +52 -40
- package/src/config.ts +70 -13
- package/src/control/fileManager.ts +40 -0
- package/src/control/middleware/auth.ts +22 -4
- package/src/control/middleware/static.ts +16 -3
- package/src/control/routeTable.ts +301 -0
- package/src/control/routes/audit.ts +14 -6
- package/src/control/routes/evidence.ts +1 -1
- package/src/control/routes/lineage.ts +1 -1
- package/src/control/routes/process.ts +341 -40
- package/src/control/routes/sessions.ts +88 -25
- package/src/control/routes/status.ts +97 -17
- package/src/control/routes/taskActions.ts +24 -58
- package/src/control/routes/tasks.ts +66 -58
- package/src/control/routes/workspace.ts +56 -25
- package/src/control/runtime.ts +69 -33
- package/src/control/server.ts +36 -342
- package/src/control/shared.ts +77 -11
- package/src/direct/directAudit.ts +17 -9
- package/src/direct/directGuards.ts +72 -44
- 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 +73 -59
- package/src/goal/acceptanceEngine.ts +1 -1
- package/src/goal/goalProgress.ts +17 -13
- package/src/goal/goalReport.ts +5 -13
- package/src/goal/goalStatus.ts +19 -3
- package/src/goal/goalStore.ts +241 -54
- package/src/goal/handoffExport.ts +6 -6
- package/src/goal/specKitImport.ts +12 -12
- package/src/goal/subgoalSync.ts +11 -8
- package/src/goal/worktreeManager.ts +139 -22
- package/src/httpServer.ts +113 -30
- package/src/logging.ts +10 -3
- 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 +291 -57
- package/src/runner/postTaskCleanup.ts +41 -17
- package/src/runner/processSecurity.ts +433 -0
- package/src/runner/runTask.ts +138 -91
- package/src/runner/simpleProcess.ts +66 -31
- 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 +281 -51
- package/src/security/contentRedaction.ts +2 -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 +25 -24
- 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} +7 -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} +99 -32
- package/src/tools/{healthCheck.ts → diagnostics/healthCheck.ts} +8 -8
- 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} +27 -17
- package/src/tools/{schemaDriftCheck.ts → diagnostics/schemaDriftCheck.ts} +3 -3
- package/src/tools/{createDirectSession.ts → direct/createDirectSession.ts} +6 -6
- package/src/tools/{finalizeDirectSession.ts → direct/finalizeDirectSession.ts} +7 -5
- 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 +36 -31
- package/src/tools/dispatch/diagnosticDispatch.ts +26 -21
- package/src/tools/dispatch/directDispatch.ts +11 -10
- package/src/tools/dispatch/goalDispatch.ts +5 -4
- package/src/tools/dispatch/releaseDispatch.ts +3 -2
- 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} +41 -26
- package/src/tools/goals/index.ts +3 -0
- package/src/tools/{savePlan.ts → goals/savePlan.ts} +38 -8
- package/src/tools/registry.ts +17 -1261
- 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} +49 -26
- package/src/tools/{diagnoseTask.ts → tasks/diagnoseTask.ts} +8 -8
- package/src/tools/{evidencePack.ts → tasks/evidencePack.ts} +36 -22
- 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} +69 -34
- 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 +12 -0
- package/ui/pages/dashboard.html +90 -147
- package/ui/pages/direct-sessions.html +123 -26
- 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 +12 -0
- package/ui/pages/tasks.html +54 -8
- 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/apply-patch.test.d.ts +0 -1
- package/dist/test/unit/apply-patch.test.js +0 -225
- 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/create-task.test.d.ts +0 -1
- package/dist/test/unit/create-task.test.js +0 -197
- 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 -328
- 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 -224
- package/dist/test/unit/get-task-status.test.d.ts +0 -1
- package/dist/test/unit/get-task-status.test.js +0 -174
- package/dist/test/unit/get-task-summary.test.d.ts +0 -1
- package/dist/test/unit/get-task-summary.test.js +0 -146
- 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-report.test.d.ts +0 -1
- package/dist/test/unit/goal-report.test.js +0 -159
- 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 -192
- 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 -133
- 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/spec-kit-import.test.d.ts +0 -1
- package/dist/test/unit/spec-kit-import.test.js +0 -341
- 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/wait-for-task.test.d.ts +0 -1
- package/dist/test/unit/wait-for-task.test.js +0 -144
- 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/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/execution-plan-2026-07-09.md +0 -315
- 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/docs/roadmap-execution-and-acceptance.md +0 -365
- 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/apply-patch.test.ts +0 -293
- 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/create-task.test.ts +0 -255
- package/src/test/unit/diagnose-task.test.ts +0 -544
- package/src/test/unit/direct-guards.test.ts +0 -467
- package/src/test/unit/discovery-token-store.test.ts +0 -181
- package/src/test/unit/evidence-pack.test.ts +0 -251
- package/src/test/unit/get-task-status.test.ts +0 -203
- package/src/test/unit/get-task-summary.test.ts +0 -173
- 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-report.test.ts +0 -189
- 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 -245
- 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 -180
- 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/spec-kit-import.test.ts +0 -429
- 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/wait-for-task.test.ts +0 -176
- 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/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/{createDirectSession.d.ts → direct/createDirectSession.d.ts} +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/{evidencePack.d.ts → tasks/evidencePack.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/{retryTask.d.ts → tasks/retryTask.d.ts} +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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "patchwarden",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.1",
|
|
4
4
|
"description": "Secure local MCP bridge for ChatGPT and local agents, with workspace-scoped task loops, safe summaries, audits, and Direct editing.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -12,9 +12,12 @@
|
|
|
12
12
|
},
|
|
13
13
|
"files": [
|
|
14
14
|
"dist/",
|
|
15
|
+
"!dist/test/",
|
|
15
16
|
"docs/",
|
|
17
|
+
"!docs/archive/",
|
|
16
18
|
"!docs/optimization-proposal.md",
|
|
17
19
|
"src/",
|
|
20
|
+
"!src/test/",
|
|
18
21
|
"examples/",
|
|
19
22
|
"scripts/",
|
|
20
23
|
"!scripts/control/kill-patchwarden.ps1",
|
|
@@ -22,18 +25,14 @@
|
|
|
22
25
|
"!.local/",
|
|
23
26
|
"ui/",
|
|
24
27
|
"PatchWarden.cmd",
|
|
25
|
-
"PatchWarden-Control.cmd",
|
|
26
|
-
"PatchWarden-Control-Tray.cmd",
|
|
27
|
-
"PatchWarden-Desktop.cmd",
|
|
28
|
-
"Restart-PatchWarden-Control.cmd",
|
|
29
|
-
"Stop-PatchWarden.cmd",
|
|
30
28
|
"README.md",
|
|
31
29
|
"README.en.md",
|
|
32
30
|
"LICENSE",
|
|
33
31
|
"tsconfig.json"
|
|
34
32
|
],
|
|
35
33
|
"scripts": {
|
|
36
|
-
"build": "node
|
|
34
|
+
"build": "node scripts/build.js",
|
|
35
|
+
"check:build-output": "node scripts/checks/build-output-check.js",
|
|
37
36
|
"start": "node dist/index.js",
|
|
38
37
|
"start:http": "node dist/httpServer.js",
|
|
39
38
|
"start:control": "node dist/controlCenter.js",
|
|
@@ -43,7 +42,7 @@
|
|
|
43
42
|
"doctor": "node dist/doctor.js",
|
|
44
43
|
"doctor:ci": "node dist/doctor.js --allow-default-config",
|
|
45
44
|
"test:unit": "node scripts/checks/unit-tests.js",
|
|
46
|
-
"test": "node dist/smoke-test.js && node scripts/checks/unit-tests.js && node scripts/checks/lifecycle-smoke.js && node scripts/checks/doctor-smoke.js && node scripts/checks/tunnel-supervisor-smoke.js && node scripts/checks/watcher-supervisor-smoke.js && node scripts/checks/control-smoke.js && node scripts/checks/mcp-manifest-check.js && node scripts/checks/brand-check.js && node scripts/checks/control-center-smoke.js",
|
|
45
|
+
"test": "node scripts/checks/build-output-check.js && node dist/smoke-test.js && node scripts/checks/unit-tests.js && node scripts/checks/lifecycle-smoke.js && node scripts/checks/doctor-smoke.js && node scripts/checks/tunnel-supervisor-smoke.js && node scripts/checks/watcher-supervisor-smoke.js && node scripts/checks/control-smoke.js && node scripts/checks/mcp-manifest-check.js && node scripts/checks/brand-check.js && node scripts/checks/control-center-smoke.js",
|
|
47
46
|
"test:lifecycle": "npm run build && node scripts/checks/lifecycle-smoke.js",
|
|
48
47
|
"test:doctor": "npm run build && node scripts/checks/doctor-smoke.js",
|
|
49
48
|
"test:tunnel-supervisor": "npm run build && node scripts/checks/tunnel-supervisor-smoke.js",
|
|
@@ -53,18 +52,36 @@
|
|
|
53
52
|
"check:search-tool-manifest": "npm run build && node scripts/checks/mcp-manifest-check.js --profile chatgpt_search",
|
|
54
53
|
"check:brand": "node scripts/checks/brand-check.js",
|
|
55
54
|
"test:mcp": "npm run build && node scripts/checks/mcp-smoke.js",
|
|
55
|
+
"test:mcp:built": "node scripts/checks/mcp-smoke.js",
|
|
56
56
|
"test:http-mcp": "npm run build && node scripts/checks/http-mcp-smoke.js",
|
|
57
|
+
"test:http-mcp:built": "node scripts/checks/http-mcp-smoke.js",
|
|
57
58
|
"pretest": "npm run build",
|
|
58
59
|
"pack:clean": "npm run build && node scripts/release/pack-clean.js",
|
|
59
|
-
"
|
|
60
|
+
"pack:clean:built": "node scripts/release/pack-clean.js",
|
|
61
|
+
"verify:package": "npm run build && node scripts/checks/package-manifest-check.js",
|
|
62
|
+
"verify:package:built": "node scripts/checks/package-manifest-check.js",
|
|
63
|
+
"desktop:install": "npm.cmd --prefix desktop install --cache ..\\.npm-cache",
|
|
64
|
+
"desktop:test": "npm.cmd --prefix desktop test",
|
|
65
|
+
"desktop:smoke:unpacked": "npm.cmd --prefix desktop run smoke:unpacked --",
|
|
66
|
+
"desktop:stage": "npm run build && npm.cmd --prefix desktop run stage",
|
|
67
|
+
"desktop:dev": "npm run build && npm.cmd --prefix desktop run dev",
|
|
68
|
+
"desktop:package:dir": "npm run build && npm.cmd --prefix desktop run build:dir",
|
|
69
|
+
"desktop:package": "npm run build && npm.cmd --prefix desktop run build",
|
|
70
|
+
"desktop:preflight": "node scripts/release/desktop-preflight.js",
|
|
71
|
+
"desktop:preflight:release": "node scripts/release/desktop-preflight.js --require-clean"
|
|
60
72
|
},
|
|
61
73
|
"dependencies": {
|
|
62
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
74
|
+
"@modelcontextprotocol/sdk": "^1.29.0"
|
|
63
75
|
},
|
|
64
76
|
"devDependencies": {
|
|
65
77
|
"@types/node": "^26.1.0",
|
|
66
78
|
"typescript": "^6.0.3"
|
|
67
79
|
},
|
|
80
|
+
"overrides": {
|
|
81
|
+
"@hono/node-server": "2.0.11",
|
|
82
|
+
"hono": "4.12.31",
|
|
83
|
+
"fast-uri": "4.1.1"
|
|
84
|
+
},
|
|
68
85
|
"engines": {
|
|
69
86
|
"node": ">=18.0.0"
|
|
70
87
|
},
|
package/scripts/README.md
CHANGED
|
@@ -4,13 +4,18 @@ This directory keeps implementation scripts out of the root folder. Normal
|
|
|
4
4
|
desktop use should start from the root entrypoints:
|
|
5
5
|
|
|
6
6
|
```powershell
|
|
7
|
-
.\PatchWarden-Desktop.cmd
|
|
8
|
-
.\PatchWarden-Control.cmd
|
|
9
|
-
.\PatchWarden-Control-Tray.cmd --foreground
|
|
10
|
-
.\Stop-PatchWarden.cmd
|
|
7
|
+
.\scripts\launchers\PatchWarden-Desktop.cmd
|
|
8
|
+
.\scripts\launchers\PatchWarden-Control.cmd
|
|
9
|
+
.\scripts\launchers\PatchWarden-Control-Tray.cmd --foreground
|
|
10
|
+
.\scripts\launchers\Stop-PatchWarden.cmd
|
|
11
11
|
.\PatchWarden.cmd status all
|
|
12
12
|
```
|
|
13
13
|
|
|
14
|
+
The installable Windows desktop shell is built separately with
|
|
15
|
+
`npm.cmd run desktop:package`. It preserves these source/npm entrypoints and
|
|
16
|
+
uses the same Control Center API rather than adding another process-management
|
|
17
|
+
surface. See `docs/desktop-app.md`.
|
|
18
|
+
|
|
14
19
|
## Control Scripts
|
|
15
20
|
|
|
16
21
|
- `control/manage-patchwarden.ps1`: backing implementation for `PatchWarden.cmd`.
|
|
@@ -34,11 +39,15 @@ desktop use should start from the root entrypoints:
|
|
|
34
39
|
- `checks/mcp-manifest-check.js`: validates MCP manifest expectations.
|
|
35
40
|
- `brand-check.js`: checks public brand strings.
|
|
36
41
|
- `checks/package-manifest-check.js`: verifies package contents.
|
|
42
|
+
- `checks/build-output-check.js`: verifies clean-output confinement and exact recursive unit-test compilation.
|
|
43
|
+
- `release/desktop-preflight.js`: builds an isolated Windows directory package and writes a bounded preflight receipt.
|
|
37
44
|
|
|
38
45
|
## Release Helpers
|
|
39
46
|
|
|
40
|
-
- `release/pack-clean.js`: rebuilds
|
|
41
|
-
|
|
47
|
+
- `release/pack-clean.js`: rebuilds the isolated `release/package/` staging
|
|
48
|
+
directory, `patchwarden-release.tar.gz`, and the versioned
|
|
49
|
+
`PatchWarden-v*.zip` artifact. Desktop and preflight siblings under
|
|
50
|
+
`release/` are preserved.
|
|
42
51
|
|
|
43
52
|
## Compatibility Launchers
|
|
44
53
|
|
package/scripts/build.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { cleanGeneratedOutput } from "./lib/clean-generated-output.js";
|
|
5
|
+
|
|
6
|
+
const root = resolve(fileURLToPath(new URL("..", import.meta.url)));
|
|
7
|
+
const dist = resolve(root, "dist");
|
|
8
|
+
|
|
9
|
+
cleanGeneratedOutput(root, dist);
|
|
10
|
+
process.chdir(root);
|
|
11
|
+
await import("../node_modules/typescript/lib/tsc.js");
|
|
@@ -12,6 +12,9 @@ const allowedLegacyFiles = new Set([
|
|
|
12
12
|
"docs/release-v0.3.0.md",
|
|
13
13
|
"docs/release-v0.4.0.md",
|
|
14
14
|
"docs/release-v0.6.0.md",
|
|
15
|
+
"docs/archive/releases/release-v0.6.0.md",
|
|
16
|
+
"docs/archive/releases/release-v0.6.1.md",
|
|
17
|
+
"docs/archive/releases/release-v0.6.4.md",
|
|
15
18
|
"scripts/checks/brand-check.js",
|
|
16
19
|
"scripts/release/pack-clean.js",
|
|
17
20
|
]);
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { existsSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
import { relative, resolve } from "node:path";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
import { cleanGeneratedOutput } from "../lib/clean-generated-output.js";
|
|
8
|
+
import { collectMatchingFiles } from "../lib/file-discovery.js";
|
|
9
|
+
|
|
10
|
+
const root = resolve(fileURLToPath(new URL("../..", import.meta.url)));
|
|
11
|
+
const sourceRoot = resolve(root, "src", "test", "unit");
|
|
12
|
+
const compiledRoot = resolve(root, "dist", "test", "unit");
|
|
13
|
+
|
|
14
|
+
const sourceTests = collectMatchingFiles(sourceRoot, (name) => name.endsWith(".test.ts"))
|
|
15
|
+
.map((path) => relative(sourceRoot, path).replace(/\\/g, "/").replace(/\.ts$/, ".js"));
|
|
16
|
+
const compiledTests = collectMatchingFiles(compiledRoot, (name) => name.endsWith(".test.js"))
|
|
17
|
+
.map((path) => relative(compiledRoot, path).replace(/\\/g, "/"));
|
|
18
|
+
|
|
19
|
+
assert.deepEqual(compiledTests, sourceTests, "compiled unit tests must exactly match source test paths");
|
|
20
|
+
assert.ok(compiledTests.some((path) => path.includes("/")), "unit discovery must include nested tests");
|
|
21
|
+
|
|
22
|
+
const fixtureRoot = mkdtempSync(resolve(tmpdir(), "patchwarden-clean-build-"));
|
|
23
|
+
try {
|
|
24
|
+
const fixtureDist = resolve(fixtureRoot, "dist");
|
|
25
|
+
const outside = resolve(fixtureRoot, "keep.txt");
|
|
26
|
+
mkdirSync(fixtureDist);
|
|
27
|
+
writeFileSync(resolve(fixtureDist, "stale.js"), "stale\n", "utf8");
|
|
28
|
+
writeFileSync(outside, "keep\n", "utf8");
|
|
29
|
+
cleanGeneratedOutput(fixtureRoot, fixtureDist);
|
|
30
|
+
assert.equal(existsSync(fixtureDist), false, "clean build must remove stale dist contents");
|
|
31
|
+
assert.equal(existsSync(outside), true, "clean build must preserve files outside dist");
|
|
32
|
+
assert.throws(() => cleanGeneratedOutput(fixtureRoot, resolve(fixtureRoot, "release")), /Refusing/);
|
|
33
|
+
} finally {
|
|
34
|
+
rmSync(fixtureRoot, { recursive: true, force: true });
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
console.log(`[build-output-check] OK: ${sourceTests.length} source tests match ${compiledTests.length} compiled tests.`);
|
|
@@ -121,9 +121,9 @@ process.on("SIGTERM", () => {
|
|
|
121
121
|
process.exit(143);
|
|
122
122
|
});
|
|
123
123
|
|
|
124
|
-
function httpGet(url) {
|
|
124
|
+
function httpGet(url, headers = {}) {
|
|
125
125
|
return new Promise((resolve, reject) => {
|
|
126
|
-
const req = http.get(url, (res) => {
|
|
126
|
+
const req = http.get(url, { headers }, (res) => {
|
|
127
127
|
let data = "";
|
|
128
128
|
res.on("data", (chunk) => (data += chunk));
|
|
129
129
|
res.on("end", () => resolve({ status: res.statusCode, headers: res.headers, body: data }));
|
|
@@ -133,6 +133,34 @@ function httpGet(url) {
|
|
|
133
133
|
});
|
|
134
134
|
}
|
|
135
135
|
|
|
136
|
+
async function testLoopbackHostBoundary() {
|
|
137
|
+
const name = "Control Center rejects DNS-rebinding Host headers and cannot be framed";
|
|
138
|
+
try {
|
|
139
|
+
const hostileToken = await httpGet(`${BASE_URL}/control-token.json`, {
|
|
140
|
+
Host: "patchwarden.attacker.invalid",
|
|
141
|
+
});
|
|
142
|
+
const hostilePage = await httpGet(`${BASE_URL}/`, {
|
|
143
|
+
Host: "127.0.0.1.attacker.invalid",
|
|
144
|
+
});
|
|
145
|
+
const localPage = await httpGet(`${BASE_URL}/`);
|
|
146
|
+
const problems = [];
|
|
147
|
+
if (hostileToken.status !== 421 || hostileToken.body.includes("token")) {
|
|
148
|
+
problems.push(`hostile token request returned ${hostileToken.status}: ${hostileToken.body.slice(0, 120)}`);
|
|
149
|
+
}
|
|
150
|
+
if (hostilePage.status !== 421) problems.push(`hostile page request returned ${hostilePage.status}`);
|
|
151
|
+
if (localPage.status !== 200) problems.push(`loopback page returned ${localPage.status}`);
|
|
152
|
+
if (String(localPage.headers["x-frame-options"] || "").toUpperCase() !== "DENY") {
|
|
153
|
+
problems.push("X-Frame-Options is not DENY");
|
|
154
|
+
}
|
|
155
|
+
if (!String(localPage.headers["content-security-policy"] || "").includes("frame-ancestors 'none'")) {
|
|
156
|
+
problems.push("CSP does not deny frame ancestors");
|
|
157
|
+
}
|
|
158
|
+
record(name, problems.length === 0, problems.join("; "));
|
|
159
|
+
} catch (err) {
|
|
160
|
+
record(name, false, `error: ${err.message}`);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
136
164
|
function httpPost(url, headers = {}) {
|
|
137
165
|
return new Promise((resolve, reject) => {
|
|
138
166
|
const parsed = new URL(url);
|
|
@@ -221,6 +249,12 @@ async function testStaticFiles() {
|
|
|
221
249
|
const root = await httpGet(`${BASE_URL}/`);
|
|
222
250
|
const vendor = await httpGet(`${BASE_URL}/vendor/tailwindcss-browser.js`);
|
|
223
251
|
const css = await httpGet(`${BASE_URL}/colors_and_type.css`);
|
|
252
|
+
const desktopCss = await httpGet(`${BASE_URL}/desktop.css`);
|
|
253
|
+
const desktopBridge = await httpGet(`${BASE_URL}/desktop-bridge.js`);
|
|
254
|
+
const i18n = await httpGet(`${BASE_URL}/i18n.js`);
|
|
255
|
+
const gettingStarted = await httpGet(`${BASE_URL}/pages/getting-started.html`);
|
|
256
|
+
const gettingStartedJs = await httpGet(`${BASE_URL}/getting-started.js`);
|
|
257
|
+
const settings = await httpGet(`${BASE_URL}/pages/settings.html`);
|
|
224
258
|
const favicon = await httpGet(`${BASE_URL}/favicon.ico`);
|
|
225
259
|
|
|
226
260
|
const checks = [];
|
|
@@ -228,7 +262,8 @@ async function testStaticFiles() {
|
|
|
228
262
|
const rootCt = root.headers["content-type"] || "";
|
|
229
263
|
if (!rootCt.includes("text/html")) checks.push(`GET / -> Content-Type ${rootCt} (expected text/html)`);
|
|
230
264
|
if (!root.body.includes("setup-checklist-card")) checks.push("dashboard missing setup checklist card");
|
|
231
|
-
if (!root.body.includes("
|
|
265
|
+
if (!root.body.includes("查看 Core / Direct 本次日志尾部")) checks.push("dashboard activity log is not collapsed behind a summary");
|
|
266
|
+
if (!root.body.includes("pw-dashboard-toolbar")) checks.push("dashboard missing stable desktop toolbar layout");
|
|
232
267
|
if (!root.body.includes("evidence-pack-card")) checks.push("dashboard missing v1.5 evidence pack card");
|
|
233
268
|
|
|
234
269
|
if (vendor.status !== 200) checks.push(`GET /vendor/tailwindcss-browser.js -> status ${vendor.status}`);
|
|
@@ -239,6 +274,31 @@ async function testStaticFiles() {
|
|
|
239
274
|
const cssCt = css.headers["content-type"] || "";
|
|
240
275
|
if (!cssCt.includes("text/css")) checks.push(`GET /colors_and_type.css -> Content-Type ${cssCt}`);
|
|
241
276
|
|
|
277
|
+
if (desktopCss.status !== 200 || !(desktopCss.headers["content-type"] || "").includes("text/css")) {
|
|
278
|
+
checks.push("desktop refinement stylesheet is not served as CSS");
|
|
279
|
+
}
|
|
280
|
+
if (desktopBridge.status !== 200 || !(desktopBridge.headers["content-type"] || "").includes("javascript")) {
|
|
281
|
+
checks.push("desktop bridge is not served as JavaScript");
|
|
282
|
+
}
|
|
283
|
+
if (i18n.status !== 200 || !i18n.body.includes("applyTranslations") || !i18n.body.includes('"zh-CN"') || !i18n.body.includes("en:")) {
|
|
284
|
+
checks.push("shared Chinese/English translation dictionary is unavailable");
|
|
285
|
+
}
|
|
286
|
+
if (gettingStarted.status !== 200 || (gettingStarted.body.match(/class="readiness-item"/g) || []).length !== 4 || !gettingStarted.body.includes("home.manual")) {
|
|
287
|
+
checks.push("desktop getting-started page is missing its four readiness checks or manual ChatGPT state");
|
|
288
|
+
}
|
|
289
|
+
if (gettingStartedJs.status !== 200 || !gettingStartedJs.body.includes('fetchJson("/api/status")') || !gettingStartedJs.body.includes("health_check")) {
|
|
290
|
+
checks.push("getting-started controller is missing bounded Tunnel status or health_check guidance");
|
|
291
|
+
}
|
|
292
|
+
if (settings.status !== 200 || !settings.body.includes("/settings.js")) {
|
|
293
|
+
checks.push("desktop settings page is unavailable or missing its bounded bridge");
|
|
294
|
+
}
|
|
295
|
+
for (const marker of ["detectTunnel", "chooseTunnel", "enableDirectProfile", "proxyScope", "saveRuntime", "tunnelId", "runtimeKey", "provisionTunnel", "forgetCredential", "language"]) {
|
|
296
|
+
if (!settings.body.includes(marker)) checks.push(`desktop settings missing runtime control: ${marker}`);
|
|
297
|
+
}
|
|
298
|
+
if (!desktopBridge.body.includes("patchwardenApplyTheme") || !desktopBridge.body.includes("请求超过 10 秒")) {
|
|
299
|
+
checks.push("desktop bridge is missing renderer theme propagation or bounded loading timeout");
|
|
300
|
+
}
|
|
301
|
+
|
|
242
302
|
if (favicon.status !== 200) checks.push(`GET /favicon.ico -> status ${favicon.status}`);
|
|
243
303
|
const faviconCt = favicon.headers["content-type"] || "";
|
|
244
304
|
if (!faviconCt.includes("image/svg+xml")) checks.push(`GET /favicon.ico -> Content-Type ${faviconCt}`);
|
|
@@ -268,6 +328,9 @@ async function testPageNavigationRoutes() {
|
|
|
268
328
|
"/pages/workspace.html",
|
|
269
329
|
"/pages/audit.html",
|
|
270
330
|
"/pages/task-detail.html?id=smoke",
|
|
331
|
+
"/settings.html",
|
|
332
|
+
"/pages/settings.html",
|
|
333
|
+
"/pages/getting-started.html",
|
|
271
334
|
];
|
|
272
335
|
|
|
273
336
|
for (const route of routes) {
|
|
@@ -411,6 +474,13 @@ async function testTasksJson() {
|
|
|
411
474
|
if (!Array.isArray(json.tasks)) problems.push("'tasks' is not an array");
|
|
412
475
|
if (typeof json.total !== "number") problems.push("'total' is not a number");
|
|
413
476
|
if (typeof json.returned !== "number") problems.push("'returned' is not a number");
|
|
477
|
+
if (!(json.nextCursor === null || typeof json.nextCursor === "string")) problems.push("'nextCursor' is neither null nor string");
|
|
478
|
+
if (!isObject(json.filters) || !isObject(json.filters.applied) || !isObject(json.filters.options)) problems.push("'filters' contract is missing");
|
|
479
|
+
|
|
480
|
+
const traversal = await httpGet(`${BASE_URL}/api/tasks/..%2Foutside`);
|
|
481
|
+
if (traversal.status !== 400) {
|
|
482
|
+
problems.push(`encoded task traversal returned ${traversal.status} (expected 400)`);
|
|
483
|
+
}
|
|
414
484
|
|
|
415
485
|
if (problems.length === 0) {
|
|
416
486
|
record(name, true);
|
|
@@ -823,7 +893,7 @@ async function testStaleTasksApi() {
|
|
|
823
893
|
|
|
824
894
|
// ── Test 12: GET /api/workspace/<traversal>/status -> 400 ───────
|
|
825
895
|
async function testWorkspaceRepoPathTraversal() {
|
|
826
|
-
const name = "Test 12:
|
|
896
|
+
const name = "Test 12: workspace status rejects traversal without rejecting dotted names";
|
|
827
897
|
// Use percent-encoded dots to bypass URL-parser segment normalization;
|
|
828
898
|
// after decode the repoParam is "../secret" which contains ".." and is
|
|
829
899
|
// rejected by the server's traversal guard.
|
|
@@ -833,7 +903,12 @@ async function testWorkspaceRepoPathTraversal() {
|
|
|
833
903
|
if (res.status === 400) {
|
|
834
904
|
const json = tryJson(res.body);
|
|
835
905
|
if (json && typeof json.error === "string" && /traversal/i.test(json.error)) {
|
|
836
|
-
|
|
906
|
+
const dotted = await httpGet(`${BASE_URL}/api/workspace/release..candidate/status`);
|
|
907
|
+
if (dotted.status === 404) {
|
|
908
|
+
record(name, true);
|
|
909
|
+
} else {
|
|
910
|
+
record(name, false, `valid dotted repo name returned ${dotted.status} (expected 404 for missing directory)`);
|
|
911
|
+
}
|
|
837
912
|
} else {
|
|
838
913
|
record(name, false, `status 400 but error field missing/invalid: ${res.body.slice(0, 200)}`);
|
|
839
914
|
}
|
|
@@ -882,10 +957,17 @@ async function testDirectSessionsEmptyList() {
|
|
|
882
957
|
const problems = [];
|
|
883
958
|
if (!Array.isArray(json.sessions)) problems.push("'sessions' is not an array");
|
|
884
959
|
if (typeof json.total !== "number") problems.push("'total' is not a number");
|
|
960
|
+
if (!(json.nextCursor === null || typeof json.nextCursor === "string")) problems.push("'nextCursor' is neither null nor string");
|
|
961
|
+
if (!isObject(json.filters) || !isObject(json.filters.options)) problems.push("'filters' contract is missing");
|
|
962
|
+
if (typeof json.direct_profile_enabled !== "boolean") problems.push("'direct_profile_enabled' is not a boolean");
|
|
885
963
|
if (json.reason !== null && typeof json.reason !== "string") problems.push("'reason' is neither null nor string");
|
|
886
964
|
if (Array.isArray(json.sessions) && json.sessions.length !== json.total) {
|
|
887
965
|
problems.push(`sessions.length (${json.sessions.length}) != total (${json.total})`);
|
|
888
966
|
}
|
|
967
|
+
const invalidDetail = await httpGet(`${BASE_URL}/api/direct-sessions/invalid.id`);
|
|
968
|
+
const invalidSummary = await httpGet(`${BASE_URL}/api/direct-sessions/invalid.id/summary`);
|
|
969
|
+
if (invalidDetail.status !== 400) problems.push(`invalid detail id returned ${invalidDetail.status} (expected 400)`);
|
|
970
|
+
if (invalidSummary.status !== 400) problems.push(`invalid summary id returned ${invalidSummary.status} (expected 400)`);
|
|
889
971
|
if (problems.length === 0) {
|
|
890
972
|
record(name, true);
|
|
891
973
|
} else {
|
|
@@ -915,6 +997,8 @@ async function testLogsTailParam() {
|
|
|
915
997
|
if (typeof json.stderr !== "string") problems.push("'stderr' is not a string");
|
|
916
998
|
if (json.tail !== 300) problems.push(`'tail' is ${json.tail} (expected 300)`);
|
|
917
999
|
if (json.category !== "core") problems.push(`'category' is ${json.category} (expected "core")`);
|
|
1000
|
+
if (json.historical_snapshot !== true) problems.push("'historical_snapshot' is not true");
|
|
1001
|
+
if (json.redacted !== true) problems.push("'redacted' is not true");
|
|
918
1002
|
if (problems.length === 0) {
|
|
919
1003
|
record(name, true);
|
|
920
1004
|
} else {
|
|
@@ -1440,7 +1524,7 @@ async function main() {
|
|
|
1440
1524
|
process.exit(1);
|
|
1441
1525
|
}
|
|
1442
1526
|
|
|
1443
|
-
child = spawn(
|
|
1527
|
+
child = spawn(process.execPath, [serverPath], {
|
|
1444
1528
|
cwd: projectRoot,
|
|
1445
1529
|
env: {
|
|
1446
1530
|
...process.env,
|
|
@@ -1480,6 +1564,7 @@ async function main() {
|
|
|
1480
1564
|
console.log(`[control-center-smoke] server ready at ${BASE_URL}`);
|
|
1481
1565
|
|
|
1482
1566
|
await testStaticFiles();
|
|
1567
|
+
await testLoopbackHostBoundary();
|
|
1483
1568
|
await testPageNavigationRoutes();
|
|
1484
1569
|
await testStatusJson();
|
|
1485
1570
|
await testTasksJson();
|
|
@@ -139,12 +139,13 @@ try {
|
|
|
139
139
|
|
|
140
140
|
const expectedFiles = [
|
|
141
141
|
"PatchWarden.cmd",
|
|
142
|
-
"PatchWarden-Control.cmd",
|
|
143
|
-
"PatchWarden-Control-Tray.cmd",
|
|
144
|
-
"PatchWarden-Desktop.cmd",
|
|
145
|
-
"Restart-PatchWarden-Control.cmd",
|
|
146
|
-
"Stop-PatchWarden.cmd",
|
|
142
|
+
"scripts/launchers/PatchWarden-Control.cmd",
|
|
143
|
+
"scripts/launchers/PatchWarden-Control-Tray.cmd",
|
|
144
|
+
"scripts/launchers/PatchWarden-Desktop.cmd",
|
|
145
|
+
"scripts/launchers/Restart-PatchWarden-Control.cmd",
|
|
146
|
+
"scripts/launchers/Stop-PatchWarden.cmd",
|
|
147
147
|
"scripts/control/manage-patchwarden.ps1",
|
|
148
|
+
"scripts/control/run-background-supervisor.ps1",
|
|
148
149
|
"scripts/control/stop-patchwarden.ps1",
|
|
149
150
|
"scripts/launchers/Start-PatchWarden-Tunnel.cmd",
|
|
150
151
|
"scripts/launchers/Start-PatchWarden-Direct-Tunnel.cmd",
|
|
@@ -158,14 +159,24 @@ try {
|
|
|
158
159
|
if (!rootEntry.includes("manage-patchwarden.ps1")) {
|
|
159
160
|
throw new Error("PatchWarden.cmd does not invoke the consolidated manager");
|
|
160
161
|
}
|
|
161
|
-
const stopEntry = readFileSync(join(root, "Stop-PatchWarden.cmd"), "utf8");
|
|
162
|
-
if (!stopEntry.includes("
|
|
162
|
+
const stopEntry = readFileSync(join(root, "scripts", "launchers", "Stop-PatchWarden.cmd"), "utf8");
|
|
163
|
+
if (!stopEntry.includes("control\\stop-patchwarden.ps1")) {
|
|
163
164
|
throw new Error("Stop-PatchWarden.cmd does not invoke the one-click shutdown script");
|
|
164
165
|
}
|
|
165
|
-
const desktopEntry = readFileSync(join(root, "PatchWarden-Desktop.cmd"), "utf8");
|
|
166
|
+
const desktopEntry = readFileSync(join(root, "scripts", "launchers", "PatchWarden-Desktop.cmd"), "utf8");
|
|
166
167
|
if (!desktopEntry.includes("control-center-tray.ps1") || !desktopEntry.includes("WindowStyle Hidden")) {
|
|
167
168
|
throw new Error("PatchWarden-Desktop.cmd must launch the tray hidden as the daily desktop entry");
|
|
168
169
|
}
|
|
170
|
+
const managerSource = readFileSync(manager, "utf8");
|
|
171
|
+
const wrapperSource = readFileSync(join(root, "scripts", "control", "run-background-supervisor.ps1"), "utf8");
|
|
172
|
+
for (const expected of ["supervisor-status.json", "supervisor.stdout.log", "supervisor.stderr.log", "run-background-supervisor.ps1", "Write-BackgroundStartingState"]) {
|
|
173
|
+
if (!managerSource.includes(expected)) {
|
|
174
|
+
throw new Error(`background manager is missing supervisor observability: ${expected}`);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
if (!wrapperSource.includes("1> $stdout 2> $stderr") || wrapperSource.includes("CONTROL_PLANE_API_KEY")) {
|
|
178
|
+
throw new Error("background wrapper must redirect supervisor logs without credential arguments");
|
|
179
|
+
}
|
|
169
180
|
console.log("ok - control handles orphan cleanup, scoped kill, port conflicts, health fallback, and Core/Direct lifecycle actions");
|
|
170
181
|
} finally {
|
|
171
182
|
if (fakeTunnel?.exitCode === null) fakeTunnel.kill();
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { spawn } from "node:child_process";
|
|
3
|
+
import http from "node:http";
|
|
3
4
|
import {
|
|
4
5
|
existsSync,
|
|
5
6
|
mkdirSync,
|
|
6
7
|
mkdtempSync,
|
|
8
|
+
readFileSync,
|
|
7
9
|
rmSync,
|
|
8
10
|
writeFileSync,
|
|
9
11
|
} from "node:fs";
|
|
@@ -11,7 +13,7 @@ import { tmpdir } from "node:os";
|
|
|
11
13
|
import { dirname, join, resolve } from "node:path";
|
|
12
14
|
import { fileURLToPath } from "node:url";
|
|
13
15
|
import { setTimeout as sleep } from "node:timers/promises";
|
|
14
|
-
import { CHATGPT_CORE_TOOL_NAMES } from "../../dist/tools/toolCatalog.js";
|
|
16
|
+
import { CHATGPT_CORE_TOOL_NAMES } from "../../dist/tools/catalog/toolCatalog.js";
|
|
15
17
|
|
|
16
18
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
17
19
|
const root = resolve(__dirname, "..", "..");
|
|
@@ -96,6 +98,25 @@ function toolJson(result) {
|
|
|
96
98
|
return JSON.parse(toolText(result));
|
|
97
99
|
}
|
|
98
100
|
|
|
101
|
+
function requestWithHost(path, hostHeader) {
|
|
102
|
+
return new Promise((resolvePromise, rejectPromise) => {
|
|
103
|
+
const request = http.request({
|
|
104
|
+
hostname: host,
|
|
105
|
+
port,
|
|
106
|
+
path,
|
|
107
|
+
method: "GET",
|
|
108
|
+
headers: { Host: hostHeader },
|
|
109
|
+
}, (response) => {
|
|
110
|
+
let body = "";
|
|
111
|
+
response.setEncoding("utf-8");
|
|
112
|
+
response.on("data", (chunk) => { body += chunk; });
|
|
113
|
+
response.on("end", () => resolvePromise({ status: response.statusCode, body }));
|
|
114
|
+
});
|
|
115
|
+
request.on("error", rejectPromise);
|
|
116
|
+
request.end();
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
99
120
|
console.log("\n=== PatchWarden HTTP MCP Smoke Tests ===\n");
|
|
100
121
|
|
|
101
122
|
try {
|
|
@@ -126,7 +147,7 @@ try {
|
|
|
126
147
|
);
|
|
127
148
|
|
|
128
149
|
console.log(`Starting HTTP MCP server on ${mcpUrl}...`);
|
|
129
|
-
serverProcess = spawn(
|
|
150
|
+
serverProcess = spawn(process.execPath, [serverPath], {
|
|
130
151
|
cwd: root,
|
|
131
152
|
env: {
|
|
132
153
|
...process.env,
|
|
@@ -163,6 +184,13 @@ try {
|
|
|
163
184
|
}
|
|
164
185
|
});
|
|
165
186
|
|
|
187
|
+
await test("HTTP MCP rejects DNS-rebinding Host headers", async () => {
|
|
188
|
+
const response = await requestWithHost("/healthz", "patchwarden.attacker.invalid");
|
|
189
|
+
if (response.status !== 421 || !response.body.includes("Untrusted Host")) {
|
|
190
|
+
throw new Error(`Unexpected hostile Host response: ${JSON.stringify(response)}`);
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
|
|
166
194
|
await test("wrong endpoint returns structured 404 guidance", async () => {
|
|
167
195
|
const response = await fetch(`http://${host}:${port}/wrong-path`);
|
|
168
196
|
const data = await response.json();
|
|
@@ -262,7 +290,7 @@ try {
|
|
|
262
290
|
|
|
263
291
|
// Start server WITH owner token
|
|
264
292
|
console.log("\n Starting server with owner token...");
|
|
265
|
-
serverProcess = spawn(
|
|
293
|
+
serverProcess = spawn(process.execPath, [serverPath], {
|
|
266
294
|
cwd: root,
|
|
267
295
|
env: {
|
|
268
296
|
...process.env,
|
|
@@ -278,7 +306,7 @@ try {
|
|
|
278
306
|
// Wait for server to be ready
|
|
279
307
|
for (let i = 0; i < 10; i++) {
|
|
280
308
|
try {
|
|
281
|
-
const r = await fetch(
|
|
309
|
+
const r = await fetch(`http://${host}:${port}/healthz`);
|
|
282
310
|
if (r.status === 200) break;
|
|
283
311
|
} catch {}
|
|
284
312
|
await sleep(500);
|
|
@@ -328,6 +356,56 @@ try {
|
|
|
328
356
|
}
|
|
329
357
|
});
|
|
330
358
|
|
|
359
|
+
const acceptanceTaskId = "task_http_acceptance";
|
|
360
|
+
const acceptanceTaskDir = join(workspaceRoot, ".patchwarden", "tasks", acceptanceTaskId);
|
|
361
|
+
mkdirSync(acceptanceTaskDir, { recursive: true });
|
|
362
|
+
writeFileSync(join(acceptanceTaskDir, "status.json"), JSON.stringify({
|
|
363
|
+
task_id: acceptanceTaskId,
|
|
364
|
+
status: "done_by_agent",
|
|
365
|
+
acceptance_status: "pending",
|
|
366
|
+
}), "utf-8");
|
|
367
|
+
|
|
368
|
+
await test("admin acceptance requires the owner token", async () => {
|
|
369
|
+
const response = await fetch(`http://${host}:${port}/admin/tasks/${acceptanceTaskId}/accept`, {
|
|
370
|
+
method: "POST",
|
|
371
|
+
headers: { "content-type": "application/json" },
|
|
372
|
+
body: JSON.stringify({ notes: "reviewed" }),
|
|
373
|
+
});
|
|
374
|
+
if (response.status !== 401) throw new Error(`Expected 401, got ${response.status}`);
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
await test("admin acceptance writes bounded atomic evidence", async () => {
|
|
378
|
+
const secret = `ghp_${"a".repeat(24)}`;
|
|
379
|
+
const response = await fetch(`http://${host}:${port}/admin/tasks/${acceptanceTaskId}/accept`, {
|
|
380
|
+
method: "POST",
|
|
381
|
+
headers: {
|
|
382
|
+
"content-type": "application/json",
|
|
383
|
+
Authorization: `Bearer ${OWNER_TOKEN}`,
|
|
384
|
+
},
|
|
385
|
+
body: JSON.stringify({ notes: `reviewed ${secret}` }),
|
|
386
|
+
});
|
|
387
|
+
if (response.status !== 200) throw new Error(`Expected 200, got ${response.status}: ${await response.text()}`);
|
|
388
|
+
const acceptance = JSON.parse(readFileSync(join(acceptanceTaskDir, "acceptance.json"), "utf-8"));
|
|
389
|
+
const status = JSON.parse(readFileSync(join(acceptanceTaskDir, "status.json"), "utf-8"));
|
|
390
|
+
if (acceptance.status !== "accepted" || status.acceptance_status !== "accepted") {
|
|
391
|
+
throw new Error("Acceptance state was not persisted consistently");
|
|
392
|
+
}
|
|
393
|
+
if (JSON.stringify(acceptance).includes(secret)) throw new Error("Acceptance notes persisted a token");
|
|
394
|
+
if (existsSync(join(acceptanceTaskDir, "status.json.lock"))) throw new Error("Status lock was not released");
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
await test("admin acceptance rejects oversized request bodies", async () => {
|
|
398
|
+
const response = await fetch(`http://${host}:${port}/admin/tasks/${acceptanceTaskId}/reject`, {
|
|
399
|
+
method: "POST",
|
|
400
|
+
headers: {
|
|
401
|
+
"content-type": "application/json",
|
|
402
|
+
Authorization: `Bearer ${OWNER_TOKEN}`,
|
|
403
|
+
},
|
|
404
|
+
body: JSON.stringify({ reason: "x".repeat(70 * 1024) }),
|
|
405
|
+
});
|
|
406
|
+
if (response.status !== 413) throw new Error(`Expected 413, got ${response.status}`);
|
|
407
|
+
});
|
|
408
|
+
|
|
331
409
|
} catch (error) {
|
|
332
410
|
fail("HTTP MCP smoke setup", error);
|
|
333
411
|
} finally {
|
|
@@ -152,16 +152,16 @@ try {
|
|
|
152
152
|
);
|
|
153
153
|
process.env.PATCHWARDEN_CONFIG = configPath;
|
|
154
154
|
|
|
155
|
-
const { savePlan } = await import("../../dist/tools/savePlan.js");
|
|
156
|
-
const { createTask } = await import("../../dist/tools/createTask.js");
|
|
157
|
-
const { getTaskStatus } = await import("../../dist/tools/getTaskStatus.js");
|
|
158
|
-
const { cancelTask } = await import("../../dist/tools/cancelTask.js");
|
|
159
|
-
const { killTask } = await import("../../dist/tools/killTask.js");
|
|
160
|
-
const { getDiff } = await import("../../dist/tools/taskOutputs.js");
|
|
161
|
-
const { listAgents } = await import("../../dist/tools/listAgents.js");
|
|
162
|
-
const { getTaskSummary } = await import("../../dist/tools/getTaskSummary.js");
|
|
163
|
-
const { auditTask } = await import("../../dist/tools/auditTask.js");
|
|
164
|
-
const { waitForTask } = await import("../../dist/tools/waitForTask.js");
|
|
155
|
+
const { savePlan } = await import("../../dist/tools/goals/savePlan.js");
|
|
156
|
+
const { createTask } = await import("../../dist/tools/tasks/createTask.js");
|
|
157
|
+
const { getTaskStatus } = await import("../../dist/tools/tasks/getTaskStatus.js");
|
|
158
|
+
const { cancelTask } = await import("../../dist/tools/tasks/cancelTask.js");
|
|
159
|
+
const { killTask } = await import("../../dist/tools/tasks/killTask.js");
|
|
160
|
+
const { getDiff } = await import("../../dist/tools/tasks/taskOutputs.js");
|
|
161
|
+
const { listAgents } = await import("../../dist/tools/workspace/listAgents.js");
|
|
162
|
+
const { getTaskSummary } = await import("../../dist/tools/tasks/getTaskSummary.js");
|
|
163
|
+
const { auditTask } = await import("../../dist/tools/diagnostics/auditTask.js");
|
|
164
|
+
const { waitForTask } = await import("../../dist/tools/tasks/waitForTask.js");
|
|
165
165
|
const { runTask } = await import("../../dist/runner/runTask.js");
|
|
166
166
|
const { reloadConfig } = await import("../../dist/config.js");
|
|
167
167
|
|
|
@@ -4,7 +4,7 @@ import { resolve } from "node:path";
|
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
6
6
|
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
7
|
-
import { CHATGPT_CORE_TOOL_NAMES, CHATGPT_DIRECT_TOOL_NAMES, CHATGPT_SEARCH_TOOL_NAMES } from "../../dist/tools/toolCatalog.js";
|
|
7
|
+
import { CHATGPT_CORE_TOOL_NAMES, CHATGPT_DIRECT_TOOL_NAMES, CHATGPT_SEARCH_TOOL_NAMES } from "../../dist/tools/catalog/toolCatalog.js";
|
|
8
8
|
|
|
9
9
|
const root = resolve(fileURLToPath(new URL("../..", import.meta.url)));
|
|
10
10
|
const jsonOnly = process.argv.includes("--json");
|