Protokoll #15687
| ID | 15687 |
|---|---|
| Zeitstempel | 2025-12-25 16:56:21.695964 |
| Client | root |
| IP | Warning: Undefined array key "request_ip" in /var/www/dev.campus.systemische-tools.de/src/View/protokoll/show.php on line 13 Deprecated: htmlspecialchars(): Passing null to parameter #1 ($string) of type string is deprecated in /var/www/dev.campus.systemische-tools.de/src/View/protokoll/show.php on line 13 |
| Modell | claude-sonnet-4-20250514 |
| Status | completed |
| Tokens | 3,141 (Input: 0, Output: 0) |
| Dauer | 150 ms |
| Request-Zeit | Warning: Undefined array key "request_timestamp" in /var/www/dev.campus.systemische-tools.de/src/View/protokoll/show.php on line 18 |
| Response-Zeit | - |
Warning: Undefined array key "error_message" in /var/www/dev.campus.systemische-tools.de/src/View/protokoll/show.php on line 22
Request
{
"event": "PreToolUse",
"tool_name": "Write",
"tool_input": {
"file_path": "\/var\/www\/tools\/ki-protokoll\/claude-hook\/log_parser.py",
"content": "#!\/usr\/bin\/env python3\n\"\"\"\nLog Parser Module\nVerantwortlich für das Parsen und Extrahieren von Daten aus Hook-Events\n\"\"\"\n\nimport json\nimport os\nimport re\nfrom pathlib import Path\nfrom typing import Dict, Any, List, Optional\n\n\n# Sicherheitseinstellungen\nMAX_FIELD_LENGTH = 10000\nSENSITIVE_KEY_PATTERNS = re.compile(\n r\"(?i)(password|pass|secret|token|apikey|api_key|authorization|auth|bearer|credential)\"\n)\nSENSITIVE_VALUE_PATTERNS = [\n re.compile(r\"(?i)\\bAKIA[0-9A-Z]{16}\\b\"),\n re.compile(r\"(?i)\\b(?:sk|rk|pk)[0-9A-Za-z]{20,}\\b\"),\n re.compile(r\"(?i)\\beyJ[a-zA-Z0-9-]{10,}\\.[a-zA-Z0-9_-]{10,}\\.[a-zA-Z0-9_-]{10,}\\b\")\n]\n\n\ndef sanitize_data(obj: Any) -> Any:\n \"\"\"Entfernt oder maskiert sensible Daten\"\"\"\n if isinstance(obj, dict):\n result = {}\n for key, value in obj.items():\n if SENSITIVE_KEY_PATTERNS.search(str(key)):\n result[key] = '[REDACTED]'\n else:\n result[key] = sanitize_data(value)\n return result\n elif isinstance(obj, list):\n return [sanitize_data(item) for item in obj]\n elif isinstance(obj, str):\n for pattern in SENSITIVE_VALUE_PATTERNS:\n if pattern.search(obj):\n return '[REDACTED]'\n if len(obj) > MAX_FIELD_LENGTH:\n import hashlib\n hash_value = hashlib.sha256(obj.encode('utf-8', errors='ignore')).hexdigest()[:16]\n return obj[:MAX_FIELD_LENGTH] + f'... [TRUNCATED-{hash_value}]'\n return obj\n return obj\n\n\ndef estimate_tokens(text: str) -> int:\n \"\"\"Grobe Token-Schätzung (4 Zeichen = 1 Token)\"\"\"\n if not text:\n return 0\n return max(1, len(text) \/\/ 4)\n\n\ndef get_client_ip() -> str:\n \"\"\"Ermittelt die Client-IP-Adresse\"\"\"\n ssh_client = os.environ.get('SSH_CLIENT', '')\n if ssh_client:\n return ssh_client.split()[0]\n ssh_connection = os.environ.get('SSH_CONNECTION', '')\n if ssh_connection:\n return ssh_connection.split()[0]\n return '127.0.0.1'\n\n\ndef parse_event_data(data: Dict[str, Any]) -> Dict[str, Any]:\n \"\"\"Extrahiert und strukturiert Event-Daten basierend auf Event-Typ\"\"\"\n event_name = data.get('hook_event_name', 'Unknown')\n request_data = {}\n\n if event_name == 'UserPromptSubmit':\n request_data = {\n 'event': event_name,\n 'prompt': sanitize_data(data.get('prompt', ''))\n }\n\n elif event_name == 'PreToolUse':\n tool_name = data.get('tool_name', '')\n tool_input = sanitize_data(data.get('tool_input', {}))\n request_data = {\n 'event': event_name,\n 'tool_name': tool_name,\n 'tool_input': tool_input\n }\n\n elif event_name == 'PostToolUse':\n tool_response = sanitize_data(data.get('tool_response', {}))\n request_data = {\n 'event': event_name,\n 'tool_name': data.get('tool_name', ''),\n 'tool_response': tool_response\n }\n\n elif event_name in ['Stop', 'SubagentStop']:\n request_data = {\n 'event': event_name,\n 'stop_hook_active': data.get('stop_hook_active', False)\n }\n\n elif event_name in ['SessionStart', 'SessionEnd']:\n request_data = {\n 'event': event_name,\n 'source': data.get('source', data.get('reason', ''))\n }\n\n else:\n request_data = {\n 'event': event_name,\n 'raw_data': sanitize_data(data)\n }\n\n return request_data\n\n\ndef get_conversation_pairs_from_session(session_id: str) -> List[Dict[str, str]]:\n \"\"\"Liest User-Prompt → Assistant-Response Paare aus der Session-JSONL\"\"\"\n pairs = []\n try:\n claude_dir = Path.home() \/ '.claude' \/ 'projects'\n\n for project_dir in claude_dir.iterdir():\n if not project_dir.is_dir():\n continue\n\n session_file = project_dir \/ f\"{session_id}.jsonl\"\n if session_file.exists():\n lines = session_file.read_text().strip().split('\\n')\n\n current_user_prompt = None\n for line in lines:\n try:\n entry = json.loads(line)\n entry_type = entry.get('type', '')\n\n # User-Message gefunden\n if entry_type == 'user':\n message = entry.get('message', {})\n content = message.get('content', '')\n if isinstance(content, str) and content.strip():\n current_user_prompt = content.strip()\n\n # Assistant-Message gefunden - pair mit letztem User-Prompt\n elif entry_type == 'assistant' and current_user_prompt:\n message = entry.get('message', {})\n content_parts = message.get('content', [])\n text_parts = []\n for part in content_parts:\n if isinstance(part, dict) and part.get('type') == 'text':\n text_parts.append(part.get('text', ''))\n elif isinstance(part, str):\n text_parts.append(part)\n\n if text_parts:\n assistant_response = '\\n'.join(text_parts)\n pairs.append({\n 'prompt': current_user_prompt,\n 'response': sanitize_data(assistant_response)\n })\n current_user_prompt = None # Reset für nächstes Paar\n\n except json.JSONDecodeError:\n continue\n\n break # Session gefunden, aufhören zu suchen\n\n return pairs\n\n except Exception:\n return []\n"
}
}
Response
{
"tool_response": {
"type": "create",
"filePath": "\/var\/www\/tools\/ki-protokoll\/claude-hook\/log_parser.py",
"content": "#!\/usr\/bin\/env python3\n\"\"\"\nLog Parser Module\nVerantwortlich für das Parsen und Extrahieren von Daten aus Hook-Events\n\"\"\"\n\nimport json\nimport os\nimport re\nfrom pathlib import Path\nfrom typing import Dict, Any, List, Optional\n\n\n# Sicherheitseinstellungen\nMAX_FIELD_LENGTH = 10000\nSENSITIVE_KEY_PATTERNS = re.compile(\n r\"(?i)(password|pass|secret|token|apikey|api_key|authorization|auth|bearer|credential)\"\n)\nSENSITIVE_VALUE_PATTERNS = [\n re.compile(r\"(?i)\\bAKIA[0-9A-Z]{16}\\b\"),\n re.compile(r\"(?i)\\b(?:sk|rk|pk)[0-9A-Za-z]{20,}\\b\"),\n re.compile(r\"(?i)\\beyJ[a-zA-Z0-9-]{10,}\\.[a-zA-Z0-9_-]{10,}\\.[a-zA-Z0-9_-]{10,}\\b\")\n]\n\n\ndef sanitize_data(obj: Any) -> Any:\n \"\"\"Entfernt oder maskiert sensible Daten\"\"\"\n if isinstance(obj, dict):\n result = {}\n for key, value in obj.items():\n if SENSITIVE_KEY_PATTERNS.search(str(key)):\n result[key] = '[REDACTED]'\n else:\n result[key] = sanitize_data(value)\n return result\n elif isinstance(obj, list):\n return [sanitize_data(item) for item in obj]\n elif isinstance(obj, str):\n for pattern in SENSITIVE_VALUE_PATTERNS:\n if pattern.search(obj):\n return '[REDACTED]'\n if len(obj) > MAX_FIELD_LENGTH:\n import hashlib\n hash_value = hashlib.sha256(obj.encode('utf-8', errors='ignore')).hexdigest()[:16]\n return obj[:MAX_FIELD_LENGTH] + f'... [TRUNCATED-{hash_value}]'\n return obj\n return obj\n\n\ndef estimate_tokens(text: str) -> int:\n \"\"\"Grobe Token-Schätzung (4 Zeichen = 1 Token)\"\"\"\n if not text:\n return 0\n return max(1, len(text) \/\/ 4)\n\n\ndef get_client_ip() -> str:\n \"\"\"Ermittelt die Client-IP-Adresse\"\"\"\n ssh_client = os.environ.get('SSH_CLIENT', '')\n if ssh_client:\n return ssh_client.split()[0]\n ssh_connection = os.environ.get('SSH_CONNECTION', '')\n if ssh_connection:\n return ssh_connection.split()[0]\n return '127.0.0.1'\n\n\ndef parse_event_data(data: Dict[str, Any]) -> Dict[str, Any]:\n \"\"\"Extrahiert und strukturiert Event-Daten basierend auf Event-Typ\"\"\"\n event_name = data.get('hook_event_name', 'Unknown')\n request_data = {}\n\n if event_name == 'UserPromptSubmit':\n request_data = {\n 'event': event_name,\n 'prompt': sanitize_data(data.get('prompt', ''))\n }\n\n elif event_name == 'PreToolUse':\n tool_name = data.get('tool_name', '')\n tool_input = sanitize_data(data.get('tool_input', {}))\n request_data = {\n 'event': event_name,\n 'tool_name': tool_name,\n 'tool_input': tool_input\n }\n\n elif event_name == 'PostToolUse':\n tool_response = sanitize_data(data.get('tool_response', {}))\n request_data = {\n 'event': event_name,\n 'tool_name': data.get('tool_name', ''),\n 'tool_response': tool_response\n }\n\n elif event_name in ['Stop', 'SubagentStop']:\n request_data = {\n 'event': event_name,\n 'stop_hook_active': data.get('stop_hook_active', False)\n }\n\n elif event_name in ['SessionStart', 'SessionEnd']:\n request_data = {\n 'event': event_name,\n 'source': data.get('source', data.get('reason', ''))\n }\n\n else:\n request_data = {\n 'event': event_name,\n 'raw_data': sanitize_data(data)\n }\n\n return request_data\n\n\ndef get_conversation_pairs_from_session(session_id: str) -> List[Dict[str, str]]:\n \"\"\"Liest User-Prompt → Assistant-Response Paare aus der Session-JSONL\"\"\"\n pairs = []\n try:\n claude_dir = Path.home() \/ '.claude' \/ 'projects'\n\n for project_dir in claude_dir.iterdir():\n if not project_dir.is_dir():\n continue\n\n session_file = project_dir \/ f\"{session_id}.jsonl\"\n if session_file.exists():\n lines = session_file.read_text().strip().split('\\n')\n\n current_user_prompt = None\n for line in lines:\n try:\n entry = json.loads(line)\n entry_type = entry.get('type', '')\n\n # User-Message gefunden\n if entry_type == 'user':\n message = entry.get('message', {})\n content = message.get('content', '')\n if isinstance(content, str) and content.strip():\n current_user_prompt = content.strip()\n\n # Assistant-Message gefunden - pair mit letztem User-Prompt\n elif entry_type == 'assistant' and current_user_prompt:\n message = entry.get('message', {})\n content_parts = message.get('content', [])\n text_parts = []\n for part in content_parts:\n if isinstance(part, dict) and part.get('type') == 'text':\n text_parts.append(part.get('text', ''))\n elif isinstance(part, str):\n text_parts.append(part)\n\n if text_parts:\n assistant_response = '\\n'.join(text_parts)\n pairs.append({\n 'prompt': current_user_prompt,\n 'response': sanitize_data(assistant_response)\n })\n current_user_prompt = None # Reset für nächstes Paar\n\n except json.JSONDecodeError:\n continue\n\n break # Session gefunden, aufhören zu suchen\n\n return pairs\n\n except Exception:\n return []\n",
"structuredPatch": [],
"originalFile": null
}
}