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
|
@@ -101,7 +101,79 @@ html.dark {
|
|
|
101
101
|
.no-scrollbar { -ms-overflow-style: none; scrollbar-width: none; }
|
|
102
102
|
.pw-spin { animation: pw-spin 1s linear infinite; }
|
|
103
103
|
@keyframes pw-spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
|
|
104
|
+
/* Toast notifications */
|
|
105
|
+
.pw-toast {
|
|
106
|
+
pointer-events: auto;
|
|
107
|
+
display: flex;
|
|
108
|
+
align-items: flex-start;
|
|
109
|
+
gap: 8px;
|
|
110
|
+
padding: 10px 12px;
|
|
111
|
+
border-radius: 8px;
|
|
112
|
+
border: 1px solid var(--pw-border);
|
|
113
|
+
background-color: var(--pw-bg-surface);
|
|
114
|
+
box-shadow: var(--pw-shadow-float);
|
|
115
|
+
font-family: var(--pw-font-sans);
|
|
116
|
+
font-size: 12px;
|
|
117
|
+
color: var(--pw-text-primary);
|
|
118
|
+
animation: pw-toast-in 200ms ease;
|
|
119
|
+
max-width: 360px;
|
|
120
|
+
}
|
|
121
|
+
.pw-toast.pw-toast-success { border-left: 3px solid var(--pw-state-success); }
|
|
122
|
+
.pw-toast.pw-toast-warning { border-left: 3px solid var(--pw-state-warning); }
|
|
123
|
+
.pw-toast.pw-toast-error { border-left: 3px solid var(--pw-state-error); }
|
|
124
|
+
.pw-toast.pw-toast-info { border-left: 3px solid var(--pw-state-info); }
|
|
125
|
+
.pw-toast .pw-toast-title { font-weight: 600; margin-bottom: 2px; }
|
|
126
|
+
.pw-toast .pw-toast-msg { color: var(--pw-text-secondary); word-break: break-word; }
|
|
127
|
+
.pw-toast .pw-toast-close { margin-left: auto; cursor: pointer; color: var(--pw-text-tertiary); background: transparent; border: 0; font-size: 14px; line-height: 1; padding: 0 2px; }
|
|
128
|
+
.pw-toast .pw-toast-close:hover { color: var(--pw-text-primary); }
|
|
129
|
+
@keyframes pw-toast-in {
|
|
130
|
+
from { opacity: 0; transform: translateX(20px); }
|
|
131
|
+
to { opacity: 1; transform: translateX(0); }
|
|
132
|
+
}
|
|
133
|
+
.pw-toast.pw-toast-leaving { animation: pw-toast-out 200ms ease forwards; }
|
|
134
|
+
@keyframes pw-toast-out {
|
|
135
|
+
to { opacity: 0; transform: translateX(20px); }
|
|
136
|
+
}
|
|
137
|
+
/* Quick action buttons */
|
|
138
|
+
.pw-quick-btn {
|
|
139
|
+
display: inline-flex;
|
|
140
|
+
align-items: center;
|
|
141
|
+
gap: 4px;
|
|
142
|
+
padding: 3px 8px;
|
|
143
|
+
border-radius: 4px;
|
|
144
|
+
border: 1px solid var(--pw-border-subtle);
|
|
145
|
+
background-color: var(--pw-bg-hover);
|
|
146
|
+
color: var(--pw-text-secondary);
|
|
147
|
+
cursor: pointer;
|
|
148
|
+
transition: background-color var(--pw-transition-fast), color var(--pw-transition-fast);
|
|
149
|
+
text-decoration: none;
|
|
150
|
+
font-size: 11px;
|
|
151
|
+
font-family: var(--pw-font-sans);
|
|
152
|
+
white-space: nowrap;
|
|
153
|
+
}
|
|
154
|
+
.pw-quick-btn:hover {
|
|
155
|
+
background-color: var(--pw-bg-active);
|
|
156
|
+
color: var(--pw-text-primary);
|
|
157
|
+
}
|
|
158
|
+
.pw-quick-btn:disabled {
|
|
159
|
+
opacity: 0.5;
|
|
160
|
+
cursor: not-allowed;
|
|
161
|
+
}
|
|
162
|
+
.pw-quick-btn.pw-quick-danger:hover {
|
|
163
|
+
background-color: var(--pw-state-error-bg);
|
|
164
|
+
color: var(--pw-state-error);
|
|
165
|
+
border-color: var(--pw-state-error);
|
|
166
|
+
}
|
|
167
|
+
.pw-quick-btn.pw-quick-accent {
|
|
168
|
+
color: var(--pw-accent);
|
|
169
|
+
border-color: var(--pw-accent-muted);
|
|
170
|
+
}
|
|
171
|
+
.pw-quick-btn.pw-quick-accent:hover {
|
|
172
|
+
background-color: var(--pw-accent-muted);
|
|
173
|
+
}
|
|
104
174
|
</style>
|
|
175
|
+
<script src="/desktop-bootstrap.js"></script>
|
|
176
|
+
<link rel="stylesheet" href="/desktop.css">
|
|
105
177
|
</head>
|
|
106
178
|
<body class="flex h-screen overflow-hidden">
|
|
107
179
|
|
|
@@ -119,6 +191,10 @@ html.dark {
|
|
|
119
191
|
|
|
120
192
|
<!-- Navigation -->
|
|
121
193
|
<nav class="flex-1 flex flex-col gap-1 px-3 pt-4 overflow-y-auto">
|
|
194
|
+
<a href="/pages/getting-started.html" data-nav-key="getting-started" class="desktop-only-nav flex items-center gap-3 px-3 py-2.5 rounded-lg text-sm font-medium transition-colors truncate" style="color: var(--pw-text-secondary); font-family: var(--pw-font-sans);">
|
|
195
|
+
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="shrink-0"><path d="M4.5 16.5c-1.5 1.26-2 5-2 5s3.74-.5 5-2c.71-.84.7-2.13-.09-2.91a2.18 2.18 0 0 0-2.91-.09z"/><path d="m12 15-3-3a22 22 0 0 1 2-3.95A12.87 12.87 0 0 1 22 2c0 2.72-.78 7.5-6.05 11a22.35 22.35 0 0 1-3.95 2z"/></svg>
|
|
196
|
+
<span class="truncate" data-i18n="nav.gettingStarted">开始使用</span>
|
|
197
|
+
</a>
|
|
122
198
|
|
|
123
199
|
<a href="/pages/dashboard.html" data-nav-key="dashboard" data-dom-id="nav-dashboard" class="flex items-center gap-3 px-3 py-2.5 rounded-lg text-sm font-medium transition-colors truncate" style="color: var(--pw-text-secondary); font-family: var(--pw-font-sans);">
|
|
124
200
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="shrink-0">
|
|
@@ -174,6 +250,10 @@ html.dark {
|
|
|
174
250
|
<span class="truncate" data-zh="日志" data-en="Logs">日志</span>
|
|
175
251
|
</a>
|
|
176
252
|
|
|
253
|
+
<a href="/pages/settings.html" data-nav-key="settings" class="desktop-only-nav flex items-center gap-3 px-3 py-2.5 rounded-lg text-sm font-medium transition-colors truncate" style="color: var(--pw-text-secondary); font-family: var(--pw-font-sans);">
|
|
254
|
+
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="shrink-0"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.7 1.7 0 0 0 .3 1.9l.1.1-2.8 2.8-.1-.1a1.7 1.7 0 0 0-1.9-.3 1.7 1.7 0 0 0-1 1.6v.2h-4V21a1.7 1.7 0 0 0-1-1.6 1.7 1.7 0 0 0-1.9.3l-.1.1L4.2 17l.1-.1a1.7 1.7 0 0 0 .3-1.9A1.7 1.7 0 0 0 3 14H2.8v-4H3a1.7 1.7 0 0 0 1.6-1 1.7 1.7 0 0 0-.3-1.9L4.2 7 7 4.2l.1.1A1.7 1.7 0 0 0 9 4.6 1.7 1.7 0 0 0 10 3V2.8h4V3a1.7 1.7 0 0 0 1 1.6 1.7 1.7 0 0 0 1.9-.3l.1-.1L19.8 7l-.1.1a1.7 1.7 0 0 0-.3 1.9A1.7 1.7 0 0 0 21 10h.2v4H21a1.7 1.7 0 0 0-1.6 1z"/></svg>
|
|
255
|
+
<span class="truncate" data-i18n="nav.settings">设置</span>
|
|
256
|
+
</a>
|
|
177
257
|
</nav>
|
|
178
258
|
|
|
179
259
|
<!-- Bottom Section -->
|
|
@@ -198,6 +278,12 @@ html.dark {
|
|
|
198
278
|
Direct 会话
|
|
199
279
|
</h1>
|
|
200
280
|
<div class="flex items-center gap-4 shrink-0">
|
|
281
|
+
<select id="session-state" class="px-2 py-1.5 rounded-md text-xs font-medium" style="background-color: var(--pw-bg-surface); color: var(--pw-text-secondary); border: 1px solid var(--pw-border-subtle); font-family: var(--pw-font-sans);">
|
|
282
|
+
<option value="active">进行中</option>
|
|
283
|
+
<option value="archive">历史记录</option>
|
|
284
|
+
<option value="audited">仅审计完成</option>
|
|
285
|
+
<option value="expired">仅已过期</option>
|
|
286
|
+
</select>
|
|
201
287
|
<span id="lastUpdated" class="text-xs whitespace-nowrap hidden" style="color: var(--pw-text-tertiary); font-family: var(--pw-font-mono);"></span>
|
|
202
288
|
<button id="refreshBtn" class="flex items-center gap-2 px-4 py-1.5 rounded-lg text-xs font-semibold whitespace-nowrap transition-colors" style="background-color: var(--pw-bg-surface); color: var(--pw-text-secondary); border: 1px solid var(--pw-border-subtle); font-family: var(--pw-font-sans);">
|
|
203
289
|
<svg id="refreshIcon" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
@@ -216,6 +302,18 @@ html.dark {
|
|
|
216
302
|
<!-- Page Content Area -->
|
|
217
303
|
<main class="flex-1 overflow-y-auto p-6" style="background-color: var(--pw-bg-elevated);">
|
|
218
304
|
|
|
305
|
+
<div class="mb-4 flex flex-wrap items-end gap-3 rounded-lg px-4 py-3" style="background-color: var(--pw-bg-surface); border: 1px solid var(--pw-border);">
|
|
306
|
+
<label class="grid gap-1 text-xs" style="color: var(--pw-text-tertiary);"><span data-zh="项目" data-en="Project">项目</span><select id="session-repo" aria-label="Direct 会话项目" class="px-2 py-1.5 rounded-md text-xs" style="min-width:180px;background-color:var(--pw-bg-elevated);color:var(--pw-text-primary);border:1px solid var(--pw-border-subtle);"><option value="">全部项目</option></select></label>
|
|
307
|
+
<label class="grid gap-1 text-xs" style="color: var(--pw-text-tertiary);"><span data-zh="开始日期" data-en="From date">开始日期</span><input id="session-date-from" type="date" aria-label="Direct 会话开始日期" class="px-2 py-1.5 rounded-md text-xs" style="background-color:var(--pw-bg-elevated);color:var(--pw-text-primary);border:1px solid var(--pw-border-subtle);"></label>
|
|
308
|
+
<label class="grid gap-1 text-xs" style="color: var(--pw-text-tertiary);"><span data-zh="结束日期" data-en="To date">结束日期</span><input id="session-date-to" type="date" aria-label="Direct 会话结束日期" class="px-2 py-1.5 rounded-md text-xs" style="background-color:var(--pw-bg-elevated);color:var(--pw-text-primary);border:1px solid var(--pw-border-subtle);"></label>
|
|
309
|
+
</div>
|
|
310
|
+
|
|
311
|
+
<section id="direct-disabled" class="hidden rounded-lg p-6 mb-4" style="background-color: var(--pw-bg-surface); border: 1px solid var(--pw-border);">
|
|
312
|
+
<h2 class="text-base font-semibold" style="color:var(--pw-text-primary);" data-zh="Direct 是可选高级能力" data-en="Direct is an optional advanced capability">Direct 是可选高级能力</h2>
|
|
313
|
+
<p class="mt-2 text-sm" style="color:var(--pw-text-secondary);" data-zh="未启用不会影响 Core、Tunnel 或普通任务。需要受控的直接编辑会话时,可在设置中启用。" data-en="Leaving Direct disabled does not affect Core, Tunnel, or regular tasks. Enable it in Settings when you need controlled direct editing sessions.">未启用不会影响 Core、Tunnel 或普通任务。需要受控的直接编辑会话时,可在设置中启用。</p>
|
|
314
|
+
<a href="/pages/settings.html" class="inline-flex mt-4 px-3 py-2 rounded-md text-sm font-semibold" style="background-color:var(--pw-accent);color:var(--pw-text-inverse);" data-zh="前往设置" data-en="Open Settings">前往设置</a>
|
|
315
|
+
</section>
|
|
316
|
+
|
|
219
317
|
<!-- ===== Error Banner ===== -->
|
|
220
318
|
<div id="error-banner" class="hidden mb-5 rounded-lg px-4 py-3 flex items-start gap-3" style="background-color: var(--pw-state-error-bg); border: 1px solid var(--pw-state-error);">
|
|
221
319
|
<svg class="shrink-0 mt-0.5" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="var(--pw-state-error)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
@@ -257,10 +355,25 @@ html.dark {
|
|
|
257
355
|
|
|
258
356
|
<!-- ===== Sessions List ===== -->
|
|
259
357
|
<div id="sessions-list" class="hidden flex flex-col gap-3"></div>
|
|
358
|
+
<div id="sessions-more-wrap" class="hidden mt-4 text-center"><button id="sessions-more" type="button" class="px-4 py-2 rounded-md text-sm font-medium" style="background-color:var(--pw-bg-surface);color:var(--pw-text-primary);border:1px solid var(--pw-border);" data-zh="加载更多" data-en="Load more">加载更多</button></div>
|
|
260
359
|
|
|
261
360
|
<!-- ===== Session Detail ===== -->
|
|
262
361
|
<div id="session-detail" class="hidden flex flex-col gap-6"></div>
|
|
263
362
|
|
|
363
|
+
<!-- ===== Toast Container ===== -->
|
|
364
|
+
<div id="toast-container" class="fixed top-4 right-4 z-50 flex flex-col gap-2 max-w-sm pointer-events-none"></div>
|
|
365
|
+
|
|
366
|
+
<!-- ===== SAFE MODAL ===== -->
|
|
367
|
+
<div id="safe-modal" class="hidden fixed inset-0 z-50 flex items-center justify-center p-4" style="background-color: rgba(0,0,0,0.6);">
|
|
368
|
+
<div class="rounded-lg w-full max-w-2xl max-h-[80vh] flex flex-col" style="background-color: var(--pw-bg-surface); border: 1px solid var(--pw-border); box-shadow: var(--pw-shadow-modal);">
|
|
369
|
+
<div class="flex items-center justify-between px-4 py-3" style="border-bottom: 1px solid var(--pw-border);">
|
|
370
|
+
<h3 id="safe-modal-title" class="text-sm font-semibold truncate" style="color: var(--pw-text-primary);">—</h3>
|
|
371
|
+
<button id="safe-modal-close" class="text-lg leading-none" style="color: var(--pw-text-tertiary);" type="button" title="关闭" aria-label="关闭结果面板">×</button>
|
|
372
|
+
</div>
|
|
373
|
+
<div id="safe-modal-body" class="flex-1 overflow-auto px-4 py-3 text-xs" style="color: var(--pw-text-primary); font-family: var(--pw-font-mono); white-space: pre-wrap; word-break: break-all;"></div>
|
|
374
|
+
</div>
|
|
375
|
+
</div>
|
|
376
|
+
|
|
264
377
|
</main>
|
|
265
378
|
</div>
|
|
266
379
|
|
|
@@ -288,6 +401,9 @@ html.dark {
|
|
|
288
401
|
'use strict';
|
|
289
402
|
|
|
290
403
|
var API_URL = '/api/direct-sessions';
|
|
404
|
+
var controlToken = null;
|
|
405
|
+
var loadedSessions = [];
|
|
406
|
+
var nextCursor = null;
|
|
291
407
|
|
|
292
408
|
function $(id) { return document.getElementById(id); }
|
|
293
409
|
function hide(el) { if (el) el.classList.add('hidden'); }
|
|
@@ -316,6 +432,13 @@ html.dark {
|
|
|
316
432
|
+ ' ' + pad2(d.getHours()) + ':' + pad2(d.getMinutes()) + ':' + pad2(d.getSeconds());
|
|
317
433
|
}
|
|
318
434
|
|
|
435
|
+
function shortId(sid) {
|
|
436
|
+
if (!sid) return '—';
|
|
437
|
+
var s = String(sid);
|
|
438
|
+
if (s.length <= 20) return s;
|
|
439
|
+
return s.slice(0, 12) + '…' + s.slice(-6);
|
|
440
|
+
}
|
|
441
|
+
|
|
319
442
|
function showError(msg) {
|
|
320
443
|
var banner = $('error-banner');
|
|
321
444
|
var message = $('error-message');
|
|
@@ -340,6 +463,7 @@ html.dark {
|
|
|
340
463
|
|
|
341
464
|
function showLoading() {
|
|
342
465
|
show($('loading-state'));
|
|
466
|
+
hide($('direct-disabled'));
|
|
343
467
|
hide($('empty-state'));
|
|
344
468
|
hide($('sessions-list'));
|
|
345
469
|
hide($('session-detail'));
|
|
@@ -349,6 +473,8 @@ html.dark {
|
|
|
349
473
|
show($('empty-state'));
|
|
350
474
|
hide($('sessions-list'));
|
|
351
475
|
hide($('session-detail'));
|
|
476
|
+
hide($('direct-disabled'));
|
|
477
|
+
hide($('sessions-more-wrap'));
|
|
352
478
|
var rEl = $('empty-reason');
|
|
353
479
|
if (rEl) rEl.textContent = reason || '';
|
|
354
480
|
}
|
|
@@ -357,12 +483,24 @@ html.dark {
|
|
|
357
483
|
hide($('empty-state'));
|
|
358
484
|
show($('sessions-list'));
|
|
359
485
|
hide($('session-detail'));
|
|
486
|
+
hide($('direct-disabled'));
|
|
360
487
|
}
|
|
361
488
|
function showDetail() {
|
|
362
489
|
hide($('loading-state'));
|
|
363
490
|
hide($('empty-state'));
|
|
364
491
|
hide($('sessions-list'));
|
|
365
492
|
show($('session-detail'));
|
|
493
|
+
hide($('direct-disabled'));
|
|
494
|
+
hide($('sessions-more-wrap'));
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
function showDirectDisabled() {
|
|
498
|
+
hide($('loading-state'));
|
|
499
|
+
hide($('empty-state'));
|
|
500
|
+
hide($('sessions-list'));
|
|
501
|
+
hide($('session-detail'));
|
|
502
|
+
hide($('sessions-more-wrap'));
|
|
503
|
+
show($('direct-disabled'));
|
|
366
504
|
}
|
|
367
505
|
|
|
368
506
|
function verdictColor(v) {
|
|
@@ -377,7 +515,8 @@ html.dark {
|
|
|
377
515
|
success: { fg: 'var(--pw-state-success)', bg: 'var(--pw-state-success-bg)' },
|
|
378
516
|
warning: { fg: 'var(--pw-state-warning)', bg: 'var(--pw-state-warning-bg)' },
|
|
379
517
|
error: { fg: 'var(--pw-state-error)', bg: 'var(--pw-state-error-bg)' },
|
|
380
|
-
muted: { fg: 'var(--pw-text-tertiary)', bg: 'var(--pw-bg-hover)' }
|
|
518
|
+
muted: { fg: 'var(--pw-text-tertiary)', bg: 'var(--pw-bg-hover)' },
|
|
519
|
+
info: { fg: 'var(--pw-state-info)', bg: 'var(--pw-state-info-bg)' }
|
|
381
520
|
};
|
|
382
521
|
|
|
383
522
|
function badgeHtml(text, color) {
|
|
@@ -385,7 +524,131 @@ html.dark {
|
|
|
385
524
|
return '<span class="text-xs px-2 py-0.5 rounded-md whitespace-nowrap" style="color: ' + c.fg + '; background-color: ' + c.bg + ';">' + escapeHtml(text) + '</span>';
|
|
386
525
|
}
|
|
387
526
|
|
|
388
|
-
// ----------
|
|
527
|
+
// ---------- Toast notifications ----------
|
|
528
|
+
function showToast(kind, title, msg) {
|
|
529
|
+
var container = $('toast-container');
|
|
530
|
+
if (!container) return;
|
|
531
|
+
var el = document.createElement('div');
|
|
532
|
+
el.className = 'pw-toast pw-toast-' + (kind || 'info');
|
|
533
|
+
el.innerHTML =
|
|
534
|
+
'<div class="min-w-0 flex-1">'
|
|
535
|
+
+ '<div class="pw-toast-title">' + escapeHtml(title || '') + '</div>'
|
|
536
|
+
+ (msg ? '<div class="pw-toast-msg">' + escapeHtml(msg) + '</div>' : '')
|
|
537
|
+
+ '</div>'
|
|
538
|
+
+ '<button type="button" class="pw-toast-close" title="关闭" aria-label="关闭通知">×</button>';
|
|
539
|
+
var closeBtn = el.querySelector('.pw-toast-close');
|
|
540
|
+
if (closeBtn) closeBtn.addEventListener('click', function () { dismissToast(el); });
|
|
541
|
+
container.appendChild(el);
|
|
542
|
+
setTimeout(function () { dismissToast(el); }, 8000);
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
function dismissToast(el) {
|
|
546
|
+
if (!el || el.classList.contains('pw-toast-leaving')) return;
|
|
547
|
+
el.classList.add('pw-toast-leaving');
|
|
548
|
+
setTimeout(function () {
|
|
549
|
+
if (el.parentNode) el.parentNode.removeChild(el);
|
|
550
|
+
}, 220);
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
// ---------- Clipboard + Safe Modal ----------
|
|
554
|
+
function copyToClipboard(text, label) {
|
|
555
|
+
if (!text) return;
|
|
556
|
+
if (navigator.clipboard && navigator.clipboard.writeText) {
|
|
557
|
+
navigator.clipboard.writeText(text).then(function () {
|
|
558
|
+
showToast('success', '已复制', (label || '内容') + '已复制到剪贴板');
|
|
559
|
+
}).catch(function () {
|
|
560
|
+
showToast('error', '复制失败', '请手动复制');
|
|
561
|
+
});
|
|
562
|
+
} else {
|
|
563
|
+
showToast('warning', '复制不支持', '浏览器不支持剪贴板 API');
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
function showSafeModal(title, data) {
|
|
568
|
+
var modal = $('safe-modal');
|
|
569
|
+
var titleEl = $('safe-modal-title');
|
|
570
|
+
var bodyEl = $('safe-modal-body');
|
|
571
|
+
if (!modal || !titleEl || !bodyEl) return;
|
|
572
|
+
titleEl.textContent = title || '—';
|
|
573
|
+
var content;
|
|
574
|
+
if (data && typeof data === 'object') {
|
|
575
|
+
content = JSON.stringify(data, null, 2);
|
|
576
|
+
} else {
|
|
577
|
+
content = String(data || '');
|
|
578
|
+
}
|
|
579
|
+
bodyEl.textContent = content;
|
|
580
|
+
modal.classList.remove('hidden');
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
function closeSafeModal() {
|
|
584
|
+
var modal = $('safe-modal');
|
|
585
|
+
if (modal) modal.classList.add('hidden');
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
// ---------- Control token + POST actions ----------
|
|
589
|
+
async function loadToken() {
|
|
590
|
+
try {
|
|
591
|
+
const res = await fetch('/control-token.json');
|
|
592
|
+
if (!res.ok) throw new Error('HTTP ' + res.status);
|
|
593
|
+
const data = await res.json();
|
|
594
|
+
controlToken = data.token || null;
|
|
595
|
+
} catch (err) {
|
|
596
|
+
controlToken = null;
|
|
597
|
+
console.error('loadToken failed:', err);
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
async function postAction(url) {
|
|
602
|
+
if (!controlToken) await loadToken();
|
|
603
|
+
const res = await fetch(url, {
|
|
604
|
+
method: 'POST',
|
|
605
|
+
headers: { 'X-PatchWarden-Control-Token': controlToken || '' }
|
|
606
|
+
});
|
|
607
|
+
let data = null;
|
|
608
|
+
try { data = await res.json(); } catch (_) { /* ignore */ }
|
|
609
|
+
if (!res.ok) {
|
|
610
|
+
const msg = (data && (data.error || data.reason || data.message)) || ('HTTP ' + res.status);
|
|
611
|
+
throw new Error(msg);
|
|
612
|
+
}
|
|
613
|
+
return data;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
// ---------- Session classification ----------
|
|
617
|
+
function classifySession(s) {
|
|
618
|
+
var nowMs = Date.now();
|
|
619
|
+
var expiresMs = s.expires_at ? Date.parse(s.expires_at) : NaN;
|
|
620
|
+
var expired = Number.isFinite(expiresMs) && expiresMs < nowMs;
|
|
621
|
+
if (expired) return 'expired';
|
|
622
|
+
if (s.audited) return 'audited';
|
|
623
|
+
if (s.finalized) return 'finalized';
|
|
624
|
+
return 'active';
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
function verificationStatusText(vs) {
|
|
628
|
+
if (!vs || typeof vs !== 'object') return '—';
|
|
629
|
+
// verification_summary is the last run object: { command, exit_code, passed, timed_out, ... }
|
|
630
|
+
if (vs.passed === true) return 'passed';
|
|
631
|
+
if (vs.timed_out === true) return 'timed_out';
|
|
632
|
+
if (vs.passed === false) return 'failed';
|
|
633
|
+
if (vs.exit_code != null) return String(vs.exit_code);
|
|
634
|
+
return '—';
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
function verificationStatusColor(vs) {
|
|
638
|
+
if (!vs || typeof vs !== 'object') return 'muted';
|
|
639
|
+
if (vs.passed === true) return 'success';
|
|
640
|
+
if (vs.timed_out === true || vs.passed === false) return 'error';
|
|
641
|
+
return 'muted';
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
// ---------- Render: sessions list (grouped) ----------
|
|
645
|
+
var GROUP_LABELS = {
|
|
646
|
+
active: { zh: '进行中', en: 'Active', color: 'info' },
|
|
647
|
+
finalized: { zh: '已 Finalize', en: 'Finalized', color: 'success' },
|
|
648
|
+
audited: { zh: '已审计', en: 'Audited', color: 'success' },
|
|
649
|
+
expired: { zh: '已过期', en: 'Expired', color: 'warning' }
|
|
650
|
+
};
|
|
651
|
+
|
|
389
652
|
function renderSessions(sessions) {
|
|
390
653
|
var listEl = $('sessions-list');
|
|
391
654
|
listEl.innerHTML = '';
|
|
@@ -394,69 +657,299 @@ html.dark {
|
|
|
394
657
|
return;
|
|
395
658
|
}
|
|
396
659
|
|
|
397
|
-
var
|
|
660
|
+
var groups = { active: [], finalized: [], audited: [], expired: [] };
|
|
398
661
|
for (var i = 0; i < sessions.length; i++) {
|
|
399
662
|
var s = sessions[i] || {};
|
|
400
|
-
var
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
html +=
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
+ '</div>'
|
|
424
|
-
+ '<div class="flex items-center gap-1.5 shrink-0 flex-wrap justify-end">'
|
|
425
|
-
+ stateBadge + decisionBadge
|
|
426
|
-
+ '</div>'
|
|
427
|
-
+ '</div>'
|
|
428
|
-
+ '<div class="grid grid-cols-2 sm:grid-cols-4 gap-3 text-xs">'
|
|
429
|
-
+ '<div class="flex flex-col gap-0.5 min-w-0">'
|
|
430
|
-
+ '<span class="text-xs" style="color: var(--pw-text-tertiary);">repo_path</span>'
|
|
431
|
-
+ '<span class="truncate" style="color: var(--pw-text-secondary); font-family: var(--pw-font-mono);">' + escapeHtml(s.repo_path || '—') + '</span>'
|
|
432
|
-
+ '</div>'
|
|
433
|
-
+ '<div class="flex flex-col gap-0.5 min-w-0">'
|
|
434
|
-
+ '<span class="text-xs" style="color: var(--pw-text-tertiary);">changed_files_total</span>'
|
|
435
|
-
+ '<span class="truncate" style="color: var(--pw-text-primary); font-family: var(--pw-font-mono);">' + escapeHtml(String(changedTotal)) + '</span>'
|
|
436
|
-
+ '</div>'
|
|
437
|
-
+ '<div class="flex flex-col gap-0.5 min-w-0">'
|
|
438
|
-
+ '<span class="text-xs" style="color: var(--pw-text-tertiary);">created_at</span>'
|
|
439
|
-
+ '<span class="truncate" style="color: var(--pw-text-secondary); font-family: var(--pw-font-mono);">' + escapeHtml(fmtTimeShort(s.created_at)) + '</span>'
|
|
440
|
-
+ '</div>'
|
|
441
|
-
+ '<div class="flex flex-col gap-0.5 min-w-0">'
|
|
442
|
-
+ '<span class="text-xs" style="color: var(--pw-text-tertiary);">expires_at</span>'
|
|
443
|
-
+ '<span class="truncate" style="color: var(--pw-text-secondary); font-family: var(--pw-font-mono);">' + escapeHtml(fmtTimeShort(s.expires_at)) + '</span>'
|
|
444
|
-
+ '</div>'
|
|
445
|
-
+ '</div>'
|
|
446
|
-
+ '</div>';
|
|
663
|
+
var cls = classifySession(s);
|
|
664
|
+
groups[cls].push(s);
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
var html = '';
|
|
668
|
+
// Active section (expanded)
|
|
669
|
+
if (groups.active.length > 0) {
|
|
670
|
+
html += renderGroupHeader('active', groups.active.length, false);
|
|
671
|
+
html += renderSessionCards(groups.active, false);
|
|
672
|
+
}
|
|
673
|
+
// Finalized section (expanded)
|
|
674
|
+
if (groups.finalized.length > 0) {
|
|
675
|
+
html += renderGroupHeader('finalized', groups.finalized.length, false);
|
|
676
|
+
html += renderSessionCards(groups.finalized, false);
|
|
677
|
+
}
|
|
678
|
+
// Audited section (expanded)
|
|
679
|
+
if (groups.audited.length > 0) {
|
|
680
|
+
html += renderGroupHeader('audited', groups.audited.length, false);
|
|
681
|
+
html += renderSessionCards(groups.audited, false);
|
|
682
|
+
}
|
|
683
|
+
// Expired section (collapsed via <details>)
|
|
684
|
+
if (groups.expired.length > 0) {
|
|
685
|
+
html += renderExpiredGroup(groups.expired);
|
|
447
686
|
}
|
|
687
|
+
|
|
448
688
|
listEl.innerHTML = html;
|
|
449
689
|
showList();
|
|
690
|
+
wireActionButtons();
|
|
691
|
+
wireSessionCards();
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
function renderGroupHeader(key, count, isExpired) {
|
|
695
|
+
var meta = GROUP_LABELS[key] || GROUP_LABELS.active;
|
|
696
|
+
var c = COLOR_VARS[meta.color] || COLOR_VARS.muted;
|
|
697
|
+
return '<div class="flex items-center gap-2 mt-2 mb-1 px-1">'
|
|
698
|
+
+ '<span class="w-2 h-2 rounded-full" style="background-color: ' + c.fg + ';"></span>'
|
|
699
|
+
+ '<h2 class="text-sm font-semibold" data-zh="' + escapeHtml(meta.zh) + '" data-en="' + escapeHtml(meta.en) + '" style="color: var(--pw-text-primary); font-family: var(--pw-font-sans);">' + escapeHtml(meta.zh) + '</h2>'
|
|
700
|
+
+ '<span class="text-xs" style="color: var(--pw-text-tertiary);">(' + count + ')</span>'
|
|
701
|
+
+ '</div>';
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
function renderExpiredGroup(sessions) {
|
|
705
|
+
var meta = GROUP_LABELS.expired;
|
|
706
|
+
var c = COLOR_VARS[meta.color] || COLOR_VARS.muted;
|
|
707
|
+
var headerHtml =
|
|
708
|
+
'<summary class="flex items-center gap-2 cursor-pointer px-3 py-2" style="color: var(--pw-text-primary); font-family: var(--pw-font-sans;">'
|
|
709
|
+
+ '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="shrink-0"><polyline points="6 9 12 15 18 9"/></svg>'
|
|
710
|
+
+ '<span class="w-2 h-2 rounded-full" style="background-color: ' + c.fg + ';"></span>'
|
|
711
|
+
+ '<span class="text-sm font-semibold" data-zh="' + escapeHtml(meta.zh) + '" data-en="' + escapeHtml(meta.en) + '">' + escapeHtml(meta.zh) + '</span>'
|
|
712
|
+
+ '<span class="text-xs" style="color: var(--pw-text-tertiary);">(' + sessions.length + ')</span>'
|
|
713
|
+
+ '<span class="ml-auto text-xs" style="color: var(--pw-text-tertiary);">点击展开 / 折叠</span>'
|
|
714
|
+
+ '</summary>';
|
|
715
|
+
return '<details class="rounded-lg mt-2" style="background-color: var(--pw-bg-surface); border: 1px solid var(--pw-border);">'
|
|
716
|
+
+ headerHtml
|
|
717
|
+
+ '<div class="px-3 pb-3 pt-1 flex flex-col gap-3">'
|
|
718
|
+
+ renderSessionCards(sessions, true)
|
|
719
|
+
+ '</div>'
|
|
720
|
+
+ '</details>';
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
function renderSessionCards(sessions, isExpired) {
|
|
724
|
+
var html = '';
|
|
725
|
+
for (var i = 0; i < sessions.length; i++) {
|
|
726
|
+
html += renderSessionCard(sessions[i] || {}, isExpired);
|
|
727
|
+
}
|
|
728
|
+
return html;
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
function renderSessionCard(s, isExpired) {
|
|
732
|
+
var sid = s.session_id || '—';
|
|
733
|
+
var finalized = !!s.finalized;
|
|
734
|
+
var audited = !!s.audited;
|
|
735
|
+
var changedTotal = s.changed_files_total != null ? s.changed_files_total : '—';
|
|
736
|
+
var decision = s.audit_decision || null;
|
|
737
|
+
var decisionBadge = decision
|
|
738
|
+
? badgeHtml(decision, verdictColor(decision))
|
|
739
|
+
: '<span class="text-xs px-2 py-0.5 rounded-md" style="color: var(--pw-text-tertiary); background-color: var(--pw-bg-hover);">未审计</span>';
|
|
740
|
+
var stateBadge = finalized
|
|
741
|
+
? badgeHtml('已完成', 'success')
|
|
742
|
+
: (isExpired ? badgeHtml('已过期', 'warning') : badgeHtml('进行中', 'warning'));
|
|
743
|
+
var auditedBadge = audited ? badgeHtml('已审计', 'success') : '';
|
|
744
|
+
var titleText = s.title || s.repo_path || sid;
|
|
745
|
+
|
|
746
|
+
// Verification status summary
|
|
747
|
+
var vs = s.verification_summary || null;
|
|
748
|
+
var vsText = verificationStatusText(vs);
|
|
749
|
+
var vsColor = verificationStatusColor(vs);
|
|
750
|
+
var vsBadge = badgeHtml(vsText, vsColor);
|
|
751
|
+
|
|
752
|
+
// Quick action buttons
|
|
753
|
+
var actions = '';
|
|
754
|
+
// safe_direct_summary
|
|
755
|
+
actions += quickBtnHtml('summary', '查看摘要', 'View summary', sid, '查看脱敏后的安全摘要', 'View the redacted safe summary', false, false);
|
|
756
|
+
// run_direct_verification_bundle — informational button (links to tasks page)
|
|
757
|
+
actions += quickBtnHtml('verify-bundle', '创建验证任务', 'Create verification task', sid, '前往任务页创建受控验证任务', 'Open Tasks to create a controlled verification task', false, false);
|
|
758
|
+
// safe_finalize_direct_session (disabled if already finalized)
|
|
759
|
+
actions += quickBtnHtml('finalize', '完成会话', 'Finalize session', sid, '完成并生成会话摘要', 'Finalize and generate the session summary', finalized, true);
|
|
760
|
+
// safe_audit_direct_session (disabled if already audited)
|
|
761
|
+
actions += quickBtnHtml('audit', '运行审计', 'Run audit', sid, '运行安全审计', 'Run the security audit', audited, true);
|
|
762
|
+
// copy session_id
|
|
763
|
+
actions += quickBtnHtml('copy', '复制 ID', 'Copy ID', sid, '复制会话 ID', 'Copy session ID', false, false);
|
|
764
|
+
// hide (expired only)
|
|
765
|
+
if (isExpired) {
|
|
766
|
+
actions += quickBtnHtml('hide', '隐藏记录', 'Hide record', sid, '仅从列表隐藏,不删除证据', 'Hide from the list without deleting evidence', false, false, true);
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
return '<div class="rounded-lg p-4 transition-colors session-card" role="button" tabindex="0" aria-label="查看 Direct 会话 ' + escapeHtml(shortId(sid)) + '" data-session-id="' + escapeHtml(sid) + '" '
|
|
770
|
+
+ 'onmouseover="this.style.backgroundColor=\'var(--pw-bg-hover)\'" '
|
|
771
|
+
+ 'onmouseout="this.style.backgroundColor=\'var(--pw-bg-surface)\'" '
|
|
772
|
+
+ 'style="background-color: var(--pw-bg-surface); border: 1px solid var(--pw-border); cursor: pointer;">'
|
|
773
|
+
+ '<div class="flex items-start justify-between gap-3 mb-3">'
|
|
774
|
+
+ '<div class="min-w-0 flex-1">'
|
|
775
|
+
+ '<div class="flex items-center gap-2 mb-1">'
|
|
776
|
+
+ '<h3 class="text-sm font-semibold truncate" style="color: var(--pw-text-primary); font-family: var(--pw-font-sans);">' + escapeHtml(titleText) + '</h3>'
|
|
777
|
+
+ '</div>'
|
|
778
|
+
+ '<div class="flex items-center gap-2 min-w-0">'
|
|
779
|
+
+ '<code class="text-xs truncate" style="color: var(--pw-text-tertiary); font-family: var(--pw-font-mono);" title="' + escapeHtml(sid) + '">' + escapeHtml(shortId(sid)) + '</code>'
|
|
780
|
+
+ '</div>'
|
|
781
|
+
+ '</div>'
|
|
782
|
+
+ '<div class="flex items-center gap-1.5 shrink-0 flex-wrap justify-end">'
|
|
783
|
+
+ stateBadge + auditedBadge + decisionBadge
|
|
784
|
+
+ '</div>'
|
|
785
|
+
+ '</div>'
|
|
786
|
+
+ '<div class="grid grid-cols-2 sm:grid-cols-4 gap-3 text-xs mb-3">'
|
|
787
|
+
+ '<div class="flex flex-col gap-0.5 min-w-0">'
|
|
788
|
+
+ '<span class="text-xs" style="color: var(--pw-text-tertiary);">repo_path</span>'
|
|
789
|
+
+ '<span class="truncate" style="color: var(--pw-text-secondary); font-family: var(--pw-font-mono);" title="' + escapeHtml(s.repo_path || '') + '">' + escapeHtml(s.repo_path || '—') + '</span>'
|
|
790
|
+
+ '</div>'
|
|
791
|
+
+ '<div class="flex flex-col gap-0.5 min-w-0">'
|
|
792
|
+
+ '<span class="text-xs" style="color: var(--pw-text-tertiary);">changed_files_total</span>'
|
|
793
|
+
+ '<span class="truncate" style="color: var(--pw-text-primary); font-family: var(--pw-font-mono);">' + escapeHtml(String(changedTotal)) + '</span>'
|
|
794
|
+
+ '</div>'
|
|
795
|
+
+ '<div class="flex flex-col gap-0.5 min-w-0">'
|
|
796
|
+
+ '<span class="text-xs" style="color: var(--pw-text-tertiary);">created_at</span>'
|
|
797
|
+
+ '<span class="truncate" style="color: var(--pw-text-secondary); font-family: var(--pw-font-mono);">' + escapeHtml(fmtTimeShort(s.created_at)) + '</span>'
|
|
798
|
+
+ '</div>'
|
|
799
|
+
+ '<div class="flex flex-col gap-0.5 min-w-0">'
|
|
800
|
+
+ '<span class="text-xs" style="color: var(--pw-text-tertiary);">expires_at</span>'
|
|
801
|
+
+ '<span class="truncate" style="color: var(--pw-text-secondary); font-family: var(--pw-font-mono);">' + escapeHtml(fmtTimeShort(s.expires_at)) + '</span>'
|
|
802
|
+
+ '</div>'
|
|
803
|
+
+ '</div>'
|
|
804
|
+
+ '<div class="flex items-center gap-2 mb-3 text-xs">'
|
|
805
|
+
+ '<span class="text-xs" style="color: var(--pw-text-tertiary);">verification:</span>'
|
|
806
|
+
+ vsBadge
|
|
807
|
+
+ '</div>'
|
|
808
|
+
+ '<div class="flex items-center gap-1.5 flex-wrap" onclick="event.stopPropagation();">'
|
|
809
|
+
+ actions
|
|
810
|
+
+ '</div>'
|
|
811
|
+
+ '</div>';
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
function quickBtnHtml(action, labelZh, labelEn, sid, titleZh, titleEn, disabled, accent, danger) {
|
|
815
|
+
var cls = 'pw-quick-btn';
|
|
816
|
+
if (accent) cls += ' pw-quick-accent';
|
|
817
|
+
if (danger) cls += ' pw-quick-danger';
|
|
818
|
+
var disAttr = disabled ? ' disabled' : '';
|
|
819
|
+
return '<button type="button" class="' + cls + '" data-action="' + escapeHtml(action) + '" data-session-id="' + escapeHtml(sid) + '" title="' + escapeHtml(titleZh) + '" aria-label="' + escapeHtml(titleZh) + '" data-zh="' + escapeHtml(labelZh) + '" data-en="' + escapeHtml(labelEn) + '"' + disAttr + '>'
|
|
820
|
+
+ escapeHtml(labelZh)
|
|
821
|
+
+ '</button>';
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
// ---------- Action button wiring ----------
|
|
825
|
+
function wireActionButtons() {
|
|
826
|
+
var btns = document.querySelectorAll('#sessions-list .pw-quick-btn[data-action]');
|
|
827
|
+
for (var i = 0; i < btns.length; i++) {
|
|
828
|
+
var btn = btns[i];
|
|
829
|
+
// Avoid double-binding on re-render.
|
|
830
|
+
if (btn.__pwBound) continue;
|
|
831
|
+
btn.__pwBound = true;
|
|
832
|
+
btn.addEventListener('click', function (ev) {
|
|
833
|
+
ev.preventDefault();
|
|
834
|
+
ev.stopPropagation();
|
|
835
|
+
handleActionClick(ev.currentTarget);
|
|
836
|
+
});
|
|
837
|
+
}
|
|
838
|
+
}
|
|
450
839
|
|
|
451
|
-
|
|
840
|
+
function wireSessionCards() {
|
|
841
|
+
var cards = $('sessions-list').querySelectorAll('.session-card');
|
|
452
842
|
for (var j = 0; j < cards.length; j++) {
|
|
453
|
-
cards[j]
|
|
843
|
+
var card = cards[j];
|
|
844
|
+
if (card.__pwBound) continue;
|
|
845
|
+
card.__pwBound = true;
|
|
846
|
+
card.addEventListener('click', function (ev) {
|
|
847
|
+
// Ignore clicks that originated on a button/details summary.
|
|
848
|
+
if (ev.target.closest('.pw-quick-btn') || ev.target.closest('summary')) return;
|
|
849
|
+
var sid2 = ev.currentTarget.getAttribute('data-session-id');
|
|
850
|
+
if (sid2) loadSessionDetail(sid2);
|
|
851
|
+
});
|
|
852
|
+
card.addEventListener('keydown', function (ev) {
|
|
853
|
+
if (ev.key !== 'Enter' && ev.key !== ' ') return;
|
|
854
|
+
if (ev.target.closest('.pw-quick-btn') || ev.target.closest('summary')) return;
|
|
855
|
+
ev.preventDefault();
|
|
454
856
|
var sid2 = ev.currentTarget.getAttribute('data-session-id');
|
|
455
857
|
if (sid2) loadSessionDetail(sid2);
|
|
456
858
|
});
|
|
457
859
|
}
|
|
458
860
|
}
|
|
459
861
|
|
|
862
|
+
function handleActionClick(btn) {
|
|
863
|
+
var action = btn.getAttribute('data-action');
|
|
864
|
+
var sid = btn.getAttribute('data-session-id');
|
|
865
|
+
if (!action || !sid) return;
|
|
866
|
+
if (action === 'summary') {
|
|
867
|
+
loadSafeSummary(sid);
|
|
868
|
+
} else if (action === 'finalize') {
|
|
869
|
+
finalizeSession(sid, btn);
|
|
870
|
+
} else if (action === 'audit') {
|
|
871
|
+
auditSession(sid, btn);
|
|
872
|
+
} else if (action === 'copy') {
|
|
873
|
+
copyToClipboard(sid, 'session_id');
|
|
874
|
+
} else if (action === 'hide') {
|
|
875
|
+
hideSession(sid, btn);
|
|
876
|
+
} else if (action === 'verify-bundle') {
|
|
877
|
+
// Informational: navigate to tasks page where verification tasks are created.
|
|
878
|
+
showToast('info', '跳转任务页', 'run_direct_verification_bundle 需在任务页发起');
|
|
879
|
+
setTimeout(function () { window.location.href = '/pages/tasks.html'; }, 600);
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
function loadSafeSummary(sessionId) {
|
|
884
|
+
var url = '/api/direct-sessions/' + encodeURIComponent(sessionId) + '/summary';
|
|
885
|
+
fetch(url, { method: 'GET', headers: { 'Accept': 'application/json' }, cache: 'no-store' })
|
|
886
|
+
.then(function (res) { return res.json(); })
|
|
887
|
+
.then(function (data) {
|
|
888
|
+
showSafeModal('safe_direct_summary · ' + shortId(sessionId), data);
|
|
889
|
+
})
|
|
890
|
+
.catch(function (err) {
|
|
891
|
+
showToast('error', '加载失败', err.message || '未知错误');
|
|
892
|
+
});
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
function withBusy(btn, fn) {
|
|
896
|
+
var orig = btn.innerHTML;
|
|
897
|
+
btn.disabled = true;
|
|
898
|
+
btn.innerHTML = '<span class="inline-block w-3 h-3 align-middle border-2 border-current border-t-transparent rounded-full pw-spin"></span>';
|
|
899
|
+
Promise.resolve()
|
|
900
|
+
.then(fn)
|
|
901
|
+
.catch(function (err) {
|
|
902
|
+
showToast('error', '操作失败', err.message || '未知错误');
|
|
903
|
+
})
|
|
904
|
+
.finally(function () {
|
|
905
|
+
btn.disabled = false;
|
|
906
|
+
btn.innerHTML = orig;
|
|
907
|
+
});
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
function finalizeSession(sessionId, btn) {
|
|
911
|
+
withBusy(btn, function () {
|
|
912
|
+
return postAction('/api/direct-sessions/' + encodeURIComponent(sessionId) + '/finalize')
|
|
913
|
+
.then(function (data) {
|
|
914
|
+
if (data && data.error) {
|
|
915
|
+
showToast('error', 'Finalize 失败', data.error);
|
|
916
|
+
return;
|
|
917
|
+
}
|
|
918
|
+
showToast('success', '已 Finalize', '会话 ' + shortId(sessionId) + ' 已完成 finalize');
|
|
919
|
+
loadSessions();
|
|
920
|
+
});
|
|
921
|
+
});
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
function auditSession(sessionId, btn) {
|
|
925
|
+
withBusy(btn, function () {
|
|
926
|
+
return postAction('/api/direct-sessions/' + encodeURIComponent(sessionId) + '/audit')
|
|
927
|
+
.then(function (data) {
|
|
928
|
+
if (data && data.error) {
|
|
929
|
+
showToast('error', '审计失败', data.error);
|
|
930
|
+
return;
|
|
931
|
+
}
|
|
932
|
+
var decision = (data && data.decision) || '完成';
|
|
933
|
+
showToast('success', '审计完成', '会话 ' + shortId(sessionId) + ' 审计结果: ' + decision);
|
|
934
|
+
loadSessions();
|
|
935
|
+
});
|
|
936
|
+
});
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
function hideSession(sessionId, btn) {
|
|
940
|
+
withBusy(btn, function () {
|
|
941
|
+
return postAction('/api/direct-sessions/' + encodeURIComponent(sessionId) + '/hide')
|
|
942
|
+
.then(function (data) {
|
|
943
|
+
if (data && data.error) {
|
|
944
|
+
showToast('error', '隐藏失败', data.error);
|
|
945
|
+
return;
|
|
946
|
+
}
|
|
947
|
+
showToast('success', '已隐藏', '会话 ' + shortId(sessionId) + ' 已从列表隐藏');
|
|
948
|
+
loadSessions();
|
|
949
|
+
});
|
|
950
|
+
});
|
|
951
|
+
}
|
|
952
|
+
|
|
460
953
|
// ---------- Render: session detail ----------
|
|
461
954
|
function renderSessionDetail(data) {
|
|
462
955
|
var el = $('session-detail');
|
|
@@ -473,7 +966,7 @@ html.dark {
|
|
|
473
966
|
var vs = s.verification_summary || null;
|
|
474
967
|
var vsHtml = '—';
|
|
475
968
|
if (vs && typeof vs === 'object') {
|
|
476
|
-
var vsStatus = vs.status || vs.verdict || '—';
|
|
969
|
+
var vsStatus = vs.status || vs.verdict || verificationStatusText(vs) || '—';
|
|
477
970
|
var vsCmds = Array.isArray(vs.commands) ? vs.commands.map(function (c) { return JSON.stringify(c); }).join(' ') : '';
|
|
478
971
|
vsHtml = '<span style="color: var(--pw-text-primary); font-family: var(--pw-font-mono);">' + escapeHtml(String(vsStatus)) + '</span>'
|
|
479
972
|
+ (vsCmds ? '<div class="mt-1 text-xs break-all" style="color: var(--pw-text-tertiary);">' + escapeHtml(vsCmds) + '</div>' : '');
|
|
@@ -569,11 +1062,29 @@ html.dark {
|
|
|
569
1062
|
}
|
|
570
1063
|
|
|
571
1064
|
// ---------- Network ----------
|
|
572
|
-
function
|
|
1065
|
+
function updateRepoOptions(data) {
|
|
1066
|
+
var select = $('session-repo');
|
|
1067
|
+
if (!select) return;
|
|
1068
|
+
var selected = select.value;
|
|
1069
|
+
var repos = data && data.filters && data.filters.options && Array.isArray(data.filters.options.repos) ? data.filters.options.repos : [];
|
|
1070
|
+
select.innerHTML = '<option value="">全部项目</option>' + repos.map(function (repo) { return '<option value="' + escapeHtml(repo) + '">' + escapeHtml(repo) + '</option>'; }).join('');
|
|
1071
|
+
select.value = repos.indexOf(selected) >= 0 ? selected : '';
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
function loadSessions(cursor, append) {
|
|
573
1075
|
clearError();
|
|
574
|
-
showLoading();
|
|
1076
|
+
if (!append) showLoading();
|
|
575
1077
|
setRefreshing(true);
|
|
576
|
-
|
|
1078
|
+
var state = $('session-state') ? $('session-state').value : 'active';
|
|
1079
|
+
var params = ['state=' + encodeURIComponent(state), 'limit=50'];
|
|
1080
|
+
var repo = $('session-repo') ? $('session-repo').value : '';
|
|
1081
|
+
var dateFrom = $('session-date-from') ? $('session-date-from').value : '';
|
|
1082
|
+
var dateTo = $('session-date-to') ? $('session-date-to').value : '';
|
|
1083
|
+
if (repo) params.push('repo_path=' + encodeURIComponent(repo));
|
|
1084
|
+
if (dateFrom) params.push('date_from=' + encodeURIComponent(dateFrom));
|
|
1085
|
+
if (dateTo) params.push('date_to=' + encodeURIComponent(dateTo));
|
|
1086
|
+
if (cursor) params.push('cursor=' + encodeURIComponent(cursor));
|
|
1087
|
+
fetch(API_URL + '?' + params.join('&'), { method: 'GET', headers: { 'Accept': 'application/json' }, cache: 'no-store' })
|
|
577
1088
|
.then(function (res) {
|
|
578
1089
|
if (!res.ok) {
|
|
579
1090
|
return res.text().then(function (body) {
|
|
@@ -584,7 +1095,23 @@ html.dark {
|
|
|
584
1095
|
})
|
|
585
1096
|
.then(function (data) {
|
|
586
1097
|
data = data || {};
|
|
587
|
-
|
|
1098
|
+
updateRepoOptions(data);
|
|
1099
|
+
if (data.direct_profile_enabled === false) {
|
|
1100
|
+
loadedSessions = append ? loadedSessions.concat(data.sessions || []) : (data.sessions || []);
|
|
1101
|
+
nextCursor = data.nextCursor || data.next_cursor || null;
|
|
1102
|
+
if (loadedSessions.length) {
|
|
1103
|
+
renderSessions(loadedSessions);
|
|
1104
|
+
show($('direct-disabled'));
|
|
1105
|
+
nextCursor ? show($('sessions-more-wrap')) : hide($('sessions-more-wrap'));
|
|
1106
|
+
} else {
|
|
1107
|
+
showDirectDisabled();
|
|
1108
|
+
}
|
|
1109
|
+
} else {
|
|
1110
|
+
loadedSessions = append ? loadedSessions.concat(data.sessions || []) : (data.sessions || []);
|
|
1111
|
+
nextCursor = data.nextCursor || data.next_cursor || null;
|
|
1112
|
+
renderSessions(loadedSessions);
|
|
1113
|
+
nextCursor ? show($('sessions-more-wrap')) : hide($('sessions-more-wrap'));
|
|
1114
|
+
}
|
|
588
1115
|
setLastUpdated();
|
|
589
1116
|
})
|
|
590
1117
|
.catch(function (err) {
|
|
@@ -640,13 +1167,35 @@ html.dark {
|
|
|
640
1167
|
|
|
641
1168
|
// ---------- Event wiring ----------
|
|
642
1169
|
var refreshBtn = $('refreshBtn');
|
|
643
|
-
if (refreshBtn) refreshBtn.addEventListener('click', loadSessions);
|
|
1170
|
+
if (refreshBtn) refreshBtn.addEventListener('click', function () { loadSessions(null, false); });
|
|
644
1171
|
var retryBtn = $('retry-btn');
|
|
645
|
-
if (retryBtn) retryBtn.addEventListener('click', loadSessions);
|
|
1172
|
+
if (retryBtn) retryBtn.addEventListener('click', function () { loadSessions(null, false); });
|
|
1173
|
+
var stateSelect = $('session-state');
|
|
1174
|
+
if (stateSelect) stateSelect.addEventListener('change', function () { loadSessions(null, false); });
|
|
1175
|
+
['session-repo', 'session-date-from', 'session-date-to'].forEach(function (id) {
|
|
1176
|
+
var input = $(id);
|
|
1177
|
+
if (input) input.addEventListener('change', function () { loadSessions(null, false); });
|
|
1178
|
+
});
|
|
1179
|
+
var moreBtn = $('sessions-more');
|
|
1180
|
+
if (moreBtn) moreBtn.addEventListener('click', function () { if (nextCursor) loadSessions(nextCursor, true); });
|
|
1181
|
+
|
|
1182
|
+
var modalClose = $('safe-modal-close');
|
|
1183
|
+
if (modalClose) modalClose.addEventListener('click', closeSafeModal);
|
|
1184
|
+
// Click on modal backdrop closes it.
|
|
1185
|
+
var safeModal = $('safe-modal');
|
|
1186
|
+
if (safeModal) safeModal.addEventListener('click', function (ev) {
|
|
1187
|
+
if (ev.target === safeModal) closeSafeModal();
|
|
1188
|
+
});
|
|
1189
|
+
// ESC closes modal.
|
|
1190
|
+
document.addEventListener('keydown', function (ev) {
|
|
1191
|
+
if (ev.key === 'Escape') closeSafeModal();
|
|
1192
|
+
});
|
|
646
1193
|
|
|
647
1194
|
// Initial load
|
|
648
|
-
loadSessions();
|
|
1195
|
+
loadSessions(null, false);
|
|
649
1196
|
})();
|
|
650
1197
|
</script>
|
|
1198
|
+
<script src="/i18n.js"></script>
|
|
1199
|
+
<script src="/desktop-bridge.js"></script>
|
|
651
1200
|
</body>
|
|
652
1201
|
</html>
|