@blunking/codexlink 0.1.16 → 0.1.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -8,18 +8,40 @@ param(
8
8
  [string]$LogFile = ""
9
9
  )
10
10
 
11
- $ErrorActionPreference = "SilentlyContinue"
12
-
13
- if (-not ("BlunEmbeddedQueueTitleWatcher" -as [type])) {
14
- Add-Type -ReferencedAssemblies "System.Web.Extensions" -TypeDefinition @"
15
- using System;
11
+ $ErrorActionPreference = "SilentlyContinue"
12
+
13
+ function Stop-EmbeddedQueueTitleWatcher {
14
+ param([string]$TypeName)
15
+
16
+ try {
17
+ $type = $TypeName -as [type]
18
+ if ($null -eq $type) { return }
19
+
20
+ $flags = [System.Reflection.BindingFlags]::NonPublic -bor [System.Reflection.BindingFlags]::Static
21
+ $field = $type.GetField("_timer", $flags)
22
+ if ($null -eq $field) { return }
23
+
24
+ $timer = $field.GetValue($null)
25
+ if ($null -ne $timer) {
26
+ $timer.Dispose()
27
+ $field.SetValue($null, $null)
28
+ }
29
+ } catch {}
30
+ }
31
+
32
+ Stop-EmbeddedQueueTitleWatcher -TypeName "BlunEmbeddedQueueTitleWatcher"
33
+ Stop-EmbeddedQueueTitleWatcher -TypeName "BlunEmbeddedQueueTitleWatcherQueueOnlyV2"
34
+
35
+ if (-not ("BlunEmbeddedQueueTitleWatcherQueueOnlyV2" -as [type])) {
36
+ Add-Type -ReferencedAssemblies "System.Web.Extensions" -TypeDefinition @"
37
+ using System;
16
38
  using System.Collections;
17
39
  using System.Collections.Generic;
18
40
  using System.IO;
19
41
  using System.Threading;
20
42
  using System.Web.Script.Serialization;
21
43
 
22
- public static class BlunEmbeddedQueueTitleWatcher
44
+ public static class BlunEmbeddedQueueTitleWatcherQueueOnlyV2
23
45
  {
24
46
  private static Timer _timer;
25
47
  private static string _stateFile;
@@ -29,6 +51,8 @@ public static class BlunEmbeddedQueueTitleWatcher
29
51
  private static string _lastNotice = "";
30
52
  private static string _lastUiNotice = "";
31
53
  private static string _lastUiKind = "";
54
+ private static int _lastOverlayTop = -1;
55
+ private static int _lastOverlayWidth = 0;
32
56
  private static string _logFile = "";
33
57
  private static readonly object Gate = new object();
34
58
 
@@ -44,6 +68,8 @@ public static class BlunEmbeddedQueueTitleWatcher
44
68
  _lastNotice = "";
45
69
  _lastUiNotice = "";
46
70
  _lastUiKind = "";
71
+ _lastOverlayTop = -1;
72
+ _lastOverlayWidth = 0;
47
73
  WriteLog("START");
48
74
  TrySetTitle(_baseTitle);
49
75
  if (_timer != null)
@@ -131,21 +157,10 @@ public static class BlunEmbeddedQueueTitleWatcher
131
157
 
132
158
  try
133
159
  {
134
- var mode = Environment.GetEnvironmentVariable("BLUN_TELEGRAM_CONSOLE_UI_NOTICES") ?? "";
135
- var disabled = string.Equals(mode, "0", StringComparison.OrdinalIgnoreCase)
136
- || string.Equals(mode, "false", StringComparison.OrdinalIgnoreCase)
137
- || string.Equals(mode, "no", StringComparison.OrdinalIgnoreCase)
138
- || string.Equals(mode, "off", StringComparison.OrdinalIgnoreCase);
139
- var allowAll = string.Equals(mode, "all", StringComparison.OrdinalIgnoreCase)
140
- || string.Equals(mode, "1", StringComparison.OrdinalIgnoreCase)
141
- || string.Equals(mode, "true", StringComparison.OrdinalIgnoreCase)
142
- || string.Equals(mode, "yes", StringComparison.OrdinalIgnoreCase)
143
- || string.Equals(mode, "on", StringComparison.OrdinalIgnoreCase);
144
- if (!disabled && (allowAll || string.Equals(normalizedKind, "inbound", StringComparison.OrdinalIgnoreCase)))
145
- {
146
- Console.WriteLine("");
147
- Console.WriteLine("[Telegram] " + normalized);
148
- }
160
+ // Never write Telegram queue notices directly into the interactive
161
+ // Codex terminal. The Codex TUI owns that screen; direct writes can
162
+ // hide the composer and make Telegram messages look like plain
163
+ // console text instead of real injected messages.
149
164
  }
150
165
  catch
151
166
  {
@@ -157,6 +172,99 @@ public static class BlunEmbeddedQueueTitleWatcher
157
172
  WriteLog("UI " + normalizedKind + " " + Normalize(normalized, 220));
158
173
  }
159
174
 
175
+ private static void TryWriteAboveInput(string kind, string notice)
176
+ {
177
+ var prefix = string.Equals(kind, "outbound", StringComparison.OrdinalIgnoreCase)
178
+ ? "Telegram Reply: "
179
+ : "Telegram Queue: ";
180
+ var width = 100;
181
+ try
182
+ {
183
+ width = Math.Max(40, Console.WindowWidth);
184
+ }
185
+ catch
186
+ {
187
+ width = 100;
188
+ }
189
+
190
+ var line = Normalize(prefix + notice, Math.Max(20, width - 1));
191
+ if (line.Length < width - 1)
192
+ {
193
+ line = line + new string(' ', Math.Max(0, width - 1 - line.Length));
194
+ }
195
+
196
+ var left = 0;
197
+ var top = 0;
198
+ try
199
+ {
200
+ left = Console.CursorLeft;
201
+ top = Console.CursorTop;
202
+ }
203
+ catch
204
+ {
205
+ Console.WriteLine(prefix + notice);
206
+ return;
207
+ }
208
+
209
+ try
210
+ {
211
+ var bufferHeight = Math.Max(1, Console.BufferHeight);
212
+ var bufferWidth = Math.Max(1, Console.BufferWidth);
213
+ var windowTop = Math.Max(0, Console.WindowTop);
214
+ var windowHeight = Math.Max(1, Console.WindowHeight);
215
+ var bottomOffset = GetOverlayBottomOffset();
216
+ var targetTop = windowTop + Math.Max(0, windowHeight - bottomOffset);
217
+ targetTop = Math.Min(Math.Max(windowTop, targetTop), Math.Max(0, bufferHeight - 1));
218
+ var targetWidth = Math.Max(1, width - 1);
219
+
220
+ if (_lastOverlayTop >= 0 && _lastOverlayTop < bufferHeight)
221
+ {
222
+ Console.SetCursorPosition(0, _lastOverlayTop);
223
+ Console.Write(new string(' ', Math.Max(0, _lastOverlayWidth)));
224
+ }
225
+
226
+ Console.SetCursorPosition(0, targetTop);
227
+ var previousForeground = Console.ForegroundColor;
228
+ try
229
+ {
230
+ Console.ForegroundColor = ConsoleColor.DarkCyan;
231
+ Console.Write(line.Substring(0, Math.Min(line.Length, targetWidth)));
232
+ }
233
+ finally
234
+ {
235
+ Console.ForegroundColor = previousForeground;
236
+ }
237
+ _lastOverlayTop = targetTop;
238
+ _lastOverlayWidth = targetWidth;
239
+
240
+ var restoreTop = Math.Min(Math.Max(0, top), Math.Max(0, bufferHeight - 1));
241
+ var restoreLeft = Math.Min(Math.Max(0, left), Math.Max(0, width - 1));
242
+ Console.SetCursorPosition(restoreLeft, restoreTop);
243
+ }
244
+ catch
245
+ {
246
+ try
247
+ {
248
+ Console.SetCursorPosition(left, top);
249
+ }
250
+ catch
251
+ {
252
+ }
253
+ Console.WriteLine(prefix + notice);
254
+ }
255
+ }
256
+
257
+ private static int GetOverlayBottomOffset()
258
+ {
259
+ var raw = Environment.GetEnvironmentVariable("BLUN_TELEGRAM_CONSOLE_UI_BOTTOM_OFFSET") ?? "";
260
+ int parsed;
261
+ if (int.TryParse(raw, out parsed) && parsed >= 2 && parsed <= 12)
262
+ {
263
+ return parsed;
264
+ }
265
+ return 3;
266
+ }
267
+
160
268
  private static void BuildSnapshot(out string title, out string notice, out string uiNotice, out string uiKind)
161
269
  {
162
270
  title = _baseTitle;
@@ -192,31 +300,11 @@ public static class BlunEmbeddedQueueTitleWatcher
192
300
  }
193
301
 
194
302
  int total = 0;
195
- int pending = 0;
196
303
  int direct = 0;
197
304
  int ambient = 0;
198
305
  int escalation = 0;
199
306
  string preview = "";
200
307
 
201
- var pendingReplies = root.ContainsKey("pendingReplies") ? AsObjects(root["pendingReplies"]) : new object[0];
202
- foreach (var item in pendingReplies)
203
- {
204
- var entry = item as Dictionary<string, object>;
205
- if (entry == null || !IsOpenPendingReply(entry))
206
- {
207
- continue;
208
- }
209
-
210
- total += 1;
211
- pending += 1;
212
- CountRelevance(entry, ref direct, ref ambient, ref escalation);
213
-
214
- if (string.IsNullOrWhiteSpace(preview))
215
- {
216
- preview = FormatPreview(entry, 44, true);
217
- }
218
- }
219
-
220
308
  var queue = AsObjects(root["queue"]);
221
309
  foreach (var item in queue)
222
310
  {
@@ -253,15 +341,13 @@ public static class BlunEmbeddedQueueTitleWatcher
253
341
  }
254
342
 
255
343
  var parts = new List<string> { "Q:" + total.ToString() };
256
- if (pending > 0) parts.Add("P:" + pending.ToString());
257
344
  if (direct > 0) parts.Add("D:" + direct.ToString());
258
345
  if (ambient > 0) parts.Add("G:" + ambient.ToString());
259
346
  if (escalation > 0) parts.Add("E:" + escalation.ToString());
260
347
 
261
348
  var suffix = string.Join(" ", parts.ToArray());
262
349
  title = _baseTitle + " | " + suffix;
263
- var noticeParts = new List<string> { total.ToString() + " waiting" };
264
- if (pending > 0) noticeParts.Add("pending " + pending.ToString());
350
+ var noticeParts = new List<string> { total.ToString() + " queued" };
265
351
  if (direct > 0) noticeParts.Add("direct " + direct.ToString());
266
352
  if (ambient > 0) noticeParts.Add("group " + ambient.ToString());
267
353
  if (escalation > 0) noticeParts.Add("escalation " + escalation.ToString());
@@ -270,8 +356,8 @@ public static class BlunEmbeddedQueueTitleWatcher
270
356
  {
271
357
  return;
272
358
  }
273
- title = title + " | " + preview;
274
- notice = notice + " | " + preview;
359
+ title = title + " | " + preview;
360
+ notice = notice + " | " + preview;
275
361
  }
276
362
 
277
363
  private static object[] AsObjects(object value)
@@ -452,4 +538,4 @@ try {
452
538
  $ambientTtlMs = 600000
453
539
  }
454
540
 
455
- [BlunEmbeddedQueueTitleWatcher]::Start($StateFile, $BaseTitle, $ambientTtlMs, $LogFile)
541
+ [BlunEmbeddedQueueTitleWatcherQueueOnlyV2]::Start($StateFile, $BaseTitle, $ambientTtlMs, $LogFile)
@@ -191,28 +191,23 @@ function Get-QueueTitle {
191
191
  }
192
192
 
193
193
  $queued = @($State.queue | Where-Object { $_.status -eq "queued" })
194
- $pendingReplies = @(Get-OpenPendingReplies -State $State)
195
- $totalWaiting = $queued.Count + $pendingReplies.Count
196
- if ($totalWaiting -eq 0) {
194
+ if ($queued.Count -eq 0) {
197
195
  return $FallbackTitle
198
196
  }
199
197
 
200
198
  $directCount = @($queued | Where-Object { @("direct", "lane") -contains [string]$_.relevance }).Count
201
199
  $ambientCount = @($queued | Where-Object { [string]$_.relevance -eq "ambient" }).Count
202
200
  $escalationCount = @($queued | Where-Object { [string]$_.relevance -eq "escalation" }).Count
203
- $pendingCount = $pendingReplies.Count
204
201
 
205
202
  $nextDirect = @($queued | Where-Object { @("direct", "lane", "escalation") -contains [string]$_.relevance } | Select-Object -First 1)
206
203
  $nextAny = @($queued | Select-Object -First 1)
207
- $pendingFocus = @($pendingReplies | Sort-Object @{ Expression = { [string]$_.createdAt } } | Select-Object -First 1)
208
- $focus = if ($pendingFocus.Count -gt 0) { $pendingFocus[0] } elseif ($nextDirect.Count -gt 0) { $nextDirect[0] } elseif ($nextAny.Count -gt 0) { $nextAny[0] } else { $null }
209
- $preview = if ($focus) { Format-QueuePreview -Entry $focus -Pending:($pendingFocus.Count -gt 0) } else { "" }
204
+ $focus = if ($nextDirect.Count -gt 0) { $nextDirect[0] } elseif ($nextAny.Count -gt 0) { $nextAny[0] } else { $null }
205
+ $preview = if ($focus) { Format-QueuePreview -Entry $focus } else { "" }
210
206
 
211
- $parts = @("Q:$totalWaiting")
212
- if ($pendingCount -gt 0) { $parts += "P:$pendingCount" }
207
+ $parts = @("Q:$($queued.Count)")
213
208
  if ($directCount -gt 0) { $parts += "D:$directCount" }
214
209
  if ($ambientCount -gt 0) { $parts += "G:$ambientCount" }
215
- if ($escalationCount -gt 0) { $parts += "E:$escalationCount" }
210
+ if ($escalationCount -gt 0) { $parts += "E:$escalationCount" }
216
211
 
217
212
  $summary = ($parts -join " ")
218
213
  $waitReason = Get-QueueWaitReason -State $State -IdleCooldownMs $IdleCooldownMs
@@ -337,36 +332,26 @@ function Get-QueueNotice {
337
332
  }
338
333
 
339
334
  $queued = @($State.queue | Where-Object { $_.status -eq "queued" })
340
- $pendingReplies = @(Get-OpenPendingReplies -State $State)
341
- $totalWaiting = $queued.Count + $pendingReplies.Count
342
- if ($totalWaiting -eq 0) {
335
+ if ($queued.Count -eq 0) {
343
336
  return ""
344
337
  }
345
338
 
346
339
  $directCount = @($queued | Where-Object { @("direct", "lane") -contains [string]$_.relevance }).Count
347
340
  $ambientCount = @($queued | Where-Object { [string]$_.relevance -eq "ambient" }).Count
348
341
  $escalationCount = @($queued | Where-Object { [string]$_.relevance -eq "escalation" }).Count
349
- $pendingCount = $pendingReplies.Count
350
342
 
351
343
  $focus = @($queued | Where-Object { @("direct", "lane", "escalation") -contains [string]$_.relevance } | Select-Object -First 1)
352
344
  if ($focus.Count -eq 0) {
353
345
  $focus = @($queued | Select-Object -First 1)
354
346
  }
355
- $pendingFocus = @($pendingReplies | Sort-Object @{ Expression = { [string]$_.createdAt } } | Select-Object -First 1)
356
347
 
357
- $parts = @("$totalWaiting waiting")
358
- if ($pendingCount -gt 0) { $parts += "pending $pendingCount" }
348
+ $parts = @("$($queued.Count) queued")
359
349
  if ($directCount -gt 0) { $parts += "direct $directCount" }
360
350
  if ($ambientCount -gt 0) { $parts += "group $ambientCount" }
361
351
  if ($escalationCount -gt 0) { $parts += "escalation $escalationCount" }
362
352
  $parts += (Get-QueueWaitReason -State $State -IdleCooldownMs $IdleCooldownMs)
363
353
 
364
- if ($pendingFocus.Count -gt 0) {
365
- $preview = Format-QueuePreview -Entry $pendingFocus[0] -MaxLength 72 -Pending
366
- if ($preview) {
367
- $parts += $preview
368
- }
369
- } elseif ($focus.Count -gt 0) {
354
+ if ($focus.Count -gt 0) {
370
355
  $preview = Format-QueuePreview -Entry $focus[0] -MaxLength 72
371
356
  if ($preview) {
372
357
  $parts += $preview