patchwarden 1.5.1 → 1.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/PatchWarden.cmd +32 -2
- package/README.en.md +158 -26
- package/README.md +124 -24
- package/dist/assessments/agentAssessor.js +21 -12
- package/dist/assessments/assessmentDiagnostics.d.ts +29 -0
- package/dist/assessments/assessmentDiagnostics.js +132 -0
- package/dist/assessments/assessmentStore.d.ts +39 -0
- package/dist/assessments/assessmentStore.js +207 -45
- package/dist/assessments/securitySnapshot.d.ts +50 -0
- package/dist/assessments/securitySnapshot.js +158 -0
- 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 +60 -1
- package/dist/control/routes/status.js +94 -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 +65 -40
- package/dist/control/server.js +37 -365
- package/dist/control/shared.d.ts +25 -1
- package/dist/control/shared.js +64 -11
- package/dist/diagnostics/allowedTestCommandSafety.d.ts +1 -0
- package/dist/diagnostics/allowedTestCommandSafety.js +11 -0
- 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 +128 -35
- package/dist/direct/directVerification.js +8 -16
- package/dist/doctor.js +65 -57
- 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 +2 -1
- package/dist/runner/runTask.js +162 -93
- 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} +3 -1
- package/dist/{taskRuntime.js → runner/taskRuntime.js} +3 -2
- package/dist/runner/taskStatusStore.d.ts +11 -0
- package/dist/runner/taskStatusStore.js +35 -0
- package/dist/runner/watch.d.ts +14 -1
- package/dist/runner/watch.js +320 -49
- 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 +27 -26
- 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} +2 -1
- package/dist/tools/{auditTask.js → diagnostics/auditTask.js} +134 -36
- package/dist/tools/{healthCheck.d.ts → diagnostics/healthCheck.d.ts} +18 -6
- package/dist/tools/{healthCheck.js → diagnostics/healthCheck.js} +36 -8
- 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 +86 -0
- package/dist/tools/{createTask.d.ts → tasks/createTask.d.ts} +12 -4
- package/dist/tools/{createTask.js → tasks/createTask.js} +110 -34
- package/dist/tools/{diagnoseTask.d.ts → tasks/diagnoseTask.d.ts} +1 -1
- package/dist/tools/{diagnoseTask.js → tasks/diagnoseTask.js} +12 -16
- 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} +11 -6
- package/dist/tools/{getTaskStdoutTail.js → tasks/getTaskStdoutTail.js} +13 -11
- package/dist/tools/{getTaskSummary.js → tasks/getTaskSummary.js} +35 -36
- 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} +5 -4
- package/dist/tools/{reconcileTasks.d.ts → tasks/reconcileTasks.d.ts} +3 -2
- package/dist/tools/tasks/reconcileTasks.js +496 -0
- 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} +63 -41
- package/dist/tools/tasks/runVerification.js +38 -0
- package/dist/tools/{taskLineage.js → tasks/taskLineage.js} +8 -7
- package/dist/tools/{taskOutputs.d.ts → tasks/taskOutputs.d.ts} +2 -2
- package/dist/tools/{taskOutputs.js → tasks/taskOutputs.js} +17 -16
- package/dist/tools/tasks/taskStates.d.ts +6 -0
- package/dist/tools/tasks/taskStates.js +52 -0
- package/dist/tools/{waitForTask.js → tasks/waitForTask.js} +3 -11
- 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/workspace/listAgents.d.ts +23 -0
- package/dist/tools/workspace/listAgents.js +56 -0
- 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 +67 -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 +16 -11
- package/docs/release-checklist.md +1 -1
- package/docs/release-evidence.md +35 -39
- 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 +102 -10
- package/scripts/checks/control-smoke.js +19 -8
- package/scripts/checks/http-mcp-smoke.js +82 -4
- package/scripts/checks/lifecycle-smoke.js +15 -12
- package/scripts/checks/mcp-manifest-check.js +1 -1
- package/scripts/checks/mcp-smoke.js +58 -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 +38 -1
- 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 +191 -33
- package/scripts/e2e/demo-runtime.mjs +153 -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 +26 -12
- package/src/assessments/assessmentDiagnostics.ts +172 -0
- package/src/assessments/assessmentStore.ts +288 -51
- package/src/assessments/securitySnapshot.ts +231 -0
- 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 +134 -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 +71 -43
- package/src/control/server.ts +37 -342
- package/src/control/shared.ts +77 -11
- package/src/diagnostics/allowedTestCommandSafety.ts +12 -0
- 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 +213 -55
- package/src/direct/directVerification.ts +9 -23
- package/src/doctor.ts +76 -64
- 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 +197 -97
- package/src/runner/simpleProcess.ts +66 -31
- package/src/{taskProgress.ts → runner/taskProgress.ts} +3 -3
- package/src/{taskRuntime.ts → runner/taskRuntime.ts} +6 -3
- package/src/runner/taskStatusStore.ts +69 -0
- package/src/runner/watch.ts +348 -52
- 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 +27 -26
- 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} +136 -39
- package/src/tools/{healthCheck.ts → diagnostics/healthCheck.ts} +37 -9
- 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 +110 -0
- package/src/tools/{createTask.ts → tasks/createTask.ts} +161 -33
- package/src/tools/{diagnoseTask.ts → tasks/diagnoseTask.ts} +12 -16
- 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} +13 -6
- package/src/tools/{getTaskStdoutTail.ts → tasks/getTaskStdoutTail.ts} +13 -11
- package/src/tools/{getTaskSummary.ts → tasks/getTaskSummary.ts} +46 -44
- package/src/tools/tasks/index.ts +18 -0
- package/src/tools/{listTasks.ts → tasks/listTasks.ts} +6 -5
- package/src/tools/{reconcileTasks.ts → tasks/reconcileTasks.ts} +228 -77
- package/src/tools/tasks/retryTask.ts +69 -0
- package/src/tools/{runTaskLoop.ts → tasks/runTaskLoop.ts} +73 -46
- 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/tasks/taskStates.ts +54 -0
- package/src/tools/{waitForTask.ts → tasks/waitForTask.ts} +3 -12
- 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/workspace/listAgents.ts +75 -0
- 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 +74 -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 +94 -147
- package/ui/pages/direct-sessions.html +123 -26
- package/ui/pages/getting-started.html +43 -0
- package/ui/pages/logs.html +134 -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 +254 -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/listAgents.d.ts +0 -12
- package/dist/tools/listAgents.js +0 -42
- package/dist/tools/reconcileTasks.js +0 -352
- 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/listAgents.ts +0 -54
- 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/{waitForTask.d.ts → tasks/waitForTask.d.ts} +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/docs/CODE_WIKI.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# PatchWarden Code Wiki
|
|
2
2
|
|
|
3
|
-
> 本文档是对 PatchWarden
|
|
4
|
-
> 源码版本:**v1.
|
|
3
|
+
> 本文档是对 PatchWarden 仓库的结构化代码导览,覆盖项目整体架构、主要模块职责、关键类与函数说明、依赖关系、运行方式,以及现有缺陷分析。
|
|
4
|
+
> 源码版本:**v1.6.2** · Schema Epoch:`2026-07-19-v15` · 复核日期:2026-07-25 · License:MIT
|
|
5
5
|
|
|
6
6
|
## 目录
|
|
7
7
|
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
- [8. 项目运行方式](#8-项目运行方式)
|
|
16
16
|
- [9. 安全设计](#9-安全设计)
|
|
17
17
|
- [10. 测试与发布](#10-测试与发布)
|
|
18
|
+
- [11. 现有缺陷与改进建议](#11-现有缺陷与改进建议)
|
|
18
19
|
|
|
19
20
|
---
|
|
20
21
|
|
|
@@ -32,11 +33,13 @@ PatchWarden 是一个面向本地编程 Agent 的**安全 MCP(Model Context Pr
|
|
|
32
33
|
- 每个任务必须指定位于 `workspaceRoot` 内的 `repo_path`
|
|
33
34
|
- Agent 启动命令来自本地配置,不来自模型输入
|
|
34
35
|
- 测试命令必须精确匹配 `allowedTestCommands` 白名单
|
|
35
|
-
-
|
|
36
|
+
- 任务完成后保存结构化结果、有界且脱敏的差异、文件统计和独立验证记录
|
|
36
37
|
- 工作区外出现变化时标记为作用域违规,而非悄悄接受
|
|
37
38
|
- `.env`、Token、SSH 密钥、Cookie、凭据文件等敏感路径默认不可读
|
|
39
|
+
- v1.6.0 引入 Electron 桌面应用,支持 8 种本地 Agent 自动检测与模型发现
|
|
40
|
+
- v1.6.1 完成跨进程 mutation 锁串行化、TypeScript 边界收紧与 Windows 并发覆盖扩展
|
|
38
41
|
|
|
39
|
-
**技术栈**:TypeScript + Node.js(≥18
|
|
42
|
+
**技术栈**:TypeScript + Node.js(≥18);主仓只有一个直接运行时依赖 `@modelcontextprotocol/sdk`(`^1.29.0`),`desktop/` 子包的直接运行时依赖为 `jsonc-parser`、`smol-toml`、`yaml` 三个解析器。
|
|
40
43
|
|
|
41
44
|
---
|
|
42
45
|
|
|
@@ -58,13 +61,16 @@ ChatGPT / Codex / OpenCode / 其他 MCP 客户端
|
|
|
58
61
|
Watcher 发现任务
|
|
59
62
|
│
|
|
60
63
|
▼
|
|
61
|
-
本地 Agent(OpenCode / Codex
|
|
64
|
+
本地 Agent(OpenCode / Codex / Claude Code / Gemini / ...)
|
|
62
65
|
│
|
|
63
66
|
▼
|
|
64
67
|
result.json / diff.patch / verify.json / status.json
|
|
65
68
|
│
|
|
66
69
|
▼
|
|
67
70
|
MCP 客户端读取 safe 摘要、审计证据并人工验收
|
|
71
|
+
│
|
|
72
|
+
▼
|
|
73
|
+
Control Center Dashboard / Desktop Tray(可选)
|
|
68
74
|
```
|
|
69
75
|
|
|
70
76
|
### 2.2 三种运行角色
|
|
@@ -76,10 +82,12 @@ ChatGPT / Codex / OpenCode / 其他 MCP 客户端
|
|
|
76
82
|
| **MCP Server** | 暴露受限的 planning/task/summary/audit 工具 | `dist/index.js`(stdio)或 `dist/httpServer.js`(HTTP) |
|
|
77
83
|
| **Watcher** | 轮询 queued 任务并启动本地 Agent | `dist/runner/watch.js` |
|
|
78
84
|
| **本地 Agent** | 真正修改代码,必须在配置中预先登记 | 由 Watcher 通过 `spawn` 启动 |
|
|
85
|
+
| **Control Center**(可选) | 本地 Dashboard HTTP 服务,聚合任务/会话/lineage/证据包 | `dist/controlCenter.js`(127.0.0.1:8090) |
|
|
86
|
+
| **Desktop App**(可选) | Electron 外壳,引导配置、监督后端、托盘管理 | `desktop/src/main.ts` |
|
|
79
87
|
|
|
80
88
|
> ⚠️ "MCP 已连接"不等于"任务一定会执行"。如果 Watcher 没有运行,`create_task` 仍能保存任务,但任务会保持 `queued` 并返回 `execution_blocked: true`。
|
|
81
89
|
|
|
82
|
-
### 2.3
|
|
90
|
+
### 2.3 四种工具 Profile
|
|
83
91
|
|
|
84
92
|
| Profile | 工具数 | 用途 |
|
|
85
93
|
| --- | --- | --- |
|
|
@@ -95,23 +103,62 @@ ChatGPT / Codex / OpenCode / 其他 MCP 客户端
|
|
|
95
103
|
| **Agent 委托模式** | ChatGPT 编写计划 → 本地 Agent 执行 → PatchWarden 审计 |
|
|
96
104
|
| **Direct 模式** | ChatGPT 创建 session → 读取/搜索文件 → 应用 JSON 补丁 → 运行白名单验证 → finalize → audit |
|
|
97
105
|
|
|
106
|
+
### 2.5 v1.6.0 Desktop 架构
|
|
107
|
+
|
|
108
|
+
```text
|
|
109
|
+
┌──────────────────────────────────────────────────┐
|
|
110
|
+
│ PatchWarden Desktop (Electron) │
|
|
111
|
+
│ ┌────────────────────────────────────────────┐ │
|
|
112
|
+
│ │ main.mjs (主进程) │ │
|
|
113
|
+
│ │ ├─ agent-adapters.mjs (8 种 Agent 检测) │ │
|
|
114
|
+
│ │ ├─ model-discovery.mjs (本地模型发现) │ │
|
|
115
|
+
│ │ ├─ config-store.mjs (DPAPI / 原子写入) │ │
|
|
116
|
+
│ │ ├─ tunnel-provisioner.mjs (隧道下发) │ │
|
|
117
|
+
│ │ ├─ runtime-settings.mjs (tunnel-client) │ │
|
|
118
|
+
│ │ ├─ backend-probe.mjs (后端探测) │ │
|
|
119
|
+
│ │ └─ runtime-root.mjs (主仓定位) │ │
|
|
120
|
+
│ └────────────────────────────────────────────┘ │
|
|
121
|
+
│ │ utilityProcess.fork │
|
|
122
|
+
│ ▼ │
|
|
123
|
+
│ PatchWarden Core (dist/controlCenter.js:8090) │
|
|
124
|
+
│ + Control Center UI (ui/pages/*) │
|
|
125
|
+
└──────────────────────────────────────────────────┘
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
桌面应用是薄壳,所有业务逻辑在主仓 `dist/`,桌面只负责安装/检测/拉起/托盘/tunnel 下发。依赖极简(3 个运行时包),采用 `contextIsolation` + `sandbox` + IPC sender 校验。
|
|
129
|
+
|
|
130
|
+
### 2.6 v1.6.1 跨进程串行化
|
|
131
|
+
|
|
132
|
+
v1.6.1 把所有持久化状态变更收敛到统一的 `withFileLockSync` / `mutateLockedJsonFileSync` 抽象:
|
|
133
|
+
|
|
134
|
+
| 状态域 | 锁文件位置 | 串行化操作 |
|
|
135
|
+
| --- | --- | --- |
|
|
136
|
+
| Task status | `tasks/<id>/status.lock` | `pending → running` 领取、cancel、状态原子替换 |
|
|
137
|
+
| Watcher | `.patchwarden/watcher.lock` | 单实例排他创建 + stale PID 接管 |
|
|
138
|
+
| Goal status | `.patchwarden/goals/<id>/goal.lock` | mutation、子目标 accept/reject |
|
|
139
|
+
| Worktree | `.patchwarden/worktrees/<id>/lifecycle.lock` | create/merge/discard 仓库级锁 |
|
|
140
|
+
| Direct session | `direct-sessions/<id>/session.lock` | patch/sync/verify/finalize/audit |
|
|
141
|
+
| Assessment | `assessments/<id>/assessment.lock` | freshness 校验与读取 |
|
|
142
|
+
| Control events | `.patchwarden/control-center/events.lock` | append + trim + 原子替换 |
|
|
143
|
+
| Bounded logs | `invocation.log.lock` 等 | 跨进程有界追加 + 截断标记 |
|
|
144
|
+
|
|
145
|
+
锁文件使用 `O_EXCL` 创建 + PID 探活,孤儿锁由后续操作自动接管;所有锁内操作完成后通过原子 `.tmp-{pid}` rename 提交,保证 crash 后磁盘上要么是旧状态、要么是新状态,不会出现半写入。
|
|
146
|
+
|
|
98
147
|
---
|
|
99
148
|
|
|
100
149
|
## 3. 目录结构
|
|
101
150
|
|
|
102
151
|
```text
|
|
103
152
|
PatchWarden/
|
|
104
|
-
├── src/ # TypeScript
|
|
153
|
+
├── src/ # TypeScript 源码(主仓)
|
|
105
154
|
│ ├── index.ts # stdio MCP Server 入口
|
|
106
155
|
│ ├── httpServer.ts # HTTP MCP Server 入口(127.0.0.1 only)
|
|
107
|
-
│ ├── controlCenter.ts #
|
|
156
|
+
│ ├── controlCenter.ts # Control Center 启动入口(调用 control/server.ts)
|
|
108
157
|
│ ├── doctor.ts # 只读诊断脚本
|
|
109
158
|
│ ├── config.ts # 配置加载与校验
|
|
110
159
|
│ ├── errors.ts # PatchWardenError 错误模型
|
|
111
160
|
│ ├── logging.ts # 审计日志
|
|
112
161
|
│ ├── version.ts # 版本与 Schema Epoch
|
|
113
|
-
│ ├── taskRuntime.ts # runtime.json 状态读写
|
|
114
|
-
│ ├── taskProgress.ts # progress.md 生成
|
|
115
162
|
│ ├── watcherStatus.ts # Watcher 心跳状态
|
|
116
163
|
│ ├── smoke-test.ts # 烟雾测试入口
|
|
117
164
|
│ ├── agents/ # Agent 路由
|
|
@@ -120,6 +167,18 @@ PatchWarden/
|
|
|
120
167
|
│ │ ├── agentAssessor.ts
|
|
121
168
|
│ │ ├── assessmentStore.ts
|
|
122
169
|
│ │ └── confirmCli.ts
|
|
170
|
+
│ ├── control/ # Control Center 后端(v1.5.1 拆分)
|
|
171
|
+
│ │ ├── server.ts # HTTP 服务、路由分发、生命周期
|
|
172
|
+
│ │ ├── shared.ts # 共享配置、令牌、HTTP/文件 helper
|
|
173
|
+
│ │ ├── runtime.ts # 健康探测、stale 分类、事件时间线
|
|
174
|
+
│ │ ├── routeTable.ts # 声明式路由表
|
|
175
|
+
│ │ ├── middleware/
|
|
176
|
+
│ │ │ ├── auth.ts # control token 校验
|
|
177
|
+
│ │ │ └── static.ts # 静态文件服务
|
|
178
|
+
│ │ └── routes/ # 路由 handler(10 个领域)
|
|
179
|
+
│ │ ├── audit.ts evidence.ts lineage.ts policy.ts
|
|
180
|
+
│ │ ├── process.ts sessions.ts status.ts
|
|
181
|
+
│ │ └── taskActions.ts tasks.ts workspace.ts
|
|
123
182
|
│ ├── direct/ # Direct 直接编辑模式
|
|
124
183
|
│ │ ├── directSessionStore.ts
|
|
125
184
|
│ │ ├── directGuards.ts
|
|
@@ -127,56 +186,64 @@ PatchWarden/
|
|
|
127
186
|
│ │ ├── directAudit.ts
|
|
128
187
|
│ │ └── directVerification.ts
|
|
129
188
|
│ ├── goal/ # Goal Session 多任务编排
|
|
130
|
-
│ │ ├── goalStore.ts
|
|
131
|
-
│ │ ├──
|
|
132
|
-
│ │ ├──
|
|
133
|
-
│ │ ├──
|
|
134
|
-
│ │ ├── goalReport.ts
|
|
135
|
-
│ │ ├── handoffExport.ts
|
|
136
|
-
│ │ ├── specKitImport.ts
|
|
137
|
-
│ │ ├── subgoalSync.ts
|
|
138
|
-
│ │ ├── acceptanceEngine.ts
|
|
139
|
-
│ │ ├── acceptanceTemplate.ts
|
|
189
|
+
│ │ ├── goalStore.ts goalGraph.ts goalStatus.ts
|
|
190
|
+
│ │ ├── goalProgress.ts goalReport.ts handoffExport.ts
|
|
191
|
+
│ │ ├── specKitImport.ts subgoalSync.ts
|
|
192
|
+
│ │ ├── acceptanceEngine.ts acceptanceTemplate.ts
|
|
140
193
|
│ │ └── worktreeManager.ts
|
|
141
194
|
│ ├── policy/ # 项目级策略
|
|
142
195
|
│ │ └── projectPolicy.ts
|
|
143
196
|
│ ├── release/ # 发布门控
|
|
144
197
|
│ │ └── releaseGate.ts
|
|
145
198
|
│ ├── runner/ # 任务执行核心
|
|
146
|
-
│ │ ├── cli.ts
|
|
147
|
-
│ │ ├──
|
|
148
|
-
│ │ ├──
|
|
149
|
-
│ │
|
|
150
|
-
│ │ ├── changeCapture.ts
|
|
151
|
-
│ │ ├── postTaskCleanup.ts
|
|
152
|
-
│ │ └── simpleProcess.ts
|
|
199
|
+
│ │ ├── cli.ts runTask.ts watch.ts
|
|
200
|
+
│ │ ├── agentInvocation.ts changeCapture.ts
|
|
201
|
+
│ │ ├── postTaskCleanup.ts simpleProcess.ts processSecurity.ts
|
|
202
|
+
│ │ └── taskRuntime.ts taskProgress.ts taskStatusStore.ts
|
|
153
203
|
│ ├── security/ # 纵深防御守卫
|
|
154
|
-
│ │ ├── commandGuard.ts
|
|
155
|
-
│ │ ├──
|
|
156
|
-
│ │ ├──
|
|
157
|
-
│ │ ├──
|
|
158
|
-
│ │ ├── riskEngine.ts
|
|
159
|
-
│ │ ├── runtimeGuard.ts
|
|
160
|
-
│ │ ├── contentRedaction.ts
|
|
161
|
-
│ │ ├── toolInvocationGuard.ts
|
|
162
|
-
│ │ └── discoveryTokenStore.ts
|
|
204
|
+
│ │ ├── commandGuard.ts pathGuard.ts sensitiveGuard.ts
|
|
205
|
+
│ │ ├── planGuard.ts riskEngine.ts runtimeGuard.ts
|
|
206
|
+
│ │ ├── contentRedaction.ts toolInvocationGuard.ts
|
|
207
|
+
│ │ ├── discoveryTokenStore.ts workspaceRootGuard.ts loopbackHost.ts
|
|
163
208
|
│ ├── tools/ # MCP 工具实现
|
|
164
209
|
│ │ ├── registry.ts # 工具注册中枢
|
|
165
|
-
│ │ ├──
|
|
166
|
-
│ │ ├── toolRegistry.ts
|
|
167
|
-
│ │ ├──
|
|
168
|
-
│ │
|
|
169
|
-
│ │ ├──
|
|
170
|
-
│ │ ├──
|
|
171
|
-
│ │ ├──
|
|
172
|
-
│ │ ├──
|
|
173
|
-
│ │ ├──
|
|
174
|
-
│ │ ├──
|
|
175
|
-
│ │ ├──
|
|
176
|
-
│ │
|
|
177
|
-
│ └──
|
|
210
|
+
│ │ ├── catalog/ # Profile / Manifest / 搜索 / 统计
|
|
211
|
+
│ │ │ ├── toolCatalog.ts toolRegistry.ts toolSearch.ts toolUsageStats.ts
|
|
212
|
+
│ │ ├── definitions/ # 工具定义
|
|
213
|
+
│ │ │ └── toolDefs.ts
|
|
214
|
+
│ │ ├── diagnostics/ # 审计与健康检查
|
|
215
|
+
│ │ │ ├── auditSession.ts auditTask.ts healthCheck.ts
|
|
216
|
+
│ │ │ ├── safeStatus.ts safeViews.ts schemaDriftCheck.ts
|
|
217
|
+
│ │ ├── direct/ # Direct 工具实现
|
|
218
|
+
│ │ ├── discovery/ # 动态工具发现
|
|
219
|
+
│ │ ├── dispatch/ # 工具分派层(v1.5.1 拆分)
|
|
220
|
+
│ │ │ ├── types.ts coreDispatch.ts diagnosticDispatch.ts
|
|
221
|
+
│ │ │ ├── directDispatch.ts goalDispatch.ts releaseDispatch.ts
|
|
222
|
+
│ │ │ └── validation.ts
|
|
223
|
+
│ │ ├── goals/ # Goal 工具
|
|
224
|
+
│ │ ├── release/ # Release mode 工具
|
|
225
|
+
│ │ ├── tasks/ # 任务管理工具
|
|
226
|
+
│ │ ├── workspace/ # 工作区与 worktree 工具
|
|
227
|
+
│ │ ├── registry.ts
|
|
228
|
+
│ │ └── taskTemplates.ts
|
|
229
|
+
│ └── test/unit/ # 单元测试(45+ 测试文件)
|
|
230
|
+
├── desktop/ # Electron 桌面应用子包(v1.6.0)
|
|
231
|
+
│ ├── package.json # 独立依赖(jsonc-parser/smol-toml/yaml)
|
|
232
|
+
│ ├── src/ # 9 个 .ts 模块
|
|
233
|
+
│ │ ├── main.ts # Electron 入口、Tray、IPC、后端生命周期
|
|
234
|
+
│ │ ├── agent-adapters.ts # 8 种 Agent 适配器
|
|
235
|
+
│ │ ├── agent-detection.ts # 门面 re-export
|
|
236
|
+
│ │ ├── backend-probe.ts # /api/diagnostics 探测
|
|
237
|
+
│ │ ├── config-store.ts # 配置/DPAPI/原子写入
|
|
238
|
+
│ │ ├── model-discovery.ts # 各 Agent 配置文件模型抽取
|
|
239
|
+
│ │ ├── runtime-root.ts # 主仓定位
|
|
240
|
+
│ │ ├── runtime-settings.ts # tunnel-client 检测
|
|
241
|
+
│ │ └── tunnel-provisioner.ts # 隧道下发
|
|
242
|
+
│ ├── onboarding/ # 首次引导 UI
|
|
243
|
+
│ ├── scripts/ # stage/checksum/icon/test
|
|
244
|
+
│ └── test/ # 桌面单元测试
|
|
178
245
|
├── ui/ # Control Center 前端
|
|
179
|
-
│ ├── pages/ # dashboard/tasks/audit
|
|
246
|
+
│ ├── pages/ # dashboard/tasks/audit 等 8 个页面
|
|
180
247
|
│ ├── partials/
|
|
181
248
|
│ └── vendor/ # tailwindcss / lucide
|
|
182
249
|
├── scripts/ # 运维脚本
|
|
@@ -187,7 +254,7 @@ PatchWarden/
|
|
|
187
254
|
│ └── release/ # 发布打包
|
|
188
255
|
├── docs/ # 文档
|
|
189
256
|
├── examples/ # 配置与 Tunnel 示例
|
|
190
|
-
├── package.json
|
|
257
|
+
├── package.json # v1.6.2,单一直接运行时依赖
|
|
191
258
|
├── tsconfig.json
|
|
192
259
|
└── PatchWarden.cmd # Windows 统一控制入口
|
|
193
260
|
```
|
|
@@ -200,19 +267,19 @@ PatchWarden/
|
|
|
200
267
|
|
|
201
268
|
| 文件 | 职责 |
|
|
202
269
|
| --- | --- |
|
|
203
|
-
| [src/index.ts](../src/index.ts) | stdio MCP Server 入口,加载配置、注册工具、连接 StdioServerTransport |
|
|
270
|
+
| [src/index.ts](../src/index.ts) | stdio MCP Server 入口,加载配置、注册工具、连接 `StdioServerTransport` |
|
|
204
271
|
| [src/httpServer.ts](../src/httpServer.ts) | HTTP MCP Server,绑定 `127.0.0.1:7331`,每请求独立 MCP 实例,支持 owner token 与 `/admin/tasks/:id/accept` 验收端点 |
|
|
205
|
-
| [src/controlCenter.ts](../src/controlCenter.ts) | 本地 Dashboard
|
|
272
|
+
| [src/controlCenter.ts](../src/controlCenter.ts) | 本地 Dashboard 启动入口,仅 `import { startServer } from "./control/server.js"` |
|
|
206
273
|
| [src/doctor.ts](../src/doctor.ts) | 只读诊断脚本,检查 15+ 项:Node/npm/Git 版本、配置、工作区、路径保护、敏感文件、Agent 命令、工具 Manifest、HTTP 端口、Watcher 目录、构建产物 |
|
|
207
274
|
|
|
208
275
|
### 4.2 配置与基础
|
|
209
276
|
|
|
210
277
|
| 文件 | 职责 |
|
|
211
278
|
| --- | --- |
|
|
212
|
-
| [src/config.ts](../src/config.ts) | 加载并校验 `patchwarden.config.json`,提供 `loadConfig`/`getConfig`/`getTasksDir`/`getPlansDir` 等路径解析;严格校验 `workspaceRoot`、`agents`、`allowedTestCommands`、`watcherStaleSeconds`、`toolProfile`
|
|
279
|
+
| [src/config.ts](../src/config.ts) | 加载并校验 `patchwarden.config.json`,提供 `loadConfig`/`getConfig`/`getTasksDir`/`getPlansDir`/`resolveWorkspaceRoot`/`getRepoAllowedTestCommands`/`getRepoDirectAllowedCommands` 等路径解析;严格校验 `workspaceRoot`、`agents`、`allowedTestCommands`、`watcherStaleSeconds`、`toolProfile`、`tunnelProxy`、Direct 数值范围等字段;含 `normalizeRepoKey` 与 `comparablePath` 工具 |
|
|
213
280
|
| [src/errors.ts](../src/errors.ts) | 定义 `PatchWardenError`(含 `reason`/`suggestion`/`blocked`/`details`)与 `errorPayload` 序列化 |
|
|
214
|
-
| [src/version.ts](../src/version.ts) | 导出 `PATCHWARDEN_VERSION = "1.
|
|
215
|
-
| [src/logging.ts](../src/logging.ts) | `Logger` 类输出 stderr JSON 日志,记录 `audit`/`info`/`warn`/`error`;`logToolInvocation` 仅写参数 digest
|
|
281
|
+
| [src/version.ts](../src/version.ts) | 导出 `PATCHWARDEN_VERSION = "1.6.2"` 与 `TOOL_SCHEMA_EPOCH = "2026-07-19-v15"` |
|
|
282
|
+
| [src/logging.ts](../src/logging.ts) | `Logger` 类输出 stderr JSON 日志,记录 `audit`/`info`/`warn`/`error`;`logToolInvocation` 仅写参数 digest,不写原参数,并通过跨进程锁有界追加到 5 MiB;`installGlobalHandlers` 捕获未处理异常但不吞错 |
|
|
216
283
|
|
|
217
284
|
### 4.3 安全模块(src/security/)
|
|
218
285
|
|
|
@@ -222,13 +289,15 @@ PatchWarden 的纵深防御核心,所有写操作前都会经过这些守卫
|
|
|
222
289
|
| --- | --- |
|
|
223
290
|
| [commandGuard.ts](../src/security/commandGuard.ts) | 命令白名单守卫:`guardAgentCommand` 校验 agent 已配置且命令无 shell 元字符;`guardTestCommand` 精确匹配测试白名单;`guardDirectCommand` 校验 Direct 白名单;`sanitizePromptArg` 清洗控制字符 |
|
|
224
291
|
| [pathGuard.ts](../src/security/pathGuard.ts) | 路径横移守卫:`guardPath`/`guardReadPath`/`guardWorkspacePath` 确保路径解析后都在 `workspaceRoot` 内,用 `realpath` 防符号链接逃逸,Windows 下做盘符一致性检查 |
|
|
225
|
-
| [sensitiveGuard.ts](../src/security/sensitiveGuard.ts) | 敏感文件守卫:`isSensitivePath` 匹配 `.env`/SSH 私钥/`credentials`/`.npmrc`/`cookies`/`.kube/config`
|
|
292
|
+
| [sensitiveGuard.ts](../src/security/sensitiveGuard.ts) | 敏感文件守卫:`isSensitivePath` 匹配 `.env`/SSH 私钥/`credentials`/`.npmrc`/`cookies`/`.kube/config` 等;没有 `.patchwarden/` 前缀豁免,敏感名称在任意目录深度都阻断;NTFS ADS、NUL 字节也拒绝 |
|
|
226
293
|
| [planGuard.ts](../src/security/planGuard.ts) | 计划内容守卫:`guardPlanContent` 扫描 plan 文本,拦截"读取密钥/破坏性删除/植入后门"等危险指令,支持中英文否定语境检测 |
|
|
227
294
|
| [riskEngine.ts](../src/security/riskEngine.ts) | 综合风险评估:`assessRisk` 串联各 guard 输出 `risk_level`(low/medium/high)+ `decision`(allow/needs_confirm/blocked)+ `reason_codes`;`collectRiskHints` 仅做关键词提示,不影响决策 |
|
|
228
|
-
| [runtimeGuard.ts](../src/security/runtimeGuard.ts) | 阻止任务把 `repo_path` 指向运行中的 PatchWarden 自身目录(`dist`/`src`/`scripts`/`release
|
|
229
|
-
| [contentRedaction.ts](../src/security/contentRedaction.ts) | 输出脱敏:`redactSensitiveContent`/`redactSensitiveValue` 按正则替换私钥、bearer token、npm token
|
|
295
|
+
| [runtimeGuard.ts](../src/security/runtimeGuard.ts) | 阻止任务把 `repo_path` 指向运行中的 PatchWarden 自身目录(`dist`/`src`/`scripts`/`release`);使用 `isSamePath`/`isPathChildOf` 做平台感知比较 |
|
|
296
|
+
| [contentRedaction.ts](../src/security/contentRedaction.ts) | 输出脱敏:`redactSensitiveContent`/`redactSensitiveValue` 按正则替换私钥、bearer token、npm token、GitLab `glpat-`、Slack `xox*`、AWS access key、Google API key、JWT 与凭据赋值 |
|
|
297
|
+
| [loopbackHost.ts](../src/security/loopbackHost.ts) | 回环 HTTP Host 白名单:仅接受当前端口的 `127.0.0.1`/`localhost`,供 HTTP MCP 与 Control Center 共同阻断 DNS rebinding |
|
|
230
298
|
| [toolInvocationGuard.ts](../src/security/toolInvocationGuard.ts) | `invoke_discovered_tool` 调用前 8 项校验:token 匹配、profile 允许、风险等级、敏感路径、assessment 必需、命令元字符、release 确认、credential 拒绝 |
|
|
231
|
-
| [discoveryTokenStore.ts](../src/security/discoveryTokenStore.ts) | 服务端 discovery token 存储:`issueToken`/`consumeToken`(单次使用)/`peekToken`/`revokeToken`,纯内存、10 分钟 TTL
|
|
299
|
+
| [discoveryTokenStore.ts](../src/security/discoveryTokenStore.ts) | 服务端 discovery token 存储:`issueToken`/`consumeToken`(单次使用)/`peekToken`/`revokeToken`,纯内存、10 分钟 TTL、活动上限 1024 |
|
|
300
|
+
| [workspaceRootGuard.ts](../src/security/workspaceRootGuard.ts) | `validateWorkspaceRoot` 校验工作区根目录合法性(被桌面应用动态 import 复用),拒绝盘符根、用户主目录、Desktop、Downloads、Documents |
|
|
232
301
|
|
|
233
302
|
### 4.4 Runner 模块(src/runner/)
|
|
234
303
|
|
|
@@ -237,64 +306,161 @@ PatchWarden 的纵深防御核心,所有写操作前都会经过这些守卫
|
|
|
237
306
|
| 文件 | 职责 |
|
|
238
307
|
| --- | --- |
|
|
239
308
|
| [cli.ts](../src/runner/cli.ts) | Runner 子进程入口,从 argv 或 `PATCHWARDEN_TASK_ID` 读取 taskId 并调用 `runTask` |
|
|
240
|
-
| [runTask.ts](../src/runner/runTask.ts) | **执行主循环**:`runTask(taskId)` 编排 preparing → executing_agent → running_tests → collecting_artifacts → done/failed;管理心跳(2s)、超时、cancel/kill;产出 16+ artifact 文件;区分 `failed_scope_violation`/`failed_policy_violation` |
|
|
241
|
-
| [watch.ts](../src/runner/watch.ts) | 常驻 watcher,每 4 秒轮询 `pending` 任务,执行 pre-flight 安全校验后调用 `runTask`;原子写心跳文件;通过 env 注入 `WATCHER_INSTANCE_ID`/`WATCHER_LAUNCHER_PID`
|
|
242
|
-
| [agentInvocation.ts](../src/runner/agentInvocation.ts) | 构建 agent 调用参数与 prompt:`buildAgentInvocation`/`buildExecutionPrompt`/`buildAssessmentPrompt`;占位符 `{repo}`/`{prompt}`/`{prompt_file}`
|
|
243
|
-
| [changeCapture.ts](../src/runner/changeCapture.ts) | 仓库快照与变更证据:`captureRepoSnapshot` 并行跑 5 个 git 命令;`buildChangeArtifacts`
|
|
244
|
-
| [postTaskCleanup.ts](../src/runner/postTaskCleanup.ts) | 任务后清理:仅删除未被 git 跟踪且被忽略的临时产物(`__pycache__`/`dist`/`*.pyc
|
|
245
|
-
| [simpleProcess.ts](../src/runner/simpleProcess.ts) | 轻量进程执行器:`runSimpleProcess`/`runSimpleProcessSync
|
|
309
|
+
| [runTask.ts](../src/runner/runTask.ts) | **执行主循环**:`runTask(taskId)` 编排 preparing → executing_agent → running_tests → collecting_artifacts → done/failed;管理心跳(2s)、超时、cancel/kill;产出 16+ artifact 文件;区分 `failed_scope_violation`/`failed_policy_violation`;通过 `claimPendingTask` 实现单次领取 |
|
|
310
|
+
| [watch.ts](../src/runner/watch.ts) | 常驻 watcher,每 4 秒轮询 `pending` 任务,执行 pre-flight 安全校验后调用 `runTask`;原子写心跳文件;通过 env 注入 `WATCHER_INSTANCE_ID`/`WATCHER_LAUNCHER_PID` 支持所有权判定;单实例排他锁 + stale PID 接管 |
|
|
311
|
+
| [agentInvocation.ts](../src/runner/agentInvocation.ts) | 构建 agent 调用参数与 prompt:`buildAgentInvocation`/`buildExecutionPrompt`/`buildAssessmentPrompt`;占位符 `{repo}`/`{prompt}`/`{prompt_file}` 替换;Windows npm shim 解析为原生 exe 或已验证的 package `bin`,不启用 shell;Agent 必须显式注册,子进程只接收该 Agent 的 `envAllowlist` 环境变量 |
|
|
312
|
+
| [changeCapture.ts](../src/runner/changeCapture.ts) | 仓库快照与变更证据:`captureRepoSnapshot` 并行跑 5 个 git 命令;`buildChangeArtifacts` 比对快照生成最多 20 MiB、写前脱敏的 diff;凭据型内容记录 redaction 元数据;`extractExternalDirtyFiles` 建立外部脏文件基线;`buildArtifactManifest` 生成带 sha256 的产物清单;Windows 下使用 `nullDevice` (`NUL` / `/dev/null`) 与小写路径键 |
|
|
313
|
+
| [postTaskCleanup.ts](../src/runner/postTaskCleanup.ts) | 任务后清理:仅删除未被 git 跟踪且被忽略的临时产物(`__pycache__`/`dist`/`*.pyc`),三道闸门保护受控文件;使用 `fs.rmSync` 有界重试,Windows 失败时只清除只读属性后重试,不再启动 `cmd.exe /c rmdir` |
|
|
314
|
+
| [simpleProcess.ts](../src/runner/simpleProcess.ts) | 轻量进程执行器:`runSimpleProcess`/`runSimpleProcessSync`,使用最小子进程环境、受信 PATH 可执行文件绑定、无 shell 的 npm/npx/pnpm 解析,以及有界脱敏日志捕获;异步完成以 child `close` 为准 |
|
|
315
|
+
| [processSecurity.ts](../src/runner/processSecurity.ts) | `SecureProcessLogCapture` 有界收集 + 写前脱敏 + `appendFileSync`;`buildChildEnvironment` 构造最小环境;`resolveTrustedExecutable` / `resolvePackageManagerInvocation` 受信解析 |
|
|
316
|
+
| [taskStatusStore.ts](../src/runner/taskStatusStore.ts) | 任务状态原子存储:`claimPendingTask` 同目录硬链接锁 + 原子 JSON 替换实现单次领取;`mutateTaskStatus` 共享状态锁,与 `cancelTask` 串行化 |
|
|
317
|
+
| [taskRuntime.ts](../src/runner/taskRuntime.ts) | `runtime.json` 浅合并读写,含 PID 重用与孤儿任务检测字段 |
|
|
318
|
+
| [taskProgress.ts](../src/runner/taskProgress.ts) | `progress.md` 生成,6 阶段标记(`[x]`/`[>]`/`[ ]`) |
|
|
246
319
|
|
|
247
320
|
### 4.5 Tools 模块(src/tools/)
|
|
248
321
|
|
|
249
|
-
MCP
|
|
322
|
+
MCP 工具实现与注册中枢,共 49 个工具文件。
|
|
250
323
|
|
|
251
324
|
| 文件 | 职责 |
|
|
252
325
|
| --- | --- |
|
|
253
|
-
| [registry.ts](../src/tools/registry.ts) |
|
|
254
|
-
| [toolCatalog.ts](../src/tools/toolCatalog.ts) | Profile 与 Manifest:`resolveToolProfile`/`selectToolsForProfile` 按 profile 过滤工具;`buildToolCatalogSnapshot` 计算 `tool_manifest_sha256`
|
|
255
|
-
| [toolRegistry.ts](../src/tools/toolRegistry.ts) |
|
|
256
|
-
| [toolSearch.ts](../src/tools/toolSearch.ts) |
|
|
257
|
-
| [
|
|
258
|
-
| [
|
|
259
|
-
| [
|
|
260
|
-
| [
|
|
261
|
-
| [
|
|
262
|
-
| [
|
|
263
|
-
| [
|
|
264
|
-
| [
|
|
265
|
-
| [
|
|
266
|
-
| [
|
|
267
|
-
|
|
268
|
-
|
|
326
|
+
| [registry.ts](../src/tools/registry.ts) | **工具注册中枢**(约 170 行):`registerTools` 绑定 `ListToolsRequestSchema`/`CallToolRequestSchema`,`handleToolCall` 分派到领域 handler map;工具定义由 `definitions/toolDefs.ts` 提供;启动时校验"每个已注册工具必有 handler";冻结一次连接的 active tool list 防止 list/call 漂移 |
|
|
327
|
+
| [catalog/toolCatalog.ts](../src/tools/catalog/toolCatalog.ts) | Profile 与 Manifest:`resolveToolProfile`/`selectToolsForProfile` 按 profile 过滤工具;`buildToolCatalogSnapshot` 计算 `tool_manifest_sha256` 用于漂移检测;导出 `CHATGPT_CORE_TOOL_NAMES`(26)/`CHATGPT_DIRECT_TOOL_NAMES`(14)/`CHATGPT_SEARCH_TOOL_NAMES`(5);`chatgpt_direct` 在未启用时降级为仅 `health_check` |
|
|
328
|
+
| [catalog/toolRegistry.ts](../src/tools/catalog/toolRegistry.ts) | 工具元数据:`buildToolRegistry` 为每个工具补全 risk/modes/tags/aliases/schema_digest;稳定 JSON 实现来自 `src/utils/stableJson.ts` |
|
|
329
|
+
| [catalog/toolSearch.ts](../src/tools/catalog/toolSearch.ts) | SafeToolSearch 搜索引擎(v0.9.0):混合排序、意图分类、风险调整与历史成功率反馈 |
|
|
330
|
+
| [catalog/toolUsageStats.ts](../src/tools/catalog/toolUsageStats.ts) | 从 `invocation.log` 聚合工具调用统计 |
|
|
331
|
+
| [diagnostics/schemaDriftCheck.ts](../src/tools/diagnostics/schemaDriftCheck.ts) | Schema 漂移自检,供 doctor 消费 |
|
|
332
|
+
| [tasks/createTask.ts](../src/tools/tasks/createTask.ts) | `createTask` 任务创建:支持 saved/inline/template 来源及 `assess_only` 风险预评估;定义 `TaskStatus` 与 `TaskPhase` 类型 |
|
|
333
|
+
| [tasks/runTaskLoop.ts](../src/tools/tasks/runTaskLoop.ts) | **安全编排入口**:组合 create_task → wait_for_task → safe summaries → audit_task,支持 worktree 隔离和 Direct 验证 |
|
|
334
|
+
| [tasks/taskLineage.ts](../src/tools/tasks/taskLineage.ts) | 链路记录,写入 `.patchwarden/lineages/<lineage_id>/` |
|
|
335
|
+
| [tasks/evidencePack.ts](../src/tools/tasks/evidencePack.ts) | 证据包导出,不含 stdout/stderr、完整 diff 或敏感内容 |
|
|
336
|
+
| [diagnostics/auditTask.ts](../src/tools/diagnostics/auditTask.ts) | 独立审计,区分 confirmed failures、possible false positives 与 manual verification;文档扫描最多 200 个 Markdown、4 MiB 总内容,证据读取有界且超限产生 warning |
|
|
337
|
+
| [diagnostics/safeViews.ts](../src/tools/diagnostics/safeViews.ts) | `safe_*` 系列有界摘要;其中 audit/finalize 包装器会写审计或会话状态,风险元数据为 `workspace_write` |
|
|
338
|
+
| [diagnostics/healthCheck.ts](../src/tools/diagnostics/healthCheck.ts) | 返回 MCP catalog、watcher、workspace 与 agents 状态 |
|
|
339
|
+
| [tasks/waitForTask.ts](../src/tools/tasks/waitForTask.ts) | 长轮询任务,返回 continuation 或终态 acceptance 证据 |
|
|
340
|
+
| [workspace/androidDoctor.ts](../src/tools/workspace/androidDoctor.ts) | 只读诊断 Android 构建环境 |
|
|
341
|
+
| [release/releaseMode.ts](../src/tools/release/releaseMode.ts) | 发布模式四件套与项目策略入口 |
|
|
342
|
+
| [discovery/explainTool.ts](../src/tools/discovery/explainTool.ts) | 展开单个工具详情并做 schema drift 检测 |
|
|
343
|
+
| [workspace/syncFile.ts](../src/tools/workspace/syncFile.ts) | Direct 会话仓库内复制文件,统一校验会话状态、真实路径、UTF-8/大小、敏感路径/内容与 sha256,并在写入前复核源/目标未变化 |
|
|
344
|
+
| [goals/goalSubgoalTask.ts](../src/tools/goals/goalSubgoalTask.ts) | 在 Goal mutation lock 内创建 subgoal 并关联新任务;以 Goal 保存的 `repo_path` 为权威,拒绝调用方仓库不一致,隔离 worktree 也从该仓库创建 |
|
|
345
|
+
| [workspace/mergeWorktree.ts](../src/tools/workspace/mergeWorktree.ts) | 合并隔离 worktree 的 branch 回主工作区 |
|
|
346
|
+
| [workspace/discardWorktree.ts](../src/tools/workspace/discardWorktree.ts) | 丢弃隔离 worktree 并归档状态 |
|
|
347
|
+
| [tasks/runVerification.ts](../src/tools/tasks/runVerification.ts) | Direct 会话跑单条白名单验证命令 |
|
|
348
|
+
| [direct/runDirectVerificationBundle.ts](../src/tools/direct/runDirectVerificationBundle.ts) | Direct 会话批量跑多个白名单验证命令并返回有界状态 |
|
|
349
|
+
| 其他工具 | `applyPatch.ts`/`searchWorkspace.ts`/`readWorkspaceFile.ts`/`savePlan.ts`/`getPlan.ts`/`listAgents.ts`/`listTasks.ts`/`listWorkspace.ts`/`cancelTask.ts`/`killTask.ts`/`retryTask.ts`/`reconcileTasks.ts`/`recommendAgentForTask.ts`/`diagnoseTask.ts`/`getTaskStatus.ts`/`getTaskSummary.ts`/`getTaskProgress.ts`/`getTaskStdoutTail.ts`/`getTaskFile.ts`/`taskOutputs.ts`/`taskTemplates.ts`/`auditSession.ts`/`createDirectSession.ts`/`finalizeDirectSession.ts`/`discoverTools.ts`/`invokeDiscoveredTool.ts`/`safeStatus.ts`/`checkReleaseGate.ts` 等 |
|
|
350
|
+
|
|
351
|
+
### 4.6 Dispatch 子模块(src/tools/dispatch/)
|
|
352
|
+
|
|
353
|
+
v1.5.1 引入的按业务领域分组的工具分派层,将原先 `registry.ts` 中庞大的 switch 拆分为 5 个 handler map。v1.6.1 加入 `validation.ts` 提供 `unknown` 输入的类型守卫辅助。
|
|
354
|
+
|
|
355
|
+
| 文件 | 职责 |
|
|
356
|
+
| --- | --- |
|
|
357
|
+
| [types.ts](../src/tools/dispatch/types.ts) | 定义 `ToolHandler`、`ToolHandlerMap` 类型与 `toResult` 辅助函数(将任意数据 JSON 序列化为 MCP `CallToolResult` 信封) |
|
|
358
|
+
| [validation.ts](../src/tools/dispatch/validation.ts) | v1.6.1 新增:从 `unknown` 提取 string/number/boolean/array 的类型守卫;dispatch 层用它取代 `as any` 断言 |
|
|
359
|
+
| [coreDispatch.ts](../src/tools/dispatch/coreDispatch.ts) | 核心任务管理 handler(30+ 工具):`save_plan`/`get_plan`/`create_task`/`run_task_loop`/`wait_for_task`/`audit_task`/`safe_*`/`list_*`/`cancel_task`/`kill_task`/`retry_task`/`reconcile_tasks`/`check_release_gate`/`health_check` 等;导出条件注册的 `runTaskHandler` |
|
|
360
|
+
| [diagnosticDispatch.ts](../src/tools/dispatch/diagnosticDispatch.ts) | 诊断/发现类 handler:`discover_tools`/`explain_tool`/`invoke_discovered_tool`;后者通过 `dispatch` 回调把请求转回 `handleToolCall` 实现"发现 → 调用"闭环 |
|
|
361
|
+
| [directDispatch.ts](../src/tools/dispatch/directDispatch.ts) | Direct session handler(9 个):所有 handler 前置 `guardDirectProfileEnabled()` 守卫;`create_direct_session`/`search_workspace`/`apply_patch`/`run_verification`/`run_direct_verification_bundle`/`finalize_direct_session`/`audit_session`/`sync_file`/`safe_direct_*` |
|
|
362
|
+
| [goalDispatch.ts](../src/tools/dispatch/goalDispatch.ts) | Goal Session handler(12 个):`create_goal`/`list_goals`/`read_goal`/`create_subgoal_task`/`accept_subgoal`/`reject_subgoal`/`suggest_next_subgoal`/`summarize_goal_progress`/`export_handoff`/`export_goal_report`/`import_speckit_tasks`/`merge_worktree`/`discard_worktree` |
|
|
363
|
+
| [releaseDispatch.ts](../src/tools/dispatch/releaseDispatch.ts) | Release mode handler(4 个):`release_check`/`release_prepare`/`release_verify`/`release_cleanup` |
|
|
364
|
+
|
|
365
|
+
**集成方式**:`registry.ts` 的 `buildDispatchMap()` 通过对象 spread 合并 5 个 handler map,并在 `enableRunTaskTool===true` 时条件加入 `runTaskHandler`。`registerTools` 启动时校验"每个已注册工具必有 handler",但反向不校验。`handleToolCallInternal` 通过 `dispatchMap[name]` 查表分派。
|
|
366
|
+
|
|
367
|
+
### 4.7 Goal 模块(src/goal/)
|
|
269
368
|
|
|
270
369
|
v0.8.0 引入的多任务编排层。
|
|
271
370
|
|
|
272
371
|
| 文件 | 职责 |
|
|
273
372
|
| --- | --- |
|
|
274
|
-
| [goalStore.ts](../src/goal/goalStore.ts) | Goal Session 目录 CRUD
|
|
373
|
+
| [goalStore.ts](../src/goal/goalStore.ts) | Goal Session 目录 CRUD;同步/异步状态变更共享跨进程 mutation lock 并原子替换,非空 Goal 的全部子目标 accepted 后自动转为 `completed`;目录结构 `.patchwarden/goals/{goal_id}/` 含 `GOAL.md`/`GOALS.md`/`goal_status.json`/`tasks/`/`artifacts/` |
|
|
275
374
|
| [goalGraph.ts](../src/goal/goalGraph.ts) | 依赖图:`suggestNextSubgoal` 返回依赖已满足的下一个子目标 |
|
|
276
375
|
| [goalStatus.ts](../src/goal/goalStatus.ts) | `GoalStatus`/`Subgoal` 类型与 `createInitialGoalStatus` |
|
|
277
376
|
| [goalProgress.ts](../src/goal/goalProgress.ts) | `acceptSubgoal`/`rejectSubgoal`/`summarizeGoalProgress` |
|
|
278
|
-
| [goalReport.ts](../src/goal/goalReport.ts) | `exportGoalReport`
|
|
377
|
+
| [goalReport.ts](../src/goal/goalReport.ts) | `exportGoalReport` 导出结构化最终报告(v1.5.1) |
|
|
279
378
|
| [handoffExport.ts](../src/goal/handoffExport.ts) | `exportHandoff` 导出 `handoff.md` 用于会话交接 |
|
|
280
|
-
| [specKitImport.ts](../src/goal/specKitImport.ts) | `parseSpecKitJson`/`importSpecKitTasks` 导入 Spec Kit
|
|
379
|
+
| [specKitImport.ts](../src/goal/specKitImport.ts) | `parseSpecKitJson`/`importSpecKitTasks` 导入 Spec Kit 任务(v1.5.1) |
|
|
281
380
|
| [subgoalSync.ts](../src/goal/subgoalSync.ts) | 子目标与任务关联同步 |
|
|
282
381
|
| [acceptanceEngine.ts](../src/goal/acceptanceEngine.ts) | 验收引擎 |
|
|
283
|
-
| [
|
|
382
|
+
| [acceptanceTemplate.ts](../src/goal/acceptanceTemplate.ts) | 验收模板 |
|
|
383
|
+
| [worktreeManager.ts](../src/goal/worktreeManager.ts) | `createWorktree`/`mergeWorktree`/`discardWorktree` 管理 git worktree 隔离;三类仓库变更共享跨进程 repository lifecycle lock |
|
|
284
384
|
|
|
285
|
-
### 4.
|
|
385
|
+
### 4.8 Direct 模块(src/direct/)
|
|
286
386
|
|
|
287
|
-
v0.6.0 引入的 ChatGPT 直接编辑模式。
|
|
387
|
+
v0.6.0 引入的 ChatGPT 直接编辑模式。v1.6.1 把 patch/sync/verify/finalize/audit 全部纳入同一 session mutation lock 串行化。
|
|
288
388
|
|
|
289
389
|
| 文件 | 职责 |
|
|
290
390
|
| --- | --- |
|
|
291
|
-
| [directSessionStore.ts](../src/direct/directSessionStore.ts) | Direct Session CRUD
|
|
292
|
-
| [directGuards.ts](../src/direct/directGuards.ts) | Direct 守卫:`guardDirectSessionActive`/`guardDirectSessionFinalized`/`guardDirectPath`/`guardDirectReadPath`/`guardDirectWritePath`/`guardDirectPatchSize`/`isBinaryFile
|
|
293
|
-
| [directPatch.ts](../src/direct/directPatch.ts) | JSON patch 应用:支持 `replace_exact`/`insert_before`/`insert_after`/`replace_whole_file
|
|
294
|
-
| [directAudit.ts](../src/direct/directAudit.ts) |
|
|
391
|
+
| [directSessionStore.ts](../src/direct/directSessionStore.ts) | Direct Session CRUD:原子创建/更新记录,锁内追加 operations/verification_runs;workspace mutation 锁串行化 patch/sync/verify/finalize/audit;审计后持久化 `audited=true` |
|
|
392
|
+
| [directGuards.ts](../src/direct/directGuards.ts) | Direct 守卫:`guardDirectSessionActive`/`guardDirectSessionFinalized`/`guardDirectPath`/`guardDirectReadPath`/`guardDirectWritePath`/`guardDirectPatchSize`/`isBinaryFile`;拒绝 junction/symlink 与 NTFS ADS |
|
|
393
|
+
| [directPatch.ts](../src/direct/directPatch.ts) | JSON patch 应用:支持 `replace_exact`/`insert_before`/`insert_after`/`replace_whole_file`;同一次读取校验 `expected_sha256`、UTF-8、文件大小和敏感内容,目标同目录写随机临时文件 + `renameSync` 原子替换并保留原权限;失败时清理临时文件 |
|
|
394
|
+
| [directAudit.ts](../src/direct/directAudit.ts) | 17 项确定性审计,包含 diff 凭据型内容检查,返回 pass/warn/fail 并原子写入审计证据 |
|
|
295
395
|
| [directVerification.ts](../src/direct/directVerification.ts) | 白名单验证命令执行 |
|
|
296
396
|
|
|
297
|
-
### 4.
|
|
397
|
+
### 4.9 Control 模块(src/control/)
|
|
398
|
+
|
|
399
|
+
v1.5.1 将原 `controlCenter.ts` 拆分为聚焦的路由/中间件/运行时/共享模块,保持入口和 HTTP 行为不变。v1.6.1 加入 `routeTable.ts` 声明式路由表。
|
|
400
|
+
|
|
401
|
+
```text
|
|
402
|
+
shared.ts (配置 bootstrap / 路径常量 / controlToken / HTTP helper)
|
|
403
|
+
├─ runtime.ts (健康探测 / stale 分类 / 事件时间线 / 隐藏 ID)
|
|
404
|
+
├─ routeTable.ts (声明式路由表)
|
|
405
|
+
├─ middleware/
|
|
406
|
+
│ ├─ auth.ts (control token 校验 + 回环 Host)
|
|
407
|
+
│ └─ static.ts (ui/ 静态文件服务)
|
|
408
|
+
└─ routes/
|
|
409
|
+
├─ 只读: audit / evidence / lineage / policy / sessions / status / tasks / workspace
|
|
410
|
+
└─ 变更: process / taskActions
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
| 文件 | 职责 |
|
|
414
|
+
| --- | --- |
|
|
415
|
+
| [server.ts](../src/control/server.ts) | HTTP 服务创建、回环 Host/DNS rebinding 前置校验、防嵌入响应头(CSP `frame-ancestors 'none'` + `X-Frame-Options: DENY`)、`handleRequest` 路由分发、POST control-token 校验、bootstrap/shutdown 生命周期 |
|
|
416
|
+
| [shared.ts](../src/control/shared.ts) | 共享基础设施:容错配置加载、路径常量、内存 `controlToken = randomUUID()`、端口解析、`sendJson`/`readBody`/`readJsonFileSafe`/`readJsonFileSafeUnder`(带 realpath 校验)/`readFileTail`/`findLatestLog`/`isPathInside` |
|
|
417
|
+
| [runtime.ts](../src/control/runtime.ts) | `probeHealthStatus`/`probeRuntimeHealth` 健康探测;`classifyStaleTask`/`augmentTaskWithStale` 四条 stale 规则;`writeStatusFile`/`recordEvent`/`readEvents`(在跨进程文件锁内 append + trim + 原子替换);`isValidTaskId`/`isValidDirectSessionId` 校验;`readHiddenStaleIds`/`readHiddenDirectSessionIds` 隐藏 ID |
|
|
418
|
+
| [routeTable.ts](../src/control/routeTable.ts) | 声明式路由表:按 method + pattern(声明顺序优先)匹配,specific pattern 优先于 generic `:id` |
|
|
419
|
+
| [middleware/auth.ts](../src/control/middleware/auth.ts) | `checkControlToken` 校验 `x-patchwarden-control-token`,使用 `crypto.timingSafeEqual` 并先校验 Buffer 长度;复用 `security/loopbackHost.ts` 拒绝非回环 Host |
|
|
420
|
+
| [middleware/static.ts](../src/control/middleware/static.ts) | `serveStatic`/`serveFavicon`,三层路径遍历防护(NUL 拒绝、`..` 段拒绝、`relative()` 词法校验) |
|
|
421
|
+
| [routes/audit.ts](../src/control/routes/audit.ts) | `handleLogs`(core/direct/watcher/control-center 四类日志,经 `redactSensitiveContent` 脱敏);`handleAudit`(聚合 audit.json 限 50 条);`handleWarnings`(7 类警告桶) |
|
|
422
|
+
| [routes/evidence.ts](../src/control/routes/evidence.ts) | 证据包列表/详情/导出(POST) |
|
|
423
|
+
| [routes/lineage.ts](../src/control/routes/lineage.ts) | lineage 列表/详情(限 50,`toSafeTaskLineage` 投影) |
|
|
424
|
+
| [routes/policy.ts](../src/control/routes/policy.ts) | 项目策略 + release 就绪状态(只读,`remote_write_performed` 始终 false) |
|
|
425
|
+
| [routes/process.ts](../src/control/routes/process.ts) | 进程生命周期代理:start/stop/restart core/direct/all;preflight + spawn + waitForStartup;`classifySupervisorFailure` 8 类失败归类 |
|
|
426
|
+
| [routes/sessions.ts](../src/control/routes/sessions.ts) | Direct session 列表/详情/安全摘要/finalize/audit/hide;统一调用 `isValidDirectSessionId` 白名单校验 |
|
|
427
|
+
| [routes/status.ts](../src/control/routes/status.ts) | 主轮询端点 `/api/status`:10 路 Promise.all 并发探测 + 6 类建议;`/api/control-center-status`/`/api/events`/`/api/tunnel-ui-url`/`/api/diagnostics` |
|
|
428
|
+
| [routes/taskActions.ts](../src/control/routes/taskActions.ts) | 变更性任务操作:reconcile/audit_task/open-task-folder/hide-stale |
|
|
429
|
+
| [routes/tasks.ts](../src/control/routes/tasks.ts) | 只读任务查询:列表过滤/stale 任务/任务详情/safe 视图;统一调用 `reconstructTaskEntry` helper |
|
|
430
|
+
| [routes/workspace.ts](../src/control/routes/workspace.ts) | 工作区列表、repo 列表(含 package.json 元数据)、单 repo `git status --short`;只拒绝值恰为 `..` 的路径段 |
|
|
431
|
+
|
|
432
|
+
### 4.10 Desktop 子包(desktop/)
|
|
433
|
+
|
|
434
|
+
v1.6.0 引入的 Electron 桌面应用,作为 Control Center 的 Windows GUI 外壳。v1.6.1 把 IPC、JSON、model-discovery、tunnel-process 数据全部从 `unknown` 校验。
|
|
435
|
+
|
|
436
|
+
| 文件 | 职责 |
|
|
437
|
+
| --- | --- |
|
|
438
|
+
| [desktop/src/main.ts](../desktop/src/main.ts) | 入口:单实例锁、BrowserWindow(1280x800,`contextIsolation`/`sandbox`/`nodeIntegration:false`)、Tray、IPC 注册、后端生命周期(`utilityProcess.fork` 拉起主仓 `dist/controlCenter.js`);模式机 `starting/setup/setup-check/ready/blocked`;`allowedSender` 限制 IPC 来源 |
|
|
439
|
+
| [desktop/src/agent-adapters.ts](../desktop/src/agent-adapters.ts) | `AGENT_ADAPTERS` 8 个适配器(codex/opencode/claude/gemini/copilot/qwen/kimi/aider);`detectAgents()` 用 `where.exe`/`which` 查找;`selectAgentLaunch()` 安全选择原生 exe(Win 跳 `WindowsApps`、强制 `.exe/.com`)或验证 npm 包 bin 入口;`refreshAgentModels()` 跑 `refreshArgs` 过滤模型 token;`validateModelId()` |
|
|
440
|
+
| [desktop/src/agent-detection.ts](../desktop/src/agent-detection.ts) | 纯 re-export 门面,把 `selectAgentLaunch` 以 `selectAgentExecutable` 别名导出 |
|
|
441
|
+
| [desktop/src/backend-probe.ts](../desktop/src/backend-probe.ts) | `probeControlCenter()` GET `/api/diagnostics`,返回 `kind: patchwarden/foreign/absent/mismatched_patchwarden`;`configIdentity()` = 规范化路径 sha256;`mayStopBackend()` 校验 owned child 身份一致性 |
|
|
442
|
+
| [desktop/src/backend-lifecycle.ts](../desktop/src/backend-lifecycle.ts) | owned backend 停止时等待 `exit` 或有界超时;配置变更触发的 restart 由 generation scheduler 串行化、合并 debounce 请求且不丢活动重启期间的新请求 |
|
|
443
|
+
| [desktop/src/child-environment.ts](../desktop/src/child-environment.ts) | 为 Desktop-owned utility/spawn 子进程构造最小运行环境;provider 变量必须显式 allow-list,Control/Tunnel owner credential 始终阻断;Windows PowerShell 与 `where.exe` 固定解析到仓库外的系统目录 |
|
|
444
|
+
| [desktop/src/config-store.ts](../desktop/src/config-store.ts) | 配置/偏好/运行时设置持久化:`resolveDesktopPaths()`(`%LOCALAPPDATA%\PatchWarden`);`buildConfig()` 生成 `patchwarden.config.json`;`normalizeProxyEndpoint()` 校验 http/https/socks5 禁止 URL 凭证;`atomicWriteJson()` 带 `.bak-{stamp}` 备份 + `.tmp-{pid}` 原子 rename |
|
|
445
|
+
| [desktop/src/model-discovery.ts](../desktop/src/model-discovery.ts) | `discoverModelsForAgent(id, workspaceRoot)`:读取各 Agent 的 home/工作区配置(TOML/JSONC/YAML),`safeRead()` 拒绝符号链接、>1MB、project-scoped 逃逸(`realpathSync` 比对 workspaceRoot);`MAX_CONFIG_BYTES=1MB`;按适配器抽取模型字段;只读 |
|
|
446
|
+
| [desktop/src/runtime-root.ts](../desktop/src/runtime-root.ts) | `resolveCoreRoot()`:打包态 → `resourcesPath/core`,开发态 → `desktopRoot/..`;`utilityProcessOptions()` 提供 cwd/env/stdio/serviceName |
|
|
447
|
+
| [desktop/src/runtime-settings.ts](../desktop/src/runtime-settings.ts) | `validateTunnelClientPath()` 强制绝对路径 + 文件名 `tunnel-client.exe` + 存在;`detectTunnelClient()` 多源查找(config/env/PATH/LOCALAPPDATA/APPDATA/USERPROFILE);`boundedSiblingSearch()` BFS 深度≤2、≤2000 条目 |
|
|
448
|
+
| [desktop/src/tunnel-provisioner.ts](../desktop/src/tunnel-provisioner.ts) | `getTunnelSetupStatus()` 报告程序/profile/凭证/tunnel_id 掩码/doctor 状态;`provisionTunnelProfile()` spawn PowerShell `scripts/control/provision-patchwarden-tunnel.ps1`,runtimeKey 走 stdin,60s 超时,输出 key 被 `[REDACTED]` 替换,env 剥离 `CONTROL_PLANE_API_KEY`;`revalidateTunnelProfile()`/`forgetTunnelCredential()`/`maskTunnelId()` |
|
|
449
|
+
|
|
450
|
+
**支持的 8 种 Agent 适配器**:
|
|
451
|
+
|
|
452
|
+
| id | displayName | npmPackage / nativePackage | buildArgs | refreshArgs |
|
|
453
|
+
| --- | --- | --- | --- | --- |
|
|
454
|
+
| `codex` | Codex CLI | `@openai/codex` | `exec --cd {repo} --model <m> {prompt}` | — |
|
|
455
|
+
| `opencode` | OpenCode | native `opencode-ai` | `run --model <m> {prompt}` | `models` |
|
|
456
|
+
| `claude` | Claude Code | `@anthropic-ai/claude-code` | `--print --permission-mode acceptEdits --model <m> {prompt}` | — |
|
|
457
|
+
| `gemini` | Gemini CLI | `@google/gemini-cli` | `--prompt {prompt} --approval-mode auto_edit --model <m>` | — |
|
|
458
|
+
| `copilot` | GitHub Copilot CLI | `@github/copilot` | `-p {prompt} --allow-tool write --deny-tool shell --model <m>` | `help` |
|
|
459
|
+
| `qwen` | Qwen Code | `@qwen-code/qwen-code` | `--prompt {prompt} --approval-mode auto_edit --model <m>` | — |
|
|
460
|
+
| `kimi` | Kimi Code | —(仅原生) | `--prompt {prompt} --work-dir {repo} --model <m>` | — |
|
|
461
|
+
| `aider` | Aider | —(仅原生) | `--message {prompt} --model <m>` | `--list-models ""` |
|
|
462
|
+
|
|
463
|
+
### 4.11 其他模块
|
|
298
464
|
|
|
299
465
|
| 文件 | 职责 |
|
|
300
466
|
| --- | --- |
|
|
@@ -302,10 +468,13 @@ v0.6.0 引入的 ChatGPT 直接编辑模式。
|
|
|
302
468
|
| [src/policy/projectPolicy.ts](../src/policy/projectPolicy.ts) | 仓库级策略:`getProjectPolicySummary` 解析 `.patchwarden/project-policy.json`;`commandAllowedByProjectPolicy`/`isProtectedByProjectPolicy`;含 `DANGEROUS_COMMAND_RE` 防护 |
|
|
303
469
|
| [src/release/releaseGate.ts](../src/release/releaseGate.ts) | v1.0.0 五阶段发布门:`local_ready` → `packed_ready` → `published_verified` → `github_release_verified` → `ci_verified`;远程阶段仅用 `node:https` GET,网络错误返回 `not_checked` |
|
|
304
470
|
| [src/assessments/agentAssessor.ts](../src/assessments/agentAssessor.ts) | `runAgentAssessment` 调用 agent 执行只读风险评估,输出 `===ASSESSMENT_JSON===` 标记后的结构化结果 |
|
|
305
|
-
| [src/assessments/assessmentStore.ts](../src/assessments/assessmentStore.ts) | Assessment 记录存储与 freshness
|
|
306
|
-
| [src/
|
|
307
|
-
| [src/
|
|
308
|
-
| [src/
|
|
471
|
+
| [src/assessments/assessmentStore.ts](../src/assessments/assessmentStore.ts) | Assessment 记录存储与 freshness 校验;v1.6.1 加入跨进程锁串行化 freshness 校验与读取 |
|
|
472
|
+
| [src/utils/atomicFile.ts](../src/utils/atomicFile.ts) | `atomicWriteFileSync`/`atomicWriteJsonFileSync`:`.tmp-{pid}` 临时文件 + `renameSync` 原子替换 |
|
|
473
|
+
| [src/utils/boundedFile.ts](../src/utils/boundedFile.ts) | 有界读取:`readBoundedFileHead`/`readBoundedFileTail`/`readBoundedFileSlice` |
|
|
474
|
+
| [src/utils/lockedJsonFile.ts](../src/utils/lockedJsonFile.ts) | v1.6.1 核心:`withFileLockSync`/`mutateLockedJsonFileSync`,`O_EXCL` 创建 + PID 探活 + stale 接管 + 锁内 append/trim/原子替换 |
|
|
475
|
+
| [src/utils/platform.ts](../src/utils/platform.ts) | `isSamePath`/`isPathChildOf` 平台感知比较(Windows 忽略大小写) |
|
|
476
|
+
| [src/utils/stableJson.ts](../src/utils/stableJson.ts) | 唯一 `stableJsonStringify`;catalog 与 registry 均导入该实现 |
|
|
477
|
+
| [src/watcherStatus.ts](../src/watcherStatus.ts) | `readWatcherStatus` 心跳读取 + task heartbeat fallback + watcher ownership 检测;将 degraded 视为不可用 |
|
|
309
478
|
|
|
310
479
|
---
|
|
311
480
|
|
|
@@ -331,27 +500,36 @@ interface PatchWardenConfig {
|
|
|
331
500
|
watcherStaleSeconds: number; // Watcher 心跳过期阈值(5-3600)
|
|
332
501
|
toolProfile?: "full" | "chatgpt_core" | "chatgpt_direct" | "chatgpt_search";
|
|
333
502
|
enableDirectProfile?: boolean; // 是否启用 Direct 模式
|
|
503
|
+
enableRunTaskTool?: boolean; // 是否暴露 run_task 工具
|
|
334
504
|
directSessionsDir: string;
|
|
335
|
-
directSessionTtlSeconds: number;
|
|
505
|
+
directSessionTtlSeconds: number; // 60-86400
|
|
336
506
|
directMaxPatchBytes: number;
|
|
337
507
|
directMaxFileBytes: number;
|
|
338
508
|
directAllowedCommands?: string[];
|
|
339
509
|
repoDirectAllowedCommands?: Record<string, string[]>;
|
|
510
|
+
tunnelClientPath?: string;
|
|
511
|
+
tunnelProxy?: {
|
|
512
|
+
scope: "shared" | "separate";
|
|
513
|
+
core: { mode: "environment" | "none" | "manual"; url?: string };
|
|
514
|
+
direct: { mode: "environment" | "none" | "manual"; url?: string };
|
|
515
|
+
};
|
|
516
|
+
httpPort?: number;
|
|
517
|
+
http?: { port?: number; host?: string; ownerTokenEnv?: string };
|
|
340
518
|
// ... 其他可选字段
|
|
341
519
|
}
|
|
342
520
|
```
|
|
343
521
|
|
|
344
|
-
#### `TaskStatus`([src/tools/createTask.ts](../src/tools/createTask.ts))
|
|
522
|
+
#### `TaskStatus`([src/tools/tasks/createTask.ts](../src/tools/tasks/createTask.ts))
|
|
345
523
|
|
|
346
524
|
```typescript
|
|
347
525
|
type TaskStatus =
|
|
348
526
|
| "pending" | "running" | "collecting_artifacts"
|
|
349
527
|
| "done_by_agent" // agent 自报完成,待验收
|
|
350
|
-
| "accepted" | "rejected" | "needs_fix" | "blocked" //
|
|
528
|
+
| "accepted" | "rejected" | "needs_fix" | "blocked" // 验收状态
|
|
351
529
|
| "done" // legacy 终态
|
|
352
530
|
| "failed" | "failed_verification"
|
|
353
531
|
| "failed_scope_violation" | "failed_policy_violation"
|
|
354
|
-
| "failed_stale" | "orphaned" //
|
|
532
|
+
| "failed_stale" | "orphaned" // 进程死亡/孤儿
|
|
355
533
|
| "canceled";
|
|
356
534
|
```
|
|
357
535
|
|
|
@@ -369,7 +547,7 @@ class PatchWardenError extends Error {
|
|
|
369
547
|
}
|
|
370
548
|
```
|
|
371
549
|
|
|
372
|
-
#### `ToolCatalogSnapshot`([src/tools/toolCatalog.ts](../src/tools/toolCatalog.ts))
|
|
550
|
+
#### `ToolCatalogSnapshot`([src/tools/catalog/toolCatalog.ts](../src/tools/catalog/toolCatalog.ts))
|
|
373
551
|
|
|
374
552
|
```typescript
|
|
375
553
|
interface ToolCatalogSnapshot {
|
|
@@ -382,6 +560,14 @@ interface ToolCatalogSnapshot {
|
|
|
382
560
|
}
|
|
383
561
|
```
|
|
384
562
|
|
|
563
|
+
#### `ToolHandlerMap`([src/tools/dispatch/types.ts](../src/tools/dispatch/types.ts))
|
|
564
|
+
|
|
565
|
+
```typescript
|
|
566
|
+
type ToolHandler = (args?: unknown) => Promise<{ content: Array<{ type: string; text: string }> }>;
|
|
567
|
+
type ToolHandlerMap = Record<string, ToolHandler>;
|
|
568
|
+
function toResult(data: unknown): { content: Array<{ type: "text"; text: string }> };
|
|
569
|
+
```
|
|
570
|
+
|
|
385
571
|
### 5.2 核心函数
|
|
386
572
|
|
|
387
573
|
#### 配置加载
|
|
@@ -393,7 +579,28 @@ function reloadConfig(configPath?: string): PatchWardenConfig; // 强制重载
|
|
|
393
579
|
function getConfig(): PatchWardenConfig;
|
|
394
580
|
function getTasksDir(config: PatchWardenConfig): string;
|
|
395
581
|
function getPlansDir(config: PatchWardenConfig): string;
|
|
582
|
+
function getAssessmentsDir(config: PatchWardenConfig): string;
|
|
583
|
+
function resolveWorkspaceRoot(config: PatchWardenConfig): string;
|
|
396
584
|
function getRepoAllowedTestCommands(config, repoPath): string[];
|
|
585
|
+
function getRepoDirectAllowedCommands(config, repoPath): string[];
|
|
586
|
+
function normalizeRepoKey(value: string): string;
|
|
587
|
+
function comparablePath(value: string): string; // Windows 下以小写绝对路径比较 repo key
|
|
588
|
+
```
|
|
589
|
+
|
|
590
|
+
#### 跨进程锁与原子存储(v1.6.1)
|
|
591
|
+
|
|
592
|
+
```typescript
|
|
593
|
+
// src/utils/lockedJsonFile.ts
|
|
594
|
+
function withFileLockSync<T>(lockPath: string, fn: () => T): T;
|
|
595
|
+
function mutateLockedJsonFileSync<T>(
|
|
596
|
+
filePath: string,
|
|
597
|
+
mutation: (current: T) => { next: T; result?: unknown },
|
|
598
|
+
options?: { lockPath?: string; initialValue?: T }
|
|
599
|
+
): unknown;
|
|
600
|
+
|
|
601
|
+
// src/utils/atomicFile.ts
|
|
602
|
+
function atomicWriteFileSync(path: string, data: string | Buffer): void;
|
|
603
|
+
function atomicWriteJsonFileSync(path: string, data: unknown): void;
|
|
397
604
|
```
|
|
398
605
|
|
|
399
606
|
#### 安全守卫
|
|
@@ -421,6 +628,10 @@ function guardPlanContent(title: string, content: string): void;
|
|
|
421
628
|
function assessRisk(input: RiskAssessmentInput): RiskAssessmentResult;
|
|
422
629
|
function collectRiskHints(input: RiskAssessmentInput): string[];
|
|
423
630
|
|
|
631
|
+
// src/security/runtimeGuard.ts
|
|
632
|
+
function guardRuntimeSelfModification(resolvedRepoPath: string): void;
|
|
633
|
+
// 使用平台感知的同路径/子路径比较,Windows 下忽略大小写
|
|
634
|
+
|
|
424
635
|
// src/security/contentRedaction.ts
|
|
425
636
|
function redactSensitiveContent(input: string): RedactionResult;
|
|
426
637
|
function redactSensitiveValue<T>(input: T): StructuredRedactionResult<T>;
|
|
@@ -432,9 +643,13 @@ function redactSensitiveValue<T>(input: T): StructuredRedactionResult<T>;
|
|
|
432
643
|
// src/runner/runTask.ts
|
|
433
644
|
async function runTask(taskId: string): Promise<TaskRunResult>;
|
|
434
645
|
// 编排完整生命周期,产出 16+ artifact
|
|
646
|
+
// 子阶段:prepareTask / executeAgent / runVerification / collectArtifacts / finalizeTask
|
|
435
647
|
|
|
436
|
-
// src/runner/watch.ts
|
|
437
|
-
|
|
648
|
+
// src/runner/watch.ts(脚本入口,部分导出供测试)
|
|
649
|
+
function acquireWatcherLock(lockFilePath?: string): void; // 单实例排他锁 + stale PID 接管
|
|
650
|
+
function releaseWatcherLock(lockFilePath?, instanceId?, pid?): boolean;
|
|
651
|
+
function createNonOverlappingRunner(operation, onSkipped?): () => Promise<boolean>;
|
|
652
|
+
// tick() 每 4 秒轮询 pending 任务,pre-flight 校验后调用 runTask
|
|
438
653
|
|
|
439
654
|
// src/runner/changeCapture.ts
|
|
440
655
|
async function captureRepoSnapshot(repoPath: string): Promise<RepoSnapshot>;
|
|
@@ -442,6 +657,8 @@ async function buildChangeArtifacts(repoPath, before, after): Promise<ChangeArti
|
|
|
442
657
|
function extractExternalDirtyFiles(snapshot, repoPath, workspaceRoot): ExternalDirtyFile[];
|
|
443
658
|
function findNewExternalDirtyFiles(baseline, current): ExternalDirtyFile[];
|
|
444
659
|
async function buildArtifactManifest(changedFiles, repoPath, taskId?): Promise<ArtifactManifest>;
|
|
660
|
+
// 通过 nullDevice 在 Windows 使用 NUL、其他平台使用 /dev/null
|
|
661
|
+
// Windows 下使用小写路径键比较 before/after 快照、tracked/ignored/dirty
|
|
445
662
|
```
|
|
446
663
|
|
|
447
664
|
#### MCP 工具
|
|
@@ -452,22 +669,43 @@ function getToolDefs(): ToolDef[]; // 生成完整工具定义
|
|
|
452
669
|
function registerTools(server: Server): void; // 绑定到 MCP Server
|
|
453
670
|
async function handleToolCall(name: string, args?): Promise<{ content }>;
|
|
454
671
|
function getToolCatalogSnapshot(): ToolCatalogSnapshot;
|
|
455
|
-
|
|
456
|
-
|
|
672
|
+
// dispatchMap 由 buildDispatchMap() 在模块加载时构造(配置无关)
|
|
673
|
+
|
|
674
|
+
// src/tools/dispatch/*.ts
|
|
675
|
+
export const coreHandlers: ToolHandlerMap; // 30+ 核心工具
|
|
676
|
+
function buildDiagnosticHandlers(dispatchTool): ToolHandlerMap; // 注入 audited dispatcher,避免循环依赖
|
|
677
|
+
export const directHandlers: ToolHandlerMap; // 9 个 Direct 工具
|
|
678
|
+
export const goalHandlers: ToolHandlerMap; // 12 个 Goal 工具
|
|
679
|
+
export const releaseHandlers: ToolHandlerMap; // 4 个 Release 工具
|
|
680
|
+
export const runTaskHandler: ToolHandler; // 条件注册
|
|
681
|
+
|
|
682
|
+
// src/tools/dispatch/validation.ts (v1.6.1)
|
|
683
|
+
function asString(value: unknown, field: string): string;
|
|
684
|
+
function asNumber(value: unknown, field: string): number;
|
|
685
|
+
function asBoolean(value: unknown, field: string): boolean;
|
|
686
|
+
function asStringArray(value: unknown, field: string): string[];
|
|
687
|
+
function asRecord(value: unknown, field: string): Record<string, unknown>;
|
|
688
|
+
|
|
689
|
+
// src/tools/tasks/createTask.ts
|
|
457
690
|
async function createTask(input: CreateTaskInput): Promise<CreateTaskResult>;
|
|
458
691
|
// 支持 assess_only 预评估与 execute 执行
|
|
459
692
|
|
|
460
|
-
// src/tools/runTaskLoop.ts
|
|
693
|
+
// src/tools/tasks/runTaskLoop.ts
|
|
461
694
|
async function runTaskLoop(input: RunTaskLoopInput): Promise<RunTaskLoopOutput>;
|
|
462
695
|
// 安全编排入口,最多 5 轮迭代
|
|
463
696
|
|
|
464
|
-
// src/tools/auditTask.ts
|
|
697
|
+
// src/tools/diagnostics/auditTask.ts
|
|
465
698
|
function auditTask(taskId: string): AuditTaskOutput;
|
|
466
699
|
// 16+ 确定性检查,区分 confirmed_failures/possible_false_positives
|
|
467
700
|
|
|
468
|
-
// src/tools/waitForTask.ts
|
|
701
|
+
// src/tools/tasks/waitForTask.ts
|
|
469
702
|
async function waitForTask(taskId: string, waitSeconds?: number): Promise<WaitResult>;
|
|
470
703
|
// 长轮询,返回 continuation_required 或终态证据
|
|
704
|
+
|
|
705
|
+
// src/tools/catalog/toolSearch.ts
|
|
706
|
+
function discoverTools(input, registry, tokenIssuer?, usageStatsProvider?): DiscoverToolsOutput;
|
|
707
|
+
function explainTool(input, registry, toolDefs?, tokenPeeker?): ExplainToolOutput | null;
|
|
708
|
+
function classifyQueryIntent(query): QueryIntent;
|
|
471
709
|
```
|
|
472
710
|
|
|
473
711
|
#### Goal Session
|
|
@@ -479,32 +717,35 @@ function listGoals(workspaceRoot?): GoalSummary[];
|
|
|
479
717
|
function readGoal(goalId, workspaceRoot?): GoalDetail;
|
|
480
718
|
function writeGoalStatus(goalId, status, workspaceRoot?): void;
|
|
481
719
|
function readGoalStatus(goalId, workspaceRoot?): GoalStatus;
|
|
720
|
+
function mutateGoalStatus<R>(goalId, mutation, workspaceRoot?): R;
|
|
721
|
+
async function mutateGoalStatusAsync<R>(goalId, mutation, workspaceRoot?): Promise<R>;
|
|
482
722
|
function generateGoalId(title: string, existingIds: string[]): string; // goal_{YYYYMMDD}_{slug}
|
|
483
723
|
|
|
484
724
|
// src/goal/goalGraph.ts
|
|
485
725
|
function suggestNextSubgoal(goalStatus: GoalStatus): SubgoalSuggestion;
|
|
486
726
|
|
|
487
727
|
// src/goal/worktreeManager.ts
|
|
488
|
-
function createWorktree(
|
|
489
|
-
function mergeWorktree(worktreeId,
|
|
490
|
-
function discardWorktree(worktreeId,
|
|
728
|
+
function createWorktree(goalId, subgoalId, workspaceRoot): WorktreeInfo;
|
|
729
|
+
function mergeWorktree(worktreeId, workspaceRoot): MergeResult;
|
|
730
|
+
function discardWorktree(worktreeId, workspaceRoot): DiscardResult;
|
|
731
|
+
// 三类操作共享仓库级 lifecycle lock
|
|
491
732
|
```
|
|
492
733
|
|
|
493
734
|
#### Direct Session
|
|
494
735
|
|
|
495
736
|
```typescript
|
|
496
737
|
// src/direct/directSessionStore.ts
|
|
497
|
-
function generateDirectSessionId(): string; //
|
|
498
|
-
function createDirectSession(input: DirectSessionCreateInput):
|
|
738
|
+
function generateDirectSessionId(): string; // direct_{YYYYMMDD_HHMMSS}_{randomHex32}
|
|
739
|
+
function createDirectSession(input: DirectSessionCreateInput): DirectSessionRecord;
|
|
499
740
|
function readDirectSession(sessionId: string): DirectSessionRecord;
|
|
500
|
-
function updateDirectSession(sessionId, patch):
|
|
501
|
-
function appendDirectSessionOperation(sessionId, operation):
|
|
741
|
+
function updateDirectSession(sessionId, patch): DirectSessionRecord;
|
|
742
|
+
function appendDirectSessionOperation(sessionId, operation): DirectSessionRecord; // 锁内 mutation + 原子替换
|
|
502
743
|
function finalizeDirectSessionRecord(sessionId, artifacts): DirectSessionRecord;
|
|
503
744
|
function validateDirectSessionFreshness(session): DirectSessionValidationResult;
|
|
504
745
|
|
|
505
746
|
// src/direct/directGuards.ts
|
|
506
747
|
function guardDirectSessionActive(session: DirectSessionRecord): void;
|
|
507
|
-
function guardDirectPath(
|
|
748
|
+
function guardDirectPath(filePath, resolvedRepoPath, workspaceRoot): string;
|
|
508
749
|
function guardDirectReadPath(session, requestedPath): string;
|
|
509
750
|
function guardDirectWritePath(session, requestedPath): string;
|
|
510
751
|
function guardDirectPatchSize(patchBytes: number): void;
|
|
@@ -519,6 +760,27 @@ function issueToken(input: IssueTokenInput): string; // dst_{YYYYMMDD}_{randomH
|
|
|
519
760
|
function consumeToken(tokenId: string): DiscoveryTokenRecord; // 单次使用,消费后删除
|
|
520
761
|
function peekToken(tokenId: string): DiscoveryTokenRecord | null;
|
|
521
762
|
function revokeToken(tokenId: string): boolean;
|
|
763
|
+
// 签发时清理过期记录,活动 token 上限为 1024
|
|
764
|
+
```
|
|
765
|
+
|
|
766
|
+
#### Control Center
|
|
767
|
+
|
|
768
|
+
```typescript
|
|
769
|
+
// src/control/server.ts
|
|
770
|
+
function startServer(): Server; // 启动 HTTP 服务,绑定 127.0.0.1
|
|
771
|
+
|
|
772
|
+
// src/control/runtime.ts
|
|
773
|
+
function probeHealthStatus(url: string): Promise<HealthProbe>;
|
|
774
|
+
function classifyStaleTask(task, watcher, nowMs): StaleClassification;
|
|
775
|
+
function recordEvent(type: string, payload: object): void;
|
|
776
|
+
function readEvents(limit: number): EventRecord[];
|
|
777
|
+
function isValidTaskId(taskId: string): boolean;
|
|
778
|
+
function isValidDirectSessionId(sessionId: string): boolean;
|
|
779
|
+
// append 与 trim 在同一跨进程文件锁内执行,裁剪结果使用原子替换提交
|
|
780
|
+
|
|
781
|
+
// src/control/middleware/auth.ts
|
|
782
|
+
function checkControlToken(req: IncomingMessage): boolean;
|
|
783
|
+
// 使用 crypto.timingSafeEqual,并先校验 Buffer 长度
|
|
522
784
|
```
|
|
523
785
|
|
|
524
786
|
---
|
|
@@ -565,18 +827,23 @@ pending ──(Watcher 拉取)──▶ running ──(agent 退出)──▶ co
|
|
|
565
827
|
| `progress.md` | Agent 写入的进度记录 |
|
|
566
828
|
| `result.md` | 人类可读的执行报告 |
|
|
567
829
|
| `result.json` | 结构化结果、路径、变更、警告和后续建议 |
|
|
568
|
-
| `diff.patch` |
|
|
830
|
+
| `diff.patch` | 最多 20 MiB 的任务差异证据;疑似凭据在落盘前脱敏,超限显式标记截断 |
|
|
569
831
|
| `artifact_manifest.json` | 构建或发布产物的路径、类型、大小与 SHA-256 |
|
|
570
832
|
| `file-stats.json` | 文件级增删统计 |
|
|
571
833
|
| `verify.json` | 每条独立验证命令的结构化记录 |
|
|
572
834
|
| `verify.log` | 独立验证的可读日志 |
|
|
573
835
|
| `test.log` | Agent 执行过程中产生的测试输出 |
|
|
574
836
|
| `git-before.json` / `git-after.json` | 任务前后仓库快照 |
|
|
837
|
+
| `workspace-before.json` | 任务前工作区快照(外部脏文件基线) |
|
|
575
838
|
| `changed-files.json` | 变更文件列表 |
|
|
576
839
|
| `independent-review.md` | `audit_task` 写入的独立审计报告 |
|
|
577
840
|
| `runtime.json` | 子进程 PID、心跳、deadline |
|
|
578
841
|
| `error.log` | 错误日志 |
|
|
579
842
|
| `post-task-cleanup.json` | 清理报告 |
|
|
843
|
+
| `reconcile.json` | Control Center reconcile 写入的注解 |
|
|
844
|
+
| `stdout.log` / `stderr.log` | Agent 与验证子进程的有界脱敏输出 |
|
|
845
|
+
|
|
846
|
+
资源边界也适用于读取和持续日志:普通任务产物受 `maxReadFileBytes` 限制,diff/summary/log tail 使用有界前缀或尾部读取;`audit_task` 的 Markdown 扫描上限为 200 个文件、4 MiB 总内容。`invocation.log` 与 `reconcile.log` 在跨进程锁内有界追加,达到上限后保留最近内容并写入截断标记,避免无界内存和磁盘增长。
|
|
580
847
|
|
|
581
848
|
### 6.4 run_task_loop 编排流
|
|
582
849
|
|
|
@@ -609,19 +876,39 @@ runTaskLoop(input)
|
|
|
609
876
|
|
|
610
877
|
### 7.1 外部依赖
|
|
611
878
|
|
|
879
|
+
**主仓**(`package.json`):
|
|
880
|
+
|
|
612
881
|
```json
|
|
613
882
|
{
|
|
614
883
|
"dependencies": {
|
|
615
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
884
|
+
"@modelcontextprotocol/sdk": "^1.29.0"
|
|
616
885
|
},
|
|
617
886
|
"devDependencies": {
|
|
618
|
-
"@types/node": "^
|
|
887
|
+
"@types/node": "^18.19.0",
|
|
619
888
|
"typescript": "^6.0.3"
|
|
889
|
+
},
|
|
890
|
+
"engines": { "node": ">=18.0.0" }
|
|
891
|
+
}
|
|
892
|
+
```
|
|
893
|
+
|
|
894
|
+
**Desktop 子包**(`desktop/package.json`):
|
|
895
|
+
|
|
896
|
+
```json
|
|
897
|
+
{
|
|
898
|
+
"dependencies": {
|
|
899
|
+
"jsonc-parser": "3.3.1",
|
|
900
|
+
"smol-toml": "1.6.1",
|
|
901
|
+
"yaml": "2.8.3"
|
|
902
|
+
},
|
|
903
|
+
"devDependencies": {
|
|
904
|
+
"@types/node": "^24.0.0",
|
|
905
|
+
"electron": "43.1.1",
|
|
906
|
+
"electron-builder": "26.15.3"
|
|
620
907
|
}
|
|
621
908
|
}
|
|
622
909
|
```
|
|
623
910
|
|
|
624
|
-
|
|
911
|
+
主仓运行时**仅依赖 MCP SDK**,所有其他功能使用 Node.js 内置模块:
|
|
625
912
|
|
|
626
913
|
- `node:fs` / `node:path` / `node:crypto` / `node:http` / `node:child_process` / `node:os` / `node:url` / `node:net` / `node:timers/promises`
|
|
627
914
|
|
|
@@ -631,15 +918,15 @@ runTaskLoop(input)
|
|
|
631
918
|
index.ts / httpServer.ts
|
|
632
919
|
│
|
|
633
920
|
▼
|
|
634
|
-
tools/registry.ts ──────────┬── tools/
|
|
635
|
-
│ ├── tools/
|
|
636
|
-
│ ├── tools/
|
|
637
|
-
│ ├── tools/
|
|
638
|
-
│ ├── tools/
|
|
639
|
-
│ ├── tools/
|
|
640
|
-
│ ├── direct/* (Direct 工具)
|
|
641
|
-
│ ├── goal/* (Goal 工具)
|
|
642
|
-
│ └── release/releaseGate.ts
|
|
921
|
+
tools/registry.ts ──────────┬── tools/dispatch/*.ts ── tools/tasks/*
|
|
922
|
+
│ │ ├── tools/diagnostics/*
|
|
923
|
+
│ │ ├── tools/workspace/*
|
|
924
|
+
│ │ ├── tools/discovery/*
|
|
925
|
+
│ │ ├── tools/catalog/*
|
|
926
|
+
│ │ ├── tools/goals/*
|
|
927
|
+
│ │ ├── direct/* (Direct 工具)
|
|
928
|
+
│ │ ├── goal/* (Goal 工具)
|
|
929
|
+
│ │ └── release/releaseGate.ts
|
|
643
930
|
│
|
|
644
931
|
▼
|
|
645
932
|
config.ts ──── security/* (所有守卫)
|
|
@@ -648,7 +935,24 @@ index.ts / httpServer.ts
|
|
|
648
935
|
runner/watch.ts ──▶ runner/runTask.ts ──▶ runner/changeCapture.ts
|
|
649
936
|
├── runner/agentInvocation.ts
|
|
650
937
|
├── runner/postTaskCleanup.ts
|
|
651
|
-
|
|
938
|
+
├── runner/simpleProcess.ts
|
|
939
|
+
├── runner/processSecurity.ts
|
|
940
|
+
└── runner/taskStatusStore.ts (锁内 mutate)
|
|
941
|
+
|
|
942
|
+
controlCenter.ts ──▶ control/server.ts ──┬── control/shared.ts
|
|
943
|
+
├── control/runtime.ts
|
|
944
|
+
├── control/routeTable.ts
|
|
945
|
+
├── control/middleware/{auth,static}.ts
|
|
946
|
+
└── control/routes/*.ts (10 个领域)
|
|
947
|
+
|
|
948
|
+
desktop/main.ts ──▶ desktop/{agent-adapters,model-discovery,config-store,
|
|
949
|
+
tunnel-provisioner,runtime-settings,runtime-root,
|
|
950
|
+
backend-probe,backend-lifecycle,child-environment}.ts
|
|
951
|
+
└─ utilityProcess.fork ──▶ dist/controlCenter.js (主仓)
|
|
952
|
+
|
|
953
|
+
utils/lockedJsonFile.ts ◀── 所有跨进程 mutation 站点
|
|
954
|
+
utils/atomicFile.ts ◀── 所有 .tmp-{pid} + rename 站点
|
|
955
|
+
utils/platform.ts ◀── runtimeGuard / pathGuard / config
|
|
652
956
|
```
|
|
653
957
|
|
|
654
958
|
### 7.3 关键依赖关系
|
|
@@ -657,9 +961,14 @@ index.ts / httpServer.ts
|
|
|
657
961
|
- **`createTask`** → `riskEngine.assessRisk` → `pathGuard` + `commandGuard` + `planGuard` + `runtimeGuard` + `sensitiveGuard`
|
|
658
962
|
- **`runTask`** → `agentInvocation.buildAgentInvocation` → `commandGuard.guardAgentCommand`
|
|
659
963
|
- **`runTask`** → `changeCapture.captureRepoSnapshot` + `buildChangeArtifacts`
|
|
964
|
+
- **`runTask`** → `claimPendingTask`(taskStatusStore 锁内单次领取)
|
|
660
965
|
- **`runTaskLoop`** → 组合 `createTask` + `waitForTask` + `safeViews` + `auditTask`,不直接调用 `runTask`
|
|
661
966
|
- **`watch.ts`** → pre-flight 调用 `guardWorkspacePath` + `guardAgentCommand` + `guardTestCommand` 后才调用 `runTask`
|
|
662
967
|
- **`invokeDiscoveredTool`** → `discoveryTokenStore.consumeToken` + `toolInvocationGuard.checkInvocation` + 实际 handler 内二次校验
|
|
968
|
+
- **`registry.ts`** → 模块加载时 `buildDispatchMap()` 一次性合并 5 个 handler map(配置热重载后不刷新,需重连)
|
|
969
|
+
- **`control/server.ts`** → POST 路由统一前置 `checkControlToken`,route 文件本身不重复校验
|
|
970
|
+
- **`desktop/main.ts`** → `utilityProcess.fork` 拉起主仓 `dist/controlCenter.js`,通过 `127.0.0.1:8090` 通信
|
|
971
|
+
- **所有持久化状态 mutation** → 经 `utils/lockedJsonFile.ts` 跨进程锁 + `utils/atomicFile.ts` 原子替换
|
|
663
972
|
|
|
664
973
|
---
|
|
665
974
|
|
|
@@ -670,8 +979,9 @@ index.ts / httpServer.ts
|
|
|
670
979
|
- Node.js ≥ 18
|
|
671
980
|
- npm
|
|
672
981
|
- Git(可选,但无 Git 无法生成可靠 `git.diff`)
|
|
673
|
-
- 至少一个本地编程 Agent(OpenCode
|
|
982
|
+
- 至少一个本地编程 Agent(OpenCode / Codex / Claude Code / Gemini / Copilot / Qwen / Kimi / Aider)
|
|
674
983
|
- Windows Tunnel 模式还需 `tunnel-client.exe`、Tunnel ID、运行时 API Key
|
|
984
|
+
- 桌面打包需要 Windows x64 + Electron
|
|
675
985
|
|
|
676
986
|
### 8.2 从源码运行(推荐)
|
|
677
987
|
|
|
@@ -707,8 +1017,8 @@ $env:PATCHWARDEN_CONFIG = "D:\path\to\patchwarden.config.json"
|
|
|
707
1017
|
"tasksDir": ".patchwarden/tasks",
|
|
708
1018
|
"toolProfile": "full",
|
|
709
1019
|
"agents": {
|
|
710
|
-
"opencode": { "command": "opencode", "args": ["run", "{prompt}"] },
|
|
711
|
-
"codex": { "command": "codex", "args": ["exec", "--cd", "{repo}", "{prompt}"] }
|
|
1020
|
+
"opencode": { "command": "opencode", "args": ["run", "{prompt}"], "envAllowlist": [] },
|
|
1021
|
+
"codex": { "command": "codex", "args": ["exec", "--cd", "{repo}", "{prompt}"], "envAllowlist": [] }
|
|
712
1022
|
},
|
|
713
1023
|
"allowedTestCommands": ["npm test", "npm run build", "npm run lint", "pytest"],
|
|
714
1024
|
"maxReadFileBytes": 200000,
|
|
@@ -719,6 +1029,8 @@ $env:PATCHWARDEN_CONFIG = "D:\path\to\patchwarden.config.json"
|
|
|
719
1029
|
}
|
|
720
1030
|
```
|
|
721
1031
|
|
|
1032
|
+
`agents` 是显式执行白名单;未登记的 Agent 不可启动。Agent 子进程默认不继承 provider 环境变量,仅转发对应 `envAllowlist` 中明确列出的变量,Tunnel/HTTP owner credential 始终禁止转发。`workspaceRoot` 在配置加载时 fail closed:路径缺失、不可访问、不是目录,或指向盘符根目录、用户主目录、Desktop、Downloads、Documents 时都会拒绝启动。
|
|
1033
|
+
|
|
722
1034
|
### 8.4 核心命令
|
|
723
1035
|
|
|
724
1036
|
| 命令 | 用途 |
|
|
@@ -731,10 +1043,17 @@ $env:PATCHWARDEN_CONFIG = "D:\path\to\patchwarden.config.json"
|
|
|
731
1043
|
| `npm.cmd run start:http` | 启动 HTTP MCP Server(127.0.0.1:7331) |
|
|
732
1044
|
| `npm.cmd run start:control` | 启动 Control Center Dashboard(127.0.0.1:8090) |
|
|
733
1045
|
| `npm.cmd test` | 完整测试链(smoke + unit + lifecycle + doctor + tunnel + watcher + control + mcp + brand) |
|
|
1046
|
+
| `npm.cmd run test:unit` | 仅单元测试 |
|
|
734
1047
|
| `npm.cmd run test:mcp` | MCP 烟雾测试 |
|
|
735
1048
|
| `npm.cmd run test:http-mcp` | HTTP MCP 烟雾测试 |
|
|
1049
|
+
| `npm.cmd run check:tool-manifest` | 工具 Manifest 校验 |
|
|
736
1050
|
| `npm.cmd run pack:clean` | 打包清理 |
|
|
737
1051
|
| `npm.cmd run verify:package` | 包清单校验 |
|
|
1052
|
+
| `npm.cmd run desktop:install` | 安装 desktop 子包依赖 |
|
|
1053
|
+
| `npm.cmd run desktop:test` | desktop 单元测试 |
|
|
1054
|
+
| `npm.cmd run desktop:package` | 打包 Windows NSIS 安装包 + ZIP |
|
|
1055
|
+
| `npm.cmd run desktop:preflight` | 桌面发布前预检(含 unpacked UI/单实例验收) |
|
|
1056
|
+
| `npm.cmd run desktop:preflight:release` | 干净 checkout 的发布前预检(拒绝未提交路径) |
|
|
738
1057
|
|
|
739
1058
|
### 8.5 启动顺序
|
|
740
1059
|
|
|
@@ -774,6 +1093,7 @@ PatchWarden.cmd restart all # 重启所有受控进程
|
|
|
774
1093
|
PatchWarden.cmd status all # 查看运行状态
|
|
775
1094
|
PatchWarden.cmd health # 深度健康检查
|
|
776
1095
|
PatchWarden.cmd kill all # 强制清理
|
|
1096
|
+
scripts\launchers\PatchWarden-Desktop.cmd # 启动 Desktop 应用(托盘 + Control Center)
|
|
777
1097
|
```
|
|
778
1098
|
|
|
779
1099
|
### 8.7 MCP 客户端接入
|
|
@@ -819,6 +1139,44 @@ PATCHWARDEN_TOOL_PROFILE = "full"
|
|
|
819
1139
|
| `/admin/tasks/:id/reject` | POST | 人工拒绝任务 |
|
|
820
1140
|
| `/admin/tasks/:id/acceptance` | GET | 读取验收状态 |
|
|
821
1141
|
|
|
1142
|
+
### 8.9 Control Center API 端点(127.0.0.1:8090)
|
|
1143
|
+
|
|
1144
|
+
| 端点 | 方法 | 用途 |
|
|
1145
|
+
| --- | --- | --- |
|
|
1146
|
+
| `/api/status` | GET | 主轮询端点(10 路并发探测 + 6 类建议) |
|
|
1147
|
+
| `/api/tasks` | GET | 任务列表(支持 repo_path/status/acceptance_status/agent/warning_type 过滤) |
|
|
1148
|
+
| `/api/tasks/:id` | GET | 任务详情(含配置上限内的脱敏 diff/test_log) |
|
|
1149
|
+
| `/api/tasks/:id/safe-result` | GET | safe 任务结果摘要 |
|
|
1150
|
+
| `/api/tasks/:id/safe-audit` | GET | safe 审计摘要 |
|
|
1151
|
+
| `/api/tasks/:id/safe-test-summary` | GET | safe 测试摘要 |
|
|
1152
|
+
| `/api/tasks/:id/safe-diff-summary` | GET | safe 差异摘要 |
|
|
1153
|
+
| `/api/tasks/stale` | GET | stale 任务列表 |
|
|
1154
|
+
| `/api/tasks/:id/reconcile` | POST | reconcile 任务状态 |
|
|
1155
|
+
| `/api/tasks/:id/audit` | POST | 运行 audit_task |
|
|
1156
|
+
| `/api/tasks/:id/hide-stale` | POST | 隐藏 stale 任务 |
|
|
1157
|
+
| `/api/direct-sessions` | GET | Direct session 列表 |
|
|
1158
|
+
| `/api/direct-sessions/:id/finalize` | POST | finalize Direct session |
|
|
1159
|
+
| `/api/direct-sessions/:id/audit` | POST | audit Direct session |
|
|
1160
|
+
| `/api/direct-sessions/:id/hide` | POST | 隐藏 Direct session |
|
|
1161
|
+
| `/api/lineages` | GET | lineage 列表 |
|
|
1162
|
+
| `/api/lineages/:id` | GET | lineage 详情 |
|
|
1163
|
+
| `/api/evidence-packs` | GET | 证据包列表 |
|
|
1164
|
+
| `/api/evidence-packs/:id/export` | POST | 导出证据包 |
|
|
1165
|
+
| `/api/workspace` | GET | 工作区信息 |
|
|
1166
|
+
| `/api/workspace/repos` | GET | 工作区 repo 列表 |
|
|
1167
|
+
| `/api/workspace/:repo/status` | GET | 单 repo git status |
|
|
1168
|
+
| `/api/project-policy` | GET | 项目策略 |
|
|
1169
|
+
| `/api/release/status` | GET | release 就绪状态 |
|
|
1170
|
+
| `/api/diagnostics` | GET | 诊断信息(脱敏) |
|
|
1171
|
+
| `/api/warnings` | GET | 警告聚合(7 类) |
|
|
1172
|
+
| `/api/audit` | GET | 审计聚合 |
|
|
1173
|
+
| `/api/logs/:category` | GET | 日志 tail(core/direct/watcher/control-center) |
|
|
1174
|
+
| `/api/events` | GET | 事件时间线 |
|
|
1175
|
+
| `/api/control-center-status` | GET | Control Center 自身状态 |
|
|
1176
|
+
| `/api/start-all` `/stop-all` `/restart-all` | POST | 进程管理(需 control token) |
|
|
1177
|
+
| `/api/core/start` `/core/stop` | POST | Core 进程管理 |
|
|
1178
|
+
| `/api/direct/start` `/direct/stop` | POST | Direct 进程管理 |
|
|
1179
|
+
|
|
822
1180
|
---
|
|
823
1181
|
|
|
824
1182
|
## 9. 安全设计
|
|
@@ -830,6 +1188,8 @@ PATCHWARDEN_TOOL_PROFILE = "full"
|
|
|
830
1188
|
│ MCP 客户端(ChatGPT/Codex/OpenCode) │ ← 模型指令被视为不可信输入
|
|
831
1189
|
├─────────────────────────────────────────────────┤
|
|
832
1190
|
│ tools/registry.ts │ ← 工具注册 + Profile 过滤
|
|
1191
|
+
│ tools/dispatch/*.ts │ ← 按领域分派
|
|
1192
|
+
│ tools/dispatch/validation.ts (v1.6.1) │ ← unknown → 类型守卫
|
|
833
1193
|
│ toolInvocationGuard (8 项调用前校验) │
|
|
834
1194
|
├─────────────────────────────────────────────────┤
|
|
835
1195
|
│ security/pathGuard ← 路径横移防护 │
|
|
@@ -842,9 +1202,12 @@ PATCHWARDEN_TOOL_PROFILE = "full"
|
|
|
842
1202
|
│ runner/runTask.ts ← 心跳/超时/cancel │
|
|
843
1203
|
│ runner/changeCapture.ts ← 作用域违规检测 │
|
|
844
1204
|
│ runner/postTaskCleanup.ts ← 受控清理 │
|
|
1205
|
+
│ runner/taskStatusStore.ts ← 锁内单次领取 │
|
|
845
1206
|
├─────────────────────────────────────────────────┤
|
|
1207
|
+
│ utils/lockedJsonFile.ts ← 跨进程 mutation 锁 │
|
|
1208
|
+
│ utils/atomicFile.ts ← .tmp-{pid} + rename │
|
|
846
1209
|
│ security/contentRedaction ← 输出脱敏 │
|
|
847
|
-
│ tools/safeViews.ts
|
|
1210
|
+
│ tools/diagnostics/safeViews.ts ← safe_* 有界摘要 │
|
|
848
1211
|
└─────────────────────────────────────────────────┘
|
|
849
1212
|
```
|
|
850
1213
|
|
|
@@ -855,12 +1218,17 @@ PatchWarden 的硬性约束(在 `watch.ts` 与 `runTask.ts` 中声明):
|
|
|
855
1218
|
- `repo_path` 必须在 `workspaceRoot` 内,不能通过 `..` 跳出
|
|
856
1219
|
- Agent 必须在 `agents` 配置白名单中,命令来自本地配置而非模型输入
|
|
857
1220
|
- `test_command` / `verify_commands` 必须逐字匹配全局或仓库专属白名单
|
|
858
|
-
-
|
|
1221
|
+
- 每个任务最多运行一次(无自动重试循环);任务领取使用同目录硬链接锁 + 原子 JSON 替换
|
|
859
1222
|
- 不自动 commit、不自动 push、不删除文件、不发布、不重置仓库
|
|
860
|
-
- 敏感文件名(`.env`/SSH 私钥/`credentials`/`cookies`
|
|
1223
|
+
- 敏感文件名(`.env`/SSH 私钥/`credentials`/`cookies` 等)默认不可读,`.patchwarden/` 不构成豁免
|
|
861
1224
|
- 任务产物中的疑似密钥值会被脱敏
|
|
862
|
-
-
|
|
1225
|
+
- Goal 状态 mutation 与 worktree create/merge/discard 都使用跨进程锁;Goal 的非空子目标全部 accepted 后自动完成
|
|
1226
|
+
- 子目标任务只能使用 Goal 记录的 `repo_path`,仓库不一致时拒绝创建
|
|
1227
|
+
- audit、任务产物和日志读取/追加都有明确的字节、行数或文件数预算
|
|
1228
|
+
- HTTP Server 只绑定 `127.0.0.1`,并拒绝非回环 Host(防 DNS rebinding)
|
|
1229
|
+
- Control Center POST 路由统一前置 `x-patchwarden-control-token` 校验,使用常量时间比较
|
|
863
1230
|
- Windows DPAPI 加密 Tunnel 凭据,不写入仓库
|
|
1231
|
+
- Desktop 应用 `contextIsolation` + `sandbox` + IPC sender 校验;IPC / JSON / model-discovery / tunnel-process 数据从 `unknown` 校验
|
|
864
1232
|
|
|
865
1233
|
### 9.3 作用域违规检测
|
|
866
1234
|
|
|
@@ -871,6 +1239,7 @@ PatchWarden 的硬性约束(在 `watch.ts` 与 `runTask.ts` 中声明):
|
|
|
871
1239
|
3. `findNewExternalDirtyFiles(baseline, current)` 只标记任务期间**新增**的外部脏文件
|
|
872
1240
|
4. 任务期间新增越界改动 → `failed_scope_violation`,写入 rollback 计划但**不自动回滚**
|
|
873
1241
|
5. `change_policy: "no_changes"` 模式下出现任何改动 → `failed_policy_violation`
|
|
1242
|
+
6. Windows 下使用小写路径键比较 before/after 快照,避免大小写敏感目录碰撞
|
|
874
1243
|
|
|
875
1244
|
### 9.4 capability 模型(v0.8.1)
|
|
876
1245
|
|
|
@@ -900,6 +1269,7 @@ invoke_discovered_tool(toolName, args, discoveryToken)
|
|
|
900
1269
|
| `.patchwarden/` | 计划、任务、差异和日志 | 否 |
|
|
901
1270
|
| `%APPDATA%\patchwarden` | DPAPI 加密的 Tunnel 凭据 | 否 |
|
|
902
1271
|
| `%LOCALAPPDATA%\patchwarden` | 运行时状态和隔离配置 | 否 |
|
|
1272
|
+
| `%LOCALAPPDATA%\PatchWarden` | Desktop 应用 userData | 否 |
|
|
903
1273
|
|
|
904
1274
|
---
|
|
905
1275
|
|
|
@@ -910,7 +1280,7 @@ invoke_discovered_tool(toolName, args, discoveryToken)
|
|
|
910
1280
|
| 测试类型 | 命令 | 覆盖范围 |
|
|
911
1281
|
| --- | --- | --- |
|
|
912
1282
|
| 完整测试链 | `npm.cmd test` | smoke + unit + lifecycle + doctor + tunnel + watcher + control + mcp + brand |
|
|
913
|
-
| 单元测试 | `npm.cmd run test:unit` | `src/test/unit/*.test.ts`(
|
|
1283
|
+
| 单元测试 | `npm.cmd run test:unit` | `src/test/unit/*.test.ts`(45+ 测试文件,按领域分组) |
|
|
914
1284
|
| MCP 烟雾测试 | `npm.cmd run test:mcp` | stdio MCP 工具调用 |
|
|
915
1285
|
| HTTP MCP 烟雾测试 | `npm.cmd run test:http-mcp` | HTTP MCP 端点 |
|
|
916
1286
|
| 生命周期测试 | `npm.cmd run test:lifecycle` | 任务完整生命周期 |
|
|
@@ -920,6 +1290,19 @@ invoke_discovered_tool(toolName, args, discoveryToken)
|
|
|
920
1290
|
| Control 烟雾测试 | `npm.cmd run test:control` | Control Center API |
|
|
921
1291
|
| 工具 Manifest 校验 | `npm.cmd run check:tool-manifest` | 工具清单一致性 |
|
|
922
1292
|
| 品牌检查 | `npm.cmd run check:brand` | 品牌命名一致性 |
|
|
1293
|
+
| Desktop 测试 | `npm.cmd run desktop:test` | 桌面应用单元测试 |
|
|
1294
|
+
|
|
1295
|
+
v1.6.1 新增/扩展的覆盖:
|
|
1296
|
+
|
|
1297
|
+
- `runtime-guard-windows.test.ts`:运行时自修改与 Windows 路径比较
|
|
1298
|
+
- `change-capture-null-device.test.ts`:跨平台空设备
|
|
1299
|
+
- `runner/simple-process.test.ts`:子进程输出与日志流完成语义
|
|
1300
|
+
- `runner/run-task-claim.test.ts`、`runner/task-status-store.test.ts`:任务单次领取及取消竞态
|
|
1301
|
+
- `watcher-lock.test.ts`、`watcher-runtime.test.ts`:Watcher 单实例、stale lock 与空闲常驻
|
|
1302
|
+
- `workspace/apply-patch.test.ts`:Direct 原子替换后的结果与临时文件清理
|
|
1303
|
+
- `direct/direct-guards.test.ts`、`security/sensitive-guard.test.ts`、`workspace/sync-file.test.ts`:junction/symlink、ADS、敏感路径段和 Direct session 状态
|
|
1304
|
+
- `runner/agent-invocation.test.ts`:原生 OpenCode、已知 npm shim、未知 wrapper 拒绝
|
|
1305
|
+
- Desktop 测试覆盖 agent-detection / backend-lifecycle / backend-probe / child-environment / config-store / model-discovery / runtime-root / runtime-settings / tunnel-provisioner / ui-contract
|
|
923
1306
|
|
|
924
1307
|
### 10.2 发布验证流程
|
|
925
1308
|
|
|
@@ -960,7 +1343,8 @@ npm.cmd run verify:package
|
|
|
960
1343
|
|
|
961
1344
|
### 10.4 打包排除
|
|
962
1345
|
|
|
963
|
-
`pack:clean`
|
|
1346
|
+
`pack:clean` 只重建 `release/package/` staging 和根目录的 tar/zip 包,不删除
|
|
1347
|
+
`release/desktop*` 产物;它与 `verify:package` 都会排除:
|
|
964
1348
|
|
|
965
1349
|
- `node_modules/`
|
|
966
1350
|
- `.patchwarden/`
|
|
@@ -969,6 +1353,284 @@ npm.cmd run verify:package
|
|
|
969
1353
|
- `patchwarden.config.json`
|
|
970
1354
|
- 本地凭据和运行时状态
|
|
971
1355
|
|
|
1356
|
+
### 10.5 Desktop 打包
|
|
1357
|
+
|
|
1358
|
+
```powershell
|
|
1359
|
+
npm.cmd run desktop:install # 安装 desktop 依赖(隔离缓存)
|
|
1360
|
+
npm.cmd run desktop:test # 运行桌面单元测试
|
|
1361
|
+
npm.cmd run desktop:package # 打包:先 stage 主仓 dist/,再 electron-builder
|
|
1362
|
+
```
|
|
1363
|
+
|
|
1364
|
+
输出到 `release/desktop/`:NSIS 安装包 + 免安装 ZIP + SHA256 校验和清单。首版不含自动更新或代码签名,Windows SmartScreen 可能提示未知发布者。`desktop:preflight` 会使用唯一目录完成 clean build、完整单测、桌面测试、npm 包面、Electron 目录包、仓库外 26-tool manifest 与隔离 unpacked UI/单实例验收,并生成 `preflight-report.json` / `preflight-report.md`。
|
|
1365
|
+
|
|
1366
|
+
---
|
|
1367
|
+
|
|
1368
|
+
## 11. 现有缺陷与改进建议
|
|
1369
|
+
|
|
1370
|
+
> 以下按严重性分级。CRITICAL 涉及安全/数据损坏;HIGH 影响功能;MEDIUM 影响可靠性/代码质量;LOW 影响可维护性。
|
|
1371
|
+
> 本节在 v1.6.1 复核(2026-07-20)后重新整理:标记 "✅ 已修复" 的条目已按当前源码与仓库内对应测试核对,不再属于开放缺陷;标记 "🟡 开放" 的条目仍需处理;标记 "ℹ️ 非缺陷" 的条目经复核不构成问题。
|
|
1372
|
+
|
|
1373
|
+
### 11.1 跨平台缺陷(Windows + POSIX)
|
|
1374
|
+
|
|
1375
|
+
#### ✅ 11.1.1 `runtimeGuard.ts` 的 Windows 路径判断 — 已修复
|
|
1376
|
+
|
|
1377
|
+
- **现状**:[src/security/runtimeGuard.ts](../src/security/runtimeGuard.ts) 从模块位置解析 package root,并通过 `isSamePath`/`isPathChildOf` 做平台感知的父子路径判断,不再使用 `resolve("/")[0]`。
|
|
1378
|
+
- **覆盖**:[src/test/unit/runtime-guard-windows.test.ts](../src/test/unit/runtime-guard-windows.test.ts) 覆盖 Windows 大小写和运行时关键子目录阻断。
|
|
1379
|
+
|
|
1380
|
+
#### ✅ 11.1.2 `changeCapture.ts` 的空设备路径 — 已修复
|
|
1381
|
+
|
|
1382
|
+
- **现状**:[src/runner/changeCapture.ts](../src/runner/changeCapture.ts) 使用平台辅助常量 `nullDevice`,Windows 为 `NUL`,其他平台为 `/dev/null`。
|
|
1383
|
+
- **覆盖**:[src/test/unit/change-capture-null-device.test.ts](../src/test/unit/change-capture-null-device.test.ts) 验证平台映射。
|
|
1384
|
+
|
|
1385
|
+
#### ✅ 11.1.3 Windows 快照路径大小写比较 — 已修复
|
|
1386
|
+
|
|
1387
|
+
- **现状**:[src/runner/changeCapture.ts](../src/runner/changeCapture.ts) 保留原始规范化路径用于证据展示,但在 Windows 下使用小写比较键处理 before/after 快照、tracked/ignored 集合、dirty path 和外部脏文件基线;POSIX 仍保持大小写敏感。
|
|
1388
|
+
- **覆盖**:[src/test/unit/runner/change-capture.test.ts](../src/test/unit/runner/change-capture.test.ts) 覆盖 Windows 大小写重命名/修改、大小写敏感目录碰撞、POSIX 重命名和外部基线匹配。
|
|
1389
|
+
|
|
1390
|
+
#### ✅ 11.1.4 Windows repo key 大小写匹配 — 已修复
|
|
1391
|
+
|
|
1392
|
+
- **现状**:[src/config.ts](../src/config.ts) 的存储 key 仍保留用户书写形式,但 `getRepoAllowedTestCommands` 与 `getRepoDirectAllowedCommands` 均通过 `comparablePath` 比较;Windows 下会转为小写绝对路径,因此 `MyRepo`/`myrepo` 不再导致 allowlist 失配。
|
|
1393
|
+
|
|
1394
|
+
#### ✅ 11.1.5 Windows Agent npm shim 无 shell 启动 — 已修复
|
|
1395
|
+
|
|
1396
|
+
- **现状**:[src/runner/agentInvocation.ts](../src/runner/agentInvocation.ts) 对 OpenCode 优先解析原生 `opencode.exe`;对 Codex、Claude、Gemini、Copilot、Qwen 等已知 npm adapter,验证受信 PATH 下的包名和 `package.json#bin` 后,使用受信 Node 可执行文件直接启动 CLI。未知 `.cmd`/`.bat`/`.ps1` wrapper 继续拒绝,不会回退到 shell。
|
|
1397
|
+
- **覆盖**:[src/test/unit/runner/agent-invocation.test.ts](../src/test/unit/runner/agent-invocation.test.ts) 覆盖原生 OpenCode、已知 npm shim、未知 wrapper 拒绝和 repo-local 同名 executable 排除。
|
|
1398
|
+
|
|
1399
|
+
#### ✅ 11.1.6 `postTaskCleanup.ts` 不再调用 `cmd rmdir` — 已修复
|
|
1400
|
+
|
|
1401
|
+
- **现状**:[src/runner/postTaskCleanup.ts](../src/runner/postTaskCleanup.ts) 使用 `fs.rmSync` 的有界重试;Windows 失败时只对目标树清除只读属性后再重试,不再启动 `cmd.exe /c rmdir`。Git 分类探测仍是同步且有界的本地命令,清理仅在任务收尾阶段执行。
|
|
1402
|
+
|
|
1403
|
+
### 11.2 安全缺陷
|
|
1404
|
+
|
|
1405
|
+
#### 🟡 11.2.1 `pathGuard.ts` 的 TOCTOU 竞争窗口 — 开放(HIGH)
|
|
1406
|
+
|
|
1407
|
+
- **文件**:[src/security/pathGuard.ts](../src/security/pathGuard.ts)
|
|
1408
|
+
- **问题**:`realCandidate` 解析时刻与后续文件操作(read/write)之间存在 TOCTOU 窗口;攻击者可在校验通过后用符号链接替换目标路径逃逸工作区。
|
|
1409
|
+
- **影响**:在多用户/共享环境下可被利用绕过工作区限制。
|
|
1410
|
+
- **修复建议**:使用 `fs.open()` + `fstat()` + `O_NOFOLLOW`(POSIX)或 `FILE_FLAG_BACKUP_SEMANTICS`(Windows)确保文件描述符不被替换。
|
|
1411
|
+
|
|
1412
|
+
#### ✅ 11.2.2 `sensitiveGuard.ts` token/credentials 误匹配 — 已修复
|
|
1413
|
+
|
|
1414
|
+
- **现状**:[src/security/sensitiveGuard.ts](../src/security/sensitiveGuard.ts) 已把 `credentials`、`token` 收紧为完整 basename、受限扩展名或明确的 token-store/API-key 形态;`tokenizer.ts`、`credentials-handler.ts` 不再命中。NTFS ADS、NUL 和伪 `.patchwarden` 前缀也会被拒绝。
|
|
1415
|
+
- **保守策略**:basename 恰为 `config.json` 仍被阻止,这是对常见本地凭据文件的有意保守策略,不再归因于子串正则缺陷。
|
|
1416
|
+
|
|
1417
|
+
#### ℹ️ 11.2.3 `commandGuard.ts` 元字符校验 — 非缺陷
|
|
1418
|
+
|
|
1419
|
+
- `\x00-\x1F` 已包含换行、回车和制表符;配置命令 basename 还必须匹配 `[a-zA-Z0-9._-]+`。任务子进程不启用 shell,且实际可执行文件会从移除 repo cwd 的受信 PATH 解析,因此 `*`/`?` 不会获得 shell 通配语义。
|
|
1420
|
+
|
|
1421
|
+
#### ℹ️ 11.2.4 验证命令保持精确匹配 — 非缺陷
|
|
1422
|
+
|
|
1423
|
+
- `guardTestCommand`/`guardDirectCommand` 仅做首尾空白清理后与本地 allowlist 精确比较。`npm.cmd` 与 `NPM.CMD` 不自动等价是 fail-closed 设计,避免模型通过"近似命令"扩大授权;Windows npm/npx/pnpm 的实际无 shell 解析由进程安全层处理。
|
|
1424
|
+
|
|
1425
|
+
#### ✅ 11.2.5 `contentRedaction.ts` 常见 token 格式覆盖 — 已修复
|
|
1426
|
+
|
|
1427
|
+
- **现状**:[src/security/contentRedaction.ts](../src/security/contentRedaction.ts) 已覆盖 GitLab `glpat-`、Slack `xox*`、AWS access key、Google API key 和 JWT 形态。
|
|
1428
|
+
- **覆盖**:[src/test/unit/security/content-redaction.test.ts](../src/test/unit/security/content-redaction.test.ts) 使用合成凭据形态验证脱敏与普通文本不误报。
|
|
1429
|
+
|
|
1430
|
+
#### ✅ 11.2.6 `directPatch.ts` 原子替换 — 已修复
|
|
1431
|
+
|
|
1432
|
+
- **现状**:[src/direct/directPatch.ts](../src/direct/directPatch.ts) 在目标文件同目录写入随机临时文件后 `renameSync` 替换,并保留原文件权限;失败时清理临时文件。
|
|
1433
|
+
- **覆盖**:[src/test/unit/workspace/apply-patch.test.ts](../src/test/unit/workspace/apply-patch.test.ts) 检查补丁结果及成功后无残留临时文件。
|
|
1434
|
+
|
|
1435
|
+
#### ✅ 11.2.7 `discoveryTokenStore.ts` 有界存储 — 已修复
|
|
1436
|
+
|
|
1437
|
+
- **现状**:[src/security/discoveryTokenStore.ts](../src/security/discoveryTokenStore.ts) 在签发时清理过期记录,并将活动 token 数限制为 1024;达到上限时淘汰最旧记录。
|
|
1438
|
+
- **覆盖**:[src/test/unit/discovery/discovery-token-store.test.ts](../src/test/unit/discovery/discovery-token-store.test.ts) 覆盖未消费过期 token 清理和容量上限。
|
|
1439
|
+
|
|
1440
|
+
#### ✅ 11.2.8 Control token 常量时间比较 — 已修复
|
|
1441
|
+
|
|
1442
|
+
- **现状**:[src/control/middleware/auth.ts](../src/control/middleware/auth.ts) 将 header 与 control token 转为 Buffer,先比较长度,再使用 `crypto.timingSafeEqual`。
|
|
1443
|
+
|
|
1444
|
+
### 11.3 错误处理缺陷
|
|
1445
|
+
|
|
1446
|
+
#### ℹ️ 11.3.1 `release_prepare`/`release_cleanup` 的同步调用 — 非缺陷
|
|
1447
|
+
|
|
1448
|
+
- **结论**:[src/tools/release/releaseMode.ts](../src/tools/release/releaseMode.ts) 中 `releasePrepare` 与 `releaseCleanup` 均为同步函数并直接返回 `ReleaseModeResult`,dispatch 不需要 `await`;`releaseCheck` 与 `releaseVerify` 才是异步函数。
|
|
1449
|
+
|
|
1450
|
+
#### ✅ 11.3.2 `watch.ts` 报告损坏的任务状态 — 已修复
|
|
1451
|
+
|
|
1452
|
+
- **现状**:[src/runner/watch.ts](../src/runner/watch.ts) 对非对象或无法解析的 `status.json` 记录带 `task_id` 和错误摘要的 warning 后跳过,不再静默吞掉。
|
|
1453
|
+
|
|
1454
|
+
#### ✅ 11.3.3 Watcher 与任务领取并发保护 — 已修复
|
|
1455
|
+
|
|
1456
|
+
- **现状**:[src/runner/watch.ts](../src/runner/watch.ts) 以排他创建的 watcher lock 拒绝活动重复实例,支持 stale PID 接管且只允许 owner 释放;tick 通过 non-overlapping runner 串行化。
|
|
1457
|
+
- **任务级保护**:[src/runner/taskStatusStore.ts](../src/runner/taskStatusStore.ts) 以同目录硬链接锁和原子 JSON 替换实现 `pending -> running` 单次领取;`cancelTask` 与 runner 共享同一状态锁。
|
|
1458
|
+
- **覆盖**:[src/test/unit/watcher-lock.test.ts](../src/test/unit/watcher-lock.test.ts)、[src/test/unit/watcher-runtime.test.ts](../src/test/unit/watcher-runtime.test.ts)、[src/test/unit/runner/task-status-store.test.ts](../src/test/unit/runner/task-status-store.test.ts) 和 [src/test/unit/runner/run-task-claim.test.ts](../src/test/unit/runner/run-task-claim.test.ts) 覆盖重复实例、stale lock、多进程领取、重复 runner 与取消竞态。
|
|
1459
|
+
|
|
1460
|
+
#### ℹ️ 11.3.4 `watch.ts` 保持轮询定时器引用 — 非缺陷
|
|
1461
|
+
|
|
1462
|
+
- **结论**:Watcher 是常驻进程,轮询定时器必须保持引用,才能在空闲时继续存活;SIGINT/SIGTERM handler 会显式释放 owner lock 并退出。`watcher-runtime.test.ts` 已覆盖空闲 watcher 常驻行为。
|
|
1463
|
+
|
|
1464
|
+
#### ✅ 11.3.5 `simpleProcess.ts` 追加写入 — 已修复
|
|
1465
|
+
|
|
1466
|
+
- **现状**:[src/runner/simpleProcess.ts](../src/runner/simpleProcess.ts) 与 [src/runner/processSecurity.ts](../src/runner/processSecurity.ts) 使用 `SecureProcessLogCapture` 有界收集、写前脱敏并最终 `appendFileSync`,不再执行 read-modify-write;异步完成以 child `close` 为准,避免在 stdio 排空前返回。
|
|
1467
|
+
|
|
1468
|
+
#### 🟡 11.3.6 `logging.ts` fatal handler 立即退出 — 开放(LOW)
|
|
1469
|
+
|
|
1470
|
+
- **现状**:[src/logging.ts](../src/logging.ts) 在 `uncaughtException` 记录结构化错误后调用 `process.exit(1)`,避免进程在未知状态继续运行。Direct patch、session/status JSON 等关键状态已使用原子替换,因此旧文档所称"中断 directPatch 非原子写入"不再成立。
|
|
1471
|
+
- **残余风险**:尚未 flush 的异步遥测或第三方输出可能丢失;若未来增加异步日志 sink,应在 fatal 路径增加有界 flush。
|
|
1472
|
+
|
|
1473
|
+
#### 🟡 11.3.7 best-effort cleanup 仍有静默 catch — 开放(LOW)
|
|
1474
|
+
|
|
1475
|
+
- **现状**:锁临时文件、失败后的清理和证据日志写入仍有少量 best-effort catch;关键状态更新会显式报错,但低价值清理失败可能只通过后续存在性检查暴露。
|
|
1476
|
+
- **建议**:增加有界 debug 计数,避免把潜在磁盘/权限问题长期静默化。
|
|
1477
|
+
|
|
1478
|
+
### 11.4 代码质量问题
|
|
1479
|
+
|
|
1480
|
+
#### ✅ 11.4.1 TaskEntry 重建逻辑复用 — 已修复
|
|
1481
|
+
|
|
1482
|
+
- **现状**:[src/control/runtime.ts](../src/control/runtime.ts) 导出 `reconstructTaskEntry`,`routes/tasks.ts` 与 `routes/taskActions.ts` 均调用该 helper;旧的 `VALID_ACCEPTANCE2`/`taskStatus2` 复制代码已删除。
|
|
1483
|
+
|
|
1484
|
+
#### 🟡 11.4.2 `runTask.ts` 仍承担较多职责 — 开放(MEDIUM)
|
|
1485
|
+
|
|
1486
|
+
- **现状**:[src/tools/registry.ts](../src/tools/registry.ts) 已缩减至约 170 行,定义、catalog 与领域 dispatch 均已拆分,旧"1412 行 registry"结论失效。[src/runner/runTask.ts](../src/runner/runTask.ts) 仍超过 1300 行,包含生命周期编排和 managed process 控制。
|
|
1487
|
+
- **建议**:继续把 task context、managed process 与 artifact collection 按稳定边界拆分,同时保持任务状态机和安全检查顺序不变。
|
|
1488
|
+
|
|
1489
|
+
#### ℹ️ 11.4.3 `dispatchMap` 为配置无关 handler map — 非缺陷
|
|
1490
|
+
|
|
1491
|
+
- **结论**:[src/tools/registry.ts](../src/tools/registry.ts) 的 `dispatchMap` 只组合领域 handler;profile/feature gate 由 `getToolDefs()` 和 handler 内运行时守卫决定。`registerTools` 有意冻结一次连接的 active tool list,避免同一 MCP 连接的 list/call 漂移;配置变化应通过重连生效。
|
|
1492
|
+
|
|
1493
|
+
#### 🟡 11.4.4 `any` 仍集中在测试夹具和少量 dispatch 转换 — 开放(MEDIUM,v1.6.1 部分收敛)
|
|
1494
|
+
|
|
1495
|
+
- **现状**:v1.6.1 引入 [src/tools/dispatch/validation.ts](../src/tools/dispatch/validation.ts),把 dispatch 层的输入从 `as any` 断言迁到 `asString`/`asNumber`/`asBoolean`/`asStringArray`/`asRecord` 类型守卫;Desktop IPC、JSON、model-discovery、tunnel-process 数据也改为从 `unknown` 校验。剩余显著使用主要集中在大型 smoke harness 与少量 fixture。
|
|
1496
|
+
- **建议**:优先为 MCP 输入和 smoke fixture 定义共享类型,逐步改为 `unknown` + 类型守卫;不要用一次性全局替换破坏现有测试意图。
|
|
1497
|
+
|
|
1498
|
+
#### 🟡 11.4.5 `coreDispatch.ts`/`directDispatch.ts`/`goalDispatch.ts`/`releaseDispatch.ts` 残余 `as any` — 开放(MEDIUM)
|
|
1499
|
+
|
|
1500
|
+
- **文件**:`src/tools/dispatch/*.ts` 多处
|
|
1501
|
+
- **问题**:`template as any`、`file as "stdout" | "stderr" | ...`、`target_stage as any`、`operations as any` 等类型断言仍存在,依赖底层工具函数自行校验。v1.6.1 的 `validation.ts` 仅覆盖了部分高频入口。
|
|
1502
|
+
- **修复建议**:在 dispatch 层做参数 schema 校验,或在底层加显式校验。
|
|
1503
|
+
|
|
1504
|
+
#### ✅ 11.4.6 共享稳定 JSON 实现 — 已修复
|
|
1505
|
+
|
|
1506
|
+
- **现状**:[src/utils/stableJson.ts](../src/utils/stableJson.ts) 提供唯一 `stableJsonStringify`;catalog 与 registry 均导入该实现。
|
|
1507
|
+
|
|
1508
|
+
#### 🟡 11.4.7 Control URL 解码策略仍未完全统一 — 开放(LOW)
|
|
1509
|
+
|
|
1510
|
+
- **现状**:多数路径参数通过 `control/shared.ts` helper 处理;`routeTable.ts` 的 repo path 与静态文件 middleware 仍分别实现严格 400 错误语义,不能直接用"失败后返回原文"的 helper 替换。
|
|
1511
|
+
- **建议**:若继续抽象,应显式区分 strict decode 与 fallback decode 两种策略。
|
|
1512
|
+
|
|
1513
|
+
#### ✅ 11.4.8 Task safe handler 冗余 catch — 已修复
|
|
1514
|
+
|
|
1515
|
+
- **现状**:[src/control/routes/tasks.ts](../src/control/routes/tasks.ts) 的四个 safe handler 各保留单层 try/catch 与统一 ID 校验,旧的嵌套死代码已删除。
|
|
1516
|
+
|
|
1517
|
+
### 11.5 架构问题
|
|
1518
|
+
|
|
1519
|
+
#### 🟡 11.5.1 模块级可变状态过多 — 开放(MEDIUM)
|
|
1520
|
+
|
|
1521
|
+
- **位置**:
|
|
1522
|
+
- `src/security/discoveryTokenStore.ts`:进程内 token store
|
|
1523
|
+
- `src/config.ts`:`_config` 模块级单例
|
|
1524
|
+
- `src/runner/watch.ts`:`executedTasks` 与连续失败计数
|
|
1525
|
+
- `src/control/runtime.ts`:状态摘要缓存
|
|
1526
|
+
- `src/tools/catalog/toolCatalog.ts`:最近 catalog snapshot
|
|
1527
|
+
- **影响**:测试需显式 reset,且多实例语义依赖进程隔离。`dispatchMap` 已确认配置无关,不再作为热重载缺陷证据。
|
|
1528
|
+
|
|
1529
|
+
#### ✅ 11.5.2 Watcher 重复 tick 失败升级 — 已修复
|
|
1530
|
+
|
|
1531
|
+
- **现状**:[src/runner/watch.ts](../src/runner/watch.ts) 记录连续 tick 失败;达到 3 次后心跳写为 `degraded` 并带有截断的 `last_error`。[src/watcherStatus.ts](../src/watcherStatus.ts) 将 degraded 视为不可用,并向 pending task 返回 `queued_but_watcher_degraded`。
|
|
1532
|
+
|
|
1533
|
+
#### ✅ 11.5.3 Diagnostic dispatch 循环依赖 — 已修复
|
|
1534
|
+
|
|
1535
|
+
- **现状**:[src/tools/dispatch/diagnosticDispatch.ts](../src/tools/dispatch/diagnosticDispatch.ts) 导出 `buildDiagnosticHandlers(dispatchTool)`;registry 注入 audited dispatcher,不再由 diagnostic 模块反向 import registry。
|
|
1536
|
+
|
|
1537
|
+
### 11.6 测试覆盖差距
|
|
1538
|
+
|
|
1539
|
+
#### 🟡 11.6.1 核心模块测试覆盖 — 部分修复(MEDIUM)
|
|
1540
|
+
|
|
1541
|
+
已覆盖的定向测试见 [10.1 节](#101-测试体系)。剩余差距主要是 `runTask.ts` 全生命周期的细粒度单元覆盖和 `control/server.ts` HTTP 路由级覆盖;现有 lifecycle/control smoke 不能完全替代单元测试。
|
|
1542
|
+
|
|
1543
|
+
#### ℹ️ 11.6.2 工具级覆盖清单 — 旧清单失效
|
|
1544
|
+
|
|
1545
|
+
- 旧版按 `src/tools/*.ts` 平铺路径列出的"未覆盖文件"已因工具分域重排和新增测试失效。当前仍应关注的是跨工具生命周期、Control HTTP 边界和真实 CLI 组合覆盖,而不是维护容易漂移的静态文件名清单。
|
|
1546
|
+
|
|
1547
|
+
### 11.7 文档漂移
|
|
1548
|
+
|
|
1549
|
+
#### ✅ 11.7.1 CODE_WIKI 关键元数据漂移 — 已修复(v1.6.1 复核)
|
|
1550
|
+
|
|
1551
|
+
| 字段 | 上一版文档声称 | 当前源码 |
|
|
1552
|
+
| --- | --- | --- |
|
|
1553
|
+
| 源码版本 | `v1.6.0` | `v1.6.1` |
|
|
1554
|
+
| Schema Epoch | `2026-07-19-v15` | `2026-07-19-v15`(保持) |
|
|
1555
|
+
| 主包 `@types/node` | `^26.1.0` | `^18.19.0`(与 Node.js 18 最低运行时对齐) |
|
|
1556
|
+
| `chatgpt_direct` 工具数 | 15 | 14 |
|
|
1557
|
+
|
|
1558
|
+
> 根目录中英文 README 的下载链接、安装器哈希和公开版本事实已同步到 v1.6.1。
|
|
1559
|
+
|
|
1560
|
+
#### ✅ 11.7.2 v1.5.1/v1.6.0/v1.6.1 新功能导览 — 已修复
|
|
1561
|
+
|
|
1562
|
+
本文现已覆盖 `src/tools/dispatch/` 拆分、`src/control/` 路由拆分、`routeTable.ts`、`dispatch/validation.ts`、`utils/lockedJsonFile.ts`、Desktop 子包、模型发现、Agent 适配器、`androidDoctor`、`releaseMode`、`schemaDriftCheck`、`toolUsageStats`、`syncFile`、`goalSubgoalTask`、`runDirectVerificationBundle` 等。
|
|
1563
|
+
|
|
1564
|
+
#### ✅ 11.7.3 README 与 Direct Profile 工具数 — 已修复
|
|
1565
|
+
|
|
1566
|
+
- `README.md`、`README.en.md` 与 [src/tools/catalog/toolCatalog.ts](../src/tools/catalog/toolCatalog.ts) 均为 14 个 `chatgpt_direct` 工具。
|
|
1567
|
+
|
|
1568
|
+
### 11.8 配置/校验差距
|
|
1569
|
+
|
|
1570
|
+
#### ✅ 11.8.1 Direct 数值配置范围 — 已修复
|
|
1571
|
+
|
|
1572
|
+
- **现状**:[src/config.ts](../src/config.ts) 要求 `directSessionTtlSeconds` 为 60–86400 的整数,并要求 `directMaxPatchBytes`、`directMaxFileBytes` 为正整数。
|
|
1573
|
+
|
|
1574
|
+
#### ✅ 11.8.2 `control/routes/sessions.ts` sessionId 校验 — 已修复
|
|
1575
|
+
|
|
1576
|
+
- **现状**:[src/control/routes/sessions.ts](../src/control/routes/sessions.ts) 的 detail、safe summary、finalize、audit、hide 路由统一调用 `isValidDirectSessionId` 白名单校验。
|
|
1577
|
+
|
|
1578
|
+
#### ✅ 11.8.3 Workspace 父目录穿越检查 — 已修复
|
|
1579
|
+
|
|
1580
|
+
- **现状**:[src/control/routes/workspace.ts](../src/control/routes/workspace.ts) 只拒绝值恰为 `..` 的路径段,不再误拒 `release..candidate` 之类合法目录名;Control Center smoke 覆盖该合法路径。
|
|
1581
|
+
|
|
1582
|
+
### 11.9 并发问题
|
|
1583
|
+
|
|
1584
|
+
#### ✅ 11.9.1 多 watcher 与任务重复领取(见 11.3.3) — 已修复
|
|
1585
|
+
|
|
1586
|
+
#### ✅ 11.9.2 Direct session 原子 mutation — 已修复
|
|
1587
|
+
|
|
1588
|
+
- **现状**:[src/direct/directSessionStore.ts](../src/direct/directSessionStore.ts) 的 update、operation append 与 verification append 均通过 `mutateLockedJsonFileSync` 串行化,并以原子 JSON 替换提交;session identity 与记录结构在锁内重新校验。
|
|
1589
|
+
|
|
1590
|
+
#### ✅ 11.9.3 Control event append/trim 竞争 — 已修复
|
|
1591
|
+
|
|
1592
|
+
- **现状**:[src/control/runtime.ts](../src/control/runtime.ts) 在 `withFileLockSync(controlCenterEventsPath, ...)` 内顺序执行 append、大小检查和 trim;裁剪结果再通过原子替换提交。并发 writer 使用同一锁文件,不再能在读取与 rename 之间插入一条随后丢失的事件。
|
|
1593
|
+
|
|
1594
|
+
#### ✅ 11.9.4 Hidden ID 文件类型校验 — 已修复
|
|
1595
|
+
|
|
1596
|
+
- **现状**:[src/control/runtime.ts](../src/control/runtime.ts) 通过 `readStoredStringArray` 校验 `Array.isArray`,过滤非字符串/空字符串并去重;写入使用原子 JSON 替换。
|
|
1597
|
+
|
|
1598
|
+
#### ✅ 11.9.5 Goal mutation 与 worktree lifecycle 串行化 — 已修复(v1.6.1)
|
|
1599
|
+
|
|
1600
|
+
- **现状**:[src/goal/goalStore.ts](../src/goal/goalStore.ts) 与 [src/goal/worktreeManager.ts](../src/goal/worktreeManager.ts) 共享仓库级 lifecycle lock;create/merge/discard 串行化,跨进程孤儿锁由后续操作自动接管。
|
|
1601
|
+
|
|
1602
|
+
### 11.10 依赖卫生
|
|
1603
|
+
|
|
1604
|
+
#### ℹ️ 11.10.1 主包保持单一直接运行时依赖 — 非缺陷
|
|
1605
|
+
|
|
1606
|
+
主仓 `package.json` 的直接运行时依赖只有 `@modelcontextprotocol/sdk`(`^1.29.0`)。这不是"零运行时依赖";准确说法是"单一直接运行时依赖"。SDK 的传递依赖仍由 lockfile 管理。
|
|
1607
|
+
|
|
1608
|
+
#### ✅ 11.10.2 Node 类型与运行时基线对齐 — 已修复
|
|
1609
|
+
|
|
1610
|
+
- **现状**:主包使用 `@types/node ^18.19.0` 对齐 `engines.node >=18.0.0`;Electron 桌面子包独立使用 `@types/node ^24.0.0` 对齐其嵌入的 Node 运行时。
|
|
1611
|
+
|
|
1612
|
+
### 11.11 优先级修复建议汇总
|
|
1613
|
+
|
|
1614
|
+
**已修复并从开放待办移除**:11.1.1–11.1.6、11.2.2、11.2.5–11.2.8、11.3.2、11.3.3、11.3.5、11.4.1、11.4.6、11.4.8、11.5.2、11.5.3、11.7.1–11.7.3、11.8.1–11.8.3、11.9.1–11.9.5、11.10.2。11.2.3、11.2.4、11.3.1、11.3.4、11.4.3、11.6.2、11.10.1 经复核不构成缺陷。
|
|
1615
|
+
|
|
1616
|
+
**仍需优先处理(HIGH,影响安全或核心功能)**:
|
|
1617
|
+
|
|
1618
|
+
1. [11.2.1] `pathGuard.ts` 的 TOCTOU → 使用文件描述符级校验或等价的无跟随链接策略
|
|
1619
|
+
|
|
1620
|
+
**短期修复(MEDIUM,提升可靠性)**:
|
|
1621
|
+
|
|
1622
|
+
2. [11.4.2] 继续按稳定边界拆分 `runTask.ts`
|
|
1623
|
+
3. [11.4.4] 持续把剩余 MCP 输入与 smoke fixture 的 `any` 迁移为共享类型/类型守卫
|
|
1624
|
+
4. [11.4.5] dispatch 层残余 `as any` 收敛
|
|
1625
|
+
5. [11.6.1] 补齐 `runTask.ts` 生命周期与 Control HTTP 路由级单元测试
|
|
1626
|
+
6. [11.5.1] 模块级可变状态逐步收敛(带 reset helper 或显式 lifecycle)
|
|
1627
|
+
|
|
1628
|
+
**长期改进(LOW,可维护性)**:
|
|
1629
|
+
|
|
1630
|
+
7. [11.3.6] fatal handler 增加有界 flush,避免异步遥测丢失
|
|
1631
|
+
8. [11.3.7] best-effort cleanup 增加有界 debug 计数
|
|
1632
|
+
9. [11.4.7] 统一 strict/fallback 两类 URL 解码策略
|
|
1633
|
+
|
|
972
1634
|
---
|
|
973
1635
|
|
|
974
1636
|
## 附录:关键设计原则
|
|
@@ -979,5 +1641,8 @@ npm.cmd run verify:package
|
|
|
979
1641
|
4. **可审计性**:所有任务产出结构化证据,支持独立验收
|
|
980
1642
|
5. **不自动破坏**:不自动 commit/push/publish/tag/release,需人工决策
|
|
981
1643
|
6. **有界输出**:`safe_*` 系列工具返回有界摘要,避免触发平台内容过滤
|
|
982
|
-
7.
|
|
983
|
-
8. **跨平台**:Windows 与 POSIX
|
|
1644
|
+
7. **依赖最小化**:主仓保持单一直接运行时依赖(MCP SDK),其他核心功能优先使用 Node.js 内置模块
|
|
1645
|
+
8. **跨平台**:Windows 与 POSIX 兼容(进程管理、路径处理、命令包装),平台特定回归集中记录在 11.1
|
|
1646
|
+
9. **薄壳桌面**:所有业务逻辑在主仓 `dist/`,桌面只负责安装/检测/拉起/托盘/tunnel 下发
|
|
1647
|
+
10. **配置即信任源**:Agent 命令、测试白名单、敏感路径模式都从本地配置读取,不从模型输入
|
|
1648
|
+
11. **跨进程串行化(v1.6.1)**:所有持久化状态变更通过 `withFileLockSync` + `mutateLockedJsonFileSync` + 原子 `.tmp-{pid}` rename,保证 crash 后磁盘状态非旧即新
|