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
|
@@ -1,165 +0,0 @@
|
|
|
1
|
-
import { describe, it, beforeEach, afterEach } from "node:test";
|
|
2
|
-
import { strict as assert } from "node:assert";
|
|
3
|
-
import { mkdtempSync, rmSync, mkdirSync, writeFileSync } from "node:fs";
|
|
4
|
-
import { join } from "node:path";
|
|
5
|
-
import { tmpdir } from "node:os";
|
|
6
|
-
import { safeStatus } from "../../tools/safeStatus.js";
|
|
7
|
-
function makeConfig(workspaceRoot) {
|
|
8
|
-
return {
|
|
9
|
-
workspaceRoot,
|
|
10
|
-
plansDir: ".patchwarden/plans",
|
|
11
|
-
tasksDir: ".patchwarden/tasks",
|
|
12
|
-
assessmentsDir: ".patchwarden/assessments",
|
|
13
|
-
assessmentTtlSeconds: 3600,
|
|
14
|
-
agents: { codex: { command: "codex", args: ["exec", "{prompt}"] } },
|
|
15
|
-
allowedTestCommands: ["npm test"],
|
|
16
|
-
repoAllowedTestCommands: {},
|
|
17
|
-
maxReadFileBytes: 200_000,
|
|
18
|
-
defaultTaskTimeoutSeconds: 900,
|
|
19
|
-
maxTaskTimeoutSeconds: 3600,
|
|
20
|
-
watcherStaleSeconds: 30,
|
|
21
|
-
directSessionsDir: ".patchwarden/direct-sessions",
|
|
22
|
-
directSessionTtlSeconds: 3600,
|
|
23
|
-
directMaxPatchBytes: 200_000,
|
|
24
|
-
directMaxFileBytes: 500_000,
|
|
25
|
-
};
|
|
26
|
-
}
|
|
27
|
-
describe("safeStatus", () => {
|
|
28
|
-
let tempDir;
|
|
29
|
-
let config;
|
|
30
|
-
let tasksDir;
|
|
31
|
-
beforeEach(() => {
|
|
32
|
-
tempDir = mkdtempSync(join(tmpdir(), "pw-safestatus-"));
|
|
33
|
-
config = makeConfig(tempDir);
|
|
34
|
-
tasksDir = join(tempDir, ".patchwarden", "tasks");
|
|
35
|
-
mkdirSync(tasksDir, { recursive: true });
|
|
36
|
-
});
|
|
37
|
-
afterEach(() => {
|
|
38
|
-
rmSync(tempDir, { recursive: true, force: true });
|
|
39
|
-
});
|
|
40
|
-
function writeTaskStatus(taskId, status) {
|
|
41
|
-
const taskDir = join(tasksDir, taskId);
|
|
42
|
-
mkdirSync(taskDir, { recursive: true });
|
|
43
|
-
writeFileSync(join(taskDir, "status.json"), JSON.stringify(status, null, 2), "utf-8");
|
|
44
|
-
return taskDir;
|
|
45
|
-
}
|
|
46
|
-
function writeTaskRuntime(taskDir, runtime) {
|
|
47
|
-
writeFileSync(join(taskDir, "runtime.json"), JSON.stringify(runtime, null, 2), "utf-8");
|
|
48
|
-
}
|
|
49
|
-
it("returns not_found for non-existent task", () => {
|
|
50
|
-
const result = safeStatus("non-existent-task", config);
|
|
51
|
-
assert.equal(result.task_id, "non-existent-task");
|
|
52
|
-
assert.equal(result.status, "not_found");
|
|
53
|
-
assert.equal(result.phase, null);
|
|
54
|
-
assert.equal(result.error_code, null);
|
|
55
|
-
assert.equal(result.error_summary, null);
|
|
56
|
-
});
|
|
57
|
-
it("returns correct status for pending task", () => {
|
|
58
|
-
writeTaskStatus("task-pending-001", {
|
|
59
|
-
task_id: "task-pending-001",
|
|
60
|
-
status: "pending",
|
|
61
|
-
phase: "queued",
|
|
62
|
-
created_at: "2026-06-24T10:00:00Z",
|
|
63
|
-
updated_at: "2026-06-24T10:00:01Z",
|
|
64
|
-
});
|
|
65
|
-
const result = safeStatus("task-pending-001", config);
|
|
66
|
-
assert.equal(result.task_id, "task-pending-001");
|
|
67
|
-
assert.equal(result.status, "pending");
|
|
68
|
-
assert.equal(result.created_at, "2026-06-24T10:00:00Z");
|
|
69
|
-
assert.equal(result.error_code, null);
|
|
70
|
-
assert.equal(result.error_summary, null);
|
|
71
|
-
});
|
|
72
|
-
it("returns correct status for running task", () => {
|
|
73
|
-
const taskDir = writeTaskStatus("task-running-001", {
|
|
74
|
-
task_id: "task-running-001",
|
|
75
|
-
status: "running",
|
|
76
|
-
phase: "executing_agent",
|
|
77
|
-
created_at: "2026-06-24T10:00:00Z",
|
|
78
|
-
started_at: "2026-06-24T10:00:05Z",
|
|
79
|
-
updated_at: "2026-06-24T10:00:10Z",
|
|
80
|
-
});
|
|
81
|
-
writeTaskRuntime(taskDir, {
|
|
82
|
-
phase: "executing_agent",
|
|
83
|
-
last_heartbeat_at: "2026-06-24T10:00:12Z",
|
|
84
|
-
current_command: "codex exec",
|
|
85
|
-
});
|
|
86
|
-
const result = safeStatus("task-running-001", config);
|
|
87
|
-
assert.equal(result.status, "running");
|
|
88
|
-
assert.equal(result.phase, "executing_agent");
|
|
89
|
-
assert.equal(result.current_command, "codex exec");
|
|
90
|
-
assert.equal(result.last_heartbeat_at, "2026-06-24T10:00:12Z");
|
|
91
|
-
assert.equal(result.started_at, "2026-06-24T10:00:05Z");
|
|
92
|
-
});
|
|
93
|
-
it("returns correct status for done task", () => {
|
|
94
|
-
writeTaskStatus("task-done-001", {
|
|
95
|
-
task_id: "task-done-001",
|
|
96
|
-
status: "done",
|
|
97
|
-
phase: "completed",
|
|
98
|
-
created_at: "2026-06-24T10:00:00Z",
|
|
99
|
-
started_at: "2026-06-24T10:00:05Z",
|
|
100
|
-
finished_at: "2026-06-24T10:05:00Z",
|
|
101
|
-
verify_status: "passed",
|
|
102
|
-
});
|
|
103
|
-
const result = safeStatus("task-done-001", config);
|
|
104
|
-
assert.equal(result.status, "done");
|
|
105
|
-
assert.equal(result.phase, "completed");
|
|
106
|
-
assert.equal(result.finished_at, "2026-06-24T10:05:00Z");
|
|
107
|
-
assert.equal(result.verify_status, "passed");
|
|
108
|
-
assert.equal(result.error_code, null);
|
|
109
|
-
});
|
|
110
|
-
it("returns correct status for failed task", () => {
|
|
111
|
-
writeTaskStatus("task-failed-001", {
|
|
112
|
-
task_id: "task-failed-001",
|
|
113
|
-
status: "failed_verification",
|
|
114
|
-
phase: "failed_verification",
|
|
115
|
-
created_at: "2026-06-24T10:00:00Z",
|
|
116
|
-
started_at: "2026-06-24T10:00:05Z",
|
|
117
|
-
finished_at: "2026-06-24T10:05:00Z",
|
|
118
|
-
error: 'Verification command "npm test" exited with code 1.',
|
|
119
|
-
verify_status: "failed",
|
|
120
|
-
});
|
|
121
|
-
const result = safeStatus("task-failed-001", config);
|
|
122
|
-
assert.equal(result.status, "failed_verification");
|
|
123
|
-
assert.equal(result.error_code, "failed_verification");
|
|
124
|
-
assert.ok(result.error_summary);
|
|
125
|
-
assert.ok(result.error_summary.includes("npm test"));
|
|
126
|
-
});
|
|
127
|
-
it("does not return diff or file content", () => {
|
|
128
|
-
const taskDir = writeTaskStatus("task-safe-001", {
|
|
129
|
-
task_id: "task-safe-001",
|
|
130
|
-
status: "done",
|
|
131
|
-
phase: "completed",
|
|
132
|
-
created_at: "2026-06-24T10:00:00Z",
|
|
133
|
-
});
|
|
134
|
-
writeFileSync(join(taskDir, "result.md"), "# Secret result content", "utf-8");
|
|
135
|
-
writeFileSync(join(taskDir, "git.diff"), "secret diff content", "utf-8");
|
|
136
|
-
writeFileSync(join(taskDir, "test.log"), "secret test log", "utf-8");
|
|
137
|
-
const result = safeStatus("task-safe-001", config);
|
|
138
|
-
const resultStr = JSON.stringify(result);
|
|
139
|
-
assert.ok(!resultStr.includes("Secret result content"));
|
|
140
|
-
assert.ok(!resultStr.includes("secret diff content"));
|
|
141
|
-
assert.ok(!resultStr.includes("secret test log"));
|
|
142
|
-
});
|
|
143
|
-
it("truncates long error messages", () => {
|
|
144
|
-
const longError = "x".repeat(300);
|
|
145
|
-
writeTaskStatus("task-longerror-001", {
|
|
146
|
-
task_id: "task-longerror-001",
|
|
147
|
-
status: "failed",
|
|
148
|
-
phase: "failed",
|
|
149
|
-
error: longError,
|
|
150
|
-
});
|
|
151
|
-
const result = safeStatus("task-longerror-001", config);
|
|
152
|
-
assert.ok(result.error_summary.length < 300);
|
|
153
|
-
assert.ok(result.error_summary.endsWith("..."));
|
|
154
|
-
});
|
|
155
|
-
it("returns artifact_status when present", () => {
|
|
156
|
-
writeTaskStatus("task-artifact-001", {
|
|
157
|
-
task_id: "task-artifact-001",
|
|
158
|
-
status: "done",
|
|
159
|
-
phase: "completed",
|
|
160
|
-
artifact_status: "collected",
|
|
161
|
-
});
|
|
162
|
-
const result = safeStatus("task-artifact-001", config);
|
|
163
|
-
assert.equal(result.artifact_status, "collected");
|
|
164
|
-
});
|
|
165
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,171 +0,0 @@
|
|
|
1
|
-
import { describe, it, beforeEach, afterEach } from "node:test";
|
|
2
|
-
import { strict as assert } from "node:assert";
|
|
3
|
-
import { mkdtempSync, rmSync, mkdirSync, writeFileSync } from "node:fs";
|
|
4
|
-
import { join } from "node:path";
|
|
5
|
-
import { tmpdir } from "node:os";
|
|
6
|
-
import { reloadConfig } from "../../config.js";
|
|
7
|
-
import { safeDirectSummary, safeAuditDirectSession, safeDiffSummary, safeResult, safeTestSummary } from "../../tools/safeViews.js";
|
|
8
|
-
let tempDir;
|
|
9
|
-
let prevConfigEnv;
|
|
10
|
-
function writeConfig() {
|
|
11
|
-
const configPath = join(tempDir, "patchwarden.config.json");
|
|
12
|
-
writeFileSync(configPath, JSON.stringify({
|
|
13
|
-
workspaceRoot: tempDir,
|
|
14
|
-
tasksDir: ".patchwarden/tasks",
|
|
15
|
-
plansDir: ".patchwarden/plans",
|
|
16
|
-
assessmentsDir: ".patchwarden/assessments",
|
|
17
|
-
directSessionsDir: ".patchwarden/direct-sessions",
|
|
18
|
-
agents: {},
|
|
19
|
-
allowedTestCommands: ["npm test"],
|
|
20
|
-
directAllowedCommands: ["npm test"],
|
|
21
|
-
enableDirectProfile: true,
|
|
22
|
-
}), "utf-8");
|
|
23
|
-
prevConfigEnv = process.env.PATCHWARDEN_CONFIG;
|
|
24
|
-
process.env.PATCHWARDEN_CONFIG = configPath;
|
|
25
|
-
reloadConfig();
|
|
26
|
-
}
|
|
27
|
-
describe("safeViews", () => {
|
|
28
|
-
beforeEach(() => {
|
|
29
|
-
tempDir = mkdtempSync(join(tmpdir(), "pw-safeviews-"));
|
|
30
|
-
writeConfig();
|
|
31
|
-
});
|
|
32
|
-
afterEach(() => {
|
|
33
|
-
if (prevConfigEnv === undefined)
|
|
34
|
-
delete process.env.PATCHWARDEN_CONFIG;
|
|
35
|
-
else
|
|
36
|
-
process.env.PATCHWARDEN_CONFIG = prevConfigEnv;
|
|
37
|
-
reloadConfig();
|
|
38
|
-
rmSync(tempDir, { recursive: true, force: true });
|
|
39
|
-
});
|
|
40
|
-
it("returns bounded task summaries without log or diff content", () => {
|
|
41
|
-
const taskId = "task-safe-001";
|
|
42
|
-
const taskDir = join(tempDir, ".patchwarden", "tasks", taskId);
|
|
43
|
-
mkdirSync(taskDir, { recursive: true });
|
|
44
|
-
writeFileSync(join(taskDir, "status.json"), JSON.stringify({
|
|
45
|
-
task_id: taskId,
|
|
46
|
-
status: "done_by_agent",
|
|
47
|
-
phase: "done_by_agent",
|
|
48
|
-
repo_path: "repo",
|
|
49
|
-
resolved_repo_path: join(tempDir, "repo"),
|
|
50
|
-
verify_status: "passed",
|
|
51
|
-
}), "utf-8");
|
|
52
|
-
writeFileSync(join(taskDir, "result.json"), JSON.stringify({
|
|
53
|
-
task_id: taskId,
|
|
54
|
-
status: "done_by_agent",
|
|
55
|
-
summary: "Finished safely",
|
|
56
|
-
changed_files: [{ path: "src/index.ts", change: "modified" }],
|
|
57
|
-
verify_status: "passed",
|
|
58
|
-
warnings: [],
|
|
59
|
-
}), "utf-8");
|
|
60
|
-
writeFileSync(join(taskDir, "verify.json"), JSON.stringify({
|
|
61
|
-
status: "passed",
|
|
62
|
-
commands: [{ command: "npm test", status: "passed", exit_code: 0, stdout_tail: "SECRET_STDOUT", stderr_tail: "SECRET_STDERR" }],
|
|
63
|
-
}), "utf-8");
|
|
64
|
-
writeFileSync(join(taskDir, "changed-files.json"), JSON.stringify({
|
|
65
|
-
changed_files: [{ path: "src/index.ts", change: "modified", tracked: true, ignored: false, kind: "source" }],
|
|
66
|
-
additions: 1,
|
|
67
|
-
deletions: 0,
|
|
68
|
-
diff_available: true,
|
|
69
|
-
diff_truncated: false,
|
|
70
|
-
patch_mode: "textual",
|
|
71
|
-
artifact_hygiene: { counts: { source_changes: 1 }, source_changes: [], tracked_build_artifacts: [], ignored_untracked_artifacts: [], runtime_generated_files: [], suspicious_changes: [] },
|
|
72
|
-
}), "utf-8");
|
|
73
|
-
writeFileSync(join(taskDir, "git.diff"), "SECRET_DIFF", "utf-8");
|
|
74
|
-
writeFileSync(join(taskDir, "test.log"), "SECRET_TEST_LOG", "utf-8");
|
|
75
|
-
const payload = JSON.stringify({
|
|
76
|
-
result: safeResult(taskId),
|
|
77
|
-
tests: safeTestSummary(taskId),
|
|
78
|
-
diff: safeDiffSummary(taskId),
|
|
79
|
-
});
|
|
80
|
-
assert.ok(!payload.includes("SECRET_STDOUT"));
|
|
81
|
-
assert.ok(!payload.includes("SECRET_STDERR"));
|
|
82
|
-
assert.ok(!payload.includes("SECRET_DIFF"));
|
|
83
|
-
assert.ok(!payload.includes("SECRET_TEST_LOG"));
|
|
84
|
-
assert.ok(payload.includes("src/index.ts"));
|
|
85
|
-
});
|
|
86
|
-
it("treats corrupted safe-view JSON artifacts as empty summaries", () => {
|
|
87
|
-
const taskId = "task-safe-corrupt";
|
|
88
|
-
const taskDir = join(tempDir, ".patchwarden", "tasks", taskId);
|
|
89
|
-
mkdirSync(taskDir, { recursive: true });
|
|
90
|
-
writeFileSync(join(taskDir, "status.json"), JSON.stringify({
|
|
91
|
-
task_id: taskId,
|
|
92
|
-
status: "done_by_agent",
|
|
93
|
-
phase: "done_by_agent",
|
|
94
|
-
repo_path: "repo",
|
|
95
|
-
resolved_repo_path: join(tempDir, "repo"),
|
|
96
|
-
verify_status: "passed",
|
|
97
|
-
}), "utf-8");
|
|
98
|
-
writeFileSync(join(taskDir, "result.json"), JSON.stringify({
|
|
99
|
-
task_id: taskId,
|
|
100
|
-
status: "done_by_agent",
|
|
101
|
-
summary: "Finished safely",
|
|
102
|
-
changed_files: [],
|
|
103
|
-
verify_status: "passed",
|
|
104
|
-
warnings: [],
|
|
105
|
-
}), "utf-8");
|
|
106
|
-
writeFileSync(join(taskDir, "verify.json"), "\uFEFF{not json", "utf-8");
|
|
107
|
-
writeFileSync(join(taskDir, "changed-files.json"), "{not json", "utf-8");
|
|
108
|
-
const tests = safeTestSummary(taskId);
|
|
109
|
-
const diff = safeDiffSummary(taskId);
|
|
110
|
-
assert.equal(tests.command_count, 0);
|
|
111
|
-
assert.equal(diff.changed_files_total, 0);
|
|
112
|
-
});
|
|
113
|
-
it("returns Direct safe summaries without verification tails", () => {
|
|
114
|
-
const sessionId = "direct-safe-001";
|
|
115
|
-
const sessionDir = join(tempDir, ".patchwarden", "direct-sessions", sessionId);
|
|
116
|
-
mkdirSync(sessionDir, { recursive: true });
|
|
117
|
-
const session = {
|
|
118
|
-
session_id: sessionId,
|
|
119
|
-
title: "safe direct",
|
|
120
|
-
repo_path: "repo",
|
|
121
|
-
resolved_repo_path: join(tempDir, "repo"),
|
|
122
|
-
created_at: new Date().toISOString(),
|
|
123
|
-
expires_at: new Date(Date.now() + 60_000).toISOString(),
|
|
124
|
-
server_version: "1.0.0",
|
|
125
|
-
schema_epoch: "test",
|
|
126
|
-
tool_manifest_sha256: "x".repeat(64),
|
|
127
|
-
workspace_snapshot_before: { head: null, status: "", files: {}, workspace_dirty: false, warnings: [], is_git: false },
|
|
128
|
-
workspace_fingerprint_before: "fingerprint",
|
|
129
|
-
allowed_commands: ["npm test"],
|
|
130
|
-
operations: [],
|
|
131
|
-
verification_runs: [{
|
|
132
|
-
command: "npm test",
|
|
133
|
-
exit_code: 0,
|
|
134
|
-
passed: true,
|
|
135
|
-
timed_out: false,
|
|
136
|
-
stdout_tail: "SECRET_DIRECT_STDOUT",
|
|
137
|
-
stderr_tail: "SECRET_DIRECT_STDERR",
|
|
138
|
-
started_at: new Date().toISOString(),
|
|
139
|
-
finished_at: new Date().toISOString(),
|
|
140
|
-
log_path: join(sessionDir, "verification.log"),
|
|
141
|
-
}],
|
|
142
|
-
finalized: true,
|
|
143
|
-
finalized_at: new Date().toISOString(),
|
|
144
|
-
audited: false,
|
|
145
|
-
change_artifacts: {
|
|
146
|
-
changed_files: [],
|
|
147
|
-
diff: "SECRET_DIRECT_DIFF",
|
|
148
|
-
diff_available: true,
|
|
149
|
-
diff_truncated: false,
|
|
150
|
-
diff_size_bytes: 18,
|
|
151
|
-
additions: 0,
|
|
152
|
-
deletions: 0,
|
|
153
|
-
file_stats: [],
|
|
154
|
-
workspace_dirty_before: false,
|
|
155
|
-
workspace_dirty_after: false,
|
|
156
|
-
patch_mode: "no_changes",
|
|
157
|
-
unavailable_reason: null,
|
|
158
|
-
artifact_hygiene: { counts: { source_changes: 0, tracked_build_artifacts: 0, ignored_untracked_artifacts: 0, runtime_generated_files: 0, suspicious_changes: 0 }, source_changes: [], tracked_build_artifacts: [], ignored_untracked_artifacts: [], runtime_generated_files: [], suspicious_changes: [] },
|
|
159
|
-
},
|
|
160
|
-
};
|
|
161
|
-
writeFileSync(join(sessionDir, "session.json"), JSON.stringify(session, null, 2), "utf-8");
|
|
162
|
-
const payload = JSON.stringify({
|
|
163
|
-
summary: safeDirectSummary(sessionId),
|
|
164
|
-
audit: safeAuditDirectSession(sessionId),
|
|
165
|
-
});
|
|
166
|
-
assert.ok(!payload.includes("SECRET_DIRECT_STDOUT"));
|
|
167
|
-
assert.ok(!payload.includes("SECRET_DIRECT_STDERR"));
|
|
168
|
-
assert.ok(!payload.includes("SECRET_DIRECT_DIFF"));
|
|
169
|
-
assert.ok(payload.includes("npm test"));
|
|
170
|
-
});
|
|
171
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,176 +0,0 @@
|
|
|
1
|
-
import { describe, it } from "node:test";
|
|
2
|
-
import { strict as assert } from "node:assert";
|
|
3
|
-
import { computeSchemaDigest } from "../../tools/toolRegistry.js";
|
|
4
|
-
import { CHATGPT_CORE_TOOL_NAMES } from "../../tools/toolCatalog.js";
|
|
5
|
-
import { checkRegistrySchemaDigest, checkChatgptCoreManifestStable, checkNewToolsProfileAppend, runAllSchemaDriftChecks, } from "../../tools/schemaDriftCheck.js";
|
|
6
|
-
// ── 测试夹具构造工具 ─────────────────────────────────────────────
|
|
7
|
-
const SAMPLE_SCHEMA = {
|
|
8
|
-
type: "object",
|
|
9
|
-
properties: {
|
|
10
|
-
task_id: { type: "string", description: "Task ID" },
|
|
11
|
-
},
|
|
12
|
-
required: ["task_id"],
|
|
13
|
-
};
|
|
14
|
-
const OTHER_SCHEMA = {
|
|
15
|
-
type: "object",
|
|
16
|
-
properties: {
|
|
17
|
-
plan_id: { type: "string", description: "Plan ID" },
|
|
18
|
-
},
|
|
19
|
-
required: ["plan_id"],
|
|
20
|
-
};
|
|
21
|
-
const CORE_COUNT = CHATGPT_CORE_TOOL_NAMES.length;
|
|
22
|
-
/** 构造一个满足 PatchWardenToolMeta 接口的元数据对象。 */
|
|
23
|
-
function makeMeta(opts) {
|
|
24
|
-
const schema = opts.inputSchema ?? SAMPLE_SCHEMA;
|
|
25
|
-
return {
|
|
26
|
-
name: opts.name,
|
|
27
|
-
title: `Tool ${opts.name}`,
|
|
28
|
-
summary: `summary for ${opts.name}`,
|
|
29
|
-
description: `description for ${opts.name}`,
|
|
30
|
-
profiles: opts.profiles ?? ["full"],
|
|
31
|
-
modes: ["diagnostic"],
|
|
32
|
-
tags: [],
|
|
33
|
-
aliases: [],
|
|
34
|
-
risk: "readonly",
|
|
35
|
-
requiresConfirmation: false,
|
|
36
|
-
inputSchemaDigest: opts.inputSchemaDigest ?? computeSchemaDigest(schema),
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
/** 构造 toolDefs Map。 */
|
|
40
|
-
function makeToolDefs(entries) {
|
|
41
|
-
const map = new Map();
|
|
42
|
-
for (const e of entries) {
|
|
43
|
-
map.set(e.name, {
|
|
44
|
-
inputSchema: e.inputSchema ?? SAMPLE_SCHEMA,
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
|
-
return map;
|
|
48
|
-
}
|
|
49
|
-
/** 构造包含全部 21 个 chatgpt_core 工具(顺序一致)的 toolDefs Map。 */
|
|
50
|
-
function makeCoreToolDefs() {
|
|
51
|
-
return makeToolDefs([...CHATGPT_CORE_TOOL_NAMES].map((name) => ({ name })));
|
|
52
|
-
}
|
|
53
|
-
/** 构造一个 registry:所有工具都属于 chatgpt_core profile。 */
|
|
54
|
-
function makeCoreRegistry(count) {
|
|
55
|
-
const names = [...CHATGPT_CORE_TOOL_NAMES].slice(0, count);
|
|
56
|
-
return names.map((name) => makeMeta({ name, profiles: ["chatgpt_core"] }));
|
|
57
|
-
}
|
|
58
|
-
// ── 1. checkRegistrySchemaDigest ─────────────────────────────────
|
|
59
|
-
describe("checkRegistrySchemaDigest", () => {
|
|
60
|
-
it("digest 一致时 ok=true,warnings 为空", () => {
|
|
61
|
-
const schema = { type: "object", properties: { a: { type: "string" } } };
|
|
62
|
-
const registry = [makeMeta({ name: "tool_a", inputSchema: schema })];
|
|
63
|
-
const toolDefs = makeToolDefs([{ name: "tool_a", inputSchema: schema }]);
|
|
64
|
-
const result = checkRegistrySchemaDigest(registry, toolDefs);
|
|
65
|
-
assert.equal(result.ok, true);
|
|
66
|
-
assert.deepEqual(result.warnings, []);
|
|
67
|
-
});
|
|
68
|
-
it("digest 不一致时 ok=false,warnings 包含工具名和两个 digest", () => {
|
|
69
|
-
const registrySchema = SAMPLE_SCHEMA;
|
|
70
|
-
const actualSchema = OTHER_SCHEMA;
|
|
71
|
-
// registry 记录的是 registrySchema 的 digest,但实际 toolDef 用的是 actualSchema
|
|
72
|
-
const registry = [
|
|
73
|
-
makeMeta({ name: "tool_drift", inputSchema: registrySchema }),
|
|
74
|
-
];
|
|
75
|
-
const toolDefs = makeToolDefs([{ name: "tool_drift", inputSchema: actualSchema }]);
|
|
76
|
-
const result = checkRegistrySchemaDigest(registry, toolDefs);
|
|
77
|
-
assert.equal(result.ok, false);
|
|
78
|
-
assert.equal(result.warnings.length, 1);
|
|
79
|
-
const warning = result.warnings[0];
|
|
80
|
-
const expectedRegistryDigest = computeSchemaDigest(registrySchema);
|
|
81
|
-
const expectedActualDigest = computeSchemaDigest(actualSchema);
|
|
82
|
-
assert.ok(warning.includes("tool_drift"), "warning 应包含工具名");
|
|
83
|
-
assert.ok(warning.includes(expectedRegistryDigest), "warning 应包含 registry digest");
|
|
84
|
-
assert.ok(warning.includes(expectedActualDigest), "warning 应包含 actual digest");
|
|
85
|
-
});
|
|
86
|
-
it("toolDefs 中找不到工具时跳过(不报 drift)", () => {
|
|
87
|
-
const registry = [makeMeta({ name: "missing_tool" })];
|
|
88
|
-
const toolDefs = makeToolDefs([]); // 空映射
|
|
89
|
-
const result = checkRegistrySchemaDigest(registry, toolDefs);
|
|
90
|
-
assert.equal(result.ok, true);
|
|
91
|
-
assert.deepEqual(result.warnings, []);
|
|
92
|
-
});
|
|
93
|
-
});
|
|
94
|
-
// ── 2. checkChatgptCoreManifestStable ────────────────────────────
|
|
95
|
-
describe("checkChatgptCoreManifestStable", () => {
|
|
96
|
-
it("21 工具全部存在时 ok=true", () => {
|
|
97
|
-
const toolDefs = makeCoreToolDefs();
|
|
98
|
-
const result = checkChatgptCoreManifestStable(toolDefs);
|
|
99
|
-
assert.equal(result.ok, true);
|
|
100
|
-
assert.deepEqual(result.warnings, []);
|
|
101
|
-
});
|
|
102
|
-
it("缺少工具时 ok=false", () => {
|
|
103
|
-
// 移除最后一个工具,使 chatgpt_core 工具数为 20
|
|
104
|
-
const names = [...CHATGPT_CORE_TOOL_NAMES].slice(0, CORE_COUNT - 1);
|
|
105
|
-
const toolDefs = makeToolDefs(names.map((name) => ({ name })));
|
|
106
|
-
const result = checkChatgptCoreManifestStable(toolDefs);
|
|
107
|
-
assert.equal(result.ok, false);
|
|
108
|
-
assert.equal(result.warnings.length, 1);
|
|
109
|
-
assert.ok(result.warnings[0].includes("missing tools"), "warning 应包含 missing tools");
|
|
110
|
-
assert.ok(result.warnings[0].includes(CHATGPT_CORE_TOOL_NAMES[CORE_COUNT - 1]), "warning 应包含缺失的工具名");
|
|
111
|
-
});
|
|
112
|
-
});
|
|
113
|
-
// ── 3. checkNewToolsProfileAppend ────────────────────────────────
|
|
114
|
-
describe("checkNewToolsProfileAppend", () => {
|
|
115
|
-
it("chatgpt_core 21 工具时 ok=true", () => {
|
|
116
|
-
const registry = makeCoreRegistry(CORE_COUNT);
|
|
117
|
-
const result = checkNewToolsProfileAppend(registry);
|
|
118
|
-
assert.equal(result.ok, true);
|
|
119
|
-
assert.deepEqual(result.warnings, []);
|
|
120
|
-
});
|
|
121
|
-
it("chatgpt_core 工具数不为 21 时 ok=false", () => {
|
|
122
|
-
// 22 个 chatgpt_core 工具:21 个标准工具 + 1 个重复名(模拟新工具追加)
|
|
123
|
-
const registry = makeCoreRegistry(CORE_COUNT);
|
|
124
|
-
registry.push(makeMeta({ name: "extra_tool", profiles: ["chatgpt_core"] }));
|
|
125
|
-
const result = checkNewToolsProfileAppend(registry);
|
|
126
|
-
assert.equal(result.ok, false);
|
|
127
|
-
assert.equal(result.warnings.length, 1);
|
|
128
|
-
assert.ok(result.warnings[0].includes(`expected ${CORE_COUNT}`), "warning 应包含预期数量 21");
|
|
129
|
-
assert.ok(result.warnings[0].includes(`got ${CORE_COUNT + 1}`), "warning 应包含实际数量 22");
|
|
130
|
-
});
|
|
131
|
-
});
|
|
132
|
-
// ── 4. runAllSchemaDriftChecks ───────────────────────────────────
|
|
133
|
-
describe("runAllSchemaDriftChecks", () => {
|
|
134
|
-
it("无 drift 时 ok=true", () => {
|
|
135
|
-
// registry 与 toolDefs 完全一致,且 chatgpt_core 工具数为 21
|
|
136
|
-
const registry = makeCoreRegistry(CORE_COUNT);
|
|
137
|
-
const toolDefs = makeCoreToolDefs();
|
|
138
|
-
const result = runAllSchemaDriftChecks(registry, toolDefs);
|
|
139
|
-
assert.equal(result.ok, true);
|
|
140
|
-
assert.deepEqual(result.warnings, []);
|
|
141
|
-
});
|
|
142
|
-
it("有 drift 时 ok=false,warnings 合并正确", () => {
|
|
143
|
-
// 构造同时触发三个检查的 drift:
|
|
144
|
-
// - check 1: 一个工具 digest 不一致
|
|
145
|
-
// - check 2: toolDefs 缺少一个 chatgpt_core 工具(数量 16)
|
|
146
|
-
// - check 3: registry 有 22 个 chatgpt_core 工具
|
|
147
|
-
const driftedSchema = OTHER_SCHEMA;
|
|
148
|
-
const registry = makeCoreRegistry(CORE_COUNT);
|
|
149
|
-
// 第一个工具的 digest 指向 SAMPLE_SCHEMA,但 toolDefs 中该工具用 driftedSchema
|
|
150
|
-
registry[0] = makeMeta({
|
|
151
|
-
name: CHATGPT_CORE_TOOL_NAMES[0],
|
|
152
|
-
inputSchema: SAMPLE_SCHEMA,
|
|
153
|
-
profiles: ["chatgpt_core"],
|
|
154
|
-
});
|
|
155
|
-
// 追加一个额外的 chatgpt_core 工具,使 profile 工具数变为 22
|
|
156
|
-
registry.push(makeMeta({ name: "extra_tool", profiles: ["chatgpt_core"] }));
|
|
157
|
-
// toolDefs:第一个工具用 driftedSchema(触发 check 1),且移除最后一个核心工具(触发 check 2)
|
|
158
|
-
const coreNames = [...CHATGPT_CORE_TOOL_NAMES];
|
|
159
|
-
const toolDefEntries = coreNames.slice(0, CORE_COUNT - 1).map((name) => ({
|
|
160
|
-
name,
|
|
161
|
-
inputSchema: name === CHATGPT_CORE_TOOL_NAMES[0] ? driftedSchema : SAMPLE_SCHEMA,
|
|
162
|
-
}));
|
|
163
|
-
const toolDefs = makeToolDefs(toolDefEntries);
|
|
164
|
-
const result = runAllSchemaDriftChecks(registry, toolDefs);
|
|
165
|
-
assert.equal(result.ok, false);
|
|
166
|
-
// 至少包含来自三个检查的警告
|
|
167
|
-
assert.ok(result.warnings.length >= 3, `应有至少 3 条警告,实际 ${result.warnings.length}`);
|
|
168
|
-
// 验证三类警告都存在
|
|
169
|
-
const hasDigestDrift = result.warnings.some((w) => w.startsWith("Schema drift for tool"));
|
|
170
|
-
const hasManifestMissing = result.warnings.some((w) => w.includes("missing tools"));
|
|
171
|
-
const hasProfileCount = result.warnings.some((w) => w.includes("profile tool count changed"));
|
|
172
|
-
assert.ok(hasDigestDrift, "应包含 registry digest drift 警告");
|
|
173
|
-
assert.ok(hasManifestMissing, "应包含 manifest 缺失工具警告");
|
|
174
|
-
assert.ok(hasProfileCount, "应包含 profile 数量变化警告");
|
|
175
|
-
});
|
|
176
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
import { describe, it } from "node:test";
|
|
2
|
-
import { strict as assert } from "node:assert";
|
|
3
|
-
import { isSensitivePath, guardSensitivePath } from "../../security/sensitiveGuard.js";
|
|
4
|
-
import { PatchWardenError } from "../../errors.js";
|
|
5
|
-
describe("isSensitivePath", () => {
|
|
6
|
-
it("blocks .env files", () => {
|
|
7
|
-
assert.equal(isSensitivePath(".env"), true);
|
|
8
|
-
assert.equal(isSensitivePath("path/to/.env"), true);
|
|
9
|
-
assert.equal(isSensitivePath(".env.production"), true);
|
|
10
|
-
assert.equal(isSensitivePath(".env.local"), true);
|
|
11
|
-
});
|
|
12
|
-
it("blocks .ENV case insensitive", () => {
|
|
13
|
-
assert.equal(isSensitivePath(".ENV"), true);
|
|
14
|
-
assert.equal(isSensitivePath("path/to/.ENV"), true);
|
|
15
|
-
assert.equal(isSensitivePath(".Env.Production"), true);
|
|
16
|
-
});
|
|
17
|
-
it("blocks config.json case insensitive", () => {
|
|
18
|
-
assert.equal(isSensitivePath("config.json"), true);
|
|
19
|
-
assert.equal(isSensitivePath("Config.json"), true);
|
|
20
|
-
assert.equal(isSensitivePath("CONFIG.JSON"), true);
|
|
21
|
-
assert.equal(isSensitivePath("path/to/Config.json"), true);
|
|
22
|
-
});
|
|
23
|
-
it("blocks SSH keys", () => {
|
|
24
|
-
assert.equal(isSensitivePath("id_rsa"), true);
|
|
25
|
-
assert.equal(isSensitivePath("id_ed25519"), true);
|
|
26
|
-
assert.equal(isSensitivePath("id_ecdsa"), true);
|
|
27
|
-
assert.equal(isSensitivePath("path/to/id_rsa"), true);
|
|
28
|
-
assert.equal(isSensitivePath(".ssh/id_rsa"), true);
|
|
29
|
-
});
|
|
30
|
-
it("blocks credentials and tokens", () => {
|
|
31
|
-
assert.equal(isSensitivePath("credentials"), true);
|
|
32
|
-
assert.equal(isSensitivePath("path/to/credentials.json"), true);
|
|
33
|
-
assert.equal(isSensitivePath("token.txt"), true);
|
|
34
|
-
// access_token does NOT match because the pattern requires token at start or after /
|
|
35
|
-
assert.equal(isSensitivePath("access_token"), false);
|
|
36
|
-
assert.equal(isSensitivePath("path/to/token"), true);
|
|
37
|
-
assert.equal(isSensitivePath(".netrc"), true);
|
|
38
|
-
assert.equal(isSensitivePath(".npmrc"), true);
|
|
39
|
-
assert.equal(isSensitivePath(".pypirc"), true);
|
|
40
|
-
});
|
|
41
|
-
it("blocks private key files", () => {
|
|
42
|
-
assert.equal(isSensitivePath("server.pem"), true);
|
|
43
|
-
assert.equal(isSensitivePath("private.key"), true);
|
|
44
|
-
assert.equal(isSensitivePath("cert.pfx"), true);
|
|
45
|
-
assert.equal(isSensitivePath("cert.p12"), true);
|
|
46
|
-
});
|
|
47
|
-
it("blocks browser data files", () => {
|
|
48
|
-
assert.equal(isSensitivePath("cookies"), true);
|
|
49
|
-
assert.equal(isSensitivePath("cookies.db"), true);
|
|
50
|
-
assert.equal(isSensitivePath("Web Data"), true);
|
|
51
|
-
assert.equal(isSensitivePath("Login Data"), true);
|
|
52
|
-
assert.equal(isSensitivePath("Local State"), true);
|
|
53
|
-
});
|
|
54
|
-
it("blocks .git-credentials", () => {
|
|
55
|
-
assert.equal(isSensitivePath(".git-credentials"), true);
|
|
56
|
-
assert.equal(isSensitivePath("path/to/.git-credentials"), true);
|
|
57
|
-
});
|
|
58
|
-
it("blocks docker and kube config", () => {
|
|
59
|
-
assert.equal(isSensitivePath(".docker/config.json"), true);
|
|
60
|
-
assert.equal(isSensitivePath(".kube/config"), true);
|
|
61
|
-
});
|
|
62
|
-
it("allows .patchwarden safe prefix", () => {
|
|
63
|
-
assert.equal(isSensitivePath(".patchwarden/tasks/task-001/status.json"), false);
|
|
64
|
-
assert.equal(isSensitivePath(".patchwarden/config.json"), false);
|
|
65
|
-
assert.equal(isSensitivePath(".patchwarden"), false);
|
|
66
|
-
});
|
|
67
|
-
it("allows non-sensitive files", () => {
|
|
68
|
-
assert.equal(isSensitivePath("src/main.ts"), false);
|
|
69
|
-
assert.equal(isSensitivePath("README.md"), false);
|
|
70
|
-
assert.equal(isSensitivePath("package.json"), false);
|
|
71
|
-
assert.equal(isSensitivePath("docs/guide.md"), false);
|
|
72
|
-
});
|
|
73
|
-
it("handles Windows backslash paths", () => {
|
|
74
|
-
assert.equal(isSensitivePath("path\\to\\.env"), true);
|
|
75
|
-
assert.equal(isSensitivePath("path\\to\\config.json"), true);
|
|
76
|
-
assert.equal(isSensitivePath(".patchwarden\\tasks\\status.json"), false);
|
|
77
|
-
});
|
|
78
|
-
it("handles null byte in path", () => {
|
|
79
|
-
// config.json with null byte appended — the $ anchor means this won't match
|
|
80
|
-
// because the string doesn't end with "config.json"
|
|
81
|
-
const nullPath = "config.json\x00.txt";
|
|
82
|
-
assert.equal(isSensitivePath(nullPath), false);
|
|
83
|
-
// But plain config.json still matches
|
|
84
|
-
assert.equal(isSensitivePath("config.json"), true);
|
|
85
|
-
});
|
|
86
|
-
it("handles Unicode lookalike characters", () => {
|
|
87
|
-
// Full-width dot (U+FF0E) should not match .env pattern
|
|
88
|
-
// This is expected behavior — Unicode lookalikes are NOT matched
|
|
89
|
-
const fullWidthPath = "\uFF0Eenv";
|
|
90
|
-
assert.equal(isSensitivePath(fullWidthPath), false);
|
|
91
|
-
});
|
|
92
|
-
});
|
|
93
|
-
describe("guardSensitivePath", () => {
|
|
94
|
-
it("throws PatchWardenError for sensitive paths", () => {
|
|
95
|
-
assert.throws(() => guardSensitivePath(".env"), (err) => err instanceof PatchWardenError && err.reason === "sensitive_path_blocked");
|
|
96
|
-
});
|
|
97
|
-
it("does not throw for non-sensitive paths", () => {
|
|
98
|
-
assert.doesNotThrow(() => guardSensitivePath("src/main.ts"));
|
|
99
|
-
assert.doesNotThrow(() => guardSensitivePath("README.md"));
|
|
100
|
-
});
|
|
101
|
-
it("does not throw for .patchwarden paths", () => {
|
|
102
|
-
assert.doesNotThrow(() => guardSensitivePath(".patchwarden/tasks/status.json"));
|
|
103
|
-
});
|
|
104
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|