patchwarden 1.5.0 → 1.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/PatchWarden.cmd +32 -2
- package/README.en.md +194 -26
- package/README.md +160 -22
- package/dist/assessments/agentAssessor.d.ts +1 -1
- package/dist/assessments/agentAssessor.js +24 -15
- package/dist/assessments/assessmentStore.d.ts +1 -1
- package/dist/assessments/assessmentStore.js +44 -36
- package/dist/assessments/confirmCli.js +5 -4
- package/dist/config.d.ts +22 -0
- package/dist/config.js +98 -11
- package/dist/control/fileManager.d.ts +2 -0
- package/dist/control/fileManager.js +39 -0
- package/dist/control/middleware/auth.d.ts +16 -0
- package/dist/control/middleware/auth.js +17 -0
- package/dist/control/middleware/static.d.ts +3 -0
- package/dist/control/middleware/static.js +78 -0
- package/dist/control/routeTable.d.ts +24 -0
- package/dist/control/routeTable.js +263 -0
- package/dist/control/routes/audit.d.ts +15 -0
- package/dist/control/routes/audit.js +287 -0
- package/dist/control/routes/evidence.d.ts +4 -0
- package/dist/control/routes/evidence.js +96 -0
- package/dist/control/routes/lineage.d.ts +3 -0
- package/dist/control/routes/lineage.js +71 -0
- package/dist/control/routes/policy.d.ts +3 -0
- package/dist/control/routes/policy.js +81 -0
- package/dist/control/routes/process.d.ts +12 -0
- package/dist/control/routes/process.js +487 -0
- package/dist/control/routes/sessions.d.ts +30 -0
- package/dist/control/routes/sessions.js +287 -0
- package/dist/control/routes/status.d.ts +51 -0
- package/dist/control/routes/status.js +307 -0
- package/dist/control/routes/taskActions.d.ts +21 -0
- package/dist/control/routes/taskActions.js +197 -0
- package/dist/control/routes/tasks.d.ts +24 -0
- package/dist/control/routes/tasks.js +309 -0
- package/dist/control/routes/workspace.d.ts +15 -0
- package/dist/control/routes/workspace.js +220 -0
- package/dist/control/runtime.d.ts +92 -0
- package/dist/control/runtime.js +425 -0
- package/dist/control/server.d.ts +13 -0
- package/dist/control/server.js +150 -0
- package/dist/control/shared.d.ts +59 -0
- package/dist/control/shared.js +341 -0
- package/dist/controlCenter.d.ts +6 -0
- package/dist/controlCenter.js +7 -2197
- package/dist/direct/directAudit.js +234 -225
- package/dist/direct/directGuards.d.ts +2 -1
- package/dist/direct/directGuards.js +72 -37
- package/dist/direct/directPatch.d.ts +6 -1
- package/dist/direct/directPatch.js +55 -8
- package/dist/direct/directSessionStore.d.ts +16 -6
- package/dist/direct/directSessionStore.js +127 -35
- package/dist/direct/directVerification.js +8 -16
- package/dist/doctor.d.ts +18 -1
- package/dist/doctor.js +614 -373
- package/dist/goal/acceptanceEngine.d.ts +1 -1
- package/dist/goal/goalProgress.js +75 -69
- package/dist/goal/goalReport.d.ts +54 -0
- package/dist/goal/goalReport.js +197 -0
- package/dist/goal/goalStatus.d.ts +8 -0
- package/dist/goal/goalStatus.js +10 -3
- package/dist/goal/goalStore.d.ts +9 -2
- package/dist/goal/goalStore.js +152 -45
- package/dist/goal/handoffExport.js +6 -6
- package/dist/goal/specKitImport.d.ts +63 -0
- package/dist/goal/specKitImport.js +221 -0
- package/dist/goal/subgoalSync.js +13 -10
- package/dist/goal/worktreeManager.d.ts +2 -0
- package/dist/goal/worktreeManager.js +209 -136
- package/dist/httpServer.js +126 -44
- package/dist/index.js +7 -4
- package/dist/logging.d.ts +7 -1
- package/dist/logging.js +13 -3
- package/dist/policy/projectPolicy.js +5 -2
- package/dist/release/releaseGate.d.ts +1 -1
- package/dist/release/releaseGate.js +78 -25
- package/dist/runner/agentInvocation.d.ts +14 -0
- package/dist/runner/agentInvocation.js +96 -4
- package/dist/runner/changeCapture.d.ts +14 -6
- package/dist/runner/changeCapture.js +287 -81
- package/dist/runner/cli.js +7 -6
- package/dist/runner/postTaskCleanup.js +60 -7
- package/dist/runner/processSecurity.d.ts +56 -0
- package/dist/runner/processSecurity.js +373 -0
- package/dist/runner/runTask.d.ts +1 -1
- package/dist/runner/runTask.js +339 -287
- package/dist/runner/simpleProcess.d.ts +3 -0
- package/dist/runner/simpleProcess.js +52 -38
- package/dist/{taskProgress.d.ts → runner/taskProgress.d.ts} +1 -1
- package/dist/{taskProgress.js → runner/taskProgress.js} +2 -2
- package/dist/{taskRuntime.d.ts → runner/taskRuntime.d.ts} +1 -1
- package/dist/{taskRuntime.js → runner/taskRuntime.js} +3 -2
- package/dist/runner/taskStatusStore.d.ts +11 -0
- package/dist/runner/taskStatusStore.js +24 -0
- package/dist/runner/watch.d.ts +14 -1
- package/dist/runner/watch.js +265 -54
- package/dist/security/contentRedaction.d.ts +6 -0
- package/dist/security/contentRedaction.js +24 -2
- package/dist/security/discoveryTokenStore.d.ts +3 -2
- package/dist/security/discoveryTokenStore.js +17 -2
- package/dist/security/loopbackHost.d.ts +2 -0
- package/dist/security/loopbackHost.js +16 -0
- package/dist/security/pathGuard.js +32 -17
- package/dist/security/planGuard.js +2 -1
- package/dist/security/runtimeGuard.js +9 -6
- package/dist/security/secretComparison.d.ts +3 -0
- package/dist/security/secretComparison.js +11 -0
- package/dist/security/sensitiveGuard.d.ts +5 -0
- package/dist/security/sensitiveGuard.js +27 -8
- package/dist/security/toolInvocationGuard.d.ts +2 -2
- package/dist/security/toolInvocationGuard.js +1 -1
- package/dist/security/workspaceRootGuard.d.ts +8 -0
- package/dist/security/workspaceRootGuard.js +47 -0
- package/dist/smoke-test.js +280 -273
- package/dist/tools/catalog/index.d.ts +4 -0
- package/dist/tools/catalog/index.js +4 -0
- package/dist/tools/{toolCatalog.js → catalog/toolCatalog.js} +3 -12
- package/dist/tools/{toolRegistry.d.ts → catalog/toolRegistry.d.ts} +3 -3
- package/dist/tools/{toolRegistry.js → catalog/toolRegistry.js} +28 -15
- package/dist/tools/definitions/toolDefs.d.ts +18 -0
- package/dist/tools/definitions/toolDefs.js +1177 -0
- package/dist/tools/{auditSession.d.ts → diagnostics/auditSession.d.ts} +1 -1
- package/dist/tools/{auditSession.js → diagnostics/auditSession.js} +2 -2
- package/dist/tools/{auditTask.d.ts → diagnostics/auditTask.d.ts} +8 -63
- package/dist/tools/{auditTask.js → diagnostics/auditTask.js} +111 -37
- package/dist/tools/{healthCheck.d.ts → diagnostics/healthCheck.d.ts} +6 -6
- package/dist/tools/{healthCheck.js → diagnostics/healthCheck.js} +10 -10
- package/dist/tools/diagnostics/index.d.ts +6 -0
- package/dist/tools/diagnostics/index.js +6 -0
- package/dist/tools/{safeStatus.d.ts → diagnostics/safeStatus.d.ts} +3 -3
- package/dist/tools/{safeStatus.js → diagnostics/safeStatus.js} +6 -6
- package/dist/tools/{safeViews.d.ts → diagnostics/safeViews.d.ts} +14 -10
- package/dist/tools/{safeViews.js → diagnostics/safeViews.js} +25 -15
- package/dist/tools/{schemaDriftCheck.d.ts → diagnostics/schemaDriftCheck.d.ts} +1 -1
- package/dist/tools/{schemaDriftCheck.js → diagnostics/schemaDriftCheck.js} +2 -2
- package/dist/tools/{createDirectSession.d.ts → direct/createDirectSession.d.ts} +1 -1
- package/dist/tools/{createDirectSession.js → direct/createDirectSession.js} +8 -8
- package/dist/tools/{finalizeDirectSession.d.ts → direct/finalizeDirectSession.d.ts} +2 -2
- package/dist/tools/direct/finalizeDirectSession.js +86 -0
- package/dist/tools/direct/index.d.ts +3 -0
- package/dist/tools/direct/index.js +3 -0
- package/dist/tools/{runDirectVerificationBundle.js → direct/runDirectVerificationBundle.js} +2 -2
- package/dist/tools/{discoverTools.d.ts → discovery/discoverTools.d.ts} +2 -2
- package/dist/tools/{discoverTools.js → discovery/discoverTools.js} +5 -5
- package/dist/tools/{explainTool.d.ts → discovery/explainTool.d.ts} +2 -2
- package/dist/tools/{explainTool.js → discovery/explainTool.js} +2 -2
- package/dist/tools/discovery/index.d.ts +3 -0
- package/dist/tools/discovery/index.js +3 -0
- package/dist/tools/{invokeDiscoveredTool.d.ts → discovery/invokeDiscoveredTool.d.ts} +2 -2
- package/dist/tools/{invokeDiscoveredTool.js → discovery/invokeDiscoveredTool.js} +5 -5
- package/dist/tools/dispatch/coreDispatch.d.ts +9 -0
- package/dist/tools/dispatch/coreDispatch.js +283 -0
- package/dist/tools/dispatch/diagnosticDispatch.d.ts +12 -0
- package/dist/tools/dispatch/diagnosticDispatch.js +80 -0
- package/dist/tools/dispatch/directDispatch.d.ts +8 -0
- package/dist/tools/dispatch/directDispatch.js +116 -0
- package/dist/tools/dispatch/goalDispatch.d.ts +8 -0
- package/dist/tools/dispatch/goalDispatch.js +92 -0
- package/dist/tools/dispatch/releaseDispatch.d.ts +8 -0
- package/dist/tools/dispatch/releaseDispatch.js +46 -0
- package/dist/tools/dispatch/types.d.ts +23 -0
- package/dist/tools/dispatch/types.js +15 -0
- package/dist/tools/dispatch/validation.d.ts +7 -0
- package/dist/tools/dispatch/validation.js +71 -0
- package/dist/tools/{getPlan.js → goals/getPlan.js} +4 -4
- package/dist/tools/{goalSubgoalTask.d.ts → goals/goalSubgoalTask.d.ts} +2 -2
- package/dist/tools/goals/goalSubgoalTask.js +118 -0
- package/dist/tools/goals/index.d.ts +3 -0
- package/dist/tools/goals/index.js +3 -0
- package/dist/tools/{savePlan.js → goals/savePlan.js} +28 -8
- package/dist/tools/registry.d.ts +10 -14
- package/dist/tools/registry.js +47 -1651
- package/dist/tools/{checkReleaseGate.d.ts → release/checkReleaseGate.d.ts} +1 -1
- package/dist/tools/{checkReleaseGate.js → release/checkReleaseGate.js} +3 -3
- package/dist/tools/release/index.d.ts +2 -0
- package/dist/tools/release/index.js +2 -0
- package/dist/tools/{releaseMode.d.ts → release/releaseMode.d.ts} +2 -2
- package/dist/tools/{releaseMode.js → release/releaseMode.js} +46 -26
- package/dist/tools/tasks/cancelTask.d.ts +11 -0
- package/dist/tools/tasks/cancelTask.js +85 -0
- package/dist/tools/{createTask.d.ts → tasks/createTask.d.ts} +12 -4
- package/dist/tools/{createTask.js → tasks/createTask.js} +46 -31
- package/dist/tools/{diagnoseTask.d.ts → tasks/diagnoseTask.d.ts} +1 -1
- package/dist/tools/{diagnoseTask.js → tasks/diagnoseTask.js} +8 -8
- package/dist/tools/{evidencePack.d.ts → tasks/evidencePack.d.ts} +6 -0
- package/dist/tools/tasks/evidencePack.js +387 -0
- package/dist/tools/{getTaskFile.js → tasks/getTaskFile.js} +4 -4
- package/dist/tools/{getTaskStatus.d.ts → tasks/getTaskStatus.d.ts} +6 -1
- package/dist/tools/{getTaskStatus.js → tasks/getTaskStatus.js} +5 -5
- package/dist/tools/{getTaskStdoutTail.js → tasks/getTaskStdoutTail.js} +13 -11
- package/dist/tools/{getTaskSummary.js → tasks/getTaskSummary.js} +33 -26
- package/dist/tools/tasks/index.d.ts +18 -0
- package/dist/tools/tasks/index.js +18 -0
- package/dist/tools/tasks/killTask.d.ts +1 -0
- package/dist/tools/{listTasks.d.ts → tasks/listTasks.d.ts} +1 -1
- package/dist/tools/{listTasks.js → tasks/listTasks.js} +3 -3
- package/dist/tools/{reconcileTasks.d.ts → tasks/reconcileTasks.d.ts} +1 -1
- package/dist/tools/{reconcileTasks.js → tasks/reconcileTasks.js} +64 -66
- package/dist/tools/{retryTask.d.ts → tasks/retryTask.d.ts} +2 -2
- package/dist/tools/tasks/retryTask.js +60 -0
- package/dist/tools/{runTaskLoop.d.ts → tasks/runTaskLoop.d.ts} +6 -6
- package/dist/tools/{runTaskLoop.js → tasks/runTaskLoop.js} +59 -31
- package/dist/tools/tasks/runVerification.js +38 -0
- package/dist/tools/{taskLineage.js → tasks/taskLineage.js} +8 -7
- package/dist/tools/{taskOutputs.js → tasks/taskOutputs.js} +17 -16
- package/dist/tools/{androidDoctor.js → workspace/androidDoctor.js} +16 -10
- package/dist/tools/{applyPatch.d.ts → workspace/applyPatch.d.ts} +1 -1
- package/dist/tools/workspace/applyPatch.js +53 -0
- package/dist/tools/{discardWorktree.js → workspace/discardWorktree.js} +3 -3
- package/dist/tools/workspace/index.d.ts +10 -0
- package/dist/tools/workspace/index.js +10 -0
- package/dist/tools/{listAgents.d.ts → workspace/listAgents.d.ts} +5 -0
- package/dist/tools/{listAgents.js → workspace/listAgents.js} +8 -4
- package/dist/tools/{listWorkspace.js → workspace/listWorkspace.js} +3 -3
- package/dist/tools/{mergeWorktree.js → workspace/mergeWorktree.js} +3 -3
- package/dist/tools/{readWorkspaceFile.js → workspace/readWorkspaceFile.js} +8 -8
- package/dist/tools/{recommendAgentForTask.js → workspace/recommendAgentForTask.js} +3 -3
- package/dist/tools/{searchWorkspace.js → workspace/searchWorkspace.js} +5 -5
- package/dist/tools/{syncFile.d.ts → workspace/syncFile.d.ts} +1 -1
- package/dist/tools/workspace/syncFile.js +134 -0
- package/dist/utils/atomicFile.d.ts +7 -0
- package/dist/utils/atomicFile.js +48 -0
- package/dist/utils/boundedFile.d.ts +8 -0
- package/dist/utils/boundedFile.js +93 -0
- package/dist/utils/lockedJsonFile.d.ts +15 -0
- package/dist/utils/lockedJsonFile.js +224 -0
- package/dist/utils/platform.d.ts +11 -0
- package/dist/utils/platform.js +26 -0
- package/dist/utils/stableJson.d.ts +2 -0
- package/dist/utils/stableJson.js +12 -0
- package/dist/version.d.ts +3 -2
- package/dist/version.js +3 -2
- package/dist/watcherStatus.d.ts +2 -2
- package/dist/watcherStatus.js +18 -0
- package/docs/CODE_WIKI.md +1648 -0
- package/docs/agentseal-integration.md +150 -0
- package/docs/architecture.md +23 -0
- package/docs/assets/patchwarden-desktop-onboarding.png +0 -0
- package/docs/assets/patchwarden-oss-demo.gif +0 -0
- package/docs/control-center/README.md +4 -4
- package/docs/dashboard-overview.md +86 -0
- package/docs/demo.md +8 -0
- package/docs/desktop-app.md +175 -0
- package/docs/direct-session-workflow.md +98 -0
- package/docs/evidence-pack-schema.md +215 -0
- package/docs/lineage-evidence-pack-workflow.md +127 -0
- package/docs/mcp-inspector-testing.md +200 -0
- package/docs/open-source-application.md +168 -0
- package/docs/opencode-worker.md +151 -0
- package/docs/openhands-worker.md +181 -0
- package/docs/release-checklist.md +1 -1
- package/docs/release-evidence.md +82 -0
- package/docs/spec-kit-integration.md +131 -0
- package/docs/task-safe-review-workflow.md +98 -0
- package/docs/threat-model.md +97 -0
- package/docs/user-feedback.md +40 -0
- package/docs/why-patchwarden.md +110 -0
- package/examples/config.example.json +4 -2
- package/package.json +28 -11
- package/scripts/README.md +15 -6
- package/scripts/build.js +11 -0
- package/scripts/checks/brand-check.js +3 -0
- package/scripts/checks/build-output-check.js +37 -0
- package/scripts/checks/control-center-smoke.js +447 -6
- package/scripts/checks/control-smoke.js +19 -8
- package/scripts/checks/http-mcp-smoke.js +82 -4
- package/scripts/checks/lifecycle-smoke.js +39 -33
- package/scripts/checks/mcp-manifest-check.js +1 -1
- package/scripts/checks/mcp-smoke.js +30 -4
- package/scripts/checks/package-manifest-check.js +20 -7
- package/scripts/checks/unit-tests.js +3 -5
- package/scripts/checks/watcher-supervisor-smoke.js +9 -0
- package/scripts/control/get-patchwarden-health.ps1 +1 -1
- package/scripts/control/manage-patchwarden.ps1 +69 -4
- package/scripts/control/provision-patchwarden-tunnel.ps1 +127 -0
- package/scripts/control/run-background-supervisor.ps1 +41 -0
- package/scripts/control/start-patchwarden-tunnel.ps1 +126 -32
- package/scripts/generate-demo-gif.py +320 -0
- package/{PatchWarden-Control-Tray.cmd → scripts/launchers/PatchWarden-Control-Tray.cmd} +3 -3
- package/scripts/launchers/PatchWarden-Control.cmd +6 -0
- package/{PatchWarden-Desktop.cmd → scripts/launchers/PatchWarden-Desktop.cmd} +2 -2
- package/scripts/launchers/Restart-PatchWarden-Control.cmd +6 -0
- package/{Stop-PatchWarden.cmd → scripts/launchers/Stop-PatchWarden.cmd} +2 -2
- package/scripts/lib/clean-generated-output.js +11 -0
- package/scripts/lib/file-discovery.js +12 -0
- package/scripts/release/desktop-preflight.js +211 -0
- package/scripts/release/pack-clean.js +59 -14
- package/src/assessments/agentAssessor.ts +29 -15
- package/src/assessments/assessmentStore.ts +55 -43
- package/src/assessments/confirmCli.ts +5 -4
- package/src/config.ts +105 -11
- package/src/control/fileManager.ts +40 -0
- package/src/control/middleware/auth.ts +35 -0
- package/src/control/middleware/static.ts +84 -0
- package/src/control/routeTable.ts +301 -0
- package/src/control/routes/audit.ts +329 -0
- package/src/control/routes/evidence.ts +107 -0
- package/src/control/routes/lineage.ts +92 -0
- package/src/control/routes/policy.ts +81 -0
- package/src/control/routes/process.ts +505 -0
- package/src/control/routes/sessions.ts +314 -0
- package/src/control/routes/status.ts +405 -0
- package/src/control/routes/taskActions.ts +214 -0
- package/src/control/routes/tasks.ts +331 -0
- package/src/control/routes/workspace.ts +234 -0
- package/src/control/runtime.ts +508 -0
- package/src/control/server.ts +165 -0
- package/src/control/shared.ts +360 -0
- package/src/controlCenter.ts +7 -2347
- package/src/direct/directAudit.ts +17 -9
- package/src/direct/directGuards.ts +100 -51
- package/src/direct/directPatch.ts +95 -8
- package/src/direct/directSessionStore.ts +211 -55
- package/src/direct/directVerification.ts +9 -23
- package/src/doctor.ts +766 -492
- package/src/goal/acceptanceEngine.ts +1 -1
- package/src/goal/goalProgress.ts +17 -13
- package/src/goal/goalReport.ts +263 -0
- package/src/goal/goalStatus.ts +25 -3
- package/src/goal/goalStore.ts +241 -54
- package/src/goal/handoffExport.ts +6 -6
- package/src/goal/specKitImport.ts +355 -0
- package/src/goal/subgoalSync.ts +15 -10
- package/src/goal/worktreeManager.ts +139 -22
- package/src/httpServer.ts +130 -44
- package/src/index.ts +7 -4
- package/src/logging.ts +20 -4
- package/src/policy/projectPolicy.ts +5 -2
- package/src/release/releaseGate.ts +74 -27
- package/src/runner/agentInvocation.ts +126 -4
- package/src/runner/changeCapture.ts +342 -80
- package/src/runner/cli.ts +7 -6
- package/src/runner/postTaskCleanup.ts +55 -7
- package/src/runner/processSecurity.ts +433 -0
- package/src/runner/runTask.ts +441 -292
- package/src/runner/simpleProcess.ts +68 -33
- package/src/{taskProgress.ts → runner/taskProgress.ts} +3 -3
- package/src/{taskRuntime.ts → runner/taskRuntime.ts} +4 -3
- package/src/runner/taskStatusStore.ts +54 -0
- package/src/runner/watch.ts +290 -57
- package/src/security/contentRedaction.ts +31 -2
- package/src/security/discoveryTokenStore.ts +18 -4
- package/src/security/loopbackHost.ts +19 -0
- package/src/security/pathGuard.ts +35 -16
- package/src/security/planGuard.ts +2 -1
- package/src/security/runtimeGuard.ts +9 -6
- package/src/security/secretComparison.ts +13 -0
- package/src/security/sensitiveGuard.ts +27 -8
- package/src/security/toolInvocationGuard.ts +2 -2
- package/src/security/workspaceRootGuard.ts +53 -0
- package/src/smoke-test.ts +276 -273
- package/src/tools/catalog/index.ts +4 -0
- package/src/tools/{toolCatalog.ts → catalog/toolCatalog.ts} +3 -12
- package/src/tools/{toolRegistry.ts → catalog/toolRegistry.ts} +29 -16
- package/src/tools/definitions/toolDefs.ts +1264 -0
- package/src/tools/{auditSession.ts → diagnostics/auditSession.ts} +2 -2
- package/src/tools/{auditTask.ts → diagnostics/auditTask.ts} +196 -89
- package/src/tools/{healthCheck.ts → diagnostics/healthCheck.ts} +11 -11
- package/src/tools/diagnostics/index.ts +6 -0
- package/src/tools/{safeStatus.ts → diagnostics/safeStatus.ts} +7 -7
- package/src/tools/{safeViews.ts → diagnostics/safeViews.ts} +29 -19
- package/src/tools/{schemaDriftCheck.ts → diagnostics/schemaDriftCheck.ts} +3 -3
- package/src/tools/{createDirectSession.ts → direct/createDirectSession.ts} +9 -9
- package/src/tools/{finalizeDirectSession.ts → direct/finalizeDirectSession.ts} +11 -9
- package/src/tools/direct/index.ts +3 -0
- package/src/tools/{runDirectVerificationBundle.ts → direct/runDirectVerificationBundle.ts} +2 -2
- package/src/tools/{discoverTools.ts → discovery/discoverTools.ts} +6 -6
- package/src/tools/{explainTool.ts → discovery/explainTool.ts} +3 -3
- package/src/tools/discovery/index.ts +3 -0
- package/src/tools/{invokeDiscoveredTool.ts → discovery/invokeDiscoveredTool.ts} +7 -7
- package/src/tools/dispatch/coreDispatch.ts +379 -0
- package/src/tools/dispatch/diagnosticDispatch.ts +106 -0
- package/src/tools/dispatch/directDispatch.ts +168 -0
- package/src/tools/dispatch/goalDispatch.ts +128 -0
- package/src/tools/dispatch/releaseDispatch.ts +66 -0
- package/src/tools/dispatch/types.ts +24 -0
- package/src/tools/dispatch/validation.ts +78 -0
- package/src/tools/{getPlan.ts → goals/getPlan.ts} +4 -4
- package/src/tools/{goalSubgoalTask.ts → goals/goalSubgoalTask.ts} +43 -28
- package/src/tools/goals/index.ts +3 -0
- package/src/tools/{savePlan.ts → goals/savePlan.ts} +38 -8
- package/src/tools/registry.ts +54 -1858
- package/src/tools/{checkReleaseGate.ts → release/checkReleaseGate.ts} +3 -3
- package/src/tools/release/index.ts +2 -0
- package/src/tools/{releaseMode.ts → release/releaseMode.ts} +42 -24
- package/src/tools/tasks/cancelTask.ts +109 -0
- package/src/tools/{createTask.ts → tasks/createTask.ts} +56 -33
- package/src/tools/{diagnoseTask.ts → tasks/diagnoseTask.ts} +8 -8
- package/src/tools/tasks/evidencePack.ts +504 -0
- package/src/tools/{getTaskFile.ts → tasks/getTaskFile.ts} +4 -4
- package/src/tools/{getTaskStatus.ts → tasks/getTaskStatus.ts} +7 -5
- package/src/tools/{getTaskStdoutTail.ts → tasks/getTaskStdoutTail.ts} +13 -11
- package/src/tools/{getTaskSummary.ts → tasks/getTaskSummary.ts} +44 -33
- package/src/tools/tasks/index.ts +18 -0
- package/src/tools/{listTasks.ts → tasks/listTasks.ts} +4 -4
- package/src/tools/{reconcileTasks.ts → tasks/reconcileTasks.ts} +68 -66
- package/src/tools/tasks/retryTask.ts +69 -0
- package/src/tools/{runTaskLoop.ts → tasks/runTaskLoop.ts} +70 -35
- package/src/tools/{runVerification.ts → tasks/runVerification.ts} +6 -3
- package/src/tools/{taskLineage.ts → tasks/taskLineage.ts} +8 -7
- package/src/tools/{taskOutputs.ts → tasks/taskOutputs.ts} +27 -20
- package/src/tools/{androidDoctor.ts → workspace/androidDoctor.ts} +15 -10
- package/src/tools/{applyPatch.ts → workspace/applyPatch.ts} +38 -16
- package/src/tools/{discardWorktree.ts → workspace/discardWorktree.ts} +3 -3
- package/src/tools/workspace/index.ts +10 -0
- package/src/tools/{listAgents.ts → workspace/listAgents.ts} +11 -4
- package/src/tools/{listWorkspace.ts → workspace/listWorkspace.ts} +3 -3
- package/src/tools/{mergeWorktree.ts → workspace/mergeWorktree.ts} +3 -3
- package/src/tools/{readWorkspaceFile.ts → workspace/readWorkspaceFile.ts} +8 -8
- package/src/tools/{recommendAgentForTask.ts → workspace/recommendAgentForTask.ts} +3 -3
- package/src/tools/{searchWorkspace.ts → workspace/searchWorkspace.ts} +5 -5
- package/src/tools/workspace/syncFile.ts +292 -0
- package/src/utils/atomicFile.ts +58 -0
- package/src/utils/boundedFile.ts +116 -0
- package/src/utils/lockedJsonFile.ts +251 -0
- package/src/utils/platform.ts +31 -0
- package/src/utils/stableJson.ts +12 -0
- package/src/version.ts +3 -2
- package/src/watcherStatus.ts +19 -1
- package/ui/desktop-bootstrap.js +11 -0
- package/ui/desktop-bridge.js +36 -0
- package/ui/desktop.css +160 -0
- package/ui/getting-started.js +93 -0
- package/ui/i18n.js +484 -0
- package/ui/log-parser.js +30 -0
- package/ui/pages/audit.html +204 -3
- package/ui/pages/dashboard.html +937 -79
- package/ui/pages/direct-sessions.html +609 -60
- package/ui/pages/getting-started.html +43 -0
- package/ui/pages/logs.html +105 -22
- package/ui/pages/settings.html +101 -0
- package/ui/pages/task-detail.html +468 -438
- package/ui/pages/tasks.html +648 -65
- package/ui/pages/workspace.html +38 -1
- package/ui/settings.js +245 -0
- package/PatchWarden-Control.cmd +0 -6
- package/Restart-PatchWarden-Control.cmd +0 -6
- package/dist/test/unit/acceptance-engine.test.d.ts +0 -1
- package/dist/test/unit/acceptance-engine.test.js +0 -228
- package/dist/test/unit/agent-router.test.d.ts +0 -1
- package/dist/test/unit/agent-router.test.js +0 -287
- package/dist/test/unit/android-doctor.test.d.ts +0 -1
- package/dist/test/unit/android-doctor.test.js +0 -118
- package/dist/test/unit/audit-checks.test.d.ts +0 -1
- package/dist/test/unit/audit-checks.test.js +0 -350
- package/dist/test/unit/chinese-path.test.d.ts +0 -1
- package/dist/test/unit/chinese-path.test.js +0 -91
- package/dist/test/unit/command-guard.test.d.ts +0 -1
- package/dist/test/unit/command-guard.test.js +0 -160
- package/dist/test/unit/diagnose-task.test.d.ts +0 -1
- package/dist/test/unit/diagnose-task.test.js +0 -457
- package/dist/test/unit/direct-guards.test.d.ts +0 -1
- package/dist/test/unit/direct-guards.test.js +0 -213
- package/dist/test/unit/discovery-token-store.test.d.ts +0 -1
- package/dist/test/unit/discovery-token-store.test.js +0 -139
- package/dist/test/unit/evidence-pack.test.d.ts +0 -1
- package/dist/test/unit/evidence-pack.test.js +0 -130
- package/dist/test/unit/goal-graph.test.d.ts +0 -1
- package/dist/test/unit/goal-graph.test.js +0 -298
- package/dist/test/unit/goal-progress.test.d.ts +0 -1
- package/dist/test/unit/goal-progress.test.js +0 -381
- package/dist/test/unit/goal-status.test.d.ts +0 -1
- package/dist/test/unit/goal-status.test.js +0 -215
- package/dist/test/unit/goal-store.test.d.ts +0 -1
- package/dist/test/unit/goal-store.test.js +0 -253
- package/dist/test/unit/goal-subgoal-task.test.d.ts +0 -1
- package/dist/test/unit/goal-subgoal-task.test.js +0 -55
- package/dist/test/unit/goal-tools-registry.test.d.ts +0 -1
- package/dist/test/unit/goal-tools-registry.test.js +0 -190
- package/dist/test/unit/handoff-export.test.d.ts +0 -1
- package/dist/test/unit/handoff-export.test.js +0 -263
- package/dist/test/unit/invoke-discovered-tool.test.d.ts +0 -1
- package/dist/test/unit/invoke-discovered-tool.test.js +0 -167
- package/dist/test/unit/logging.test.d.ts +0 -1
- package/dist/test/unit/logging.test.js +0 -397
- package/dist/test/unit/path-guard.test.d.ts +0 -1
- package/dist/test/unit/path-guard.test.js +0 -109
- package/dist/test/unit/post-task-cleanup.test.d.ts +0 -1
- package/dist/test/unit/post-task-cleanup.test.js +0 -48
- package/dist/test/unit/project-policy-release-mode.test.d.ts +0 -1
- package/dist/test/unit/project-policy-release-mode.test.js +0 -125
- package/dist/test/unit/reconcile-tasks.test.d.ts +0 -1
- package/dist/test/unit/reconcile-tasks.test.js +0 -456
- package/dist/test/unit/release-gate.test.d.ts +0 -1
- package/dist/test/unit/release-gate.test.js +0 -242
- package/dist/test/unit/run-task-loop.test.d.ts +0 -1
- package/dist/test/unit/run-task-loop.test.js +0 -380
- package/dist/test/unit/safe-status.test.d.ts +0 -1
- package/dist/test/unit/safe-status.test.js +0 -165
- package/dist/test/unit/safe-views.test.d.ts +0 -1
- package/dist/test/unit/safe-views.test.js +0 -171
- package/dist/test/unit/schema-drift-check.test.d.ts +0 -1
- package/dist/test/unit/schema-drift-check.test.js +0 -176
- package/dist/test/unit/sensitive-guard.test.d.ts +0 -1
- package/dist/test/unit/sensitive-guard.test.js +0 -104
- package/dist/test/unit/subgoal-sync.test.d.ts +0 -1
- package/dist/test/unit/subgoal-sync.test.js +0 -183
- package/dist/test/unit/sync-file.test.d.ts +0 -1
- package/dist/test/unit/sync-file.test.js +0 -154
- package/dist/test/unit/tool-invocation-guard.test.d.ts +0 -1
- package/dist/test/unit/tool-invocation-guard.test.js +0 -432
- package/dist/test/unit/tool-usage-stats.test.d.ts +0 -1
- package/dist/test/unit/tool-usage-stats.test.js +0 -300
- package/dist/test/unit/toolSearch.test.d.ts +0 -1
- package/dist/test/unit/toolSearch.test.js +0 -571
- package/dist/test/unit/watcher-status.test.d.ts +0 -1
- package/dist/test/unit/watcher-status.test.js +0 -169
- package/dist/test/unit/worktree-manager.test.d.ts +0 -1
- package/dist/test/unit/worktree-manager.test.js +0 -176
- package/dist/tools/applyPatch.js +0 -41
- package/dist/tools/cancelTask.d.ts +0 -30
- package/dist/tools/cancelTask.js +0 -64
- package/dist/tools/evidencePack.js +0 -168
- package/dist/tools/finalizeDirectSession.js +0 -84
- package/dist/tools/goalSubgoalTask.js +0 -110
- package/dist/tools/killTask.d.ts +0 -15
- package/dist/tools/retryTask.js +0 -45
- package/dist/tools/runVerification.js +0 -36
- package/dist/tools/syncFile.js +0 -65
- package/docs/control-center/control-center-daily-driver.md +0 -211
- package/docs/control-center/control-center-mvp.md +0 -205
- package/docs/control-center/control-center-phase2.md +0 -159
- package/docs/release-v0.6.0.md +0 -71
- package/docs/release-v0.6.1.md +0 -75
- package/docs/release-v0.6.4.md +0 -45
- package/src/test/unit/acceptance-engine.test.ts +0 -261
- package/src/test/unit/agent-router.test.ts +0 -342
- package/src/test/unit/android-doctor.test.ts +0 -158
- package/src/test/unit/audit-checks.test.ts +0 -567
- package/src/test/unit/chinese-path.test.ts +0 -106
- package/src/test/unit/command-guard.test.ts +0 -221
- package/src/test/unit/diagnose-task.test.ts +0 -544
- package/src/test/unit/direct-guards.test.ts +0 -297
- package/src/test/unit/discovery-token-store.test.ts +0 -181
- package/src/test/unit/evidence-pack.test.ts +0 -142
- package/src/test/unit/goal-graph.test.ts +0 -347
- package/src/test/unit/goal-progress.test.ts +0 -538
- package/src/test/unit/goal-status.test.ts +0 -270
- package/src/test/unit/goal-store.test.ts +0 -318
- package/src/test/unit/goal-subgoal-task.test.ts +0 -72
- package/src/test/unit/goal-tools-registry.test.ts +0 -243
- package/src/test/unit/handoff-export.test.ts +0 -295
- package/src/test/unit/invoke-discovered-tool.test.ts +0 -216
- package/src/test/unit/logging.test.ts +0 -497
- package/src/test/unit/path-guard.test.ts +0 -150
- package/src/test/unit/post-task-cleanup.test.ts +0 -53
- package/src/test/unit/project-policy-release-mode.test.ts +0 -156
- package/src/test/unit/reconcile-tasks.test.ts +0 -551
- package/src/test/unit/release-gate.test.ts +0 -314
- package/src/test/unit/run-task-loop.test.ts +0 -425
- package/src/test/unit/safe-status.test.ts +0 -187
- package/src/test/unit/safe-views.test.ts +0 -184
- package/src/test/unit/schema-drift-check.test.ts +0 -260
- package/src/test/unit/sensitive-guard.test.ts +0 -124
- package/src/test/unit/subgoal-sync.test.ts +0 -236
- package/src/test/unit/sync-file.test.ts +0 -231
- package/src/test/unit/tool-invocation-guard.test.ts +0 -542
- package/src/test/unit/tool-usage-stats.test.ts +0 -384
- package/src/test/unit/toolSearch.test.ts +0 -756
- package/src/test/unit/watcher-status.test.ts +0 -190
- package/src/test/unit/worktree-manager.test.ts +0 -247
- package/src/tools/cancelTask.ts +0 -70
- package/src/tools/evidencePack.ts +0 -205
- package/src/tools/retryTask.ts +0 -53
- package/src/tools/syncFile.ts +0 -122
- /package/dist/tools/{toolCatalog.d.ts → catalog/toolCatalog.d.ts} +0 -0
- /package/dist/tools/{toolSearch.d.ts → catalog/toolSearch.d.ts} +0 -0
- /package/dist/tools/{toolSearch.js → catalog/toolSearch.js} +0 -0
- /package/dist/tools/{toolUsageStats.d.ts → catalog/toolUsageStats.d.ts} +0 -0
- /package/dist/tools/{toolUsageStats.js → catalog/toolUsageStats.js} +0 -0
- /package/dist/tools/{runDirectVerificationBundle.d.ts → direct/runDirectVerificationBundle.d.ts} +0 -0
- /package/dist/tools/{getPlan.d.ts → goals/getPlan.d.ts} +0 -0
- /package/dist/tools/{savePlan.d.ts → goals/savePlan.d.ts} +0 -0
- /package/dist/tools/{getTaskFile.d.ts → tasks/getTaskFile.d.ts} +0 -0
- /package/dist/tools/{getTaskProgress.d.ts → tasks/getTaskProgress.d.ts} +0 -0
- /package/dist/tools/{getTaskProgress.js → tasks/getTaskProgress.js} +0 -0
- /package/dist/tools/{getTaskStdoutTail.d.ts → tasks/getTaskStdoutTail.d.ts} +0 -0
- /package/dist/tools/{getTaskSummary.d.ts → tasks/getTaskSummary.d.ts} +0 -0
- /package/dist/tools/{killTask.js → tasks/killTask.js} +0 -0
- /package/dist/tools/{runVerification.d.ts → tasks/runVerification.d.ts} +0 -0
- /package/dist/tools/{taskLineage.d.ts → tasks/taskLineage.d.ts} +0 -0
- /package/dist/tools/{taskOutputs.d.ts → tasks/taskOutputs.d.ts} +0 -0
- /package/dist/tools/{waitForTask.d.ts → tasks/waitForTask.d.ts} +0 -0
- /package/dist/tools/{waitForTask.js → tasks/waitForTask.js} +0 -0
- /package/dist/tools/{androidDoctor.d.ts → workspace/androidDoctor.d.ts} +0 -0
- /package/dist/tools/{discardWorktree.d.ts → workspace/discardWorktree.d.ts} +0 -0
- /package/dist/tools/{listWorkspace.d.ts → workspace/listWorkspace.d.ts} +0 -0
- /package/dist/tools/{mergeWorktree.d.ts → workspace/mergeWorktree.d.ts} +0 -0
- /package/dist/tools/{readWorkspaceFile.d.ts → workspace/readWorkspaceFile.d.ts} +0 -0
- /package/dist/tools/{recommendAgentForTask.d.ts → workspace/recommendAgentForTask.d.ts} +0 -0
- /package/dist/tools/{searchWorkspace.d.ts → workspace/searchWorkspace.d.ts} +0 -0
- /package/src/tools/{toolSearch.ts → catalog/toolSearch.ts} +0 -0
- /package/src/tools/{toolUsageStats.ts → catalog/toolUsageStats.ts} +0 -0
- /package/src/tools/{getTaskProgress.ts → tasks/getTaskProgress.ts} +0 -0
- /package/src/tools/{killTask.ts → tasks/killTask.ts} +0 -0
- /package/src/tools/{waitForTask.ts → tasks/waitForTask.ts} +0 -0
|
@@ -1,26 +1,40 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
-
import { lstatSync, readFileSync, readdirSync,
|
|
2
|
+
import { createReadStream, lstatSync, readFileSync, readdirSync, } from "node:fs";
|
|
3
3
|
import { join, relative, resolve, isAbsolute } from "node:path";
|
|
4
|
-
import {
|
|
4
|
+
import { execFile } from "node:child_process";
|
|
5
|
+
import { redactSensitiveContent } from "../security/contentRedaction.js";
|
|
5
6
|
import { isSensitivePath } from "../security/sensitiveGuard.js";
|
|
7
|
+
import { nullDevice } from "../utils/platform.js";
|
|
8
|
+
import { buildGitEnvironment, resolveTrustedExecutable } from "./processSecurity.js";
|
|
9
|
+
import { atomicWriteJsonFileSync } from "../utils/atomicFile.js";
|
|
6
10
|
const MAX_HASH_BYTES = 5 * 1024 * 1024;
|
|
7
11
|
const MAX_SNAPSHOT_FILES = 5000;
|
|
8
12
|
const MAX_DIFF_BYTES = 20 * 1024 * 1024;
|
|
13
|
+
const DIFF_TRUNCATION_MARKER = "\n[PATCHWARDEN DIFF TRUNCATED]\n";
|
|
9
14
|
const SKIP_DIRECTORIES = new Set([".git", ".patchwarden", "node_modules"]);
|
|
10
|
-
export function captureRepoSnapshot(repoPath) {
|
|
15
|
+
export async function captureRepoSnapshot(repoPath, signal) {
|
|
16
|
+
throwIfAborted(signal);
|
|
11
17
|
const warnings = [];
|
|
12
|
-
const
|
|
18
|
+
const isGitResult = await runGit(repoPath, ["rev-parse", "--is-inside-work-tree"], signal);
|
|
19
|
+
const isGit = isGitResult.stdout.trim() === "true";
|
|
13
20
|
let head = null;
|
|
14
21
|
let status = "";
|
|
15
22
|
let paths = [];
|
|
16
|
-
const trackedPaths = new
|
|
17
|
-
const ignoredPaths = new
|
|
23
|
+
const trackedPaths = new Map();
|
|
24
|
+
const ignoredPaths = new Map();
|
|
18
25
|
const dirtyPaths = new Set();
|
|
19
26
|
if (isGit) {
|
|
20
|
-
|
|
27
|
+
// The following five git calls are independent of each other and can run in parallel.
|
|
28
|
+
const [headResult, statusResult, trackedResult, ignoredResult, listedResult] = await Promise.all([
|
|
29
|
+
runGit(repoPath, ["rev-parse", "HEAD"], signal),
|
|
30
|
+
runGit(repoPath, ["status", "--porcelain=v1", "-uall"], signal),
|
|
31
|
+
runGit(repoPath, ["ls-files", "-z"], signal),
|
|
32
|
+
runGit(repoPath, ["ls-files", "-o", "-i", "--exclude-standard", "-z"], signal),
|
|
33
|
+
runGit(repoPath, ["ls-files", "-co", "--exclude-standard", "-z"], signal),
|
|
34
|
+
]);
|
|
21
35
|
if (headResult.status === 0)
|
|
22
36
|
head = headResult.stdout.trim() || null;
|
|
23
|
-
status =
|
|
37
|
+
status = statusResult.stdout.trimEnd();
|
|
24
38
|
// Parse git status --porcelain to collect all dirty paths
|
|
25
39
|
for (const line of status.split("\n")) {
|
|
26
40
|
if (line.length < 4)
|
|
@@ -45,34 +59,31 @@ export function captureRepoSnapshot(repoPath) {
|
|
|
45
59
|
}
|
|
46
60
|
}
|
|
47
61
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
trackedPaths.add(normalizePath(path));
|
|
62
|
+
if (trackedResult.status === 0) {
|
|
63
|
+
for (const path of trackedResult.stdout.split("\0").filter(Boolean))
|
|
64
|
+
addSnapshotPath(trackedPaths, path);
|
|
52
65
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
ignoredPaths.add(normalizePath(path));
|
|
66
|
+
if (ignoredResult.status === 0) {
|
|
67
|
+
for (const path of ignoredResult.stdout.split("\0").filter(Boolean))
|
|
68
|
+
addSnapshotPath(ignoredPaths, path);
|
|
57
69
|
}
|
|
58
70
|
else {
|
|
59
71
|
warnings.push("git ignored-file discovery failed; ignored classification may be incomplete");
|
|
60
72
|
}
|
|
61
|
-
|
|
62
|
-
if (listed.status === 0) {
|
|
73
|
+
if (listedResult.status === 0) {
|
|
63
74
|
paths = [...new Set([
|
|
64
|
-
...
|
|
65
|
-
...walkWorkspace(repoPath),
|
|
75
|
+
...listedResult.stdout.split("\0").filter(Boolean),
|
|
76
|
+
...walkWorkspace(repoPath, signal),
|
|
66
77
|
])];
|
|
67
78
|
}
|
|
68
79
|
else {
|
|
69
80
|
warnings.push("git ls-files failed; using bounded filesystem scan");
|
|
70
|
-
paths = walkWorkspace(repoPath);
|
|
81
|
+
paths = walkWorkspace(repoPath, signal);
|
|
71
82
|
}
|
|
72
83
|
}
|
|
73
84
|
else {
|
|
74
85
|
warnings.push("repository is not a Git worktree; diff will contain file-change evidence only");
|
|
75
|
-
paths = walkWorkspace(repoPath);
|
|
86
|
+
paths = walkWorkspace(repoPath, signal);
|
|
76
87
|
}
|
|
77
88
|
if (paths.length > MAX_SNAPSHOT_FILES) {
|
|
78
89
|
warnings.push(`snapshot limited to ${MAX_SNAPSHOT_FILES} files`);
|
|
@@ -80,6 +91,7 @@ export function captureRepoSnapshot(repoPath) {
|
|
|
80
91
|
}
|
|
81
92
|
const files = {};
|
|
82
93
|
for (const inputPath of paths.sort()) {
|
|
94
|
+
throwIfAborted(signal);
|
|
83
95
|
const normalized = normalizePath(inputPath);
|
|
84
96
|
if (!normalized || normalized.startsWith(".patchwarden/") || isSensitivePath(normalized))
|
|
85
97
|
continue;
|
|
@@ -94,8 +106,8 @@ export function captureRepoSnapshot(repoPath) {
|
|
|
94
106
|
files[normalized] = {
|
|
95
107
|
size: stat.size,
|
|
96
108
|
sha256,
|
|
97
|
-
tracked: trackedPaths
|
|
98
|
-
ignored: !trackedPaths
|
|
109
|
+
tracked: hasSnapshotPath(trackedPaths, normalized),
|
|
110
|
+
ignored: !hasSnapshotPath(trackedPaths, normalized) && hasSnapshotPath(ignoredPaths, normalized),
|
|
99
111
|
};
|
|
100
112
|
}
|
|
101
113
|
catch {
|
|
@@ -114,32 +126,14 @@ export function captureRepoSnapshot(repoPath) {
|
|
|
114
126
|
};
|
|
115
127
|
}
|
|
116
128
|
export function writeSnapshot(taskDir, filename, snapshot) {
|
|
117
|
-
|
|
129
|
+
atomicWriteJsonFileSync(join(taskDir, filename), snapshot);
|
|
118
130
|
}
|
|
119
|
-
export function buildChangeArtifacts(repoPath, before, after) {
|
|
131
|
+
export async function buildChangeArtifacts(repoPath, before, after, signal) {
|
|
132
|
+
throwIfAborted(signal);
|
|
120
133
|
const changedFiles = compareSnapshots(before, after);
|
|
121
134
|
const artifactHygiene = classifyArtifactHygiene(changedFiles);
|
|
122
135
|
const sections = [];
|
|
123
136
|
const scopedPaths = [...new Set(changedFiles.flatMap((file) => file.old_path ? [file.old_path, file.path] : [file.path]))];
|
|
124
|
-
if (before.is_git && after.is_git && scopedPaths.length > 0) {
|
|
125
|
-
if (before.head && after.head && before.head !== after.head) {
|
|
126
|
-
const committed = runGit(repoPath, ["diff", "--no-color", "--binary", before.head, after.head, "--", ...scopedPaths]);
|
|
127
|
-
if (committed.stdout.trim())
|
|
128
|
-
sections.push("# Changes committed during task\n", committed.stdout.trimEnd());
|
|
129
|
-
}
|
|
130
|
-
const base = after.head || "HEAD";
|
|
131
|
-
const working = runGit(repoPath, ["diff", "--no-color", "--binary", base, "--", ...scopedPaths]);
|
|
132
|
-
if (working.stdout.trim())
|
|
133
|
-
sections.push("# Staged and unstaged changes\n", working.stdout.trimEnd());
|
|
134
|
-
for (const file of changedFiles.filter((item) => item.change === "added").slice(0, 100)) {
|
|
135
|
-
const tracked = runGit(repoPath, ["ls-files", "--error-unmatch", "--", file.path]);
|
|
136
|
-
if (tracked.status === 0)
|
|
137
|
-
continue;
|
|
138
|
-
const untracked = runGit(repoPath, ["diff", "--no-index", "--no-color", "--binary", "--", "/dev/null", file.path]);
|
|
139
|
-
if (untracked.stdout.trim())
|
|
140
|
-
sections.push("# Untracked file\n", untracked.stdout.trimEnd());
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
137
|
const evidence = [
|
|
144
138
|
"# PatchWarden change evidence",
|
|
145
139
|
`# changed_files: ${changedFiles.length}`,
|
|
@@ -147,16 +141,70 @@ export function buildChangeArtifacts(repoPath, before, after) {
|
|
|
147
141
|
`# workspace_dirty_after: ${after.workspace_dirty}`,
|
|
148
142
|
...changedFiles.map((file) => `# ${file.change}: ${file.path}`),
|
|
149
143
|
].join("\n");
|
|
150
|
-
|
|
151
|
-
|
|
144
|
+
let retainedSectionBytes = 0;
|
|
145
|
+
let diffTruncated = Buffer.byteLength(evidence, "utf-8") > MAX_DIFF_BYTES;
|
|
146
|
+
const sectionBudget = Math.max(0, MAX_DIFF_BYTES
|
|
147
|
+
- Math.min(Buffer.byteLength(evidence, "utf-8"), MAX_DIFF_BYTES)
|
|
148
|
+
- Buffer.byteLength(DIFF_TRUNCATION_MARKER, "utf-8")
|
|
149
|
+
- 2);
|
|
150
|
+
const appendSection = (label, content, commandTruncated = false) => {
|
|
151
|
+
if (!content.trim()) {
|
|
152
|
+
if (commandTruncated)
|
|
153
|
+
diffTruncated = true;
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
const block = `${sections.length > 0 ? "\n\n" : ""}${label}\n\n${content.trimEnd()}`;
|
|
157
|
+
const remaining = Math.max(0, sectionBudget - retainedSectionBytes);
|
|
158
|
+
const bounded = utf8Prefix(block, remaining);
|
|
159
|
+
if (bounded) {
|
|
160
|
+
sections.push(bounded);
|
|
161
|
+
retainedSectionBytes += Buffer.byteLength(bounded, "utf-8");
|
|
162
|
+
}
|
|
163
|
+
if (commandTruncated || Buffer.byteLength(bounded, "utf-8") < Buffer.byteLength(block, "utf-8")) {
|
|
164
|
+
diffTruncated = true;
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
if (before.is_git && after.is_git && scopedPaths.length > 0) {
|
|
168
|
+
if (before.head && after.head && before.head !== after.head) {
|
|
169
|
+
const committed = await runGit(repoPath, ["diff", "--no-ext-diff", "--no-textconv", "--no-color", "--binary", before.head, after.head, "--", ...scopedPaths], signal);
|
|
170
|
+
appendSection("# Changes committed during task", committed.stdout, committed.truncated);
|
|
171
|
+
}
|
|
172
|
+
if (retainedSectionBytes < sectionBudget) {
|
|
173
|
+
const base = after.head || "HEAD";
|
|
174
|
+
const working = await runGit(repoPath, ["diff", "--no-ext-diff", "--no-textconv", "--no-color", "--binary", base, "--", ...scopedPaths], signal);
|
|
175
|
+
appendSection("# Staged and unstaged changes", working.stdout, working.truncated);
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
diffTruncated = true;
|
|
179
|
+
}
|
|
180
|
+
for (const file of changedFiles.filter((item) => item.change === "added").slice(0, 100)) {
|
|
181
|
+
throwIfAborted(signal);
|
|
182
|
+
if (retainedSectionBytes >= sectionBudget) {
|
|
183
|
+
diffTruncated = true;
|
|
184
|
+
break;
|
|
185
|
+
}
|
|
186
|
+
const tracked = await runGit(repoPath, ["ls-files", "--error-unmatch", "--", file.path], signal);
|
|
187
|
+
if (tracked.status === 0)
|
|
188
|
+
continue;
|
|
189
|
+
const untracked = await runGit(repoPath, ["diff", "--no-ext-diff", "--no-textconv", "--no-index", "--no-color", "--binary", "--", nullDevice, file.path], signal);
|
|
190
|
+
appendSection("# Untracked file", untracked.stdout, untracked.truncated);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
const body = sections.join("");
|
|
194
|
+
const rawDiff = `${evidence}\n\n${body || (changedFiles.length ? "(textual patch unavailable; see changed-files.json for hash evidence)" : "(no task file changes detected)")}\n`;
|
|
195
|
+
const sanitized = sanitizeDiffEvidence(rawDiff, MAX_DIFF_BYTES);
|
|
196
|
+
const fullDiff = sanitized.content;
|
|
197
|
+
diffTruncated ||= sanitized.truncated;
|
|
152
198
|
const additions = fullDiff.split(/\r?\n/).filter((line) => line.startsWith("+") && !line.startsWith("+++")).length;
|
|
153
199
|
const deletions = fullDiff.split(/\r?\n/).filter((line) => line.startsWith("-") && !line.startsWith("---")).length;
|
|
154
|
-
const fileStats = buildFileStats(repoPath, before, after, changedFiles);
|
|
200
|
+
const fileStats = await buildFileStats(repoPath, before, after, changedFiles, signal);
|
|
155
201
|
return {
|
|
156
202
|
changed_files: changedFiles,
|
|
157
203
|
diff: fullDiff,
|
|
158
204
|
diff_available: changedFiles.length > 0,
|
|
159
|
-
diff_truncated:
|
|
205
|
+
diff_truncated: diffTruncated,
|
|
206
|
+
diff_redacted: sanitized.redacted,
|
|
207
|
+
diff_redaction_categories: sanitized.redaction_categories,
|
|
160
208
|
diff_size_bytes: Buffer.byteLength(fullDiff, "utf-8"),
|
|
161
209
|
additions,
|
|
162
210
|
deletions,
|
|
@@ -172,8 +220,32 @@ export function buildChangeArtifacts(repoPath, before, after) {
|
|
|
172
220
|
artifact_hygiene: artifactHygiene,
|
|
173
221
|
};
|
|
174
222
|
}
|
|
175
|
-
function
|
|
176
|
-
|
|
223
|
+
export function sanitizeDiffEvidence(input, maxBytes = MAX_DIFF_BYTES) {
|
|
224
|
+
if (!Number.isInteger(maxBytes) || maxBytes <= 0) {
|
|
225
|
+
throw new Error("maxBytes must be a positive integer");
|
|
226
|
+
}
|
|
227
|
+
const redaction = redactSensitiveContent(input);
|
|
228
|
+
const encodedBytes = Buffer.byteLength(redaction.content, "utf-8");
|
|
229
|
+
if (encodedBytes <= maxBytes) {
|
|
230
|
+
return {
|
|
231
|
+
content: redaction.content,
|
|
232
|
+
truncated: false,
|
|
233
|
+
redacted: redaction.redacted,
|
|
234
|
+
redaction_categories: redaction.redaction_categories,
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
const marker = utf8Prefix(DIFF_TRUNCATION_MARKER, maxBytes);
|
|
238
|
+
const markerBytes = Buffer.byteLength(marker, "utf-8");
|
|
239
|
+
return {
|
|
240
|
+
content: utf8Prefix(redaction.content, Math.max(0, maxBytes - markerBytes)) + marker,
|
|
241
|
+
truncated: true,
|
|
242
|
+
redacted: redaction.redacted,
|
|
243
|
+
redaction_categories: redaction.redaction_categories,
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
async function buildFileStats(repoPath, before, after, changedFiles, signal) {
|
|
247
|
+
const results = await Promise.all(changedFiles.map(async (file) => {
|
|
248
|
+
throwIfAborted(signal);
|
|
177
249
|
let additions = 0;
|
|
178
250
|
let deletions = 0;
|
|
179
251
|
const paths = file.old_path ? [file.old_path, file.path] : [file.path];
|
|
@@ -183,8 +255,8 @@ function buildFileStats(repoPath, before, after, changedFiles) {
|
|
|
183
255
|
ranges.push([before.head, after.head]);
|
|
184
256
|
}
|
|
185
257
|
ranges.push([after.head || "HEAD"]);
|
|
186
|
-
|
|
187
|
-
|
|
258
|
+
const rangeResults = await Promise.all(ranges.map((range) => runGit(repoPath, ["diff", "--no-ext-diff", "--no-textconv", "--numstat", ...range, "--", ...paths], signal)));
|
|
259
|
+
for (const result of rangeResults) {
|
|
188
260
|
for (const line of result.stdout.split(/\r?\n/).filter(Boolean)) {
|
|
189
261
|
const [added, removed] = line.split(/\s+/);
|
|
190
262
|
if (/^\d+$/.test(added))
|
|
@@ -199,29 +271,41 @@ function buildFileStats(repoPath, before, after, changedFiles) {
|
|
|
199
271
|
const content = readFileSync(resolve(repoPath, file.path), "utf-8");
|
|
200
272
|
additions = countLines(content);
|
|
201
273
|
}
|
|
202
|
-
catch { }
|
|
274
|
+
catch { } // probe failure handled by return value (additions stays 0)
|
|
203
275
|
}
|
|
204
276
|
return { path: file.path, status: file.change, additions, deletions };
|
|
205
|
-
});
|
|
277
|
+
}));
|
|
278
|
+
return results;
|
|
206
279
|
}
|
|
207
280
|
function countLines(content) {
|
|
208
281
|
if (content.length === 0)
|
|
209
282
|
return 0;
|
|
210
283
|
return content.split(/\r?\n/).length - (content.endsWith("\n") ? 1 : 0);
|
|
211
284
|
}
|
|
212
|
-
export function compareSnapshots(before, after) {
|
|
213
|
-
const paths = [...new Set([...Object.keys(before.files), ...Object.keys(after.files)])].sort();
|
|
285
|
+
export function compareSnapshots(before, after, platform = process.platform) {
|
|
214
286
|
const changed = [];
|
|
215
|
-
for (const path of
|
|
216
|
-
const left = before.files[path];
|
|
217
|
-
const right = after.files[path];
|
|
287
|
+
for (const { path, leftPath, rightPath, left, right } of pairSnapshotFiles(before.files, after.files, platform)) {
|
|
218
288
|
if (!left && right) {
|
|
219
289
|
changed.push(classifyChangedFile(path, "added", null, right));
|
|
220
290
|
}
|
|
221
291
|
else if (left && !right) {
|
|
222
292
|
changed.push(classifyChangedFile(path, "deleted", left, null));
|
|
223
293
|
}
|
|
224
|
-
else if (left.sha256
|
|
294
|
+
else if (left && right && left.sha256 === right.sha256 && leftPath !== rightPath) {
|
|
295
|
+
// Windows lookup is case-insensitive, but a case-only Git rename is still
|
|
296
|
+
// auditable work and must not disappear from the evidence set.
|
|
297
|
+
changed.push({
|
|
298
|
+
path: rightPath,
|
|
299
|
+
old_path: leftPath,
|
|
300
|
+
change: "renamed",
|
|
301
|
+
before_sha256: left.sha256,
|
|
302
|
+
after_sha256: right.sha256,
|
|
303
|
+
tracked: left.tracked || right.tracked,
|
|
304
|
+
ignored: right.ignored,
|
|
305
|
+
kind: classifyPathKind(rightPath),
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
else if (left && right && left.sha256 !== right.sha256) {
|
|
225
309
|
changed.push(classifyChangedFile(path, "modified", left, right));
|
|
226
310
|
}
|
|
227
311
|
}
|
|
@@ -274,9 +358,11 @@ export function emptyArtifactHygiene() {
|
|
|
274
358
|
* Extract files that are dirty in the workspace but outside the target repo.
|
|
275
359
|
* Used to establish a baseline before task execution.
|
|
276
360
|
*/
|
|
277
|
-
export function extractExternalDirtyFiles(workspaceSnapshot, repoPath, workspaceRoot) {
|
|
361
|
+
export function extractExternalDirtyFiles(workspaceSnapshot, repoPath, workspaceRoot, platform = process.platform) {
|
|
278
362
|
const dirtyFiles = [];
|
|
279
|
-
const dirtyPathSet = new
|
|
363
|
+
const dirtyPathSet = new Map();
|
|
364
|
+
for (const path of workspaceSnapshot.dirty_paths)
|
|
365
|
+
addSnapshotPath(dirtyPathSet, path, platform);
|
|
280
366
|
for (const [path, fingerprint] of Object.entries(workspaceSnapshot.files)) {
|
|
281
367
|
const absolutePath = resolve(workspaceRoot, path);
|
|
282
368
|
const rel = relative(repoPath, absolutePath);
|
|
@@ -286,7 +372,7 @@ export function extractExternalDirtyFiles(workspaceSnapshot, repoPath, workspace
|
|
|
286
372
|
// 1. Git reports it as dirty (modified/added/deleted/untracked) via dirty_paths, OR
|
|
287
373
|
// 2. It's not tracked by git (untracked file), OR
|
|
288
374
|
// 3. It's explicitly ignored
|
|
289
|
-
const isDirty = dirtyPathSet
|
|
375
|
+
const isDirty = hasSnapshotPath(dirtyPathSet, path, platform);
|
|
290
376
|
const isUntracked = !fingerprint.tracked;
|
|
291
377
|
const isIgnored = fingerprint.ignored;
|
|
292
378
|
if (isDirty || isUntracked || isIgnored) {
|
|
@@ -306,10 +392,17 @@ export function extractExternalDirtyFiles(workspaceSnapshot, repoPath, workspace
|
|
|
306
392
|
* Returns files that are NEW (not present in baseline) or CHANGED
|
|
307
393
|
* (same path but different sha256, meaning the task modified them).
|
|
308
394
|
*/
|
|
309
|
-
export function findNewExternalDirtyFiles(baseline, current) {
|
|
310
|
-
const baselineMap = new Map(
|
|
395
|
+
export function findNewExternalDirtyFiles(baseline, current, platform = process.platform) {
|
|
396
|
+
const baselineMap = new Map();
|
|
397
|
+
for (const file of baseline) {
|
|
398
|
+
const key = comparableSnapshotPath(file.path, platform);
|
|
399
|
+
baselineMap.set(key, [...(baselineMap.get(key) ?? []), file]);
|
|
400
|
+
}
|
|
311
401
|
return current.filter((f) => {
|
|
312
|
-
const
|
|
402
|
+
const candidates = baselineMap.get(comparableSnapshotPath(f.path, platform)) ?? [];
|
|
403
|
+
const baselineFile = candidates.length <= 1
|
|
404
|
+
? candidates[0]
|
|
405
|
+
: candidates.find((candidate) => normalizePath(candidate.path) === normalizePath(f.path));
|
|
313
406
|
if (!baselineFile)
|
|
314
407
|
return true; // New path — definitely new
|
|
315
408
|
// Same path but content changed during task execution
|
|
@@ -318,7 +411,7 @@ export function findNewExternalDirtyFiles(baseline, current) {
|
|
|
318
411
|
return false;
|
|
319
412
|
});
|
|
320
413
|
}
|
|
321
|
-
export function buildArtifactManifest(changedFiles, repoPath, taskId) {
|
|
414
|
+
export async function buildArtifactManifest(changedFiles, repoPath, taskId) {
|
|
322
415
|
const entries = [];
|
|
323
416
|
for (const file of changedFiles) {
|
|
324
417
|
if (file.kind !== "build_artifact")
|
|
@@ -331,7 +424,7 @@ export function buildArtifactManifest(changedFiles, repoPath, taskId) {
|
|
|
331
424
|
if (stat.isFile()) {
|
|
332
425
|
size = stat.size;
|
|
333
426
|
if (size <= MAX_HASH_BYTES) {
|
|
334
|
-
sha256 =
|
|
427
|
+
sha256 = await hashFileAsync(absolutePath);
|
|
335
428
|
}
|
|
336
429
|
}
|
|
337
430
|
}
|
|
@@ -475,9 +568,72 @@ function classificationReason(change) {
|
|
|
475
568
|
function normalizePath(value) {
|
|
476
569
|
return value.replace(/\\/g, "/");
|
|
477
570
|
}
|
|
478
|
-
function
|
|
571
|
+
function comparableSnapshotPath(value, platform = process.platform) {
|
|
572
|
+
const normalized = normalizePath(value);
|
|
573
|
+
return platform === "win32" ? normalized.toLowerCase() : normalized;
|
|
574
|
+
}
|
|
575
|
+
function addSnapshotPath(index, value, platform = process.platform) {
|
|
576
|
+
const normalized = normalizePath(value);
|
|
577
|
+
const key = comparableSnapshotPath(normalized, platform);
|
|
578
|
+
const exactPaths = index.get(key) ?? new Set();
|
|
579
|
+
exactPaths.add(normalized);
|
|
580
|
+
index.set(key, exactPaths);
|
|
581
|
+
}
|
|
582
|
+
function hasSnapshotPath(index, value, platform = process.platform) {
|
|
583
|
+
const normalized = normalizePath(value);
|
|
584
|
+
const exactPaths = index.get(comparableSnapshotPath(normalized, platform));
|
|
585
|
+
if (!exactPaths)
|
|
586
|
+
return false;
|
|
587
|
+
return platform !== "win32" || exactPaths.size <= 1 || exactPaths.has(normalized);
|
|
588
|
+
}
|
|
589
|
+
function pairSnapshotFiles(before, after, platform) {
|
|
590
|
+
const beforeGroups = groupSnapshotFiles(before, platform);
|
|
591
|
+
const afterGroups = groupSnapshotFiles(after, platform);
|
|
592
|
+
const pairs = [];
|
|
593
|
+
const comparablePaths = [...new Set([...beforeGroups.keys(), ...afterGroups.keys()])].sort();
|
|
594
|
+
for (const comparablePath of comparablePaths) {
|
|
595
|
+
const beforeEntries = beforeGroups.get(comparablePath) ?? [];
|
|
596
|
+
const afterEntries = afterGroups.get(comparablePath) ?? [];
|
|
597
|
+
if (platform !== "win32" || (beforeEntries.length <= 1 && afterEntries.length <= 1)) {
|
|
598
|
+
pairs.push({
|
|
599
|
+
path: afterEntries[0]?.path ?? beforeEntries[0].path,
|
|
600
|
+
leftPath: beforeEntries[0]?.path,
|
|
601
|
+
rightPath: afterEntries[0]?.path,
|
|
602
|
+
left: beforeEntries[0]?.fingerprint,
|
|
603
|
+
right: afterEntries[0]?.fingerprint,
|
|
604
|
+
});
|
|
605
|
+
continue;
|
|
606
|
+
}
|
|
607
|
+
// NTFS can opt individual directories into case-sensitive mode. If either
|
|
608
|
+
// snapshot contains a case collision, preserve every exact path instead of
|
|
609
|
+
// silently dropping one entry from the evidence.
|
|
610
|
+
const beforeExact = new Map(beforeEntries.map((entry) => [normalizePath(entry.path), entry]));
|
|
611
|
+
const afterExact = new Map(afterEntries.map((entry) => [normalizePath(entry.path), entry]));
|
|
612
|
+
const exactPaths = [...new Set([...beforeExact.keys(), ...afterExact.keys()])].sort();
|
|
613
|
+
for (const exactPath of exactPaths) {
|
|
614
|
+
pairs.push({
|
|
615
|
+
path: afterExact.get(exactPath)?.path ?? beforeExact.get(exactPath).path,
|
|
616
|
+
leftPath: beforeExact.get(exactPath)?.path,
|
|
617
|
+
rightPath: afterExact.get(exactPath)?.path,
|
|
618
|
+
left: beforeExact.get(exactPath)?.fingerprint,
|
|
619
|
+
right: afterExact.get(exactPath)?.fingerprint,
|
|
620
|
+
});
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
return pairs;
|
|
624
|
+
}
|
|
625
|
+
function groupSnapshotFiles(files, platform) {
|
|
626
|
+
const grouped = new Map();
|
|
627
|
+
for (const [path, fingerprint] of Object.entries(files)) {
|
|
628
|
+
const key = comparableSnapshotPath(path, platform);
|
|
629
|
+
grouped.set(key, [...(grouped.get(key) ?? []), { path, fingerprint }]);
|
|
630
|
+
}
|
|
631
|
+
return grouped;
|
|
632
|
+
}
|
|
633
|
+
function walkWorkspace(root, signal) {
|
|
479
634
|
const result = [];
|
|
480
635
|
const visit = (directory) => {
|
|
636
|
+
throwIfAborted(signal);
|
|
481
637
|
if (result.length >= MAX_SNAPSHOT_FILES)
|
|
482
638
|
return;
|
|
483
639
|
let entries;
|
|
@@ -488,6 +644,7 @@ function walkWorkspace(root) {
|
|
|
488
644
|
return;
|
|
489
645
|
}
|
|
490
646
|
for (const entry of entries) {
|
|
647
|
+
throwIfAborted(signal);
|
|
491
648
|
if (result.length >= MAX_SNAPSHOT_FILES)
|
|
492
649
|
break;
|
|
493
650
|
if (entry.isDirectory() && SKIP_DIRECTORIES.has(entry.name))
|
|
@@ -502,16 +659,65 @@ function walkWorkspace(root) {
|
|
|
502
659
|
visit(root);
|
|
503
660
|
return result;
|
|
504
661
|
}
|
|
505
|
-
function runGit(repoPath, args) {
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
662
|
+
function runGit(repoPath, args, signal) {
|
|
663
|
+
throwIfAborted(signal);
|
|
664
|
+
return new Promise((resolve, reject) => {
|
|
665
|
+
const env = buildGitEnvironment(repoPath);
|
|
666
|
+
const git = resolveTrustedExecutable("git", repoPath, { pathValue: env.PATH });
|
|
667
|
+
execFile(git, args, {
|
|
668
|
+
cwd: repoPath,
|
|
669
|
+
encoding: "utf-8",
|
|
670
|
+
timeout: 30_000,
|
|
671
|
+
maxBuffer: MAX_DIFF_BYTES,
|
|
672
|
+
windowsHide: true,
|
|
673
|
+
env,
|
|
674
|
+
signal,
|
|
675
|
+
}, (err, stdout, stderr) => {
|
|
676
|
+
if (signal?.aborted) {
|
|
677
|
+
reject(abortReason(signal));
|
|
678
|
+
return;
|
|
679
|
+
}
|
|
680
|
+
let status;
|
|
681
|
+
if (err) {
|
|
682
|
+
// err.code is the exit code (number) when the process exited with a non-zero status;
|
|
683
|
+
// it is a string (e.g. "ENOENT") when spawning failed or the buffer limit was hit.
|
|
684
|
+
status = typeof err.code === "number" ? err.code : null;
|
|
685
|
+
}
|
|
686
|
+
else {
|
|
687
|
+
status = 0;
|
|
688
|
+
}
|
|
689
|
+
const truncated = Boolean(err
|
|
690
|
+
&& typeof err.code === "string"
|
|
691
|
+
&& err.code.includes("MAXBUFFER"));
|
|
692
|
+
resolve({ status, stdout: stdout || "", stderr: stderr || "", truncated });
|
|
693
|
+
});
|
|
694
|
+
});
|
|
695
|
+
}
|
|
696
|
+
function throwIfAborted(signal) {
|
|
697
|
+
if (signal?.aborted)
|
|
698
|
+
throw abortReason(signal);
|
|
699
|
+
}
|
|
700
|
+
function abortReason(signal) {
|
|
701
|
+
return signal.reason instanceof Error ? signal.reason : new Error("Operation aborted");
|
|
702
|
+
}
|
|
703
|
+
function utf8Prefix(value, maxBytes) {
|
|
704
|
+
if (maxBytes <= 0)
|
|
705
|
+
return "";
|
|
706
|
+
const encoded = Buffer.from(value, "utf-8");
|
|
707
|
+
if (encoded.length <= maxBytes)
|
|
708
|
+
return value;
|
|
709
|
+
let prefix = encoded.subarray(0, maxBytes).toString("utf-8").replace(/\uFFFD$/u, "");
|
|
710
|
+
while (Buffer.byteLength(prefix, "utf-8") > maxBytes)
|
|
711
|
+
prefix = prefix.slice(0, -1);
|
|
712
|
+
return prefix;
|
|
713
|
+
}
|
|
714
|
+
function hashFileAsync(filePath) {
|
|
715
|
+
return new Promise((resolve, reject) => {
|
|
716
|
+
const hash = createHash("sha256");
|
|
717
|
+
const stream = createReadStream(filePath);
|
|
718
|
+
stream.on("error", reject);
|
|
719
|
+
hash.on("error", reject);
|
|
720
|
+
hash.on("finish", () => resolve(hash.digest("hex")));
|
|
721
|
+
stream.pipe(hash);
|
|
511
722
|
});
|
|
512
|
-
return {
|
|
513
|
-
status: result.status,
|
|
514
|
-
stdout: result.stdout || "",
|
|
515
|
-
stderr: result.stderr || "",
|
|
516
|
-
};
|
|
517
723
|
}
|
package/dist/runner/cli.js
CHANGED
|
@@ -8,19 +8,20 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { runTask } from "./runTask.js";
|
|
10
10
|
import { loadConfig } from "../config.js";
|
|
11
|
+
import { logger } from "../logging.js";
|
|
11
12
|
// Load config early
|
|
12
13
|
loadConfig();
|
|
13
14
|
const taskId = process.argv[2] || process.env.PATCHWARDEN_TASK_ID;
|
|
14
15
|
if (!taskId) {
|
|
15
|
-
|
|
16
|
-
|
|
16
|
+
logger.info("Usage: node dist/runner/cli.js <task_id>");
|
|
17
|
+
logger.info(" or: npm run runner -- <task_id>");
|
|
17
18
|
process.exit(1);
|
|
18
19
|
}
|
|
19
|
-
|
|
20
|
+
logger.info(`[runner] Starting task: ${taskId}`);
|
|
20
21
|
const result = await runTask(taskId);
|
|
21
|
-
|
|
22
|
+
logger.info(`[runner] Task ${result.task_id}: ${result.status}`);
|
|
22
23
|
if (result.error) {
|
|
23
|
-
|
|
24
|
+
logger.fatal(`[runner] Error: ${result.error}`);
|
|
24
25
|
process.exit(1);
|
|
25
26
|
}
|
|
26
|
-
|
|
27
|
+
logger.info("[runner] Done.");
|