loki-mode 5.36.0 → 5.37.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/SKILL.md +2 -2
- package/VERSION +1 -1
- package/autonomy/loki +317 -30
- package/autonomy/run.sh +310 -6
- package/autonomy/sandbox.sh +2 -2
- package/autonomy/serve.sh +25 -0
- package/dashboard/__init__.py +1 -1
- package/dashboard/audit.py +77 -5
- package/dashboard/auth.py +255 -22
- package/dashboard/secrets.py +152 -0
- package/dashboard/server.py +266 -11
- package/docs/INSTALLATION.md +1 -1
- package/package.json +1 -1
- package/providers/gemini.sh +4 -2
package/SKILL.md
CHANGED
|
@@ -3,7 +3,7 @@ name: loki-mode
|
|
|
3
3
|
description: Multi-agent autonomous startup system. Triggers on "Loki Mode". Takes PRD to deployed product with zero human intervention. Requires --dangerously-skip-permissions flag.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# Loki Mode v5.
|
|
6
|
+
# Loki Mode v5.37.1
|
|
7
7
|
|
|
8
8
|
**You are an autonomous agent. You make decisions. You do not ask questions. You do not stop.**
|
|
9
9
|
|
|
@@ -260,4 +260,4 @@ The following features are documented in skill modules but not yet fully automat
|
|
|
260
260
|
| Quality gates 3-reviewer system | Implemented (v5.35.0) | 5 specialist reviewers in `skills/quality-gates.md`; execution in run.sh |
|
|
261
261
|
| Benchmarks (HumanEval, SWE-bench) | Infrastructure only | Runner scripts and datasets exist in `benchmarks/`; no published results |
|
|
262
262
|
|
|
263
|
-
**v5.
|
|
263
|
+
**v5.37.1 | security: WebSocket auth, RBAC roles, syslog forwarding, sandbox hardening | ~260 lines core**
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
5.
|
|
1
|
+
5.37.1
|
package/autonomy/loki
CHANGED
|
@@ -322,8 +322,10 @@ show_help() {
|
|
|
322
322
|
echo " compound [cmd] Knowledge compounding (list|show|search|run|stats)"
|
|
323
323
|
echo " council [cmd] Completion council (status|verdicts|convergence|force-review|report)"
|
|
324
324
|
echo " dogfood Show self-development statistics"
|
|
325
|
+
echo " secrets [cmd] API key status and validation (status|validate)"
|
|
325
326
|
echo " reset [target] Reset session state (all|retries|failed)"
|
|
326
327
|
echo " doctor [--json] Check system prerequisites"
|
|
328
|
+
echo " watchdog [cmd] Process health monitoring (status|help)"
|
|
327
329
|
echo " version Show version"
|
|
328
330
|
echo " help Show this help"
|
|
329
331
|
echo ""
|
|
@@ -1324,8 +1326,10 @@ cmd_dashboard_help() {
|
|
|
1324
1326
|
echo " help Show this help"
|
|
1325
1327
|
echo ""
|
|
1326
1328
|
echo "Options for 'start':"
|
|
1327
|
-
echo " --port PORT
|
|
1328
|
-
echo " --host HOST
|
|
1329
|
+
echo " --port PORT Port to listen on (default: $DASHBOARD_DEFAULT_PORT)"
|
|
1330
|
+
echo " --host HOST Host to bind to (default: $DASHBOARD_DEFAULT_HOST)"
|
|
1331
|
+
echo " --tls-cert PATH Path to PEM certificate (enables HTTPS)"
|
|
1332
|
+
echo " --tls-key PATH Path to PEM private key (enables HTTPS)"
|
|
1329
1333
|
echo ""
|
|
1330
1334
|
echo "Options for 'url':"
|
|
1331
1335
|
echo " --format FMT Output format: text (default) or json"
|
|
@@ -1333,10 +1337,13 @@ cmd_dashboard_help() {
|
|
|
1333
1337
|
echo "Environment:"
|
|
1334
1338
|
echo " LOKI_DASHBOARD_PORT Default port (overrides $DASHBOARD_DEFAULT_PORT)"
|
|
1335
1339
|
echo " LOKI_DASHBOARD_HOST Default host (overrides $DASHBOARD_DEFAULT_HOST)"
|
|
1340
|
+
echo " LOKI_TLS_CERT Path to PEM certificate (enables HTTPS)"
|
|
1341
|
+
echo " LOKI_TLS_KEY Path to PEM private key (enables HTTPS)"
|
|
1336
1342
|
echo ""
|
|
1337
1343
|
echo "Examples:"
|
|
1338
1344
|
echo " loki dashboard start # Start on default port"
|
|
1339
1345
|
echo " loki dashboard start --port 8080 # Start on port 8080"
|
|
1346
|
+
echo " loki dashboard start --tls-cert cert.pem --tls-key key.pem"
|
|
1340
1347
|
echo " loki dashboard status # Check if running"
|
|
1341
1348
|
echo " loki dashboard url --format json # Get URL as JSON"
|
|
1342
1349
|
echo " loki dashboard open # Open in browser"
|
|
@@ -1346,6 +1353,8 @@ cmd_dashboard_help() {
|
|
|
1346
1353
|
cmd_dashboard_start() {
|
|
1347
1354
|
local port="${LOKI_DASHBOARD_PORT:-$DASHBOARD_DEFAULT_PORT}"
|
|
1348
1355
|
local host="${LOKI_DASHBOARD_HOST:-$DASHBOARD_DEFAULT_HOST}"
|
|
1356
|
+
local tls_cert="${LOKI_TLS_CERT:-}"
|
|
1357
|
+
local tls_key="${LOKI_TLS_KEY:-}"
|
|
1349
1358
|
|
|
1350
1359
|
# Parse arguments
|
|
1351
1360
|
while [[ $# -gt 0 ]]; do
|
|
@@ -1376,6 +1385,32 @@ cmd_dashboard_start() {
|
|
|
1376
1385
|
host="${1#*=}"
|
|
1377
1386
|
shift
|
|
1378
1387
|
;;
|
|
1388
|
+
--tls-cert)
|
|
1389
|
+
if [[ -n "${2:-}" ]]; then
|
|
1390
|
+
tls_cert="$2"
|
|
1391
|
+
shift 2
|
|
1392
|
+
else
|
|
1393
|
+
echo -e "${RED}--tls-cert requires a path${NC}"
|
|
1394
|
+
exit 1
|
|
1395
|
+
fi
|
|
1396
|
+
;;
|
|
1397
|
+
--tls-cert=*)
|
|
1398
|
+
tls_cert="${1#*=}"
|
|
1399
|
+
shift
|
|
1400
|
+
;;
|
|
1401
|
+
--tls-key)
|
|
1402
|
+
if [[ -n "${2:-}" ]]; then
|
|
1403
|
+
tls_key="$2"
|
|
1404
|
+
shift 2
|
|
1405
|
+
else
|
|
1406
|
+
echo -e "${RED}--tls-key requires a path${NC}"
|
|
1407
|
+
exit 1
|
|
1408
|
+
fi
|
|
1409
|
+
;;
|
|
1410
|
+
--tls-key=*)
|
|
1411
|
+
tls_key="${1#*=}"
|
|
1412
|
+
shift
|
|
1413
|
+
;;
|
|
1379
1414
|
--help|-h)
|
|
1380
1415
|
cmd_dashboard_help
|
|
1381
1416
|
exit 0
|
|
@@ -1445,14 +1480,27 @@ cmd_dashboard_start() {
|
|
|
1445
1480
|
mkdir -p "$log_dir"
|
|
1446
1481
|
local log_file="${log_dir}/dashboard.log"
|
|
1447
1482
|
|
|
1483
|
+
# Determine URL scheme based on TLS
|
|
1484
|
+
local url_scheme="http"
|
|
1485
|
+
local tls_info=""
|
|
1486
|
+
if [ -n "$tls_cert" ] && [ -n "$tls_key" ]; then
|
|
1487
|
+
url_scheme="https"
|
|
1488
|
+
tls_info=" (TLS enabled)"
|
|
1489
|
+
fi
|
|
1490
|
+
|
|
1448
1491
|
echo -e "${GREEN}Starting dashboard server...${NC}"
|
|
1449
1492
|
echo -e "${CYAN}Host:${NC} $host"
|
|
1450
1493
|
echo -e "${CYAN}Port:${NC} $port"
|
|
1494
|
+
if [ -n "$tls_info" ]; then
|
|
1495
|
+
echo -e "${CYAN}TLS:${NC} enabled"
|
|
1496
|
+
fi
|
|
1451
1497
|
echo ""
|
|
1452
1498
|
|
|
1453
1499
|
# Start the dashboard server in background
|
|
1454
1500
|
# LOKI_SKILL_DIR tells server.py where to find static files
|
|
1455
|
-
LOKI_DIR="$LOKI_DIR" LOKI_SKILL_DIR="$SKILL_DIR" PYTHONPATH="$SKILL_DIR"
|
|
1501
|
+
LOKI_DIR="$LOKI_DIR" LOKI_SKILL_DIR="$SKILL_DIR" PYTHONPATH="$SKILL_DIR" \
|
|
1502
|
+
LOKI_DASHBOARD_HOST="$host" LOKI_DASHBOARD_PORT="$port" \
|
|
1503
|
+
LOKI_TLS_CERT="$tls_cert" LOKI_TLS_KEY="$tls_key" \
|
|
1456
1504
|
nohup "$python_cmd" -m dashboard.server > "$log_file" 2>&1 &
|
|
1457
1505
|
local new_pid=$!
|
|
1458
1506
|
|
|
@@ -1462,12 +1510,13 @@ cmd_dashboard_start() {
|
|
|
1462
1510
|
# Also save port and host for status command
|
|
1463
1511
|
echo "$port" > "${DASHBOARD_PID_DIR}/port"
|
|
1464
1512
|
echo "$host" > "${DASHBOARD_PID_DIR}/host"
|
|
1513
|
+
echo "$url_scheme" > "${DASHBOARD_PID_DIR}/scheme"
|
|
1465
1514
|
|
|
1466
1515
|
# Wait a moment and check if process is still running
|
|
1467
1516
|
sleep 1
|
|
1468
1517
|
|
|
1469
1518
|
if kill -0 "$new_pid" 2>/dev/null; then
|
|
1470
|
-
local url="
|
|
1519
|
+
local url="${url_scheme}://${host}:${port}"
|
|
1471
1520
|
echo -e "${GREEN}Dashboard server started${NC}"
|
|
1472
1521
|
echo ""
|
|
1473
1522
|
echo " PID: $new_pid"
|
|
@@ -1549,9 +1598,10 @@ cmd_dashboard_status() {
|
|
|
1549
1598
|
fi
|
|
1550
1599
|
|
|
1551
1600
|
if kill -0 "$pid" 2>/dev/null; then
|
|
1552
|
-
# Read saved host and
|
|
1601
|
+
# Read saved host, port, and scheme
|
|
1553
1602
|
local host="${DASHBOARD_DEFAULT_HOST}"
|
|
1554
1603
|
local port="${DASHBOARD_DEFAULT_PORT}"
|
|
1604
|
+
local scheme="http"
|
|
1555
1605
|
|
|
1556
1606
|
if [ -f "${DASHBOARD_PID_DIR}/host" ]; then
|
|
1557
1607
|
host=$(cat "${DASHBOARD_PID_DIR}/host" 2>/dev/null)
|
|
@@ -1559,20 +1609,30 @@ cmd_dashboard_status() {
|
|
|
1559
1609
|
if [ -f "${DASHBOARD_PID_DIR}/port" ]; then
|
|
1560
1610
|
port=$(cat "${DASHBOARD_PID_DIR}/port" 2>/dev/null)
|
|
1561
1611
|
fi
|
|
1612
|
+
if [ -f "${DASHBOARD_PID_DIR}/scheme" ]; then
|
|
1613
|
+
scheme=$(cat "${DASHBOARD_PID_DIR}/scheme" 2>/dev/null)
|
|
1614
|
+
fi
|
|
1562
1615
|
|
|
1563
|
-
local url="
|
|
1616
|
+
local url="${scheme}://${host}:${port}"
|
|
1564
1617
|
|
|
1565
1618
|
echo -e "${GREEN}Status: Running${NC}"
|
|
1566
1619
|
echo ""
|
|
1567
1620
|
echo " PID: $pid"
|
|
1568
1621
|
echo " Host: $host"
|
|
1569
1622
|
echo " Port: $port"
|
|
1623
|
+
if [ "$scheme" = "https" ]; then
|
|
1624
|
+
echo " TLS: enabled"
|
|
1625
|
+
fi
|
|
1570
1626
|
echo " URL: $url"
|
|
1571
1627
|
echo ""
|
|
1572
1628
|
|
|
1573
1629
|
# Check if server is responding
|
|
1630
|
+
local curl_flags="-s --connect-timeout 2"
|
|
1631
|
+
if [ "$scheme" = "https" ]; then
|
|
1632
|
+
curl_flags="$curl_flags -k"
|
|
1633
|
+
fi
|
|
1574
1634
|
if command -v curl &> /dev/null; then
|
|
1575
|
-
if curl
|
|
1635
|
+
if curl $curl_flags "$url" &> /dev/null; then
|
|
1576
1636
|
echo -e "${GREEN}Server is responding${NC}"
|
|
1577
1637
|
else
|
|
1578
1638
|
echo -e "${YELLOW}Server process running but not responding on $url${NC}"
|
|
@@ -1653,9 +1713,10 @@ cmd_dashboard_url() {
|
|
|
1653
1713
|
exit 1
|
|
1654
1714
|
fi
|
|
1655
1715
|
|
|
1656
|
-
# Read saved host and
|
|
1716
|
+
# Read saved host, port, and scheme
|
|
1657
1717
|
local host="${DASHBOARD_DEFAULT_HOST}"
|
|
1658
1718
|
local port="${DASHBOARD_DEFAULT_PORT}"
|
|
1719
|
+
local scheme="http"
|
|
1659
1720
|
|
|
1660
1721
|
if [ -f "${DASHBOARD_PID_DIR}/host" ]; then
|
|
1661
1722
|
host=$(cat "${DASHBOARD_PID_DIR}/host" 2>/dev/null)
|
|
@@ -1663,11 +1724,14 @@ cmd_dashboard_url() {
|
|
|
1663
1724
|
if [ -f "${DASHBOARD_PID_DIR}/port" ]; then
|
|
1664
1725
|
port=$(cat "${DASHBOARD_PID_DIR}/port" 2>/dev/null)
|
|
1665
1726
|
fi
|
|
1727
|
+
if [ -f "${DASHBOARD_PID_DIR}/scheme" ]; then
|
|
1728
|
+
scheme=$(cat "${DASHBOARD_PID_DIR}/scheme" 2>/dev/null)
|
|
1729
|
+
fi
|
|
1666
1730
|
|
|
1667
|
-
local url="
|
|
1731
|
+
local url="${scheme}://${host}:${port}"
|
|
1668
1732
|
|
|
1669
1733
|
if [[ "$format" == "json" ]]; then
|
|
1670
|
-
echo "{\"running\":true,\"url\":\"$url\",\"host\":\"$host\",\"port\":$port,\"pid\":$pid}"
|
|
1734
|
+
echo "{\"running\":true,\"url\":\"$url\",\"scheme\":\"$scheme\",\"host\":\"$host\",\"port\":$port,\"pid\":$pid}"
|
|
1671
1735
|
else
|
|
1672
1736
|
echo "$url"
|
|
1673
1737
|
fi
|
|
@@ -1693,9 +1757,10 @@ cmd_dashboard_open() {
|
|
|
1693
1757
|
exit 1
|
|
1694
1758
|
fi
|
|
1695
1759
|
|
|
1696
|
-
# Read saved host and
|
|
1760
|
+
# Read saved host, port, and scheme
|
|
1697
1761
|
local host="${DASHBOARD_DEFAULT_HOST}"
|
|
1698
1762
|
local port="${DASHBOARD_DEFAULT_PORT}"
|
|
1763
|
+
local scheme="http"
|
|
1699
1764
|
|
|
1700
1765
|
if [ -f "${DASHBOARD_PID_DIR}/host" ]; then
|
|
1701
1766
|
host=$(cat "${DASHBOARD_PID_DIR}/host" 2>/dev/null)
|
|
@@ -1703,8 +1768,11 @@ cmd_dashboard_open() {
|
|
|
1703
1768
|
if [ -f "${DASHBOARD_PID_DIR}/port" ]; then
|
|
1704
1769
|
port=$(cat "${DASHBOARD_PID_DIR}/port" 2>/dev/null)
|
|
1705
1770
|
fi
|
|
1771
|
+
if [ -f "${DASHBOARD_PID_DIR}/scheme" ]; then
|
|
1772
|
+
scheme=$(cat "${DASHBOARD_PID_DIR}/scheme" 2>/dev/null)
|
|
1773
|
+
fi
|
|
1706
1774
|
|
|
1707
|
-
local url="
|
|
1775
|
+
local url="${scheme}://${host}:${port}"
|
|
1708
1776
|
|
|
1709
1777
|
echo -e "${GREEN}Opening dashboard: $url${NC}"
|
|
1710
1778
|
|
|
@@ -2747,6 +2815,78 @@ cmd_version() {
|
|
|
2747
2815
|
echo "Loki Mode v$(get_version)"
|
|
2748
2816
|
}
|
|
2749
2817
|
|
|
2818
|
+
# Secrets / credential management
|
|
2819
|
+
cmd_secrets() {
|
|
2820
|
+
case "${1:-status}" in
|
|
2821
|
+
status)
|
|
2822
|
+
echo "API Key Status:"
|
|
2823
|
+
echo ""
|
|
2824
|
+
for key_var in ANTHROPIC_API_KEY OPENAI_API_KEY GOOGLE_API_KEY; do
|
|
2825
|
+
local value="${!key_var:-}"
|
|
2826
|
+
if [[ -n "$value" ]]; then
|
|
2827
|
+
local masked="${value:0:8}...${value: -4}"
|
|
2828
|
+
echo " $key_var: SET ($masked, ${#value} chars)"
|
|
2829
|
+
else
|
|
2830
|
+
# Check secret file mounts
|
|
2831
|
+
local lower_name
|
|
2832
|
+
lower_name=$(echo "$key_var" | tr '[:upper:]' '[:lower:]')
|
|
2833
|
+
local found=false
|
|
2834
|
+
for mount_path in /run/secrets /var/run/secrets; do
|
|
2835
|
+
if [[ -f "$mount_path/$lower_name" ]]; then
|
|
2836
|
+
echo " $key_var: AVAILABLE (secret file: $mount_path/$lower_name)"
|
|
2837
|
+
found=true
|
|
2838
|
+
break
|
|
2839
|
+
fi
|
|
2840
|
+
done
|
|
2841
|
+
if [[ "$found" != "true" ]]; then
|
|
2842
|
+
echo " $key_var: NOT SET"
|
|
2843
|
+
fi
|
|
2844
|
+
fi
|
|
2845
|
+
done
|
|
2846
|
+
;;
|
|
2847
|
+
validate)
|
|
2848
|
+
echo "Validating API keys..."
|
|
2849
|
+
python3 -c "
|
|
2850
|
+
import sys
|
|
2851
|
+
sys.path.insert(0, '$(dirname "$(dirname "$(resolve_script_path "$0")")")')
|
|
2852
|
+
from dashboard.secrets import load_secrets
|
|
2853
|
+
result = load_secrets()
|
|
2854
|
+
for name, info in result.items():
|
|
2855
|
+
status = 'OK' if info['valid_format'] else 'INVALID'
|
|
2856
|
+
source = info['source']
|
|
2857
|
+
masked = info['masked']
|
|
2858
|
+
warning = info.get('warning', '')
|
|
2859
|
+
if info['set']:
|
|
2860
|
+
print(f' {name}: {status} ({masked}, source: {source})')
|
|
2861
|
+
if warning:
|
|
2862
|
+
print(f' WARNING: {warning}')
|
|
2863
|
+
else:
|
|
2864
|
+
print(f' {name}: NOT SET')
|
|
2865
|
+
" 2>/dev/null || echo " (Python validation unavailable, showing basic status)"
|
|
2866
|
+
;;
|
|
2867
|
+
help)
|
|
2868
|
+
echo "Usage: loki secrets [status|validate|help]"
|
|
2869
|
+
echo ""
|
|
2870
|
+
echo "Manage API keys and credentials."
|
|
2871
|
+
echo ""
|
|
2872
|
+
echo "Commands:"
|
|
2873
|
+
echo " status Show which API keys are configured (default)"
|
|
2874
|
+
echo " validate Validate API key formats"
|
|
2875
|
+
echo " help Show this help"
|
|
2876
|
+
echo ""
|
|
2877
|
+
echo "Secret Loading Priority:"
|
|
2878
|
+
echo " 1. Environment variable (ANTHROPIC_API_KEY, etc.)"
|
|
2879
|
+
echo " 2. Docker/K8s secret file (/run/secrets/anthropic_api_key)"
|
|
2880
|
+
echo " 3. Not set"
|
|
2881
|
+
;;
|
|
2882
|
+
*)
|
|
2883
|
+
echo "Unknown secrets command: $1"
|
|
2884
|
+
cmd_secrets help
|
|
2885
|
+
return 1
|
|
2886
|
+
;;
|
|
2887
|
+
esac
|
|
2888
|
+
}
|
|
2889
|
+
|
|
2750
2890
|
# Show recent logs
|
|
2751
2891
|
cmd_logs() {
|
|
2752
2892
|
local lines="${1:-50}"
|
|
@@ -2788,7 +2928,11 @@ cmd_api() {
|
|
|
2788
2928
|
|
|
2789
2929
|
# Start server
|
|
2790
2930
|
mkdir -p "$LOKI_DIR/logs" "$LOKI_DIR/dashboard"
|
|
2791
|
-
|
|
2931
|
+
local uvicorn_args="--host 0.0.0.0 --port $port"
|
|
2932
|
+
if [ -n "${LOKI_TLS_CERT:-}" ] && [ -n "${LOKI_TLS_KEY:-}" ]; then
|
|
2933
|
+
uvicorn_args="$uvicorn_args --ssl-certfile ${LOKI_TLS_CERT} --ssl-keyfile ${LOKI_TLS_KEY}"
|
|
2934
|
+
fi
|
|
2935
|
+
LOKI_DIR="$LOKI_DIR" nohup python3 -m uvicorn dashboard.server:app $uvicorn_args > "$LOKI_DIR/logs/api.log" 2>&1 &
|
|
2792
2936
|
local new_pid=$!
|
|
2793
2937
|
echo "$new_pid" > "$pid_file"
|
|
2794
2938
|
|
|
@@ -3957,6 +4101,113 @@ _list_templates() {
|
|
|
3957
4101
|
echo "Usage: loki init --template <name>"
|
|
3958
4102
|
}
|
|
3959
4103
|
|
|
4104
|
+
# Watchdog: process health monitoring
|
|
4105
|
+
cmd_watchdog() {
|
|
4106
|
+
local subcommand="${1:-status}"
|
|
4107
|
+
shift 2>/dev/null || true
|
|
4108
|
+
|
|
4109
|
+
case "$subcommand" in
|
|
4110
|
+
status)
|
|
4111
|
+
# Show watchdog state
|
|
4112
|
+
if [[ "${LOKI_WATCHDOG:-false}" == "true" ]]; then
|
|
4113
|
+
echo -e "${GREEN}Watchdog: ENABLED${NC} (interval: ${LOKI_WATCHDOG_INTERVAL:-30}s)"
|
|
4114
|
+
else
|
|
4115
|
+
echo "Watchdog: DISABLED"
|
|
4116
|
+
echo "Enable with: LOKI_WATCHDOG=true loki start"
|
|
4117
|
+
fi
|
|
4118
|
+
|
|
4119
|
+
echo ""
|
|
4120
|
+
echo -e "${BOLD}Process Health:${NC}"
|
|
4121
|
+
|
|
4122
|
+
# Check dashboard
|
|
4123
|
+
local dpid_file="$LOKI_DIR/dashboard/dashboard.pid"
|
|
4124
|
+
if [[ -f "$dpid_file" ]]; then
|
|
4125
|
+
local dpid
|
|
4126
|
+
dpid=$(cat "$dpid_file" 2>/dev/null)
|
|
4127
|
+
if [[ -n "$dpid" ]] && kill -0 "$dpid" 2>/dev/null; then
|
|
4128
|
+
echo -e " Dashboard (PID $dpid): ${GREEN}alive${NC}"
|
|
4129
|
+
else
|
|
4130
|
+
echo -e " Dashboard (PID ${dpid:-?}): ${RED}DEAD${NC}"
|
|
4131
|
+
fi
|
|
4132
|
+
else
|
|
4133
|
+
echo -e " Dashboard: ${DIM}not running${NC}"
|
|
4134
|
+
fi
|
|
4135
|
+
|
|
4136
|
+
# Check session
|
|
4137
|
+
local spid_file="$LOKI_DIR/loki.pid"
|
|
4138
|
+
if [[ -f "$spid_file" ]]; then
|
|
4139
|
+
local spid
|
|
4140
|
+
spid=$(cat "$spid_file" 2>/dev/null)
|
|
4141
|
+
if [[ -n "$spid" ]] && kill -0 "$spid" 2>/dev/null; then
|
|
4142
|
+
echo -e " Session (PID $spid): ${GREEN}alive${NC}"
|
|
4143
|
+
else
|
|
4144
|
+
echo -e " Session (PID ${spid:-?}): ${RED}DEAD${NC}"
|
|
4145
|
+
fi
|
|
4146
|
+
else
|
|
4147
|
+
echo -e " Session: ${DIM}not running${NC}"
|
|
4148
|
+
fi
|
|
4149
|
+
|
|
4150
|
+
# Check agents
|
|
4151
|
+
local agents_file="$LOKI_DIR/state/agents.json"
|
|
4152
|
+
if [[ -f "$agents_file" ]]; then
|
|
4153
|
+
local agent_info
|
|
4154
|
+
agent_info=$(python3 -c "
|
|
4155
|
+
import json, os
|
|
4156
|
+
try:
|
|
4157
|
+
agents = json.load(open('$agents_file'))
|
|
4158
|
+
alive = dead = other = 0
|
|
4159
|
+
for a in agents:
|
|
4160
|
+
pid = a.get('pid')
|
|
4161
|
+
status = a.get('status', '')
|
|
4162
|
+
if status in ('terminated', 'completed', 'failed', 'crashed'):
|
|
4163
|
+
other += 1
|
|
4164
|
+
continue
|
|
4165
|
+
if pid:
|
|
4166
|
+
try:
|
|
4167
|
+
os.kill(int(pid), 0)
|
|
4168
|
+
alive += 1
|
|
4169
|
+
except (OSError, ValueError):
|
|
4170
|
+
dead += 1
|
|
4171
|
+
else:
|
|
4172
|
+
other += 1
|
|
4173
|
+
print(f'{alive}:{dead}:{other}')
|
|
4174
|
+
except Exception:
|
|
4175
|
+
print('0:0:0')
|
|
4176
|
+
" 2>/dev/null || echo "0:0:0")
|
|
4177
|
+
local a_alive a_dead a_other
|
|
4178
|
+
IFS=: read -r a_alive a_dead a_other <<< "$agent_info"
|
|
4179
|
+
if [[ "$a_alive" -gt 0 ]] || [[ "$a_dead" -gt 0 ]]; then
|
|
4180
|
+
echo -e " Agents: ${GREEN}${a_alive} alive${NC}, ${RED}${a_dead} dead${NC}, ${DIM}${a_other} finished${NC}"
|
|
4181
|
+
elif [[ "$a_other" -gt 0 ]]; then
|
|
4182
|
+
echo -e " Agents: ${DIM}${a_other} finished${NC}"
|
|
4183
|
+
else
|
|
4184
|
+
echo -e " Agents: ${DIM}none${NC}"
|
|
4185
|
+
fi
|
|
4186
|
+
else
|
|
4187
|
+
echo -e " Agents: ${DIM}none${NC}"
|
|
4188
|
+
fi
|
|
4189
|
+
;;
|
|
4190
|
+
help)
|
|
4191
|
+
echo "Usage: loki watchdog [status|help]"
|
|
4192
|
+
echo ""
|
|
4193
|
+
echo "Monitor process health for Loki Mode sessions."
|
|
4194
|
+
echo ""
|
|
4195
|
+
echo "Subcommands:"
|
|
4196
|
+
echo " status Show health of dashboard, session, and agent processes (default)"
|
|
4197
|
+
echo " help Show this help"
|
|
4198
|
+
echo ""
|
|
4199
|
+
echo "Environment:"
|
|
4200
|
+
echo " LOKI_WATCHDOG=true Enable watchdog monitoring during sessions"
|
|
4201
|
+
echo " LOKI_WATCHDOG_INTERVAL=30 Check interval in seconds (default: 30)"
|
|
4202
|
+
;;
|
|
4203
|
+
*)
|
|
4204
|
+
echo -e "${RED}Unknown watchdog command: $subcommand${NC}"
|
|
4205
|
+
cmd_watchdog help
|
|
4206
|
+
return 1
|
|
4207
|
+
;;
|
|
4208
|
+
esac
|
|
4209
|
+
}
|
|
4210
|
+
|
|
3960
4211
|
# Main command dispatcher
|
|
3961
4212
|
main() {
|
|
3962
4213
|
if [ $# -eq 0 ]; then
|
|
@@ -4049,9 +4300,15 @@ main() {
|
|
|
4049
4300
|
voice)
|
|
4050
4301
|
cmd_voice "$@"
|
|
4051
4302
|
;;
|
|
4303
|
+
secrets)
|
|
4304
|
+
cmd_secrets "$@"
|
|
4305
|
+
;;
|
|
4052
4306
|
doctor)
|
|
4053
4307
|
cmd_doctor "$@"
|
|
4054
4308
|
;;
|
|
4309
|
+
watchdog)
|
|
4310
|
+
cmd_watchdog "$@"
|
|
4311
|
+
;;
|
|
4055
4312
|
version|--version|-v)
|
|
4056
4313
|
cmd_version
|
|
4057
4314
|
;;
|
|
@@ -6251,24 +6508,43 @@ cmd_enterprise() {
|
|
|
6251
6508
|
echo ""
|
|
6252
6509
|
|
|
6253
6510
|
local auth_enabled="${LOKI_ENTERPRISE_AUTH:-false}"
|
|
6254
|
-
local
|
|
6511
|
+
local audit_disabled="${LOKI_AUDIT_DISABLED:-false}"
|
|
6512
|
+
local audit_force_on="${LOKI_ENTERPRISE_AUDIT:-false}"
|
|
6255
6513
|
|
|
6256
6514
|
if [ "$auth_enabled" = "true" ] || [ "$auth_enabled" = "1" ]; then
|
|
6257
|
-
echo -e "
|
|
6515
|
+
echo -e " Token Auth: ${GREEN}Enabled${NC}"
|
|
6258
6516
|
else
|
|
6259
|
-
echo -e "
|
|
6517
|
+
echo -e " Token Auth: ${DIM}Disabled${NC}"
|
|
6260
6518
|
fi
|
|
6261
6519
|
|
|
6262
|
-
|
|
6263
|
-
|
|
6520
|
+
# OIDC/SSO status
|
|
6521
|
+
local oidc_issuer="${LOKI_OIDC_ISSUER:-}"
|
|
6522
|
+
local oidc_client="${LOKI_OIDC_CLIENT_ID:-}"
|
|
6523
|
+
if [ -n "$oidc_issuer" ] && [ -n "$oidc_client" ]; then
|
|
6524
|
+
echo -e " OIDC/SSO: ${GREEN}Enabled${NC} (${oidc_issuer})"
|
|
6264
6525
|
else
|
|
6526
|
+
echo -e " OIDC/SSO: ${DIM}Disabled${NC}"
|
|
6527
|
+
fi
|
|
6528
|
+
|
|
6529
|
+
# Audit is on by default; disabled only if LOKI_AUDIT_DISABLED=true
|
|
6530
|
+
if [ "$audit_force_on" = "true" ] || [ "$audit_force_on" = "1" ]; then
|
|
6531
|
+
echo -e " Audit Logging: ${GREEN}Enabled (enterprise)${NC}"
|
|
6532
|
+
elif [ "$audit_disabled" = "true" ] || [ "$audit_disabled" = "1" ]; then
|
|
6265
6533
|
echo -e " Audit Logging: ${DIM}Disabled${NC}"
|
|
6534
|
+
else
|
|
6535
|
+
echo -e " Audit Logging: ${GREEN}Enabled (default)${NC}"
|
|
6266
6536
|
fi
|
|
6267
6537
|
|
|
6268
6538
|
echo ""
|
|
6269
|
-
echo "
|
|
6270
|
-
echo " export LOKI_ENTERPRISE_AUTH=true"
|
|
6271
|
-
echo " export
|
|
6539
|
+
echo "Configure with environment variables:"
|
|
6540
|
+
echo " export LOKI_ENTERPRISE_AUTH=true # Token authentication"
|
|
6541
|
+
echo " export LOKI_AUDIT_DISABLED=true # Disable audit logging"
|
|
6542
|
+
echo " export LOKI_ENTERPRISE_AUDIT=true # Force audit on (legacy)"
|
|
6543
|
+
echo ""
|
|
6544
|
+
echo "OIDC/SSO (optional, works alongside token auth):"
|
|
6545
|
+
echo " export LOKI_OIDC_ISSUER=https://accounts.google.com"
|
|
6546
|
+
echo " export LOKI_OIDC_CLIENT_ID=your-client-id"
|
|
6547
|
+
echo " export LOKI_OIDC_AUDIENCE=your-audience # Optional, defaults to client_id"
|
|
6272
6548
|
;;
|
|
6273
6549
|
|
|
6274
6550
|
token)
|
|
@@ -6547,10 +6823,15 @@ else:
|
|
|
6547
6823
|
audit)
|
|
6548
6824
|
local audit_cmd="${2:-summary}"
|
|
6549
6825
|
|
|
6550
|
-
|
|
6551
|
-
|
|
6552
|
-
|
|
6553
|
-
|
|
6826
|
+
# Audit is on by default. Only blocked if explicitly disabled and not force-enabled.
|
|
6827
|
+
local _audit_disabled="${LOKI_AUDIT_DISABLED:-false}"
|
|
6828
|
+
local _audit_force="${LOKI_ENTERPRISE_AUDIT:-false}"
|
|
6829
|
+
if [ "$_audit_force" != "true" ] && [ "$_audit_force" != "1" ]; then
|
|
6830
|
+
if [ "$_audit_disabled" = "true" ] || [ "$_audit_disabled" = "1" ]; then
|
|
6831
|
+
echo -e "${YELLOW}Audit logging is disabled${NC}"
|
|
6832
|
+
echo "Remove LOKI_AUDIT_DISABLED or set LOKI_ENTERPRISE_AUDIT=true"
|
|
6833
|
+
exit 1
|
|
6834
|
+
fi
|
|
6554
6835
|
fi
|
|
6555
6836
|
|
|
6556
6837
|
local audit_dir="${HOME}/.loki/dashboard/audit"
|
|
@@ -6658,13 +6939,19 @@ for line in sys.stdin:
|
|
|
6658
6939
|
echo "Commands:"
|
|
6659
6940
|
echo " status Show which enterprise features are enabled"
|
|
6660
6941
|
echo " token <cmd> Manage API tokens (requires LOKI_ENTERPRISE_AUTH=true)"
|
|
6661
|
-
echo " audit <cmd> Query audit logs (
|
|
6942
|
+
echo " audit <cmd> Query audit logs (enabled by default)"
|
|
6943
|
+
echo ""
|
|
6944
|
+
echo "Configure enterprise features:"
|
|
6945
|
+
echo " export LOKI_ENTERPRISE_AUTH=true # Token authentication"
|
|
6946
|
+
echo " export LOKI_AUDIT_DISABLED=true # Disable audit logging"
|
|
6947
|
+
echo " export LOKI_ENTERPRISE_AUDIT=true # Force audit on (legacy)"
|
|
6662
6948
|
echo ""
|
|
6663
|
-
echo "
|
|
6664
|
-
echo " export
|
|
6665
|
-
echo " export
|
|
6949
|
+
echo "OIDC/SSO (optional, works alongside token auth):"
|
|
6950
|
+
echo " export LOKI_OIDC_ISSUER=https://accounts.google.com"
|
|
6951
|
+
echo " export LOKI_OIDC_CLIENT_ID=your-client-id"
|
|
6952
|
+
echo " export LOKI_OIDC_AUDIENCE=your-audience # Optional"
|
|
6666
6953
|
echo ""
|
|
6667
|
-
echo "
|
|
6954
|
+
echo "Audit logging is enabled by default. Auth is optional."
|
|
6668
6955
|
;;
|
|
6669
6956
|
|
|
6670
6957
|
*)
|