Forge
csharpdeeb25a2
1using System.Text.Json;
2using System.Text.Json.Serialization;
3
4namespace CascadeIDE.Features.Intercom.Transport;
5
6/// <summary>Состояние transport per workspace: <c>last_seq</c>, mapping thread→topic.</summary>
7public sealed class IntercomTransportStateStore
8{
9 private static readonly JsonSerializerOptions Json = new()
10 {
11 PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
12 WriteIndented = true,
13 };
14
15 private string? _workspaceRoot;
16 private long _lastSeq;
17 private readonly Dictionary<string, string> _threadTopics = new(StringComparer.OrdinalIgnoreCase);
18
19 public long LastSeq => _lastSeq;
20
21 public void SetWorkspaceRoot(string? workspaceRoot) => _workspaceRoot = workspaceRoot;
22
23 public void UpdateLastSeq(long seq)
24 {
25 if (seq <= _lastSeq)
26 return;
27 _lastSeq = seq;
28 Persist();
29 }
30
31 public bool TryGetTopicForThread(string threadId, out string topicId)
32 {
33 if (string.IsNullOrWhiteSpace(threadId))
34 return _threadTopics.TryGetValue("general", out topicId!);
35 return _threadTopics.TryGetValue(threadId, out topicId!);
36 }
37
38 public void SetTopicForThread(string threadId, string topicId)
39 {
40 var key = string.IsNullOrWhiteSpace(threadId) ? "general" : threadId;
41 _threadTopics[key] = topicId;
42 Persist();
43 }
44
45 public void Load()
46 {
47 _lastSeq = 0;
48 _threadTopics.Clear();
49 var path = StatePath();
50 if (path is null || !File.Exists(path))
51 return;
52
53 try
54 {
55 var json = File.ReadAllText(path);
56 var state = JsonSerializer.Deserialize<StateDto>(json, Json);
57 if (state is null)
58 return;
59 _lastSeq = state.LastSeq;
60 if (state.ThreadTopics is not null)
61 {
62 foreach (var kv in state.ThreadTopics)
63 _threadTopics[kv.Key] = kv.Value;
64 }
65 }
66 catch
67 {
68 _lastSeq = 0;
69 _threadTopics.Clear();
70 }
71 }
72
73 private void Persist()
74 {
75 var path = StatePath();
76 if (path is null)
77 return;
78
79 try
80 {
81 Directory.CreateDirectory(Path.GetDirectoryName(path)!);
82 var dto = new StateDto(_lastSeq, new Dictionary<string, string>(_threadTopics));
83 File.WriteAllText(path, JsonSerializer.Serialize(dto, Json));
84 }
85 catch
86 {
87 // best-effort
88 }
89 }
90
91 private string? StatePath()
92 {
93 if (string.IsNullOrWhiteSpace(_workspaceRoot))
94 return null;
95 return Path.Combine(_workspaceRoot, ".cascade-ide", "intercom-transport-state.json");
96 }
97
98 private sealed record StateDto(
99 [property: JsonPropertyName("last_seq")] long LastSeq,
100 [property: JsonPropertyName("thread_topics")] Dictionary<string, string>? ThreadTopics);
101}
102
View only · write via MCP/CIDE