AI Newsletter Digest improvements: fixed QP soft line break decoding, URL extraction, and content cleaning
This commit is contained in:
331
skills/clawdtalk-client/scripts/approval.sh
Normal file
331
skills/clawdtalk-client/scripts/approval.sh
Normal file
@@ -0,0 +1,331 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# ClawdTalk Approval Requests
|
||||
#
|
||||
# Request user approval for sensitive actions during voice calls.
|
||||
# Sends push notification to user's phone and waits for response.
|
||||
#
|
||||
# Usage:
|
||||
# ./approval.sh request "Book flight LAX→JFK for $450"
|
||||
# ./approval.sh request "Send email to john@example.com" --details "Subject: Meeting tomorrow"
|
||||
# ./approval.sh request "Delete 50 files" --biometric --timeout 120
|
||||
# ./approval.sh status <request_id>
|
||||
#
|
||||
# Env vars: none
|
||||
# Endpoints: https://clawdtalk.com
|
||||
# Reads: skill-config.json
|
||||
# Writes: none
|
||||
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
SKILL_DIR="$(dirname "$SCRIPT_DIR")"
|
||||
CONFIG_FILE="$SKILL_DIR/skill-config.json"
|
||||
|
||||
# Default timeout for waiting on approval (seconds)
|
||||
DEFAULT_TIMEOUT=300
|
||||
POLL_INTERVAL=2
|
||||
|
||||
# Colors
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m'
|
||||
|
||||
check_config() {
|
||||
if [ ! -f "$CONFIG_FILE" ]; then
|
||||
echo -e "${RED}Error: Configuration not found. Run ./setup.sh first.${NC}" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Check if user has any registered devices (from ws-client cache)
|
||||
check_devices() {
|
||||
local status_file="$SKILL_DIR/.device-status"
|
||||
if [ -f "$status_file" ]; then
|
||||
local has_devices
|
||||
has_devices=$(jq -r '.has_devices // false' "$status_file" 2>/dev/null)
|
||||
if [ "$has_devices" = "false" ]; then
|
||||
return 1 # No devices
|
||||
fi
|
||||
fi
|
||||
return 0 # Has devices (or unknown - assume yes)
|
||||
}
|
||||
|
||||
get_config() {
|
||||
local key="$1"
|
||||
local value
|
||||
value=$(jq -r ".$key // empty" "$CONFIG_FILE" 2>/dev/null)
|
||||
|
||||
# Resolve ${ENV_VAR} references
|
||||
if [[ "$value" =~ ^\$\{([A-Z_][A-Z0-9_]*)\}$ ]]; then
|
||||
local env_var="${BASH_REMATCH[1]}"
|
||||
value="${!env_var:-$value}"
|
||||
fi
|
||||
|
||||
echo "$value"
|
||||
}
|
||||
|
||||
request_approval() {
|
||||
local action=""
|
||||
local details=""
|
||||
local require_biometric=false
|
||||
local timeout=$DEFAULT_TIMEOUT
|
||||
local wait=true
|
||||
|
||||
# Parse arguments
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--details)
|
||||
details="$2"
|
||||
shift 2
|
||||
;;
|
||||
--biometric)
|
||||
require_biometric=true
|
||||
shift
|
||||
;;
|
||||
--timeout)
|
||||
timeout="$2"
|
||||
shift 2
|
||||
;;
|
||||
--no-wait)
|
||||
wait=false
|
||||
shift
|
||||
;;
|
||||
-*)
|
||||
echo -e "${RED}Unknown option: $1${NC}" >&2
|
||||
exit 1
|
||||
;;
|
||||
*)
|
||||
if [ -z "$action" ]; then
|
||||
action="$1"
|
||||
fi
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ -z "$action" ]; then
|
||||
echo -e "${RED}Error: Action description required${NC}" >&2
|
||||
echo "Usage: $0 request \"Description of action\" [--details \"More info\"] [--biometric] [--timeout 300]" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if user has devices - skip API call entirely if not
|
||||
if ! check_devices; then
|
||||
echo "no_devices"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
local api_key
|
||||
api_key=$(get_config "api_key")
|
||||
local server
|
||||
server=$(get_config "server")
|
||||
server="${server:-https://clawdtalk.com}"
|
||||
|
||||
if [ -z "$api_key" ]; then
|
||||
echo -e "${RED}Error: No API key configured${NC}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Build request body
|
||||
local body
|
||||
body=$(jq -n \
|
||||
--arg action "$action" \
|
||||
--arg details "$details" \
|
||||
--argjson biometric "$require_biometric" \
|
||||
--argjson expires_in "$timeout" \
|
||||
'{
|
||||
action: $action,
|
||||
require_biometric: $biometric,
|
||||
expires_in: $expires_in
|
||||
} + (if $details != "" then {details: $details} else {} end)'
|
||||
)
|
||||
|
||||
# Create approval request
|
||||
local response
|
||||
response=$(curl -s -X POST "$server/v1/approvals" \
|
||||
-H "Authorization: Bearer $api_key" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "$body")
|
||||
|
||||
local request_id
|
||||
request_id=$(echo "$response" | jq -r '.request_id // empty')
|
||||
|
||||
if [ -z "$request_id" ]; then
|
||||
local error
|
||||
error=$(echo "$response" | jq -r '.message // .error // "Unknown error"')
|
||||
echo -e "${RED}Error creating approval request: $error${NC}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
local devices_notified
|
||||
devices_notified=$(echo "$response" | jq -r '.devices_notified // 0')
|
||||
|
||||
if [ "$devices_notified" -eq 0 ]; then
|
||||
# No devices registered — return immediately
|
||||
echo "no_devices"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ "$wait" = false ]; then
|
||||
# Just return the request ID, don't wait
|
||||
echo "$request_id"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Wait for response
|
||||
echo -e "Waiting for approval (timeout: ${timeout}s)..." >&2
|
||||
|
||||
local start_time
|
||||
start_time=$(date +%s)
|
||||
local end_time=$((start_time + timeout))
|
||||
|
||||
while true; do
|
||||
local current_time
|
||||
current_time=$(date +%s)
|
||||
|
||||
if [ "$current_time" -ge "$end_time" ]; then
|
||||
echo "timeout"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Check status
|
||||
local status_response
|
||||
status_response=$(curl -s "$server/v1/approvals/$request_id" \
|
||||
-H "Authorization: Bearer $api_key")
|
||||
|
||||
local status
|
||||
status=$(echo "$status_response" | jq -r '.status // "pending"')
|
||||
|
||||
case "$status" in
|
||||
approved)
|
||||
echo "approved"
|
||||
exit 0
|
||||
;;
|
||||
denied)
|
||||
echo "denied"
|
||||
exit 0
|
||||
;;
|
||||
expired)
|
||||
echo "expired"
|
||||
exit 0
|
||||
;;
|
||||
pending)
|
||||
# Still waiting
|
||||
sleep "$POLL_INTERVAL"
|
||||
;;
|
||||
*)
|
||||
echo -e "${RED}Unexpected status: $status${NC}" >&2
|
||||
echo "error"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
check_status() {
|
||||
local request_id="$1"
|
||||
|
||||
if [ -z "$request_id" ]; then
|
||||
echo -e "${RED}Error: Request ID required${NC}" >&2
|
||||
echo "Usage: $0 status <request_id>" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
local api_key
|
||||
api_key=$(get_config "api_key")
|
||||
local server
|
||||
server=$(get_config "server")
|
||||
server="${server:-https://clawdtalk.com}"
|
||||
|
||||
local response
|
||||
response=$(curl -s "$server/v1/approvals/$request_id" \
|
||||
-H "Authorization: Bearer $api_key")
|
||||
|
||||
local status
|
||||
status=$(echo "$response" | jq -r '.status // "unknown"')
|
||||
|
||||
echo "$status"
|
||||
}
|
||||
|
||||
list_approvals() {
|
||||
local status="${1:-pending}"
|
||||
|
||||
local api_key
|
||||
api_key=$(get_config "api_key")
|
||||
local server
|
||||
server=$(get_config "server")
|
||||
server="${server:-https://clawdtalk.com}"
|
||||
|
||||
curl -s "$server/v1/approvals?status=$status" \
|
||||
-H "Authorization: Bearer $api_key" | jq '.'
|
||||
}
|
||||
|
||||
show_help() {
|
||||
cat << 'EOF'
|
||||
ClawdTalk Approval Requests
|
||||
|
||||
Request user approval for sensitive actions. Sends a push notification
|
||||
to the user's phone and waits for their response.
|
||||
|
||||
COMMANDS:
|
||||
request <action> Create approval request and wait for response
|
||||
status <id> Check status of an existing request
|
||||
list [status] List approval requests (default: pending)
|
||||
|
||||
OPTIONS (for request):
|
||||
--details "text" Additional details to show user
|
||||
--biometric Require biometric auth (fingerprint/face) to approve
|
||||
--timeout <secs> How long to wait for response (default: 300)
|
||||
--no-wait Return request ID immediately, don't wait
|
||||
|
||||
EXAMPLES:
|
||||
# Simple approval
|
||||
./approval.sh request "Send email to boss@company.com"
|
||||
|
||||
# With details
|
||||
./approval.sh request "Book flight" --details "Delta 123, LAX→JFK, $450, Feb 15"
|
||||
|
||||
# Require biometric for sensitive action
|
||||
./approval.sh request "Transfer $5000 to external account" --biometric
|
||||
|
||||
# Quick check without waiting
|
||||
id=$(./approval.sh request "Delete files" --no-wait)
|
||||
# ... do other things ...
|
||||
status=$(./approval.sh status "$id")
|
||||
|
||||
OUTPUT:
|
||||
approved - User approved the action
|
||||
denied - User denied the action
|
||||
timeout - No response within timeout period
|
||||
expired - Request expired before user responded
|
||||
no_devices - User has no mobile app installed (no registered devices)
|
||||
pending - Still waiting (for status command)
|
||||
EOF
|
||||
}
|
||||
|
||||
# Main
|
||||
check_config
|
||||
|
||||
case "${1:-}" in
|
||||
request)
|
||||
shift
|
||||
request_approval "$@"
|
||||
;;
|
||||
status)
|
||||
shift
|
||||
check_status "$@"
|
||||
;;
|
||||
list)
|
||||
shift
|
||||
list_approvals "$@"
|
||||
;;
|
||||
help|--help|-h)
|
||||
show_help
|
||||
;;
|
||||
*)
|
||||
show_help
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
Reference in New Issue
Block a user