shipwright-cli 2.4.0 → 3.0.0
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.md +16 -11
- package/completions/_shipwright +1 -1
- package/completions/shipwright.bash +3 -8
- package/completions/shipwright.fish +1 -1
- package/config/defaults.json +111 -0
- package/config/event-schema.json +81 -0
- package/config/policy.json +13 -18
- package/dashboard/coverage/coverage-summary.json +14 -0
- package/dashboard/public/index.html +1 -1
- package/dashboard/server.ts +306 -17
- package/dashboard/src/components/charts/bar.test.ts +79 -0
- package/dashboard/src/components/charts/donut.test.ts +68 -0
- package/dashboard/src/components/charts/pipeline-rail.test.ts +117 -0
- package/dashboard/src/components/charts/sparkline.test.ts +125 -0
- package/dashboard/src/core/api.test.ts +309 -0
- package/dashboard/src/core/helpers.test.ts +301 -0
- package/dashboard/src/core/router.test.ts +307 -0
- package/dashboard/src/core/router.ts +7 -0
- package/dashboard/src/core/sse.test.ts +144 -0
- package/dashboard/src/views/metrics.test.ts +186 -0
- package/dashboard/src/views/overview.test.ts +173 -0
- package/dashboard/src/views/pipelines.test.ts +183 -0
- package/dashboard/src/views/team.test.ts +253 -0
- package/dashboard/vitest.config.ts +14 -5
- package/docs/TIPS.md +1 -1
- package/docs/patterns/README.md +1 -1
- package/package.json +5 -7
- package/scripts/adapters/docker-deploy.sh +1 -1
- package/scripts/adapters/tmux-adapter.sh +11 -1
- package/scripts/adapters/wezterm-adapter.sh +1 -1
- package/scripts/check-version-consistency.sh +1 -1
- package/scripts/lib/architecture.sh +126 -0
- package/scripts/lib/bootstrap.sh +75 -0
- package/scripts/lib/compat.sh +89 -6
- package/scripts/lib/config.sh +91 -0
- package/scripts/lib/daemon-adaptive.sh +3 -3
- package/scripts/lib/daemon-dispatch.sh +39 -16
- package/scripts/lib/daemon-health.sh +1 -1
- package/scripts/lib/daemon-patrol.sh +24 -12
- package/scripts/lib/daemon-poll.sh +37 -25
- package/scripts/lib/daemon-state.sh +115 -23
- package/scripts/lib/daemon-triage.sh +30 -8
- package/scripts/lib/fleet-failover.sh +63 -0
- package/scripts/lib/helpers.sh +30 -6
- package/scripts/lib/pipeline-detection.sh +2 -2
- package/scripts/lib/pipeline-github.sh +9 -9
- package/scripts/lib/pipeline-intelligence.sh +85 -35
- package/scripts/lib/pipeline-quality-checks.sh +16 -16
- package/scripts/lib/pipeline-quality.sh +1 -1
- package/scripts/lib/pipeline-stages.sh +242 -28
- package/scripts/lib/pipeline-state.sh +40 -4
- package/scripts/lib/test-helpers.sh +247 -0
- package/scripts/postinstall.mjs +3 -11
- package/scripts/sw +10 -4
- package/scripts/sw-activity.sh +1 -11
- package/scripts/sw-adaptive.sh +109 -85
- package/scripts/sw-adversarial.sh +4 -14
- package/scripts/sw-architecture-enforcer.sh +1 -11
- package/scripts/sw-auth.sh +8 -17
- package/scripts/sw-autonomous.sh +111 -49
- package/scripts/sw-changelog.sh +1 -11
- package/scripts/sw-checkpoint.sh +144 -20
- package/scripts/sw-ci.sh +2 -12
- package/scripts/sw-cleanup.sh +13 -17
- package/scripts/sw-code-review.sh +16 -36
- package/scripts/sw-connect.sh +5 -12
- package/scripts/sw-context.sh +9 -26
- package/scripts/sw-cost.sh +6 -16
- package/scripts/sw-daemon.sh +75 -70
- package/scripts/sw-dashboard.sh +57 -17
- package/scripts/sw-db.sh +506 -15
- package/scripts/sw-decompose.sh +1 -11
- package/scripts/sw-deps.sh +15 -25
- package/scripts/sw-developer-simulation.sh +1 -11
- package/scripts/sw-discovery.sh +112 -30
- package/scripts/sw-doc-fleet.sh +7 -17
- package/scripts/sw-docs-agent.sh +6 -16
- package/scripts/sw-docs.sh +4 -12
- package/scripts/sw-doctor.sh +134 -43
- package/scripts/sw-dora.sh +11 -19
- package/scripts/sw-durable.sh +35 -52
- package/scripts/sw-e2e-orchestrator.sh +11 -27
- package/scripts/sw-eventbus.sh +115 -115
- package/scripts/sw-evidence.sh +114 -30
- package/scripts/sw-feedback.sh +3 -13
- package/scripts/sw-fix.sh +2 -20
- package/scripts/sw-fleet-discover.sh +1 -11
- package/scripts/sw-fleet-viz.sh +10 -18
- package/scripts/sw-fleet.sh +13 -17
- package/scripts/sw-github-app.sh +6 -16
- package/scripts/sw-github-checks.sh +1 -11
- package/scripts/sw-github-deploy.sh +1 -11
- package/scripts/sw-github-graphql.sh +2 -12
- package/scripts/sw-guild.sh +1 -11
- package/scripts/sw-heartbeat.sh +49 -12
- package/scripts/sw-hygiene.sh +45 -43
- package/scripts/sw-incident.sh +48 -74
- package/scripts/sw-init.sh +35 -37
- package/scripts/sw-instrument.sh +1 -11
- package/scripts/sw-intelligence.sh +362 -51
- package/scripts/sw-jira.sh +5 -14
- package/scripts/sw-launchd.sh +2 -12
- package/scripts/sw-linear.sh +8 -17
- package/scripts/sw-logs.sh +4 -12
- package/scripts/sw-loop.sh +641 -90
- package/scripts/sw-memory.sh +243 -17
- package/scripts/sw-mission-control.sh +2 -12
- package/scripts/sw-model-router.sh +73 -34
- package/scripts/sw-otel.sh +11 -21
- package/scripts/sw-oversight.sh +1 -11
- package/scripts/sw-patrol-meta.sh +5 -11
- package/scripts/sw-pipeline-composer.sh +7 -17
- package/scripts/sw-pipeline-vitals.sh +1 -11
- package/scripts/sw-pipeline.sh +478 -122
- package/scripts/sw-pm.sh +2 -12
- package/scripts/sw-pr-lifecycle.sh +27 -25
- package/scripts/sw-predictive.sh +16 -22
- package/scripts/sw-prep.sh +6 -16
- package/scripts/sw-ps.sh +1 -11
- package/scripts/sw-public-dashboard.sh +2 -12
- package/scripts/sw-quality.sh +77 -10
- package/scripts/sw-reaper.sh +1 -11
- package/scripts/sw-recruit.sh +15 -25
- package/scripts/sw-regression.sh +11 -21
- package/scripts/sw-release-manager.sh +19 -28
- package/scripts/sw-release.sh +8 -16
- package/scripts/sw-remote.sh +1 -11
- package/scripts/sw-replay.sh +48 -44
- package/scripts/sw-retro.sh +70 -92
- package/scripts/sw-review-rerun.sh +1 -1
- package/scripts/sw-scale.sh +109 -32
- package/scripts/sw-security-audit.sh +12 -22
- package/scripts/sw-self-optimize.sh +239 -23
- package/scripts/sw-session.sh +3 -13
- package/scripts/sw-setup.sh +8 -18
- package/scripts/sw-standup.sh +5 -15
- package/scripts/sw-status.sh +32 -23
- package/scripts/sw-strategic.sh +129 -13
- package/scripts/sw-stream.sh +1 -11
- package/scripts/sw-swarm.sh +76 -36
- package/scripts/sw-team-stages.sh +10 -20
- package/scripts/sw-templates.sh +4 -14
- package/scripts/sw-testgen.sh +3 -13
- package/scripts/sw-tmux-pipeline.sh +1 -19
- package/scripts/sw-tmux-role-color.sh +0 -10
- package/scripts/sw-tmux-status.sh +3 -11
- package/scripts/sw-tmux.sh +2 -20
- package/scripts/sw-trace.sh +1 -19
- package/scripts/sw-tracker-github.sh +0 -10
- package/scripts/sw-tracker-jira.sh +1 -11
- package/scripts/sw-tracker-linear.sh +1 -11
- package/scripts/sw-tracker.sh +7 -24
- package/scripts/sw-triage.sh +24 -34
- package/scripts/sw-upgrade.sh +5 -23
- package/scripts/sw-ux.sh +1 -19
- package/scripts/sw-webhook.sh +18 -32
- package/scripts/sw-widgets.sh +3 -21
- package/scripts/sw-worktree.sh +11 -27
- package/scripts/update-homebrew-sha.sh +67 -0
- package/templates/pipelines/tdd.json +72 -0
- package/scripts/sw-pipeline.sh.mock +0 -7
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
# ╚═══════════════════════════════════════════════════════════════════════════╝
|
|
6
6
|
set -euo pipefail
|
|
7
7
|
|
|
8
|
-
VERSION="
|
|
8
|
+
VERSION="3.0.0"
|
|
9
9
|
|
|
10
10
|
# ─── Script directory resolution ────────────────────────────────────────────
|
|
11
11
|
SOURCE="${BASH_SOURCE[0]}"
|
|
@@ -35,24 +35,6 @@ if [[ "$(type -t now_iso 2>/dev/null)" != "function" ]]; then
|
|
|
35
35
|
now_iso() { date -u +"%Y-%m-%dT%H:%M:%SZ"; }
|
|
36
36
|
now_epoch() { date +%s; }
|
|
37
37
|
fi
|
|
38
|
-
if [[ "$(type -t emit_event 2>/dev/null)" != "function" ]]; then
|
|
39
|
-
emit_event() {
|
|
40
|
-
local event_type="$1"; shift; mkdir -p "${HOME}/.shipwright"
|
|
41
|
-
local payload="{\"ts\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"type\":\"$event_type\""
|
|
42
|
-
while [[ $# -gt 0 ]]; do local key="${1%%=*}" val="${1#*=}"; payload="${payload},\"${key}\":\"${val}\""; shift; done
|
|
43
|
-
echo "${payload}}" >> "${HOME}/.shipwright/events.jsonl"
|
|
44
|
-
}
|
|
45
|
-
fi
|
|
46
|
-
CYAN="${CYAN:-\033[38;2;0;212;255m}"
|
|
47
|
-
PURPLE="${PURPLE:-\033[38;2;124;58;237m}"
|
|
48
|
-
BLUE="${BLUE:-\033[38;2;0;102;255m}"
|
|
49
|
-
GREEN="${GREEN:-\033[38;2;74;222;128m}"
|
|
50
|
-
YELLOW="${YELLOW:-\033[38;2;250;204;21m}"
|
|
51
|
-
RED="${RED:-\033[38;2;248;113;113m}"
|
|
52
|
-
DIM="${DIM:-\033[2m}"
|
|
53
|
-
BOLD="${BOLD:-\033[1m}"
|
|
54
|
-
RESET="${RESET:-\033[0m}"
|
|
55
|
-
|
|
56
38
|
# E2E-specific: log suite result to RESULTS_LOG (different from canonical emit_event)
|
|
57
39
|
_log_suite_result() {
|
|
58
40
|
local event_type="$1"
|
|
@@ -250,9 +232,11 @@ run_suite() {
|
|
|
250
232
|
info "Running: $suite_name ($features)"
|
|
251
233
|
local start_time=$(date +%s)
|
|
252
234
|
|
|
253
|
-
# Run with timeout
|
|
235
|
+
# Run with timeout (gtimeout on macOS, timeout on Linux)
|
|
236
|
+
local timeout_cmd="timeout"
|
|
237
|
+
command -v gtimeout >/dev/null 2>&1 && timeout_cmd="gtimeout"
|
|
254
238
|
local exit_code=0
|
|
255
|
-
|
|
239
|
+
"$timeout_cmd" "$timeout" bash "$test_script" || exit_code=$?
|
|
256
240
|
|
|
257
241
|
local end_time=$(date +%s)
|
|
258
242
|
local duration=$((end_time - start_time))
|
|
@@ -306,8 +290,8 @@ run_parallel() {
|
|
|
306
290
|
for (( i = 0; i < max_parallel && i < ${#suite_array[@]}; i++ )); do
|
|
307
291
|
run_suite "${suite_array[$i]}" &
|
|
308
292
|
pids+=($!)
|
|
309
|
-
((running
|
|
310
|
-
((idx
|
|
293
|
+
running=$((running + 1))
|
|
294
|
+
idx=$((idx + 1))
|
|
311
295
|
done
|
|
312
296
|
|
|
313
297
|
# Process remaining suites as workers finish
|
|
@@ -321,7 +305,7 @@ run_parallel() {
|
|
|
321
305
|
if [[ $idx -lt ${#suite_array[@]} ]]; then
|
|
322
306
|
run_suite "${suite_array[$idx]}" &
|
|
323
307
|
pids[$i]=$!
|
|
324
|
-
((idx
|
|
308
|
+
idx=$((idx + 1))
|
|
325
309
|
else
|
|
326
310
|
unset 'pids[$i]'
|
|
327
311
|
fi
|
|
@@ -359,11 +343,11 @@ cmd_report() {
|
|
|
359
343
|
while IFS= read -r line; do
|
|
360
344
|
local exit_code=$(echo "$line" | jq -r '.exit_code // 0')
|
|
361
345
|
if [[ $exit_code -eq 0 ]]; then
|
|
362
|
-
((pass
|
|
346
|
+
pass=$((pass + 1))
|
|
363
347
|
elif [[ $exit_code -eq 124 ]]; then
|
|
364
|
-
((timeout
|
|
348
|
+
timeout=$((timeout + 1))
|
|
365
349
|
else
|
|
366
|
-
((fail
|
|
350
|
+
fail=$((fail + 1))
|
|
367
351
|
fi
|
|
368
352
|
done < "$RESULTS_LOG"
|
|
369
353
|
|
package/scripts/sw-eventbus.sh
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
set -euo pipefail
|
|
8
8
|
trap 'echo "ERROR: $BASH_SOURCE:$LINENO exited with status $?" >&2' ERR
|
|
9
9
|
|
|
10
|
-
VERSION="
|
|
10
|
+
VERSION="3.0.0"
|
|
11
11
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
12
12
|
|
|
13
13
|
# ─── Cross-platform compatibility ──────────────────────────────────────────
|
|
@@ -17,6 +17,8 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
|
17
17
|
# Canonical helpers (colors, output, events)
|
|
18
18
|
# shellcheck source=lib/helpers.sh
|
|
19
19
|
[[ -f "$SCRIPT_DIR/lib/helpers.sh" ]] && source "$SCRIPT_DIR/lib/helpers.sh"
|
|
20
|
+
# shellcheck source=sw-db.sh
|
|
21
|
+
[[ -f "$SCRIPT_DIR/sw-db.sh" ]] && source "$SCRIPT_DIR/sw-db.sh"
|
|
20
22
|
# Fallbacks when helpers not loaded (e.g. test env with overridden SCRIPT_DIR)
|
|
21
23
|
[[ "$(type -t info 2>/dev/null)" == "function" ]] || info() { echo -e "\033[38;2;0;212;255m\033[1m▸\033[0m $*"; }
|
|
22
24
|
[[ "$(type -t success 2>/dev/null)" == "function" ]] || success() { echo -e "\033[38;2;74;222;128m\033[1m✓\033[0m $*"; }
|
|
@@ -34,24 +36,14 @@ if [[ "$(type -t emit_event 2>/dev/null)" != "function" ]]; then
|
|
|
34
36
|
echo "${payload}}" >> "${HOME}/.shipwright/events.jsonl"
|
|
35
37
|
}
|
|
36
38
|
fi
|
|
37
|
-
CYAN="${CYAN:-\033[38;2;0;212;255m}"
|
|
38
|
-
PURPLE="${PURPLE:-\033[38;2;124;58;237m}"
|
|
39
|
-
BLUE="${BLUE:-\033[38;2;0;102;255m}"
|
|
40
|
-
GREEN="${GREEN:-\033[38;2;74;222;128m}"
|
|
41
|
-
YELLOW="${YELLOW:-\033[38;2;250;204;21m}"
|
|
42
|
-
RED="${RED:-\033[38;2;248;113;113m}"
|
|
43
|
-
DIM="${DIM:-\033[2m}"
|
|
44
|
-
BOLD="${BOLD:-\033[1m}"
|
|
45
|
-
RESET="${RESET:-\033[0m}"
|
|
46
|
-
|
|
47
39
|
# ─── Configuration ──────────────────────────────────────────────────────────
|
|
48
|
-
|
|
40
|
+
EVENTS_FILE="${EVENTS_FILE:-${HOME}/.shipwright/events.jsonl}"
|
|
49
41
|
EVENT_TTL_DAYS=7 # Default TTL for events (seconds = 7 * 86400)
|
|
50
42
|
|
|
51
43
|
# ─── Initialize eventbus directory ──────────────────────────────────────────
|
|
52
44
|
ensure_eventbus_dir() {
|
|
53
45
|
local dir
|
|
54
|
-
dir="$(dirname "$
|
|
46
|
+
dir="$(dirname "$EVENTS_FILE")"
|
|
55
47
|
[[ -d "$dir" ]] || mkdir -p "$dir"
|
|
56
48
|
}
|
|
57
49
|
|
|
@@ -66,7 +58,7 @@ generate_uuid() {
|
|
|
66
58
|
cmd_publish() {
|
|
67
59
|
local event_type="$1"
|
|
68
60
|
local source="${2:-unknown}"
|
|
69
|
-
local correlation_id="$3"
|
|
61
|
+
local correlation_id="${3:-}"
|
|
70
62
|
local payload_json="${4:-{}}"
|
|
71
63
|
|
|
72
64
|
if [[ -z "$event_type" ]]; then
|
|
@@ -80,47 +72,54 @@ cmd_publish() {
|
|
|
80
72
|
correlation_id="$(generate_uuid)"
|
|
81
73
|
fi
|
|
82
74
|
|
|
83
|
-
|
|
84
|
-
timestamp="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
|
85
|
-
|
|
86
|
-
# Build event JSON on single line
|
|
87
|
-
local event_json
|
|
88
|
-
event_json="{\"type\": \"$event_type\", \"source\": \"$source\", \"correlation_id\": \"$correlation_id\", \"timestamp\": \"$timestamp\", \"payload\": $payload_json}"
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
# Atomic write (append to JSONL file)
|
|
92
|
-
local tmp_file
|
|
93
|
-
tmp_file="$(mktemp)"
|
|
94
|
-
if [[ -f "$EVENTBUS_FILE" ]]; then
|
|
95
|
-
cat "$EVENTBUS_FILE" > "$tmp_file"
|
|
96
|
-
fi
|
|
97
|
-
echo "$event_json" >> "$tmp_file"
|
|
98
|
-
mv "$tmp_file" "$EVENTBUS_FILE"
|
|
75
|
+
emit_event "$event_type" "source=$source" "correlation_id=$correlation_id" "payload=$payload_json"
|
|
99
76
|
|
|
100
77
|
success "Published event: $event_type (correlation_id: $correlation_id)"
|
|
101
78
|
}
|
|
102
79
|
|
|
103
80
|
# ─── Subscribe command ──────────────────────────────────────────────────────
|
|
104
81
|
cmd_subscribe() {
|
|
105
|
-
local
|
|
106
|
-
local
|
|
107
|
-
|
|
108
|
-
ensure_eventbus_dir
|
|
82
|
+
local filter="${1:-}"
|
|
83
|
+
local poll_interval=1
|
|
84
|
+
local last_id=0
|
|
109
85
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
86
|
+
# Try to resume from last consumer offset
|
|
87
|
+
if db_available 2>/dev/null; then
|
|
88
|
+
last_id=$(db_get_consumer_offset "eventbus-subscribe-$$" 2>/dev/null || echo "0")
|
|
89
|
+
fi
|
|
114
90
|
|
|
115
|
-
info "Subscribing to
|
|
91
|
+
info "Subscribing to events (${filter:-(all types)})..."
|
|
116
92
|
echo ""
|
|
117
93
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
94
|
+
while true; do
|
|
95
|
+
if db_available 2>/dev/null; then
|
|
96
|
+
local events batch_last_id=0
|
|
97
|
+
events=$(sqlite3 -json "$DB_FILE" "SELECT * FROM events WHERE id > $last_id ORDER BY id ASC LIMIT 50;" 2>/dev/null || echo "[]")
|
|
98
|
+
if [[ "$events" != "[]" && -n "$events" ]]; then
|
|
99
|
+
while IFS= read -r event; do
|
|
100
|
+
[[ -z "$event" ]] && continue
|
|
101
|
+
local etype
|
|
102
|
+
etype=$(echo "$event" | jq -r '.type // ""')
|
|
103
|
+
if [[ -z "$filter" || "$etype" == *"$filter"* ]]; then
|
|
104
|
+
echo "$event"
|
|
105
|
+
fi
|
|
106
|
+
batch_last_id=$(echo "$event" | jq -r '.id // 0')
|
|
107
|
+
[[ "${batch_last_id:-0}" -gt 0 ]] && last_id="$batch_last_id"
|
|
108
|
+
done < <(echo "$events" | jq -c '.[]' 2>/dev/null)
|
|
109
|
+
[[ "$last_id" -gt 0 ]] && db_set_consumer_offset "eventbus-subscribe-$$" "$last_id" 2>/dev/null || true
|
|
110
|
+
fi
|
|
111
|
+
else
|
|
112
|
+
# Fallback: tail the JSONL
|
|
113
|
+
[[ ! -f "$EVENTS_FILE" ]] && touch "$EVENTS_FILE"
|
|
114
|
+
tail -n 0 -f "$EVENTS_FILE" 2>/dev/null | while IFS= read -r line; do
|
|
115
|
+
if [[ -z "$filter" ]] || echo "$line" | jq -r '.type // ""' 2>/dev/null | grep -q "$filter"; then
|
|
116
|
+
echo "$line"
|
|
117
|
+
fi
|
|
118
|
+
done
|
|
119
|
+
return
|
|
120
|
+
fi
|
|
121
|
+
sleep "$poll_interval"
|
|
122
|
+
done
|
|
124
123
|
}
|
|
125
124
|
|
|
126
125
|
# ─── Process reaper (SIGCHLD monitor) ──────────────────────────────────────
|
|
@@ -176,14 +175,14 @@ cmd_watch() {
|
|
|
176
175
|
echo ""
|
|
177
176
|
|
|
178
177
|
# Determine platform and use appropriate watcher
|
|
179
|
-
if command -v fswatch
|
|
178
|
+
if command -v fswatch >/dev/null 2>&1; then
|
|
180
179
|
# macOS with fswatch
|
|
181
180
|
fswatch -r "$watch_dir" | while read -r file; do
|
|
182
181
|
local payload
|
|
183
182
|
payload="{\"file\": \"$file\", \"timestamp\": \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\"}"
|
|
184
183
|
cmd_publish "file.changed" "watcher" "$(generate_uuid)" "$payload"
|
|
185
184
|
done
|
|
186
|
-
elif command -v inotifywait
|
|
185
|
+
elif command -v inotifywait >/dev/null 2>&1; then
|
|
187
186
|
# Linux with inotify-tools
|
|
188
187
|
inotifywait -m -r "$watch_dir" | while read -r dir action file; do
|
|
189
188
|
local filepath="${dir}${file}"
|
|
@@ -204,28 +203,27 @@ cmd_replay() {
|
|
|
204
203
|
|
|
205
204
|
ensure_eventbus_dir
|
|
206
205
|
|
|
207
|
-
[[ ! -f "$EVENTBUS_FILE" ]] && {
|
|
208
|
-
warn "Event bus is empty or does not exist yet"
|
|
209
|
-
return 0
|
|
210
|
-
}
|
|
211
|
-
|
|
212
206
|
info "Replaying events from the last ${minutes} minutes..."
|
|
213
207
|
echo ""
|
|
214
208
|
|
|
215
|
-
# Calculate cutoff timestamp
|
|
216
209
|
local cutoff_epoch
|
|
217
210
|
cutoff_epoch=$(($(date +%s) - (minutes * 60)))
|
|
218
|
-
local cutoff_iso
|
|
219
|
-
cutoff_iso="$(date -u -j -f %s "$cutoff_epoch" +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -u -d @"$cutoff_epoch" +%Y-%m-%dT%H:%M:%SZ 2>/dev/null)"
|
|
220
211
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
212
|
+
if db_available 2>/dev/null; then
|
|
213
|
+
sqlite3 -json "$DB_FILE" "SELECT * FROM events WHERE ts_epoch >= $cutoff_epoch ORDER BY id ASC;" 2>/dev/null | jq -c '.[]' 2>/dev/null | while IFS= read -r event; do
|
|
214
|
+
[[ -n "$event" ]] && echo "$event"
|
|
215
|
+
done
|
|
216
|
+
elif [[ -f "$EVENTS_FILE" ]]; then
|
|
217
|
+
local cutoff_iso
|
|
218
|
+
cutoff_iso="$(date -u -j -f %s "$cutoff_epoch" +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -u -d @"$cutoff_epoch" +%Y-%m-%dT%H:%M:%SZ 2>/dev/null)"
|
|
219
|
+
grep "ts" "$EVENTS_FILE" 2>/dev/null | while read -r line; do
|
|
220
|
+
local ts
|
|
221
|
+
ts=$(echo "$line" | jq -r '.ts // ""' 2>/dev/null)
|
|
222
|
+
[[ -n "$ts" && "$ts" > "$cutoff_iso" ]] && echo "$line"
|
|
223
|
+
done
|
|
224
|
+
else
|
|
225
|
+
warn "Event bus is empty or does not exist yet"
|
|
226
|
+
fi
|
|
229
227
|
}
|
|
230
228
|
|
|
231
229
|
# ─── Status command ────────────────────────────────────────────────────────
|
|
@@ -237,31 +235,37 @@ cmd_status() {
|
|
|
237
235
|
echo -e "${DIM}$(date '+%Y-%m-%d %H:%M:%S')${RESET}"
|
|
238
236
|
echo ""
|
|
239
237
|
|
|
240
|
-
if
|
|
241
|
-
|
|
238
|
+
if db_available 2>/dev/null; then
|
|
239
|
+
local total_events last_event_ts
|
|
240
|
+
total_events=$(_db_query "SELECT COUNT(*) FROM events;" 2>/dev/null || echo "0")
|
|
241
|
+
last_event_ts=$(_db_query "SELECT ts FROM events ORDER BY id DESC LIMIT 1;" 2>/dev/null || echo "never")
|
|
242
|
+
echo -e " ${CYAN}Event Store:${RESET} SQLite ($DB_FILE)"
|
|
243
|
+
echo -e " ${CYAN}Total Events:${RESET} ${BOLD}${total_events}${RESET}"
|
|
244
|
+
echo -e " ${CYAN}Last Event:${RESET} $last_event_ts"
|
|
245
|
+
echo ""
|
|
246
|
+
if [[ "${total_events:-0}" -gt 0 ]]; then
|
|
247
|
+
echo -e " ${PURPLE}${BOLD}Events by Type${RESET}"
|
|
248
|
+
_db_query "SELECT type, COUNT(*) as cnt FROM events GROUP BY type ORDER BY cnt DESC;" 2>/dev/null | while IFS='|' read -r etype count; do
|
|
249
|
+
printf " ${DIM}%-40s${RESET} %3d events\n" "$etype" "$count"
|
|
250
|
+
done
|
|
251
|
+
fi
|
|
252
|
+
elif [[ -f "$EVENTS_FILE" ]]; then
|
|
253
|
+
local total_events last_event_ts
|
|
254
|
+
total_events=$(wc -l < "$EVENTS_FILE" || echo 0)
|
|
255
|
+
last_event_ts=$(tail -1 "$EVENTS_FILE" | jq -r '.ts // "never"' 2>/dev/null || echo "never")
|
|
256
|
+
echo -e " ${CYAN}Event Store:${RESET} $EVENTS_FILE (file fallback)"
|
|
257
|
+
echo -e " ${CYAN}Total Events:${RESET} ${BOLD}${total_events}${RESET}"
|
|
258
|
+
echo -e " ${CYAN}Last Event:${RESET} $last_event_ts"
|
|
242
259
|
echo ""
|
|
243
|
-
|
|
260
|
+
if [[ "${total_events:-0}" -gt 0 ]]; then
|
|
261
|
+
echo -e " ${PURPLE}${BOLD}Events by Type${RESET}"
|
|
262
|
+
jq -r '.type' "$EVENTS_FILE" 2>/dev/null | sort | uniq -c | sort -rn | while read -r count type; do
|
|
263
|
+
printf " ${DIM}%-40s${RESET} %3d events\n" "$type" "$count"
|
|
264
|
+
done
|
|
265
|
+
fi
|
|
266
|
+
else
|
|
267
|
+
echo -e " ${YELLOW}Event bus not yet initialized${RESET}"
|
|
244
268
|
fi
|
|
245
|
-
|
|
246
|
-
local total_events
|
|
247
|
-
total_events=$(wc -l < "$EVENTBUS_FILE" || echo 0)
|
|
248
|
-
|
|
249
|
-
local last_event_ts
|
|
250
|
-
last_event_ts=$(tail -1 "$EVENTBUS_FILE" | grep -o '"timestamp": "[^"]*"' | cut -d'"' -f4 || echo "never")
|
|
251
|
-
|
|
252
|
-
echo -e " ${CYAN}Event Bus:${RESET} $EVENTBUS_FILE"
|
|
253
|
-
echo -e " ${CYAN}Total Events:${RESET} ${BOLD}${total_events}${RESET}"
|
|
254
|
-
echo -e " ${CYAN}Last Event:${RESET} $last_event_ts"
|
|
255
|
-
echo ""
|
|
256
|
-
|
|
257
|
-
# Count events by type
|
|
258
|
-
if [[ $total_events -gt 0 ]]; then
|
|
259
|
-
echo -e " ${PURPLE}${BOLD}Events by Type${RESET}"
|
|
260
|
-
grep '"type"' "$EVENTBUS_FILE" | cut -d'"' -f4 | sort | uniq -c | sort -rn | while read -r count type; do
|
|
261
|
-
printf " ${DIM}%-40s${RESET} %3d events\n" "$type" "$count"
|
|
262
|
-
done
|
|
263
|
-
fi
|
|
264
|
-
|
|
265
269
|
echo ""
|
|
266
270
|
}
|
|
267
271
|
|
|
@@ -271,40 +275,36 @@ cmd_clean() {
|
|
|
271
275
|
|
|
272
276
|
ensure_eventbus_dir
|
|
273
277
|
|
|
274
|
-
[[ ! -f "$EVENTBUS_FILE" ]] && {
|
|
275
|
-
success "Event bus is empty"
|
|
276
|
-
return 0
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
info "Cleaning events older than ${ttl_days} days..."
|
|
280
|
-
|
|
281
|
-
# Calculate cutoff timestamp
|
|
282
278
|
local cutoff_epoch
|
|
283
279
|
cutoff_epoch=$(($(date +%s) - (ttl_days * 86400)))
|
|
284
280
|
local cutoff_iso
|
|
285
281
|
cutoff_iso="$(date -u -j -f %s "$cutoff_epoch" +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -u -d @"$cutoff_epoch" +%Y-%m-%dT%H:%M:%SZ 2>/dev/null)"
|
|
286
282
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
283
|
+
if db_available 2>/dev/null; then
|
|
284
|
+
local old_count new_count removed
|
|
285
|
+
old_count=$(_db_query "SELECT COUNT(*) FROM events;" 2>/dev/null || echo 0)
|
|
286
|
+
_db_exec "DELETE FROM events WHERE ts < '${cutoff_iso}';" 2>/dev/null || true
|
|
287
|
+
new_count=$(_db_query "SELECT COUNT(*) FROM events;" 2>/dev/null || echo 0)
|
|
288
|
+
removed=$((old_count - new_count))
|
|
289
|
+
success "Removed $removed old events. Remaining: $new_count"
|
|
290
|
+
elif [[ -f "$EVENTS_FILE" ]]; then
|
|
291
|
+
info "Cleaning events older than ${ttl_days} days..."
|
|
292
|
+
local old_count tmp_file new_count removed
|
|
293
|
+
old_count=$(grep -c "ts" "$EVENTS_FILE" 2>/dev/null || echo 0)
|
|
294
|
+
tmp_file="$(mktemp)"
|
|
295
|
+
while IFS= read -r line; do
|
|
296
|
+
[[ -z "$line" ]] && continue
|
|
297
|
+
local ts
|
|
298
|
+
ts=$(echo "$line" | jq -r '.ts // ""' 2>/dev/null)
|
|
299
|
+
[[ -n "$ts" && "$ts" > "$cutoff_iso" ]] && echo "$line" >> "$tmp_file"
|
|
300
|
+
done < "$EVENTS_FILE"
|
|
301
|
+
mv "$tmp_file" "$EVENTS_FILE"
|
|
302
|
+
new_count=$(wc -l < "$EVENTS_FILE" || echo 0)
|
|
303
|
+
removed=$((old_count - new_count))
|
|
304
|
+
success "Removed $removed old events. Remaining: $new_count"
|
|
305
|
+
else
|
|
306
|
+
success "Event bus is empty"
|
|
307
|
+
fi
|
|
308
308
|
}
|
|
309
309
|
|
|
310
310
|
# ─── Help command ──────────────────────────────────────────────────────────
|