Initial commit
This commit is contained in:
140
sidecar/Program.cs
Normal file
140
sidecar/Program.cs
Normal file
@@ -0,0 +1,140 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
using ArtificersScrollwork.Sidecar.Parsing;
|
||||
using ArtificersScrollwork.Sidecar.Gumps;
|
||||
|
||||
/// <summary>
|
||||
/// Entry point for the ASW C# sidecar.
|
||||
/// Reads newline-delimited JSON requests from stdin, writes responses to stdout.
|
||||
///
|
||||
/// Request: { "id": "uuid", "command": "cmd_name", "args": { ... } }
|
||||
/// Response: { "id": "uuid", "ok": true, "data": { ... } }
|
||||
/// | { "id": "uuid", "ok": false, "error": "message" }
|
||||
/// </summary>
|
||||
|
||||
var options = new JsonSerializerOptions
|
||||
{
|
||||
PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower,
|
||||
WriteIndented = false,
|
||||
};
|
||||
|
||||
ScriptIndexer? indexer = null;
|
||||
CallChainTracer? tracer = null;
|
||||
|
||||
while (true)
|
||||
{
|
||||
var line = Console.ReadLine();
|
||||
if (line is null) break;
|
||||
|
||||
JsonObject? request;
|
||||
try { request = JsonSerializer.Deserialize<JsonObject>(line, options); }
|
||||
catch { continue; }
|
||||
if (request is null) continue;
|
||||
|
||||
var id = request["id"]?.GetValue<string>() ?? "";
|
||||
var command = request["command"]?.GetValue<string>() ?? "";
|
||||
var reqArgs = request["args"] as JsonObject ?? new JsonObject();
|
||||
|
||||
try
|
||||
{
|
||||
JsonNode? data = command switch
|
||||
{
|
||||
"index_scripts" => HandleIndexScripts(reqArgs),
|
||||
"get_class" => HandleGetClass(reqArgs),
|
||||
"trace_method" => HandleTraceMethod(reqArgs),
|
||||
"extract_gump" => HandleExtractGump(reqArgs),
|
||||
"search" => HandleSearch(reqArgs),
|
||||
_ => throw new InvalidOperationException($"Unknown command: {command}"),
|
||||
};
|
||||
|
||||
WriteResponse(id, true, data, null, options);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
WriteResponse(id, false, null, ex.Message, options);
|
||||
}
|
||||
}
|
||||
|
||||
// ── Command handlers ──────────────────────────────────────────────────────────
|
||||
|
||||
JsonNode HandleIndexScripts(JsonObject args)
|
||||
{
|
||||
var path = args["path"]?.GetValue<string>()
|
||||
?? throw new ArgumentException("Missing 'path' argument");
|
||||
|
||||
indexer = new ScriptIndexer();
|
||||
var index = indexer.IndexDirectory(path);
|
||||
|
||||
// Build a compilation for the tracer
|
||||
tracer = new CallChainTracer(indexer.Compilation
|
||||
?? throw new InvalidOperationException("Compilation not available after indexing"));
|
||||
|
||||
return JsonSerializer.SerializeToNode(index, options)!;
|
||||
}
|
||||
|
||||
JsonNode HandleGetClass(JsonObject args)
|
||||
{
|
||||
EnsureIndexer();
|
||||
var className = args["class"]?.GetValue<string>()
|
||||
?? throw new ArgumentException("Missing 'class' argument");
|
||||
|
||||
var info = indexer!.GetClass(className)
|
||||
?? throw new KeyNotFoundException($"Class '{className}' not found in index");
|
||||
|
||||
return JsonSerializer.SerializeToNode(info, options)!;
|
||||
}
|
||||
|
||||
JsonNode HandleTraceMethod(JsonObject args)
|
||||
{
|
||||
EnsureIndexer();
|
||||
|
||||
if (tracer is null)
|
||||
throw new InvalidOperationException("Tracer not initialized — send index_scripts first");
|
||||
|
||||
var className = args["class"]?.GetValue<string>()
|
||||
?? throw new ArgumentException("Missing 'class' argument");
|
||||
var methodName = args["method"]?.GetValue<string>()
|
||||
?? throw new ArgumentException("Missing 'method' argument");
|
||||
|
||||
var flowNode = tracer.TraceMethod(className, methodName);
|
||||
return JsonSerializer.SerializeToNode(flowNode, options)!;
|
||||
}
|
||||
|
||||
JsonNode HandleExtractGump(JsonObject args)
|
||||
{
|
||||
EnsureIndexer();
|
||||
throw new NotImplementedException("extract_gump not yet implemented (Phase 4)");
|
||||
}
|
||||
|
||||
JsonNode HandleSearch(JsonObject args)
|
||||
{
|
||||
EnsureIndexer();
|
||||
var query = args["query"]?.GetValue<string>()
|
||||
?? throw new ArgumentException("Missing 'query' argument");
|
||||
|
||||
var results = indexer!.Search(query);
|
||||
return JsonSerializer.SerializeToNode(results, options)!;
|
||||
}
|
||||
|
||||
void EnsureIndexer()
|
||||
{
|
||||
if (indexer is null)
|
||||
throw new InvalidOperationException("Scripts not indexed — send index_scripts first");
|
||||
}
|
||||
|
||||
static void WriteResponse(string id, bool ok, JsonNode? data, string? error, JsonSerializerOptions opts)
|
||||
{
|
||||
var response = new JsonObject
|
||||
{
|
||||
["id"] = id,
|
||||
["ok"] = ok,
|
||||
};
|
||||
|
||||
if (ok && data is not null)
|
||||
response["data"] = data;
|
||||
else if (!ok)
|
||||
response["error"] = error ?? "Unknown error";
|
||||
|
||||
Console.WriteLine(JsonSerializer.Serialize(response, opts));
|
||||
Console.Out.Flush();
|
||||
}
|
||||
Reference in New Issue
Block a user