patchwarden 1.5.1 → 1.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/PatchWarden.cmd +32 -2
- package/README.en.md +152 -25
- package/README.md +120 -23
- package/dist/assessments/agentAssessor.js +21 -12
- package/dist/assessments/assessmentStore.js +42 -34
- package/dist/config.d.ts +15 -0
- package/dist/config.js +66 -13
- package/dist/control/fileManager.d.ts +2 -0
- package/dist/control/fileManager.js +39 -0
- package/dist/control/middleware/auth.d.ts +6 -0
- package/dist/control/middleware/auth.js +14 -5
- package/dist/control/middleware/static.js +16 -3
- package/dist/control/routeTable.d.ts +24 -0
- package/dist/control/routeTable.js +263 -0
- package/dist/control/routes/audit.js +17 -7
- package/dist/control/routes/evidence.js +1 -1
- package/dist/control/routes/lineage.js +1 -1
- package/dist/control/routes/process.d.ts +7 -0
- package/dist/control/routes/process.js +329 -42
- package/dist/control/routes/sessions.d.ts +9 -1
- package/dist/control/routes/sessions.js +84 -21
- package/dist/control/routes/status.d.ts +46 -1
- package/dist/control/routes/status.js +72 -15
- package/dist/control/routes/taskActions.js +24 -60
- package/dist/control/routes/tasks.d.ts +4 -0
- package/dist/control/routes/tasks.js +61 -62
- package/dist/control/routes/workspace.js +50 -23
- package/dist/control/runtime.d.ts +3 -2
- package/dist/control/runtime.js +63 -30
- package/dist/control/server.js +36 -365
- package/dist/control/shared.d.ts +25 -1
- package/dist/control/shared.js +64 -11
- package/dist/direct/directAudit.js +234 -225
- package/dist/direct/directGuards.d.ts +2 -1
- package/dist/direct/directGuards.js +42 -29
- package/dist/direct/directPatch.d.ts +6 -1
- package/dist/direct/directPatch.js +55 -8
- package/dist/direct/directSessionStore.d.ts +16 -6
- package/dist/direct/directSessionStore.js +127 -35
- package/dist/direct/directVerification.js +8 -16
- package/dist/doctor.js +62 -52
- package/dist/goal/acceptanceEngine.d.ts +1 -1
- package/dist/goal/goalProgress.js +75 -69
- package/dist/goal/goalReport.js +5 -12
- package/dist/goal/goalStatus.d.ts +2 -0
- package/dist/goal/goalStatus.js +10 -3
- package/dist/goal/goalStore.d.ts +9 -2
- package/dist/goal/goalStore.js +152 -45
- package/dist/goal/handoffExport.js +6 -6
- package/dist/goal/specKitImport.js +97 -96
- package/dist/goal/subgoalSync.js +11 -9
- package/dist/goal/worktreeManager.d.ts +2 -0
- package/dist/goal/worktreeManager.js +209 -136
- package/dist/httpServer.js +111 -32
- package/dist/logging.js +5 -3
- package/dist/policy/projectPolicy.js +5 -2
- package/dist/release/releaseGate.d.ts +1 -1
- package/dist/release/releaseGate.js +78 -25
- package/dist/runner/agentInvocation.d.ts +14 -0
- package/dist/runner/agentInvocation.js +96 -4
- package/dist/runner/changeCapture.d.ts +13 -5
- package/dist/runner/changeCapture.js +243 -61
- package/dist/runner/postTaskCleanup.js +48 -19
- package/dist/runner/processSecurity.d.ts +56 -0
- package/dist/runner/processSecurity.js +373 -0
- package/dist/runner/runTask.d.ts +1 -1
- package/dist/runner/runTask.js +115 -87
- package/dist/runner/simpleProcess.d.ts +3 -0
- package/dist/runner/simpleProcess.js +50 -36
- package/dist/{taskProgress.d.ts → runner/taskProgress.d.ts} +1 -1
- package/dist/{taskProgress.js → runner/taskProgress.js} +2 -2
- package/dist/{taskRuntime.d.ts → runner/taskRuntime.d.ts} +1 -1
- package/dist/{taskRuntime.js → runner/taskRuntime.js} +3 -2
- package/dist/runner/taskStatusStore.d.ts +11 -0
- package/dist/runner/taskStatusStore.js +24 -0
- package/dist/runner/watch.d.ts +14 -1
- package/dist/runner/watch.js +256 -48
- package/dist/security/contentRedaction.js +2 -2
- package/dist/security/discoveryTokenStore.d.ts +3 -2
- package/dist/security/discoveryTokenStore.js +17 -2
- package/dist/security/loopbackHost.d.ts +2 -0
- package/dist/security/loopbackHost.js +16 -0
- package/dist/security/pathGuard.js +32 -17
- package/dist/security/planGuard.js +2 -1
- package/dist/security/runtimeGuard.js +9 -6
- package/dist/security/secretComparison.d.ts +3 -0
- package/dist/security/secretComparison.js +11 -0
- package/dist/security/sensitiveGuard.d.ts +5 -0
- package/dist/security/sensitiveGuard.js +27 -8
- package/dist/security/toolInvocationGuard.d.ts +2 -2
- package/dist/security/toolInvocationGuard.js +1 -1
- package/dist/security/workspaceRootGuard.d.ts +8 -0
- package/dist/security/workspaceRootGuard.js +47 -0
- package/dist/smoke-test.js +24 -23
- package/dist/tools/catalog/index.d.ts +4 -0
- package/dist/tools/catalog/index.js +4 -0
- package/dist/tools/{toolCatalog.js → catalog/toolCatalog.js} +3 -12
- package/dist/tools/{toolRegistry.d.ts → catalog/toolRegistry.d.ts} +3 -3
- package/dist/tools/{toolRegistry.js → catalog/toolRegistry.js} +6 -15
- package/dist/tools/definitions/toolDefs.d.ts +18 -0
- package/dist/tools/definitions/toolDefs.js +1177 -0
- package/dist/tools/{auditSession.d.ts → diagnostics/auditSession.d.ts} +1 -1
- package/dist/tools/{auditSession.js → diagnostics/auditSession.js} +2 -2
- package/dist/tools/{auditTask.d.ts → diagnostics/auditTask.d.ts} +1 -1
- package/dist/tools/{auditTask.js → diagnostics/auditTask.js} +99 -29
- package/dist/tools/{healthCheck.d.ts → diagnostics/healthCheck.d.ts} +6 -6
- package/dist/tools/{healthCheck.js → diagnostics/healthCheck.js} +7 -7
- package/dist/tools/diagnostics/index.d.ts +6 -0
- package/dist/tools/diagnostics/index.js +6 -0
- package/dist/tools/{safeStatus.d.ts → diagnostics/safeStatus.d.ts} +3 -3
- package/dist/tools/{safeStatus.js → diagnostics/safeStatus.js} +6 -6
- package/dist/tools/{safeViews.d.ts → diagnostics/safeViews.d.ts} +12 -8
- package/dist/tools/{safeViews.js → diagnostics/safeViews.js} +23 -13
- package/dist/tools/{schemaDriftCheck.d.ts → diagnostics/schemaDriftCheck.d.ts} +1 -1
- package/dist/tools/{schemaDriftCheck.js → diagnostics/schemaDriftCheck.js} +2 -2
- package/dist/tools/{createDirectSession.js → direct/createDirectSession.js} +6 -6
- package/dist/tools/{finalizeDirectSession.d.ts → direct/finalizeDirectSession.d.ts} +1 -1
- package/dist/tools/direct/finalizeDirectSession.js +86 -0
- package/dist/tools/direct/index.d.ts +3 -0
- package/dist/tools/direct/index.js +3 -0
- package/dist/tools/{runDirectVerificationBundle.js → direct/runDirectVerificationBundle.js} +2 -2
- package/dist/tools/{discoverTools.d.ts → discovery/discoverTools.d.ts} +2 -2
- package/dist/tools/{discoverTools.js → discovery/discoverTools.js} +5 -5
- package/dist/tools/{explainTool.d.ts → discovery/explainTool.d.ts} +2 -2
- package/dist/tools/{explainTool.js → discovery/explainTool.js} +2 -2
- package/dist/tools/discovery/index.d.ts +3 -0
- package/dist/tools/discovery/index.js +3 -0
- package/dist/tools/{invokeDiscoveredTool.d.ts → discovery/invokeDiscoveredTool.d.ts} +2 -2
- package/dist/tools/{invokeDiscoveredTool.js → discovery/invokeDiscoveredTool.js} +5 -5
- package/dist/tools/dispatch/coreDispatch.js +32 -31
- package/dist/tools/dispatch/diagnosticDispatch.d.ts +3 -5
- package/dist/tools/dispatch/diagnosticDispatch.js +70 -68
- package/dist/tools/dispatch/directDispatch.js +11 -10
- package/dist/tools/dispatch/goalDispatch.js +5 -4
- package/dist/tools/dispatch/releaseDispatch.js +3 -2
- package/dist/tools/dispatch/validation.d.ts +7 -0
- package/dist/tools/dispatch/validation.js +71 -0
- package/dist/tools/{getPlan.js → goals/getPlan.js} +4 -4
- package/dist/tools/{goalSubgoalTask.d.ts → goals/goalSubgoalTask.d.ts} +1 -1
- package/dist/tools/goals/goalSubgoalTask.js +118 -0
- package/dist/tools/goals/index.d.ts +3 -0
- package/dist/tools/goals/index.js +3 -0
- package/dist/tools/{savePlan.js → goals/savePlan.js} +28 -8
- package/dist/tools/registry.d.ts +7 -11
- package/dist/tools/registry.js +17 -1178
- package/dist/tools/{checkReleaseGate.d.ts → release/checkReleaseGate.d.ts} +1 -1
- package/dist/tools/{checkReleaseGate.js → release/checkReleaseGate.js} +3 -3
- package/dist/tools/release/index.d.ts +2 -0
- package/dist/tools/release/index.js +2 -0
- package/dist/tools/{releaseMode.d.ts → release/releaseMode.d.ts} +2 -2
- package/dist/tools/{releaseMode.js → release/releaseMode.js} +46 -26
- package/dist/tools/tasks/cancelTask.d.ts +11 -0
- package/dist/tools/tasks/cancelTask.js +85 -0
- package/dist/tools/{createTask.d.ts → tasks/createTask.d.ts} +10 -2
- package/dist/tools/{createTask.js → tasks/createTask.js} +42 -27
- package/dist/tools/{diagnoseTask.d.ts → tasks/diagnoseTask.d.ts} +1 -1
- package/dist/tools/{diagnoseTask.js → tasks/diagnoseTask.js} +8 -8
- package/dist/tools/{evidencePack.js → tasks/evidencePack.js} +34 -22
- package/dist/tools/{getTaskFile.js → tasks/getTaskFile.js} +4 -4
- package/dist/tools/{getTaskStatus.d.ts → tasks/getTaskStatus.d.ts} +6 -1
- package/dist/tools/{getTaskStatus.js → tasks/getTaskStatus.js} +5 -5
- package/dist/tools/{getTaskStdoutTail.js → tasks/getTaskStdoutTail.js} +13 -11
- package/dist/tools/{getTaskSummary.js → tasks/getTaskSummary.js} +33 -26
- package/dist/tools/tasks/index.d.ts +18 -0
- package/dist/tools/tasks/index.js +18 -0
- package/dist/tools/tasks/killTask.d.ts +1 -0
- package/dist/tools/{listTasks.d.ts → tasks/listTasks.d.ts} +1 -1
- package/dist/tools/{listTasks.js → tasks/listTasks.js} +3 -3
- package/dist/tools/{reconcileTasks.d.ts → tasks/reconcileTasks.d.ts} +1 -1
- package/dist/tools/{reconcileTasks.js → tasks/reconcileTasks.js} +64 -66
- package/dist/tools/tasks/retryTask.js +60 -0
- package/dist/tools/{runTaskLoop.d.ts → tasks/runTaskLoop.d.ts} +6 -6
- package/dist/tools/{runTaskLoop.js → tasks/runTaskLoop.js} +58 -30
- package/dist/tools/tasks/runVerification.js +38 -0
- package/dist/tools/{taskLineage.js → tasks/taskLineage.js} +8 -7
- package/dist/tools/{taskOutputs.js → tasks/taskOutputs.js} +17 -16
- package/dist/tools/{androidDoctor.js → workspace/androidDoctor.js} +16 -10
- package/dist/tools/{applyPatch.d.ts → workspace/applyPatch.d.ts} +1 -1
- package/dist/tools/workspace/applyPatch.js +53 -0
- package/dist/tools/{discardWorktree.js → workspace/discardWorktree.js} +3 -3
- package/dist/tools/workspace/index.d.ts +10 -0
- package/dist/tools/workspace/index.js +10 -0
- package/dist/tools/{listAgents.d.ts → workspace/listAgents.d.ts} +5 -0
- package/dist/tools/{listAgents.js → workspace/listAgents.js} +8 -4
- package/dist/tools/{listWorkspace.js → workspace/listWorkspace.js} +3 -3
- package/dist/tools/{mergeWorktree.js → workspace/mergeWorktree.js} +3 -3
- package/dist/tools/{readWorkspaceFile.js → workspace/readWorkspaceFile.js} +8 -8
- package/dist/tools/{recommendAgentForTask.js → workspace/recommendAgentForTask.js} +3 -3
- package/dist/tools/{searchWorkspace.js → workspace/searchWorkspace.js} +5 -5
- package/dist/tools/{syncFile.d.ts → workspace/syncFile.d.ts} +1 -1
- package/dist/tools/workspace/syncFile.js +134 -0
- package/dist/utils/atomicFile.d.ts +7 -0
- package/dist/utils/atomicFile.js +48 -0
- package/dist/utils/boundedFile.d.ts +8 -0
- package/dist/utils/boundedFile.js +93 -0
- package/dist/utils/lockedJsonFile.d.ts +15 -0
- package/dist/utils/lockedJsonFile.js +224 -0
- package/dist/utils/platform.d.ts +11 -0
- package/dist/utils/platform.js +26 -0
- package/dist/utils/stableJson.d.ts +2 -0
- package/dist/utils/stableJson.js +12 -0
- package/dist/version.d.ts +3 -2
- package/dist/version.js +3 -2
- package/dist/watcherStatus.d.ts +2 -2
- package/dist/watcherStatus.js +18 -0
- package/docs/CODE_WIKI.md +808 -143
- package/docs/architecture.md +15 -55
- package/docs/assets/patchwarden-desktop-onboarding.png +0 -0
- package/docs/control-center/README.md +4 -4
- package/docs/dashboard-overview.md +4 -4
- package/docs/desktop-app.md +175 -0
- package/docs/evidence-pack-schema.md +3 -3
- package/docs/open-source-application.md +17 -11
- package/docs/release-checklist.md +1 -1
- package/docs/release-evidence.md +39 -29
- package/docs/task-safe-review-workflow.md +5 -5
- package/docs/threat-model.md +19 -1
- package/examples/config.example.json +4 -2
- package/package.json +27 -10
- package/scripts/README.md +15 -6
- package/scripts/build.js +11 -0
- package/scripts/checks/brand-check.js +3 -0
- package/scripts/checks/build-output-check.js +37 -0
- package/scripts/checks/control-center-smoke.js +91 -6
- package/scripts/checks/control-smoke.js +19 -8
- package/scripts/checks/http-mcp-smoke.js +82 -4
- package/scripts/checks/lifecycle-smoke.js +10 -10
- package/scripts/checks/mcp-manifest-check.js +1 -1
- package/scripts/checks/mcp-smoke.js +27 -3
- package/scripts/checks/package-manifest-check.js +20 -7
- package/scripts/checks/unit-tests.js +3 -5
- package/scripts/checks/watcher-supervisor-smoke.js +9 -0
- package/scripts/control/get-patchwarden-health.ps1 +1 -1
- package/scripts/control/manage-patchwarden.ps1 +69 -4
- package/scripts/control/provision-patchwarden-tunnel.ps1 +127 -0
- package/scripts/control/run-background-supervisor.ps1 +41 -0
- package/scripts/control/start-patchwarden-tunnel.ps1 +126 -32
- package/{PatchWarden-Control-Tray.cmd → scripts/launchers/PatchWarden-Control-Tray.cmd} +3 -3
- package/scripts/launchers/PatchWarden-Control.cmd +6 -0
- package/{PatchWarden-Desktop.cmd → scripts/launchers/PatchWarden-Desktop.cmd} +2 -2
- package/scripts/launchers/Restart-PatchWarden-Control.cmd +6 -0
- package/{Stop-PatchWarden.cmd → scripts/launchers/Stop-PatchWarden.cmd} +2 -2
- package/scripts/lib/clean-generated-output.js +11 -0
- package/scripts/lib/file-discovery.js +12 -0
- package/scripts/release/desktop-preflight.js +211 -0
- package/scripts/release/pack-clean.js +59 -14
- package/src/assessments/agentAssessor.ts +26 -12
- package/src/assessments/assessmentStore.ts +52 -40
- package/src/config.ts +70 -13
- package/src/control/fileManager.ts +40 -0
- package/src/control/middleware/auth.ts +22 -4
- package/src/control/middleware/static.ts +16 -3
- package/src/control/routeTable.ts +301 -0
- package/src/control/routes/audit.ts +14 -6
- package/src/control/routes/evidence.ts +1 -1
- package/src/control/routes/lineage.ts +1 -1
- package/src/control/routes/process.ts +341 -40
- package/src/control/routes/sessions.ts +88 -25
- package/src/control/routes/status.ts +97 -17
- package/src/control/routes/taskActions.ts +24 -58
- package/src/control/routes/tasks.ts +66 -58
- package/src/control/routes/workspace.ts +56 -25
- package/src/control/runtime.ts +69 -33
- package/src/control/server.ts +36 -342
- package/src/control/shared.ts +77 -11
- package/src/direct/directAudit.ts +17 -9
- package/src/direct/directGuards.ts +72 -44
- package/src/direct/directPatch.ts +95 -8
- package/src/direct/directSessionStore.ts +211 -55
- package/src/direct/directVerification.ts +9 -23
- package/src/doctor.ts +73 -59
- package/src/goal/acceptanceEngine.ts +1 -1
- package/src/goal/goalProgress.ts +17 -13
- package/src/goal/goalReport.ts +5 -13
- package/src/goal/goalStatus.ts +19 -3
- package/src/goal/goalStore.ts +241 -54
- package/src/goal/handoffExport.ts +6 -6
- package/src/goal/specKitImport.ts +12 -12
- package/src/goal/subgoalSync.ts +11 -8
- package/src/goal/worktreeManager.ts +139 -22
- package/src/httpServer.ts +113 -30
- package/src/logging.ts +10 -3
- package/src/policy/projectPolicy.ts +5 -2
- package/src/release/releaseGate.ts +74 -27
- package/src/runner/agentInvocation.ts +126 -4
- package/src/runner/changeCapture.ts +291 -57
- package/src/runner/postTaskCleanup.ts +41 -17
- package/src/runner/processSecurity.ts +433 -0
- package/src/runner/runTask.ts +138 -91
- package/src/runner/simpleProcess.ts +66 -31
- package/src/{taskProgress.ts → runner/taskProgress.ts} +3 -3
- package/src/{taskRuntime.ts → runner/taskRuntime.ts} +4 -3
- package/src/runner/taskStatusStore.ts +54 -0
- package/src/runner/watch.ts +281 -51
- package/src/security/contentRedaction.ts +2 -2
- package/src/security/discoveryTokenStore.ts +18 -4
- package/src/security/loopbackHost.ts +19 -0
- package/src/security/pathGuard.ts +35 -16
- package/src/security/planGuard.ts +2 -1
- package/src/security/runtimeGuard.ts +9 -6
- package/src/security/secretComparison.ts +13 -0
- package/src/security/sensitiveGuard.ts +27 -8
- package/src/security/toolInvocationGuard.ts +2 -2
- package/src/security/workspaceRootGuard.ts +53 -0
- package/src/smoke-test.ts +25 -24
- package/src/tools/catalog/index.ts +4 -0
- package/src/tools/{toolCatalog.ts → catalog/toolCatalog.ts} +3 -12
- package/src/tools/{toolRegistry.ts → catalog/toolRegistry.ts} +7 -16
- package/src/tools/definitions/toolDefs.ts +1264 -0
- package/src/tools/{auditSession.ts → diagnostics/auditSession.ts} +2 -2
- package/src/tools/{auditTask.ts → diagnostics/auditTask.ts} +99 -32
- package/src/tools/{healthCheck.ts → diagnostics/healthCheck.ts} +8 -8
- package/src/tools/diagnostics/index.ts +6 -0
- package/src/tools/{safeStatus.ts → diagnostics/safeStatus.ts} +7 -7
- package/src/tools/{safeViews.ts → diagnostics/safeViews.ts} +27 -17
- package/src/tools/{schemaDriftCheck.ts → diagnostics/schemaDriftCheck.ts} +3 -3
- package/src/tools/{createDirectSession.ts → direct/createDirectSession.ts} +6 -6
- package/src/tools/{finalizeDirectSession.ts → direct/finalizeDirectSession.ts} +7 -5
- package/src/tools/direct/index.ts +3 -0
- package/src/tools/{runDirectVerificationBundle.ts → direct/runDirectVerificationBundle.ts} +2 -2
- package/src/tools/{discoverTools.ts → discovery/discoverTools.ts} +6 -6
- package/src/tools/{explainTool.ts → discovery/explainTool.ts} +3 -3
- package/src/tools/discovery/index.ts +3 -0
- package/src/tools/{invokeDiscoveredTool.ts → discovery/invokeDiscoveredTool.ts} +7 -7
- package/src/tools/dispatch/coreDispatch.ts +36 -31
- package/src/tools/dispatch/diagnosticDispatch.ts +26 -21
- package/src/tools/dispatch/directDispatch.ts +11 -10
- package/src/tools/dispatch/goalDispatch.ts +5 -4
- package/src/tools/dispatch/releaseDispatch.ts +3 -2
- package/src/tools/dispatch/validation.ts +78 -0
- package/src/tools/{getPlan.ts → goals/getPlan.ts} +4 -4
- package/src/tools/{goalSubgoalTask.ts → goals/goalSubgoalTask.ts} +41 -26
- package/src/tools/goals/index.ts +3 -0
- package/src/tools/{savePlan.ts → goals/savePlan.ts} +38 -8
- package/src/tools/registry.ts +17 -1261
- package/src/tools/{checkReleaseGate.ts → release/checkReleaseGate.ts} +3 -3
- package/src/tools/release/index.ts +2 -0
- package/src/tools/{releaseMode.ts → release/releaseMode.ts} +42 -24
- package/src/tools/tasks/cancelTask.ts +109 -0
- package/src/tools/{createTask.ts → tasks/createTask.ts} +49 -26
- package/src/tools/{diagnoseTask.ts → tasks/diagnoseTask.ts} +8 -8
- package/src/tools/{evidencePack.ts → tasks/evidencePack.ts} +36 -22
- package/src/tools/{getTaskFile.ts → tasks/getTaskFile.ts} +4 -4
- package/src/tools/{getTaskStatus.ts → tasks/getTaskStatus.ts} +7 -5
- package/src/tools/{getTaskStdoutTail.ts → tasks/getTaskStdoutTail.ts} +13 -11
- package/src/tools/{getTaskSummary.ts → tasks/getTaskSummary.ts} +44 -33
- package/src/tools/tasks/index.ts +18 -0
- package/src/tools/{listTasks.ts → tasks/listTasks.ts} +4 -4
- package/src/tools/{reconcileTasks.ts → tasks/reconcileTasks.ts} +68 -66
- package/src/tools/tasks/retryTask.ts +69 -0
- package/src/tools/{runTaskLoop.ts → tasks/runTaskLoop.ts} +69 -34
- package/src/tools/{runVerification.ts → tasks/runVerification.ts} +6 -3
- package/src/tools/{taskLineage.ts → tasks/taskLineage.ts} +8 -7
- package/src/tools/{taskOutputs.ts → tasks/taskOutputs.ts} +27 -20
- package/src/tools/{androidDoctor.ts → workspace/androidDoctor.ts} +15 -10
- package/src/tools/{applyPatch.ts → workspace/applyPatch.ts} +38 -16
- package/src/tools/{discardWorktree.ts → workspace/discardWorktree.ts} +3 -3
- package/src/tools/workspace/index.ts +10 -0
- package/src/tools/{listAgents.ts → workspace/listAgents.ts} +11 -4
- package/src/tools/{listWorkspace.ts → workspace/listWorkspace.ts} +3 -3
- package/src/tools/{mergeWorktree.ts → workspace/mergeWorktree.ts} +3 -3
- package/src/tools/{readWorkspaceFile.ts → workspace/readWorkspaceFile.ts} +8 -8
- package/src/tools/{recommendAgentForTask.ts → workspace/recommendAgentForTask.ts} +3 -3
- package/src/tools/{searchWorkspace.ts → workspace/searchWorkspace.ts} +5 -5
- package/src/tools/workspace/syncFile.ts +292 -0
- package/src/utils/atomicFile.ts +58 -0
- package/src/utils/boundedFile.ts +116 -0
- package/src/utils/lockedJsonFile.ts +251 -0
- package/src/utils/platform.ts +31 -0
- package/src/utils/stableJson.ts +12 -0
- package/src/version.ts +3 -2
- package/src/watcherStatus.ts +19 -1
- package/ui/desktop-bootstrap.js +11 -0
- package/ui/desktop-bridge.js +36 -0
- package/ui/desktop.css +160 -0
- package/ui/getting-started.js +93 -0
- package/ui/i18n.js +484 -0
- package/ui/log-parser.js +30 -0
- package/ui/pages/audit.html +12 -0
- package/ui/pages/dashboard.html +90 -147
- package/ui/pages/direct-sessions.html +123 -26
- package/ui/pages/getting-started.html +43 -0
- package/ui/pages/logs.html +105 -22
- package/ui/pages/settings.html +101 -0
- package/ui/pages/task-detail.html +12 -0
- package/ui/pages/tasks.html +54 -8
- package/ui/pages/workspace.html +38 -1
- package/ui/settings.js +245 -0
- package/PatchWarden-Control.cmd +0 -6
- package/Restart-PatchWarden-Control.cmd +0 -6
- package/dist/test/unit/acceptance-engine.test.d.ts +0 -1
- package/dist/test/unit/acceptance-engine.test.js +0 -228
- package/dist/test/unit/agent-router.test.d.ts +0 -1
- package/dist/test/unit/agent-router.test.js +0 -287
- package/dist/test/unit/android-doctor.test.d.ts +0 -1
- package/dist/test/unit/android-doctor.test.js +0 -118
- package/dist/test/unit/apply-patch.test.d.ts +0 -1
- package/dist/test/unit/apply-patch.test.js +0 -225
- package/dist/test/unit/audit-checks.test.d.ts +0 -1
- package/dist/test/unit/audit-checks.test.js +0 -350
- package/dist/test/unit/chinese-path.test.d.ts +0 -1
- package/dist/test/unit/chinese-path.test.js +0 -91
- package/dist/test/unit/command-guard.test.d.ts +0 -1
- package/dist/test/unit/command-guard.test.js +0 -160
- package/dist/test/unit/create-task.test.d.ts +0 -1
- package/dist/test/unit/create-task.test.js +0 -197
- package/dist/test/unit/diagnose-task.test.d.ts +0 -1
- package/dist/test/unit/diagnose-task.test.js +0 -457
- package/dist/test/unit/direct-guards.test.d.ts +0 -1
- package/dist/test/unit/direct-guards.test.js +0 -328
- package/dist/test/unit/discovery-token-store.test.d.ts +0 -1
- package/dist/test/unit/discovery-token-store.test.js +0 -139
- package/dist/test/unit/evidence-pack.test.d.ts +0 -1
- package/dist/test/unit/evidence-pack.test.js +0 -224
- package/dist/test/unit/get-task-status.test.d.ts +0 -1
- package/dist/test/unit/get-task-status.test.js +0 -174
- package/dist/test/unit/get-task-summary.test.d.ts +0 -1
- package/dist/test/unit/get-task-summary.test.js +0 -146
- package/dist/test/unit/goal-graph.test.d.ts +0 -1
- package/dist/test/unit/goal-graph.test.js +0 -298
- package/dist/test/unit/goal-progress.test.d.ts +0 -1
- package/dist/test/unit/goal-progress.test.js +0 -381
- package/dist/test/unit/goal-report.test.d.ts +0 -1
- package/dist/test/unit/goal-report.test.js +0 -159
- package/dist/test/unit/goal-status.test.d.ts +0 -1
- package/dist/test/unit/goal-status.test.js +0 -215
- package/dist/test/unit/goal-store.test.d.ts +0 -1
- package/dist/test/unit/goal-store.test.js +0 -253
- package/dist/test/unit/goal-subgoal-task.test.d.ts +0 -1
- package/dist/test/unit/goal-subgoal-task.test.js +0 -55
- package/dist/test/unit/goal-tools-registry.test.d.ts +0 -1
- package/dist/test/unit/goal-tools-registry.test.js +0 -192
- package/dist/test/unit/handoff-export.test.d.ts +0 -1
- package/dist/test/unit/handoff-export.test.js +0 -263
- package/dist/test/unit/invoke-discovered-tool.test.d.ts +0 -1
- package/dist/test/unit/invoke-discovered-tool.test.js +0 -167
- package/dist/test/unit/logging.test.d.ts +0 -1
- package/dist/test/unit/logging.test.js +0 -397
- package/dist/test/unit/path-guard.test.d.ts +0 -1
- package/dist/test/unit/path-guard.test.js +0 -133
- package/dist/test/unit/post-task-cleanup.test.d.ts +0 -1
- package/dist/test/unit/post-task-cleanup.test.js +0 -48
- package/dist/test/unit/project-policy-release-mode.test.d.ts +0 -1
- package/dist/test/unit/project-policy-release-mode.test.js +0 -125
- package/dist/test/unit/reconcile-tasks.test.d.ts +0 -1
- package/dist/test/unit/reconcile-tasks.test.js +0 -456
- package/dist/test/unit/release-gate.test.d.ts +0 -1
- package/dist/test/unit/release-gate.test.js +0 -242
- package/dist/test/unit/run-task-loop.test.d.ts +0 -1
- package/dist/test/unit/run-task-loop.test.js +0 -380
- package/dist/test/unit/safe-status.test.d.ts +0 -1
- package/dist/test/unit/safe-status.test.js +0 -165
- package/dist/test/unit/safe-views.test.d.ts +0 -1
- package/dist/test/unit/safe-views.test.js +0 -171
- package/dist/test/unit/schema-drift-check.test.d.ts +0 -1
- package/dist/test/unit/schema-drift-check.test.js +0 -176
- package/dist/test/unit/sensitive-guard.test.d.ts +0 -1
- package/dist/test/unit/sensitive-guard.test.js +0 -104
- package/dist/test/unit/spec-kit-import.test.d.ts +0 -1
- package/dist/test/unit/spec-kit-import.test.js +0 -341
- package/dist/test/unit/subgoal-sync.test.d.ts +0 -1
- package/dist/test/unit/subgoal-sync.test.js +0 -183
- package/dist/test/unit/sync-file.test.d.ts +0 -1
- package/dist/test/unit/sync-file.test.js +0 -154
- package/dist/test/unit/tool-invocation-guard.test.d.ts +0 -1
- package/dist/test/unit/tool-invocation-guard.test.js +0 -432
- package/dist/test/unit/tool-usage-stats.test.d.ts +0 -1
- package/dist/test/unit/tool-usage-stats.test.js +0 -300
- package/dist/test/unit/toolSearch.test.d.ts +0 -1
- package/dist/test/unit/toolSearch.test.js +0 -571
- package/dist/test/unit/wait-for-task.test.d.ts +0 -1
- package/dist/test/unit/wait-for-task.test.js +0 -144
- package/dist/test/unit/watcher-status.test.d.ts +0 -1
- package/dist/test/unit/watcher-status.test.js +0 -169
- package/dist/test/unit/worktree-manager.test.d.ts +0 -1
- package/dist/test/unit/worktree-manager.test.js +0 -176
- package/dist/tools/applyPatch.js +0 -41
- package/dist/tools/cancelTask.d.ts +0 -30
- package/dist/tools/cancelTask.js +0 -64
- package/dist/tools/finalizeDirectSession.js +0 -84
- package/dist/tools/goalSubgoalTask.js +0 -110
- package/dist/tools/killTask.d.ts +0 -15
- package/dist/tools/retryTask.js +0 -45
- package/dist/tools/runVerification.js +0 -36
- package/dist/tools/syncFile.js +0 -65
- package/docs/control-center/control-center-daily-driver.md +0 -211
- package/docs/control-center/control-center-mvp.md +0 -205
- package/docs/control-center/control-center-phase2.md +0 -159
- package/docs/execution-plan-2026-07-09.md +0 -315
- package/docs/release-v0.6.0.md +0 -71
- package/docs/release-v0.6.1.md +0 -75
- package/docs/release-v0.6.4.md +0 -45
- package/docs/roadmap-execution-and-acceptance.md +0 -365
- package/src/test/unit/acceptance-engine.test.ts +0 -261
- package/src/test/unit/agent-router.test.ts +0 -342
- package/src/test/unit/android-doctor.test.ts +0 -158
- package/src/test/unit/apply-patch.test.ts +0 -293
- package/src/test/unit/audit-checks.test.ts +0 -567
- package/src/test/unit/chinese-path.test.ts +0 -106
- package/src/test/unit/command-guard.test.ts +0 -221
- package/src/test/unit/create-task.test.ts +0 -255
- package/src/test/unit/diagnose-task.test.ts +0 -544
- package/src/test/unit/direct-guards.test.ts +0 -467
- package/src/test/unit/discovery-token-store.test.ts +0 -181
- package/src/test/unit/evidence-pack.test.ts +0 -251
- package/src/test/unit/get-task-status.test.ts +0 -203
- package/src/test/unit/get-task-summary.test.ts +0 -173
- package/src/test/unit/goal-graph.test.ts +0 -347
- package/src/test/unit/goal-progress.test.ts +0 -538
- package/src/test/unit/goal-report.test.ts +0 -189
- package/src/test/unit/goal-status.test.ts +0 -270
- package/src/test/unit/goal-store.test.ts +0 -318
- package/src/test/unit/goal-subgoal-task.test.ts +0 -72
- package/src/test/unit/goal-tools-registry.test.ts +0 -245
- package/src/test/unit/handoff-export.test.ts +0 -295
- package/src/test/unit/invoke-discovered-tool.test.ts +0 -216
- package/src/test/unit/logging.test.ts +0 -497
- package/src/test/unit/path-guard.test.ts +0 -180
- package/src/test/unit/post-task-cleanup.test.ts +0 -53
- package/src/test/unit/project-policy-release-mode.test.ts +0 -156
- package/src/test/unit/reconcile-tasks.test.ts +0 -551
- package/src/test/unit/release-gate.test.ts +0 -314
- package/src/test/unit/run-task-loop.test.ts +0 -425
- package/src/test/unit/safe-status.test.ts +0 -187
- package/src/test/unit/safe-views.test.ts +0 -184
- package/src/test/unit/schema-drift-check.test.ts +0 -260
- package/src/test/unit/sensitive-guard.test.ts +0 -124
- package/src/test/unit/spec-kit-import.test.ts +0 -429
- package/src/test/unit/subgoal-sync.test.ts +0 -236
- package/src/test/unit/sync-file.test.ts +0 -231
- package/src/test/unit/tool-invocation-guard.test.ts +0 -542
- package/src/test/unit/tool-usage-stats.test.ts +0 -384
- package/src/test/unit/toolSearch.test.ts +0 -756
- package/src/test/unit/wait-for-task.test.ts +0 -176
- package/src/test/unit/watcher-status.test.ts +0 -190
- package/src/test/unit/worktree-manager.test.ts +0 -247
- package/src/tools/cancelTask.ts +0 -70
- package/src/tools/retryTask.ts +0 -53
- package/src/tools/syncFile.ts +0 -122
- /package/dist/tools/{toolCatalog.d.ts → catalog/toolCatalog.d.ts} +0 -0
- /package/dist/tools/{toolSearch.d.ts → catalog/toolSearch.d.ts} +0 -0
- /package/dist/tools/{toolSearch.js → catalog/toolSearch.js} +0 -0
- /package/dist/tools/{toolUsageStats.d.ts → catalog/toolUsageStats.d.ts} +0 -0
- /package/dist/tools/{toolUsageStats.js → catalog/toolUsageStats.js} +0 -0
- /package/dist/tools/{createDirectSession.d.ts → direct/createDirectSession.d.ts} +0 -0
- /package/dist/tools/{runDirectVerificationBundle.d.ts → direct/runDirectVerificationBundle.d.ts} +0 -0
- /package/dist/tools/{getPlan.d.ts → goals/getPlan.d.ts} +0 -0
- /package/dist/tools/{savePlan.d.ts → goals/savePlan.d.ts} +0 -0
- /package/dist/tools/{evidencePack.d.ts → tasks/evidencePack.d.ts} +0 -0
- /package/dist/tools/{getTaskFile.d.ts → tasks/getTaskFile.d.ts} +0 -0
- /package/dist/tools/{getTaskProgress.d.ts → tasks/getTaskProgress.d.ts} +0 -0
- /package/dist/tools/{getTaskProgress.js → tasks/getTaskProgress.js} +0 -0
- /package/dist/tools/{getTaskStdoutTail.d.ts → tasks/getTaskStdoutTail.d.ts} +0 -0
- /package/dist/tools/{getTaskSummary.d.ts → tasks/getTaskSummary.d.ts} +0 -0
- /package/dist/tools/{killTask.js → tasks/killTask.js} +0 -0
- /package/dist/tools/{retryTask.d.ts → tasks/retryTask.d.ts} +0 -0
- /package/dist/tools/{runVerification.d.ts → tasks/runVerification.d.ts} +0 -0
- /package/dist/tools/{taskLineage.d.ts → tasks/taskLineage.d.ts} +0 -0
- /package/dist/tools/{taskOutputs.d.ts → tasks/taskOutputs.d.ts} +0 -0
- /package/dist/tools/{waitForTask.d.ts → tasks/waitForTask.d.ts} +0 -0
- /package/dist/tools/{waitForTask.js → tasks/waitForTask.js} +0 -0
- /package/dist/tools/{androidDoctor.d.ts → workspace/androidDoctor.d.ts} +0 -0
- /package/dist/tools/{discardWorktree.d.ts → workspace/discardWorktree.d.ts} +0 -0
- /package/dist/tools/{listWorkspace.d.ts → workspace/listWorkspace.d.ts} +0 -0
- /package/dist/tools/{mergeWorktree.d.ts → workspace/mergeWorktree.d.ts} +0 -0
- /package/dist/tools/{readWorkspaceFile.d.ts → workspace/readWorkspaceFile.d.ts} +0 -0
- /package/dist/tools/{recommendAgentForTask.d.ts → workspace/recommendAgentForTask.d.ts} +0 -0
- /package/dist/tools/{searchWorkspace.d.ts → workspace/searchWorkspace.d.ts} +0 -0
- /package/src/tools/{toolSearch.ts → catalog/toolSearch.ts} +0 -0
- /package/src/tools/{toolUsageStats.ts → catalog/toolUsageStats.ts} +0 -0
- /package/src/tools/{getTaskProgress.ts → tasks/getTaskProgress.ts} +0 -0
- /package/src/tools/{killTask.ts → tasks/killTask.ts} +0 -0
- /package/src/tools/{waitForTask.ts → tasks/waitForTask.ts} +0 -0
|
@@ -20,13 +20,13 @@ const RULES = [
|
|
|
20
20
|
{
|
|
21
21
|
category: "credential_assignment",
|
|
22
22
|
reason: "matched credential assignment",
|
|
23
|
-
pattern: /\b((?:access[_ -]?token|api[_ -]?key|secret|password|credential|token)\s*[:=]\s*)(["']?)([^\s,"'\]}]{8,})(["']?)/gi,
|
|
23
|
+
pattern: /\b((?:(?:[A-Za-z][A-Za-z0-9]*[_ -])*(?:access[_ -]?token|refresh[_ -]?token|api[_ -]?key|secret(?:[_ -]?access)?[_ -]?key|client[_ -]?secret|private[_ -]?key|secret|password|credential|token))\s*[:=]\s*)(["']?)([^\s,"'\]}]{8,})(["']?)/gi,
|
|
24
24
|
replace: "$1$2[REDACTED]$4",
|
|
25
25
|
},
|
|
26
26
|
{
|
|
27
27
|
category: "known_token_format",
|
|
28
28
|
reason: "matched known token format",
|
|
29
|
-
pattern: /\b(?:sk|
|
|
29
|
+
pattern: /\b(?:sk-(?:proj-|ant-)?[A-Za-z0-9_-]{16,}|sk_[A-Za-z0-9_-]{16,}|gh[pousr]_[A-Za-z0-9_-]{16,}|github_pat_[A-Za-z0-9_-]{16,}|glpat-[A-Za-z0-9_-]{16,}|xox[baprs]-[A-Za-z0-9-]{16,}|(?:hf|npm)_[A-Za-z0-9_-]{16,}|(?:sk|rk)_live_[A-Za-z0-9_-]{16,}|AKIA[0-9A-Z]{16}|AIza[0-9A-Za-z_-]{35}|eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,})\b/g,
|
|
30
30
|
replace: "[REDACTED TOKEN]",
|
|
31
31
|
},
|
|
32
32
|
];
|
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
* - 不持久化到磁盘(内存 Map,进程内共享,单例)
|
|
10
10
|
* - token 单次使用语义(consumeToken 后即删除)
|
|
11
11
|
*/
|
|
12
|
-
import type { ToolRisk } from "../tools/toolRegistry.js";
|
|
13
|
-
import type { ToolProfile } from "../tools/toolCatalog.js";
|
|
12
|
+
import type { ToolRisk } from "../tools/catalog/toolRegistry.js";
|
|
13
|
+
import type { ToolProfile } from "../tools/catalog/toolCatalog.js";
|
|
14
14
|
export interface DiscoveryTokenRecord {
|
|
15
15
|
token: string;
|
|
16
16
|
toolName: string;
|
|
@@ -31,6 +31,7 @@ export interface IssueTokenInput {
|
|
|
31
31
|
allowedScope?: string[];
|
|
32
32
|
ttlMs?: number;
|
|
33
33
|
}
|
|
34
|
+
export declare const MAX_ACTIVE_DISCOVERY_TOKENS = 1024;
|
|
34
35
|
/**
|
|
35
36
|
* 生成并保存 discovery token,返回 token id。
|
|
36
37
|
* token id 格式:dst_{YYYYMMDD}_{randomHex12}
|
|
@@ -13,6 +13,7 @@ import { randomBytes } from "node:crypto";
|
|
|
13
13
|
import { PatchWardenError } from "../errors.js";
|
|
14
14
|
// ── 常量 ──────────────────────────────────────────────────────────
|
|
15
15
|
const DEFAULT_TTL_MS = 10 * 60 * 1000; // 10 分钟
|
|
16
|
+
export const MAX_ACTIVE_DISCOVERY_TOKENS = 1024;
|
|
16
17
|
// ── 模块级 token store(单例,进程内共享) ───────────────────────
|
|
17
18
|
const tokenStore = new Map();
|
|
18
19
|
// ── 内部工具 ──────────────────────────────────────────────────────
|
|
@@ -27,15 +28,29 @@ function generateTokenId() {
|
|
|
27
28
|
const randomPart = randomBytes(6).toString("hex"); // 12 位 hex
|
|
28
29
|
return `dst_${datePart}_${randomPart}`;
|
|
29
30
|
}
|
|
31
|
+
function pruneTokenStore(nowMs) {
|
|
32
|
+
for (const [token, record] of tokenStore) {
|
|
33
|
+
if (Date.parse(record.expiresAt) <= nowMs)
|
|
34
|
+
tokenStore.delete(token);
|
|
35
|
+
}
|
|
36
|
+
while (tokenStore.size >= MAX_ACTIVE_DISCOVERY_TOKENS) {
|
|
37
|
+
const oldest = tokenStore.keys().next().value;
|
|
38
|
+
if (!oldest)
|
|
39
|
+
break;
|
|
40
|
+
tokenStore.delete(oldest);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
30
43
|
// ── 公共 API ──────────────────────────────────────────────────────
|
|
31
44
|
/**
|
|
32
45
|
* 生成并保存 discovery token,返回 token id。
|
|
33
46
|
* token id 格式:dst_{YYYYMMDD}_{randomHex12}
|
|
34
47
|
*/
|
|
35
48
|
export function issueToken(input) {
|
|
36
|
-
const
|
|
49
|
+
const nowMs = Date.now();
|
|
50
|
+
pruneTokenStore(nowMs);
|
|
51
|
+
const now = new Date(nowMs);
|
|
37
52
|
const issuedAt = now.toISOString();
|
|
38
|
-
const expiresAt = new Date(
|
|
53
|
+
const expiresAt = new Date(nowMs + (input.ttlMs ?? DEFAULT_TTL_MS)).toISOString();
|
|
39
54
|
const token = generateTokenId();
|
|
40
55
|
const record = {
|
|
41
56
|
token,
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { firstHeaderValue } from "./secretComparison.js";
|
|
2
|
+
const LOOPBACK_HOST_RE = /^(127\.0\.0\.1|localhost)(?::([0-9]{1,5}))?$/i;
|
|
3
|
+
/** Validate an HTTP Host header for a service bound to IPv4 loopback. */
|
|
4
|
+
export function isTrustedLoopbackHostHeader(value, expectedPort) {
|
|
5
|
+
const raw = firstHeaderValue(value).trim();
|
|
6
|
+
const match = LOOPBACK_HOST_RE.exec(raw);
|
|
7
|
+
if (!match)
|
|
8
|
+
return false;
|
|
9
|
+
if (match[2] === undefined)
|
|
10
|
+
return true;
|
|
11
|
+
const declaredPort = Number(match[2]);
|
|
12
|
+
return Number.isInteger(declaredPort)
|
|
13
|
+
&& declaredPort >= 1
|
|
14
|
+
&& declaredPort <= 65_535
|
|
15
|
+
&& declaredPort === expectedPort;
|
|
16
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { basename, dirname, isAbsolute, normalize, parse, resolve, sep, } from "node:path";
|
|
2
|
-
import { realpathSync } from "node:fs";
|
|
2
|
+
import { lstatSync, readlinkSync, realpathSync } from "node:fs";
|
|
3
3
|
import { PatchWardenError } from "../errors.js";
|
|
4
4
|
export function guardPath(requestedPath, workspaceRoot, allowedPrefix) {
|
|
5
5
|
const ws = normalize(resolve(workspaceRoot));
|
|
@@ -55,7 +55,7 @@ function realPathOrSelf(value) {
|
|
|
55
55
|
return normalize(value);
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
|
-
function realPathOrExistingPrefix(candidate) {
|
|
58
|
+
function realPathOrExistingPrefix(candidate, seenLinks = new Set()) {
|
|
59
59
|
const normalized = normalize(candidate);
|
|
60
60
|
const root = parse(normalized).root;
|
|
61
61
|
const suffix = [];
|
|
@@ -64,7 +64,33 @@ function realPathOrExistingPrefix(candidate) {
|
|
|
64
64
|
try {
|
|
65
65
|
return resolve(realpathSync(current), ...suffix);
|
|
66
66
|
}
|
|
67
|
-
catch {
|
|
67
|
+
catch (error) {
|
|
68
|
+
try {
|
|
69
|
+
const metadata = lstatSync(current);
|
|
70
|
+
if (metadata.isSymbolicLink()) {
|
|
71
|
+
const linkKey = process.platform === "win32" ? current.toLowerCase() : current;
|
|
72
|
+
if (seenLinks.has(linkKey)) {
|
|
73
|
+
throw new PatchWardenError("workspace_path_escape", `Symbolic link cycle detected while resolving "${candidate}"`, "Remove the cyclic link and use a path inside the configured workspace.", true, { path: candidate, operation: "path_access", safe_alternative: "Use a non-link path inside workspaceRoot." });
|
|
74
|
+
}
|
|
75
|
+
const nextSeen = new Set(seenLinks);
|
|
76
|
+
nextSeen.add(linkKey);
|
|
77
|
+
const linkTarget = readlinkSync(current);
|
|
78
|
+
const resolvedTarget = isAbsolute(linkTarget)
|
|
79
|
+
? resolve(linkTarget)
|
|
80
|
+
: resolve(dirname(current), linkTarget);
|
|
81
|
+
return resolve(realPathOrExistingPrefix(resolvedTarget, nextSeen), ...suffix);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
catch (linkError) {
|
|
85
|
+
if (linkError instanceof PatchWardenError)
|
|
86
|
+
throw linkError;
|
|
87
|
+
const linkCode = linkError.code;
|
|
88
|
+
if (linkCode !== "ENOENT" && linkCode !== "ENOTDIR")
|
|
89
|
+
throw linkError;
|
|
90
|
+
}
|
|
91
|
+
const code = error.code;
|
|
92
|
+
if (code !== "ENOENT" && code !== "ENOTDIR")
|
|
93
|
+
throw error;
|
|
68
94
|
suffix.unshift(basename(current));
|
|
69
95
|
const parent = dirname(current);
|
|
70
96
|
if (parent === current)
|
|
@@ -72,24 +98,13 @@ function realPathOrExistingPrefix(candidate) {
|
|
|
72
98
|
current = parent;
|
|
73
99
|
}
|
|
74
100
|
}
|
|
75
|
-
|
|
76
|
-
return resolve(realpathSync(root || current), ...suffix);
|
|
77
|
-
}
|
|
78
|
-
catch {
|
|
79
|
-
return normalized;
|
|
80
|
-
}
|
|
101
|
+
return resolve(realpathSync(root || current), ...suffix);
|
|
81
102
|
}
|
|
82
103
|
function assertInside(candidate, root, requestedPath, message) {
|
|
83
104
|
const normalizedRoot = normalize(root);
|
|
84
105
|
const normalizedCandidate = normalize(candidate);
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
: normalizedRoot;
|
|
88
|
-
const checkCandidate = process.platform === "win32"
|
|
89
|
-
? normalizedCandidate.toLowerCase()
|
|
90
|
-
: normalizedCandidate;
|
|
91
|
-
if (checkCandidate !== checkRoot &&
|
|
92
|
-
!checkCandidate.startsWith(checkRoot + sep)) {
|
|
106
|
+
if (normalizedCandidate !== normalizedRoot &&
|
|
107
|
+
!normalizedCandidate.startsWith(normalizedRoot + sep)) {
|
|
93
108
|
if (message.startsWith("Path escapes workspace")) {
|
|
94
109
|
throw new PatchWardenError("workspace_path_escape", `Path escapes workspace: "${requestedPath}" resolves to "${normalizedCandidate}" which is outside "${normalizedRoot}"`, "Use a path inside the configured workspace and allowed prefix.", true, { path: requestedPath, operation: "path_access", safe_alternative: "Read a path inside the configured workspace and allowed prefix." });
|
|
95
110
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { PatchWardenError } from "../errors.js";
|
|
2
|
+
import { redactSensitiveContent } from "./contentRedaction.js";
|
|
2
3
|
const PLAN_RULES = [
|
|
3
4
|
{
|
|
4
5
|
id: "plan_secret_access",
|
|
@@ -35,7 +36,7 @@ export function guardPlanContent(title, content) {
|
|
|
35
36
|
throw new PatchWardenError(rule.id, `save_plan blocked content in category "${rule.category}".`, rule.suggestion, true, {
|
|
36
37
|
operation: "save_plan",
|
|
37
38
|
matched_category: rule.category,
|
|
38
|
-
matched_text: match[0].slice(0, 160),
|
|
39
|
+
matched_text: redactSensitiveContent(match[0]).content.slice(0, 160),
|
|
39
40
|
});
|
|
40
41
|
}
|
|
41
42
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { resolve, join, dirname } from "node:path";
|
|
2
2
|
import { fileURLToPath } from "node:url";
|
|
3
3
|
import { PatchWardenError } from "../errors.js";
|
|
4
|
+
import { isPathChildOf, isSamePath } from "../utils/platform.js";
|
|
4
5
|
const CRITICAL_RUNTIME_DIRS = ["dist", "src", "scripts", "release"];
|
|
5
6
|
/**
|
|
6
7
|
* Refuse operations against the active PatchWarden runtime directory or its
|
|
@@ -8,15 +9,17 @@ const CRITICAL_RUNTIME_DIRS = ["dist", "src", "scripts", "release"];
|
|
|
8
9
|
* and direct-session flows share the same protection.
|
|
9
10
|
*/
|
|
10
11
|
export function guardRuntimeSelfModification(resolvedRepoPath) {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
// runtimeGuard lives at <package-root>/{src,dist}/security/runtimeGuard.*.
|
|
13
|
+
const runtimeRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..", "..");
|
|
14
|
+
const isRuntimeRoot = isSamePath(resolvedRepoPath, runtimeRoot);
|
|
15
|
+
if (isRuntimeRoot ||
|
|
16
|
+
isPathChildOf(resolvedRepoPath, runtimeRoot)) {
|
|
14
17
|
const isCritical = CRITICAL_RUNTIME_DIRS.some((dir) => {
|
|
15
18
|
const full = join(runtimeRoot, dir);
|
|
16
|
-
return (resolvedRepoPath
|
|
17
|
-
resolvedRepoPath
|
|
19
|
+
return (isSamePath(resolvedRepoPath, full) ||
|
|
20
|
+
isPathChildOf(resolvedRepoPath, full));
|
|
18
21
|
});
|
|
19
|
-
if (
|
|
22
|
+
if (isRuntimeRoot || isCritical) {
|
|
20
23
|
throw new PatchWardenError("runtime_self_modification_blocked", `repo_path points to the active PatchWarden runtime or its critical subdirectories.`, "Use a dev copy or git worktree for PatchWarden development. The running MCP server must not be modified by a task.", true, {
|
|
21
24
|
operation: "runtime_guard",
|
|
22
25
|
path: resolvedRepoPath,
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { timingSafeEqual } from "node:crypto";
|
|
2
|
+
/** Compare secret text without content-dependent byte comparison. */
|
|
3
|
+
export function timingSafeStringEqual(actual, expected) {
|
|
4
|
+
const actualBytes = Buffer.from(actual, "utf-8");
|
|
5
|
+
const expectedBytes = Buffer.from(expected, "utf-8");
|
|
6
|
+
return actualBytes.length === expectedBytes.length
|
|
7
|
+
&& timingSafeEqual(actualBytes, expectedBytes);
|
|
8
|
+
}
|
|
9
|
+
export function firstHeaderValue(value) {
|
|
10
|
+
return Array.isArray(value) ? (value[0] || "") : (value || "");
|
|
11
|
+
}
|
|
@@ -1,2 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Detect a Windows NTFS alternate data stream suffix. The colon in an
|
|
3
|
+
* absolute drive prefix (for example, C:\\) is the only permitted colon.
|
|
4
|
+
*/
|
|
5
|
+
export declare function hasWindowsAlternateDataStream(filePath: string): boolean;
|
|
1
6
|
export declare function isSensitivePath(filePath: string): boolean;
|
|
2
7
|
export declare function guardSensitivePath(filePath: string): void;
|
|
@@ -7,22 +7,28 @@ const SENSITIVE_PATTERNS = [
|
|
|
7
7
|
// Exact filenames
|
|
8
8
|
/(?:^|[\\/])\.env$/i,
|
|
9
9
|
/(?:^|[\\/])\.env\..+$/i,
|
|
10
|
+
/(?:^|[\\/])\.envrc$/i,
|
|
10
11
|
/(?:^|[\\/])id_rsa$/i,
|
|
12
|
+
/(?:^|[\\/])id_dsa$/i,
|
|
11
13
|
/(?:^|[\\/])id_ed25519$/i,
|
|
12
14
|
/(?:^|[\\/])id_ecdsa$/i,
|
|
13
15
|
/(?:^|[\\/])\.ssh[\\/]/i,
|
|
14
16
|
// Credentials / tokens
|
|
15
|
-
/(?:^|[\\/])credentials
|
|
17
|
+
/(?:^|[\\/])credentials(?:\.(?:txt|json|ya?ml|toml|ini|cfg|db))?$/i,
|
|
16
18
|
/(?:^|[\\/])\.?aws[\\/]credentials/i,
|
|
17
19
|
/(?:^|[\\/])\.netrc$/i,
|
|
18
20
|
/(?:^|[\\/])\.npmrc$/i,
|
|
19
21
|
/(?:^|[\\/])\.pypirc$/i,
|
|
20
|
-
/(?:^|[\\/])token
|
|
22
|
+
/(?:^|[\\/])token$/i,
|
|
23
|
+
/(?:^|[\\/])tokens?\.(?:txt|json|ya?ml|toml|ini|cfg|db)$/i,
|
|
24
|
+
/(?:^|[\\/])token[-_](?:store|cache|data|secret|auth|credentials|backup|local|prod|production|dev|development)(?:\.[^\\/]+)?$/i,
|
|
25
|
+
/(?:^|[\\/])(?:access[_-]?token|refresh[_-]?token|api[_-]?key|client[_-]?secret)(?:\.(?:txt|json|ya?ml|ini|cfg))?$/i,
|
|
21
26
|
// Private keys
|
|
22
27
|
/(?:^|[\\/])[^.]+\.pem$/i,
|
|
23
28
|
/(?:^|[\\/])[^.]+\.key$/i,
|
|
24
29
|
/(?:^|[\\/])[^.]+\.pfx$/i,
|
|
25
30
|
/(?:^|[\\/])[^.]+\.p12$/i,
|
|
31
|
+
/(?:^|[\\/])[^.]+\.ppk$/i,
|
|
26
32
|
// Browser data
|
|
27
33
|
/(?:^|[\\/])cookies/i,
|
|
28
34
|
/(?:^|[\\/])web data$/i,
|
|
@@ -30,17 +36,30 @@ const SENSITIVE_PATTERNS = [
|
|
|
30
36
|
/(?:^|[\\/])local state$/i,
|
|
31
37
|
// Other sensitive files
|
|
32
38
|
/(?:^|[\\/])\.git-credentials$/i,
|
|
39
|
+
/(?:^|[\\/])\.git[\\/]config$/i,
|
|
33
40
|
/(?:^|[\\/])\.docker[\\/]config\.json$/i,
|
|
34
41
|
/(?:^|[\\/])\.kube[\\/]config$/i,
|
|
42
|
+
/(?:^|[\\/])kubeconfig$/i,
|
|
43
|
+
/(?:^|[\\/])application_default_credentials\.json$/i,
|
|
44
|
+
/(?:^|[\\/])service[-_]?account(?:[-_][^\\/]+)?\.json$/i,
|
|
35
45
|
/(?:^|[\\/])config\.json$/i, // generic config files often contain local tokens or service credentials
|
|
36
46
|
];
|
|
37
|
-
|
|
38
|
-
|
|
47
|
+
/**
|
|
48
|
+
* Detect a Windows NTFS alternate data stream suffix. The colon in an
|
|
49
|
+
* absolute drive prefix (for example, C:\\) is the only permitted colon.
|
|
50
|
+
*/
|
|
51
|
+
export function hasWindowsAlternateDataStream(filePath) {
|
|
52
|
+
const withoutDrivePrefix = /^[A-Za-z]:[\\/]/.test(filePath)
|
|
53
|
+
? filePath.slice(2)
|
|
54
|
+
: filePath;
|
|
55
|
+
return withoutDrivePrefix.includes(":");
|
|
56
|
+
}
|
|
39
57
|
export function isSensitivePath(filePath) {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
58
|
+
if (filePath.includes("\0"))
|
|
59
|
+
return true;
|
|
60
|
+
// ADS can hide content behind an otherwise safe-looking filename.
|
|
61
|
+
if (hasWindowsAlternateDataStream(filePath)) {
|
|
62
|
+
return true;
|
|
44
63
|
}
|
|
45
64
|
for (const pattern of SENSITIVE_PATTERNS) {
|
|
46
65
|
if (pattern.test(filePath)) {
|
|
@@ -14,8 +14,8 @@
|
|
|
14
14
|
*
|
|
15
15
|
* 加上 consumeToken 内的 token_not_found / token_expired,共 10 项调用前强制校验。
|
|
16
16
|
*/
|
|
17
|
-
import { type PatchWardenToolMeta } from "../tools/toolRegistry.js";
|
|
18
|
-
import type { ToolProfile } from "../tools/toolCatalog.js";
|
|
17
|
+
import { type PatchWardenToolMeta } from "../tools/catalog/toolRegistry.js";
|
|
18
|
+
import type { ToolProfile } from "../tools/catalog/toolCatalog.js";
|
|
19
19
|
import type { DiscoveryTokenRecord } from "./discoveryTokenStore.js";
|
|
20
20
|
export interface GuardInput {
|
|
21
21
|
toolName: string;
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
*/
|
|
17
17
|
import { PatchWardenError } from "../errors.js";
|
|
18
18
|
import { guardSensitivePath } from "./sensitiveGuard.js";
|
|
19
|
-
import { TOOL_RISK_RANK } from "../tools/toolRegistry.js";
|
|
19
|
+
import { TOOL_RISK_RANK } from "../tools/catalog/toolRegistry.js";
|
|
20
20
|
// ── 内部常量 ──────────────────────────────────────────────────────
|
|
21
21
|
/**
|
|
22
22
|
* 常见路径参数字段名,用于 workspace_read_sensitive 校验时扫描 args。
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export interface WorkspaceRootValidation {
|
|
2
|
+
ok: boolean;
|
|
3
|
+
path: string;
|
|
4
|
+
reason: string | null;
|
|
5
|
+
category: "valid" | "missing" | "not_directory" | "unsafe_root";
|
|
6
|
+
}
|
|
7
|
+
export declare function unsafeWorkspaceRootLabel(value: string, userHome?: string): string | null;
|
|
8
|
+
export declare function validateWorkspaceRoot(value: string, userHome?: string): WorkspaceRootValidation;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { existsSync, realpathSync, statSync } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { basename, normalize, resolve, win32 } from "node:path";
|
|
4
|
+
function normalizeInput(value) {
|
|
5
|
+
return /^[a-z]:[\\/]/i.test(value) ? win32.normalize(value) : normalize(resolve(value));
|
|
6
|
+
}
|
|
7
|
+
function comparable(value) {
|
|
8
|
+
return normalizeInput(value).replace(/[\\/]+$/, "").toLowerCase();
|
|
9
|
+
}
|
|
10
|
+
export function unsafeWorkspaceRootLabel(value, userHome = homedir()) {
|
|
11
|
+
const resolved = normalizeInput(value);
|
|
12
|
+
const trimmed = resolved.replace(/[\\/]+$/, "");
|
|
13
|
+
const windowsStyle = /^[a-z]:[\\/]/i.test(value) || /^\\\\/.test(value);
|
|
14
|
+
const leaf = (windowsStyle ? win32.basename(trimmed) : basename(trimmed)).toLowerCase();
|
|
15
|
+
if (/^[a-z]:$/i.test(trimmed) || trimmed === "" || trimmed === "/")
|
|
16
|
+
return "drive root";
|
|
17
|
+
if (comparable(trimmed) === comparable(userHome))
|
|
18
|
+
return "user home directory";
|
|
19
|
+
if (leaf === "desktop")
|
|
20
|
+
return "Desktop";
|
|
21
|
+
if (leaf === "downloads")
|
|
22
|
+
return "Downloads";
|
|
23
|
+
if (leaf === "documents")
|
|
24
|
+
return "Documents";
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
export function validateWorkspaceRoot(value, userHome = homedir()) {
|
|
28
|
+
const lexicalPath = normalize(resolve(value || "."));
|
|
29
|
+
if (!existsSync(lexicalPath)) {
|
|
30
|
+
return { ok: false, path: lexicalPath, reason: "workspaceRoot does not exist", category: "missing" };
|
|
31
|
+
}
|
|
32
|
+
let resolved;
|
|
33
|
+
try {
|
|
34
|
+
resolved = normalize(realpathSync(lexicalPath));
|
|
35
|
+
if (!statSync(resolved).isDirectory()) {
|
|
36
|
+
return { ok: false, path: resolved, reason: "workspaceRoot is not a directory", category: "not_directory" };
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return { ok: false, path: lexicalPath, reason: "workspaceRoot is not accessible", category: "not_directory" };
|
|
41
|
+
}
|
|
42
|
+
const unsafeLabel = unsafeWorkspaceRootLabel(resolved, userHome);
|
|
43
|
+
if (unsafeLabel) {
|
|
44
|
+
return { ok: false, path: resolved, reason: `workspaceRoot cannot be ${unsafeLabel}`, category: "unsafe_root" };
|
|
45
|
+
}
|
|
46
|
+
return { ok: true, path: resolved, reason: null, category: "valid" };
|
|
47
|
+
}
|
package/dist/smoke-test.js
CHANGED
|
@@ -18,32 +18,32 @@ import { tmpdir } from "node:os";
|
|
|
18
18
|
import { fileURLToPath } from "node:url";
|
|
19
19
|
import { spawnSync } from "node:child_process";
|
|
20
20
|
import { loadConfig, getConfig, getTasksDir, reloadConfig } from "./config.js";
|
|
21
|
-
import { savePlan } from "./tools/savePlan.js";
|
|
22
|
-
import { getPlan } from "./tools/getPlan.js";
|
|
23
|
-
import { createTask } from "./tools/createTask.js";
|
|
21
|
+
import { savePlan } from "./tools/goals/savePlan.js";
|
|
22
|
+
import { getPlan } from "./tools/goals/getPlan.js";
|
|
23
|
+
import { createTask } from "./tools/tasks/createTask.js";
|
|
24
24
|
import { confirmAssessment, readAssessment, createAssessment } from "./assessments/assessmentStore.js";
|
|
25
25
|
import { captureRepoSnapshot } from "./runner/changeCapture.js";
|
|
26
|
-
import { getTaskStatus } from "./tools/getTaskStatus.js";
|
|
27
|
-
import { getResult, getDiff, getTestLog } from "./tools/taskOutputs.js";
|
|
28
|
-
import { listWorkspace } from "./tools/listWorkspace.js";
|
|
29
|
-
import { readWorkspaceFile } from "./tools/readWorkspaceFile.js";
|
|
30
|
-
import { listTasks } from "./tools/listTasks.js";
|
|
31
|
-
import { cancelTask } from "./tools/cancelTask.js";
|
|
32
|
-
import { retryTask } from "./tools/retryTask.js";
|
|
33
|
-
import { getTaskStdoutTail } from "./tools/getTaskStdoutTail.js";
|
|
34
|
-
import { auditTask } from "./tools/auditTask.js";
|
|
35
|
-
import { getTaskSummary } from "./tools/getTaskSummary.js";
|
|
26
|
+
import { getTaskStatus } from "./tools/tasks/getTaskStatus.js";
|
|
27
|
+
import { getResult, getDiff, getTestLog } from "./tools/tasks/taskOutputs.js";
|
|
28
|
+
import { listWorkspace } from "./tools/workspace/listWorkspace.js";
|
|
29
|
+
import { readWorkspaceFile } from "./tools/workspace/readWorkspaceFile.js";
|
|
30
|
+
import { listTasks } from "./tools/tasks/listTasks.js";
|
|
31
|
+
import { cancelTask } from "./tools/tasks/cancelTask.js";
|
|
32
|
+
import { retryTask } from "./tools/tasks/retryTask.js";
|
|
33
|
+
import { getTaskStdoutTail } from "./tools/tasks/getTaskStdoutTail.js";
|
|
34
|
+
import { auditTask } from "./tools/diagnostics/auditTask.js";
|
|
35
|
+
import { getTaskSummary } from "./tools/tasks/getTaskSummary.js";
|
|
36
36
|
import { guardAgentCommand } from "./security/commandGuard.js";
|
|
37
37
|
import { getToolDefs } from "./tools/registry.js";
|
|
38
|
-
import { buildToolCatalogSnapshot, CHATGPT_CORE_TOOL_NAMES, CHATGPT_DIRECT_TOOL_NAMES, selectToolsForProfile, } from "./tools/toolCatalog.js";
|
|
38
|
+
import { buildToolCatalogSnapshot, CHATGPT_CORE_TOOL_NAMES, CHATGPT_DIRECT_TOOL_NAMES, selectToolsForProfile, } from "./tools/catalog/toolCatalog.js";
|
|
39
39
|
import { errorPayload } from "./errors.js";
|
|
40
40
|
import { readWatcherStatus } from "./watcherStatus.js";
|
|
41
|
-
import { createDirectSession } from "./tools/createDirectSession.js";
|
|
42
|
-
import { searchWorkspace } from "./tools/searchWorkspace.js";
|
|
43
|
-
import { applyPatch } from "./tools/applyPatch.js";
|
|
44
|
-
import { runVerification } from "./tools/runVerification.js";
|
|
45
|
-
import { finalizeDirectSession } from "./tools/finalizeDirectSession.js";
|
|
46
|
-
import { auditSession } from "./tools/auditSession.js";
|
|
41
|
+
import { createDirectSession } from "./tools/direct/createDirectSession.js";
|
|
42
|
+
import { searchWorkspace } from "./tools/workspace/searchWorkspace.js";
|
|
43
|
+
import { applyPatch } from "./tools/workspace/applyPatch.js";
|
|
44
|
+
import { runVerification } from "./tools/tasks/runVerification.js";
|
|
45
|
+
import { finalizeDirectSession } from "./tools/direct/finalizeDirectSession.js";
|
|
46
|
+
import { auditSession } from "./tools/diagnostics/auditSession.js";
|
|
47
47
|
import { readDirectSession, updateDirectSession } from "./direct/directSessionStore.js";
|
|
48
48
|
import { createHash } from "node:crypto";
|
|
49
49
|
// Resolve the actual node binary path (spawnSync needs it on WSL/Windows)
|
|
@@ -418,15 +418,16 @@ const sensitiveFiles = [
|
|
|
418
418
|
"cookies.sqlite",
|
|
419
419
|
".git-credentials",
|
|
420
420
|
"config.json",
|
|
421
|
+
".patchwarden/.env",
|
|
422
|
+
".patchwarden/credentials.json",
|
|
421
423
|
];
|
|
422
424
|
for (const sf of sensitiveFiles) {
|
|
423
425
|
await testReject(`C. readWorkspaceFile blocks "${sf}"`, () => {
|
|
424
426
|
readWorkspaceFile(sf);
|
|
425
427
|
});
|
|
426
428
|
}
|
|
427
|
-
//
|
|
428
|
-
await test("C.
|
|
429
|
-
// This should work because .patchwarden files are whitelisted
|
|
429
|
+
// Ordinary PatchWarden artifacts remain readable through their dedicated tool.
|
|
430
|
+
await test("C. getPlan reads ordinary .patchwarden/plans artifacts", () => {
|
|
430
431
|
const plan = savePlan({ title: "Allowlist Test", content: "test" });
|
|
431
432
|
const result = getPlan({ plan_id: plan.plan_id });
|
|
432
433
|
if (!result.content.includes("test"))
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
-
import { PATCHWARDEN_VERSION, TOOL_SCHEMA_EPOCH } from "
|
|
2
|
+
import { PATCHWARDEN_VERSION, TOOL_SCHEMA_EPOCH } from "../../version.js";
|
|
3
|
+
import { stableJsonStringify } from "../../utils/stableJson.js";
|
|
3
4
|
export const CHATGPT_CORE_TOOL_NAMES = [
|
|
4
5
|
"health_check",
|
|
5
6
|
"list_agents",
|
|
@@ -101,7 +102,7 @@ export function buildToolCatalogSnapshot(tools, profile) {
|
|
|
101
102
|
inputSchema: tool.inputSchema,
|
|
102
103
|
}));
|
|
103
104
|
const tool_manifest_sha256 = createHash("sha256")
|
|
104
|
-
.update(
|
|
105
|
+
.update(stableJsonStringify(manifestInput))
|
|
105
106
|
.digest("hex");
|
|
106
107
|
lastSnapshot = {
|
|
107
108
|
server_version: PATCHWARDEN_VERSION,
|
|
@@ -116,13 +117,3 @@ export function buildToolCatalogSnapshot(tools, profile) {
|
|
|
116
117
|
export function getLastToolCatalogSnapshot() {
|
|
117
118
|
return lastSnapshot ? { ...lastSnapshot, tool_names: [...lastSnapshot.tool_names] } : null;
|
|
118
119
|
}
|
|
119
|
-
function stableJson(value) {
|
|
120
|
-
if (Array.isArray(value))
|
|
121
|
-
return `[${value.map(stableJson).join(",")}]`;
|
|
122
|
-
if (value && typeof value === "object") {
|
|
123
|
-
const entries = Object.entries(value)
|
|
124
|
-
.sort(([left], [right]) => left.localeCompare(right));
|
|
125
|
-
return `{${entries.map(([key, entry]) => `${JSON.stringify(key)}:${stableJson(entry)}`).join(",")}}`;
|
|
126
|
-
}
|
|
127
|
-
return JSON.stringify(value);
|
|
128
|
-
}
|
|
@@ -5,8 +5,9 @@
|
|
|
5
5
|
* schema digest 使用稳定字段顺序的 canonical JSON → sha256 计算,
|
|
6
6
|
* 用于检测工具 schema 漂移、release check 校验、discover_tools 返回。
|
|
7
7
|
*/
|
|
8
|
+
import { stableJsonStringify } from "../../utils/stableJson.js";
|
|
8
9
|
import type { ToolProfile } from "./toolCatalog.js";
|
|
9
|
-
import type { ToolDef } from "
|
|
10
|
+
import type { ToolDef } from "../definitions/toolDefs.js";
|
|
10
11
|
export type ToolRisk = "readonly" | "workspace_read_sensitive" | "workspace_write" | "command" | "release" | "credential_sensitive";
|
|
11
12
|
export declare const TOOL_RISK_RANK: Record<ToolRisk, number>;
|
|
12
13
|
export type ToolMode = "delegate" | "direct" | "audit" | "release" | "diagnostic";
|
|
@@ -42,7 +43,7 @@ interface StaticToolMeta {
|
|
|
42
43
|
/**
|
|
43
44
|
* 稳定 JSON 序列化:按字段名排序,避免 key 顺序导致 digest 漂移。
|
|
44
45
|
*/
|
|
45
|
-
export
|
|
46
|
+
export { stableJsonStringify };
|
|
46
47
|
/**
|
|
47
48
|
* 计算 inputSchema 的 sha256 digest。
|
|
48
49
|
* 返回格式:"sha256:<hex>"
|
|
@@ -58,4 +59,3 @@ export declare function buildToolRegistry(tools: ToolDef[]): PatchWardenToolMeta
|
|
|
58
59
|
* 获取单个工具的元数据(不含 schema digest,digest 需从 registry 获取)。
|
|
59
60
|
*/
|
|
60
61
|
export declare function getStaticToolMeta(name: string): StaticToolMeta | undefined;
|
|
61
|
-
export {};
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* 用于检测工具 schema 漂移、release check 校验、discover_tools 返回。
|
|
7
7
|
*/
|
|
8
8
|
import { createHash } from "node:crypto";
|
|
9
|
+
import { stableJsonStringify } from "../../utils/stableJson.js";
|
|
9
10
|
export const TOOL_RISK_RANK = {
|
|
10
11
|
readonly: 0,
|
|
11
12
|
workspace_read_sensitive: 1,
|
|
@@ -56,7 +57,7 @@ const STATIC_TOOL_META = {
|
|
|
56
57
|
summary: "列出已配置的本地 agent 及其可用性",
|
|
57
58
|
profiles: ["full", "chatgpt_core"],
|
|
58
59
|
modes: ["delegate", "diagnostic"],
|
|
59
|
-
tags: ["agent", "list", "config", "
|
|
60
|
+
tags: ["agent", "list", "config", "local-cli", "model"],
|
|
60
61
|
aliases: ["agents", "list_agent"],
|
|
61
62
|
risk: "readonly",
|
|
62
63
|
requiresConfirmation: false,
|
|
@@ -201,7 +202,7 @@ const STATIC_TOOL_META = {
|
|
|
201
202
|
modes: ["delegate", "audit"],
|
|
202
203
|
tags: ["safe", "audit", "checks", "acceptance", "task"],
|
|
203
204
|
aliases: ["safe_audit", "audit_summary"],
|
|
204
|
-
risk: "
|
|
205
|
+
risk: "workspace_write",
|
|
205
206
|
requiresConfirmation: false,
|
|
206
207
|
relatedTools: ["audit_task", "safe_result"],
|
|
207
208
|
},
|
|
@@ -545,7 +546,7 @@ const STATIC_TOOL_META = {
|
|
|
545
546
|
modes: ["direct", "audit"],
|
|
546
547
|
tags: ["safe", "direct", "finalize", "summary"],
|
|
547
548
|
aliases: ["safe_finalize", "finalize_summary"],
|
|
548
|
-
risk: "
|
|
549
|
+
risk: "workspace_write",
|
|
549
550
|
requiresConfirmation: false,
|
|
550
551
|
relatedTools: ["finalize_direct_session", "safe_direct_summary"],
|
|
551
552
|
},
|
|
@@ -556,7 +557,7 @@ const STATIC_TOOL_META = {
|
|
|
556
557
|
modes: ["direct", "audit"],
|
|
557
558
|
tags: ["safe", "direct", "audit", "session"],
|
|
558
559
|
aliases: ["safe_direct_audit", "audit_direct_summary"],
|
|
559
|
-
risk: "
|
|
560
|
+
risk: "workspace_write",
|
|
560
561
|
requiresConfirmation: false,
|
|
561
562
|
relatedTools: ["audit_session", "safe_finalize_direct_session"],
|
|
562
563
|
},
|
|
@@ -798,17 +799,7 @@ const STATIC_TOOL_META = {
|
|
|
798
799
|
/**
|
|
799
800
|
* 稳定 JSON 序列化:按字段名排序,避免 key 顺序导致 digest 漂移。
|
|
800
801
|
*/
|
|
801
|
-
export
|
|
802
|
-
if (Array.isArray(value)) {
|
|
803
|
-
return `[${value.map(stableJsonStringify).join(",")}]`;
|
|
804
|
-
}
|
|
805
|
-
if (value !== null && typeof value === "object") {
|
|
806
|
-
const entries = Object.entries(value)
|
|
807
|
-
.sort(([left], [right]) => left.localeCompare(right));
|
|
808
|
-
return `{${entries.map(([key, entry]) => `${JSON.stringify(key)}:${stableJsonStringify(entry)}`).join(",")}}`;
|
|
809
|
-
}
|
|
810
|
-
return JSON.stringify(value);
|
|
811
|
-
}
|
|
802
|
+
export { stableJsonStringify };
|
|
812
803
|
/**
|
|
813
804
|
* 计算 inputSchema 的 sha256 digest。
|
|
814
805
|
* 返回格式:"sha256:<hex>"
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool definitions for PatchWarden MCP server.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from registry.ts to keep the registry focused on dispatch
|
|
5
|
+
* wiring. getToolDefs() builds the active tool list based on config
|
|
6
|
+
* (test/direct allow-lists, agent names, profile selection) and refreshes
|
|
7
|
+
* the tool catalog snapshot as a side effect.
|
|
8
|
+
*/
|
|
9
|
+
export interface ToolDef {
|
|
10
|
+
name: string;
|
|
11
|
+
description: string;
|
|
12
|
+
inputSchema: {
|
|
13
|
+
type: "object";
|
|
14
|
+
properties: Record<string, unknown>;
|
|
15
|
+
required?: string[];
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
export declare function getToolDefs(): ToolDef[];
|