using System.Reflection; using System.Text.Json; using System.Collections.Generic; using System.IO; using System.Threading.Tasks; using Scalar.AspNetCore; // Ensure S8n.Components.Packages assembly is loaded _ = typeof(S8n.Components.Basics.Calculator).Assembly; var builder = WebApplication.CreateBuilder(args); // Add services to the container. // Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi builder.Services.AddOpenApi(); var app = builder.Build(); // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { app.MapOpenApi(); app.MapScalarApiReference(); } app.UseHttpsRedirection(); var summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" }; app.MapGet("/weatherforecast", () => { var forecast = Enumerable.Range(1, 5).Select(index => new WeatherForecast ( DateOnly.FromDateTime(DateTime.Now.AddDays(index)), Random.Shared.Next(-20, 55), summaries[Random.Shared.Next(summaries.Length)] )) .ToArray(); return forecast; }) .WithName("GetWeatherForecast"); // Component registry endpoint app.MapGet("/api/components", () => { var components = new List(); var componentsPath = Path.Combine(Directory.GetCurrentDirectory(), "..", "components"); if (!Directory.Exists(componentsPath)) { // Fallback to absolute path from workspace root componentsPath = Path.Combine(Directory.GetCurrentDirectory(), "components"); } if (Directory.Exists(componentsPath)) { var jsonFiles = Directory.GetFiles(componentsPath, "*.json", SearchOption.AllDirectories); foreach (var file in jsonFiles) { try { var json = File.ReadAllText(file); var component = JsonSerializer.Deserialize(json, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); if (component != null) { components.Add(component); } } catch (Exception ex) { // Log error but continue Console.WriteLine($"Error reading component file {file}: {ex.Message}"); } } } return Results.Ok(components); }) .WithName("GetComponents"); // Runtime execution endpoint app.MapPost("/api/runtime/execute", async (RuntimeRequest request) => { try { // Get the type from the class name var type = Type.GetType(request.ClassName); if (type == null) { // Try to find in loaded assemblies foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) { type = assembly.GetType(request.ClassName); if (type != null) break; } } if (type == null) { return Results.BadRequest(new RuntimeResponse { Error = $"Class not found: {request.ClassName}" }); } // Create instance var instance = Activator.CreateInstance(type); if (instance == null) { return Results.BadRequest(new RuntimeResponse { Error = $"Failed to create instance of: {request.ClassName}" }); } // Get the method var method = type.GetMethod(request.MethodName); if (method == null) { return Results.BadRequest(new RuntimeResponse { Error = $"Method not found: {request.MethodName}" }); } // Parse inputs and invoke method var inputs = request.Inputs as JsonElement?; object? result; if (inputs.HasValue) { // Get method parameters var parameters = method.GetParameters(); var args = new List(); foreach (var param in parameters) { if (inputs.Value.TryGetProperty(param.Name!, out var property)) { var value = JsonSerializer.Deserialize(property.GetRawText(), param.ParameterType); args.Add(value); } else if (param.IsOptional) { args.Add(param.DefaultValue); } else { return Results.BadRequest(new RuntimeResponse { Error = $"Missing required parameter: {param.Name}" }); } } result = method.Invoke(instance, args.ToArray()); } else { result = method.Invoke(instance, null); } Console.Error.WriteLine($"Invocation result type: {result?.GetType()}"); // If method returns a Task, await it if (result is Task taskResult) { Console.Error.WriteLine($"Detected Task, awaiting..."); await taskResult.ConfigureAwait(false); Console.Error.WriteLine($"Task completed, status: {taskResult.Status}"); // If it's a Task, get the result via reflection var resultType = taskResult.GetType(); Console.Error.WriteLine($"Task result type: {resultType}"); if (typeof(Task).IsAssignableFrom(resultType)) { Console.Error.WriteLine($"Generic Task detected"); // Get the Result property var resultProperty = resultType.GetProperty("Result"); if (resultProperty != null) { result = resultProperty.GetValue(taskResult); Console.Error.WriteLine($"Result value: {result}"); Console.Error.WriteLine($"Result value type: {result?.GetType()}"); } else { Console.Error.WriteLine($"Result property not found"); result = null; } } else { // Task (non-generic) returns nothing Console.Error.WriteLine($"Non-generic Task, setting result to null"); result = null; } } Console.Error.WriteLine($"Final result: {result}"); return Results.Ok(new RuntimeResponse { Outputs = result }); } catch (TargetInvocationException tie) when (tie.InnerException != null) { return Results.BadRequest(new RuntimeResponse { Error = tie.InnerException.Message }); } catch (Exception ex) { return Results.BadRequest(new RuntimeResponse { Error = ex.Message }); } }) .WithName("ExecuteRuntime"); app.Run(); record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary) { public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); } record RuntimeRequest { public string ClassName { get; set; } = string.Empty; public string MethodName { get; set; } = string.Empty; public object? Inputs { get; set; } } record RuntimeResponse { public object? Outputs { get; set; } public string? Error { get; set; } } record ComponentDefinition { public string Code { get; set; } = string.Empty; public string Name { get; set; } = string.Empty; public string Description { get; set; } = string.Empty; public string Icon { get; set; } = string.Empty; public List Inputs { get; set; } = new(); public List Outputs { get; set; } = new(); public string Source { get; set; } = string.Empty; public string Class { get; set; } = string.Empty; public List Methods { get; set; } = new(); public string Gui { get; set; } = string.Empty; public string Category { get; set; } = string.Empty; public List Tags { get; set; } = new(); } record ComponentInput { public string Name { get; set; } = string.Empty; public string Type { get; set; } = string.Empty; public string Description { get; set; } = string.Empty; public List? Enum { get; set; } public bool Required { get; set; } = true; } record ComponentOutput { public string Name { get; set; } = string.Empty; public string Type { get; set; } = string.Empty; public string Description { get; set; } = string.Empty; }