Synapse-OS-Assistant-And-AI.../AI C2 Server/WebServer.cs
0% [█ █ █ █ █ █ █ █ █ █] 100% ace1e61285 First commit!
2026-06-04 20:36:13 -05:00

502 lines
23 KiB
C#

// File: WebServer.cs
using System;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Text.Json.Nodes;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace AI_C2_Server
{
public class WebServer
{
public static WebServer Instance { get; } = new WebServer();
private HttpListener _listener;
private bool _isRunning;
private static readonly HttpClient _httpClient = new HttpClient();
private WebServer() { }
public void Start(int port = 8080)
{
if (_isRunning) return;
_listener = new HttpListener();
_listener.Prefixes.Add($"http://localhost:{port}/");
_listener.Prefixes.Add($"http://127.0.0.1:{port}/");
try
{
_listener.Start();
_isRunning = true;
Task.Run(ListenLoop);
Logger.Log($"[SYSTEM] Initializing C2 Server on Port {port}...");
}
catch (Exception ex)
{
Logger.Log($"[ERROR] Failed to start server: {ex.Message}");
}
}
public void Stop()
{
if (!_isRunning) return;
_isRunning = false;
_listener?.Stop();
_listener?.Close();
Logger.Log("[SYSTEM] Server stopped.");
}
private async Task ListenLoop()
{
while (_isRunning)
{
try
{
var context = await _listener.GetContextAsync();
_ = Task.Run(() => ProcessRequestAsync(context));
}
catch (HttpListenerException)
{
// Thrown when listener is stopped, safe to ignore
}
catch (Exception ex)
{
Logger.Log($"[ERROR] Listener loop error: {ex.Message}");
}
}
}
private async Task ProcessRequestAsync(HttpListenerContext context)
{
var request = context.Request;
var response = context.Response;
// CORS Headers
response.AddHeader("Access-Control-Allow-Origin", "*");
response.AddHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
response.AddHeader("Access-Control-Allow-Headers", "Content-Type");
if (request.HttpMethod == "OPTIONS")
{
response.StatusCode = 200;
response.Close();
return;
}
try
{
string path = request.Url.AbsolutePath.TrimEnd('/');
// Extract instance ID for logging
string instanceId = "";
if (path.StartsWith("/api/relay/state/")) instanceId = path.Substring("/api/relay/state/".Length);
else if (path.StartsWith("/api/relay/command/")) instanceId = path.Substring("/api/relay/command/".Length);
else if (path.StartsWith("/api/relay/result/")) instanceId = path.Substring("/api/relay/result/".Length);
if (!string.IsNullOrEmpty(instanceId))
{
string platform = OperationCenter.Instance.GetInstancePlatform(instanceId);
Logger.Log($"[HTTP] [{platform} | {instanceId}] {request.HttpMethod} {path}");
}
else
{
Logger.Log($"[HTTP] {request.HttpMethod} {path}");
}
string body = "";
if (request.HasEntityBody)
{
using var reader = new StreamReader(request.InputStream, request.ContentEncoding);
body = await reader.ReadToEndAsync();
}
JsonNode payload = null;
if (!string.IsNullOrWhiteSpace(body))
{
try { payload = JsonNode.Parse(body); } catch { }
}
// --- RELAY ENDPOINTS ---
if (path.StartsWith("/api/relay/state/"))
{
OperationCenter.Instance.UpdateInstanceState(instanceId, payload as JsonObject);
SendJson(response, new JsonObject { ["status"] = "ok" });
}
else if (path.StartsWith("/api/relay/command/"))
{
OperationCenter.Instance.UpdateInstanceState(instanceId, new JsonObject());
var cmd = OperationCenter.Instance.GetPendingCommand(instanceId);
SendJson(response, cmd ?? new JsonObject());
}
else if (path.StartsWith("/api/relay/result/"))
{
OperationCenter.Instance.LogResult(instanceId, payload as JsonObject);
SendJson(response, new JsonObject { ["status"] = "acknowledged" });
}
// --- ADMIN ENDPOINTS ---
else if (path == "/api/admin/instances")
{
SendJson(response, OperationCenter.Instance.GetActiveInstances());
}
else if (path == "/api/admin/inject")
{
await HandleCommand(response, "GENERATE", payload?["prompt"]?.DeepClone(), GetBool(payload, "blocking", true), GetInt(payload, "timeout", 600), GetString(payload, "instance_id"));
}
else if (path == "/api/admin/history")
{
string id = request.QueryString["instance_id"];
await HandleCommand(response, "SCRAPE_HISTORY", null, true, 10, id);
}
else if (path == "/api/admin/session/analyze")
{
await HandleCommand(response, "ANALYZE_SESSION", null, true, 5, GetString(payload, "instance_id"));
}
else if (path == "/api/admin/upload_media")
{
if (payload?["data"] == null || payload?["mime"] == null)
{
SendJson(response, new JsonObject { ["error"] = "Invalid Media Payload" }, 400);
return;
}
await HandleCommand(response, "UPLOAD_MEDIA", payload, true, 30, GetString(payload, "instance_id"));
}
else if (path == "/api/admin/action/new_chat")
{
await HandleCommand(response, "NEW_CHAT", null, true, 30, GetString(payload, "instance_id"));
}
else if (path == "/api/admin/action/toggle_url_context")
{
await HandleCommand(response, "SET_URL_CONTEXT", GetBool(payload, "enabled", true), true, 30, GetString(payload, "instance_id"));
}
else if (path == "/api/admin/action/resolution")
{
await HandleCommand(response, "SET_RESOLUTION", GetString(payload, "level", "Default"), true, 30, GetString(payload, "instance_id"));
}
else if (path == "/api/admin/action/delete")
{
await HandleCommand(response, "DELETE_TURN", GetInt(payload, "index", -1), true, 30, GetString(payload, "instance_id"));
}
else if (path == "/api/admin/action/settings")
{
await HandleCommand(response, "SET_SETTINGS", payload, true, 45, GetString(payload, "instance_id"));
}
else if (path == "/api/admin/action/get_settings")
{
await HandleCommand(response, "GET_SETTINGS", null, true, 60, GetString(payload, "instance_id"));
}
else if (path == "/api/admin/action/get_chat_history")
{
string id = GetString(payload, "instance_id");
bool forceSync = GetBool(payload, "force_sync", false);
if (!forceSync && OperationCenter.Instance.HasChatHistory(id))
{
SendJson(response, new JsonObject
{
["status"] = "completed",
["output"] = OperationCenter.Instance.GetChatHistory(id).ToJsonString(),
["timestamp"] = DateTime.Now.ToString("o"),
["instance_id"] = id
});
return;
}
await HandleCommand(response, "GET_CHAT_HISTORY", null, true, 120, id);
}
else if (path == "/api/admin/action/create_shard")
{
await HandleCommand(response, "CREATE_SHARD", null, true, 600, GetString(payload, "instance_id"));
}
else if (path == "/api/admin/action/inject_shard")
{
if (payload?["shard_data"] == null)
{
SendJson(response, new JsonObject { ["error"] = "Missing shard_data" }, 400);
return;
}
await HandleCommand(response, "INJECT_SHARD", payload["shard_data"]?.DeepClone(), true, 600, GetString(payload, "instance_id"));
}
else if (path == "/api/admin/action/inject_doc")
{
if (payload?["content"] == null)
{
SendJson(response, new JsonObject { ["error"] = "Missing content" }, 400);
return;
}
await HandleCommand(response, "INJECT_DOC", payload, true, 30, GetString(payload, "instance_id"));
}
else if (path == "/api/admin/openai/system_instructions")
{
string instructions = GetString(payload, "instructions", "");
OperationCenter.Instance.OpenAISystemInstructions = instructions;
SendJson(response, new JsonObject { ["status"] = "completed", ["output"] = "System instructions updated." });
}
else if (path.StartsWith("/api/admin/result/"))
{
string taskId = path.Substring("/api/admin/result/".Length);
var res = OperationCenter.Instance.GetResult(taskId);
if (res != null)
{
SendJson(response, new JsonObject { ["status"] = "completed", ["result"] = res.DeepClone() });
}
else
{
SendJson(response, new JsonObject { ["status"] = "pending" }, 202);
}
}
else
{
SendJson(response, new JsonObject { ["error"] = "Not Found" }, 404);
}
}
catch (Exception ex)
{
Logger.Log($"[ERROR] Request processing failed: {ex.Message}");
SendJson(response, new JsonObject { ["error"] = "Internal Server Error" }, 500);
}
}
private async Task HandleCommand(HttpListenerResponse response, string commandType, JsonNode payload, bool blocking, int timeout, string instanceId)
{
if (string.IsNullOrEmpty(instanceId))
{
instanceId = OperationCenter.Instance.GetDefaultInstanceId();
}
// --- OPENAI API INTERCEPTION ---
if (OperationCenter.Instance.IsOpenAIInstance(instanceId))
{
if (commandType == "GENERATE")
{
try
{
OperationCenter.Instance.SetInstanceBusy(instanceId, true);
string prompt = payload?.ToString() ?? "";
// Auto-detect model if not cached
string modelName = OperationCenter.Instance.OpenAIModelName;
if (string.IsNullOrEmpty(modelName))
{
string modelsUrl = OperationCenter.Instance.OpenAIApiUrl.Replace("/chat/completions", "/models");
try
{
var modelReq = new HttpRequestMessage(HttpMethod.Get, modelsUrl);
if (!string.IsNullOrEmpty(OperationCenter.Instance.OpenAIApiKey))
{
modelReq.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", OperationCenter.Instance.OpenAIApiKey);
}
var modelRes = await _httpClient.SendAsync(modelReq);
if (modelRes.IsSuccessStatusCode)
{
string modelStr = await modelRes.Content.ReadAsStringAsync();
var modelJson = JsonNode.Parse(modelStr);
var dataArray = modelJson?["data"] as JsonArray;
if (dataArray != null && dataArray.Count > 0)
{
modelName = dataArray[0]?["id"]?.ToString();
OperationCenter.Instance.OpenAIModelName = modelName;
Logger.Log($"[OPENAI] Auto-detected model: {modelName}");
}
}
}
catch (Exception ex)
{
Logger.Log($"[OPENAI] Warning: Failed to fetch models from {modelsUrl}. Using 'default'. Error: {ex.Message}");
}
if (string.IsNullOrEmpty(modelName))
{
modelName = "default";
}
}
// Add user prompt to history
OperationCenter.Instance.AddChatHistory(instanceId, "User", prompt);
// Build messages array from history
var messagesArray = new JsonArray();
if (!string.IsNullOrEmpty(OperationCenter.Instance.OpenAISystemInstructions))
{
messagesArray.Add(new JsonObject { ["role"] = "system", ["content"] = OperationCenter.Instance.OpenAISystemInstructions });
}
var history = OperationCenter.Instance.GetChatHistory(instanceId);
foreach (var msg in history)
{
string role = msg["role"]?.ToString().ToLower() == "user" ? "user" : "assistant";
messagesArray.Add(new JsonObject { ["role"] = role, ["content"] = msg["text"]?.ToString() });
}
var openAiRequest = new JsonObject
{
["model"] = modelName,
["messages"] = messagesArray
};
var requestMsg = new HttpRequestMessage(HttpMethod.Post, OperationCenter.Instance.OpenAIApiUrl);
if (!string.IsNullOrEmpty(OperationCenter.Instance.OpenAIApiKey))
{
requestMsg.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", OperationCenter.Instance.OpenAIApiKey);
}
requestMsg.Content = new StringContent(openAiRequest.ToJsonString(), Encoding.UTF8, "application/json");
Logger.Log($"[OPENAI] Sending request to {OperationCenter.Instance.OpenAIApiUrl}");
var responseMsg = await _httpClient.SendAsync(requestMsg);
string resultStr = await responseMsg.Content.ReadAsStringAsync();
OperationCenter.Instance.SetInstanceBusy(instanceId, false);
if (!responseMsg.IsSuccessStatusCode)
{
Logger.Log($"[OPENAI ERROR] {responseMsg.StatusCode}: {resultStr}");
SendJson(response, new JsonObject { ["error"] = $"OpenAI API Error: {responseMsg.StatusCode}" }, 500);
return;
}
var jsonResult = JsonNode.Parse(resultStr);
string outputText = jsonResult?["choices"]?[0]?["message"]?["content"]?.ToString() ?? resultStr;
// Strip out <think>...</think> blocks generated by DeepSeek R1 models
outputText = Regex.Replace(outputText, @"<think>.*?</think>\s*", "", RegexOptions.Singleline | RegexOptions.IgnoreCase);
// Handle edge cases where the model omits the opening <think> tag but includes the closing </think> tag
outputText = Regex.Replace(outputText, @"^.*?<\/think>\s*", "", RegexOptions.Singleline | RegexOptions.IgnoreCase);
outputText = outputText.Trim();
// Add assistant response to history
OperationCenter.Instance.AddChatHistory(instanceId, "Model", outputText);
SendJson(response, new JsonObject
{
["status"] = "completed",
["output"] = outputText,
["timestamp"] = DateTime.Now.ToString("o"),
["instance_id"] = instanceId
}, 200);
return;
}
catch (Exception ex)
{
OperationCenter.Instance.SetInstanceBusy(instanceId, false);
Logger.Log($"[ERROR] OpenAI API call failed: {ex.Message}");
SendJson(response, new JsonObject { ["error"] = "OpenAI API Error: " + ex.Message }, 500);
return;
}
}
else if (commandType == "NEW_CHAT")
{
OperationCenter.Instance.ClearChatHistory(instanceId);
SendJson(response, new JsonObject { ["status"] = "completed", ["output"] = "Chat reset.", ["timestamp"] = DateTime.Now.ToString("o"), ["instance_id"] = instanceId }, 200);
return;
}
else if (commandType == "SHUTDOWN")
{
OperationCenter.Instance.ShutdownInstance(instanceId);
SendJson(response, new JsonObject { ["status"] = "completed", ["output"] = "Harness shut down.", ["timestamp"] = DateTime.Now.ToString("o"), ["instance_id"] = instanceId }, 200);
return;
}
else if (commandType == "GET_CHAT_HISTORY")
{
SendJson(response, new JsonObject
{
["status"] = "completed",
["output"] = OperationCenter.Instance.GetChatHistory(instanceId).ToJsonString(),
["timestamp"] = DateTime.Now.ToString("o"),
["instance_id"] = instanceId
}, 200);
return;
}
else
{
SendJson(response, new JsonObject { ["error"] = $"Command {commandType} not supported in OpenAI API mode.", ["instance_id"] = instanceId }, 400);
return;
}
}
// -------------------------------
var cmdData = new JsonObject
{
["type"] = commandType,
["payload"] = payload?.DeepClone()
};
string taskId = OperationCenter.Instance.QueueCommand(cmdData, instanceId);
if (!blocking)
{
SendJson(response, new JsonObject { ["status"] = "queued", ["task_id"] = taskId, ["instance_id"] = instanceId }, 200);
return;
}
DateTime startTime = DateTime.Now;
while ((DateTime.Now - startTime).TotalSeconds < timeout)
{
var result = OperationCenter.Instance.GetResult(taskId);
if (result != null)
{
SendJson(response, new JsonObject
{
["status"] = "completed",
["output"] = result["data"]?["output"]?.ToString(),
["timestamp"] = result["received_at"]?.ToString(),
["instance_id"] = result["instance_id"]?.ToString()
}, 200);
return;
}
await Task.Delay(500);
}
// Timeout handling: Cancel the task so it doesn't permanently block the FIFO queue
OperationCenter.Instance.CancelPendingTask(instanceId, taskId);
SendJson(response, new JsonObject { ["error"] = "Timeout", ["task_id"] = taskId, ["instance_id"] = instanceId }, 504);
}
private void SendJson(HttpListenerResponse response, JsonNode data, int statusCode = 200)
{
response.StatusCode = statusCode;
response.ContentType = "application/json";
byte[] buffer = Encoding.UTF8.GetBytes(data.ToJsonString());
response.ContentLength64 = buffer.Length;
try
{
response.OutputStream.Write(buffer, 0, buffer.Length);
}
catch { }
finally
{
response.Close();
}
}
private string GetString(JsonNode node, string key, string def = null)
{
if (node is JsonObject obj && obj.TryGetPropertyValue(key, out var val) && val != null)
return val.ToString();
return def;
}
private bool GetBool(JsonNode node, string key, bool def = false)
{
if (node is JsonObject obj && obj.TryGetPropertyValue(key, out var val) && val != null)
{
try { return val.GetValue<bool>(); } catch { return def; }
}
return def;
}
private int GetInt(JsonNode node, string key, int def = 0)
{
if (node is JsonObject obj && obj.TryGetPropertyValue(key, out var val) && val != null)
{
try { return val.GetValue<int>(); } catch { return def; }
}
return def;
}
}
}