/dev', 'systemctl stop', 'systemctl disable', 'shutdown', 'reboot', 'useradd', 'userdel', 'passwd ', '/etc/passwd', '/etc/shadow', 'iptables -F', 'kill -9']; foreach ($BLOCKED as $b) if (stripos($cmd, $b) !== false) return false; foreach ($SAFE as $p) { if (stripos(ltrim($cmd), $p) === 0) return true; if (stripos($cmd, " $p") !== false) return true; } return false; } function wpl_match_intent($message) { $message_lower = strtolower(trim($message)); if (strlen($message_lower) < 3) return null; $stubs = glob('/var/www/html/api/wired-pending/intent-opus4-*.php') ?: []; $best = null; $best_score = 0; $exact_matches = []; foreach ($stubs as $s) { $info = @include $s; if (!is_array($info)) continue; if (empty($info['triggers']) || empty($info['cmd'])) continue; if (!empty($info['status'])) { $_skip_statuses = ['PENDING_SECURITY_REVIEW', 'DEPRECATED_HARDCODED_20AVR_OPUS46', 'DEPRECATED', 'DISABLED']; if (in_array($info['status'], $_skip_statuses, true)) continue; } foreach ($info['triggers'] as $trigger) { $t = strtolower(trim($trigger)); if (strlen($t) < 4) continue; // Exact match - collect all candidates, do not return early if ($message_lower === $t) { $exact_matches[] = $info; break; // next stub } // Full trigger in message if (strpos($message_lower, $t) !== false) { // Compound score: trigger length + triggers_count + cmd_length/100 (prefer richer stubs) $richness = count($info['triggers']) + strlen($info['cmd']) / 100.0; $score = strlen($t) + $richness; if ($score > $best_score) { $best = $info; $best_score = $score; } } } } // Priority on exact match: richest stub wins (most triggers + longest cmd) if (!empty($exact_matches)) { usort($exact_matches, function($a, $b) { $score_a = count($a['triggers']) + strlen($a['cmd']) / 100.0; $score_b = count($b['triggers']) + strlen($b['cmd']) / 100.0; return $score_b <=> $score_a; }); return $exact_matches[0]; } return $best; } function wpl_execute_intent($info) { $name = $info['name'] ?? 'unknown'; $cmd = $info['cmd'] ?? ''; if (!wpl_is_safe($cmd)) { return [ 'ok' => false, 'name' => $name, 'text' => "Intent '$name' detecte mais commande non whitelistee (securite). Review manuel requis.", 'intent' => 'pending_unsafe' ]; } $start = microtime(true); $out = @shell_exec("timeout 20 $cmd 2>&1"); $ms = round((microtime(true) - $start) * 1000); $text = trim((string)$out); if (strlen($text) > 1500) $text = substr($text, 0, 1500) . "\n... (tronque)"; if ($text === '') $text = "Intent '$name' execute (cmd sans output)."; wpl_log("MATCH name=$name ms=$ms bytes=" . strlen($text)); return [ 'ok' => true, 'name' => $name, 'text' => $text, 'intent' => 'pending_' . $name, 'ms' => $ms ]; } // Main entry point - supports both standalone API and include function wevia_pending_loader($message) { $info = wpl_match_intent($message); if (!$info) return null; return wpl_execute_intent($info); } // If called directly as API if (basename($_SERVER['SCRIPT_NAME'] ?? '') === 'wevia-pending-loader.php') { $input = json_decode(file_get_contents("php://input"), true); $message = $input['message'] ?? ($_GET['message'] ?? ''); if (!$message) { http_response_code(400); echo json_encode(['error' => 'no message']); exit; } $r = wevia_pending_loader($message); if (!$r) { echo json_encode(['match' => false, 'message' => $message]); exit; } header("Content-Type: text/event-stream"); echo "data: " . json_encode([ 'type' => 'answer', 'text' => $r['text'], 'engine' => 'PendingLoader/' . $r['name'], 'intent' => $r['intent'] ], JSON_UNESCAPED_UNICODE) . "\n\n"; echo "data: [DONE]\n\n"; }