patchwarden 1.5.0 → 1.5.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/README.en.md +43 -2
- package/README.md +43 -2
- package/dist/assessments/agentAssessor.d.ts +1 -1
- package/dist/assessments/agentAssessor.js +3 -3
- package/dist/assessments/assessmentStore.d.ts +1 -1
- package/dist/assessments/assessmentStore.js +2 -2
- package/dist/assessments/confirmCli.js +5 -4
- package/dist/config.d.ts +7 -0
- package/dist/config.js +34 -0
- package/dist/control/middleware/auth.d.ts +10 -0
- package/dist/control/middleware/auth.js +8 -0
- package/dist/control/middleware/static.d.ts +3 -0
- package/dist/control/middleware/static.js +65 -0
- package/dist/control/routes/audit.d.ts +15 -0
- package/dist/control/routes/audit.js +277 -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 +5 -0
- package/dist/control/routes/process.js +200 -0
- package/dist/control/routes/sessions.d.ts +22 -0
- package/dist/control/routes/sessions.js +224 -0
- package/dist/control/routes/status.d.ts +6 -0
- package/dist/control/routes/status.js +250 -0
- package/dist/control/routes/taskActions.d.ts +21 -0
- package/dist/control/routes/taskActions.js +233 -0
- package/dist/control/routes/tasks.d.ts +20 -0
- package/dist/control/routes/tasks.js +310 -0
- package/dist/control/routes/workspace.d.ts +15 -0
- package/dist/control/routes/workspace.js +193 -0
- package/dist/control/runtime.d.ts +91 -0
- package/dist/control/runtime.js +392 -0
- package/dist/control/server.d.ts +13 -0
- package/dist/control/server.js +479 -0
- package/dist/control/shared.d.ts +35 -0
- package/dist/control/shared.js +288 -0
- package/dist/controlCenter.d.ts +6 -0
- package/dist/controlCenter.js +7 -2197
- package/dist/direct/directGuards.js +30 -8
- package/dist/doctor.d.ts +18 -1
- package/dist/doctor.js +579 -348
- package/dist/goal/goalReport.d.ts +54 -0
- package/dist/goal/goalReport.js +204 -0
- package/dist/goal/goalStatus.d.ts +6 -0
- package/dist/goal/specKitImport.d.ts +63 -0
- package/dist/goal/specKitImport.js +220 -0
- package/dist/goal/subgoalSync.js +2 -1
- package/dist/httpServer.js +15 -12
- package/dist/index.js +7 -4
- package/dist/logging.d.ts +7 -1
- package/dist/logging.js +8 -0
- package/dist/runner/changeCapture.d.ts +3 -3
- package/dist/runner/changeCapture.js +63 -39
- package/dist/runner/cli.js +7 -6
- package/dist/runner/postTaskCleanup.js +26 -2
- package/dist/runner/runTask.js +245 -221
- package/dist/runner/simpleProcess.js +4 -4
- package/dist/runner/watch.js +17 -14
- package/dist/security/contentRedaction.d.ts +6 -0
- package/dist/security/contentRedaction.js +22 -0
- package/dist/smoke-test.js +257 -251
- package/dist/test/unit/apply-patch.test.d.ts +1 -0
- package/dist/test/unit/apply-patch.test.js +225 -0
- package/dist/test/unit/create-task.test.d.ts +1 -0
- package/dist/test/unit/create-task.test.js +197 -0
- package/dist/test/unit/direct-guards.test.js +124 -9
- package/dist/test/unit/evidence-pack.test.js +95 -1
- package/dist/test/unit/get-task-status.test.d.ts +1 -0
- package/dist/test/unit/get-task-status.test.js +174 -0
- package/dist/test/unit/get-task-summary.test.d.ts +1 -0
- package/dist/test/unit/get-task-summary.test.js +146 -0
- package/dist/test/unit/goal-report.test.d.ts +1 -0
- package/dist/test/unit/goal-report.test.js +159 -0
- package/dist/test/unit/goal-subgoal-task.test.js +6 -6
- package/dist/test/unit/goal-tools-registry.test.js +6 -4
- package/dist/test/unit/path-guard.test.js +24 -0
- package/dist/test/unit/spec-kit-import.test.d.ts +1 -0
- package/dist/test/unit/spec-kit-import.test.js +341 -0
- package/dist/test/unit/wait-for-task.test.d.ts +1 -0
- package/dist/test/unit/wait-for-task.test.js +144 -0
- package/dist/tools/auditTask.d.ts +8 -63
- package/dist/tools/auditTask.js +12 -8
- package/dist/tools/createDirectSession.d.ts +1 -1
- package/dist/tools/createDirectSession.js +2 -2
- package/dist/tools/createTask.d.ts +2 -2
- package/dist/tools/createTask.js +4 -4
- package/dist/tools/dispatch/coreDispatch.d.ts +9 -0
- package/dist/tools/dispatch/coreDispatch.js +282 -0
- package/dist/tools/dispatch/diagnosticDispatch.d.ts +14 -0
- package/dist/tools/dispatch/diagnosticDispatch.js +78 -0
- package/dist/tools/dispatch/directDispatch.d.ts +8 -0
- package/dist/tools/dispatch/directDispatch.js +115 -0
- package/dist/tools/dispatch/goalDispatch.d.ts +8 -0
- package/dist/tools/dispatch/goalDispatch.js +91 -0
- package/dist/tools/dispatch/releaseDispatch.d.ts +8 -0
- package/dist/tools/dispatch/releaseDispatch.js +45 -0
- package/dist/tools/dispatch/types.d.ts +23 -0
- package/dist/tools/dispatch/types.js +15 -0
- package/dist/tools/evidencePack.d.ts +6 -0
- package/dist/tools/evidencePack.js +213 -6
- package/dist/tools/finalizeDirectSession.d.ts +1 -1
- package/dist/tools/finalizeDirectSession.js +3 -3
- package/dist/tools/goalSubgoalTask.d.ts +1 -1
- package/dist/tools/goalSubgoalTask.js +2 -2
- package/dist/tools/healthCheck.js +3 -3
- package/dist/tools/registry.d.ts +3 -3
- package/dist/tools/registry.js +60 -503
- package/dist/tools/retryTask.d.ts +2 -2
- package/dist/tools/retryTask.js +2 -2
- package/dist/tools/runTaskLoop.js +4 -4
- package/dist/tools/safeViews.d.ts +2 -2
- package/dist/tools/safeViews.js +2 -2
- package/dist/tools/toolRegistry.js +22 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/docs/CODE_WIKI.md +983 -0
- package/docs/agentseal-integration.md +150 -0
- package/docs/architecture.md +63 -0
- package/docs/assets/patchwarden-oss-demo.gif +0 -0
- package/docs/dashboard-overview.md +86 -0
- package/docs/demo.md +8 -0
- package/docs/direct-session-workflow.md +98 -0
- package/docs/evidence-pack-schema.md +215 -0
- package/docs/execution-plan-2026-07-09.md +315 -0
- package/docs/lineage-evidence-pack-workflow.md +127 -0
- package/docs/mcp-inspector-testing.md +200 -0
- package/docs/open-source-application.md +162 -0
- package/docs/opencode-worker.md +151 -0
- package/docs/openhands-worker.md +181 -0
- package/docs/release-evidence.md +72 -0
- package/docs/roadmap-execution-and-acceptance.md +365 -0
- package/docs/spec-kit-integration.md +131 -0
- package/docs/task-safe-review-workflow.md +98 -0
- package/docs/threat-model.md +79 -0
- package/docs/user-feedback.md +40 -0
- package/docs/why-patchwarden.md +110 -0
- package/package.json +2 -2
- package/scripts/checks/control-center-smoke.js +356 -0
- package/scripts/checks/lifecycle-smoke.js +29 -23
- package/scripts/checks/mcp-smoke.js +3 -1
- package/scripts/generate-demo-gif.py +320 -0
- package/src/assessments/agentAssessor.ts +3 -3
- package/src/assessments/assessmentStore.ts +2 -2
- package/src/assessments/confirmCli.ts +5 -4
- package/src/config.ts +37 -0
- package/src/control/middleware/auth.ts +17 -0
- package/src/control/middleware/static.ts +71 -0
- package/src/control/routes/audit.ts +321 -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 +204 -0
- package/src/control/routes/sessions.ts +251 -0
- package/src/control/routes/status.ts +325 -0
- package/src/control/routes/taskActions.ts +248 -0
- package/src/control/routes/tasks.ts +323 -0
- package/src/control/routes/workspace.ts +203 -0
- package/src/control/runtime.ts +472 -0
- package/src/control/server.ts +471 -0
- package/src/control/shared.ts +294 -0
- package/src/controlCenter.ts +7 -2347
- package/src/direct/directGuards.ts +28 -7
- package/src/doctor.ts +741 -481
- package/src/goal/goalReport.ts +271 -0
- package/src/goal/goalStatus.ts +6 -0
- package/src/goal/specKitImport.ts +355 -0
- package/src/goal/subgoalSync.ts +4 -2
- package/src/httpServer.ts +17 -14
- package/src/index.ts +7 -4
- package/src/logging.ts +10 -1
- package/src/runner/changeCapture.ts +70 -42
- package/src/runner/cli.ts +7 -6
- package/src/runner/postTaskCleanup.ts +26 -2
- package/src/runner/runTask.ts +325 -223
- package/src/runner/simpleProcess.ts +4 -4
- package/src/runner/watch.ts +17 -14
- package/src/security/contentRedaction.ts +29 -0
- package/src/smoke-test.ts +252 -250
- package/src/test/unit/apply-patch.test.ts +293 -0
- package/src/test/unit/create-task.test.ts +255 -0
- package/src/test/unit/direct-guards.test.ts +178 -8
- package/src/test/unit/evidence-pack.test.ts +110 -1
- package/src/test/unit/get-task-status.test.ts +203 -0
- package/src/test/unit/get-task-summary.test.ts +173 -0
- package/src/test/unit/goal-report.test.ts +189 -0
- package/src/test/unit/goal-subgoal-task.test.ts +6 -6
- package/src/test/unit/goal-tools-registry.test.ts +7 -5
- package/src/test/unit/path-guard.test.ts +30 -0
- package/src/test/unit/spec-kit-import.test.ts +429 -0
- package/src/test/unit/wait-for-task.test.ts +176 -0
- package/src/tools/auditTask.ts +99 -59
- package/src/tools/createDirectSession.ts +3 -3
- package/src/tools/createTask.ts +7 -7
- package/src/tools/dispatch/coreDispatch.ts +374 -0
- package/src/tools/dispatch/diagnosticDispatch.ts +101 -0
- package/src/tools/dispatch/directDispatch.ts +167 -0
- package/src/tools/dispatch/goalDispatch.ts +127 -0
- package/src/tools/dispatch/releaseDispatch.ts +65 -0
- package/src/tools/dispatch/types.ts +24 -0
- package/src/tools/evidencePack.ts +291 -6
- package/src/tools/finalizeDirectSession.ts +4 -4
- package/src/tools/goalSubgoalTask.ts +2 -2
- package/src/tools/healthCheck.ts +3 -3
- package/src/tools/registry.ts +68 -628
- package/src/tools/retryTask.ts +2 -2
- package/src/tools/runTaskLoop.ts +4 -4
- package/src/tools/safeViews.ts +2 -2
- package/src/tools/toolRegistry.ts +22 -0
- package/src/version.ts +1 -1
- package/ui/pages/audit.html +192 -3
- package/ui/pages/dashboard.html +959 -44
- package/ui/pages/direct-sessions.html +505 -53
- package/ui/pages/task-detail.html +456 -438
- package/ui/pages/tasks.html +598 -61
|
@@ -372,176 +372,192 @@ html.dark {
|
|
|
372
372
|
</div>
|
|
373
373
|
</section>
|
|
374
374
|
|
|
375
|
-
<!--
|
|
376
|
-
<
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
<div class="flex flex-col gap-0.5 min-w-0">
|
|
386
|
-
<span class="text-xs whitespace-nowrap" style="color: var(--pw-text-tertiary);" data-zh="运行阶段" data-en="Runtime Phase">运行阶段</span>
|
|
387
|
-
<span id="rtPhase" class="truncate" style="color: var(--pw-text-primary); font-family: var(--pw-font-mono);">—</span>
|
|
388
|
-
</div>
|
|
389
|
-
<div class="flex flex-col gap-0.5 min-w-0">
|
|
390
|
-
<span class="text-xs whitespace-nowrap" style="color: var(--pw-text-tertiary);" data-zh="最近心跳" data-en="Last Heartbeat">最近心跳</span>
|
|
391
|
-
<span id="rtHeartbeat" class="truncate" style="color: var(--pw-text-secondary); font-family: var(--pw-font-mono);">—</span>
|
|
392
|
-
</div>
|
|
393
|
-
<div class="flex flex-col gap-0.5 min-w-0">
|
|
394
|
-
<span class="text-xs whitespace-nowrap" style="color: var(--pw-text-tertiary);" data-zh="当前命令" data-en="Current Command">当前命令</span>
|
|
395
|
-
<span id="rtCurrentCmd" class="truncate" style="color: var(--pw-text-secondary); font-family: var(--pw-font-mono);">—</span>
|
|
396
|
-
</div>
|
|
397
|
-
</div>
|
|
398
|
-
</section>
|
|
375
|
+
<!-- ===== Safe Summary Section (DEFAULT visible, loaded from 4 safe endpoints) ===== -->
|
|
376
|
+
<section class="rounded-lg p-5" style="background-color: var(--pw-bg-surface); border: 1px solid var(--pw-border);">
|
|
377
|
+
<div class="flex items-center gap-2 mb-1">
|
|
378
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="var(--pw-accent)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg>
|
|
379
|
+
<h2 class="text-base font-semibold" data-zh="安全摘要" data-en="Safe Summary" style="color: var(--pw-text-primary); font-family: var(--pw-font-sans);">安全摘要</h2>
|
|
380
|
+
<span class="text-xs px-2 py-0.5 rounded-md whitespace-nowrap" style="color: var(--pw-accent); background-color: var(--pw-accent-subtle); font-family: var(--pw-font-mono);">safe-first</span>
|
|
381
|
+
</div>
|
|
382
|
+
<p class="text-xs mb-5" style="color: var(--pw-text-tertiary); font-family: var(--pw-font-sans);" data-zh="默认仅展示脱敏后的安全摘要。完整结果、diff、测试日志请见下方「高级」区域。" data-en="Shows redacted safe summaries by default. Full result/diff/test log are in the Advanced section below.">默认仅展示脱敏后的安全摘要。完整结果、diff、测试日志请见下方「高级」区域。</p>
|
|
383
|
+
|
|
384
|
+
<div class="grid grid-cols-1 lg:grid-cols-2 gap-5">
|
|
399
385
|
|
|
400
|
-
<!--
|
|
401
|
-
<
|
|
402
|
-
<div class="flex items-center
|
|
403
|
-
<
|
|
404
|
-
<
|
|
386
|
+
<!-- Safe Result Overview -->
|
|
387
|
+
<div class="rounded-lg p-4 flex flex-col gap-3" style="background-color: var(--pw-bg); border: 1px solid var(--pw-border-subtle);">
|
|
388
|
+
<div class="flex items-center justify-between">
|
|
389
|
+
<h3 class="text-sm font-semibold" data-zh="结果概览" data-en="Safe Result" style="color: var(--pw-text-primary); font-family: var(--pw-font-sans);">结果概览 (safe_result)</h3>
|
|
390
|
+
<span id="srStatusBadge" class="text-xs px-2 py-0.5 rounded-md whitespace-nowrap" style="color: var(--pw-text-tertiary); background-color: var(--pw-bg-hover); font-family: var(--pw-font-mono);">—</span>
|
|
405
391
|
</div>
|
|
406
|
-
<
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
<div class="flex
|
|
413
|
-
<
|
|
414
|
-
|
|
415
|
-
<h2 class="text-base font-semibold" data-zh="变更文件" data-en="Changed Files" style="color: var(--pw-text-primary); font-family: var(--pw-font-sans);">变更文件</h2>
|
|
416
|
-
</div>
|
|
417
|
-
<span id="cfCountBadge" class="hidden text-xs px-2 py-0.5 rounded-md whitespace-nowrap" style="color: var(--pw-text-tertiary); background-color: var(--pw-bg-hover); font-family: var(--pw-font-mono);">—</span>
|
|
392
|
+
<dl class="grid grid-cols-2 gap-x-4 gap-y-2 text-xs">
|
|
393
|
+
<div class="flex flex-col gap-0.5 min-w-0"><dt style="color: var(--pw-text-tertiary);">terminal</dt><dd id="srTerminal" class="truncate" style="color: var(--pw-text-primary); font-family: var(--pw-font-mono);">—</dd></div>
|
|
394
|
+
<div class="flex flex-col gap-0.5 min-w-0"><dt style="color: var(--pw-text-tertiary);">acceptance_status</dt><dd id="srAcceptance" class="truncate" style="color: var(--pw-text-primary); font-family: var(--pw-font-mono);">—</dd></div>
|
|
395
|
+
<div class="flex flex-col gap-0.5 min-w-0"><dt style="color: var(--pw-text-tertiary);">changed_files_total</dt><dd id="srChangedTotal" class="truncate" style="color: var(--pw-text-primary); font-family: var(--pw-font-mono);">—</dd></div>
|
|
396
|
+
<div class="flex flex-col gap-0.5 min-w-0"><dt style="color: var(--pw-text-tertiary);">out_of_scope_changes_total</dt><dd id="srOosTotal" class="truncate" style="color: var(--pw-text-primary); font-family: var(--pw-font-mono);">—</dd></div>
|
|
397
|
+
</dl>
|
|
398
|
+
<div class="flex flex-col gap-0.5 min-w-0">
|
|
399
|
+
<span class="text-xs" style="color: var(--pw-text-tertiary);">verification.headline</span>
|
|
400
|
+
<span id="srVerifyHeadline" class="text-xs break-words" style="color: var(--pw-text-secondary); font-family: var(--pw-font-mono);">—</span>
|
|
418
401
|
</div>
|
|
419
|
-
<div id="
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
<div id="cfStatsRow" class="hidden grid grid-cols-2 sm:grid-cols-4 gap-2 text-xs"></div>
|
|
423
|
-
<!-- changed files list (truncated) -->
|
|
424
|
-
<div id="cfList" class="rounded-md p-2 text-xs leading-relaxed overflow-auto" style="background-color: #0d1117; color: var(--pw-text-secondary); font-family: var(--pw-font-mono); max-height: 320px;"></div>
|
|
402
|
+
<div id="srFailureBlock" class="hidden flex flex-col gap-1">
|
|
403
|
+
<div class="flex flex-col gap-0.5 min-w-0"><span class="text-xs" style="color: var(--pw-state-error);">failure_reason</span><span id="srFailureReason" class="text-xs break-words" style="color: var(--pw-state-error); font-family: var(--pw-font-mono);">—</span></div>
|
|
404
|
+
<div class="flex flex-col gap-0.5 min-w-0"><span class="text-xs" style="color: var(--pw-state-error);">failed_command</span><span id="srFailedCommand" class="text-xs break-all" style="color: var(--pw-state-error); font-family: var(--pw-font-mono);">—</span></div>
|
|
425
405
|
</div>
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
<div class="flex
|
|
431
|
-
<
|
|
432
|
-
<
|
|
406
|
+
<div class="flex flex-col gap-0.5 min-w-0">
|
|
407
|
+
<span class="text-xs" style="color: var(--pw-text-tertiary);">next_action</span>
|
|
408
|
+
<span id="srNextAction" class="text-xs break-words" style="color: var(--pw-accent); font-family: var(--pw-font-mono);">—</span>
|
|
409
|
+
</div>
|
|
410
|
+
<div id="srBlockedBlock" class="hidden flex flex-col gap-0.5 min-w-0">
|
|
411
|
+
<span class="text-xs" style="color: var(--pw-state-warning);">execution_blocked / pending_reason</span>
|
|
412
|
+
<span id="srPendingReason" class="text-xs break-words" style="color: var(--pw-state-warning); font-family: var(--pw-font-mono);">—</span>
|
|
433
413
|
</div>
|
|
434
|
-
<div id="
|
|
435
|
-
|
|
436
|
-
|
|
414
|
+
<div id="srWarningsBlock" class="hidden flex flex-col gap-1 min-w-0">
|
|
415
|
+
<span class="text-xs font-medium" style="color: var(--pw-state-warning);">warnings</span>
|
|
416
|
+
<pre id="srWarnings" class="rounded-md p-2 text-xs leading-relaxed whitespace-pre-wrap break-all" style="background-color: var(--pw-state-warning-bg); color: var(--pw-state-warning); font-family: var(--pw-font-mono); max-height: 160px; overflow-y: auto;"></pre>
|
|
417
|
+
</div>
|
|
418
|
+
</div>
|
|
437
419
|
|
|
438
|
-
|
|
420
|
+
<!-- Safe Test Summary -->
|
|
421
|
+
<div class="rounded-lg p-4 flex flex-col gap-3" style="background-color: var(--pw-bg); border: 1px solid var(--pw-border-subtle);">
|
|
422
|
+
<div class="flex items-center justify-between">
|
|
423
|
+
<h3 class="text-sm font-semibold" data-zh="测试摘要" data-en="Safe Test Summary" style="color: var(--pw-text-primary); font-family: var(--pw-font-sans);">测试摘要 (safe_test_summary)</h3>
|
|
424
|
+
<span id="stStatusBadge" class="text-xs px-2 py-0.5 rounded-md whitespace-nowrap" style="color: var(--pw-text-tertiary); background-color: var(--pw-bg-hover); font-family: var(--pw-font-mono);">—</span>
|
|
425
|
+
</div>
|
|
426
|
+
<dl class="grid grid-cols-4 gap-x-3 gap-y-2 text-xs">
|
|
427
|
+
<div class="flex flex-col gap-0.5"><dt style="color: var(--pw-text-tertiary);">commands</dt><dd id="stCmdCount" style="color: var(--pw-text-primary); font-family: var(--pw-font-mono);">—</dd></div>
|
|
428
|
+
<div class="flex flex-col gap-0.5"><dt style="color: var(--pw-text-tertiary);">passed</dt><dd id="stPassed" style="color: var(--pw-state-success); font-family: var(--pw-font-mono);">—</dd></div>
|
|
429
|
+
<div class="flex flex-col gap-0.5"><dt style="color: var(--pw-text-tertiary);">failed</dt><dd id="stFailed" style="color: var(--pw-state-error); font-family: var(--pw-font-mono);">—</dd></div>
|
|
430
|
+
<div class="flex flex-col gap-0.5"><dt style="color: var(--pw-text-tertiary);">skipped</dt><dd id="stSkipped" style="color: var(--pw-text-secondary); font-family: var(--pw-font-mono);">—</dd></div>
|
|
431
|
+
</dl>
|
|
432
|
+
<div class="flex flex-col gap-0.5 min-w-0">
|
|
433
|
+
<span class="text-xs" style="color: var(--pw-text-tertiary);">headline</span>
|
|
434
|
+
<span id="stHeadline" class="text-xs break-words" style="color: var(--pw-text-secondary); font-family: var(--pw-font-mono);">—</span>
|
|
435
|
+
</div>
|
|
436
|
+
<div class="flex flex-col gap-1 min-w-0">
|
|
437
|
+
<span class="text-xs" style="color: var(--pw-text-tertiary);">commands</span>
|
|
438
|
+
<div id="stCommandsBody" class="rounded-md p-2 text-xs leading-relaxed overflow-auto" style="background-color: #0d1117; color: var(--pw-text-secondary); font-family: var(--pw-font-mono); max-height: 220px;"></div>
|
|
439
|
+
</div>
|
|
440
|
+
</div>
|
|
439
441
|
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
<h2 class="text-base font-semibold mb-4" data-zh="结果摘要" data-en="Result Summary" style="color: var(--pw-text-primary); font-family: var(--pw-font-sans);">结果摘要</h2>
|
|
446
|
-
<div class="flex flex-col gap-3">
|
|
447
|
-
<div class="flex items-center justify-between text-sm">
|
|
448
|
-
<span style="color: var(--pw-text-secondary);" data-zh="任务状态" data-en="Status">任务状态</span>
|
|
449
|
-
<span id="rsStatus" class="font-semibold" style="color: var(--pw-text-primary); font-family: var(--pw-font-mono);">—</span>
|
|
450
|
-
</div>
|
|
451
|
-
<div class="flex items-center justify-between text-sm">
|
|
452
|
-
<span style="color: var(--pw-text-secondary);" data-zh="验证状态" data-en="Verify Status">验证状态</span>
|
|
453
|
-
<span id="rsVerifyStatus" class="font-semibold" style="color: var(--pw-text-primary); font-family: var(--pw-font-mono);">—</span>
|
|
454
|
-
</div>
|
|
455
|
-
<div id="rsSummaryBlock" class="hidden rounded-md p-3 text-xs leading-relaxed" style="background-color: var(--pw-bg); border: 1px solid var(--pw-border-subtle); color: var(--pw-text-secondary); font-family: var(--pw-font-sans);">
|
|
456
|
-
<span id="rsSummary" class="break-all whitespace-pre-wrap"></span>
|
|
457
|
-
</div>
|
|
442
|
+
<!-- Safe Diff Summary -->
|
|
443
|
+
<div class="rounded-lg p-4 flex flex-col gap-3" style="background-color: var(--pw-bg); border: 1px solid var(--pw-border-subtle);">
|
|
444
|
+
<div class="flex items-center justify-between">
|
|
445
|
+
<h3 class="text-sm font-semibold" data-zh="变更摘要" data-en="Safe Diff Summary" style="color: var(--pw-text-primary); font-family: var(--pw-font-sans);">变更摘要 (safe_diff_summary)</h3>
|
|
446
|
+
<span id="sdPatchMode" class="text-xs px-2 py-0.5 rounded-md whitespace-nowrap" style="color: var(--pw-text-tertiary); background-color: var(--pw-bg-hover); font-family: var(--pw-font-mono);">—</span>
|
|
458
447
|
</div>
|
|
459
|
-
|
|
448
|
+
<dl class="grid grid-cols-4 gap-x-3 gap-y-2 text-xs">
|
|
449
|
+
<div class="flex flex-col gap-0.5"><dt style="color: var(--pw-text-tertiary);">files</dt><dd id="sdChangedTotal" style="color: var(--pw-text-primary); font-family: var(--pw-font-mono);">—</dd></div>
|
|
450
|
+
<div class="flex flex-col gap-0.5"><dt style="color: var(--pw-text-tertiary);">additions</dt><dd id="sdAdditions" style="color: var(--pw-state-success); font-family: var(--pw-font-mono);">—</dd></div>
|
|
451
|
+
<div class="flex flex-col gap-0.5"><dt style="color: var(--pw-text-tertiary);">deletions</dt><dd id="sdDeletions" style="color: var(--pw-state-error); font-family: var(--pw-font-mono);">—</dd></div>
|
|
452
|
+
<div class="flex flex-col gap-0.5"><dt style="color: var(--pw-text-tertiary);">diff_available</dt><dd id="sdDiffAvailable" style="color: var(--pw-text-secondary); font-family: var(--pw-font-mono);">—</dd></div>
|
|
453
|
+
</dl>
|
|
454
|
+
<div id="sdTruncatedBlock" class="hidden text-xs" style="color: var(--pw-state-warning); font-family: var(--pw-font-mono);">⚠ diff_truncated / large_diff_omitted</div>
|
|
455
|
+
<div class="flex flex-col gap-1 min-w-0">
|
|
456
|
+
<span class="text-xs" style="color: var(--pw-text-tertiary);">files (path · change · kind)</span>
|
|
457
|
+
<div id="sdFilesBody" class="rounded-md p-2 text-xs leading-relaxed overflow-auto" style="background-color: #0d1117; color: var(--pw-text-secondary); font-family: var(--pw-font-mono); max-height: 220px;"></div>
|
|
458
|
+
</div>
|
|
459
|
+
<div id="sdHygieneBlock" class="hidden flex flex-col gap-0.5 min-w-0">
|
|
460
|
+
<span class="text-xs" style="color: var(--pw-text-tertiary);">artifact_hygiene_counts</span>
|
|
461
|
+
<pre id="sdHygiene" class="text-xs whitespace-pre-wrap break-all" style="color: var(--pw-text-secondary); font-family: var(--pw-font-mono);"></pre>
|
|
462
|
+
</div>
|
|
463
|
+
</div>
|
|
460
464
|
|
|
461
|
-
<!-- Audit
|
|
462
|
-
<
|
|
463
|
-
<div class="flex items-center justify-between
|
|
464
|
-
<
|
|
465
|
-
<span id="
|
|
465
|
+
<!-- Safe Audit -->
|
|
466
|
+
<div class="rounded-lg p-4 flex flex-col gap-3" style="background-color: var(--pw-bg); border: 1px solid var(--pw-border-subtle);">
|
|
467
|
+
<div class="flex items-center justify-between">
|
|
468
|
+
<h3 class="text-sm font-semibold" data-zh="审计结论" data-en="Safe Audit" style="color: var(--pw-text-primary); font-family: var(--pw-font-sans);">审计结论 (safe_audit)</h3>
|
|
469
|
+
<span id="saVerdictBadge" class="text-xs px-2 py-0.5 rounded-md whitespace-nowrap" style="color: var(--pw-text-tertiary); background-color: var(--pw-bg-hover); font-family: var(--pw-font-mono);">—</span>
|
|
470
|
+
</div>
|
|
471
|
+
<dl class="grid grid-cols-3 gap-x-3 gap-y-2 text-xs">
|
|
472
|
+
<div class="flex flex-col gap-0.5"><dt style="color: var(--pw-text-tertiary);">pass</dt><dd id="saCountsPass" style="color: var(--pw-state-success); font-family: var(--pw-font-mono);">—</dd></div>
|
|
473
|
+
<div class="flex flex-col gap-0.5"><dt style="color: var(--pw-text-tertiary);">warn</dt><dd id="saCountsWarn" style="color: var(--pw-state-warning); font-family: var(--pw-font-mono);">—</dd></div>
|
|
474
|
+
<div class="flex flex-col gap-0.5"><dt style="color: var(--pw-text-tertiary);">fail</dt><dd id="saCountsFail" style="color: var(--pw-state-error); font-family: var(--pw-font-mono);">—</dd></div>
|
|
475
|
+
</dl>
|
|
476
|
+
<div class="flex flex-col gap-0.5 min-w-0">
|
|
477
|
+
<span class="text-xs" style="color: var(--pw-text-tertiary);">acceptance.verdict / status</span>
|
|
478
|
+
<span id="saAccVerdict" class="text-xs break-words" style="color: var(--pw-text-secondary); font-family: var(--pw-font-mono);">—</span>
|
|
479
|
+
</div>
|
|
480
|
+
<div class="flex flex-col gap-0.5 min-w-0">
|
|
481
|
+
<span class="text-xs" style="color: var(--pw-text-tertiary);">acceptance.reason</span>
|
|
482
|
+
<span id="saAccReason" class="text-xs break-words" style="color: var(--pw-text-secondary); font-family: var(--pw-font-mono);">—</span>
|
|
466
483
|
</div>
|
|
467
|
-
<div class="flex flex-col gap-
|
|
468
|
-
<
|
|
469
|
-
|
|
470
|
-
<span id="auditScope" class="font-semibold" style="color: var(--pw-text-primary); font-family: var(--pw-font-mono);">—</span>
|
|
471
|
-
</div>
|
|
472
|
-
<div class="flex items-center justify-between text-sm">
|
|
473
|
-
<span style="color: var(--pw-text-secondary);" data-zh="检查时间" data-en="Checked At">检查时间</span>
|
|
474
|
-
<span id="auditCheckedAt" class="truncate" style="color: var(--pw-text-secondary); font-family: var(--pw-font-mono);">—</span>
|
|
475
|
-
</div>
|
|
476
|
-
<div id="auditEmptyHint" class="text-xs" style="color: var(--pw-text-tertiary); font-family: var(--pw-font-sans);" data-zh="暂无审计记录" data-en="No audit record yet">暂无审计记录</div>
|
|
484
|
+
<div class="flex flex-col gap-0.5 min-w-0">
|
|
485
|
+
<span class="text-xs" style="color: var(--pw-text-tertiary);">next_suggested_task</span>
|
|
486
|
+
<span id="saAccNextTask" class="text-xs break-words" style="color: var(--pw-accent); font-family: var(--pw-font-mono);">—</span>
|
|
477
487
|
</div>
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
<section class="rounded-lg overflow-hidden" style="background-color: var(--pw-bg-surface); border: 1px solid var(--pw-border);">
|
|
482
|
-
<div class="flex items-center justify-between px-4 py-3" style="border-bottom: 1px solid var(--pw-border);">
|
|
483
|
-
<div class="flex items-center gap-2">
|
|
484
|
-
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="var(--pw-text-secondary)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg>
|
|
485
|
-
<h2 class="text-base font-semibold" data-zh="独立审计报告" data-en="Independent Review" style="color: var(--pw-text-primary); font-family: var(--pw-font-sans);">独立审计报告</h2>
|
|
486
|
-
</div>
|
|
487
|
-
<span id="reviewVerdictBadge" class="text-xs px-2 py-0.5 rounded-md whitespace-nowrap" style="color: var(--pw-text-tertiary); background-color: var(--pw-bg-hover); font-family: var(--pw-font-mono);">—</span>
|
|
488
|
+
<div id="saFailChecksBlock" class="hidden flex flex-col gap-0.5 min-w-0">
|
|
489
|
+
<span class="text-xs" style="color: var(--pw-state-error);">fail_checks</span>
|
|
490
|
+
<pre id="saFailChecks" class="rounded-md p-2 text-xs whitespace-pre-wrap break-all" style="background-color: var(--pw-state-error-bg); color: var(--pw-state-error); font-family: var(--pw-font-mono); max-height: 120px; overflow-y: auto;"></pre>
|
|
488
491
|
</div>
|
|
489
|
-
<div id="
|
|
490
|
-
<
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
<!-- Verification Summary Card -->
|
|
494
|
-
<section class="rounded-lg p-5" style="background-color: var(--pw-bg-surface); border: 1px solid var(--pw-border);">
|
|
495
|
-
<h2 class="text-base font-semibold mb-4" data-zh="验证摘要" data-en="Verification Summary" style="color: var(--pw-text-primary); font-family: var(--pw-font-sans);">验证摘要</h2>
|
|
496
|
-
<div class="flex flex-col gap-3">
|
|
497
|
-
<div class="flex items-center justify-between text-sm">
|
|
498
|
-
<span style="color: var(--pw-text-secondary);" data-zh="验证状态" data-en="Status">验证状态</span>
|
|
499
|
-
<span id="vsStatus" class="font-semibold" style="color: var(--pw-text-primary); font-family: var(--pw-font-mono);">—</span>
|
|
500
|
-
</div>
|
|
501
|
-
<div class="flex items-center justify-between text-sm">
|
|
502
|
-
<span style="color: var(--pw-text-secondary);" data-zh="检查时间" data-en="Checked At">检查时间</span>
|
|
503
|
-
<span id="vsCheckedAt" class="truncate" style="color: var(--pw-text-secondary); font-family: var(--pw-font-mono);">—</span>
|
|
504
|
-
</div>
|
|
505
|
-
<div id="vsCommandsBlock" class="hidden rounded-md p-2 text-xs leading-relaxed" style="background-color: var(--pw-bg); border: 1px solid var(--pw-border-subtle); color: var(--pw-text-secondary); font-family: var(--pw-font-mono);">
|
|
506
|
-
<div class="text-xs mb-1" style="color: var(--pw-text-tertiary);" data-zh="验证命令" data-en="Commands">验证命令</div>
|
|
507
|
-
<pre id="vsCommands" class="whitespace-pre-wrap break-all"></pre>
|
|
508
|
-
</div>
|
|
509
|
-
<div id="vsEmptyHint" class="text-xs" style="color: var(--pw-text-tertiary); font-family: var(--pw-font-sans);" data-zh="暂无验证摘要" data-en="No verification summary">暂无验证摘要</div>
|
|
492
|
+
<div id="saManualBlock" class="hidden text-xs" style="color: var(--pw-state-warning); font-family: var(--pw-font-mono);">⚠ manual_verification_required</div>
|
|
493
|
+
<div id="saActionsBlock" class="hidden flex flex-col gap-0.5 min-w-0">
|
|
494
|
+
<span class="text-xs" style="color: var(--pw-text-tertiary);">recommended_next_actions</span>
|
|
495
|
+
<pre id="saActions" class="text-xs whitespace-pre-wrap break-all" style="color: var(--pw-text-secondary); font-family: var(--pw-font-mono);"></pre>
|
|
510
496
|
</div>
|
|
511
|
-
</
|
|
497
|
+
</div>
|
|
498
|
+
|
|
499
|
+
</div>
|
|
500
|
+
</section>
|
|
512
501
|
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
502
|
+
<!-- ===== Advanced Section (collapsed by default; full content loaded ON DEMAND) ===== -->
|
|
503
|
+
<details class="rounded-lg overflow-hidden" style="background-color: var(--pw-bg-surface); border: 1px solid var(--pw-border);">
|
|
504
|
+
<summary class="flex items-center gap-2 px-5 py-4 cursor-pointer select-none" style="color: var(--pw-text-primary); font-family: var(--pw-font-sans);">
|
|
505
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="var(--pw-text-secondary)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"/></svg>
|
|
506
|
+
<span class="text-base font-semibold" data-zh="高级" data-en="Advanced">高级</span>
|
|
507
|
+
<span class="text-xs px-2 py-0.5 rounded-md whitespace-nowrap" style="color: var(--pw-state-warning); background-color: var(--pw-state-warning-bg); font-family: var(--pw-font-mono);">⚠️ may contain large output</span>
|
|
508
|
+
</summary>
|
|
509
|
+
<div class="flex flex-col gap-4 px-5 pb-5">
|
|
510
|
+
<p class="text-xs" style="color: var(--pw-text-tertiary); font-family: var(--pw-font-sans);" data-zh="以下内容仅在点击时按需加载,可能包含完整 stdout/stderr/diff 等较大输出。" data-en="The following content is loaded on demand only and may contain full stdout/stderr/diff.">以下内容仅在点击时按需加载,可能包含完整 stdout/stderr/diff 等较大输出。</p>
|
|
511
|
+
|
|
512
|
+
<!-- Advanced: full result -->
|
|
513
|
+
<div class="flex flex-col gap-2">
|
|
514
|
+
<div class="flex items-center gap-2 flex-wrap">
|
|
515
|
+
<button id="advBtnResult" class="flex items-center gap-1.5 px-3 py-1.5 rounded-md text-xs font-medium transition-colors" style="background-color: var(--pw-bg-hover); color: var(--pw-text-primary); border: 1px solid var(--pw-border-subtle); font-family: var(--pw-font-sans);">
|
|
516
|
+
<svg id="advSpinnerResult" class="hidden pw-spin" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="12" y1="2" x2="12" y2="6"/><line x1="12" y1="18" x2="12" y2="22"/><line x1="4.93" y1="4.93" x2="7.76" y2="7.76"/><line x1="16.24" y1="16.24" x2="19.07" y2="19.07"/><line x1="2" y1="12" x2="6" y2="12"/><line x1="18" y1="12" x2="22" y2="12"/><line x1="4.93" y1="19.07" x2="7.76" y2="16.24"/><line x1="16.24" y1="7.76" x2="19.07" y2="4.93"/></svg>
|
|
517
|
+
<span data-zh="加载完整结果 (get_result)" data-en="Load full result (get_result)">加载完整结果 (get_result)</span>
|
|
518
|
+
</button>
|
|
519
|
+
<span class="text-xs" style="color: var(--pw-state-warning); font-family: var(--pw-font-mono);">⚠️ Advanced view: may contain large output</span>
|
|
518
520
|
</div>
|
|
519
|
-
<
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
<
|
|
526
|
-
<
|
|
527
|
-
<
|
|
528
|
-
</
|
|
521
|
+
<pre id="advPreResult" class="hidden px-4 py-3 text-xs leading-relaxed overflow-x-auto whitespace-pre-wrap break-all" style="background-color: #0d1117; color: var(--pw-text-secondary); font-family: var(--pw-font-mono); max-height: 520px; overflow-y: auto;"></pre>
|
|
522
|
+
</div>
|
|
523
|
+
|
|
524
|
+
<!-- Advanced: full test log -->
|
|
525
|
+
<div class="flex flex-col gap-2">
|
|
526
|
+
<div class="flex items-center gap-2 flex-wrap">
|
|
527
|
+
<button id="advBtnTestLog" class="flex items-center gap-1.5 px-3 py-1.5 rounded-md text-xs font-medium transition-colors" style="background-color: var(--pw-bg-hover); color: var(--pw-text-primary); border: 1px solid var(--pw-border-subtle); font-family: var(--pw-font-sans);">
|
|
528
|
+
<svg id="advSpinnerTestLog" class="hidden pw-spin" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="12" y1="2" x2="12" y2="6"/><line x1="12" y1="18" x2="12" y2="22"/><line x1="4.93" y1="4.93" x2="7.76" y2="7.76"/><line x1="16.24" y1="16.24" x2="19.07" y2="19.07"/><line x1="2" y1="12" x2="6" y2="12"/><line x1="18" y1="12" x2="22" y2="12"/><line x1="4.93" y1="19.07" x2="7.76" y2="16.24"/><line x1="16.24" y1="7.76" x2="19.07" y2="4.93"/></svg>
|
|
529
|
+
<span data-zh="加载完整测试日志 (get_test_log)" data-en="Load full test log (get_test_log)">加载完整测试日志 (get_test_log)</span>
|
|
530
|
+
</button>
|
|
531
|
+
<span class="text-xs" style="color: var(--pw-state-warning); font-family: var(--pw-font-mono);">⚠️ Advanced view: may contain large output</span>
|
|
529
532
|
</div>
|
|
530
|
-
|
|
533
|
+
<pre id="advPreTestLog" class="hidden px-4 py-3 text-xs leading-relaxed overflow-x-auto whitespace-pre-wrap break-all" style="background-color: #0d1117; color: var(--pw-text-secondary); font-family: var(--pw-font-mono); max-height: 520px; overflow-y: auto;"></pre>
|
|
534
|
+
</div>
|
|
531
535
|
|
|
532
|
-
<!--
|
|
533
|
-
<
|
|
534
|
-
<div class="flex items-center gap-2
|
|
535
|
-
<
|
|
536
|
-
|
|
536
|
+
<!-- Advanced: full diff -->
|
|
537
|
+
<div class="flex flex-col gap-2">
|
|
538
|
+
<div class="flex items-center gap-2 flex-wrap">
|
|
539
|
+
<button id="advBtnDiff" class="flex items-center gap-1.5 px-3 py-1.5 rounded-md text-xs font-medium transition-colors" style="background-color: var(--pw-bg-hover); color: var(--pw-text-primary); border: 1px solid var(--pw-border-subtle); font-family: var(--pw-font-sans);">
|
|
540
|
+
<svg id="advSpinnerDiff" class="hidden pw-spin" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="12" y1="2" x2="12" y2="6"/><line x1="12" y1="18" x2="12" y2="22"/><line x1="4.93" y1="4.93" x2="7.76" y2="7.76"/><line x1="16.24" y1="16.24" x2="19.07" y2="19.07"/><line x1="2" y1="12" x2="6" y2="12"/><line x1="18" y1="12" x2="22" y2="12"/><line x1="4.93" y1="19.07" x2="7.76" y2="16.24"/><line x1="16.24" y1="7.76" x2="19.07" y2="4.93"/></svg>
|
|
541
|
+
<span data-zh="加载完整 diff (get_diff)" data-en="Load full diff (get_diff)">加载完整 diff (get_diff)</span>
|
|
542
|
+
</button>
|
|
543
|
+
<span class="text-xs" style="color: var(--pw-state-warning); font-family: var(--pw-font-mono);">⚠️ Advanced view: may contain large output</span>
|
|
537
544
|
</div>
|
|
538
|
-
<
|
|
539
|
-
|
|
540
|
-
</section>
|
|
545
|
+
<pre id="advPreDiff" class="hidden px-4 py-3 text-xs leading-relaxed overflow-x-auto whitespace-pre-wrap break-all" style="background-color: #0d1117; color: var(--pw-text-secondary); font-family: var(--pw-font-mono); max-height: 520px; overflow-y: auto;"></pre>
|
|
546
|
+
</div>
|
|
541
547
|
|
|
548
|
+
<!-- Advanced: full audit_task review -->
|
|
549
|
+
<div class="flex flex-col gap-2">
|
|
550
|
+
<div class="flex items-center gap-2 flex-wrap">
|
|
551
|
+
<button id="advBtnAudit" class="flex items-center gap-1.5 px-3 py-1.5 rounded-md text-xs font-medium transition-colors" style="background-color: var(--pw-accent-muted); color: var(--pw-accent); border: 1px solid rgba(45, 212, 168, 0.3); font-family: var(--pw-font-sans);">
|
|
552
|
+
<svg id="advSpinnerAudit" class="hidden pw-spin" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="12" y1="2" x2="12" y2="6"/><line x1="12" y1="18" x2="12" y2="22"/><line x1="4.93" y1="4.93" x2="7.76" y2="7.76"/><line x1="16.24" y1="16.24" x2="19.07" y2="19.07"/><line x1="2" y1="12" x2="6" y2="12"/><line x1="18" y1="12" x2="22" y2="12"/><line x1="4.93" y1="19.07" x2="7.76" y2="16.24"/><line x1="16.24" y1="7.76" x2="19.07" y2="4.93"/></svg>
|
|
553
|
+
<span data-zh="运行 audit_task 完整审计" data-en="Run audit_task full review">运行 audit_task 完整审计</span>
|
|
554
|
+
</button>
|
|
555
|
+
<span class="text-xs" style="color: var(--pw-state-warning); font-family: var(--pw-font-mono);">⚠️ Advanced view: may contain large output</span>
|
|
556
|
+
</div>
|
|
557
|
+
<pre id="advPreAudit" class="hidden px-4 py-3 text-xs leading-relaxed overflow-x-auto whitespace-pre-wrap break-all" style="background-color: #0d1117; color: var(--pw-text-secondary); font-family: var(--pw-font-mono); max-height: 520px; overflow-y: auto;"></pre>
|
|
558
|
+
</div>
|
|
542
559
|
</div>
|
|
543
|
-
|
|
544
|
-
</div>
|
|
560
|
+
</details>
|
|
545
561
|
|
|
546
562
|
</div>
|
|
547
563
|
|
|
@@ -705,311 +721,171 @@ html.dark {
|
|
|
705
721
|
show($('detailContent'));
|
|
706
722
|
}
|
|
707
723
|
|
|
708
|
-
// ---- Render
|
|
709
|
-
|
|
710
|
-
|
|
724
|
+
// ---- Render Safe Summary (from 4 safe endpoints) ----
|
|
725
|
+
// renderHeader populates the Task Header Card from safe_result
|
|
726
|
+
function renderHeader(taskId, safeResult) {
|
|
711
727
|
text($('breadcrumbId'), taskId);
|
|
712
728
|
text($('hdrTaskId'), taskId);
|
|
713
729
|
|
|
714
|
-
|
|
715
|
-
var status = (data.status && data.status.status) || data.status_status || null;
|
|
730
|
+
var status = safeResult.status || null;
|
|
716
731
|
var statusMeta = getStatusMeta(status);
|
|
717
732
|
text($('hdrStatusText'), statusMeta.text);
|
|
718
733
|
applyBadgeColors($('hdrStatusBadge'), statusMeta.color);
|
|
719
734
|
|
|
720
|
-
|
|
721
|
-
var phase = (data.status && data.status.phase) || null;
|
|
722
|
-
text($('hdrPhase'), getPhaseLabel(phase));
|
|
735
|
+
text($('hdrPhase'), getPhaseLabel(safeResult.phase));
|
|
723
736
|
|
|
724
|
-
//
|
|
725
|
-
|
|
726
|
-
text($('hdrAgent'), agent || '—');
|
|
737
|
+
// repo_path from safe_result
|
|
738
|
+
text($('hdrRepoPath'), safeResult.repo_path || '—');
|
|
727
739
|
|
|
728
|
-
//
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
text($('hdrCreatedAt'), formatTime(createdAt));
|
|
732
|
-
text($('hdrUpdatedAt'), formatTime(updatedAt));
|
|
740
|
+
// created_at/updated_at not available from safe endpoints; keep placeholder
|
|
741
|
+
text($('hdrCreatedAt'), '—');
|
|
742
|
+
text($('hdrUpdatedAt'), '—');
|
|
733
743
|
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
text($('
|
|
737
|
-
|
|
738
|
-
text($('
|
|
744
|
+
text($('hdrTestCommand'), '—');
|
|
745
|
+
text($('hdrTimeout'), '—');
|
|
746
|
+
text($('hdrAgent'), '—');
|
|
747
|
+
text($('hdrVerifyCommands'), '—');
|
|
748
|
+
text($('hdrPlanId'), '—');
|
|
739
749
|
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
text($('hdrVerifyCommands'), renderListOrJoin(verifyCommands));
|
|
746
|
-
|
|
747
|
-
var planId = (data.status && data.status.plan_id) || null;
|
|
748
|
-
text($('hdrPlanId'), planId || '—');
|
|
749
|
-
|
|
750
|
-
// Error block
|
|
751
|
-
var statusError = (data.status && data.status.error) || null;
|
|
752
|
-
if (statusError) {
|
|
753
|
-
text($('hdrErrorText'), escapeText(statusError));
|
|
750
|
+
if (safeResult.errors && safeResult.errors.length > 0) {
|
|
751
|
+
text($('hdrErrorText'), safeResult.errors.join('\n'));
|
|
752
|
+
show($('hdrErrorBlock'));
|
|
753
|
+
} else if (safeResult.failure_reason) {
|
|
754
|
+
text($('hdrErrorText'), safeResult.failure_reason);
|
|
754
755
|
show($('hdrErrorBlock'));
|
|
755
756
|
} else {
|
|
756
757
|
hide($('hdrErrorBlock'));
|
|
757
758
|
}
|
|
759
|
+
}
|
|
758
760
|
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
var rtHeartbeat = runtime ? (runtime.last_heartbeat_at || null) : null;
|
|
764
|
-
text($('rtHeartbeat'), formatTime(rtHeartbeat));
|
|
765
|
-
var rtCmd = runtime ? (runtime.current_command || null) : null;
|
|
766
|
-
text($('rtCurrentCmd'), rtCmd || '—');
|
|
767
|
-
|
|
768
|
-
// ---- Result summary ----
|
|
769
|
-
var result = data.result || null;
|
|
770
|
-
var rsStatus = result ? (result.status || null) : null;
|
|
771
|
-
text($('rsStatus'), rsStatus || '—');
|
|
772
|
-
var rsVerify = result ? (result.verify_status || null) : null;
|
|
773
|
-
text($('rsVerifyStatus'), rsVerify || '—');
|
|
774
|
-
var rsSummary = result ? (result.summary || null) : null;
|
|
775
|
-
if (rsSummary) {
|
|
776
|
-
text($('rsSummary'), escapeText(rsSummary));
|
|
777
|
-
show($('rsSummaryBlock'));
|
|
778
|
-
} else {
|
|
779
|
-
hide($('rsSummaryBlock'));
|
|
780
|
-
}
|
|
761
|
+
// Render safe_result card
|
|
762
|
+
function renderSafeResult(sr) {
|
|
763
|
+
text($('srStatusBadge'), sr.status || '—');
|
|
764
|
+
applyBadgeColors($('srStatusBadge'), getStatusMeta(sr.status).color);
|
|
781
765
|
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
var scopeChanges = audit ? (audit.scope_changes || null) : null;
|
|
787
|
-
text($('auditScope'), scopeChanges || '—');
|
|
788
|
-
var checkedAt = audit ? (audit.checked_at || null) : null;
|
|
789
|
-
text($('auditCheckedAt'), formatTime(checkedAt));
|
|
790
|
-
if (audit) {
|
|
791
|
-
hide($('auditEmptyHint'));
|
|
792
|
-
} else {
|
|
793
|
-
show($('auditEmptyHint'));
|
|
794
|
-
}
|
|
766
|
+
text($('srTerminal'), sr.terminal ? 'true' : 'false');
|
|
767
|
+
text($('srAcceptance'), sr.acceptance_status || '—');
|
|
768
|
+
text($('srChangedTotal'), sr.changed_files_total != null ? String(sr.changed_files_total) : '—');
|
|
769
|
+
text($('srOosTotal'), sr.out_of_scope_changes_total != null ? String(sr.out_of_scope_changes_total) : '—');
|
|
795
770
|
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
var diff = data.diff_patch || data.diff || data.diffPatch || null;
|
|
799
|
-
var diffEl = $('diffContent');
|
|
800
|
-
var diffEmpty = $('diffEmpty');
|
|
801
|
-
if (diff) {
|
|
802
|
-
text(diffEl, escapeText(diff));
|
|
803
|
-
show(diffEl);
|
|
804
|
-
hide(diffEmpty);
|
|
805
|
-
} else {
|
|
806
|
-
hide(diffEl);
|
|
807
|
-
show(diffEmpty);
|
|
808
|
-
}
|
|
771
|
+
var verifyHeadline = (sr.verification && sr.verification.headline) || null;
|
|
772
|
+
text($('srVerifyHeadline'), verifyHeadline || '—');
|
|
809
773
|
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
if (testLog) {
|
|
815
|
-
text(logEl, escapeText(testLog));
|
|
816
|
-
show(logEl);
|
|
817
|
-
hide(logEmpty);
|
|
774
|
+
if (sr.failure_reason || sr.failed_command) {
|
|
775
|
+
text($('srFailureReason'), sr.failure_reason || '—');
|
|
776
|
+
text($('srFailedCommand'), sr.failed_command || '—');
|
|
777
|
+
show($('srFailureBlock'));
|
|
818
778
|
} else {
|
|
819
|
-
hide(
|
|
820
|
-
show(logEmpty);
|
|
779
|
+
hide($('srFailureBlock'));
|
|
821
780
|
}
|
|
822
781
|
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
var vlEl = $('verifyLogContent');
|
|
829
|
-
var vlEmpty = $('verifyLogEmpty');
|
|
830
|
-
if (verifyLog) {
|
|
831
|
-
text(vlEl, escapeText(verifyLog));
|
|
832
|
-
show(vlEl);
|
|
833
|
-
hide(vlEmpty);
|
|
782
|
+
text($('srNextAction'), sr.next_action || '—');
|
|
783
|
+
|
|
784
|
+
if (sr.execution_blocked || sr.pending_reason) {
|
|
785
|
+
text($('srPendingReason'), sr.pending_reason || (sr.execution_blocked ? 'blocked' : '—'));
|
|
786
|
+
show($('srBlockedBlock'));
|
|
834
787
|
} else {
|
|
835
|
-
hide(
|
|
836
|
-
show(vlEmpty);
|
|
788
|
+
hide($('srBlockedBlock'));
|
|
837
789
|
}
|
|
838
790
|
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
var rvVerdict = review ? (review.verdict || null) : null;
|
|
843
|
-
var rvEl = $('reviewContent');
|
|
844
|
-
var rvEmpty = $('reviewEmpty');
|
|
845
|
-
var rvBadge = $('reviewVerdictBadge');
|
|
846
|
-
if (rvContent) {
|
|
847
|
-
text(rvEl, escapeText(rvContent));
|
|
848
|
-
show(rvEl);
|
|
849
|
-
hide(rvEmpty);
|
|
791
|
+
if (Array.isArray(sr.warnings) && sr.warnings.length > 0) {
|
|
792
|
+
$('srWarnings').textContent = sr.warnings.join('\n');
|
|
793
|
+
show($('srWarningsBlock'));
|
|
850
794
|
} else {
|
|
851
|
-
hide(
|
|
852
|
-
show(rvEmpty);
|
|
795
|
+
hide($('srWarningsBlock'));
|
|
853
796
|
}
|
|
854
|
-
|
|
797
|
+
}
|
|
855
798
|
|
|
856
|
-
|
|
857
|
-
|
|
799
|
+
// Render safe_test_summary card
|
|
800
|
+
function renderSafeTestSummary(sts) {
|
|
801
|
+
text($('stStatusBadge'), sts.status || '—');
|
|
802
|
+
applyBadgeColors($('stStatusBadge'), getStatusMeta(sts.status).color);
|
|
858
803
|
|
|
859
|
-
|
|
860
|
-
|
|
804
|
+
text($('stCmdCount'), String(sts.command_count || 0));
|
|
805
|
+
text($('stPassed'), String(sts.passed_commands || 0));
|
|
806
|
+
text($('stFailed'), String(sts.failed_commands || 0));
|
|
807
|
+
text($('stSkipped'), String(sts.skipped_commands || 0));
|
|
861
808
|
|
|
862
|
-
|
|
863
|
-
renderStaleBanner(data.stale, data.reconcile);
|
|
809
|
+
text($('stHeadline'), sts.headline || '—');
|
|
864
810
|
|
|
865
|
-
|
|
811
|
+
var cmds = Array.isArray(sts.commands) ? sts.commands : [];
|
|
812
|
+
var cmdsHtml = '';
|
|
813
|
+
for (var i = 0; i < cmds.length; i++) {
|
|
814
|
+
var cmd = cmds[i];
|
|
815
|
+
var statusColor = cmd.status === 'passed' ? 'var(--pw-state-success)' : (cmd.status === 'failed' || cmd.status === 'timed_out') ? 'var(--pw-state-error)' : 'var(--pw-text-secondary)';
|
|
816
|
+
cmdsHtml += '<div style="margin-bottom:4px;"><span style="color:' + statusColor + ';">' + escapeHtml(cmd.status || 'unknown') + '</span> <span style="color:var(--pw-text-tertiary);">exit=' + (cmd.exit_code !== null ? cmd.exit_code : '—') + '</span> <span style="color:var(--pw-text-primary);">' + escapeHtml(cmd.command || '') + '</span></div>';
|
|
817
|
+
}
|
|
818
|
+
if (!cmdsHtml) cmdsHtml = '<div style="color:var(--pw-text-tertiary);">—</div>';
|
|
819
|
+
$('stCommandsBody').innerHTML = cmdsHtml;
|
|
866
820
|
}
|
|
867
821
|
|
|
868
|
-
//
|
|
869
|
-
function
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
if (!changedFiles) {
|
|
877
|
-
hide(bodyEl);
|
|
878
|
-
hide(badgeEl);
|
|
879
|
-
show(emptyEl);
|
|
880
|
-
return;
|
|
881
|
-
}
|
|
822
|
+
// Render safe_diff_summary card
|
|
823
|
+
function renderSafeDiffSummary(sds) {
|
|
824
|
+
text($('sdPatchMode'), sds.patch_mode || '—');
|
|
825
|
+
text($('sdChangedTotal'), String(sds.changed_files_total || 0));
|
|
826
|
+
text($('sdAdditions'), String(sds.additions || 0));
|
|
827
|
+
text($('sdDeletions'), String(sds.deletions || 0));
|
|
828
|
+
text($('sdDiffAvailable'), sds.diff_available ? 'true' : 'false');
|
|
882
829
|
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
if (Array.isArray(changedFiles.files)) {
|
|
886
|
-
for (var i = 0; i < changedFiles.files.length; i++) {
|
|
887
|
-
var f = changedFiles.files[i];
|
|
888
|
-
if (typeof f === 'string') paths.push(f);
|
|
889
|
-
else if (f && typeof f === 'object' && typeof f.path === 'string') paths.push(f.path);
|
|
890
|
-
}
|
|
891
|
-
}
|
|
892
|
-
if (paths.length === 0 && Array.isArray(changedFiles.changed_files)) {
|
|
893
|
-
for (var j = 0; j < changedFiles.changed_files.length; j++) {
|
|
894
|
-
var cf = changedFiles.changed_files[j];
|
|
895
|
-
if (typeof cf === 'string') paths.push(cf);
|
|
896
|
-
else if (cf && typeof cf === 'object' && typeof cf.path === 'string') paths.push(cf.path);
|
|
897
|
-
}
|
|
898
|
-
}
|
|
899
|
-
|
|
900
|
-
// file_stats summary
|
|
901
|
-
var statsHtml = '';
|
|
902
|
-
if (fileStats && typeof fileStats === 'object') {
|
|
903
|
-
var counts = fileStats.counts || fileStats;
|
|
904
|
-
var statItems = [
|
|
905
|
-
{ label: 'modified', value: counts.modified != null ? counts.modified : null },
|
|
906
|
-
{ label: 'added', value: counts.added != null ? counts.added : null },
|
|
907
|
-
{ label: 'deleted', value: counts.deleted != null ? counts.deleted : null },
|
|
908
|
-
{ label: 'untracked', value: counts.untracked != null ? counts.untracked : null }
|
|
909
|
-
];
|
|
910
|
-
var hasAny = false;
|
|
911
|
-
statsHtml = statItems.map(function (it) {
|
|
912
|
-
if (it.value === null || it.value === undefined) return '';
|
|
913
|
-
hasAny = true;
|
|
914
|
-
return '<div class="rounded-md px-2 py-1.5" style="background-color: var(--pw-bg); border: 1px solid var(--pw-border-subtle);">'
|
|
915
|
-
+ '<div class="text-xs" style="color: var(--pw-text-tertiary);">' + escapeHtml(it.label) + '</div>'
|
|
916
|
-
+ '<div class="text-sm font-semibold" style="color: var(--pw-text-primary); font-family: var(--pw-font-mono);">' + escapeHtml(String(it.value)) + '</div>'
|
|
917
|
-
+ '</div>';
|
|
918
|
-
}).join('');
|
|
919
|
-
if (hasAny) {
|
|
920
|
-
show(statsEl);
|
|
921
|
-
statsEl.innerHTML = statsHtml;
|
|
922
|
-
} else {
|
|
923
|
-
hide(statsEl);
|
|
924
|
-
}
|
|
830
|
+
if (sds.diff_truncated || sds.large_diff_omitted) {
|
|
831
|
+
show($('sdTruncatedBlock'));
|
|
925
832
|
} else {
|
|
926
|
-
hide(
|
|
833
|
+
hide($('sdTruncatedBlock'));
|
|
927
834
|
}
|
|
928
835
|
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
// Render path list (cap at 500 to avoid rendering huge DOM).
|
|
937
|
-
var displayCount = Math.min(paths.length, 500);
|
|
938
|
-
var listHtml = '';
|
|
939
|
-
for (var k = 0; k < displayCount; k++) {
|
|
940
|
-
listHtml += escapeHtml(paths[k]) + '\n';
|
|
941
|
-
}
|
|
942
|
-
if (paths.length > displayCount) {
|
|
943
|
-
listHtml += '\n... (' + (paths.length - displayCount) + ' more truncated)';
|
|
836
|
+
var files = Array.isArray(sds.files) ? sds.files : [];
|
|
837
|
+
var filesHtml = '';
|
|
838
|
+
for (var j = 0; j < files.length; j++) {
|
|
839
|
+
var f = files[j];
|
|
840
|
+
var changeColor = (f.change === 'added' || f.change === 'A') ? 'var(--pw-state-success)' : (f.change === 'deleted' || f.change === 'D') ? 'var(--pw-state-error)' : 'var(--pw-accent)';
|
|
841
|
+
filesHtml += '<div style="margin-bottom:2px;"><span style="color:' + changeColor + ';">' + escapeHtml(f.change || '?') + '</span> <span style="color:var(--pw-text-tertiary);">' + escapeHtml(f.kind || 'file') + '</span> <span style="color:var(--pw-text-primary);">' + escapeHtml(f.path || '') + '</span></div>';
|
|
944
842
|
}
|
|
945
|
-
|
|
843
|
+
if (!filesHtml) filesHtml = '<div style="color:var(--pw-text-tertiary);">—</div>';
|
|
844
|
+
$('sdFilesBody').innerHTML = filesHtml;
|
|
946
845
|
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
hide(emptyEl);
|
|
952
|
-
show(bodyEl);
|
|
953
|
-
}
|
|
954
|
-
|
|
955
|
-
// ---- Render: verification summary ----
|
|
956
|
-
function renderVerificationSummary(vs) {
|
|
957
|
-
var statusEl = $('vsStatus');
|
|
958
|
-
var checkedEl = $('vsCheckedAt');
|
|
959
|
-
var cmdsBlock = $('vsCommandsBlock');
|
|
960
|
-
var cmdsEl = $('vsCommands');
|
|
961
|
-
var emptyHint = $('vsEmptyHint');
|
|
962
|
-
|
|
963
|
-
if (!vs) {
|
|
964
|
-
text(statusEl, '—');
|
|
965
|
-
text(checkedEl, '—');
|
|
966
|
-
hide(cmdsBlock);
|
|
967
|
-
show(emptyHint);
|
|
968
|
-
return;
|
|
969
|
-
}
|
|
970
|
-
hide(emptyHint);
|
|
971
|
-
text(statusEl, vs.status || '—');
|
|
972
|
-
text(checkedEl, formatTime(vs.checked_at));
|
|
973
|
-
if (Array.isArray(vs.commands) && vs.commands.length > 0) {
|
|
974
|
-
text(cmdsEl, vs.commands.map(function (c) { return JSON.stringify(c); }).join('\n'));
|
|
975
|
-
show(cmdsBlock);
|
|
846
|
+
if (sds.artifact_hygiene_counts && Object.keys(sds.artifact_hygiene_counts).length > 0) {
|
|
847
|
+
$('sdHygiene').textContent = JSON.stringify(sds.artifact_hygiene_counts, null, 2);
|
|
848
|
+
show($('sdHygieneBlock'));
|
|
976
849
|
} else {
|
|
977
|
-
hide(
|
|
850
|
+
hide($('sdHygieneBlock'));
|
|
978
851
|
}
|
|
979
852
|
}
|
|
980
853
|
|
|
981
|
-
//
|
|
982
|
-
function
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
var
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
var
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
854
|
+
// Render safe_audit card
|
|
855
|
+
function renderSafeAudit(sa) {
|
|
856
|
+
text($('saVerdictBadge'), sa.verdict || '—');
|
|
857
|
+
applyVerdictBadge($('saVerdictBadge'), sa.verdict);
|
|
858
|
+
|
|
859
|
+
var cc = sa.check_counts || {};
|
|
860
|
+
text($('saCountsPass'), String(cc.pass || 0));
|
|
861
|
+
text($('saCountsWarn'), String(cc.warn || 0));
|
|
862
|
+
text($('saCountsFail'), String(cc.fail || 0));
|
|
863
|
+
|
|
864
|
+
var acc = sa.acceptance || {};
|
|
865
|
+
text($('saAccVerdict'), (acc.verdict || '—') + ' / ' + (acc.status || '—'));
|
|
866
|
+
text($('saAccReason'), acc.reason || '—');
|
|
867
|
+
text($('saAccNextTask'), acc.next_suggested_task || '—');
|
|
868
|
+
|
|
869
|
+
var failChecks = Array.isArray(sa.fail_checks) ? sa.fail_checks : [];
|
|
870
|
+
if (failChecks.length > 0) {
|
|
871
|
+
$('saFailChecks').textContent = failChecks.map(function (c) { return c.name || String(c); }).join('\n');
|
|
872
|
+
show($('saFailChecksBlock'));
|
|
873
|
+
} else {
|
|
874
|
+
hide($('saFailChecksBlock'));
|
|
997
875
|
}
|
|
998
|
-
hide(emptyEl);
|
|
999
|
-
show(bodyEl);
|
|
1000
876
|
|
|
1001
|
-
if (
|
|
1002
|
-
|
|
1003
|
-
show(warnBlock);
|
|
877
|
+
if (sa.manual_verification_required) {
|
|
878
|
+
show($('saManualBlock'));
|
|
1004
879
|
} else {
|
|
1005
|
-
hide(
|
|
880
|
+
hide($('saManualBlock'));
|
|
1006
881
|
}
|
|
1007
882
|
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
883
|
+
var actions = Array.isArray(sa.recommended_next_actions) ? sa.recommended_next_actions : [];
|
|
884
|
+
if (actions.length > 0) {
|
|
885
|
+
$('saActions').textContent = actions.join('\n');
|
|
886
|
+
show($('saActionsBlock'));
|
|
1011
887
|
} else {
|
|
1012
|
-
hide(
|
|
888
|
+
hide($('saActionsBlock'));
|
|
1013
889
|
}
|
|
1014
890
|
}
|
|
1015
891
|
|
|
@@ -1055,7 +931,7 @@ html.dark {
|
|
|
1055
931
|
alert('运行审计失败: ' + msg);
|
|
1056
932
|
} else {
|
|
1057
933
|
alert('审计完成。verdict=' + ((r.data && r.data.audit && r.data.audit.verdict) || '—'));
|
|
1058
|
-
|
|
934
|
+
loadSafeSummary(taskId);
|
|
1059
935
|
}
|
|
1060
936
|
})
|
|
1061
937
|
.catch(function (err) {
|
|
@@ -1089,7 +965,7 @@ html.dark {
|
|
|
1089
965
|
} else {
|
|
1090
966
|
var decision = (r.data && r.data.decision) || '—';
|
|
1091
967
|
alert('Reconcile 完成: ' + decision);
|
|
1092
|
-
|
|
968
|
+
loadSafeSummary(taskId);
|
|
1093
969
|
}
|
|
1094
970
|
})
|
|
1095
971
|
.catch(function (err) {
|
|
@@ -1146,45 +1022,167 @@ html.dark {
|
|
|
1146
1022
|
} catch (e) { /* ignore */ }
|
|
1147
1023
|
}
|
|
1148
1024
|
|
|
1149
|
-
// ----
|
|
1150
|
-
function
|
|
1025
|
+
// ---- Safe summary fetch (4 safe endpoints, parallel; default on init) ----
|
|
1026
|
+
function safeGet(url) {
|
|
1027
|
+
return fetch(url, { method: 'GET', headers: { 'Accept': 'application/json' }, cache: 'no-store' })
|
|
1028
|
+
.then(function (res) {
|
|
1029
|
+
return res.json().then(function (data) {
|
|
1030
|
+
return { ok: res.ok, status: res.status, data: data };
|
|
1031
|
+
});
|
|
1032
|
+
});
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
function loadSafeSummary(taskId) {
|
|
1151
1036
|
showLoading();
|
|
1152
|
-
var
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1037
|
+
var base = '/api/tasks/' + encodeURIComponent(taskId);
|
|
1038
|
+
Promise.all([
|
|
1039
|
+
safeGet(base + '/safe-result'),
|
|
1040
|
+
safeGet(base + '/safe-test-summary'),
|
|
1041
|
+
safeGet(base + '/safe-diff-summary'),
|
|
1042
|
+
safeGet(base + '/safe-audit')
|
|
1043
|
+
]).then(function (results) {
|
|
1044
|
+
var safeResult = results[0].data || {};
|
|
1045
|
+
var safeTest = results[1].data || {};
|
|
1046
|
+
var safeDiff = results[2].data || {};
|
|
1047
|
+
var safeAudit = results[3].data || {};
|
|
1048
|
+
|
|
1049
|
+
// If safe-result returned an explicit error and no status, treat as not found.
|
|
1050
|
+
if (safeResult.error && safeResult.status === undefined) {
|
|
1051
|
+
showError('任务不存在', safeResult.error || ('未找到任务 ID: ' + taskId));
|
|
1052
|
+
return;
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
renderHeader(taskId, safeResult);
|
|
1056
|
+
renderSafeResult(safeResult);
|
|
1057
|
+
renderSafeTestSummary(safeTest);
|
|
1058
|
+
renderSafeDiffSummary(safeDiff);
|
|
1059
|
+
renderSafeAudit(safeAudit);
|
|
1060
|
+
showDetail();
|
|
1061
|
+
}).catch(function (err) {
|
|
1062
|
+
var msg = (err && err.message) ? err.message : '未知错误';
|
|
1063
|
+
if (err && err.name === 'TypeError' && /fetch/i.test(msg)) {
|
|
1064
|
+
showError('无法连接服务', '请确认 PatchWarden Control Center 正在运行 (127.0.0.1:8090)');
|
|
1065
|
+
} else {
|
|
1066
|
+
showError('加载失败', msg);
|
|
1067
|
+
}
|
|
1068
|
+
});
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
// ---- Advanced on-demand loaders (full content; only fetched when clicked) ----
|
|
1072
|
+
// HTML id pattern: advBtn<Suffix>, advSpinner<Suffix>, advPre<Suffix>
|
|
1073
|
+
function setAdvLoading(suffix, loading) {
|
|
1074
|
+
var spinner = $('advSpinner' + suffix);
|
|
1075
|
+
var btn = $('advBtn' + suffix);
|
|
1076
|
+
if (spinner) { if (loading) show(spinner); else hide(spinner); }
|
|
1077
|
+
if (btn) { btn.disabled = loading; btn.style.opacity = loading ? '0.6' : ''; }
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
function showAdvPre(suffix, content) {
|
|
1081
|
+
var pre = $('advPre' + suffix);
|
|
1082
|
+
if (!pre) return;
|
|
1083
|
+
pre.textContent = content;
|
|
1084
|
+
show(pre);
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
// Load full result (GET /api/tasks/:taskId) — NOT auto-loaded
|
|
1088
|
+
function loadFullResult(taskId) {
|
|
1089
|
+
setAdvLoading('Result', true);
|
|
1090
|
+
fetch('/api/tasks/' + encodeURIComponent(taskId), { method: 'GET', headers: { 'Accept': 'application/json' }, cache: 'no-store' })
|
|
1158
1091
|
.then(function (res) {
|
|
1159
|
-
if (res.status === 404)
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1092
|
+
if (res.status === 404) throw new Error('NOT_FOUND');
|
|
1093
|
+
if (!res.ok) throw new Error('HTTP ' + res.status);
|
|
1094
|
+
return res.json();
|
|
1095
|
+
})
|
|
1096
|
+
.then(function (data) {
|
|
1097
|
+
// Refresh header metadata that is only available from the full endpoint.
|
|
1098
|
+
if (data && data.status) {
|
|
1099
|
+
var s = data.status;
|
|
1100
|
+
text($('hdrAgent'), s.agent || '—');
|
|
1101
|
+
text($('hdrCreatedAt'), formatTime(s.created_at));
|
|
1102
|
+
text($('hdrUpdatedAt'), formatTime(s.updated_at));
|
|
1103
|
+
text($('hdrTestCommand'), s.test_command || '—');
|
|
1104
|
+
text($('hdrTimeout'), (s.timeout_seconds === null || s.timeout_seconds === undefined) ? '—' : String(s.timeout_seconds));
|
|
1105
|
+
text($('hdrVerifyCommands'), renderListOrJoin(s.verify_commands));
|
|
1106
|
+
text($('hdrPlanId'), s.plan_id || '—');
|
|
1107
|
+
if (s.error) {
|
|
1108
|
+
text($('hdrErrorText'), escapeText(s.error));
|
|
1109
|
+
show($('hdrErrorBlock'));
|
|
1110
|
+
}
|
|
1164
1111
|
}
|
|
1165
|
-
if (
|
|
1166
|
-
|
|
1167
|
-
throw new Error('HTTP ' + res.status + (body ? (': ' + body.slice(0, 200)) : ''));
|
|
1168
|
-
});
|
|
1112
|
+
if (data && (data.stale || data.reconcile)) {
|
|
1113
|
+
renderStaleBanner(data.stale, data.reconcile);
|
|
1169
1114
|
}
|
|
1115
|
+
showAdvPre('Result', JSON.stringify(data, null, 2));
|
|
1116
|
+
})
|
|
1117
|
+
.catch(function (err) {
|
|
1118
|
+
var msg = (err && err.message) === 'NOT_FOUND' ? '任务不存在' : ((err && err.message) || '未知错误');
|
|
1119
|
+
showAdvPre('Result', '⚠ ' + msg);
|
|
1120
|
+
})
|
|
1121
|
+
.finally(function () { setAdvLoading('Result', false); });
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
// Load full test log portion (GET /api/tasks/:taskId, extract test_log)
|
|
1125
|
+
function loadFullTestLog(taskId) {
|
|
1126
|
+
setAdvLoading('TestLog', true);
|
|
1127
|
+
fetch('/api/tasks/' + encodeURIComponent(taskId), { method: 'GET', headers: { 'Accept': 'application/json' }, cache: 'no-store' })
|
|
1128
|
+
.then(function (res) {
|
|
1129
|
+
if (res.status === 404) throw new Error('NOT_FOUND');
|
|
1130
|
+
if (!res.ok) throw new Error('HTTP ' + res.status);
|
|
1170
1131
|
return res.json();
|
|
1171
1132
|
})
|
|
1172
1133
|
.then(function (data) {
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1134
|
+
var testLog = (data && (data.test_log || data.testLog)) || null;
|
|
1135
|
+
showAdvPre('TestLog', testLog ? escapeText(testLog) : '⚠ No test_log available for this task.');
|
|
1136
|
+
})
|
|
1137
|
+
.catch(function (err) {
|
|
1138
|
+
var msg = (err && err.message) === 'NOT_FOUND' ? '任务不存在' : ((err && err.message) || '未知错误');
|
|
1139
|
+
showAdvPre('TestLog', '⚠ ' + msg);
|
|
1140
|
+
})
|
|
1141
|
+
.finally(function () { setAdvLoading('TestLog', false); });
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
// Load full diff portion (GET /api/tasks/:taskId, extract diff_patch/diff)
|
|
1145
|
+
function loadFullDiff(taskId) {
|
|
1146
|
+
setAdvLoading('Diff', true);
|
|
1147
|
+
fetch('/api/tasks/' + encodeURIComponent(taskId), { method: 'GET', headers: { 'Accept': 'application/json' }, cache: 'no-store' })
|
|
1148
|
+
.then(function (res) {
|
|
1149
|
+
if (res.status === 404) throw new Error('NOT_FOUND');
|
|
1150
|
+
if (!res.ok) throw new Error('HTTP ' + res.status);
|
|
1151
|
+
return res.json();
|
|
1152
|
+
})
|
|
1153
|
+
.then(function (data) {
|
|
1154
|
+
var diff = (data && (data.diff_patch || data.diff || data.diffPatch)) || null;
|
|
1155
|
+
showAdvPre('Diff', diff ? escapeText(diff) : '⚠ No diff available for this task.');
|
|
1177
1156
|
})
|
|
1178
1157
|
.catch(function (err) {
|
|
1179
|
-
var msg = (err && err.message) ? err.message
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1158
|
+
var msg = (err && err.message) === 'NOT_FOUND' ? '任务不存在' : ((err && err.message) || '未知错误');
|
|
1159
|
+
showAdvPre('Diff', '⚠ ' + msg);
|
|
1160
|
+
})
|
|
1161
|
+
.finally(function () { setAdvLoading('Diff', false); });
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1164
|
+
// Run audit_task full review (POST /api/tasks/:taskId/audit with control token)
|
|
1165
|
+
function runFullAuditReview(taskId) {
|
|
1166
|
+
setAdvLoading('Audit', true);
|
|
1167
|
+
postAction('/api/tasks/' + encodeURIComponent(taskId) + '/audit')
|
|
1168
|
+
.then(function (res) {
|
|
1169
|
+
return res.json().then(function (data) { return { ok: res.ok, status: res.status, data: data }; });
|
|
1170
|
+
})
|
|
1171
|
+
.then(function (r) {
|
|
1172
|
+
if (!r.ok) {
|
|
1173
|
+
var msg = (r.data && r.data.error) || ('HTTP ' + r.status);
|
|
1174
|
+
showAdvPre('Audit', '⚠ ' + msg);
|
|
1184
1175
|
} else {
|
|
1185
|
-
|
|
1176
|
+
showAdvPre('Audit', JSON.stringify(r.data, null, 2));
|
|
1177
|
+
// Refresh the safe-audit card so conclusions stay in sync.
|
|
1178
|
+
safeGet('/api/tasks/' + encodeURIComponent(taskId) + '/safe-audit')
|
|
1179
|
+
.then(function (res2) { if (res2.data && !res2.data.error) renderSafeAudit(res2.data); });
|
|
1186
1180
|
}
|
|
1187
|
-
})
|
|
1181
|
+
})
|
|
1182
|
+
.catch(function (err) {
|
|
1183
|
+
showAdvPre('Audit', '⚠ ' + ((err && err.message) || '未知错误'));
|
|
1184
|
+
})
|
|
1185
|
+
.finally(function () { setAdvLoading('Audit', false); });
|
|
1188
1186
|
}
|
|
1189
1187
|
|
|
1190
1188
|
// ---- Entry point ----
|
|
@@ -1193,13 +1191,13 @@ html.dark {
|
|
|
1193
1191
|
if (!taskId) {
|
|
1194
1192
|
showError('缺少 task id', '请在 URL 中提供 ?id=<task_id>');
|
|
1195
1193
|
} else {
|
|
1196
|
-
|
|
1194
|
+
loadSafeSummary(taskId);
|
|
1197
1195
|
}
|
|
1198
1196
|
|
|
1199
1197
|
// ---- Action button wiring ----
|
|
1200
1198
|
var btnRefresh = $('btnRefresh');
|
|
1201
1199
|
if (btnRefresh) btnRefresh.addEventListener('click', function () {
|
|
1202
|
-
if (taskId)
|
|
1200
|
+
if (taskId) loadSafeSummary(taskId);
|
|
1203
1201
|
});
|
|
1204
1202
|
var btnCopyId = $('btnCopyId');
|
|
1205
1203
|
if (btnCopyId) btnCopyId.addEventListener('click', function () {
|
|
@@ -1221,6 +1219,26 @@ html.dark {
|
|
|
1221
1219
|
if (!confirm('确认 reconcile 该任务?将写入 reconcile 记录(不会删除任务)。')) return;
|
|
1222
1220
|
runReconcile(taskId);
|
|
1223
1221
|
});
|
|
1222
|
+
|
|
1223
|
+
// ---- Advanced section on-demand button wiring ----
|
|
1224
|
+
var advBtnResult = $('advBtnResult');
|
|
1225
|
+
if (advBtnResult) advBtnResult.addEventListener('click', function () {
|
|
1226
|
+
if (taskId) loadFullResult(taskId);
|
|
1227
|
+
});
|
|
1228
|
+
var advBtnTestLog = $('advBtnTestLog');
|
|
1229
|
+
if (advBtnTestLog) advBtnTestLog.addEventListener('click', function () {
|
|
1230
|
+
if (taskId) loadFullTestLog(taskId);
|
|
1231
|
+
});
|
|
1232
|
+
var advBtnDiff = $('advBtnDiff');
|
|
1233
|
+
if (advBtnDiff) advBtnDiff.addEventListener('click', function () {
|
|
1234
|
+
if (taskId) loadFullDiff(taskId);
|
|
1235
|
+
});
|
|
1236
|
+
var advBtnAudit = $('advBtnAudit');
|
|
1237
|
+
if (advBtnAudit) advBtnAudit.addEventListener('click', function () {
|
|
1238
|
+
if (!taskId) return;
|
|
1239
|
+
if (!confirm('确认运行 audit_task 完整审计?')) return;
|
|
1240
|
+
runFullAuditReview(taskId);
|
|
1241
|
+
});
|
|
1224
1242
|
})();
|
|
1225
1243
|
</script>
|
|
1226
1244
|
|