#!/usr/bin/env bash # telemetry_prelude — Wrapper de telemetria para funciones bash del registry. # Issue 0085c-bash. # # Uso 1 (manual): source bash/functions//.sh primero, despues source # este archivo, y todas las funciones bash del registry # quedaran envueltas con logging a call_monitor.operations.db. # # Uso 2 (auto): exportar FN_TELEMETRY=1 antes de sourcear; este prelude # detecta cada funcion definida que coincida con una funcion # del registry (bash/functions//.sh) y la # redefine como wrapper. # # Reglas: # - Idempotente: marca cada wrapper con flag _FN_T_WRAPPED_=1; no # re-envuelve si ya esta envuelta. # - No-op silencioso si BD no existe o INSERT falla. NUNCA aborta el caller. # - Solo guarda function_id, duration_ms, success, error_class. NUNCA args. if [ "${FN_TELEMETRY:-0}" != "1" ]; then return 0 2>/dev/null || exit 0 fi # ---- Resolve registry root ---- _fn_t_root() { if [ -n "${FN_REGISTRY_ROOT:-}" ] && [ -f "$FN_REGISTRY_ROOT/registry.db" ]; then printf '%s' "$FN_REGISTRY_ROOT" return 0 fi local d="${PWD}" while [ "$d" != "/" ]; do if [ -f "$d/registry.db" ]; then printf '%s' "$d" return 0 fi d=$(dirname "$d") done return 1 } # ---- Resolve operations.db ---- _FN_T_DB="" _fn_t_resolve_db() { if [ -n "$_FN_T_DB" ] && [ -f "$_FN_T_DB" ]; then return 0; fi local root root=$(_fn_t_root) || return 1 _FN_T_DB="$root/projects/fn_monitoring/apps/call_monitor/operations.db" if [ ! -f "$_FN_T_DB" ]; then _FN_T_DB="" return 1 fi return 0 } # ---- Log call ---- _fn_t_log() { local fn_id="$1" duration_ms="$2" success="$3" error_class="${4:-}" _fn_t_resolve_db || return 0 command -v sqlite3 >/dev/null 2>&1 || return 0 local sid="${CLAUDE_SESSION_ID:-}" local ts ts=$(date -u +%s) sid="${sid//\'/\'\'}" fn_id="${fn_id//\'/\'\'}" error_class="${error_class//\'/\'\'}" sqlite3 "$_FN_T_DB" "INSERT INTO calls (session_id, function_id, tool_used, args_hash, duration_ms, success, error_class, error_snippet, ts) VALUES ('$sid','$fn_id','bash_wrapper','',$duration_ms,$success,'$error_class','',$ts);" 2>/dev/null || true } # ---- Wrap a single function by name ---- _fn_t_wrap() { local orig="$1" fn_id="$2" # already wrapped? local guard guard="_FN_T_WRAPPED_${orig}" if [ "${!guard:-0}" = "1" ]; then return 0; fi # must exist as a function declare -F "$orig" >/dev/null 2>&1 || return 1 # capture original body, rename to _fn_t_orig_ local body body=$(declare -f "$orig") || return 1 # body starts with " ()"; prepend prefix to rename local renamed="_fn_t_orig_${body}" eval "$renamed" # define wrapper with the original name eval " ${orig}() { local _t0_ms _t1_ms _rc _dur _t0_ms=\$(date +%s%3N) _fn_t_orig_${orig} \"\$@\" _rc=\$? _t1_ms=\$(date +%s%3N) _dur=\$((_t1_ms - _t0_ms)) if [ \$_rc -eq 0 ]; then _fn_t_log \"${fn_id}\" \$_dur 1 \"\" else _fn_t_log \"${fn_id}\" \$_dur 0 \"exit_\$_rc\" fi return \$_rc } " eval "$guard=1" } # ---- Auto-wrap: walk bash/functions and wrap every function currently defined ---- _fn_t_autowrap() { local root root=$(_fn_t_root) || return 1 local f domain name fn_id while IFS= read -r f; do domain=$(basename "$(dirname "$f")") name=$(basename "$f" .sh) fn_id="${name}_bash_${domain}" if declare -F "$name" >/dev/null 2>&1; then _fn_t_wrap "$name" "$fn_id" fi done < <(find "$root/bash/functions" -type f -name '*.sh' 2>/dev/null) } # ---- Run autowrap on source ---- _fn_t_autowrap