@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.
- package/README.md +2 -0
- package/package.json +5 -4
- package/start-codex-agent.ps1 +28 -12
- package/telegram-console-input.ps1 +151 -0
- package/telegram-plugin/README.md +4 -3
- package/telegram-plugin/lib/app-server-client.js +141 -7
- package/telegram-plugin/lib/bridge.js +203 -15
- package/telegram-plugin/lib/codex.js +227 -7
- package/telegram-plugin/lib/env.js +1 -0
- package/telegram-plugin/lib/paths.js +2 -1
- package/telegram-plugin/lib/telegram.js +15 -0
- package/telegram-title-embed.ps1 +133 -47
- package/telegram-title-watcher.ps1 +8 -23
package/telegram-title-embed.ps1
CHANGED
|
@@ -8,18 +8,40 @@ param(
|
|
|
8
8
|
[string]$LogFile = ""
|
|
9
9
|
)
|
|
10
10
|
|
|
11
|
-
$ErrorActionPreference = "SilentlyContinue"
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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
|
|
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
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
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() + "
|
|
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
|
-
[
|
|
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
|
-
|
|
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
|
-
$
|
|
208
|
-
$
|
|
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:$
|
|
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
|
-
|
|
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 = @("$
|
|
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 ($
|
|
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
|