fix(smartbadge-watch): handle null stdout from RMM and add diagnostic context

jq -r '.stdout' returns the literal string "null" when the API field is JSON
null, causing the RESULT: grep to fail and fire a false drift alert. Fixes:
- Use `.stdout // empty` so null becomes empty string
- Add FINAL_ST tracking; treat non-terminal status as INFRA-ERROR, not drift
- Increase poll window from 20x4s=80s to 30x4s=120s for slow commands
- Read .stderr and .exit_code; include them in the no-RESULT diagnostic

Live check 2026-06-02: KSTEENBB2025 is PASS (today's alert was a false positive).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-02 09:07:35 -07:00
parent 320824c9ef
commit 551aaf2fe1

View File

@@ -55,12 +55,26 @@ CID=$(curl -s -X POST "$RMM/api/agents/$KSTEEN/command" -H "Authorization: Beare
-H "Content-Type: application/json" -d "$PAYLOAD" | jq -r '.command_id // empty')
[ -z "$CID" ] && fail_exit "command dispatch failed"
for i in $(seq 1 20); do
ST=$(curl -s "$RMM/api/commands/$CID" -H "Authorization: Bearer $TOKEN" | jq -r '.status')
case "$ST" in completed|failed|cancelled|interrupted) break;; esac
FINAL_ST=""
for i in $(seq 1 30); do
FINAL_ST=$(curl -s "$RMM/api/commands/$CID" -H "Authorization: Bearer $TOKEN" | jq -r '.status')
case "$FINAL_ST" in completed|failed|cancelled|interrupted) break;; esac
sleep 4
done
OUT=$(curl -s "$RMM/api/commands/$CID" -H "Authorization: Bearer $TOKEN" | jq -r '.stdout')
CMD_JSON=$(curl -s "$RMM/api/commands/$CID" -H "Authorization: Bearer $TOKEN")
OUT=$(printf '%s' "$CMD_JSON" | jq -r '.stdout // empty')
ERR=$(printf '%s' "$CMD_JSON" | jq -r '.stderr // empty')
EXIT_CODE=$(printf '%s' "$CMD_JSON" | jq -r '.exit_code // empty')
# If the command never completed, treat as a transient infrastructure error (not drift)
if [[ "$FINAL_ST" != "completed" && "$FINAL_ST" != "failed" ]]; then
INFRA_REASON="command status=$FINAL_ST after poll window (exit_code=${EXIT_CODE:-?})"
echo "$(ts) INFRA-ERROR | $INFRA_REASON" >> "$LOG"
alert "[SMARTBADGE-WATCH] INFRA-ERROR KSTEENBB2025 | $INFRA_REASON - NOT counted as drift"
exit 0
fi
RESULT=$(printf '%s' "$OUT" | grep -m1 'RESULT:')
if printf '%s' "$RESULT" | grep -q 'RESULT: PASS'; then
@@ -68,10 +82,17 @@ if printf '%s' "$RESULT" | grep -q 'RESULT: PASS'; then
alert "[SMARTBADGE-WATCH] KSTEENBB2025 PASS - Datto Workplace v10 + SmartBadge _CC add-in aligned"
else
REASON=$(printf '%s' "$RESULT" | sed 's/^RESULT: //')
[ -z "$REASON" ] && REASON="no RESULT line returned: ${OUT:0:200}"
if [ -z "$REASON" ]; then
# stdout was null/empty - use stderr or command status as diagnostic
if [ -n "$ERR" ]; then
REASON="no RESULT line; status=$FINAL_ST exit_code=${EXIT_CODE:-?}; stderr: ${ERR:0:300}"
else
REASON="no RESULT line; status=$FINAL_ST exit_code=${EXIT_CODE:-?}; stdout: ${OUT:0:200}"
fi
fi
echo "$(ts) FAIL | $REASON" >> "$LOG"
alert "[SMARTBADGE-WATCH] *** DRIFT *** KSTEENBB2025 FAIL | $REASON"
alert "[SMARTBADGE-WATCH] DRIFT KSTEENBB2025 FAIL | $REASON"
curl -s -X POST "$COORD/messages" -H "Content-Type: application/json" --data-binary @- <<JSON >/dev/null 2>&1
{"from_session":"GURU-5070/smartbadge-watch","to_user":"mike","subject":"KSTEENBB2025 SmartBadge drift detected","body":"Daily watch found drift on Kristin Steen's machine: $REASON. Re-run the SmartBadge remediation (.claude/scripts ksteen-smartbadge-verify.ps1 + datto-fix.ps1).","priority":"high"}
{"from_session":"GURU-5070/smartbadge-watch","to_user":"mike","subject":"KSTEENBB2025 SmartBadge drift detected","body":"Daily watch found drift on Kristin Steen's machine: $REASON. Re-run the SmartBadge remediation (.claude/scripts/ksteen-smartbadge-verify.ps1 + ksteen-smartbadge-fix.ps1).","priority":"high"}
JSON
fi