@aiassesstech/mighty-mark 0.5.4 → 0.5.6
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/CHANGELOG.md +13 -0
- package/package.json +1 -1
- package/src/scripts/fleet-bus-diagnostics.sh +454 -0
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,19 @@ All notable changes to `@aiassesstech/mighty-mark` will be documented in this fi
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.5.5] — 2026-03-08
|
|
9
|
+
|
|
10
|
+
**Fleet-bus diagnostics script + updated VPS smoke test.**
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- `fleet-bus-diagnostics.sh` — command-line fleet-bus health checker (cards, audit chain, message flow, gateway init, live ping test).
|
|
14
|
+
- Supports `--live` (gateway ping test), `--verbose` (detailed event listing).
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
- Updated `vps-smoke-test.mjs` to use current `enforceRouting(FleetMessage)` API (was using outdated 3-arg signature).
|
|
18
|
+
- Added all 6 agent cards to smoke test (was only checking 4).
|
|
19
|
+
- Fixed `AuditTrailWriter` usage to use `.record()` instead of `.write()`.
|
|
20
|
+
|
|
8
21
|
## [0.5.4] — 2026-03-07
|
|
9
22
|
|
|
10
23
|
**Documentation sync — updated check counts, rules, guides, and deployment playbook.**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aiassesstech/mighty-mark",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.6",
|
|
4
4
|
"description": "System Health Sentinel for AI Assess Tech Fleet — autonomous monitoring, watchdog recovery, and fleet infrastructure oversight.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -0,0 +1,454 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
##############################################################################
|
|
5
|
+
# Fleet-Bus Diagnostics — Command-Line Health & Connectivity Checks
|
|
6
|
+
#
|
|
7
|
+
# Run from the VPS:
|
|
8
|
+
# bash /opt/mighty-mark/fleet-bus-diagnostics.sh
|
|
9
|
+
# bash /opt/mighty-mark/fleet-bus-diagnostics.sh --live # includes live ping test
|
|
10
|
+
# bash /opt/mighty-mark/fleet-bus-diagnostics.sh --verbose # show audit event details
|
|
11
|
+
#
|
|
12
|
+
# What it checks:
|
|
13
|
+
# 1. Agent cards on disk (structure, staleness)
|
|
14
|
+
# 2. Audit trail integrity (SHA-256 hash chain per agent)
|
|
15
|
+
# 3. Recent message flow (who sent what to whom)
|
|
16
|
+
# 4. Gateway fleet-bus initialization logs
|
|
17
|
+
# 5. (--live) Send a fleet/ping via gateway and check for response
|
|
18
|
+
##############################################################################
|
|
19
|
+
|
|
20
|
+
OPENCLAW_HOME="${OPENCLAW_HOME:-$HOME/.openclaw}"
|
|
21
|
+
FLEET_BUS_DIR="$OPENCLAW_HOME/workspace/fleet-bus"
|
|
22
|
+
CARDS_DIR="$FLEET_BUS_DIR/cards"
|
|
23
|
+
AUDIT_DIR="$FLEET_BUS_DIR/audit"
|
|
24
|
+
AGENTS=("jessie" "grillo" "noah" "nole" "sam" "mighty-mark")
|
|
25
|
+
LIVE_TEST=false
|
|
26
|
+
VERBOSE=false
|
|
27
|
+
|
|
28
|
+
for arg in "$@"; do
|
|
29
|
+
case "$arg" in
|
|
30
|
+
--live) LIVE_TEST=true ;;
|
|
31
|
+
--verbose) VERBOSE=true ;;
|
|
32
|
+
--help|-h)
|
|
33
|
+
echo "Usage: $0 [--live] [--verbose]"
|
|
34
|
+
echo " --live Send a live fleet/ping through the gateway"
|
|
35
|
+
echo " --verbose Show detailed audit event data"
|
|
36
|
+
exit 0
|
|
37
|
+
;;
|
|
38
|
+
esac
|
|
39
|
+
done
|
|
40
|
+
|
|
41
|
+
PASS=0
|
|
42
|
+
WARN=0
|
|
43
|
+
FAIL=0
|
|
44
|
+
|
|
45
|
+
check() {
|
|
46
|
+
local label=$1 status=$2 detail=$3
|
|
47
|
+
case "$status" in
|
|
48
|
+
PASS) echo " ✅ $label — $detail"; PASS=$((PASS + 1)) ;;
|
|
49
|
+
WARN) echo " ⚠️ $label — $detail"; WARN=$((WARN + 1)) ;;
|
|
50
|
+
FAIL) echo " ❌ $label — $detail"; FAIL=$((FAIL + 1)) ;;
|
|
51
|
+
esac
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
echo ""
|
|
55
|
+
echo "╔══════════════════════════════════════════════╗"
|
|
56
|
+
echo "║ Fleet-Bus Diagnostics ║"
|
|
57
|
+
echo "╚══════════════════════════════════════════════╝"
|
|
58
|
+
echo ""
|
|
59
|
+
echo " Fleet-bus dir: $FLEET_BUS_DIR"
|
|
60
|
+
echo " Timestamp: $(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
|
61
|
+
echo ""
|
|
62
|
+
|
|
63
|
+
###############################################################################
|
|
64
|
+
# 1. Fleet-Bus Directory Structure
|
|
65
|
+
###############################################################################
|
|
66
|
+
echo "=== 1. Directory Structure ==="
|
|
67
|
+
|
|
68
|
+
if [ -d "$FLEET_BUS_DIR" ]; then
|
|
69
|
+
check "Fleet-bus directory" "PASS" "$FLEET_BUS_DIR exists"
|
|
70
|
+
else
|
|
71
|
+
check "Fleet-bus directory" "FAIL" "$FLEET_BUS_DIR not found"
|
|
72
|
+
echo ""
|
|
73
|
+
echo " Fleet-bus directory missing. Is the gateway running?"
|
|
74
|
+
echo " Try: systemctl restart openclaw-gateway"
|
|
75
|
+
exit 1
|
|
76
|
+
fi
|
|
77
|
+
|
|
78
|
+
if [ -d "$CARDS_DIR" ]; then
|
|
79
|
+
CARD_COUNT=$(ls -1 "$CARDS_DIR"/*.json 2>/dev/null | wc -l | tr -d ' ')
|
|
80
|
+
check "Cards directory" "PASS" "$CARDS_DIR — $CARD_COUNT card file(s)"
|
|
81
|
+
else
|
|
82
|
+
check "Cards directory" "FAIL" "$CARDS_DIR not found"
|
|
83
|
+
fi
|
|
84
|
+
|
|
85
|
+
if [ -d "$AUDIT_DIR" ]; then
|
|
86
|
+
AUDIT_COUNT=$(ls -1 "$AUDIT_DIR"/*.audit.jsonl 2>/dev/null | wc -l | tr -d ' ')
|
|
87
|
+
check "Audit directory" "PASS" "$AUDIT_DIR — $AUDIT_COUNT audit file(s)"
|
|
88
|
+
else
|
|
89
|
+
check "Audit directory" "WARN" "$AUDIT_DIR not found — no audit trail yet"
|
|
90
|
+
fi
|
|
91
|
+
echo ""
|
|
92
|
+
|
|
93
|
+
###############################################################################
|
|
94
|
+
# 2. Agent Cards
|
|
95
|
+
###############################################################################
|
|
96
|
+
echo "=== 2. Agent Cards ==="
|
|
97
|
+
|
|
98
|
+
NOW_EPOCH=$(date +%s)
|
|
99
|
+
|
|
100
|
+
for AGENT in "${AGENTS[@]}"; do
|
|
101
|
+
CARD_FILE="$CARDS_DIR/${AGENT}.json"
|
|
102
|
+
if [ ! -f "$CARD_FILE" ]; then
|
|
103
|
+
check "Card: $AGENT" "FAIL" "not found at $CARD_FILE"
|
|
104
|
+
continue
|
|
105
|
+
fi
|
|
106
|
+
|
|
107
|
+
CARD_DATA=$(python3 -c "
|
|
108
|
+
import json, sys
|
|
109
|
+
with open('$CARD_FILE') as f:
|
|
110
|
+
c = json.load(f)
|
|
111
|
+
role = c.get('role', '?')
|
|
112
|
+
status = c.get('status', '?')
|
|
113
|
+
auth = c.get('authLevel', '?')
|
|
114
|
+
mode = c.get('communicationMode', '?')
|
|
115
|
+
last_seen = c.get('lastSeen', '')
|
|
116
|
+
methods = len(c.get('supportedMethods', []))
|
|
117
|
+
print(f'{role}|{status}|{auth}|{mode}|{last_seen}|{methods}')
|
|
118
|
+
" 2>/dev/null || echo "ERROR")
|
|
119
|
+
|
|
120
|
+
if [ "$CARD_DATA" = "ERROR" ]; then
|
|
121
|
+
check "Card: $AGENT" "FAIL" "could not parse JSON"
|
|
122
|
+
continue
|
|
123
|
+
fi
|
|
124
|
+
|
|
125
|
+
IFS='|' read -r ROLE STATUS AUTH MODE LAST_SEEN METHODS <<< "$CARD_DATA"
|
|
126
|
+
|
|
127
|
+
STALE=""
|
|
128
|
+
if [ -n "$LAST_SEEN" ]; then
|
|
129
|
+
SEEN_EPOCH=$(date -d "$LAST_SEEN" +%s 2>/dev/null || date -j -f "%Y-%m-%dT%H:%M:%S" "${LAST_SEEN%%.*}" +%s 2>/dev/null || echo "0")
|
|
130
|
+
if [ "$SEEN_EPOCH" -gt 0 ]; then
|
|
131
|
+
AGE_HOURS=$(( (NOW_EPOCH - SEEN_EPOCH) / 3600 ))
|
|
132
|
+
if [ "$AGE_HOURS" -gt 48 ]; then
|
|
133
|
+
STALE=" ⚠️ stale (${AGE_HOURS}h ago)"
|
|
134
|
+
fi
|
|
135
|
+
fi
|
|
136
|
+
fi
|
|
137
|
+
|
|
138
|
+
check "Card: $AGENT" "PASS" "role=$ROLE status=$STATUS auth=$AUTH mode=$MODE methods=$METHODS${STALE}"
|
|
139
|
+
done
|
|
140
|
+
echo ""
|
|
141
|
+
|
|
142
|
+
###############################################################################
|
|
143
|
+
# 3. Audit Trail Integrity
|
|
144
|
+
###############################################################################
|
|
145
|
+
echo "=== 3. Audit Trail ==="
|
|
146
|
+
|
|
147
|
+
TOTAL_EVENTS=0
|
|
148
|
+
declare -A AGENT_EVENTS
|
|
149
|
+
|
|
150
|
+
for AGENT in "${AGENTS[@]}"; do
|
|
151
|
+
AUDIT_FILE="$AUDIT_DIR/${AGENT}.audit.jsonl"
|
|
152
|
+
if [ ! -f "$AUDIT_FILE" ]; then
|
|
153
|
+
check "Audit: $AGENT" "WARN" "no audit file yet"
|
|
154
|
+
AGENT_EVENTS[$AGENT]=0
|
|
155
|
+
continue
|
|
156
|
+
fi
|
|
157
|
+
|
|
158
|
+
EVENT_COUNT=$(wc -l < "$AUDIT_FILE" | tr -d ' ')
|
|
159
|
+
TOTAL_EVENTS=$((TOTAL_EVENTS + EVENT_COUNT))
|
|
160
|
+
AGENT_EVENTS[$AGENT]=$EVENT_COUNT
|
|
161
|
+
|
|
162
|
+
CHAIN_FILE="$AUDIT_DIR/${AGENT}.chain.json"
|
|
163
|
+
if [ -f "$CHAIN_FILE" ]; then
|
|
164
|
+
CHAIN_COUNT=$(python3 -c "
|
|
165
|
+
import json
|
|
166
|
+
with open('$CHAIN_FILE') as f:
|
|
167
|
+
c = json.load(f)
|
|
168
|
+
print(c.get('eventCount', 0))
|
|
169
|
+
" 2>/dev/null || echo "?")
|
|
170
|
+
if [ "$CHAIN_COUNT" = "$EVENT_COUNT" ]; then
|
|
171
|
+
check "Audit: $AGENT" "PASS" "$EVENT_COUNT events, chain state consistent"
|
|
172
|
+
else
|
|
173
|
+
check "Audit: $AGENT" "WARN" "$EVENT_COUNT events in JSONL but chain says $CHAIN_COUNT"
|
|
174
|
+
fi
|
|
175
|
+
else
|
|
176
|
+
check "Audit: $AGENT" "WARN" "$EVENT_COUNT events but no chain state file"
|
|
177
|
+
fi
|
|
178
|
+
done
|
|
179
|
+
|
|
180
|
+
echo ""
|
|
181
|
+
echo " Total audit events across fleet: $TOTAL_EVENTS"
|
|
182
|
+
echo ""
|
|
183
|
+
|
|
184
|
+
###############################################################################
|
|
185
|
+
# 4. Recent Message Flow
|
|
186
|
+
###############################################################################
|
|
187
|
+
echo "=== 4. Message Flow (Last 50 Events) ==="
|
|
188
|
+
|
|
189
|
+
HAS_MESSAGES=false
|
|
190
|
+
|
|
191
|
+
for AGENT in "${AGENTS[@]}"; do
|
|
192
|
+
AUDIT_FILE="$AUDIT_DIR/${AGENT}.audit.jsonl"
|
|
193
|
+
[ ! -f "$AUDIT_FILE" ] && continue
|
|
194
|
+
[ "${AGENT_EVENTS[$AGENT]}" = "0" ] && continue
|
|
195
|
+
|
|
196
|
+
FLOW=$(python3 -c "
|
|
197
|
+
import json, sys
|
|
198
|
+
|
|
199
|
+
events = []
|
|
200
|
+
with open('$AUDIT_FILE') as f:
|
|
201
|
+
for line in f:
|
|
202
|
+
line = line.strip()
|
|
203
|
+
if not line:
|
|
204
|
+
continue
|
|
205
|
+
try:
|
|
206
|
+
events.append(json.loads(line))
|
|
207
|
+
except:
|
|
208
|
+
pass
|
|
209
|
+
|
|
210
|
+
recent = events[-50:] if len(events) > 50 else events
|
|
211
|
+
|
|
212
|
+
sent = []
|
|
213
|
+
received = []
|
|
214
|
+
violations = []
|
|
215
|
+
errors = []
|
|
216
|
+
|
|
217
|
+
for e in recent:
|
|
218
|
+
et = e.get('eventType', '')
|
|
219
|
+
msg = e.get('message', {}) or {}
|
|
220
|
+
ts = e.get('timestamp', '?')[:19]
|
|
221
|
+
outcome = e.get('outcome', '?')
|
|
222
|
+
fr = msg.get('from', '?')
|
|
223
|
+
to = msg.get('to', '?')
|
|
224
|
+
method = msg.get('method', '?')
|
|
225
|
+
|
|
226
|
+
if et == 'message_sent':
|
|
227
|
+
sent.append(f' {ts} {fr} → {to} [{method}] outcome={outcome}')
|
|
228
|
+
elif et == 'message_received':
|
|
229
|
+
received.append(f' {ts} {fr} → {to} [{method}] outcome={outcome}')
|
|
230
|
+
elif et == 'routing_violation':
|
|
231
|
+
violations.append(f' {ts} {fr} → {to} [{method}] reason={e.get(\"routingDecision\", \"?\")}'[:120])
|
|
232
|
+
elif outcome in ('error', 'failed'):
|
|
233
|
+
errors.append(f' {ts} {et} outcome={outcome} details={e.get(\"details\", \"?\")}'[:120])
|
|
234
|
+
|
|
235
|
+
verbose = '$VERBOSE' == 'true'
|
|
236
|
+
if sent:
|
|
237
|
+
print(f' 📤 Sent ({len(sent)}):')
|
|
238
|
+
for s in (sent if verbose else sent[-5:]):
|
|
239
|
+
print(s)
|
|
240
|
+
if not verbose and len(sent) > 5:
|
|
241
|
+
print(f' ... and {len(sent) - 5} more (use --verbose)')
|
|
242
|
+
if received:
|
|
243
|
+
print(f' 📥 Received ({len(received)}):')
|
|
244
|
+
for r in (received if verbose else received[-5:]):
|
|
245
|
+
print(r)
|
|
246
|
+
if not verbose and len(received) > 5:
|
|
247
|
+
print(f' ... and {len(received) - 5} more (use --verbose)')
|
|
248
|
+
if violations:
|
|
249
|
+
print(f' 🚫 Routing Violations ({len(violations)}):')
|
|
250
|
+
for v in violations:
|
|
251
|
+
print(v)
|
|
252
|
+
if errors:
|
|
253
|
+
print(f' 💥 Errors ({len(errors)}):')
|
|
254
|
+
for e in errors:
|
|
255
|
+
print(e)
|
|
256
|
+
|
|
257
|
+
if sent or received:
|
|
258
|
+
sys.exit(0)
|
|
259
|
+
elif violations or errors:
|
|
260
|
+
sys.exit(2)
|
|
261
|
+
else:
|
|
262
|
+
sys.exit(1)
|
|
263
|
+
" 2>/dev/null)
|
|
264
|
+
|
|
265
|
+
EXIT_CODE=$?
|
|
266
|
+
|
|
267
|
+
if [ $EXIT_CODE -eq 0 ]; then
|
|
268
|
+
HAS_MESSAGES=true
|
|
269
|
+
echo " ── $AGENT ──"
|
|
270
|
+
echo "$FLOW"
|
|
271
|
+
echo ""
|
|
272
|
+
elif [ $EXIT_CODE -eq 2 ]; then
|
|
273
|
+
HAS_MESSAGES=true
|
|
274
|
+
echo " ── $AGENT (issues detected) ──"
|
|
275
|
+
echo "$FLOW"
|
|
276
|
+
echo ""
|
|
277
|
+
fi
|
|
278
|
+
done
|
|
279
|
+
|
|
280
|
+
if ! $HAS_MESSAGES; then
|
|
281
|
+
echo " No fleet messages found in any agent's audit trail."
|
|
282
|
+
echo " This means agents haven't communicated via fleet-bus yet."
|
|
283
|
+
echo ""
|
|
284
|
+
echo " To trigger a test, ask Jessie to delegate a task to Sam:"
|
|
285
|
+
echo ' "Jessie, ask Sam to write a hello world script"'
|
|
286
|
+
echo ""
|
|
287
|
+
fi
|
|
288
|
+
|
|
289
|
+
###############################################################################
|
|
290
|
+
# 5. Gateway Fleet-Bus Initialization
|
|
291
|
+
###############################################################################
|
|
292
|
+
echo "=== 5. Gateway Initialization ==="
|
|
293
|
+
|
|
294
|
+
if command -v journalctl &>/dev/null; then
|
|
295
|
+
INIT_LOGS=$(journalctl -u openclaw-gateway --since "24 hours ago" --no-pager 2>/dev/null | grep -i 'fleet-bus.*initialized' || true)
|
|
296
|
+
|
|
297
|
+
if [ -n "$INIT_LOGS" ]; then
|
|
298
|
+
INIT_COUNT=$(echo "$INIT_LOGS" | wc -l | tr -d ' ')
|
|
299
|
+
check "Fleet-bus init logs" "PASS" "$INIT_COUNT initialization(s) in last 24h"
|
|
300
|
+
|
|
301
|
+
INITIALIZED_AGENTS=""
|
|
302
|
+
for AGENT in "${AGENTS[@]}"; do
|
|
303
|
+
if echo "$INIT_LOGS" | grep -q "fleet-bus:${AGENT}"; then
|
|
304
|
+
INITIALIZED_AGENTS="$INITIALIZED_AGENTS $AGENT"
|
|
305
|
+
fi
|
|
306
|
+
done
|
|
307
|
+
|
|
308
|
+
MISSING_AGENTS=""
|
|
309
|
+
for AGENT in "${AGENTS[@]}"; do
|
|
310
|
+
if ! echo "$INITIALIZED_AGENTS" | grep -q "$AGENT"; then
|
|
311
|
+
MISSING_AGENTS="$MISSING_AGENTS $AGENT"
|
|
312
|
+
fi
|
|
313
|
+
done
|
|
314
|
+
|
|
315
|
+
if [ -n "$MISSING_AGENTS" ]; then
|
|
316
|
+
check "Agent init coverage" "WARN" "not seen in logs:$MISSING_AGENTS"
|
|
317
|
+
else
|
|
318
|
+
check "Agent init coverage" "PASS" "all 6 agents initialized"
|
|
319
|
+
fi
|
|
320
|
+
|
|
321
|
+
if $VERBOSE; then
|
|
322
|
+
echo ""
|
|
323
|
+
echo " Recent init logs:"
|
|
324
|
+
echo "$INIT_LOGS" | tail -12 | while read -r line; do
|
|
325
|
+
echo " $line"
|
|
326
|
+
done
|
|
327
|
+
fi
|
|
328
|
+
else
|
|
329
|
+
check "Fleet-bus init logs" "WARN" "no fleet-bus initialization in last 24h"
|
|
330
|
+
fi
|
|
331
|
+
else
|
|
332
|
+
check "Fleet-bus init logs" "WARN" "journalctl not available — cannot check gateway logs"
|
|
333
|
+
fi
|
|
334
|
+
echo ""
|
|
335
|
+
|
|
336
|
+
###############################################################################
|
|
337
|
+
# 6. Fleet-Bus Transport Test (requires --live)
|
|
338
|
+
###############################################################################
|
|
339
|
+
if $LIVE_TEST; then
|
|
340
|
+
echo "=== 6. Live Transport Test (fleet/ping via callGateway) ==="
|
|
341
|
+
|
|
342
|
+
LIVE_SCRIPT=$(mktemp /tmp/fleet-live-ping-XXXXXX.mjs)
|
|
343
|
+
cat > "$LIVE_SCRIPT" << 'NODESCRIPT'
|
|
344
|
+
import { existsSync } from 'node:fs';
|
|
345
|
+
import { dirname, join } from 'node:path';
|
|
346
|
+
import { randomBytes } from 'node:crypto';
|
|
347
|
+
|
|
348
|
+
async function resolveCallGateway() {
|
|
349
|
+
const mainScript = process.argv[1] ?? '';
|
|
350
|
+
const home = process.env.HOME ?? '/root';
|
|
351
|
+
const packageNames = ['clawdbot', 'openclaw'];
|
|
352
|
+
const globalRoots = [
|
|
353
|
+
'/usr/lib/node_modules',
|
|
354
|
+
'/usr/local/lib/node_modules',
|
|
355
|
+
join(home, '.local', 'lib', 'node_modules'),
|
|
356
|
+
];
|
|
357
|
+
const paths = globalRoots.flatMap(root =>
|
|
358
|
+
packageNames.map(pkg => join(root, pkg, 'dist', 'gateway', 'call.js'))
|
|
359
|
+
);
|
|
360
|
+
for (const p of paths) {
|
|
361
|
+
if (!existsSync(p)) continue;
|
|
362
|
+
try {
|
|
363
|
+
const mod = await import(`file://${p}`);
|
|
364
|
+
if (typeof mod.callGateway === 'function') return mod.callGateway;
|
|
365
|
+
} catch {}
|
|
366
|
+
}
|
|
367
|
+
return null;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
try {
|
|
371
|
+
const callGateway = await resolveCallGateway();
|
|
372
|
+
if (!callGateway) {
|
|
373
|
+
console.log('RESULT:FAIL:callGateway not resolved — gateway/call.js not found');
|
|
374
|
+
process.exit(0);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
const msg = {
|
|
378
|
+
id: `fm-diag-${Date.now()}-${randomBytes(4).toString('hex')}`,
|
|
379
|
+
from: 'mighty-mark',
|
|
380
|
+
to: 'jessie',
|
|
381
|
+
method: 'fleet/ping',
|
|
382
|
+
params: { source: 'diagnostics', timestamp: new Date().toISOString() },
|
|
383
|
+
timestamp: new Date().toISOString(),
|
|
384
|
+
nonce: randomBytes(8).toString('hex'),
|
|
385
|
+
version: '1.0',
|
|
386
|
+
};
|
|
387
|
+
|
|
388
|
+
const serialized = '%%FLEET%%' + JSON.stringify(msg);
|
|
389
|
+
const start = Date.now();
|
|
390
|
+
|
|
391
|
+
const result = await callGateway({
|
|
392
|
+
method: 'agent',
|
|
393
|
+
params: {
|
|
394
|
+
agentId: 'jessie',
|
|
395
|
+
message: serialized,
|
|
396
|
+
deliver: false,
|
|
397
|
+
timeoutSeconds: 15,
|
|
398
|
+
label: 'fleet:fleet/ping',
|
|
399
|
+
},
|
|
400
|
+
timeoutMs: 20000,
|
|
401
|
+
mode: 'backend',
|
|
402
|
+
clientName: 'fleet-bus-diagnostics',
|
|
403
|
+
clientDisplayName: 'Fleet Diagnostics',
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
const duration = Date.now() - start;
|
|
407
|
+
const hasResponse = result && (typeof result === 'object' || typeof result === 'string');
|
|
408
|
+
|
|
409
|
+
if (hasResponse) {
|
|
410
|
+
const summary = typeof result === 'string' ? result.slice(0, 100) : JSON.stringify(result).slice(0, 100);
|
|
411
|
+
console.log(`RESULT:PASS:fleet/ping delivered to jessie (${duration}ms) — response: ${summary}`);
|
|
412
|
+
} else {
|
|
413
|
+
console.log(`RESULT:PASS:fleet/ping accepted by gateway (${duration}ms) — fire-and-forget`);
|
|
414
|
+
}
|
|
415
|
+
} catch (err) {
|
|
416
|
+
console.log(`RESULT:FAIL:${err.message?.slice(0, 150) || 'unknown error'}`);
|
|
417
|
+
}
|
|
418
|
+
process.exit(0);
|
|
419
|
+
NODESCRIPT
|
|
420
|
+
|
|
421
|
+
LIVE_RESULT=$(node "$LIVE_SCRIPT" 2>/dev/null || echo "RESULT:FAIL:node execution failed")
|
|
422
|
+
rm -f "$LIVE_SCRIPT"
|
|
423
|
+
|
|
424
|
+
LIVE_STATUS=$(echo "$LIVE_RESULT" | grep '^RESULT:' | head -1 | cut -d: -f2)
|
|
425
|
+
LIVE_DETAIL=$(echo "$LIVE_RESULT" | grep '^RESULT:' | head -1 | cut -d: -f3-)
|
|
426
|
+
|
|
427
|
+
if [ "$LIVE_STATUS" = "PASS" ]; then
|
|
428
|
+
check "Live ping (Mark → Jessie)" "PASS" "$LIVE_DETAIL"
|
|
429
|
+
elif [ "$LIVE_STATUS" = "FAIL" ]; then
|
|
430
|
+
check "Live ping (Mark → Jessie)" "FAIL" "$LIVE_DETAIL"
|
|
431
|
+
else
|
|
432
|
+
check "Live ping (Mark → Jessie)" "WARN" "unexpected result: $LIVE_RESULT"
|
|
433
|
+
fi
|
|
434
|
+
echo ""
|
|
435
|
+
fi
|
|
436
|
+
|
|
437
|
+
###############################################################################
|
|
438
|
+
# Summary
|
|
439
|
+
###############################################################################
|
|
440
|
+
echo "══════════════════════════════════════════════"
|
|
441
|
+
echo " Results: $PASS passed, $WARN warnings, $FAIL failed"
|
|
442
|
+
echo "══════════════════════════════════════════════"
|
|
443
|
+
echo ""
|
|
444
|
+
|
|
445
|
+
if [ $FAIL -gt 0 ]; then
|
|
446
|
+
echo " ⚡ Fleet-bus has $FAIL failing checks — investigate above."
|
|
447
|
+
exit 1
|
|
448
|
+
elif [ $WARN -gt 0 ]; then
|
|
449
|
+
echo " ⚠️ $WARN warnings — review above for details."
|
|
450
|
+
exit 0
|
|
451
|
+
else
|
|
452
|
+
echo " 🎯 Fleet-bus is healthy."
|
|
453
|
+
exit 0
|
|
454
|
+
fi
|