gator-command 1.1.9__tar.gz → 1.2.1__tar.gz
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.
- {gator_command-1.1.9/src/gator_command.egg-info → gator_command-1.2.1}/PKG-INFO +1 -1
- {gator_command-1.1.9 → gator_command-1.2.1}/pyproject.toml +1 -1
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/scripts/dashboard/dashboard.css +168 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/scripts/dashboard/dashboard.html +1 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/scripts/dashboard/dashboard.js +13 -8
- gator_command-1.2.1/src/gator_command/scripts/dashboard/views/audit.js +414 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/scripts/dashboard/views/repo.js +154 -2
- gator_command-1.2.1/src/gator_command/scripts/gator-audit-renderers.py +361 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/scripts/gator-audit.py +115 -333
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/scripts/gator-dashboard.py +201 -2
- gator_command-1.2.1/src/gator_command/scripts/gator-session-aggregator.py +429 -0
- {gator_command-1.1.9 → gator_command-1.2.1/src/gator_command.egg-info}/PKG-INFO +1 -1
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command.egg-info/SOURCES.txt +4 -0
- gator_command-1.2.1/tests/test_audit.py +415 -0
- gator_command-1.2.1/tests/test_dashboard_sessions.py +261 -0
- gator_command-1.2.1/tests/test_session_aggregator.py +397 -0
- gator_command-1.1.9/src/gator_command/scripts/dashboard/views/audit.js +0 -270
- gator_command-1.1.9/tests/test_audit.py +0 -116
- {gator_command-1.1.9 → gator_command-1.2.1}/LICENSE +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/PYPI_README.md +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/README.md +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/setup.cfg +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/__init__.py +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/cli.py +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/scripts/crawler.py +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/scripts/dashboard/views/fleet.js +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/scripts/dashboard/views/settings.js +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/scripts/dashboard/views/updates.js +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/scripts/enforcer-prompt.md +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/scripts/extract-claude-sessions.py +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/scripts/extract-codex-sessions.py +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/scripts/extract-gemini-sessions.py +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/scripts/gator-charter-draft.py +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/scripts/gator-charter-lint.py +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/scripts/gator-charter-verify.py +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/scripts/gator-deploy.py +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/scripts/gator-drift.py +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/scripts/gator-enforce.py +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/scripts/gator-fleet-intel.py +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/scripts/gator-fleet-report.py +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/scripts/gator-init-command-post.py +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/scripts/gator-init.py +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/scripts/gator-machine-id.py +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/scripts/gator-policy-status.py +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/scripts/gator-pulse.py +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/scripts/gator-repo-status.py +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/scripts/gator-session-common.py +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/scripts/gator-session-sink.py +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/scripts/gator-sessions.py +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/scripts/gator-update.py +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/scripts/gator-version.py +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/scripts/gator_core.py +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/scripts/gator_remote.py +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/scripts/gator_runtime.py +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/scripts/gatorize-actions.sh +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/scripts/gatorize-lib.sh +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/scripts/gatorize-post.sh +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/scripts/gatorize.py +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/scripts/gatorize.sh +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/scripts/generate_markdown.py +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/scripts/generate_wiki.py +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/scripts/graph_health.py +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/scripts/memex-lint.py +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/scripts/memex-lint.sh +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/scripts/memex.py +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/scripts/memex_formatters.py +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/scripts/memex_state.py +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/scripts/spawn.py +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/templates/gator-starter/blueprints/README.md +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/templates/gator-starter/charterignore +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/templates/gator-starter/charters/README.md +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/templates/gator-starter/charters/_template.md +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/templates/gator-starter/commands/commit.md +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/templates/gator-starter/commands/init.md +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/templates/gator-starter/commands/update.md +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/templates/gator-starter/constitution.md +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/templates/gator-starter/field-guides/README.md +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/templates/gator-starter/gator-start-up.md +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/templates/gator-starter/procedures/charter-alignment.md +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/templates/gator-starter/procedures/enforcer-review.md +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/templates/gator-starter/procedures/field-guide-generation.md +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/templates/gator-starter/procedures/knowledge-capture.md +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/templates/gator-starter/procedures/significance-check.md +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/templates/gator-starter/reference-notes/concierge-responses.md +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/templates/gator-starter/reference-notes/dangerous-patterns.md +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/templates/gator-starter/reference-notes/dashboard-operations.md +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/templates/gator-starter/reference-notes/enforcer-configuration.md +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/templates/gator-starter/reference-notes/example-project.md +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/templates/gator-starter/reference-notes/failure-modes-and-self-correction.md +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/templates/gator-starter/reference-notes/git-workflow.md +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/templates/gator-starter/reference-notes/identity-and-ownership.md +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/templates/gator-starter/reference-notes/refactor-approach.md +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/templates/gator-starter/reference-notes/what-gator-requires-from-a-model.md +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/templates/gator-starter/reference-notes/why-navigation-coding-feels-different.md +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/templates/gator-starter/reference-notes/workflow-profiles.md +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/templates/gator-starter/scripts/__pycache__/enforcer-review.cpython-313.pyc +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/templates/gator-starter/scripts/__pycache__/gator-approve.cpython-313.pyc +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/templates/gator-starter/scripts/__pycache__/gator-init.cpython-313.pyc +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/templates/gator-starter/scripts/__pycache__/gator-pre-commit.cpython-313.pyc +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/templates/gator-starter/scripts/__pycache__/gator-update.cpython-313.pyc +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/templates/gator-starter/scripts/enforcer-prompt.md +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/templates/gator-starter/scripts/enforcer-review.py +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/templates/gator-starter/scripts/gator-approve.py +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/templates/gator-starter/scripts/gator-enforce.py +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/templates/gator-starter/scripts/gator-init.py +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/templates/gator-starter/scripts/gator-pre-commit.py +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/templates/gator-starter/scripts/gator-pulse.py +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/templates/gator-starter/scripts/gator-update.py +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/templates/gator-starter/scripts/gator-version.py +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/templates/gator-starter/scripts/gator_core.py +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/templates/gator-starter/scripts/hooks/__pycache__/commit-msgcpython-313.pyc +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/templates/gator-starter/scripts/hooks/__pycache__/post-commitcpython-313.pyc +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/templates/gator-starter/scripts/hooks/__pycache__/pre-commitcpython-313.pyc +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/templates/gator-starter/scripts/hooks/commit-msg +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/templates/gator-starter/scripts/hooks/post-commit +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/templates/gator-starter/scripts/hooks/pre-commit +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/templates/gator-starter/sessions/.gitignore +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/templates/gator-starter/vault/.gitkeep +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/templates/gator-starter/whiteboard.md +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command.egg-info/dependency_links.txt +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command.egg-info/entry_points.txt +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command.egg-info/top_level.txt +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/tests/test_audit_integration.py +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/tests/test_charter_lint.py +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/tests/test_deploy.py +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/tests/test_drift.py +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/tests/test_fleet_intel.py +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/tests/test_fleet_report.py +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/tests/test_gator_core.py +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/tests/test_gatorize.py +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/tests/test_hooks.py +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/tests/test_init.py +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/tests/test_memex_state.py +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/tests/test_packaging.py +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/tests/test_pre_commit.py +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/tests/test_remote.py +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/tests/test_session_sink.py +0 -0
- {gator_command-1.1.9 → gator_command-1.2.1}/tests/test_sessions.py +0 -0
{gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/scripts/dashboard/dashboard.css
RENAMED
|
@@ -360,6 +360,91 @@ html, body {
|
|
|
360
360
|
background: #f8f9fb;
|
|
361
361
|
}
|
|
362
362
|
|
|
363
|
+
/* ── session table ─────────────────────────────────────────────────────── */
|
|
364
|
+
|
|
365
|
+
.session-row:hover td {
|
|
366
|
+
background: #eef3fa;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
.session-row-expanded td {
|
|
370
|
+
background: #f0f4fa;
|
|
371
|
+
border-bottom-color: transparent;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
.session-detail td {
|
|
375
|
+
padding: 0;
|
|
376
|
+
background: #fafbfd;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
.session-detail-content {
|
|
380
|
+
padding: 10px 16px 14px;
|
|
381
|
+
border-left: 3px solid #d0d8e4;
|
|
382
|
+
margin: 0 8px 8px;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
.detail-section {
|
|
386
|
+
margin-bottom: 8px;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
.commit-entry {
|
|
390
|
+
font-size: 12px;
|
|
391
|
+
margin: 2px 0;
|
|
392
|
+
color: #333;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
.commit-type {
|
|
396
|
+
display: inline-block;
|
|
397
|
+
font-size: 10px;
|
|
398
|
+
padding: 1px 5px;
|
|
399
|
+
border-radius: 3px;
|
|
400
|
+
background: #eee;
|
|
401
|
+
color: #555;
|
|
402
|
+
margin: 0 4px;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
.tag-badge {
|
|
406
|
+
display: inline-block;
|
|
407
|
+
font-size: 10px;
|
|
408
|
+
padding: 1px 6px;
|
|
409
|
+
border-radius: 8px;
|
|
410
|
+
background: #e8eef5;
|
|
411
|
+
color: #3a5a8a;
|
|
412
|
+
margin: 1px 2px;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
.status-complete {
|
|
416
|
+
color: #4a7a4a;
|
|
417
|
+
font-size: 11px;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
.status-inflight {
|
|
421
|
+
color: #b8860b;
|
|
422
|
+
font-size: 11px;
|
|
423
|
+
font-weight: 600;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
.fleet-toggle-btn {
|
|
427
|
+
border: 1px solid #ccc;
|
|
428
|
+
background: #f5f5f5;
|
|
429
|
+
color: #555;
|
|
430
|
+
padding: 2px 10px;
|
|
431
|
+
border-radius: 4px;
|
|
432
|
+
font-size: 11px;
|
|
433
|
+
cursor: pointer;
|
|
434
|
+
margin-left: 10px;
|
|
435
|
+
vertical-align: middle;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
.fleet-toggle-btn:hover {
|
|
439
|
+
background: #e8e8e8;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
.fleet-toggle-btn.active {
|
|
443
|
+
background: #3a5a8a;
|
|
444
|
+
color: #fff;
|
|
445
|
+
border-color: #3a5a8a;
|
|
446
|
+
}
|
|
447
|
+
|
|
363
448
|
/* ── health badges ─────────────────────────────────────────────────────── */
|
|
364
449
|
|
|
365
450
|
.badge {
|
|
@@ -930,6 +1015,89 @@ a:hover, .link-btn:hover {
|
|
|
930
1015
|
color: #0366d6;
|
|
931
1016
|
}
|
|
932
1017
|
|
|
1018
|
+
/* ── search ────────────────────────────────────────────────────────────── */
|
|
1019
|
+
|
|
1020
|
+
.topbar-search {
|
|
1021
|
+
border: 1px solid #ccc;
|
|
1022
|
+
border-radius: 4px;
|
|
1023
|
+
padding: 3px 8px;
|
|
1024
|
+
font-size: 12px;
|
|
1025
|
+
width: 180px;
|
|
1026
|
+
margin-right: 10px;
|
|
1027
|
+
outline: none;
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
.topbar-search:focus {
|
|
1031
|
+
border-color: #0366d6;
|
|
1032
|
+
box-shadow: 0 0 0 2px rgba(3,102,214,0.15);
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
.in-doc-search {
|
|
1036
|
+
border: 1px solid #ccc;
|
|
1037
|
+
border-radius: 4px;
|
|
1038
|
+
padding: 2px 6px;
|
|
1039
|
+
font-size: 11px;
|
|
1040
|
+
width: 130px;
|
|
1041
|
+
margin-right: 6px;
|
|
1042
|
+
outline: none;
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
.in-doc-search:focus {
|
|
1046
|
+
border-color: #0366d6;
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
.file-header-right {
|
|
1050
|
+
display: flex;
|
|
1051
|
+
align-items: center;
|
|
1052
|
+
gap: 4px;
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
.copy-content-btn {
|
|
1056
|
+
border: none;
|
|
1057
|
+
background: none;
|
|
1058
|
+
cursor: pointer;
|
|
1059
|
+
font-size: 13px;
|
|
1060
|
+
color: #aaa;
|
|
1061
|
+
padding: 0 4px;
|
|
1062
|
+
vertical-align: middle;
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
.copy-content-btn:hover {
|
|
1066
|
+
color: #0366d6;
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
mark.search-hit {
|
|
1070
|
+
background: #fff3b0;
|
|
1071
|
+
padding: 0 1px;
|
|
1072
|
+
border-radius: 2px;
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
.search-result {
|
|
1076
|
+
padding: 8px 12px;
|
|
1077
|
+
margin-bottom: 6px;
|
|
1078
|
+
border: 1px solid #e8e8e8;
|
|
1079
|
+
border-radius: 4px;
|
|
1080
|
+
background: #fafbfd;
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
.search-result:hover {
|
|
1084
|
+
background: #f0f4fa;
|
|
1085
|
+
border-color: #d0d8e4;
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
.search-result-title {
|
|
1089
|
+
font-size: 13px;
|
|
1090
|
+
font-weight: 600;
|
|
1091
|
+
color: #0366d6;
|
|
1092
|
+
margin-bottom: 3px;
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
.search-result-snippet {
|
|
1096
|
+
font-size: 12px;
|
|
1097
|
+
color: #555;
|
|
1098
|
+
line-height: 1.4;
|
|
1099
|
+
}
|
|
1100
|
+
|
|
933
1101
|
/* ── Markdown rendering ────────────────────────────────────────────────── */
|
|
934
1102
|
|
|
935
1103
|
.repo-markdown h1 { font-size: 22px; font-weight: 600; margin: 20px 0 12px; color: #111; }
|
{gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/scripts/dashboard/dashboard.html
RENAMED
|
@@ -55,6 +55,7 @@
|
|
|
55
55
|
<span id="topbar-subtitle"></span>
|
|
56
56
|
</div>
|
|
57
57
|
<div id="topbar-right">
|
|
58
|
+
<input type="text" id="repo-search-input" class="topbar-search" placeholder="Search... (AND / OR)" style="display:none" />
|
|
58
59
|
<span id="generated-at"></span>
|
|
59
60
|
<button id="refresh-btn" title="Re-run fleet scan">↺ Refresh</button>
|
|
60
61
|
</div>
|
{gator_command-1.1.9 → gator_command-1.2.1}/src/gator_command/scripts/dashboard/dashboard.js
RENAMED
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
data: null, // fast_data from server
|
|
21
21
|
activeView: "fleet",
|
|
22
22
|
activeRepo: null, // name of currently loaded repo (Repo view)
|
|
23
|
+
activeRepoKey: null, // path-hash key for session audit identity
|
|
23
24
|
settings: null, // dashboard settings (mode, etc.)
|
|
24
25
|
standalone: false, // true when no command post detected
|
|
25
26
|
};
|
|
@@ -93,6 +94,13 @@
|
|
|
93
94
|
btn.classList.toggle("active", btn.dataset.view === name);
|
|
94
95
|
});
|
|
95
96
|
|
|
97
|
+
// Show/hide repo search input
|
|
98
|
+
const repoSearchInput = document.getElementById("repo-search-input");
|
|
99
|
+
if (repoSearchInput) {
|
|
100
|
+
repoSearchInput.style.display = (name === "repo") ? "" : "none";
|
|
101
|
+
if (name !== "repo") repoSearchInput.value = "";
|
|
102
|
+
}
|
|
103
|
+
|
|
96
104
|
// Clear slot
|
|
97
105
|
viewSlot.innerHTML = "";
|
|
98
106
|
|
|
@@ -115,7 +123,7 @@
|
|
|
115
123
|
if (name === "audit") {
|
|
116
124
|
updateTopbar("audit", "Governance posture");
|
|
117
125
|
if (views.audit) {
|
|
118
|
-
views.audit(state.data, viewSlot);
|
|
126
|
+
views.audit(state.data, viewSlot, state.activeRepoKey);
|
|
119
127
|
} else {
|
|
120
128
|
viewSlot.innerHTML = "<p class='muted'>Audit view not available.</p>";
|
|
121
129
|
}
|
|
@@ -130,6 +138,10 @@
|
|
|
130
138
|
return;
|
|
131
139
|
}
|
|
132
140
|
state.activeRepo = repoName;
|
|
141
|
+
// Resolve repo_key from fleet data
|
|
142
|
+
const allRepos = (state.data && state.data.fleet && state.data.fleet.repos) || (state.data && state.data.repos) || [];
|
|
143
|
+
const matchedRepo = allRepos.find(r => r.name === repoName);
|
|
144
|
+
state.activeRepoKey = (matchedRepo && matchedRepo.repo_key) || null;
|
|
133
145
|
// Update repo sidebar label
|
|
134
146
|
repoTab.textContent = "";
|
|
135
147
|
const icon = document.createElement("span");
|
|
@@ -405,13 +417,6 @@
|
|
|
405
417
|
|
|
406
418
|
state.standalone = !!(state.data && state.data.standalone);
|
|
407
419
|
|
|
408
|
-
// Hide Audit tab in standalone mode (no data model without command post)
|
|
409
|
-
if (state.standalone) {
|
|
410
|
-
document.querySelectorAll('.sidebar-item[data-view="audit"]').forEach(el => {
|
|
411
|
-
el.style.display = "none";
|
|
412
|
-
});
|
|
413
|
-
}
|
|
414
|
-
|
|
415
420
|
// Check for ?repo= query param
|
|
416
421
|
const params = new URLSearchParams(window.location.search);
|
|
417
422
|
const preloadRepo = params.get("repo");
|
|
@@ -0,0 +1,414 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* audit.js — Audit view for Gator Dashboard.
|
|
3
|
+
*
|
|
4
|
+
* Data source: data.audit (gator-audit --json) for governance metrics,
|
|
5
|
+
* GET /api/audit/sessions for session summaries (lazy).
|
|
6
|
+
*
|
|
7
|
+
* Primary question: can I show human oversight? where are the gaps?
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
(function () {
|
|
11
|
+
"use strict";
|
|
12
|
+
|
|
13
|
+
window.GatorViews = window.GatorViews || {};
|
|
14
|
+
|
|
15
|
+
function escHtml(str) {
|
|
16
|
+
return String(str)
|
|
17
|
+
.replace(/&/g, "&")
|
|
18
|
+
.replace(/</g, "<")
|
|
19
|
+
.replace(/>/g, ">")
|
|
20
|
+
.replace(/"/g, """);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function fmtTs(ts) {
|
|
24
|
+
if (!ts) return "—";
|
|
25
|
+
try {
|
|
26
|
+
return new Date(ts).toLocaleDateString(undefined, {
|
|
27
|
+
year: "numeric", month: "short", day: "numeric",
|
|
28
|
+
});
|
|
29
|
+
} catch (_) { return ts; }
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function fmtDateRange(startedAt, endedAt) {
|
|
33
|
+
if (!startedAt) return "—";
|
|
34
|
+
try {
|
|
35
|
+
const s = new Date(startedAt);
|
|
36
|
+
const e = endedAt ? new Date(endedAt) : null;
|
|
37
|
+
const datePart = s.toLocaleDateString(undefined, { month: "short", day: "numeric" });
|
|
38
|
+
const startTime = s.toLocaleTimeString(undefined, { hour: "2-digit", minute: "2-digit", hour12: false });
|
|
39
|
+
const endTime = e ? e.toLocaleTimeString(undefined, { hour: "2-digit", minute: "2-digit", hour12: false }) : "";
|
|
40
|
+
return endTime ? `${datePart} ${startTime}\u2013${endTime}` : `${datePart} ${startTime}`;
|
|
41
|
+
} catch (_) { return startedAt.slice(0, 16); }
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function sessionStatus(endedAt) {
|
|
45
|
+
if (!endedAt) return "in-flight";
|
|
46
|
+
try {
|
|
47
|
+
const ago = Date.now() - new Date(endedAt).getTime();
|
|
48
|
+
return ago > 2 * 60 * 60 * 1000 ? "complete" : "in-flight";
|
|
49
|
+
} catch (_) { return "?"; }
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// ── significance bar chart ─────────────────────────────────────────────────
|
|
53
|
+
|
|
54
|
+
function renderSigBars(dist) {
|
|
55
|
+
const order = ["critical", "high", "medium", "low", "routine"];
|
|
56
|
+
const colors = {
|
|
57
|
+
critical: "#a02020",
|
|
58
|
+
high: "#c44020",
|
|
59
|
+
medium: "#b8860b",
|
|
60
|
+
low: "#4a7a4a",
|
|
61
|
+
routine: "#888",
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const total = Object.values(dist).reduce((s, v) => s + v, 0);
|
|
65
|
+
if (total === 0) return "<p class='muted'>No governed commits found in this period.</p>";
|
|
66
|
+
|
|
67
|
+
let html = "";
|
|
68
|
+
for (const level of order) {
|
|
69
|
+
const count = dist[level] || 0;
|
|
70
|
+
if (count === 0) continue;
|
|
71
|
+
const pct = Math.round((count / total) * 100);
|
|
72
|
+
html += `
|
|
73
|
+
<div class="sig-bar">
|
|
74
|
+
<span class="sig-bar-label">${escHtml(level)}</span>
|
|
75
|
+
<div class="sig-bar-track">
|
|
76
|
+
<div class="sig-bar-fill" style="width:${pct}%;background:${colors[level] || '#888'}"></div>
|
|
77
|
+
</div>
|
|
78
|
+
<span class="sig-bar-count">${count}</span>
|
|
79
|
+
</div>
|
|
80
|
+
`;
|
|
81
|
+
}
|
|
82
|
+
return html;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// ── governed commits table ─────────────────────────────────────────────────
|
|
86
|
+
|
|
87
|
+
function renderGoverned(govCommits) {
|
|
88
|
+
const entries = Object.entries(govCommits).filter(([k]) => k !== "total");
|
|
89
|
+
if (entries.length === 0) return "<p class='muted'>No data.</p>";
|
|
90
|
+
|
|
91
|
+
entries.sort((a, b) => b[1] - a[1]);
|
|
92
|
+
|
|
93
|
+
let html = `
|
|
94
|
+
<table class="data-table">
|
|
95
|
+
<thead><tr><th>Repo</th><th>Governed commits (7d)</th></tr></thead>
|
|
96
|
+
<tbody>
|
|
97
|
+
`;
|
|
98
|
+
for (const [repo, count] of entries) {
|
|
99
|
+
html += `<tr><td>${escHtml(repo)}</td><td class="mono">${count}</td></tr>`;
|
|
100
|
+
}
|
|
101
|
+
html += "</tbody></table>";
|
|
102
|
+
return html;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// ── override events table ──────────────────────────────────────────────────
|
|
106
|
+
|
|
107
|
+
function renderOverrides(events) {
|
|
108
|
+
if (!events || events.length === 0) {
|
|
109
|
+
return "<p class='muted'>No override events found.</p>";
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
let html = `
|
|
113
|
+
<table class="data-table">
|
|
114
|
+
<thead>
|
|
115
|
+
<tr>
|
|
116
|
+
<th>Repo</th>
|
|
117
|
+
<th>Commit</th>
|
|
118
|
+
<th>Date</th>
|
|
119
|
+
<th>Type</th>
|
|
120
|
+
<th>Approver</th>
|
|
121
|
+
</tr>
|
|
122
|
+
</thead>
|
|
123
|
+
<tbody>
|
|
124
|
+
`;
|
|
125
|
+
for (const ev of events) {
|
|
126
|
+
html += `
|
|
127
|
+
<tr>
|
|
128
|
+
<td>${escHtml(ev.repo || "—")}</td>
|
|
129
|
+
<td class="mono">${escHtml((ev.hash || "").slice(0, 7))}</td>
|
|
130
|
+
<td class="nowrap">${fmtTs(ev.timestamp)}</td>
|
|
131
|
+
<td>${escHtml(ev.override_type || "—")}</td>
|
|
132
|
+
<td>${escHtml(ev.approver || "—")}</td>
|
|
133
|
+
</tr>
|
|
134
|
+
`;
|
|
135
|
+
}
|
|
136
|
+
html += "</tbody></table>";
|
|
137
|
+
return html;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// ── session summary table (from /api/audit/sessions) ───────────────────────
|
|
141
|
+
|
|
142
|
+
function renderSessionTable(summaries, fleet) {
|
|
143
|
+
if (!summaries || summaries.length === 0) {
|
|
144
|
+
return "<p class='muted'>No session summaries available. Sessions are generated from committed snippets.</p>";
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
let html = `
|
|
148
|
+
<table class="data-table session-table">
|
|
149
|
+
<thead>
|
|
150
|
+
<tr>
|
|
151
|
+
<th>Session</th>
|
|
152
|
+
${fleet ? '<th>Repo</th>' : ''}
|
|
153
|
+
<th>Model</th>
|
|
154
|
+
<th>Commits</th>
|
|
155
|
+
<th>Goal</th>
|
|
156
|
+
<th>Tags</th>
|
|
157
|
+
<th>Status</th>
|
|
158
|
+
</tr>
|
|
159
|
+
</thead>
|
|
160
|
+
<tbody>
|
|
161
|
+
`;
|
|
162
|
+
|
|
163
|
+
for (const s of summaries) {
|
|
164
|
+
const dateRange = fmtDateRange(s.started_at, s.ended_at);
|
|
165
|
+
const goal = s.goal || "";
|
|
166
|
+
const goalDisplay = goal.length > 50 ? goal.slice(0, 50) + "…" : goal;
|
|
167
|
+
const tags = (s.decision_tags || []).slice(0, 4);
|
|
168
|
+
const tagBadges = tags.map(t => `<span class="tag-badge">${escHtml(t)}</span>`).join(" ");
|
|
169
|
+
const extraTags = (s.decision_tags || []).length > 4 ? ` <span class="muted">+${s.decision_tags.length - 4}</span>` : "";
|
|
170
|
+
const status = sessionStatus(s.ended_at);
|
|
171
|
+
const statusClass = status === "complete" ? "status-complete" : "status-inflight";
|
|
172
|
+
const rowKey = escHtml((s.repo_key || "") + "-" + (s.session_id || ""));
|
|
173
|
+
|
|
174
|
+
html += `
|
|
175
|
+
<tr class="session-row" data-row-key="${rowKey}">
|
|
176
|
+
<td class="nowrap">${escHtml(dateRange)}</td>
|
|
177
|
+
${fleet ? `<td>${escHtml(s.repo || "—")}</td>` : ''}
|
|
178
|
+
<td>${escHtml(s.model || "—")}</td>
|
|
179
|
+
<td class="mono">${s.commit_count || 0}</td>
|
|
180
|
+
<td title="${escHtml(goal)}">${escHtml(goalDisplay || "—")}</td>
|
|
181
|
+
<td>${tagBadges}${extraTags}</td>
|
|
182
|
+
<td><span class="${statusClass}">${status}</span></td>
|
|
183
|
+
</tr>
|
|
184
|
+
<tr class="session-detail" id="detail-${rowKey}" style="display:none">
|
|
185
|
+
<td colspan="${fleet ? 7 : 6}">
|
|
186
|
+
<div class="session-detail-content">
|
|
187
|
+
${renderSessionDetail(s)}
|
|
188
|
+
</div>
|
|
189
|
+
</td>
|
|
190
|
+
</tr>
|
|
191
|
+
`;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
html += "</tbody></table>";
|
|
195
|
+
return html;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function renderSessionDetail(s) {
|
|
199
|
+
let html = "";
|
|
200
|
+
|
|
201
|
+
// Commits
|
|
202
|
+
const commits = s.commits || [];
|
|
203
|
+
if (commits.length > 0) {
|
|
204
|
+
html += '<div class="detail-section"><strong>Commits</strong><div class="commit-list">';
|
|
205
|
+
for (const c of commits) {
|
|
206
|
+
html += `<div class="commit-entry"><span class="mono">${escHtml(c.short_commit || "?")}</span> <span class="commit-type">${escHtml(c.change_type || "")}</span> ${escHtml(c.intent || "")}</div>`;
|
|
207
|
+
}
|
|
208
|
+
html += "</div></div>";
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Files touched
|
|
212
|
+
const files = s.files_touched || [];
|
|
213
|
+
if (files.length > 0) {
|
|
214
|
+
html += '<div class="detail-section"><strong>Files touched</strong> <span class="muted">(' + files.length + ')</span><div class="file-list">';
|
|
215
|
+
const displayFiles = files.slice(0, 20);
|
|
216
|
+
for (const f of displayFiles) {
|
|
217
|
+
html += `<div class="mono" style="font-size:11px;color:#555">${escHtml(f)}</div>`;
|
|
218
|
+
}
|
|
219
|
+
if (files.length > 20) {
|
|
220
|
+
html += `<div class="muted">… and ${files.length - 20} more</div>`;
|
|
221
|
+
}
|
|
222
|
+
html += "</div></div>";
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Notes
|
|
226
|
+
const notes = s.notes || [];
|
|
227
|
+
if (notes.length > 0) {
|
|
228
|
+
html += '<div class="detail-section"><strong>Notes</strong><div class="notes-list">';
|
|
229
|
+
for (const n of notes) {
|
|
230
|
+
html += `<div style="font-size:12px;color:#444;margin:2px 0">• ${escHtml(n)}</div>`;
|
|
231
|
+
}
|
|
232
|
+
html += "</div></div>";
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// Transcript link
|
|
236
|
+
if (s.transcript_ref) {
|
|
237
|
+
html += `<div class="detail-section"><strong>Transcript</strong> <span class="mono muted">${escHtml(s.transcript_ref)}</span></div>`;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
if (!html) {
|
|
241
|
+
html = '<span class="muted">No additional details.</span>';
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
return html;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// ── main render ─────────────────────────────────────────────────────────────
|
|
248
|
+
|
|
249
|
+
window.GatorViews.audit = function (data, container, activeRepoKey) {
|
|
250
|
+
const audit = data.audit || {};
|
|
251
|
+
|
|
252
|
+
if (audit.error) {
|
|
253
|
+
container.innerHTML = `<div class="error-block">Audit data unavailable: ${escHtml(audit.error)}</div>`;
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
const governance = audit.governance || {};
|
|
258
|
+
const sessions = audit.sessions || {};
|
|
259
|
+
const overrides = audit.override_events || [];
|
|
260
|
+
const sigDist = audit.significance_distribution || {};
|
|
261
|
+
const govCommits = audit.governed_commits || {};
|
|
262
|
+
const sinceDays = audit.since_days || 7;
|
|
263
|
+
|
|
264
|
+
// Compute summary metrics
|
|
265
|
+
const totalRepos = governance.repos || 0;
|
|
266
|
+
const hookCoverage = totalRepos > 0
|
|
267
|
+
? Math.round((governance.hooks_installed / totalRepos) * 100)
|
|
268
|
+
: 0;
|
|
269
|
+
const overrideRate = (govCommits.total || 0) > 0
|
|
270
|
+
? Math.round((overrides.length / govCommits.total) * 100)
|
|
271
|
+
: 0;
|
|
272
|
+
|
|
273
|
+
// Session summary coverage: repos with recent sessions / total repos
|
|
274
|
+
const reposWithSessions = Object.keys(sessions.by_repo || {}).length;
|
|
275
|
+
const sessionCoverage = totalRepos > 0
|
|
276
|
+
? Math.round((reposWithSessions / totalRepos) * 100)
|
|
277
|
+
: 0;
|
|
278
|
+
|
|
279
|
+
let html = `
|
|
280
|
+
<div class="view-header">
|
|
281
|
+
<span class="view-title">Audit</span>
|
|
282
|
+
<span class="view-subtitle">Last ${sinceDays} days · ${totalRepos} repos</span>
|
|
283
|
+
</div>
|
|
284
|
+
|
|
285
|
+
<div class="card-row">
|
|
286
|
+
<div class="card">
|
|
287
|
+
<div class="card-label">Hook coverage</div>
|
|
288
|
+
<div class="card-value">${hookCoverage}%</div>
|
|
289
|
+
<div class="card-note">${governance.hooks_installed || 0} of ${totalRepos} repos</div>
|
|
290
|
+
</div>
|
|
291
|
+
<div class="card">
|
|
292
|
+
<div class="card-label">Governed commits</div>
|
|
293
|
+
<div class="card-value">${govCommits.total || 0}</div>
|
|
294
|
+
<div class="card-note">fleet total, ${sinceDays}d window</div>
|
|
295
|
+
</div>
|
|
296
|
+
<div class="card">
|
|
297
|
+
<div class="card-label">Override events</div>
|
|
298
|
+
<div class="card-value" style="color:${overrides.length > 0 ? 'var(--color-drifted)' : 'inherit'}">${overrides.length}</div>
|
|
299
|
+
<div class="card-note">${overrideRate}% of governed commits</div>
|
|
300
|
+
</div>
|
|
301
|
+
<div class="card">
|
|
302
|
+
<div class="card-label">Session coverage</div>
|
|
303
|
+
<div class="card-value">${sessionCoverage}%</div>
|
|
304
|
+
<div class="card-note">${reposWithSessions} repos with sessions (${sinceDays}d)</div>
|
|
305
|
+
</div>
|
|
306
|
+
</div>
|
|
307
|
+
|
|
308
|
+
<div class="section">
|
|
309
|
+
<div class="section-title">Significance distribution (fleet)</div>
|
|
310
|
+
${renderSigBars(sigDist)}
|
|
311
|
+
</div>
|
|
312
|
+
|
|
313
|
+
<div class="section">
|
|
314
|
+
<div class="section-title">Governed commits by repo (${sinceDays}d)</div>
|
|
315
|
+
${renderGoverned(govCommits)}
|
|
316
|
+
</div>
|
|
317
|
+
|
|
318
|
+
<div class="section">
|
|
319
|
+
<div class="section-title">Override events</div>
|
|
320
|
+
${renderOverrides(overrides)}
|
|
321
|
+
</div>
|
|
322
|
+
|
|
323
|
+
<div class="section">
|
|
324
|
+
<div class="section-title">
|
|
325
|
+
Session summaries
|
|
326
|
+
<button class="fleet-toggle-btn" id="session-fleet-toggle" title="Toggle fleet view">Fleet</button>
|
|
327
|
+
</div>
|
|
328
|
+
<div id="session-table-container">
|
|
329
|
+
<p class="muted" style="padding:12px">Loading sessions...</p>
|
|
330
|
+
</div>
|
|
331
|
+
</div>
|
|
332
|
+
`;
|
|
333
|
+
|
|
334
|
+
// Sessions by vendor
|
|
335
|
+
if (sessions.by_vendor && Object.keys(sessions.by_vendor).length > 0) {
|
|
336
|
+
html += `
|
|
337
|
+
<div class="section">
|
|
338
|
+
<div class="section-title">Sessions by agent (${sinceDays}d)</div>
|
|
339
|
+
<table class="data-table">
|
|
340
|
+
<thead><tr><th>Agent</th><th>Sessions</th></tr></thead>
|
|
341
|
+
<tbody>
|
|
342
|
+
`;
|
|
343
|
+
for (const [vendor, count] of Object.entries(sessions.by_vendor)) {
|
|
344
|
+
html += `<tr><td>${escHtml(vendor)}</td><td class="mono">${count}</td></tr>`;
|
|
345
|
+
}
|
|
346
|
+
html += "</tbody></table></div>";
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
container.innerHTML = html;
|
|
350
|
+
|
|
351
|
+
// Load session summaries lazily from /api/audit/sessions
|
|
352
|
+
// If no repo selected, start in fleet mode and hide the toggle
|
|
353
|
+
let fleetMode = !activeRepoKey;
|
|
354
|
+
const tableContainer = container.querySelector("#session-table-container");
|
|
355
|
+
const fleetBtn = container.querySelector("#session-fleet-toggle");
|
|
356
|
+
|
|
357
|
+
if (!activeRepoKey && fleetBtn) {
|
|
358
|
+
fleetBtn.style.display = "none"; // no repo context — fleet-only, hide toggle
|
|
359
|
+
} else if (fleetBtn) {
|
|
360
|
+
fleetBtn.classList.toggle("active", fleetMode);
|
|
361
|
+
fleetBtn.textContent = fleetMode ? "Single repo" : "Fleet";
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
function loadSessions() {
|
|
365
|
+
let url;
|
|
366
|
+
if (fleetMode) {
|
|
367
|
+
url = "/api/audit/sessions?fleet=true";
|
|
368
|
+
} else {
|
|
369
|
+
url = "/api/audit/sessions?repo=" + encodeURIComponent(activeRepoKey);
|
|
370
|
+
}
|
|
371
|
+
tableContainer.innerHTML = '<p class="muted" style="padding:12px">Loading sessions...</p>';
|
|
372
|
+
fetch(url)
|
|
373
|
+
.then(r => r.json())
|
|
374
|
+
.then(summaries => {
|
|
375
|
+
if (summaries.error) {
|
|
376
|
+
tableContainer.innerHTML = `<p class="muted" style="padding:12px">${escHtml(summaries.error)}</p>`;
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
tableContainer.innerHTML = renderSessionTable(summaries, fleetMode);
|
|
380
|
+
|
|
381
|
+
// Expandable row click handlers
|
|
382
|
+
tableContainer.querySelectorAll(".session-row").forEach(row => {
|
|
383
|
+
row.style.cursor = "pointer";
|
|
384
|
+
row.addEventListener("click", function () {
|
|
385
|
+
const key = this.dataset.rowKey;
|
|
386
|
+
const detail = document.getElementById("detail-" + key);
|
|
387
|
+
if (detail) {
|
|
388
|
+
const visible = detail.style.display !== "none";
|
|
389
|
+
detail.style.display = visible ? "none" : "";
|
|
390
|
+
this.classList.toggle("session-row-expanded", !visible);
|
|
391
|
+
}
|
|
392
|
+
});
|
|
393
|
+
});
|
|
394
|
+
})
|
|
395
|
+
.catch(err => {
|
|
396
|
+
tableContainer.innerHTML = `<p class="muted" style="padding:12px">Failed to load sessions: ${escHtml(err.message)}</p>`;
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
if (fleetBtn) {
|
|
401
|
+
fleetBtn.addEventListener("click", function () {
|
|
402
|
+
fleetMode = !fleetMode;
|
|
403
|
+
this.classList.toggle("active", fleetMode);
|
|
404
|
+
this.textContent = fleetMode ? "Single repo" : "Fleet";
|
|
405
|
+
loadSessions();
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// Initial load
|
|
410
|
+
if (!window.GATOR_SNAPSHOT) {
|
|
411
|
+
loadSessions();
|
|
412
|
+
}
|
|
413
|
+
};
|
|
414
|
+
})();
|