@blunking/codexlink 0.1.9 → 0.1.12

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.
@@ -7,15 +7,15 @@ param(
7
7
 
8
8
  [string]$LogFile = ""
9
9
  )
10
-
11
- $ErrorActionPreference = "SilentlyContinue"
12
-
13
- if (-not ("BlunEmbeddedQueueTitleWatcher" -as [type])) {
14
- Add-Type -ReferencedAssemblies "System.Web.Extensions" -TypeDefinition @"
15
- using System;
16
- using System.Collections;
17
- using System.Collections.Generic;
18
- using System.IO;
10
+
11
+ $ErrorActionPreference = "SilentlyContinue"
12
+
13
+ if (-not ("BlunEmbeddedQueueTitleWatcher" -as [type])) {
14
+ Add-Type -ReferencedAssemblies "System.Web.Extensions" -TypeDefinition @"
15
+ using System;
16
+ using System.Collections;
17
+ using System.Collections.Generic;
18
+ using System.IO;
19
19
  using System.Threading;
20
20
  using System.Web.Script.Serialization;
21
21
 
@@ -48,14 +48,14 @@ public static class BlunEmbeddedQueueTitleWatcher
48
48
  TrySetTitle(_baseTitle);
49
49
  if (_timer != null)
50
50
  {
51
- _timer.Dispose();
52
- }
53
- _timer = new Timer(_ => Tick(), null, 0, 900);
54
- }
55
- }
56
-
57
- private static void Tick()
58
- {
51
+ _timer.Dispose();
52
+ }
53
+ _timer = new Timer(_ => Tick(), null, 0, 900);
54
+ }
55
+ }
56
+
57
+ private static void Tick()
58
+ {
59
59
  try
60
60
  {
61
61
  string title;
@@ -91,15 +91,15 @@ public static class BlunEmbeddedQueueTitleWatcher
91
91
  WriteLog("TITLE_ERROR");
92
92
  }
93
93
  }
94
-
95
- private static void TryWriteNotice(string notice)
96
- {
97
- var normalized = notice ?? "";
98
- if (string.Equals(normalized, _lastNotice, StringComparison.Ordinal))
99
- {
100
- return;
101
- }
102
-
94
+
95
+ private static void TryWriteNotice(string notice)
96
+ {
97
+ var normalized = notice ?? "";
98
+ if (string.Equals(normalized, _lastNotice, StringComparison.Ordinal))
99
+ {
100
+ return;
101
+ }
102
+
103
103
  try
104
104
  {
105
105
  // Do not write background queue notices into the interactive Codex
@@ -154,13 +154,13 @@ public static class BlunEmbeddedQueueTitleWatcher
154
154
  {
155
155
  return;
156
156
  }
157
-
158
- var raw = File.ReadAllText(_stateFile);
159
- if (string.IsNullOrWhiteSpace(raw))
160
- {
161
- return;
162
- }
163
-
157
+
158
+ var raw = File.ReadAllText(_stateFile);
159
+ if (string.IsNullOrWhiteSpace(raw))
160
+ {
161
+ return;
162
+ }
163
+
164
164
  var serializer = new JavaScriptSerializer();
165
165
  var root = serializer.DeserializeObject(raw) as Dictionary<string, object>;
166
166
  if (root == null || !root.ContainsKey("queue"))
@@ -210,20 +210,20 @@ public static class BlunEmbeddedQueueTitleWatcher
210
210
  var entry = item as Dictionary<string, object>;
211
211
  if (entry == null)
212
212
  {
213
- continue;
214
- }
215
-
216
- var status = GetString(entry, "status");
217
- if (!string.Equals(status, "queued", StringComparison.OrdinalIgnoreCase))
218
- {
219
- continue;
220
- }
221
-
222
- var relevance = GetString(entry, "relevance");
223
- if (string.Equals(relevance, "ambient", StringComparison.OrdinalIgnoreCase) && IsOlderThan(entry, _ambientTtlMs))
224
- {
225
- continue;
226
- }
213
+ continue;
214
+ }
215
+
216
+ var status = GetString(entry, "status");
217
+ if (!string.Equals(status, "queued", StringComparison.OrdinalIgnoreCase))
218
+ {
219
+ continue;
220
+ }
221
+
222
+ var relevance = GetString(entry, "relevance");
223
+ if (string.Equals(relevance, "ambient", StringComparison.OrdinalIgnoreCase) && IsOlderThan(entry, _ambientTtlMs))
224
+ {
225
+ continue;
226
+ }
227
227
 
228
228
  total += 1;
229
229
  CountRelevance(entry, ref direct, ref ambient, ref escalation);
@@ -236,8 +236,8 @@ public static class BlunEmbeddedQueueTitleWatcher
236
236
 
237
237
  if (total == 0)
238
238
  {
239
- return;
240
- }
239
+ return;
240
+ }
241
241
 
242
242
  var parts = new List<string> { "Q:" + total.ToString() };
243
243
  if (pending > 0) parts.Add("P:" + pending.ToString());
@@ -252,15 +252,15 @@ public static class BlunEmbeddedQueueTitleWatcher
252
252
  if (direct > 0) noticeParts.Add("direct " + direct.ToString());
253
253
  if (ambient > 0) noticeParts.Add("group " + ambient.ToString());
254
254
  if (escalation > 0) noticeParts.Add("escalation " + escalation.ToString());
255
- notice = string.Join(" | ", noticeParts.ToArray());
256
- if (string.IsNullOrWhiteSpace(preview))
257
- {
258
- return;
259
- }
260
- title = title + " | " + preview;
261
- notice = notice + " | " + preview;
262
- }
263
-
255
+ notice = string.Join(" | ", noticeParts.ToArray());
256
+ if (string.IsNullOrWhiteSpace(preview))
257
+ {
258
+ return;
259
+ }
260
+ title = title + " | " + preview;
261
+ notice = notice + " | " + preview;
262
+ }
263
+
264
264
  private static object[] AsObjects(object value)
265
265
  {
266
266
  var arr = value as object[];
@@ -273,16 +273,16 @@ public static class BlunEmbeddedQueueTitleWatcher
273
273
  {
274
274
  return list.ToArray();
275
275
  }
276
- return new object[0];
277
- }
278
-
276
+ return new object[0];
277
+ }
278
+
279
279
  private static string GetString(Dictionary<string, object> entry, string key)
280
280
  {
281
- if (!entry.ContainsKey(key) || entry[key] == null)
282
- {
283
- return "";
284
- }
285
- return Convert.ToString(entry[key]) ?? "";
281
+ if (!entry.ContainsKey(key) || entry[key] == null)
282
+ {
283
+ return "";
284
+ }
285
+ return Convert.ToString(entry[key]) ?? "";
286
286
  }
287
287
 
288
288
  private static void CountRelevance(Dictionary<string, object> entry, ref int direct, ref int ambient, ref int escalation)
@@ -337,7 +337,7 @@ public static class BlunEmbeddedQueueTitleWatcher
337
337
  }
338
338
  return state + text;
339
339
  }
340
-
340
+
341
341
  private static string Normalize(string value, int maxLength)
342
342
  {
343
343
  if (string.IsNullOrWhiteSpace(value))
@@ -350,11 +350,11 @@ public static class BlunEmbeddedQueueTitleWatcher
350
350
  {
351
351
  compact = compact.Replace(" ", " ");
352
352
  }
353
-
354
- if (compact.Length <= maxLength)
355
- {
356
- return compact;
357
- }
353
+
354
+ if (compact.Length <= maxLength)
355
+ {
356
+ return compact;
357
+ }
358
358
 
359
359
  return compact.Substring(0, Math.Max(0, maxLength - 3)).TrimEnd() + "...";
360
360
  }
@@ -384,21 +384,21 @@ public static class BlunEmbeddedQueueTitleWatcher
384
384
  .Replace("ü", "ü")
385
385
  .Replace("ß", "ß");
386
386
  }
387
-
387
+
388
388
  private static bool IsOlderThan(Dictionary<string, object> entry, long ttlMs)
389
389
  {
390
- if (!entry.ContainsKey("ts") || entry["ts"] == null)
391
- {
392
- return true;
393
- }
394
- try
395
- {
396
- var parsed = DateTimeOffset.Parse(Convert.ToString(entry["ts"]) ?? "");
397
- return (DateTimeOffset.UtcNow - parsed.ToUniversalTime()).TotalMilliseconds >= ttlMs;
398
- }
399
- catch
400
- {
401
- return true;
390
+ if (!entry.ContainsKey("ts") || entry["ts"] == null)
391
+ {
392
+ return true;
393
+ }
394
+ try
395
+ {
396
+ var parsed = DateTimeOffset.Parse(Convert.ToString(entry["ts"]) ?? "");
397
+ return (DateTimeOffset.UtcNow - parsed.ToUniversalTime()).TotalMilliseconds >= ttlMs;
398
+ }
399
+ catch
400
+ {
401
+ return true;
402
402
  }
403
403
  }
404
404
 
@@ -419,24 +419,24 @@ public static class BlunEmbeddedQueueTitleWatcher
419
419
  }
420
420
  "@
421
421
  }
422
-
423
- $ambientTtlMs = 600000
424
- try {
425
- $stateDir = Split-Path -Parent $StateFile
426
- $envPath = Join-Path $stateDir ".env"
427
- if (Test-Path $envPath) {
428
- foreach ($line in (Get-Content -Path $envPath)) {
429
- if (-not $line) { continue }
430
- if ($line.Trim().StartsWith("#")) { continue }
431
- $parts = $line -split "=", 2
432
- if ($parts.Count -ne 2) { continue }
433
- if ($parts[0].Trim() -eq "BLUN_TELEGRAM_AMBIENT_QUEUE_TTL_MS") {
434
- $ambientTtlMs = [int64]$parts[1].Trim()
435
- }
436
- }
437
- }
438
- } catch {
439
- $ambientTtlMs = 600000
440
- }
441
-
422
+
423
+ $ambientTtlMs = 600000
424
+ try {
425
+ $stateDir = Split-Path -Parent $StateFile
426
+ $envPath = Join-Path $stateDir ".env"
427
+ if (Test-Path $envPath) {
428
+ foreach ($line in (Get-Content -Path $envPath)) {
429
+ if (-not $line) { continue }
430
+ if ($line.Trim().StartsWith("#")) { continue }
431
+ $parts = $line -split "=", 2
432
+ if ($parts.Count -ne 2) { continue }
433
+ if ($parts[0].Trim() -eq "BLUN_TELEGRAM_AMBIENT_QUEUE_TTL_MS") {
434
+ $ambientTtlMs = [int64]$parts[1].Trim()
435
+ }
436
+ }
437
+ }
438
+ } catch {
439
+ $ambientTtlMs = 600000
440
+ }
441
+
442
442
  [BlunEmbeddedQueueTitleWatcher]::Start($StateFile, $BaseTitle, $ambientTtlMs, $LogFile)
@@ -6,18 +6,18 @@ param(
6
6
 
7
7
  [Parameter(Mandatory = $true)]
8
8
  [string]$RuntimeFile,
9
-
10
- [Parameter(Mandatory = $true)]
11
- [string]$StateFile,
12
-
13
- [Parameter(Mandatory = $true)]
14
- [string]$BaseTitle,
15
-
16
- [string]$LogFile = ""
17
- )
18
-
19
- $ErrorActionPreference = "SilentlyContinue"
20
-
9
+
10
+ [Parameter(Mandatory = $true)]
11
+ [string]$StateFile,
12
+
13
+ [Parameter(Mandatory = $true)]
14
+ [string]$BaseTitle,
15
+
16
+ [string]$LogFile = ""
17
+ )
18
+
19
+ $ErrorActionPreference = "SilentlyContinue"
20
+
21
21
  if (-not ("CodexLink.NativeMethods" -as [type])) {
22
22
  Add-Type -TypeDefinition @"
23
23
  using System;
@@ -45,28 +45,28 @@ namespace CodexLink
45
45
  }
46
46
  "@
47
47
  }
48
-
49
- function Try-ReadJson {
50
- param([string]$Path)
51
- if (-not (Test-Path $Path)) { return $null }
52
- try {
53
- $raw = Get-Content -Raw -Path $Path
54
- if ($null -eq $raw) { return $null }
55
- return ($raw -replace "^\uFEFF", "") | ConvertFrom-Json
56
- } catch {
57
- return $null
58
- }
59
- }
60
-
61
- function Write-WatcherLog {
62
- param([string]$Message)
63
- if (-not $LogFile) { return }
64
- try {
65
- Add-Content -Path $LogFile -Value (((Get-Date).ToUniversalTime().ToString("o")) + " " + $Message) -Encoding UTF8
66
- } catch {
67
- }
68
- }
69
-
48
+
49
+ function Try-ReadJson {
50
+ param([string]$Path)
51
+ if (-not (Test-Path $Path)) { return $null }
52
+ try {
53
+ $raw = Get-Content -Raw -Path $Path
54
+ if ($null -eq $raw) { return $null }
55
+ return ($raw -replace "^\uFEFF", "") | ConvertFrom-Json
56
+ } catch {
57
+ return $null
58
+ }
59
+ }
60
+
61
+ function Write-WatcherLog {
62
+ param([string]$Message)
63
+ if (-not $LogFile) { return }
64
+ try {
65
+ Add-Content -Path $LogFile -Value (((Get-Date).ToUniversalTime().ToString("o")) + " " + $Message) -Encoding UTF8
66
+ } catch {
67
+ }
68
+ }
69
+
70
70
  function Test-PidAlive {
71
71
  param([int]$ProcId)
72
72
  if ($ProcId -le 0) { return $false }
@@ -103,14 +103,14 @@ function Get-EffectiveAttachPid {
103
103
 
104
104
  return 0
105
105
  }
106
-
106
+
107
107
  function Normalize-Preview {
108
108
  param([string]$Value, [int]$MaxLength = 44)
109
- $text = [string]$Value
110
- $text = $text -replace "\s+", " "
111
- $text = $text.Trim()
112
- if (-not $text) { return "" }
113
- if ($text.Length -le $MaxLength) { return $text }
109
+ $text = [string]$Value
110
+ $text = $text -replace "\s+", " "
111
+ $text = $text.Trim()
112
+ if (-not $text) { return "" }
113
+ if ($text.Length -le $MaxLength) { return $text }
114
114
  return ($text.Substring(0, [Math]::Max(0, $MaxLength - 3)).TrimEnd() + "...")
115
115
  }
116
116
 
@@ -178,18 +178,18 @@ function Format-QueuePreview {
178
178
  }
179
179
  return "${prefix}${text}"
180
180
  }
181
-
181
+
182
182
  function Get-QueueTitle {
183
183
  param(
184
184
  [object]$State,
185
185
  [string]$FallbackTitle,
186
186
  [int]$IdleCooldownMs = 15000
187
187
  )
188
-
189
- if ($null -eq $State -or $null -eq $State.queue) {
190
- return $FallbackTitle
191
- }
192
-
188
+
189
+ if ($null -eq $State -or $null -eq $State.queue) {
190
+ return $FallbackTitle
191
+ }
192
+
193
193
  $queued = @($State.queue | Where-Object { $_.status -eq "queued" })
194
194
  $pendingReplies = @(Get-OpenPendingReplies -State $State)
195
195
  $totalWaiting = $queued.Count + $pendingReplies.Count
@@ -212,8 +212,8 @@ function Get-QueueTitle {
212
212
  if ($pendingCount -gt 0) { $parts += "P:$pendingCount" }
213
213
  if ($directCount -gt 0) { $parts += "D:$directCount" }
214
214
  if ($ambientCount -gt 0) { $parts += "G:$ambientCount" }
215
- if ($escalationCount -gt 0) { $parts += "E:$escalationCount" }
216
-
215
+ if ($escalationCount -gt 0) { $parts += "E:$escalationCount" }
216
+
217
217
  $summary = ($parts -join " ")
218
218
  $waitReason = Get-QueueWaitReason -State $State -IdleCooldownMs $IdleCooldownMs
219
219
  if ($preview) {
@@ -221,7 +221,7 @@ function Get-QueueTitle {
221
221
  }
222
222
  return "$FallbackTitle | $summary | $waitReason"
223
223
  }
224
-
224
+
225
225
  function Update-ConsoleTitle {
226
226
  param([string]$Title)
227
227
  try {
@@ -235,11 +235,11 @@ function Update-ConsoleTitle {
235
235
  $updated = [CodexLink.NativeMethods]::SetConsoleTitle($Title)
236
236
  [void][CodexLink.NativeMethods]::FreeConsole()
237
237
  return $updated
238
- } catch {
239
- return $false
240
- }
241
- }
242
-
238
+ } catch {
239
+ return $false
240
+ }
241
+ }
242
+
243
243
  function Write-ConsoleNotice {
244
244
  param([string]$Notice)
245
245
  if ($env:BLUN_TELEGRAM_CONSOLE_NOTICES -ne "1") {
@@ -253,15 +253,15 @@ function Write-ConsoleNotice {
253
253
  Write-WatcherLog ("WAIT attach_console_failed_notice target=" + $targetPid)
254
254
  return $false
255
255
  }
256
- $handle = [CodexLink.NativeMethods]::GetStdHandle(-11)
257
- if ($handle -eq [IntPtr]::Zero -or $handle -eq [IntPtr](-1)) {
258
- [void][CodexLink.NativeMethods]::FreeConsole()
259
- return $false
260
- }
261
- $line = "[CodexLink Queue] $Notice`r`n"
262
- [uint32]$written = 0
263
- $ok = [CodexLink.NativeMethods]::WriteConsole($handle, $line, [uint32]$line.Length, [ref]$written, [IntPtr]::Zero)
264
- [void][CodexLink.NativeMethods]::FreeConsole()
256
+ $handle = [CodexLink.NativeMethods]::GetStdHandle(-11)
257
+ if ($handle -eq [IntPtr]::Zero -or $handle -eq [IntPtr](-1)) {
258
+ [void][CodexLink.NativeMethods]::FreeConsole()
259
+ return $false
260
+ }
261
+ $line = "[CodexLink Queue] $Notice`r`n"
262
+ [uint32]$written = 0
263
+ $ok = [CodexLink.NativeMethods]::WriteConsole($handle, $line, [uint32]$line.Length, [ref]$written, [IntPtr]::Zero)
264
+ [void][CodexLink.NativeMethods]::FreeConsole()
265
265
  return $ok
266
266
  } catch {
267
267
  return $false
@@ -318,17 +318,17 @@ function Get-UiNoticeSnapshot {
318
318
  text = $text
319
319
  }
320
320
  }
321
-
321
+
322
322
  function Get-QueueNotice {
323
323
  param(
324
324
  [object]$State,
325
325
  [int]$IdleCooldownMs = 15000
326
326
  )
327
-
328
- if ($null -eq $State -or $null -eq $State.queue) {
329
- return ""
330
- }
331
-
327
+
328
+ if ($null -eq $State -or $null -eq $State.queue) {
329
+ return ""
330
+ }
331
+
332
332
  $queued = @($State.queue | Where-Object { $_.status -eq "queued" })
333
333
  $pendingReplies = @(Get-OpenPendingReplies -State $State)
334
334
  $totalWaiting = $queued.Count + $pendingReplies.Count
@@ -365,7 +365,7 @@ function Get-QueueNotice {
365
365
  $parts += $preview
366
366
  }
367
367
  }
368
-
368
+
369
369
  return ($parts -join " | ")
370
370
  }
371
371
 
@@ -396,48 +396,48 @@ $lastNotice = ""
396
396
  $lastUiKind = ""
397
397
  $lastUiNotice = ""
398
398
  Write-WatcherLog "START"
399
-
400
- while ($true) {
401
- if (-not (Test-PidAlive -ProcId $FrontendPid)) {
402
- Write-WatcherLog "EXIT frontend_dead"
403
- break
404
- }
405
-
406
- $runtime = Try-ReadJson -Path $RuntimeFile
407
- if ($null -eq $runtime) {
408
- Write-WatcherLog "WAIT runtime_missing"
409
- Start-Sleep -Milliseconds 300
410
- continue
411
- }
412
-
413
- $runtimeOwnerPid = 0
414
- try { $runtimeOwnerPid = [int]$runtime.frontend_host_pid } catch { $runtimeOwnerPid = 0 }
415
- if ($runtimeOwnerPid -ne $FrontendPid) {
416
- Write-WatcherLog ("EXIT owner_changed owner=" + $runtimeOwnerPid)
417
- break
418
- }
419
-
399
+
400
+ while ($true) {
401
+ if (-not (Test-PidAlive -ProcId $FrontendPid)) {
402
+ Write-WatcherLog "EXIT frontend_dead"
403
+ break
404
+ }
405
+
406
+ $runtime = Try-ReadJson -Path $RuntimeFile
407
+ if ($null -eq $runtime) {
408
+ Write-WatcherLog "WAIT runtime_missing"
409
+ Start-Sleep -Milliseconds 300
410
+ continue
411
+ }
412
+
413
+ $runtimeOwnerPid = 0
414
+ try { $runtimeOwnerPid = [int]$runtime.frontend_host_pid } catch { $runtimeOwnerPid = 0 }
415
+ if ($runtimeOwnerPid -ne $FrontendPid) {
416
+ Write-WatcherLog ("EXIT owner_changed owner=" + $runtimeOwnerPid)
417
+ break
418
+ }
419
+
420
420
  $state = Try-ReadJson -Path $StateFile
421
421
  if ($null -eq $state) {
422
422
  Write-WatcherLog "WAIT state_missing"
423
- Start-Sleep -Milliseconds 700
424
- continue
425
- }
426
-
423
+ Start-Sleep -Milliseconds 700
424
+ continue
425
+ }
426
+
427
427
  $title = Get-QueueTitle -State $state -FallbackTitle $BaseTitle -IdleCooldownMs $idleCooldownMs
428
428
  $notice = Get-QueueNotice -State $state -IdleCooldownMs $idleCooldownMs
429
429
  $ui = Get-UiNoticeSnapshot -State $state
430
430
  if ($title -ne $lastTitle) {
431
431
  $updated = Update-ConsoleTitle -Title $title
432
432
  Write-WatcherLog ("TITLE updated=" + $updated + " text=" + $title)
433
- $lastTitle = $title
434
- }
435
- if ($notice -ne $lastNotice) {
436
- if ($notice) {
437
- $noticeUpdated = Write-ConsoleNotice -Notice $notice
438
- Write-WatcherLog ("NOTICE updated=" + $noticeUpdated + " text=" + $notice)
439
- } else {
440
- Write-WatcherLog "NOTICE clear"
433
+ $lastTitle = $title
434
+ }
435
+ if ($notice -ne $lastNotice) {
436
+ if ($notice) {
437
+ $noticeUpdated = Write-ConsoleNotice -Notice $notice
438
+ Write-WatcherLog ("NOTICE updated=" + $noticeUpdated + " text=" + $notice)
439
+ } else {
440
+ Write-WatcherLog "NOTICE clear"
441
441
  }
442
442
  $lastNotice = $notice
443
443
  }