| 1 | using System.Diagnostics.CodeAnalysis; |
| 2 | using System.Text.Json; |
| 3 | |
| 4 | namespace CascadeIDE.Features.WebAiPortal.Application; |
| 5 | |
| 6 | /// <summary>Сбор JSON для моста: fenced <c>json-cascade</c> или голый объект с <c>command_id</c> (копипаст из UI чата без backtick).</summary> |
| 7 | public static class WebAiPortalBridgePayloadResolution |
| 8 | { |
| 9 | /// <summary>Вытащить <c>command_id</c> из уже разрешённой JSON-тела execute (до merge).</summary> |
| 10 | public static bool TryGetCommandId(string jsonPayload, [NotNullWhen(true)] out string? commandId) |
| 11 | { |
| 12 | commandId = null; |
| 13 | if (string.IsNullOrWhiteSpace(jsonPayload)) |
| 14 | return false; |
| 15 | try |
| 16 | { |
| 17 | using var doc = JsonDocument.Parse(jsonPayload); |
| 18 | if (doc.RootElement.ValueKind != JsonValueKind.Object |
| 19 | || !doc.RootElement.TryGetProperty("command_id", out var cid) |
| 20 | || cid.ValueKind != JsonValueKind.String) |
| 21 | return false; |
| 22 | var s = cid.GetString(); |
| 23 | if (string.IsNullOrWhiteSpace(s)) |
| 24 | return false; |
| 25 | commandId = s.Trim(); |
| 26 | return true; |
| 27 | } |
| 28 | catch (JsonException) |
| 29 | { |
| 30 | return false; |
| 31 | } |
| 32 | } |
| 33 | |
| 34 | public static bool TryResolvePayload(string raw, [NotNullWhen(true)] out string? json, out PayloadSourceHint source) |
| 35 | { |
| 36 | json = null; |
| 37 | source = default; |
| 38 | if (string.IsNullOrWhiteSpace(raw)) |
| 39 | return false; |
| 40 | |
| 41 | raw = raw.Trim(); |
| 42 | if (WebAiPortalJsonCascadeFence.TryExtractFirst(raw, out var fenced)) |
| 43 | { |
| 44 | json = fenced; |
| 45 | source = PayloadSourceHint.FencedMarkdown; |
| 46 | return true; |
| 47 | } |
| 48 | |
| 49 | if (TryParseBareExecuteCommand(raw, out var bare)) |
| 50 | { |
| 51 | json = bare; |
| 52 | source = PayloadSourceHint.BareJson; |
| 53 | return true; |
| 54 | } |
| 55 | |
| 56 | return false; |
| 57 | } |
| 58 | |
| 59 | public static bool TryParseBareExecuteCommand(string trimmedText, [NotNullWhen(true)] out string? json) |
| 60 | { |
| 61 | json = null; |
| 62 | var t = trimmedText.Trim(); |
| 63 | if (t.Length < 12 || !t.StartsWith('{') || !t.Contains("\"command_id\"", StringComparison.Ordinal)) |
| 64 | return false; |
| 65 | try |
| 66 | { |
| 67 | using var doc = JsonDocument.Parse(t); |
| 68 | if (doc.RootElement.ValueKind != JsonValueKind.Object) |
| 69 | return false; |
| 70 | if (!doc.RootElement.TryGetProperty("command_id", out var cidEl) || |
| 71 | cidEl.ValueKind != JsonValueKind.String) |
| 72 | return false; |
| 73 | if (string.IsNullOrWhiteSpace(cidEl.GetString())) |
| 74 | return false; |
| 75 | json = t; |
| 76 | return true; |
| 77 | } |
| 78 | catch (JsonException) |
| 79 | { |
| 80 | return false; |
| 81 | } |
| 82 | } |
| 83 | |
| 84 | public enum PayloadSourceHint |
| 85 | { |
| 86 | FencedMarkdown, |
| 87 | BareJson, |
| 88 | DomProbe, |
| 89 | } |
| 90 | } |
| 91 | |