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,9 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { spawn } from "node:child_process";
|
|
3
|
+
import http from "node:http";
|
|
3
4
|
import {
|
|
4
5
|
existsSync,
|
|
5
6
|
mkdirSync,
|
|
6
7
|
mkdtempSync,
|
|
8
|
+
readFileSync,
|
|
7
9
|
rmSync,
|
|
8
10
|
writeFileSync,
|
|
9
11
|
} from "node:fs";
|
|
@@ -11,7 +13,7 @@ import { tmpdir } from "node:os";
|
|
|
11
13
|
import { dirname, join, resolve } from "node:path";
|
|
12
14
|
import { fileURLToPath } from "node:url";
|
|
13
15
|
import { setTimeout as sleep } from "node:timers/promises";
|
|
14
|
-
import { CHATGPT_CORE_TOOL_NAMES } from "../../dist/tools/toolCatalog.js";
|
|
16
|
+
import { CHATGPT_CORE_TOOL_NAMES } from "../../dist/tools/catalog/toolCatalog.js";
|
|
15
17
|
|
|
16
18
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
17
19
|
const root = resolve(__dirname, "..", "..");
|
|
@@ -96,6 +98,25 @@ function toolJson(result) {
|
|
|
96
98
|
return JSON.parse(toolText(result));
|
|
97
99
|
}
|
|
98
100
|
|
|
101
|
+
function requestWithHost(path, hostHeader) {
|
|
102
|
+
return new Promise((resolvePromise, rejectPromise) => {
|
|
103
|
+
const request = http.request({
|
|
104
|
+
hostname: host,
|
|
105
|
+
port,
|
|
106
|
+
path,
|
|
107
|
+
method: "GET",
|
|
108
|
+
headers: { Host: hostHeader },
|
|
109
|
+
}, (response) => {
|
|
110
|
+
let body = "";
|
|
111
|
+
response.setEncoding("utf-8");
|
|
112
|
+
response.on("data", (chunk) => { body += chunk; });
|
|
113
|
+
response.on("end", () => resolvePromise({ status: response.statusCode, body }));
|
|
114
|
+
});
|
|
115
|
+
request.on("error", rejectPromise);
|
|
116
|
+
request.end();
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
99
120
|
console.log("\n=== PatchWarden HTTP MCP Smoke Tests ===\n");
|
|
100
121
|
|
|
101
122
|
try {
|
|
@@ -126,7 +147,7 @@ try {
|
|
|
126
147
|
);
|
|
127
148
|
|
|
128
149
|
console.log(`Starting HTTP MCP server on ${mcpUrl}...`);
|
|
129
|
-
serverProcess = spawn(
|
|
150
|
+
serverProcess = spawn(process.execPath, [serverPath], {
|
|
130
151
|
cwd: root,
|
|
131
152
|
env: {
|
|
132
153
|
...process.env,
|
|
@@ -163,6 +184,13 @@ try {
|
|
|
163
184
|
}
|
|
164
185
|
});
|
|
165
186
|
|
|
187
|
+
await test("HTTP MCP rejects DNS-rebinding Host headers", async () => {
|
|
188
|
+
const response = await requestWithHost("/healthz", "patchwarden.attacker.invalid");
|
|
189
|
+
if (response.status !== 421 || !response.body.includes("Untrusted Host")) {
|
|
190
|
+
throw new Error(`Unexpected hostile Host response: ${JSON.stringify(response)}`);
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
|
|
166
194
|
await test("wrong endpoint returns structured 404 guidance", async () => {
|
|
167
195
|
const response = await fetch(`http://${host}:${port}/wrong-path`);
|
|
168
196
|
const data = await response.json();
|
|
@@ -262,7 +290,7 @@ try {
|
|
|
262
290
|
|
|
263
291
|
// Start server WITH owner token
|
|
264
292
|
console.log("\n Starting server with owner token...");
|
|
265
|
-
serverProcess = spawn(
|
|
293
|
+
serverProcess = spawn(process.execPath, [serverPath], {
|
|
266
294
|
cwd: root,
|
|
267
295
|
env: {
|
|
268
296
|
...process.env,
|
|
@@ -278,7 +306,7 @@ try {
|
|
|
278
306
|
// Wait for server to be ready
|
|
279
307
|
for (let i = 0; i < 10; i++) {
|
|
280
308
|
try {
|
|
281
|
-
const r = await fetch(
|
|
309
|
+
const r = await fetch(`http://${host}:${port}/healthz`);
|
|
282
310
|
if (r.status === 200) break;
|
|
283
311
|
} catch {}
|
|
284
312
|
await sleep(500);
|
|
@@ -328,6 +356,56 @@ try {
|
|
|
328
356
|
}
|
|
329
357
|
});
|
|
330
358
|
|
|
359
|
+
const acceptanceTaskId = "task_http_acceptance";
|
|
360
|
+
const acceptanceTaskDir = join(workspaceRoot, ".patchwarden", "tasks", acceptanceTaskId);
|
|
361
|
+
mkdirSync(acceptanceTaskDir, { recursive: true });
|
|
362
|
+
writeFileSync(join(acceptanceTaskDir, "status.json"), JSON.stringify({
|
|
363
|
+
task_id: acceptanceTaskId,
|
|
364
|
+
status: "done_by_agent",
|
|
365
|
+
acceptance_status: "pending",
|
|
366
|
+
}), "utf-8");
|
|
367
|
+
|
|
368
|
+
await test("admin acceptance requires the owner token", async () => {
|
|
369
|
+
const response = await fetch(`http://${host}:${port}/admin/tasks/${acceptanceTaskId}/accept`, {
|
|
370
|
+
method: "POST",
|
|
371
|
+
headers: { "content-type": "application/json" },
|
|
372
|
+
body: JSON.stringify({ notes: "reviewed" }),
|
|
373
|
+
});
|
|
374
|
+
if (response.status !== 401) throw new Error(`Expected 401, got ${response.status}`);
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
await test("admin acceptance writes bounded atomic evidence", async () => {
|
|
378
|
+
const secret = `ghp_${"a".repeat(24)}`;
|
|
379
|
+
const response = await fetch(`http://${host}:${port}/admin/tasks/${acceptanceTaskId}/accept`, {
|
|
380
|
+
method: "POST",
|
|
381
|
+
headers: {
|
|
382
|
+
"content-type": "application/json",
|
|
383
|
+
Authorization: `Bearer ${OWNER_TOKEN}`,
|
|
384
|
+
},
|
|
385
|
+
body: JSON.stringify({ notes: `reviewed ${secret}` }),
|
|
386
|
+
});
|
|
387
|
+
if (response.status !== 200) throw new Error(`Expected 200, got ${response.status}: ${await response.text()}`);
|
|
388
|
+
const acceptance = JSON.parse(readFileSync(join(acceptanceTaskDir, "acceptance.json"), "utf-8"));
|
|
389
|
+
const status = JSON.parse(readFileSync(join(acceptanceTaskDir, "status.json"), "utf-8"));
|
|
390
|
+
if (acceptance.status !== "accepted" || status.acceptance_status !== "accepted") {
|
|
391
|
+
throw new Error("Acceptance state was not persisted consistently");
|
|
392
|
+
}
|
|
393
|
+
if (JSON.stringify(acceptance).includes(secret)) throw new Error("Acceptance notes persisted a token");
|
|
394
|
+
if (existsSync(join(acceptanceTaskDir, "status.json.lock"))) throw new Error("Status lock was not released");
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
await test("admin acceptance rejects oversized request bodies", async () => {
|
|
398
|
+
const response = await fetch(`http://${host}:${port}/admin/tasks/${acceptanceTaskId}/reject`, {
|
|
399
|
+
method: "POST",
|
|
400
|
+
headers: {
|
|
401
|
+
"content-type": "application/json",
|
|
402
|
+
Authorization: `Bearer ${OWNER_TOKEN}`,
|
|
403
|
+
},
|
|
404
|
+
body: JSON.stringify({ reason: "x".repeat(70 * 1024) }),
|
|
405
|
+
});
|
|
406
|
+
if (response.status !== 413) throw new Error(`Expected 413, got ${response.status}`);
|
|
407
|
+
});
|
|
408
|
+
|
|
331
409
|
} catch (error) {
|
|
332
410
|
fail("HTTP MCP smoke setup", error);
|
|
333
411
|
} finally {
|
|
@@ -152,16 +152,16 @@ try {
|
|
|
152
152
|
);
|
|
153
153
|
process.env.PATCHWARDEN_CONFIG = configPath;
|
|
154
154
|
|
|
155
|
-
const { savePlan } = await import("../../dist/tools/savePlan.js");
|
|
156
|
-
const { createTask } = await import("../../dist/tools/createTask.js");
|
|
157
|
-
const { getTaskStatus } = await import("../../dist/tools/getTaskStatus.js");
|
|
158
|
-
const { cancelTask } = await import("../../dist/tools/cancelTask.js");
|
|
159
|
-
const { killTask } = await import("../../dist/tools/killTask.js");
|
|
160
|
-
const { getDiff } = await import("../../dist/tools/taskOutputs.js");
|
|
161
|
-
const { listAgents } = await import("../../dist/tools/listAgents.js");
|
|
162
|
-
const { getTaskSummary } = await import("../../dist/tools/getTaskSummary.js");
|
|
163
|
-
const { auditTask } = await import("../../dist/tools/auditTask.js");
|
|
164
|
-
const { waitForTask } = await import("../../dist/tools/waitForTask.js");
|
|
155
|
+
const { savePlan } = await import("../../dist/tools/goals/savePlan.js");
|
|
156
|
+
const { createTask } = await import("../../dist/tools/tasks/createTask.js");
|
|
157
|
+
const { getTaskStatus } = await import("../../dist/tools/tasks/getTaskStatus.js");
|
|
158
|
+
const { cancelTask } = await import("../../dist/tools/tasks/cancelTask.js");
|
|
159
|
+
const { killTask } = await import("../../dist/tools/tasks/killTask.js");
|
|
160
|
+
const { getDiff } = await import("../../dist/tools/tasks/taskOutputs.js");
|
|
161
|
+
const { listAgents } = await import("../../dist/tools/workspace/listAgents.js");
|
|
162
|
+
const { getTaskSummary } = await import("../../dist/tools/tasks/getTaskSummary.js");
|
|
163
|
+
const { auditTask } = await import("../../dist/tools/diagnostics/auditTask.js");
|
|
164
|
+
const { waitForTask } = await import("../../dist/tools/tasks/waitForTask.js");
|
|
165
165
|
const { runTask } = await import("../../dist/runner/runTask.js");
|
|
166
166
|
const { reloadConfig } = await import("../../dist/config.js");
|
|
167
167
|
|
|
@@ -175,7 +175,7 @@ try {
|
|
|
175
175
|
await test("git diff captures tracked and untracked task changes", async () => {
|
|
176
176
|
writeFileSync(join(repoPath, "preexisting-user-file.txt"), "preexisting and untouched\n", "utf-8");
|
|
177
177
|
const plan = savePlan({ title: "Change capture", content: "Modify fixture files." });
|
|
178
|
-
const task = createTask({
|
|
178
|
+
const task = await createTask({
|
|
179
179
|
plan_id: plan.plan_id,
|
|
180
180
|
agent: "writer",
|
|
181
181
|
repo_path: "repo",
|
|
@@ -231,7 +231,7 @@ try {
|
|
|
231
231
|
});
|
|
232
232
|
|
|
233
233
|
await test("inspect_only template fails when the agent changes repository files", async () => {
|
|
234
|
-
const task = createTask({
|
|
234
|
+
const task = await createTask({
|
|
235
235
|
template: "inspect_only",
|
|
236
236
|
goal: "Inspect the fixture without modifying files",
|
|
237
237
|
agent: "writer",
|
|
@@ -249,7 +249,7 @@ try {
|
|
|
249
249
|
|
|
250
250
|
await test("legacy test_command becomes one independent verification command", async () => {
|
|
251
251
|
const plan = savePlan({ title: "Legacy verification", content: "No changes." });
|
|
252
|
-
const task = createTask({
|
|
252
|
+
const task = await createTask({
|
|
253
253
|
plan_id: plan.plan_id,
|
|
254
254
|
agent: "noop",
|
|
255
255
|
repo_path: "repo",
|
|
@@ -267,7 +267,7 @@ try {
|
|
|
267
267
|
|
|
268
268
|
await test("no-change task returns an explicit empty diff result", async () => {
|
|
269
269
|
const plan = savePlan({ title: "No diff", content: "Do not change files." });
|
|
270
|
-
const task = createTask({ plan_id: plan.plan_id, agent: "noop", repo_path: "repo" });
|
|
270
|
+
const task = await createTask({ plan_id: plan.plan_id, agent: "noop", repo_path: "repo" });
|
|
271
271
|
const result = await runTask(task.task_id);
|
|
272
272
|
if (!isDoneStatus(result.status)) throw new Error(`No-op task failed: ${JSON.stringify(result)}`);
|
|
273
273
|
const diff = getDiff(task.task_id);
|
|
@@ -288,7 +288,7 @@ try {
|
|
|
288
288
|
|
|
289
289
|
await test("large diff response is truncated while diff.patch remains complete", async () => {
|
|
290
290
|
const plan = savePlan({ title: "Large diff", content: "Create a large text file." });
|
|
291
|
-
const task = createTask({ plan_id: plan.plan_id, agent: "largewriter", repo_path: "repo" });
|
|
291
|
+
const task = await createTask({ plan_id: plan.plan_id, agent: "largewriter", repo_path: "repo" });
|
|
292
292
|
const result = await runTask(task.task_id);
|
|
293
293
|
if (!isDoneStatus(result.status)) throw new Error(`Large task failed: ${JSON.stringify(result)}`);
|
|
294
294
|
const diff = getDiff(task.task_id);
|
|
@@ -300,7 +300,7 @@ try {
|
|
|
300
300
|
|
|
301
301
|
await test("non-Git repositories return hash-only evidence with a reason", async () => {
|
|
302
302
|
const plan = savePlan({ title: "Non-Git evidence", content: "Modify files in a non-Git repository." });
|
|
303
|
-
const task = createTask({ plan_id: plan.plan_id, agent: "writer", repo_path: "plain-repo" });
|
|
303
|
+
const task = await createTask({ plan_id: plan.plan_id, agent: "writer", repo_path: "plain-repo" });
|
|
304
304
|
const result = await runTask(task.task_id);
|
|
305
305
|
if (!isDoneStatus(result.status)) throw new Error(`Non-Git task failed: ${JSON.stringify(result)}`);
|
|
306
306
|
const diff = getDiff(task.task_id);
|
|
@@ -315,7 +315,7 @@ try {
|
|
|
315
315
|
|
|
316
316
|
await test("binary Git changes remain reviewable as a textual Git binary patch", async () => {
|
|
317
317
|
const plan = savePlan({ title: "Binary evidence", content: "Create a binary fixture." });
|
|
318
|
-
const task = createTask({ plan_id: plan.plan_id, agent: "binarywriter", repo_path: "repo" });
|
|
318
|
+
const task = await createTask({ plan_id: plan.plan_id, agent: "binarywriter", repo_path: "repo" });
|
|
319
319
|
const result = await runTask(task.task_id);
|
|
320
320
|
if (!isDoneStatus(result.status)) throw new Error(`Binary task failed: ${JSON.stringify(result)}`);
|
|
321
321
|
const diff = getDiff(task.task_id);
|
|
@@ -326,7 +326,7 @@ try {
|
|
|
326
326
|
|
|
327
327
|
await test("artifact hygiene separates source, ignored output, runtime data, and suspicious changes", async () => {
|
|
328
328
|
const plan = savePlan({ title: "Artifact hygiene", content: "Generate representative task outputs." });
|
|
329
|
-
const task = createTask({ plan_id: plan.plan_id, agent: "artifactwriter", repo_path: "repo" });
|
|
329
|
+
const task = await createTask({ plan_id: plan.plan_id, agent: "artifactwriter", repo_path: "repo" });
|
|
330
330
|
const result = await runTask(task.task_id);
|
|
331
331
|
if (!isDoneStatus(result.status)) throw new Error(`Artifact task failed: ${JSON.stringify(result)}`);
|
|
332
332
|
const standard = getTaskSummary(task.task_id);
|
|
@@ -355,7 +355,7 @@ try {
|
|
|
355
355
|
|
|
356
356
|
await test("deleted tracked files are identified with file stats", async () => {
|
|
357
357
|
const plan = savePlan({ title: "Delete fixture", content: "Delete the designated fixture file." });
|
|
358
|
-
const task = createTask({ plan_id: plan.plan_id, agent: "deleter", repo_path: "repo" });
|
|
358
|
+
const task = await createTask({ plan_id: plan.plan_id, agent: "deleter", repo_path: "repo" });
|
|
359
359
|
const result = await runTask(task.task_id);
|
|
360
360
|
if (!isDoneStatus(result.status)) throw new Error(`Delete task failed: ${JSON.stringify(result)}`);
|
|
361
361
|
const diff = getDiff(task.task_id);
|
|
@@ -366,7 +366,7 @@ try {
|
|
|
366
366
|
|
|
367
367
|
await test("wait_for_task stays in the tool loop and returns terminal acceptance", async () => {
|
|
368
368
|
const plan = savePlan({ title: "Wait loop", content: "Finish normally." });
|
|
369
|
-
const task = createTask({ plan_id: plan.plan_id, agent: "writer", repo_path: "repo" });
|
|
369
|
+
const task = await createTask({ plan_id: plan.plan_id, agent: "writer", repo_path: "repo" });
|
|
370
370
|
const running = runTask(task.task_id);
|
|
371
371
|
const waited = await waitForTask(task.task_id, 5);
|
|
372
372
|
await raceWithTimeout(running, 15000, "wait_for_task loop did not terminate within 15s");
|
|
@@ -380,7 +380,7 @@ try {
|
|
|
380
380
|
|
|
381
381
|
await test("wait_for_task explicitly requires another call when the task is not terminal", async () => {
|
|
382
382
|
const plan = savePlan({ title: "Pending wait", content: "Remain queued for this check." });
|
|
383
|
-
const task = createTask({ plan_id: plan.plan_id, agent: "slow", repo_path: "repo" });
|
|
383
|
+
const task = await createTask({ plan_id: plan.plan_id, agent: "slow", repo_path: "repo" });
|
|
384
384
|
const waited = await waitForTask(task.task_id, 1);
|
|
385
385
|
if (waited.terminal || !waited.timed_out || !waited.continuation_required) {
|
|
386
386
|
throw new Error(`Expected continuation response: ${JSON.stringify(waited)}`);
|
|
@@ -390,11 +390,17 @@ try {
|
|
|
390
390
|
|
|
391
391
|
await test("running task summary includes heartbeat, phase, command, and elapsed time", async () => {
|
|
392
392
|
const plan = savePlan({ title: "Running summary", content: "Wait." });
|
|
393
|
-
const task = createTask({ plan_id: plan.plan_id, agent: "slow", repo_path: "repo", timeout_seconds: 30 });
|
|
393
|
+
const task = await createTask({ plan_id: plan.plan_id, agent: "slow", repo_path: "repo", timeout_seconds: 30 });
|
|
394
394
|
const running = runTask(task.task_id);
|
|
395
395
|
await waitForRunning(getTaskStatus, task.task_id);
|
|
396
|
-
|
|
397
|
-
|
|
396
|
+
// Wait for the task to transition from "preparing" to actually running a command.
|
|
397
|
+
// Async snapshot may delay the preparing phase, so poll until current_command appears.
|
|
398
|
+
let summary;
|
|
399
|
+
for (let attempt = 0; attempt < 100; attempt++) {
|
|
400
|
+
await sleep(50);
|
|
401
|
+
summary = getTaskSummary(task.task_id);
|
|
402
|
+
if (summary.current_command) break;
|
|
403
|
+
}
|
|
398
404
|
if (summary.terminal || !summary.last_heartbeat_at || !summary.phase || !summary.current_command || summary.elapsed_ms < 0) {
|
|
399
405
|
throw new Error(`Running summary incomplete: ${JSON.stringify(summary)}`);
|
|
400
406
|
}
|
|
@@ -404,7 +410,7 @@ try {
|
|
|
404
410
|
|
|
405
411
|
await test("verification failure produces failed_verification and structured evidence", async () => {
|
|
406
412
|
const plan = savePlan({ title: "Verify failure", content: "Finish normally." });
|
|
407
|
-
const task = createTask({
|
|
413
|
+
const task = await createTask({
|
|
408
414
|
plan_id: plan.plan_id,
|
|
409
415
|
agent: "writer",
|
|
410
416
|
repo_path: "repo",
|
|
@@ -434,7 +440,7 @@ try {
|
|
|
434
440
|
|
|
435
441
|
await test("out-of-scope workspace changes fail the task and generate a rollback plan", async () => {
|
|
436
442
|
const plan = savePlan({ title: "Scope violation", content: "Do not leave the repository." });
|
|
437
|
-
const task = createTask({
|
|
443
|
+
const task = await createTask({
|
|
438
444
|
plan_id: plan.plan_id,
|
|
439
445
|
agent: "scopebreaker",
|
|
440
446
|
repo_path: "repo",
|
|
@@ -505,7 +511,7 @@ try {
|
|
|
505
511
|
reloadConfig(cfg2);
|
|
506
512
|
|
|
507
513
|
const plan = savePlan({ title: "Noop with external dirty", content: "Do nothing." });
|
|
508
|
-
const task = createTask({ plan_id: plan.plan_id, agent: "noop", repo_path: "repo", verify_commands: ["node --check main.js"] });
|
|
514
|
+
const task = await createTask({ plan_id: plan.plan_id, agent: "noop", repo_path: "repo", verify_commands: ["node --check main.js"] });
|
|
509
515
|
const result = await runTask(task.task_id);
|
|
510
516
|
|
|
511
517
|
if (!isDoneStatus(result.status)) {
|
|
@@ -590,7 +596,7 @@ try {
|
|
|
590
596
|
reloadConfig(cfg2);
|
|
591
597
|
|
|
592
598
|
const plan = savePlan({ title: "External dirty modifier", content: "Modify external tracked file." });
|
|
593
|
-
const task = createTask({ plan_id: plan.plan_id, agent: "extmod", repo_path: "repo", verify_commands: ["node --check main.js"] });
|
|
599
|
+
const task = await createTask({ plan_id: plan.plan_id, agent: "extmod", repo_path: "repo", verify_commands: ["node --check main.js"] });
|
|
594
600
|
const result = await runTask(task.task_id);
|
|
595
601
|
|
|
596
602
|
if (result.status !== "failed_scope_violation") {
|
|
@@ -675,7 +681,7 @@ try {
|
|
|
675
681
|
reloadConfig(cfg2);
|
|
676
682
|
|
|
677
683
|
const plan = savePlan({ title: "Clean external modifier", content: "Modify clean external tracked file." });
|
|
678
|
-
const task = createTask({ plan_id: plan.plan_id, agent: "extmod", repo_path: "repo", verify_commands: ["node --check main.js"] });
|
|
684
|
+
const task = await createTask({ plan_id: plan.plan_id, agent: "extmod", repo_path: "repo", verify_commands: ["node --check main.js"] });
|
|
679
685
|
const result = await runTask(task.task_id);
|
|
680
686
|
|
|
681
687
|
if (result.status !== "failed_scope_violation") {
|
|
@@ -752,7 +758,7 @@ try {
|
|
|
752
758
|
reloadConfig(cfg2);
|
|
753
759
|
|
|
754
760
|
const plan = savePlan({ title: "External rename", content: "Rename external tracked file." });
|
|
755
|
-
const task = createTask({ plan_id: plan.plan_id, agent: "extrenamer", repo_path: "repo", verify_commands: ["node --check main.js"] });
|
|
761
|
+
const task = await createTask({ plan_id: plan.plan_id, agent: "extrenamer", repo_path: "repo", verify_commands: ["node --check main.js"] });
|
|
756
762
|
const result = await runTask(task.task_id);
|
|
757
763
|
|
|
758
764
|
if (result.status !== "failed_scope_violation") {
|
|
@@ -794,7 +800,7 @@ try {
|
|
|
794
800
|
|
|
795
801
|
await test("timeout terminates a long-running agent", async () => {
|
|
796
802
|
const plan = savePlan({ title: "Timeout", content: "Wait." });
|
|
797
|
-
const task = createTask({ plan_id: plan.plan_id, agent: "slow", repo_path: "repo", timeout_seconds: 1 });
|
|
803
|
+
const task = await createTask({ plan_id: plan.plan_id, agent: "slow", repo_path: "repo", timeout_seconds: 1 });
|
|
798
804
|
const started = Date.now();
|
|
799
805
|
const result = await runTask(task.task_id);
|
|
800
806
|
if (result.status !== "failed" || !result.error?.includes("timed out")) {
|
|
@@ -810,7 +816,7 @@ try {
|
|
|
810
816
|
|
|
811
817
|
await test("cancel_task safely stops a running agent", async () => {
|
|
812
818
|
const plan = savePlan({ title: "Cancel", content: "Wait." });
|
|
813
|
-
const task = createTask({ plan_id: plan.plan_id, agent: "slow", repo_path: "repo", timeout_seconds: 30 });
|
|
819
|
+
const task = await createTask({ plan_id: plan.plan_id, agent: "slow", repo_path: "repo", timeout_seconds: 30 });
|
|
814
820
|
const running = runTask(task.task_id);
|
|
815
821
|
await waitForRunning(getTaskStatus, task.task_id);
|
|
816
822
|
const request = cancelTask(task.task_id);
|
|
@@ -823,7 +829,7 @@ try {
|
|
|
823
829
|
|
|
824
830
|
await test("kill_task immediately stops a running agent", async () => {
|
|
825
831
|
const plan = savePlan({ title: "Kill", content: "Wait." });
|
|
826
|
-
const task = createTask({ plan_id: plan.plan_id, agent: "slow", repo_path: "repo", timeout_seconds: 30 });
|
|
832
|
+
const task = await createTask({ plan_id: plan.plan_id, agent: "slow", repo_path: "repo", timeout_seconds: 30 });
|
|
827
833
|
const running = runTask(task.task_id);
|
|
828
834
|
await waitForRunning(getTaskStatus, task.task_id);
|
|
829
835
|
const request = killTask(task.task_id);
|
|
@@ -4,7 +4,7 @@ import { resolve } from "node:path";
|
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
6
6
|
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
7
|
-
import { CHATGPT_CORE_TOOL_NAMES, CHATGPT_DIRECT_TOOL_NAMES, CHATGPT_SEARCH_TOOL_NAMES } from "../../dist/tools/toolCatalog.js";
|
|
7
|
+
import { CHATGPT_CORE_TOOL_NAMES, CHATGPT_DIRECT_TOOL_NAMES, CHATGPT_SEARCH_TOOL_NAMES } from "../../dist/tools/catalog/toolCatalog.js";
|
|
8
8
|
|
|
9
9
|
const root = resolve(fileURLToPath(new URL("../..", import.meta.url)));
|
|
10
10
|
const jsonOnly = process.argv.includes("--json");
|
|
@@ -54,6 +54,8 @@ try {
|
|
|
54
54
|
}), "utf-8");
|
|
55
55
|
writeFileSync(join(workspaceRoot, "hello.txt"), "hello from mcp smoke\n", "utf-8");
|
|
56
56
|
writeFileSync(join(workspaceRoot, ".env"), "SECRET=blocked\n", "utf-8");
|
|
57
|
+
writeFileSync(join(workspaceRoot, "npm.cmd"), "@echo repo npm hijack executed\r\n@exit /b 99\r\n", "utf-8");
|
|
58
|
+
writeFileSync(join(workspaceRoot, "git.cmd"), "@echo repo git hijack executed\r\n@exit /b 98\r\n", "utf-8");
|
|
57
59
|
writeFileSync(
|
|
58
60
|
join(workspaceRoot, "package.json"),
|
|
59
61
|
JSON.stringify({
|
|
@@ -117,6 +119,7 @@ try {
|
|
|
117
119
|
"discard_worktree",
|
|
118
120
|
"discover_tools",
|
|
119
121
|
"explain_tool",
|
|
122
|
+
"export_goal_report",
|
|
120
123
|
"export_handoff",
|
|
121
124
|
"export_task_evidence_pack",
|
|
122
125
|
"finalize_direct_session",
|
|
@@ -133,6 +136,7 @@ try {
|
|
|
133
136
|
"get_task_summary",
|
|
134
137
|
"get_test_log",
|
|
135
138
|
"health_check",
|
|
139
|
+
"import_speckit_tasks",
|
|
136
140
|
"invoke_discovered_tool",
|
|
137
141
|
"kill_task",
|
|
138
142
|
"list_agents",
|
|
@@ -177,7 +181,7 @@ try {
|
|
|
177
181
|
if (!tools._meta || typeof tools._meta.tool_manifest_sha256 !== "string" || tools._meta.tool_manifest_sha256.length !== 64) {
|
|
178
182
|
throw new Error(`tools/list _meta missing manifest hash: ${JSON.stringify(tools._meta || null)}`);
|
|
179
183
|
}
|
|
180
|
-
if (tools._meta.tool_profile !== "full" || tools._meta.tool_count !==
|
|
184
|
+
if (tools._meta.tool_profile !== "full" || tools._meta.tool_count !== 66) {
|
|
181
185
|
throw new Error(`tools/list _meta profile/count mismatch: ${JSON.stringify(tools._meta)}`);
|
|
182
186
|
}
|
|
183
187
|
if (typeof tools._meta.schema_epoch !== "string" || typeof tools._meta.server_version !== "string") {
|
|
@@ -318,7 +322,11 @@ try {
|
|
|
318
322
|
);
|
|
319
323
|
ok("sensitive file and path escape checks reject access");
|
|
320
324
|
|
|
321
|
-
|
|
325
|
+
// The runner executes with the temporary workspace as cwd; use an absolute
|
|
326
|
+
// package path so the smoke test does not depend on that workspace having a
|
|
327
|
+
// copied `dist/` tree.
|
|
328
|
+
const runnerPath = resolve(root, "dist", "runner", "cli.js");
|
|
329
|
+
const runner = spawnSync("node", [runnerPath, task.task_id], {
|
|
322
330
|
cwd: root,
|
|
323
331
|
env: { ...process.env, PATCHWARDEN_CONFIG: configPath },
|
|
324
332
|
encoding: "utf-8",
|
|
@@ -435,7 +443,9 @@ try {
|
|
|
435
443
|
writeFileSync(join(directRepo, "package.json"), JSON.stringify({
|
|
436
444
|
name: "direct-fixture",
|
|
437
445
|
private: true,
|
|
438
|
-
|
|
446
|
+
// Exercise the npm-script execution path without relying on a temporary
|
|
447
|
+
// script file or platform-specific quoting.
|
|
448
|
+
scripts: { test: "node --version" },
|
|
439
449
|
}, null, 2), "utf-8");
|
|
440
450
|
|
|
441
451
|
writeFileSync(
|
|
@@ -515,7 +525,23 @@ try {
|
|
|
515
525
|
})).content?.[0]?.text || "{}"
|
|
516
526
|
);
|
|
517
527
|
if (bundleResult.status !== "passed" || bundleResult.command_count !== 1 || bundleResult.passed_commands !== 1) {
|
|
518
|
-
|
|
528
|
+
// This repository and command are generated entirely by this smoke test,
|
|
529
|
+
// so a bounded log excerpt is safe and makes platform-only CI failures
|
|
530
|
+
// actionable without weakening the production tool's safe summary.
|
|
531
|
+
const sessionDir = join(workspaceRoot, ".patchwarden", "direct-sessions", sessionResult.session_id);
|
|
532
|
+
const verificationPath = join(sessionDir, "verification.json");
|
|
533
|
+
const logPath = join(sessionDir, "verification.log");
|
|
534
|
+
const verification = existsSync(verificationPath)
|
|
535
|
+
? readFileSync(verificationPath, "utf-8").slice(-2000)
|
|
536
|
+
: "missing verification.json";
|
|
537
|
+
const fixtureLog = existsSync(logPath)
|
|
538
|
+
? readFileSync(logPath, "utf-8").slice(-2000)
|
|
539
|
+
: "missing verification.log";
|
|
540
|
+
throw new Error(
|
|
541
|
+
`run_direct_verification_bundle failed: ${JSON.stringify(bundleResult)}; ` +
|
|
542
|
+
`synthetic_fixture_verification=${JSON.stringify(verification)}; ` +
|
|
543
|
+
`synthetic_fixture_log=${JSON.stringify(fixtureLog)}`
|
|
544
|
+
);
|
|
519
545
|
}
|
|
520
546
|
const bundleText = JSON.stringify(bundleResult).toLowerCase();
|
|
521
547
|
if (bundleText.includes("stdout_tail") || bundleText.includes("stderr_tail") || bundleText.includes("verification.log")) {
|
|
@@ -1,15 +1,24 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { spawnSync } from "node:child_process";
|
|
3
3
|
import { readFileSync } from "node:fs";
|
|
4
|
+
import { resolve } from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
import {
|
|
7
|
+
buildChildEnvironment,
|
|
8
|
+
resolvePackageManagerInvocation,
|
|
9
|
+
} from "../../dist/runner/processSecurity.js";
|
|
4
10
|
|
|
5
|
-
const
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
const result = spawnSync(command,
|
|
11
|
+
const root = resolve(fileURLToPath(new URL("../..", import.meta.url)));
|
|
12
|
+
const env = buildChildEnvironment({ cwd: root });
|
|
13
|
+
const invocation = resolvePackageManagerInvocation(process.platform === "win32" ? "npm.cmd" : "npm", root, {
|
|
14
|
+
pathValue: env.PATH,
|
|
15
|
+
});
|
|
16
|
+
const result = spawnSync(invocation.command, [...invocation.argsPrefix, "pack", "--dry-run", "--json"], {
|
|
17
|
+
cwd: root,
|
|
11
18
|
encoding: "utf8",
|
|
19
|
+
env,
|
|
12
20
|
shell: false,
|
|
21
|
+
windowsHide: true,
|
|
13
22
|
});
|
|
14
23
|
|
|
15
24
|
if (result.status !== 0) {
|
|
@@ -35,6 +44,8 @@ const forbidden = [
|
|
|
35
44
|
/\.dpapi$/i,
|
|
36
45
|
/^docs\/optimization-proposal\.md$/i,
|
|
37
46
|
/(^|\/)kill-patchwarden\.(cmd|ps1)$/i,
|
|
47
|
+
/^(?:dist|src)\/test\//i,
|
|
48
|
+
/^docs\/archive\//i,
|
|
38
49
|
];
|
|
39
50
|
const leaked = files.filter((file) => forbidden.some((pattern) => pattern.test(file)));
|
|
40
51
|
if (leaked.length > 0) {
|
|
@@ -44,8 +55,10 @@ if (leaked.length > 0) {
|
|
|
44
55
|
}
|
|
45
56
|
|
|
46
57
|
const required = [
|
|
58
|
+
"dist/index.js",
|
|
59
|
+
"dist/index.d.ts",
|
|
47
60
|
"PatchWarden.cmd",
|
|
48
|
-
"PatchWarden-Desktop.cmd",
|
|
61
|
+
"scripts/launchers/PatchWarden-Desktop.cmd",
|
|
49
62
|
"scripts/control/manage-patchwarden.ps1",
|
|
50
63
|
"scripts/launchers/Start-PatchWarden-Tunnel.cmd",
|
|
51
64
|
"scripts/launchers/Start-PatchWarden-Direct-Tunnel.cmd",
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { existsSync
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
3
|
import { spawnSync } from "node:child_process";
|
|
4
4
|
import { resolve } from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
|
+
import { collectMatchingFiles } from "../lib/file-discovery.js";
|
|
6
7
|
|
|
7
8
|
const scriptDir = resolve(fileURLToPath(new URL(".", import.meta.url)));
|
|
8
9
|
const root = resolve(scriptDir, "..", "..");
|
|
@@ -14,10 +15,7 @@ if (!existsSync(unitDir)) {
|
|
|
14
15
|
process.exit(1);
|
|
15
16
|
}
|
|
16
17
|
|
|
17
|
-
const testFiles =
|
|
18
|
-
.filter((name) => name.endsWith(".test.js"))
|
|
19
|
-
.sort()
|
|
20
|
-
.map((name) => resolve(unitDir, name));
|
|
18
|
+
const testFiles = collectMatchingFiles(unitDir, (name) => name.endsWith(".test.js"));
|
|
21
19
|
|
|
22
20
|
if (testFiles.length === 0) {
|
|
23
21
|
console.error(`[unit-tests] No compiled unit tests found in ${unitDir}`);
|
|
@@ -60,6 +60,8 @@ async function runScenario(label, mode, maxRestarts, lifetimeMs, assertState) {
|
|
|
60
60
|
async function runExternalScenario() {
|
|
61
61
|
const fixture = createFixture("external");
|
|
62
62
|
const env = fixtureEnv(fixture, "healthy", 7_000);
|
|
63
|
+
delete env.CONTROL_PLANE_API_KEY;
|
|
64
|
+
delete env.PATCHWARDEN_OWNER_TOKEN;
|
|
63
65
|
const external = spawn(process.execPath, [fixture.watcherPath], {
|
|
64
66
|
cwd: fixture.project,
|
|
65
67
|
env: {
|
|
@@ -168,6 +170,9 @@ function fixtureEnv(fixture, mode, lifetimeMs) {
|
|
|
168
170
|
APPDATA: fixture.appData,
|
|
169
171
|
LOCALAPPDATA: fixture.localAppData,
|
|
170
172
|
CONTROL_PLANE_API_KEY: "test-key",
|
|
173
|
+
PATCHWARDEN_OWNER_TOKEN: "test-owner-token",
|
|
174
|
+
PATCHWARDEN_AGENT_ENV_ALLOWLIST: "PATCHWARDEN_TEST_PROVIDER_KEY",
|
|
175
|
+
PATCHWARDEN_TEST_PROVIDER_KEY: "provider-key-canary",
|
|
171
176
|
WATCHER_FIXTURE_MODE: mode,
|
|
172
177
|
TUNNEL_FIXTURE_LIFETIME_MS: String(lifetimeMs),
|
|
173
178
|
};
|
|
@@ -177,6 +182,9 @@ function watcherFixtureSource(attemptPath) {
|
|
|
177
182
|
return `
|
|
178
183
|
const fs=require('fs');const path=require('path');
|
|
179
184
|
if(!process.env.XDG_CONFIG_HOME){console.error('watcher did not receive XDG_CONFIG_HOME');process.exit(12)}
|
|
185
|
+
if(process.env.CONTROL_PLANE_API_KEY){console.error('watcher received tunnel owner credential');process.exit(13)}
|
|
186
|
+
if(process.env.PATCHWARDEN_OWNER_TOKEN){console.error('watcher received HTTP owner credential');process.exit(14)}
|
|
187
|
+
if(process.env.PATCHWARDEN_TEST_PROVIDER_KEY!=='provider-key-canary'){console.error('watcher did not receive allow-listed provider credential');process.exit(15)}
|
|
180
188
|
let attempt=0;try{attempt=Number(fs.readFileSync(${JSON.stringify(attemptPath)},'utf8'))||0}catch{}
|
|
181
189
|
attempt++;fs.writeFileSync(${JSON.stringify(attemptPath)},String(attempt));
|
|
182
190
|
const cfg=JSON.parse(fs.readFileSync(process.env.PATCHWARDEN_CONFIG,'utf8'));
|
|
@@ -192,6 +200,7 @@ else setInterval(write,250);
|
|
|
192
200
|
function tunnelFixtureSource() {
|
|
193
201
|
return `
|
|
194
202
|
const fs=require('fs');const path=require('path');const args=process.argv.slice(2);const command=args[0]||'';
|
|
203
|
+
if(process.env.PATCHWARDEN_TEST_PROVIDER_KEY){console.error('allow-listed provider credential leaked into tunnel-client');process.exit(22)}
|
|
195
204
|
const flag=(name)=>{const i=args.indexOf(name);return i>=0?args[i+1]:''};
|
|
196
205
|
if(command==='init'){const profile=flag('--profile')||'patchwarden';const mcpCommand=flag('--mcp-command')||'';const yamlDir=path.join(process.env.APPDATA,'tunnel-client');const yamlPath=path.join(yamlDir,profile+'.yaml');fs.mkdirSync(yamlDir,{recursive:true});fs.writeFileSync(yamlPath,'mcp:\\n commands:\\n - channel: main\\n command: \"'+mcpCommand.replace(/\\\\/g,'/')+'\"\\n\\nhealth:\\n listen_addr: \"127.0.0.1:8080\"\\n');console.log('{}');process.exit(0)}
|
|
197
206
|
if(command==='doctor'){console.log('{}');process.exit(0)}
|
|
@@ -6,7 +6,7 @@ $RuntimeDirectory = Join-Path $env:LOCALAPPDATA "patchwarden\runtime"
|
|
|
6
6
|
$HealthUrlFile = Join-Path $RuntimeDirectory "tunnel-health-url.txt"
|
|
7
7
|
|
|
8
8
|
$env:PATCHWARDEN_CONFIG = $ConfigPath
|
|
9
|
-
$healthJson = node --input-type=module -e "Promise.all([import('./dist/tools/healthCheck.js'),import('./dist/tools/registry.js')]).then(([h,r])=>console.log(JSON.stringify(h.healthCheck(r.getToolCatalogSnapshot()))))" 2>$null
|
|
9
|
+
$healthJson = node --input-type=module -e "Promise.all([import('./dist/tools/diagnostics/healthCheck.js'),import('./dist/tools/registry.js')]).then(([h,r])=>console.log(JSON.stringify(h.healthCheck(r.getToolCatalogSnapshot()))))" 2>$null
|
|
10
10
|
if ($LASTEXITCODE -ne 0 -or -not $healthJson) {
|
|
11
11
|
throw "Could not load PatchWarden health information. Run npm.cmd run build first."
|
|
12
12
|
}
|