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
package/dist/httpServer.js
CHANGED
|
@@ -15,44 +15,60 @@
|
|
|
15
15
|
* or: npm run start:http
|
|
16
16
|
*/
|
|
17
17
|
import { createServer } from "node:http";
|
|
18
|
-
import { existsSync, readFileSync
|
|
18
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
19
19
|
import { join, resolve } from "node:path";
|
|
20
20
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
21
21
|
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
22
22
|
import { loadConfig, getTasksDir } from "./config.js";
|
|
23
23
|
import { registerTools } from "./tools/registry.js";
|
|
24
|
-
import { healthCheck } from "./tools/healthCheck.js";
|
|
24
|
+
import { healthCheck } from "./tools/diagnostics/healthCheck.js";
|
|
25
25
|
import { getToolCatalogSnapshot } from "./tools/registry.js";
|
|
26
26
|
import { PATCHWARDEN_VERSION } from "./version.js";
|
|
27
|
+
import { logger } from "./logging.js";
|
|
28
|
+
import { firstHeaderValue, timingSafeStringEqual } from "./security/secretComparison.js";
|
|
29
|
+
import { redactSensitiveContent } from "./security/contentRedaction.js";
|
|
30
|
+
import { atomicWriteJsonFileSync } from "./utils/atomicFile.js";
|
|
31
|
+
import { mutateTaskStatus } from "./runner/taskStatusStore.js";
|
|
32
|
+
import { isTrustedLoopbackHostHeader } from "./security/loopbackHost.js";
|
|
27
33
|
// ── Bootstrap ─────────────────────────────────────────────────────
|
|
28
34
|
const config = loadConfig();
|
|
29
35
|
const port = parseInt(process.env.PATCHWARDEN_HTTP_PORT || "") ||
|
|
30
36
|
config.httpPort ||
|
|
31
37
|
7331;
|
|
32
38
|
const host = "127.0.0.1";
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
39
|
+
logger.info(`[patchwarden-http] Workspace: ${config.workspaceRoot}`);
|
|
40
|
+
logger.info(`[patchwarden-http] Listening: http://${host}:${port}/mcp`);
|
|
41
|
+
logger.info(`[patchwarden-http] ⚠️ Bound to 127.0.0.1 only — not exposed to network`);
|
|
36
42
|
// ── Owner token (optional) ────────────────────────────────────────
|
|
37
43
|
const httpCfg = config.http || {};
|
|
38
44
|
const ownerTokenEnv = httpCfg.ownerTokenEnv || "PATCHWARDEN_OWNER_TOKEN";
|
|
39
45
|
const ownerToken = process.env[ownerTokenEnv] || "";
|
|
46
|
+
const MAX_ADMIN_BODY_BYTES = 64 * 1024;
|
|
47
|
+
const MAX_ACCEPTANCE_NOTES_CHARS = 10_000;
|
|
48
|
+
const REVIEWABLE_TASK_STATUSES = new Set([
|
|
49
|
+
"done_by_agent",
|
|
50
|
+
"done",
|
|
51
|
+
"accepted",
|
|
52
|
+
"rejected",
|
|
53
|
+
"needs_fix",
|
|
54
|
+
"blocked",
|
|
55
|
+
]);
|
|
40
56
|
if (ownerToken) {
|
|
41
|
-
|
|
57
|
+
logger.info(`[patchwarden-http] 🔒 Owner token required (env: ${ownerTokenEnv})`);
|
|
42
58
|
}
|
|
43
59
|
else {
|
|
44
|
-
|
|
60
|
+
logger.info(`[patchwarden-http] ⚠️ No owner token set — all local requests accepted`);
|
|
45
61
|
}
|
|
46
62
|
function checkOwnerToken(req) {
|
|
47
63
|
if (!ownerToken)
|
|
48
64
|
return true; // no token configured — allow all
|
|
49
|
-
const authHeader = req.headers["authorization"]
|
|
50
|
-
const customHeader = req.headers["x-patchwarden-token"]
|
|
65
|
+
const authHeader = firstHeaderValue(req.headers["authorization"]);
|
|
66
|
+
const customHeader = firstHeaderValue(req.headers["x-patchwarden-token"]);
|
|
51
67
|
if (authHeader.startsWith("Bearer ")) {
|
|
52
|
-
return authHeader.slice(7)
|
|
68
|
+
return timingSafeStringEqual(authHeader.slice(7), ownerToken);
|
|
53
69
|
}
|
|
54
|
-
if (
|
|
55
|
-
return customHeader
|
|
70
|
+
if (customHeader.length > 0) {
|
|
71
|
+
return timingSafeStringEqual(customHeader, ownerToken);
|
|
56
72
|
}
|
|
57
73
|
return false;
|
|
58
74
|
}
|
|
@@ -68,12 +84,23 @@ function handleAcceptance(taskId, status, body) {
|
|
|
68
84
|
throw Object.assign(new Error(`Task "${taskId}" not found.`), { statusCode: 404 });
|
|
69
85
|
}
|
|
70
86
|
let notes = "";
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
87
|
+
if (body.trim()) {
|
|
88
|
+
let parsed;
|
|
89
|
+
try {
|
|
90
|
+
parsed = JSON.parse(body);
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
throw httpError(400, "Acceptance body must be valid JSON.");
|
|
94
|
+
}
|
|
95
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
96
|
+
throw httpError(400, "Acceptance body must be a JSON object.");
|
|
97
|
+
}
|
|
98
|
+
const record = parsed;
|
|
99
|
+
const rawNotes = record.notes ?? record.reason ?? "";
|
|
100
|
+
if (typeof rawNotes !== "string") {
|
|
101
|
+
throw httpError(400, "Acceptance notes or reason must be a string.");
|
|
102
|
+
}
|
|
103
|
+
notes = redactSensitiveContent(rawNotes).content.slice(0, MAX_ACCEPTANCE_NOTES_CHARS);
|
|
77
104
|
}
|
|
78
105
|
const acceptance = {
|
|
79
106
|
status,
|
|
@@ -81,22 +108,67 @@ function handleAcceptance(taskId, status, body) {
|
|
|
81
108
|
reviewer: "human",
|
|
82
109
|
notes,
|
|
83
110
|
};
|
|
84
|
-
|
|
85
|
-
//
|
|
111
|
+
// Commit the review artifact and status annotation while holding the same
|
|
112
|
+
// status lock used by the runner, cancel, and audit paths.
|
|
86
113
|
const statusFile = join(taskDir, "status.json");
|
|
87
|
-
if (existsSync(statusFile)) {
|
|
88
|
-
|
|
89
|
-
const current = JSON.parse(readFileSync(statusFile, "utf-8"));
|
|
90
|
-
current.acceptance_status = status;
|
|
91
|
-
current.acceptance_reviewed_at = acceptance.reviewed_at;
|
|
92
|
-
writeFileSync(statusFile, JSON.stringify(current, null, 2), "utf-8");
|
|
93
|
-
}
|
|
94
|
-
catch {
|
|
95
|
-
// status.json update is best-effort
|
|
96
|
-
}
|
|
114
|
+
if (!existsSync(statusFile)) {
|
|
115
|
+
throw httpError(409, `Task "${taskId}" has no status.json to review.`);
|
|
97
116
|
}
|
|
117
|
+
mutateTaskStatus(statusFile, (current) => {
|
|
118
|
+
if (!REVIEWABLE_TASK_STATUSES.has(String(current.status || ""))) {
|
|
119
|
+
throw httpError(409, `Task "${taskId}" is not in a reviewable terminal state.`);
|
|
120
|
+
}
|
|
121
|
+
atomicWriteJsonFileSync(filePath, acceptance);
|
|
122
|
+
const next = {
|
|
123
|
+
...current,
|
|
124
|
+
acceptance_status: status,
|
|
125
|
+
acceptance_reviewed_at: acceptance.reviewed_at,
|
|
126
|
+
updated_at: new Date().toISOString(),
|
|
127
|
+
};
|
|
128
|
+
return { next, result: next };
|
|
129
|
+
});
|
|
98
130
|
return acceptance;
|
|
99
131
|
}
|
|
132
|
+
function httpError(statusCode, message) {
|
|
133
|
+
return Object.assign(new Error(message), { statusCode });
|
|
134
|
+
}
|
|
135
|
+
function readBoundedRequestBody(req, maxBytes) {
|
|
136
|
+
const declaredLength = Number(firstHeaderValue(req.headers["content-length"]));
|
|
137
|
+
if (Number.isFinite(declaredLength) && declaredLength > maxBytes) {
|
|
138
|
+
req.resume();
|
|
139
|
+
return Promise.reject(httpError(413, `Request body exceeds ${maxBytes} bytes.`));
|
|
140
|
+
}
|
|
141
|
+
return new Promise((resolveBody, rejectBody) => {
|
|
142
|
+
const chunks = [];
|
|
143
|
+
let total = 0;
|
|
144
|
+
let settled = false;
|
|
145
|
+
req.on("data", (chunk) => {
|
|
146
|
+
if (settled)
|
|
147
|
+
return;
|
|
148
|
+
const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
149
|
+
total += buffer.length;
|
|
150
|
+
if (total > maxBytes) {
|
|
151
|
+
settled = true;
|
|
152
|
+
req.resume();
|
|
153
|
+
rejectBody(httpError(413, `Request body exceeds ${maxBytes} bytes.`));
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
chunks.push(buffer);
|
|
157
|
+
});
|
|
158
|
+
req.on("end", () => {
|
|
159
|
+
if (!settled) {
|
|
160
|
+
settled = true;
|
|
161
|
+
resolveBody(Buffer.concat(chunks).toString("utf-8"));
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
req.on("error", (error) => {
|
|
165
|
+
if (!settled) {
|
|
166
|
+
settled = true;
|
|
167
|
+
rejectBody(error);
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
}
|
|
100
172
|
function readAcceptance(taskId) {
|
|
101
173
|
const filePath = getAcceptancePath(taskId);
|
|
102
174
|
if (!existsSync(filePath)) {
|
|
@@ -123,7 +195,9 @@ async function handleMcpRequest(req, res) {
|
|
|
123
195
|
await transport.handleRequest(req, res);
|
|
124
196
|
}
|
|
125
197
|
catch (err) {
|
|
126
|
-
|
|
198
|
+
logger.error("[patchwarden-http] Request error", {
|
|
199
|
+
error: err instanceof Error ? err.message : String(err),
|
|
200
|
+
});
|
|
127
201
|
if (!res.headersSent) {
|
|
128
202
|
res.writeHead(500, { "Content-Type": "application/json" });
|
|
129
203
|
res.end(JSON.stringify({ error: "Internal server error" }));
|
|
@@ -161,6 +235,15 @@ function parseAdminUrl(url) {
|
|
|
161
235
|
}
|
|
162
236
|
// ── HTTP server ───────────────────────────────────────────────────
|
|
163
237
|
const httpServer = createServer(async (req, res) => {
|
|
238
|
+
if (!isTrustedLoopbackHostHeader(req.headers.host, port)) {
|
|
239
|
+
res.writeHead(421, {
|
|
240
|
+
"Content-Type": "application/json; charset=utf-8",
|
|
241
|
+
"Cache-Control": "no-store",
|
|
242
|
+
"X-Content-Type-Options": "nosniff",
|
|
243
|
+
});
|
|
244
|
+
res.end(JSON.stringify({ error: "Untrusted Host header" }));
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
164
247
|
// Health check endpoints
|
|
165
248
|
if (req.method === "GET" && (req.url === "/healthz" || req.url === "/readyz")) {
|
|
166
249
|
const health = healthCheck(getToolCatalogSnapshot());
|
|
@@ -185,11 +268,7 @@ const httpServer = createServer(async (req, res) => {
|
|
|
185
268
|
return;
|
|
186
269
|
}
|
|
187
270
|
if ((admin.action === "accept" || admin.action === "reject") && req.method === "POST") {
|
|
188
|
-
const
|
|
189
|
-
for await (const chunk of req) {
|
|
190
|
-
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
191
|
-
}
|
|
192
|
-
const body = Buffer.concat(chunks).toString("utf-8");
|
|
271
|
+
const body = await readBoundedRequestBody(req, MAX_ADMIN_BODY_BYTES);
|
|
193
272
|
const acceptance = handleAcceptance(admin.taskId, admin.action === "accept" ? "accepted" : "rejected", body);
|
|
194
273
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
195
274
|
res.end(JSON.stringify(acceptance, null, 2));
|
|
@@ -199,9 +278,12 @@ const httpServer = createServer(async (req, res) => {
|
|
|
199
278
|
res.end(JSON.stringify({ error: "Method not allowed for this admin endpoint." }));
|
|
200
279
|
}
|
|
201
280
|
catch (err) {
|
|
202
|
-
const statusCode = err
|
|
281
|
+
const statusCode = err && typeof err === "object" && "statusCode" in err
|
|
282
|
+
? Number(err.statusCode) || 500
|
|
283
|
+
: 500;
|
|
284
|
+
const message = err instanceof Error ? err.message : "Internal server error";
|
|
203
285
|
res.writeHead(statusCode, { "Content-Type": "application/json" });
|
|
204
|
-
res.end(JSON.stringify({ error:
|
|
286
|
+
res.end(JSON.stringify({ error: message }));
|
|
205
287
|
}
|
|
206
288
|
return;
|
|
207
289
|
}
|
|
@@ -233,21 +315,21 @@ const httpServer = createServer(async (req, res) => {
|
|
|
233
315
|
// ── Start ─────────────────────────────────────────────────────────
|
|
234
316
|
httpServer.on("error", (err) => {
|
|
235
317
|
if (err.code === "EADDRINUSE") {
|
|
236
|
-
|
|
237
|
-
|
|
318
|
+
logger.fatal(`[patchwarden-http] Fatal: port ${port} is already in use on ${host}.`);
|
|
319
|
+
logger.fatal("[patchwarden-http] Stop the other PatchWarden HTTP instance or change httpPort in patchwarden.config.json.");
|
|
238
320
|
}
|
|
239
321
|
else {
|
|
240
|
-
|
|
322
|
+
logger.fatal(`[patchwarden-http] Fatal: ${err.message}`);
|
|
241
323
|
}
|
|
242
324
|
process.exit(1);
|
|
243
325
|
});
|
|
244
326
|
httpServer.listen(port, host, () => {
|
|
245
|
-
|
|
246
|
-
|
|
327
|
+
logger.info(`[patchwarden-http] ✅ Ready`);
|
|
328
|
+
logger.info(`[patchwarden-http] Admin: http://${host}:${port}/admin/tasks/:id/accept`);
|
|
247
329
|
});
|
|
248
330
|
// Graceful shutdown
|
|
249
331
|
process.on("SIGINT", () => {
|
|
250
|
-
|
|
332
|
+
logger.info("[patchwarden-http] Shutting down...");
|
|
251
333
|
httpServer.close(() => process.exit(0));
|
|
252
334
|
});
|
|
253
335
|
process.on("SIGTERM", () => {
|
package/dist/index.js
CHANGED
|
@@ -11,14 +11,17 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
|
|
|
11
11
|
import { loadConfig } from "./config.js";
|
|
12
12
|
import { registerTools } from "./tools/registry.js";
|
|
13
13
|
import { PATCHWARDEN_VERSION } from "./version.js";
|
|
14
|
+
import { logger } from "./logging.js";
|
|
14
15
|
const config = loadConfig();
|
|
15
|
-
|
|
16
|
-
|
|
16
|
+
logger.info(`[patchwarden] Workspace: ${config.workspaceRoot}`);
|
|
17
|
+
logger.info("[patchwarden] Transport: stdio");
|
|
17
18
|
const server = new Server({ name: "patchwarden", version: PATCHWARDEN_VERSION }, { capabilities: { tools: {} } });
|
|
18
19
|
registerTools(server);
|
|
19
20
|
const transport = new StdioServerTransport();
|
|
20
21
|
server.connect(transport).catch((err) => {
|
|
21
|
-
|
|
22
|
+
logger.fatal("[patchwarden] Fatal", {
|
|
23
|
+
error: err instanceof Error ? err.message : String(err),
|
|
24
|
+
});
|
|
22
25
|
process.exit(1);
|
|
23
26
|
});
|
|
24
|
-
|
|
27
|
+
logger.info("[patchwarden] MCP server ready on stdio");
|
package/dist/logging.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type LogLevel = "info" | "warn" | "error" | "audit";
|
|
1
|
+
export type LogLevel = "info" | "warn" | "error" | "fatal" | "audit";
|
|
2
2
|
export interface LogEntry {
|
|
3
3
|
timestamp: string;
|
|
4
4
|
level: LogLevel;
|
|
@@ -39,6 +39,12 @@ export declare class Logger {
|
|
|
39
39
|
info(message: string, context?: Record<string, unknown>): void;
|
|
40
40
|
warn(message: string, context?: Record<string, unknown>): void;
|
|
41
41
|
error(message: string, context?: Record<string, unknown>): void;
|
|
42
|
+
/**
|
|
43
|
+
* Emit a fatal-level log entry. Use this immediately before a process
|
|
44
|
+
* exits with a non-zero status, to record the unrecoverable condition.
|
|
45
|
+
* Level is higher than `error`.
|
|
46
|
+
*/
|
|
47
|
+
fatal(message: string, context?: Record<string, unknown>): void;
|
|
42
48
|
/**
|
|
43
49
|
* Emit a tool-call audit log entry.
|
|
44
50
|
*
|
package/dist/logging.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
-
import {
|
|
2
|
+
import { mkdirSync } from "node:fs";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { redactSensitiveContent } from "./security/contentRedaction.js";
|
|
5
|
-
import { stableJsonStringify } from "./
|
|
5
|
+
import { stableJsonStringify } from "./utils/stableJson.js";
|
|
6
6
|
import { getConfig } from "./config.js";
|
|
7
|
+
import { appendBoundedTextFileSync } from "./utils/boundedFile.js";
|
|
8
|
+
const MAX_INVOCATION_LOG_BYTES = 5 * 1024 * 1024;
|
|
7
9
|
// ── Helpers ───────────────────────────────────────────────────────
|
|
8
10
|
/**
|
|
9
11
|
* Returns true when PATCHWARDEN_VERBOSE_LOG is set to "true".
|
|
@@ -51,6 +53,14 @@ export class Logger {
|
|
|
51
53
|
error(message, context) {
|
|
52
54
|
emit({ timestamp: new Date().toISOString(), level: "error", message, ...context });
|
|
53
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* Emit a fatal-level log entry. Use this immediately before a process
|
|
58
|
+
* exits with a non-zero status, to record the unrecoverable condition.
|
|
59
|
+
* Level is higher than `error`.
|
|
60
|
+
*/
|
|
61
|
+
fatal(message, context) {
|
|
62
|
+
emit({ timestamp: new Date().toISOString(), level: "fatal", message, ...context });
|
|
63
|
+
}
|
|
54
64
|
/**
|
|
55
65
|
* Emit a tool-call audit log entry.
|
|
56
66
|
*
|
|
@@ -171,7 +181,7 @@ export function logToolInvocation(entry, options) {
|
|
|
171
181
|
}
|
|
172
182
|
mkdirSync(logsDir, { recursive: true });
|
|
173
183
|
const logFilePath = join(logsDir, "invocation.log");
|
|
174
|
-
|
|
184
|
+
appendBoundedTextFileSync(logFilePath, JSON.stringify(entry) + "\n", MAX_INVOCATION_LOG_BYTES);
|
|
175
185
|
}
|
|
176
186
|
catch (err) {
|
|
177
187
|
// 日志失败不应阻断主流程,仅向 stderr 记录错误
|
|
@@ -276,10 +276,13 @@ function pathMatchesPattern(relPath, pattern) {
|
|
|
276
276
|
relPath.toLowerCase().startsWith(`${normalizedPattern.toLowerCase()}/`);
|
|
277
277
|
}
|
|
278
278
|
function parseGithubRepo(repository) {
|
|
279
|
+
const repositoryObject = repository && typeof repository === "object" && !Array.isArray(repository)
|
|
280
|
+
? repository
|
|
281
|
+
: null;
|
|
279
282
|
const raw = typeof repository === "string"
|
|
280
283
|
? repository
|
|
281
|
-
:
|
|
282
|
-
?
|
|
284
|
+
: typeof repositoryObject?.url === "string"
|
|
285
|
+
? repositoryObject.url
|
|
283
286
|
: "";
|
|
284
287
|
const match = raw.match(/github\.com[:/](.+?\/.+?)(?:\.git)?(?:[#?].*)?$/i);
|
|
285
288
|
return match ? match[1] : null;
|
|
@@ -30,7 +30,7 @@ export interface PackedCheckResult extends StageCheckResult {
|
|
|
30
30
|
}
|
|
31
31
|
export interface HttpResponse {
|
|
32
32
|
statusCode: number;
|
|
33
|
-
data:
|
|
33
|
+
data: unknown;
|
|
34
34
|
}
|
|
35
35
|
export type HttpGetFn = (url: string, headers?: Record<string, string>, timeoutMs?: number) => Promise<HttpResponse>;
|
|
36
36
|
export interface ReleaseGateOptions {
|
|
@@ -14,11 +14,13 @@
|
|
|
14
14
|
* Network errors resolve to "not_checked" so transient issues don't block.
|
|
15
15
|
* GITHUB_TOKEN is used as Bearer auth but never logged or returned.
|
|
16
16
|
*/
|
|
17
|
-
import { execSync } from "node:child_process";
|
|
18
17
|
import { createHash } from "node:crypto";
|
|
19
|
-
import { readFileSync
|
|
18
|
+
import { readFileSync } from "node:fs";
|
|
20
19
|
import { join } from "node:path";
|
|
21
20
|
import { get } from "node:https";
|
|
21
|
+
import { resolveTrustedCommandLine } from "../runner/processSecurity.js";
|
|
22
|
+
import { runSimpleProcessSync } from "../runner/simpleProcess.js";
|
|
23
|
+
import { atomicWriteJsonFileSync } from "../utils/atomicFile.js";
|
|
22
24
|
// ── Constants ──────────────────────────────────────────────────────
|
|
23
25
|
const STAGE_ORDER = [
|
|
24
26
|
"local_ready",
|
|
@@ -29,6 +31,7 @@ const STAGE_ORDER = [
|
|
|
29
31
|
];
|
|
30
32
|
const LOCAL_TIMEOUT_MS = 300000;
|
|
31
33
|
const REMOTE_TIMEOUT_MS = 10000;
|
|
34
|
+
const MAX_REMOTE_RESPONSE_BYTES = 5 * 1024 * 1024;
|
|
32
35
|
const USER_AGENT = "PatchWarden-ReleaseGate";
|
|
33
36
|
const PACK_FORBIDDEN_PATTERNS = [
|
|
34
37
|
/(^|[\\/])\.env$/,
|
|
@@ -55,6 +58,14 @@ export function computeTokenDigest(token) {
|
|
|
55
58
|
export async function httpsGetJson(url, headers, timeoutMs) {
|
|
56
59
|
const timeout = timeoutMs ?? REMOTE_TIMEOUT_MS;
|
|
57
60
|
return new Promise((resolve, reject) => {
|
|
61
|
+
let settled = false;
|
|
62
|
+
const fail = (error) => {
|
|
63
|
+
if (settled)
|
|
64
|
+
return;
|
|
65
|
+
settled = true;
|
|
66
|
+
clearTimeout(timer);
|
|
67
|
+
reject(error);
|
|
68
|
+
};
|
|
58
69
|
const timer = setTimeout(() => {
|
|
59
70
|
req.destroy(new Error(`Request timed out after ${timeout}ms`));
|
|
60
71
|
}, timeout);
|
|
@@ -65,9 +76,31 @@ export async function httpsGetJson(url, headers, timeoutMs) {
|
|
|
65
76
|
...(headers || {}),
|
|
66
77
|
},
|
|
67
78
|
}, (res) => {
|
|
79
|
+
const declaredLength = Number(res.headers["content-length"]);
|
|
80
|
+
if (Number.isFinite(declaredLength) && declaredLength > MAX_REMOTE_RESPONSE_BYTES) {
|
|
81
|
+
res.destroy();
|
|
82
|
+
fail(new Error(`Response exceeds ${MAX_REMOTE_RESPONSE_BYTES} bytes`));
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
68
85
|
const chunks = [];
|
|
69
|
-
|
|
86
|
+
let totalBytes = 0;
|
|
87
|
+
let oversized = false;
|
|
88
|
+
res.on("data", (chunk) => {
|
|
89
|
+
if (oversized)
|
|
90
|
+
return;
|
|
91
|
+
totalBytes += chunk.length;
|
|
92
|
+
if (totalBytes > MAX_REMOTE_RESPONSE_BYTES) {
|
|
93
|
+
oversized = true;
|
|
94
|
+
res.destroy();
|
|
95
|
+
fail(new Error(`Response exceeds ${MAX_REMOTE_RESPONSE_BYTES} bytes`));
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
chunks.push(chunk);
|
|
99
|
+
});
|
|
70
100
|
res.on("end", () => {
|
|
101
|
+
if (oversized || settled)
|
|
102
|
+
return;
|
|
103
|
+
settled = true;
|
|
71
104
|
clearTimeout(timer);
|
|
72
105
|
const body = Buffer.concat(chunks).toString("utf-8");
|
|
73
106
|
let data = null;
|
|
@@ -81,11 +114,10 @@ export async function httpsGetJson(url, headers, timeoutMs) {
|
|
|
81
114
|
}
|
|
82
115
|
resolve({ statusCode: res.statusCode ?? 0, data });
|
|
83
116
|
});
|
|
117
|
+
res.on("aborted", () => fail(new Error("Response was aborted before completion")));
|
|
118
|
+
res.on("error", fail);
|
|
84
119
|
});
|
|
85
|
-
req.on("error",
|
|
86
|
-
clearTimeout(timer);
|
|
87
|
-
reject(err);
|
|
88
|
-
});
|
|
120
|
+
req.on("error", fail);
|
|
89
121
|
});
|
|
90
122
|
}
|
|
91
123
|
/**
|
|
@@ -138,12 +170,14 @@ export function checkLocalReady(repoPath) {
|
|
|
138
170
|
const commands = ["npm.cmd run build", "npm.cmd test", "npm.cmd run doctor:ci"];
|
|
139
171
|
for (const cmdStr of commands) {
|
|
140
172
|
try {
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
173
|
+
const result = runLocalCommand(repoPath, cmdStr, LOCAL_TIMEOUT_MS);
|
|
174
|
+
if (result.spawnError)
|
|
175
|
+
throw new Error(result.spawnError);
|
|
176
|
+
if (result.timedOut)
|
|
177
|
+
throw new Error(`Command timed out after ${LOCAL_TIMEOUT_MS}ms`);
|
|
178
|
+
if (result.exitCode !== 0) {
|
|
179
|
+
throw Object.assign(new Error(`Command exited with code ${result.exitCode ?? -1}`), { status: result.exitCode ?? -1 });
|
|
180
|
+
}
|
|
147
181
|
}
|
|
148
182
|
catch (err) {
|
|
149
183
|
if (err && typeof err === "object" && "status" in err) {
|
|
@@ -170,12 +204,14 @@ export function checkLocalReady(repoPath) {
|
|
|
170
204
|
export function checkPackedReady(repoPath) {
|
|
171
205
|
let packOutput;
|
|
172
206
|
try {
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
207
|
+
const result = runLocalCommand(repoPath, "npm.cmd pack --dry-run --json", LOCAL_TIMEOUT_MS, 10 * 1024 * 1024);
|
|
208
|
+
if (result.spawnError)
|
|
209
|
+
throw new Error(result.spawnError);
|
|
210
|
+
if (result.timedOut)
|
|
211
|
+
throw new Error(`npm pack timed out after ${LOCAL_TIMEOUT_MS}ms`);
|
|
212
|
+
if (result.exitCode !== 0)
|
|
213
|
+
throw new Error(`npm pack exited with code ${result.exitCode ?? -1}`);
|
|
214
|
+
packOutput = result.stdout;
|
|
179
215
|
}
|
|
180
216
|
catch (err) {
|
|
181
217
|
return {
|
|
@@ -240,7 +276,7 @@ export function checkPackedReady(repoPath) {
|
|
|
240
276
|
};
|
|
241
277
|
const manifestPath = join(repoPath, "release-artifact-manifest.json");
|
|
242
278
|
try {
|
|
243
|
-
|
|
279
|
+
atomicWriteJsonFileSync(manifestPath, manifest);
|
|
244
280
|
}
|
|
245
281
|
catch (err) {
|
|
246
282
|
return {
|
|
@@ -264,6 +300,17 @@ export function checkPackedReady(repoPath) {
|
|
|
264
300
|
}
|
|
265
301
|
return { status: "passed", manifestPath };
|
|
266
302
|
}
|
|
303
|
+
function runLocalCommand(repoPath, commandLine, timeoutMs, maxStdoutBytes = 512 * 1024) {
|
|
304
|
+
const invocation = resolveTrustedCommandLine(commandLine, repoPath);
|
|
305
|
+
return runSimpleProcessSync({
|
|
306
|
+
command: invocation.command,
|
|
307
|
+
args: invocation.argsPrefix,
|
|
308
|
+
cwd: repoPath,
|
|
309
|
+
timeoutMs,
|
|
310
|
+
maxStdoutBytes,
|
|
311
|
+
maxStderrBytes: 512 * 1024,
|
|
312
|
+
});
|
|
313
|
+
}
|
|
267
314
|
/**
|
|
268
315
|
* Stage 3: published_verified
|
|
269
316
|
* Queries the npm registry and confirms the given version exists.
|
|
@@ -280,11 +327,12 @@ export async function checkPublishedVerified(packageName, version, httpGet) {
|
|
|
280
327
|
if (res.statusCode === 404) {
|
|
281
328
|
return { status: "failed", reason: `Package "${packageName}" not found on npm registry` };
|
|
282
329
|
}
|
|
283
|
-
const data = res.data;
|
|
284
|
-
|
|
330
|
+
const data = asRecord(res.data);
|
|
331
|
+
const versions = asRecord(data?.versions);
|
|
332
|
+
if (!versions) {
|
|
285
333
|
return { status: "failed", reason: "npm registry response missing versions object" };
|
|
286
334
|
}
|
|
287
|
-
if (Object.prototype.hasOwnProperty.call(
|
|
335
|
+
if (Object.prototype.hasOwnProperty.call(versions, version)) {
|
|
288
336
|
return { status: "passed" };
|
|
289
337
|
}
|
|
290
338
|
return {
|
|
@@ -340,12 +388,12 @@ export async function checkCiVerified(repo, branch, httpGet) {
|
|
|
340
388
|
if (res.statusCode !== 200) {
|
|
341
389
|
return { status: "not_checked", reason: `GitHub API returned ${res.statusCode}` };
|
|
342
390
|
}
|
|
343
|
-
const data = res.data;
|
|
391
|
+
const data = asRecord(res.data);
|
|
344
392
|
const runs = Array.isArray(data?.workflow_runs) ? data.workflow_runs : [];
|
|
345
393
|
if (runs.length === 0) {
|
|
346
394
|
return { status: "not_checked", reason: `No CI runs found for branch "${branch}"` };
|
|
347
395
|
}
|
|
348
|
-
const conclusion = runs[0]?.conclusion;
|
|
396
|
+
const conclusion = asRecord(runs[0])?.conclusion;
|
|
349
397
|
if (conclusion === "success") {
|
|
350
398
|
return { status: "passed" };
|
|
351
399
|
}
|
|
@@ -364,6 +412,11 @@ export async function checkCiVerified(repo, branch, httpGet) {
|
|
|
364
412
|
};
|
|
365
413
|
}
|
|
366
414
|
}
|
|
415
|
+
function asRecord(value) {
|
|
416
|
+
return value && typeof value === "object" && !Array.isArray(value)
|
|
417
|
+
? value
|
|
418
|
+
: null;
|
|
419
|
+
}
|
|
367
420
|
// ── Orchestrator ───────────────────────────────────────────────────
|
|
368
421
|
/**
|
|
369
422
|
* Run release-gate stages up to and including targetStage, in order.
|
|
@@ -6,7 +6,21 @@ export interface AgentInvocation {
|
|
|
6
6
|
commandLabel: string;
|
|
7
7
|
promptMode: "inline" | "file";
|
|
8
8
|
promptFilePath?: string;
|
|
9
|
+
environmentVariableNames: string[];
|
|
10
|
+
blockedEnvironmentVariableNames: string[];
|
|
9
11
|
}
|
|
12
|
+
export interface ResolvedAgentLaunch {
|
|
13
|
+
command: string;
|
|
14
|
+
argsPrefix: string[];
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Resolve the Windows npm shim used by OpenCode to its native executable.
|
|
18
|
+
* Node cannot spawn .cmd/.ps1 shims directly with shell disabled (EINVAL), and
|
|
19
|
+
* enabling a shell would make the task prompt part of shell parsing.
|
|
20
|
+
*/
|
|
21
|
+
export declare function resolveAgentExecutable(agentName: string, command: string, platform?: NodeJS.Platform, pathValue?: string, fileExists?: (path: string) => boolean, cwd?: string): string;
|
|
22
|
+
/** Resolve a configured Agent to a native executable or verified npm CLI. */
|
|
23
|
+
export declare function resolveAgentLaunch(agentName: string, command: string, platform?: NodeJS.Platform, pathValue?: string, fileExists?: (path: string) => boolean, cwd?: string, adapterName?: string, readText?: (path: string) => string): ResolvedAgentLaunch;
|
|
10
24
|
/**
|
|
11
25
|
* Build agent invocation parameters from config.
|
|
12
26
|
* Replaces {repo}, {prompt}, and {prompt_file} placeholders.
|