From 5e2f822ad7038577eaf78393c7ba73417c500e6f Mon Sep 17 00:00:00 2001 From: OpenClaw Agent Date: Tue, 7 Apr 2026 21:30:00 +0000 Subject: [PATCH] Add .bms-actions logging (partial): auth login/refresh, create, note, delete. Need: update, assign, resolve. --- scripts/bms-auth.sh | 17 +++++++++ scripts/bms-logging.sh | 59 ++++++++++++++++++++++++++++++ scripts/bms-tickets.sh | 81 ++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 154 insertions(+), 3 deletions(-) mode change 100755 => 100644 scripts/bms-auth.sh create mode 100755 scripts/bms-logging.sh mode change 100755 => 100644 scripts/bms-tickets.sh diff --git a/scripts/bms-auth.sh b/scripts/bms-auth.sh old mode 100755 new mode 100644 index 88f7cf6..26074d1 --- a/scripts/bms-auth.sh +++ b/scripts/bms-auth.sh @@ -4,6 +4,10 @@ set -euo pipefail +# Import logging +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +source "${SCRIPT_DIR}/bms-logging.sh" + BMS_API_BASE="${BMS_API_BASE:-https://api.bms.kaseya.com}" BMS_TOKEN_FILE="${BMS_TOKEN_FILE:-$HOME/.bms_token.json}" @@ -76,6 +80,15 @@ cmd_auth_login() { save_token "$response" echo "Authenticated successfully. Token cached at $BMS_TOKEN_FILE" >&2 + + # Log successful login + local args_json + args_json=$(jq -n \ + --arg tenant "${BMS_TENANT}" \ + --arg username "${BMS_USERNAME}" \ + '{"tenant": $tenant, "username": $username}') + local result_json='{"success": true}' + log_action "auth.login" "$args_json" "$result_json" "success" } cmd_auth_refresh() { @@ -94,6 +107,10 @@ cmd_auth_refresh() { save_token "$response" echo "Token refreshed." >&2 + + # Log token refresh + local result_json='{"success": true}' + log_action "auth.refresh" "{}" "$result_json" "success" } cmd_auth_status() { diff --git a/scripts/bms-logging.sh b/scripts/bms-logging.sh new file mode 100755 index 0000000..c5bf66e --- /dev/null +++ b/scripts/bms-logging.sh @@ -0,0 +1,59 @@ +#!/usr/bin/env bash +# bms-logging.sh — Action logging for BMS skill +# Centralized logging of user-initiated actions for audit/review + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +# Log directory (can be overridden) +BMS_LOG_DIR="${BMS_LOG_DIR:-$HOME/.bms-actions}" + +# Ensure log directory exists +mkdir -p "$BMS_LOG_DIR" + +# Current log file (by date, UTC) +BMS_LOG_FILE="$BMS_LOG_DIR/$(date -u +%Y-%m-%d).jsonl" + +# Sanitize arguments: strip any sensitive values from a JSON object +# Usage: sanitized=$(sanitize_args '{"password":"secret","token":"abc"}') +sanitize_args() { + local input="$1" + # Remove known sensitive keys; preserve structure + jq 'del(.["BMS_PASSWORD"], .["BMS_MFA_CODE"], .["BMS_CLIENT_SECRET"], .["access_token"], .["refresh_token"], .["token"], .["Authorization"])' 2>/dev/null <<<"$input" || echo "$input" +} + +# Log an action +# Arguments: command, args_json, result_json, status (success|error) +log_action() { + local command="$1" + local args_json="${2:-{}}" + local result_json="${3:-{}}" + local status="${4:-success}" + + local timestamp + timestamp=$(date -u +%Y-%m-%dT%H:%M:%SZ) + + # Sanitize args and result + local safe_args safe_result + safe_args=$(sanitize_args "$args_json") + safe_result=$(sanitize_args "$result_json") + + # Build log entry as single JSON line + local entry + entry=$(jq -n \ + --arg ts "$timestamp" \ + --arg cmd "$command" \ + --argjson args "$safe_args" \ + --argjson result "$safe_result" \ + --arg stat "$status" \ + '{timestamp: $ts, command: $cmd, args: $args, result: $result, status: $stat}') + + # Append atomically + echo "$entry" >> "$BMS_LOG_FILE" +} + +# Get current log file path +get_log_path() { + echo "$BMS_LOG_FILE" +} diff --git a/scripts/bms-tickets.sh b/scripts/bms-tickets.sh old mode 100755 new mode 100644 index 6f6d487..63d785e --- a/scripts/bms-tickets.sh +++ b/scripts/bms-tickets.sh @@ -3,7 +3,10 @@ set -euo pipefail +# Import logging SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +source "${SCRIPT_DIR}/bms-logging.sh" + BMS_API_BASE="${BMS_API_BASE:-https://api.bms.kaseya.com}" # ─── Helpers ───────────────────────────────────────────────────────────────── @@ -272,10 +275,43 @@ cmd_create() { if [[ "$success" != "true" ]] || [[ -z "$ticket_id" ]] || [[ "$ticket_id" == "null" ]]; then echo "Create ticket failed or returned ambiguous response:" >&2 echo "$response" | jq . >&2 + # Log failure + local args_json result_json + args_json=$(jq -n \ + --arg title "$title" \ + --arg details "$details" \ + --argjson account_id "$account_id" \ + --argjson location_id "$location_id" \ + --argjson status_id "$status_id" \ + --argjson priority_id "$priority_id" \ + --argjson type_id "$type_id" \ + --argjson source_id "$source_id" \ + --argjson queue_id "${queue_id:-null}" \ + --argjson assignee_id "${assignee_id:-null}" \ + '{title: $title, details: $details, account_id: $account_id, location_id: $location_id, status_id: $status_id, priority_id: $priority_id, type_id: $type_id, source_id: $source_id, queue_id: $queue_id, assignee_id: $assignee_id}') + result_json=$(jq -n '{error: "creation_failed", response: ("$response" | fromjson? // "$response")}') + log_action "tickets.create" "$args_json" "$result_json" "error" exit 1 fi echo "Created ticket ID: ${ticket_id} — ${ticket_number:-N/A}" + # Log success + local args_json result_json + args_json=$(jq -n \ + --arg title "$title" \ + --arg details "$details" \ + --argjson account_id "$account_id" \ + --argjson location_id "$location_id" \ + --argjson status_id "$status_id" \ + --argjson priority_id "$priority_id" \ + --argjson type_id "$type_id" \ + --argjson source_id "$source_id" \ + --argjson queue_id "${queue_id:-null}" \ + --argjson assignee_id "${assignee_id:-null}" \ + '{title: $title, details: $details, account_id: $account_id, location_id: $location_id, status_id: $status_id, priority_id: $priority_id, type_id: $type_id, source_id: $source_id, queue_id: $queue_id, assignee_id: $assignee_id}') + result_json=$(jq -n --argjson tid "$ticket_id" --arg tn "${ticket_number:-}" '{ticket_id: $tid, ticket_number: $tn}') + log_action "tickets.create" "$args_json" "$result_json" "success" + } cmd_update() { @@ -362,7 +398,25 @@ cmd_note() { local response response=$(bms_curl POST "/v2/servicedesk/tickets/${ticket_id}/notes" -d "$body") - echo "$response" | jq -r '"Note added (ID: \(.Data.Id // .Id // "ok"))"' + local note_id + note_id=$(echo "$response" | jq -r '.Data.Id // .Id // empty') + if [[ -z "$note_id" || "$note_id" == "null" ]]; then + echo "Note add failed or returned ambiguous response:" >&2 + echo "$response" | jq . >&2 + # Log failure + local args_json result_json + args_json=$(jq -n --argjson ticket_id "$ticket_id" --arg message "$message" --argjson type_id "$type_id" '{ticket_id: $ticket_id, message: $message, type_id: $type_id}') + result_json=$(jq -n '{error: "note_add_failed", response: ("$response" | fromjson? // "$response")}') + log_action "tickets.note" "$args_json" "$result_json" "error" + exit 1 + fi + echo "Note added (ID: ${note_id})" + # Log success + local args_json result_json + args_json=$(jq -n --argjson ticket_id "$ticket_id" --arg message "$message" --argjson type_id "$type_id" '{ticket_id: $ticket_id, message: $message, type_id: $type_id}') + result_json=$(jq -n --argjson note_id "$note_id" '{note_id: $note_id}') + log_action "tickets.note" "$args_json" "$result_json" "success" + } cmd_assign() { @@ -447,15 +501,36 @@ cmd_delete() { local ids=("$@") [[ ${#ids[@]} -gt 0 ]] || die "Usage: bms tickets delete [id2 ...]" + local response="" if [[ ${#ids[@]} -eq 1 ]]; then - bms_curl DELETE "/v2/servicedesk/tickets/${ids[0]}" >/dev/null + response=$(bms_curl DELETE "/v2/servicedesk/tickets/${ids[0]}") echo "Deleted ticket ${ids[0]}" else local body body=$(printf '%s\n' "${ids[@]}" | jq -Rs 'split("\n") | map(select(. != "")) | map(tonumber) | {Ids: .}') - bms_curl DELETE "/v2/servicedesk/tickets" -d "$body" >/dev/null + response=$(bms_curl DELETE "/v2/servicedesk/tickets" -d "$body") echo "Deleted tickets: ${ids[*]}" fi + + # Check success (DELETE often returns { success: true } or empty) + local success + success=$(echo "$response" | jq -r '.success // .Success // ""') + if [[ "$success" != "true" ]] && [[ -n "$response" ]] && echo "$response" | jq -e . >/dev/null 2>&1; then + # If response is JSON but not success=true, treat as failure + echo "Delete operation may have failed:" >&2 + echo "$response" | jq . >&2 + local args_json result_json + args_json=$(jq -n --argjson ids "${ids}" '{ids: $ids}') + result_json=$(jq -n '{error: "delete_failed", response: ("$response" | fromjson? // "$response")}') + log_action "tickets.delete" "$args_json" "$result_json" "error" + exit 1 + fi + + # Log success + local args_json result_json + args_json=$(jq -n --argjson ids "${ids}" '{ids: $ids}') + result_json=$(jq -n --argjson deleted_ids "${ids}" '{deleted_ids: $deleted_ids}') + log_action "tickets.delete" "$args_json" "$result_json" "success" } # ─── Dispatch ────────────────────────────────────────────────────────────────