@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.
@@ -1,135 +1,135 @@
1
- param(
2
- [string]$Profile = "default",
3
- [switch]$Json
4
- )
5
-
6
- $ErrorActionPreference = "Stop"
7
-
8
- function Read-DotEnvFile {
9
- param([string]$Path)
10
- $values = @{}
11
- if (-not (Test-Path $Path)) { return $values }
12
- foreach ($line in (Get-Content -Path $Path)) {
13
- if (-not $line) { continue }
14
- if ($line.Trim().StartsWith("#")) { continue }
15
- $parts = $line -split "=", 2
16
- if ($parts.Count -ne 2) { continue }
17
- $values[$parts[0].Trim()] = $parts[1]
18
- }
19
- return $values
20
- }
21
-
22
- function Add-Check {
23
- param(
24
- [System.Collections.Generic.List[object]]$List,
25
- [string]$Name,
26
- [string]$Status,
27
- [string]$Detail
28
- )
29
- $List.Add([pscustomobject]@{
30
- name = $Name
31
- status = $Status
32
- detail = $Detail
33
- }) | Out-Null
34
- }
35
-
36
- function Test-TelegramTokenFormat {
37
- param([string]$Value)
38
- if (-not $Value) { return $false }
39
- return $Value -match '^\d{6,}:[A-Za-z0-9_-]{20,}$'
40
- }
41
-
42
- function Test-AllowedChatIdsFormat {
43
- param([string]$Value)
44
- if (-not $Value) { return $false }
45
- $parts = @($Value -split "," | ForEach-Object { $_.Trim() } | Where-Object { $_ })
46
- if ($parts.Count -eq 0) { return $false }
47
- foreach ($part in $parts) {
48
- if ($part -notmatch '^-?\d+$') {
49
- return $false
50
- }
51
- }
52
- return $true
53
- }
54
-
55
- function Get-OverallStatus {
56
- param([System.Collections.Generic.List[object]]$Checks)
57
- if (@($Checks | Where-Object { $_.status -eq "fail" }).Count -gt 0) {
58
- return "fail"
59
- }
60
- if (@($Checks | Where-Object { $_.status -eq "warn" }).Count -gt 0) {
61
- return "warn"
62
- }
63
- return "ok"
64
- }
65
-
1
+ param(
2
+ [string]$Profile = "default",
3
+ [switch]$Json
4
+ )
5
+
6
+ $ErrorActionPreference = "Stop"
7
+
8
+ function Read-DotEnvFile {
9
+ param([string]$Path)
10
+ $values = @{}
11
+ if (-not (Test-Path $Path)) { return $values }
12
+ foreach ($line in (Get-Content -Path $Path)) {
13
+ if (-not $line) { continue }
14
+ if ($line.Trim().StartsWith("#")) { continue }
15
+ $parts = $line -split "=", 2
16
+ if ($parts.Count -ne 2) { continue }
17
+ $values[$parts[0].Trim()] = $parts[1]
18
+ }
19
+ return $values
20
+ }
21
+
22
+ function Add-Check {
23
+ param(
24
+ [System.Collections.Generic.List[object]]$List,
25
+ [string]$Name,
26
+ [string]$Status,
27
+ [string]$Detail
28
+ )
29
+ $List.Add([pscustomobject]@{
30
+ name = $Name
31
+ status = $Status
32
+ detail = $Detail
33
+ }) | Out-Null
34
+ }
35
+
36
+ function Test-TelegramTokenFormat {
37
+ param([string]$Value)
38
+ if (-not $Value) { return $false }
39
+ return $Value -match '^\d{6,}:[A-Za-z0-9_-]{20,}$'
40
+ }
41
+
42
+ function Test-AllowedChatIdsFormat {
43
+ param([string]$Value)
44
+ if (-not $Value) { return $false }
45
+ $parts = @($Value -split "," | ForEach-Object { $_.Trim() } | Where-Object { $_ })
46
+ if ($parts.Count -eq 0) { return $false }
47
+ foreach ($part in $parts) {
48
+ if ($part -notmatch '^-?\d+$') {
49
+ return $false
50
+ }
51
+ }
52
+ return $true
53
+ }
54
+
55
+ function Get-OverallStatus {
56
+ param([System.Collections.Generic.List[object]]$Checks)
57
+ if (@($Checks | Where-Object { $_.status -eq "fail" }).Count -gt 0) {
58
+ return "fail"
59
+ }
60
+ if (@($Checks | Where-Object { $_.status -eq "warn" }).Count -gt 0) {
61
+ return "warn"
62
+ }
63
+ return "ok"
64
+ }
65
+
66
66
  function Write-DoctorReport {
67
- param(
68
- [object]$Result,
69
- [string]$TokenSource,
70
- [string]$AllowedChatSource
71
- )
72
-
73
- $emoji = switch ($Result.overall) {
74
- "ok" { "[OK]" }
75
- "warn" { "[WARN]" }
76
- default { "[FAIL]" }
77
- }
78
-
79
- Write-Host ""
80
- Write-Host "CodexLink Telegram Doctor $emoji" -ForegroundColor Cyan
81
- Write-Host "Profil: $($Result.profile)"
82
- Write-Host "State-Ordner: $($Result.state_dir)"
83
- Write-Host ""
84
-
85
- foreach ($check in $Result.checks) {
86
- $prefix = switch ($check.status) {
87
- "ok" { "[OK]" }
88
- "warn" { "[WARN]" }
89
- default { "[FAIL]" }
90
- }
91
- $color = switch ($check.status) {
92
- "ok" { "Green" }
93
- "warn" { "Yellow" }
94
- default { "Red" }
95
- }
96
- Write-Host "$prefix $($check.name): $($check.detail)" -ForegroundColor $color
97
- }
98
-
99
- Write-Host ""
100
- if ($TokenSource) {
101
- Write-Host "Bot-Token gefunden aus: $TokenSource" -ForegroundColor DarkGray
102
- }
103
- if ($AllowedChatSource) {
104
- Write-Host "Erlaubte Chat-ID(s) gefunden aus: $AllowedChatSource" -ForegroundColor DarkGray
105
- }
106
-
107
- $failed = @($Result.checks | Where-Object { $_.status -eq "fail" })
108
- if ($failed.Count -gt 0) {
109
- Write-Host ""
110
- Write-Host "Was jetzt fehlt:" -ForegroundColor Yellow
111
- foreach ($item in $failed) {
112
- switch ($item.name) {
113
- "bot_token" { Write-Host " - Telegram Bot Token fehlt. Starte: blun-codex --profile $($Result.profile) telegram-setup" }
114
- "allowed_chat_ids" { Write-Host " - Erlaubte Chat-ID(s) fehlen. Starte: blun-codex --profile $($Result.profile) telegram-setup" }
115
- "state_dir" { Write-Host " - Der lokale Telegram-State-Ordner fehlt noch. Ein Setup-Lauf legt ihn automatisch an." }
116
- "profile_file" { Write-Host " - Das angegebene Profil existiert nicht." }
117
- "node" { Write-Host " - Node.js fehlt in PATH." }
118
- "codex" { Write-Host " - Der lokale codex-Befehl fehlt in PATH." }
119
- "telegram_plugin_root" { Write-Host " - Der Telegram-Plugin-Ordner konnte nicht gefunden werden." }
120
- default { Write-Host " - $($item.detail)" }
121
- }
122
- }
123
- }
124
-
125
- if ($Result.overall -eq "ok") {
126
- Write-Host ""
127
- Write-Host "Telegram ist sauber eingerichtet." -ForegroundColor Green
128
- Write-Host "Starten: blun-codex --profile $($Result.profile) telegram-plugin"
129
- } elseif ($Result.overall -eq "warn") {
130
- Write-Host ""
131
- Write-Host "Die Grundkonfiguration steht, aber es gibt noch Laufzeit-Hinweise." -ForegroundColor Yellow
132
- Write-Host "Das ist oft normal, wenn Telegram noch nicht aktiv gestartet wurde oder noch keine Nachricht durchlief."
67
+ param(
68
+ [object]$Result,
69
+ [string]$TokenSource,
70
+ [string]$AllowedChatSource
71
+ )
72
+
73
+ $emoji = switch ($Result.overall) {
74
+ "ok" { "[OK]" }
75
+ "warn" { "[WARN]" }
76
+ default { "[FAIL]" }
77
+ }
78
+
79
+ Write-Host ""
80
+ Write-Host "CodexLink Telegram Doctor $emoji" -ForegroundColor Cyan
81
+ Write-Host "Profil: $($Result.profile)"
82
+ Write-Host "State-Ordner: $($Result.state_dir)"
83
+ Write-Host ""
84
+
85
+ foreach ($check in $Result.checks) {
86
+ $prefix = switch ($check.status) {
87
+ "ok" { "[OK]" }
88
+ "warn" { "[WARN]" }
89
+ default { "[FAIL]" }
90
+ }
91
+ $color = switch ($check.status) {
92
+ "ok" { "Green" }
93
+ "warn" { "Yellow" }
94
+ default { "Red" }
95
+ }
96
+ Write-Host "$prefix $($check.name): $($check.detail)" -ForegroundColor $color
97
+ }
98
+
99
+ Write-Host ""
100
+ if ($TokenSource) {
101
+ Write-Host "Bot-Token gefunden aus: $TokenSource" -ForegroundColor DarkGray
102
+ }
103
+ if ($AllowedChatSource) {
104
+ Write-Host "Erlaubte Chat-ID(s) gefunden aus: $AllowedChatSource" -ForegroundColor DarkGray
105
+ }
106
+
107
+ $failed = @($Result.checks | Where-Object { $_.status -eq "fail" })
108
+ if ($failed.Count -gt 0) {
109
+ Write-Host ""
110
+ Write-Host "Was jetzt fehlt:" -ForegroundColor Yellow
111
+ foreach ($item in $failed) {
112
+ switch ($item.name) {
113
+ "bot_token" { Write-Host " - Telegram Bot Token fehlt. Starte: blun-codex --profile $($Result.profile) telegram-setup" }
114
+ "allowed_chat_ids" { Write-Host " - Erlaubte Chat-ID(s) fehlen. Starte: blun-codex --profile $($Result.profile) telegram-setup" }
115
+ "state_dir" { Write-Host " - Der lokale Telegram-State-Ordner fehlt noch. Ein Setup-Lauf legt ihn automatisch an." }
116
+ "profile_file" { Write-Host " - Das angegebene Profil existiert nicht." }
117
+ "node" { Write-Host " - Node.js fehlt in PATH." }
118
+ "codex" { Write-Host " - Der lokale codex-Befehl fehlt in PATH." }
119
+ "telegram_plugin_root" { Write-Host " - Der Telegram-Plugin-Ordner konnte nicht gefunden werden." }
120
+ default { Write-Host " - $($item.detail)" }
121
+ }
122
+ }
123
+ }
124
+
125
+ if ($Result.overall -eq "ok") {
126
+ Write-Host ""
127
+ Write-Host "Telegram ist sauber eingerichtet." -ForegroundColor Green
128
+ Write-Host "Starten: blun-codex --profile $($Result.profile) telegram-plugin"
129
+ } elseif ($Result.overall -eq "warn") {
130
+ Write-Host ""
131
+ Write-Host "Die Grundkonfiguration steht, aber es gibt noch Laufzeit-Hinweise." -ForegroundColor Yellow
132
+ Write-Host "Das ist oft normal, wenn Telegram noch nicht aktiv gestartet wurde oder noch keine Nachricht durchlief."
133
133
  }
134
134
  }
135
135
 
@@ -161,40 +161,40 @@ function Get-ProfilePath {
161
161
  $runtimeRoot = Split-Path -Parent $MyInvocation.MyCommand.Path
162
162
  $profilePath = Get-ProfilePath -RuntimeRoot $runtimeRoot -ProfileName $Profile
163
163
  $checks = New-Object 'System.Collections.Generic.List[object]'
164
-
165
- if (Test-Path $profilePath) {
166
- Add-Check -List $checks -Name "profile_file" -Status "ok" -Detail $profilePath
167
- } else {
168
- Add-Check -List $checks -Name "profile_file" -Status "fail" -Detail ("Missing profile: " + $profilePath)
169
- }
170
-
171
- $statusRaw = & powershell -ExecutionPolicy Bypass -File (Join-Path $runtimeRoot "telegram-status.ps1") -Profile $Profile
172
- $status = $statusRaw | ConvertFrom-Json
173
-
174
- $nodeCommand = Get-Command node -ErrorAction SilentlyContinue
175
- $codexCommand = Get-Command codex -ErrorAction SilentlyContinue
176
- Add-Check -List $checks -Name "node" -Status $(if ($nodeCommand) { "ok" } else { "fail" }) -Detail $(if ($nodeCommand) { $nodeCommand.Source } else { "node not found in PATH" })
177
- Add-Check -List $checks -Name "codex" -Status $(if ($codexCommand) { "ok" } else { "fail" }) -Detail $(if ($codexCommand) { $codexCommand.Source } else { "codex not found in PATH" })
178
-
179
- if ($status.plugin_root) {
180
- Add-Check -List $checks -Name "telegram_plugin_root" -Status "ok" -Detail $status.plugin_root
181
- } else {
182
- Add-Check -List $checks -Name "telegram_plugin_root" -Status "fail" -Detail "Telegram plugin root could not be resolved."
183
- }
184
-
185
- if ($status.state_dir -and (Test-Path $status.state_dir)) {
186
- Add-Check -List $checks -Name "state_dir" -Status "ok" -Detail $status.state_dir
187
- } else {
188
- Add-Check -List $checks -Name "state_dir" -Status "fail" -Detail ("Missing state dir: " + $status.state_dir)
189
- }
190
-
191
- $activeEnvPath = Join-Path $status.state_dir ".env"
192
- $activeEnv = Read-DotEnvFile -Path $activeEnvPath
193
- $legacyEnvPath = Join-Path $env:USERPROFILE ".codex\channels\codexlink-telegram\.env"
194
- $legacyEnv = Read-DotEnvFile -Path $legacyEnvPath
195
-
196
- $tokenValue = ""
197
- $tokenSource = ""
164
+
165
+ if (Test-Path $profilePath) {
166
+ Add-Check -List $checks -Name "profile_file" -Status "ok" -Detail $profilePath
167
+ } else {
168
+ Add-Check -List $checks -Name "profile_file" -Status "fail" -Detail ("Missing profile: " + $profilePath)
169
+ }
170
+
171
+ $statusRaw = & powershell -ExecutionPolicy Bypass -File (Join-Path $runtimeRoot "telegram-status.ps1") -Profile $Profile
172
+ $status = $statusRaw | ConvertFrom-Json
173
+
174
+ $nodeCommand = Get-Command node -ErrorAction SilentlyContinue
175
+ $codexCommand = Get-Command codex -ErrorAction SilentlyContinue
176
+ Add-Check -List $checks -Name "node" -Status $(if ($nodeCommand) { "ok" } else { "fail" }) -Detail $(if ($nodeCommand) { $nodeCommand.Source } else { "node not found in PATH" })
177
+ Add-Check -List $checks -Name "codex" -Status $(if ($codexCommand) { "ok" } else { "fail" }) -Detail $(if ($codexCommand) { $codexCommand.Source } else { "codex not found in PATH" })
178
+
179
+ if ($status.plugin_root) {
180
+ Add-Check -List $checks -Name "telegram_plugin_root" -Status "ok" -Detail $status.plugin_root
181
+ } else {
182
+ Add-Check -List $checks -Name "telegram_plugin_root" -Status "fail" -Detail "Telegram plugin root could not be resolved."
183
+ }
184
+
185
+ if ($status.state_dir -and (Test-Path $status.state_dir)) {
186
+ Add-Check -List $checks -Name "state_dir" -Status "ok" -Detail $status.state_dir
187
+ } else {
188
+ Add-Check -List $checks -Name "state_dir" -Status "fail" -Detail ("Missing state dir: " + $status.state_dir)
189
+ }
190
+
191
+ $activeEnvPath = Join-Path $status.state_dir ".env"
192
+ $activeEnv = Read-DotEnvFile -Path $activeEnvPath
193
+ $legacyEnvPath = Join-Path $env:USERPROFILE ".codex\channels\codexlink-telegram\.env"
194
+ $legacyEnv = Read-DotEnvFile -Path $legacyEnvPath
195
+
196
+ $tokenValue = ""
197
+ $tokenSource = ""
198
198
  if (Test-TelegramTokenFormat -Value $activeEnv["BLUN_TELEGRAM_BOT_TOKEN"]) {
199
199
  $tokenValue = [string]$activeEnv["BLUN_TELEGRAM_BOT_TOKEN"]
200
200
  $tokenSource = "state env"
@@ -208,10 +208,10 @@ if (Test-TelegramTokenFormat -Value $activeEnv["BLUN_TELEGRAM_BOT_TOKEN"]) {
208
208
  $tokenValue = [string]$legacyEnv["TELEGRAM_BOT_TOKEN"]
209
209
  $tokenSource = "legacy env fallback legacy key"
210
210
  }
211
- Add-Check -List $checks -Name "bot_token" -Status $(if ($tokenValue) { "ok" } else { "fail" }) -Detail $(if ($tokenValue) { $tokenSource } else { "No valid BLUN_TELEGRAM_BOT_TOKEN found." })
212
-
213
- $allowedChatIds = ""
214
- $allowedChatSource = ""
211
+ Add-Check -List $checks -Name "bot_token" -Status $(if ($tokenValue) { "ok" } else { "fail" }) -Detail $(if ($tokenValue) { $tokenSource } else { "No valid BLUN_TELEGRAM_BOT_TOKEN found." })
212
+
213
+ $allowedChatIds = ""
214
+ $allowedChatSource = ""
215
215
  if (Test-AllowedChatIdsFormat -Value $activeEnv["BLUN_TELEGRAM_ALLOWED_CHAT_ID"]) {
216
216
  $allowedChatIds = [string]$activeEnv["BLUN_TELEGRAM_ALLOWED_CHAT_ID"]
217
217
  $allowedChatSource = "state env"
@@ -226,7 +226,7 @@ if (Test-AllowedChatIdsFormat -Value $activeEnv["BLUN_TELEGRAM_ALLOWED_CHAT_ID"]
226
226
  $allowedChatSource = "legacy env fallback legacy key"
227
227
  }
228
228
  Add-Check -List $checks -Name "allowed_chat_ids" -Status $(if ($allowedChatIds) { "ok" } else { "warn" }) -Detail $(if ($allowedChatIds) { $allowedChatIds } else { "No allowlist set. Telegram currently accepts any chat the bot can see." })
229
-
229
+
230
230
  Add-Check -List $checks -Name "app_server_ws" -Status $(if ($status.active_ws) { "ok" } else { "warn" }) -Detail $(if ($status.active_ws) { $status.active_ws } else { "No active websocket recorded." })
231
231
  Add-Check -List $checks -Name "dispatch_mode" -Status $(if ($status.dispatch_mode -eq "deferred") { "ok" } else { "warn" }) -Detail ("mode=" + [string]$status.dispatch_mode + " cooldown_ms=" + [string]$status.idle_cooldown_ms + " pending_reply_timeout_ms=" + [string]$status.pending_reply_timeout_ms)
232
232
  Add-Check -List $checks -Name "bound_thread" -Status $(if ($status.active_thread_id) { "ok" } else { "warn" }) -Detail $(if ($status.active_thread_id) { $status.active_thread_id } else { "No active thread bound yet." })
@@ -235,48 +235,48 @@ Add-Check -List $checks -Name "queue_notifier" -Status $(if (($null -eq $status.
235
235
  Add-Check -List $checks -Name "poller" -Status $(if ($status.poller_alive) { "ok" } else { "warn" }) -Detail ("pid=" + [string]$status.poller_pid + " alive=" + [string]$status.poller_alive)
236
236
  Add-Check -List $checks -Name "dispatcher" -Status $(if ($status.dispatcher_alive) { "ok" } else { "warn" }) -Detail ("pid=" + [string]$status.dispatcher_pid + " alive=" + [string]$status.dispatcher_alive)
237
237
  Add-Check -List $checks -Name "responder" -Status $(if ($status.responder_alive) { "ok" } else { "warn" }) -Detail ("pid=" + [string]$status.responder_pid + " alive=" + [string]$status.responder_alive)
238
-
239
- if ($status.last_inbound) {
240
- $lastInboundSummary = [string]::Format(
241
- "chat={0} message={1} type={2} thread={3}",
242
- $status.last_inbound.chatId,
243
- $status.last_inbound.messageId,
244
- $status.last_inbound.chatType,
245
- $(if ($status.last_inbound.telegramThreadId) { $status.last_inbound.telegramThreadId } else { "-" })
246
- )
247
- Add-Check -List $checks -Name "last_inbound" -Status "ok" -Detail $lastInboundSummary
248
- } else {
249
- Add-Check -List $checks -Name "last_inbound" -Status "warn" -Detail "No inbound Telegram message recorded yet."
250
- }
251
-
252
- if ($status.last_outbound) {
253
- $lastOutboundSummary = [string]::Format(
254
- "chat={0} message={1} reply_to={2} thread={3}",
255
- $status.last_outbound.chatId,
256
- $status.last_outbound.messageId,
257
- $(if ($status.last_outbound.replyToMessageId) { $status.last_outbound.replyToMessageId } else { "-" }),
258
- $(if ($status.last_outbound.telegramThreadId) { $status.last_outbound.telegramThreadId } else { "-" })
259
- )
260
- Add-Check -List $checks -Name "last_outbound" -Status "ok" -Detail $lastOutboundSummary
261
- } else {
262
- Add-Check -List $checks -Name "last_outbound" -Status "warn" -Detail "No outbound Telegram message recorded yet."
263
- }
264
-
238
+
239
+ if ($status.last_inbound) {
240
+ $lastInboundSummary = [string]::Format(
241
+ "chat={0} message={1} type={2} thread={3}",
242
+ $status.last_inbound.chatId,
243
+ $status.last_inbound.messageId,
244
+ $status.last_inbound.chatType,
245
+ $(if ($status.last_inbound.telegramThreadId) { $status.last_inbound.telegramThreadId } else { "-" })
246
+ )
247
+ Add-Check -List $checks -Name "last_inbound" -Status "ok" -Detail $lastInboundSummary
248
+ } else {
249
+ Add-Check -List $checks -Name "last_inbound" -Status "warn" -Detail "No inbound Telegram message recorded yet."
250
+ }
251
+
252
+ if ($status.last_outbound) {
253
+ $lastOutboundSummary = [string]::Format(
254
+ "chat={0} message={1} reply_to={2} thread={3}",
255
+ $status.last_outbound.chatId,
256
+ $status.last_outbound.messageId,
257
+ $(if ($status.last_outbound.replyToMessageId) { $status.last_outbound.replyToMessageId } else { "-" }),
258
+ $(if ($status.last_outbound.telegramThreadId) { $status.last_outbound.telegramThreadId } else { "-" })
259
+ )
260
+ Add-Check -List $checks -Name "last_outbound" -Status "ok" -Detail $lastOutboundSummary
261
+ } else {
262
+ Add-Check -List $checks -Name "last_outbound" -Status "warn" -Detail "No outbound Telegram message recorded yet."
263
+ }
264
+
265
265
  Add-Check -List $checks -Name "queue" -Status $(if (([int]$status.queue_depth -eq 0) -and ([int]$status.pending_reply_depth -eq 0)) { "ok" } else { "warn" }) -Detail ("queued=" + $status.queue_depth + " ambient=" + $status.ambient_queue_depth + " parked=" + $status.parked_queue_depth + " submitted=" + $status.submitted_depth + " pending_replies=" + $status.pending_reply_depth + " expired_pending_replies=" + $status.expired_pending_reply_depth)
266
-
267
- $result = [ordered]@{
268
- profile = $status.profile
269
- overall = Get-OverallStatus -Checks $checks
270
- runtime_root = $runtimeRoot
271
- state_dir = $status.state_dir
272
- plugin_root = $status.plugin_root
273
- checks = $checks
274
- status = $status
275
- }
276
-
277
- if ($Json) {
278
- $result | ConvertTo-Json -Depth 8
279
- exit 0
280
- }
281
-
282
- Write-DoctorReport -Result $result -TokenSource $tokenSource -AllowedChatSource $allowedChatSource
266
+
267
+ $result = [ordered]@{
268
+ profile = $status.profile
269
+ overall = Get-OverallStatus -Checks $checks
270
+ runtime_root = $runtimeRoot
271
+ state_dir = $status.state_dir
272
+ plugin_root = $status.plugin_root
273
+ checks = $checks
274
+ status = $status
275
+ }
276
+
277
+ if ($Json) {
278
+ $result | ConvertTo-Json -Depth 8
279
+ exit 0
280
+ }
281
+
282
+ Write-DoctorReport -Result $result -TokenSource $tokenSource -AllowedChatSource $allowedChatSource
@@ -2,7 +2,7 @@
2
2
  import { injectNext } from "./lib/bridge.js";
3
3
  import { isCurrentSidecarPid } from "./lib/singleton.js";
4
4
 
5
- const intervalMs = Number.parseInt(process.env.BLUN_TELEGRAM_INJECT_INTERVAL_MS || "1500", 10) || 1500;
5
+ const intervalMs = Number.parseInt(process.env.BLUN_TELEGRAM_INJECT_INTERVAL_MS || "700", 10) || 700;
6
6
  let stopping = false;
7
7
 
8
8
  function sleep(ms) {
@@ -389,7 +389,10 @@ function looksLikeStatusBroadcast(text) {
389
389
  if (/^status(?:\s|$|[~:.-])/u.test(normalized)) {
390
390
  return true;
391
391
  }
392
- return /^[a-z][a-z0-9_-]{1,24}\s+\d{1,2}:\d{2}\b/u.test(normalized);
392
+ if (/^status\s+~?\s*\d{1,2}\s+\d{2}\b/u.test(normalized)) {
393
+ return true;
394
+ }
395
+ return /^[a-z][a-z0-9_-]{1,24}\s+~?\s*\d{1,2}\s+\d{2}\b/u.test(normalized);
393
396
  }
394
397
 
395
398
  function isAgentAddressed(config, text) {
@@ -415,7 +418,7 @@ function isAgentAddressed(config, text) {
415
418
  return true;
416
419
  }
417
420
 
418
- const briefDirective = new RegExp(`\\bbrief\\b[\\s\\S]{0,60}\\b@?${mention}\\b|\\b@?${mention}\\b[\\s\\S]{0,60}\\bbrief\\b`, "u").test(normalized);
421
+ const briefDirective = new RegExp(`\\bbrief\\b(?:\\s+#?\\d+)?\\s+(?:fuer|fur|for|an|to)\\s+@?${mention}\\b|\\b@?${mention}\\b\\s*[-:,]?\\s*brief\\b`, "u").test(normalized);
419
422
  if (briefDirective) {
420
423
  return true;
421
424
  }
@@ -452,7 +455,7 @@ function isOtherAgentAddressed(config, text) {
452
455
  return true;
453
456
  }
454
457
 
455
- const briefDirective = new RegExp(`\\bbrief\\b[\\s\\S]{0,60}\\b@?${mention}\\b|\\b@?${mention}\\b[\\s\\S]{0,60}\\bbrief\\b`, "u").test(normalized);
458
+ const briefDirective = new RegExp(`\\bbrief\\b(?:\\s+#?\\d+)?\\s+(?:fuer|fur|for|an|to)\\s+@?${mention}\\b|\\b@?${mention}\\b\\s*[-:,]?\\s*brief\\b`, "u").test(normalized);
456
459
  if (briefDirective) {
457
460
  return true;
458
461
  }
@@ -467,31 +470,27 @@ function isOtherAgentAddressed(config, text) {
467
470
  }
468
471
 
469
472
  function classifyInboundRelevance(config, inbound) {
470
- if (looksLikeEscalation(inbound.text)) {
471
- return "escalation";
472
- }
473
-
474
- if (String(inbound.chatType || "") === "private") {
475
- return "direct";
476
- }
477
-
478
473
  const text = String(inbound.text || "");
479
- if (!looksLikeStatusBroadcast(text) && isAgentAddressed(config, text)) {
474
+ const isStatusBroadcast = looksLikeStatusBroadcast(text);
475
+
476
+ if (!isStatusBroadcast && isAgentAddressed(config, text)) {
480
477
  return "direct";
481
478
  }
482
479
 
483
- if (!looksLikeStatusBroadcast(text) && isOtherAgentAddressed(config, text)) {
480
+ if (!isStatusBroadcast && isOtherAgentAddressed(config, text)) {
484
481
  return "ambient";
485
482
  }
486
483
 
487
- const allowedUserIds = Array.isArray(config.allowedChatIds) ? config.allowedChatIds : [];
488
- if (!inbound.senderIsBot && allowedUserIds.includes(String(inbound.userId || ""))) {
484
+ if (String(inbound.chatType || "") === "private") {
489
485
  return "direct";
490
486
  }
491
487
 
492
- const agentName = String(config.agentName || "").trim();
493
- if (agentName && agentName.toLowerCase() !== "default" && !looksLikeStatusBroadcast(text) && containsToken(text, agentName)) {
494
- return "direct";
488
+ if (inbound.senderIsBot || isStatusBroadcast) {
489
+ return "ambient";
490
+ }
491
+
492
+ if (looksLikeEscalation(text)) {
493
+ return "escalation";
495
494
  }
496
495
 
497
496
  const lane = String(config.lane || "").trim();
@@ -759,6 +758,33 @@ function parkExpiredAmbientQueueEntriesInPlace(config, queue) {
759
758
  return parked;
760
759
  }
761
760
 
761
+ function reclassifyQueuedEntriesInPlace(config, queue) {
762
+ const entries = Array.isArray(queue) ? queue : [];
763
+ let changed = 0;
764
+ let parked = 0;
765
+ for (const entry of entries) {
766
+ if (!entry || entry.status !== "queued") {
767
+ continue;
768
+ }
769
+ const previous = String(entry.relevance || "").trim().toLowerCase();
770
+ const next = classifyInboundRelevance(config, entry);
771
+ if (next === previous) {
772
+ continue;
773
+ }
774
+ const reclassifiedAt = nowIso();
775
+ entry.relevance = next;
776
+ entry.reclassifiedAt = reclassifiedAt;
777
+ if (next === "ambient" && String(entry.chatType || "").trim().toLowerCase() !== "private") {
778
+ entry.status = "parked";
779
+ entry.parkedAt = entry.parkedAt || reclassifiedAt;
780
+ entry.responsePreview = entry.responsePreview || "[reclassified ambient]";
781
+ parked += 1;
782
+ }
783
+ changed += 1;
784
+ }
785
+ return { changed, parked };
786
+ }
787
+
762
788
  function mergeQueueEntry(current, incoming) {
763
789
  if (!current && !incoming) {
764
790
  return null;
@@ -2068,11 +2094,15 @@ function isFastDispatchEntry(entry) {
2068
2094
  export async function injectNext(threadId, options = {}) {
2069
2095
  const config = loadConfig();
2070
2096
  const state = loadState(config);
2097
+ const reclassified = reclassifyQueuedEntriesInPlace(config, state.queue || []);
2071
2098
  const parkedAmbient = parkExpiredAmbientQueueEntriesInPlace(config, state.queue || []);
2072
2099
  const runtimeOwner = getRuntimeOwner(config);
2073
2100
  state.pendingReplies = reconcilePendingRepliesInPlace(state.pendingReplies || []);
2074
2101
  const expiredPendingReplies = closeExpiredPendingRepliesInPlace(config, state.pendingReplies || []);
2075
- if (expiredPendingReplies > 0 || parkedAmbient > 0) {
2102
+ if (expiredPendingReplies > 0 || parkedAmbient > 0 || reclassified.changed > 0) {
2103
+ if (reclassified.changed > 0) {
2104
+ appendLog(config.paths.activityFile, `QUEUE_RECLASSIFIED changed=${reclassified.changed} parked=${reclassified.parked}`);
2105
+ }
2076
2106
  if (parkedAmbient > 0) {
2077
2107
  appendLog(config.paths.activityFile, `AMBIENT_PARKED count=${parkedAmbient}`);
2078
2108
  }
@@ -55,31 +55,31 @@ export function saveJson(path, value) {
55
55
 
56
56
  throw lastError || new Error(`Failed to save JSON file: ${path}`);
57
57
  }
58
-
59
- export function appendJsonl(path, value) {
60
- appendFileSync(path, `${JSON.stringify(value)}\n`, "utf8");
61
- }
62
-
63
- export function appendLog(path, message) {
64
- appendFileSync(path, `${nowIso()} ${message}\n`, "utf8");
65
- }
66
-
67
- export function readTail(path, lines = 20) {
68
- if (!existsSync(path)) {
69
- return [];
70
- }
71
- const text = readFileSync(path, "utf8");
72
- return text.split(/\r?\n/).filter(Boolean).slice(-lines);
73
- }
74
-
75
- export function defaultState() {
76
- return {
77
- offset: 0,
78
- currentThreadId: "",
79
- queue: [],
80
- pendingReplies: [],
81
- replyOffsets: {},
82
- replyBuffers: {},
58
+
59
+ export function appendJsonl(path, value) {
60
+ appendFileSync(path, `${JSON.stringify(value)}\n`, "utf8");
61
+ }
62
+
63
+ export function appendLog(path, message) {
64
+ appendFileSync(path, `${nowIso()} ${message}\n`, "utf8");
65
+ }
66
+
67
+ export function readTail(path, lines = 20) {
68
+ if (!existsSync(path)) {
69
+ return [];
70
+ }
71
+ const text = readFileSync(path, "utf8");
72
+ return text.split(/\r?\n/).filter(Boolean).slice(-lines);
73
+ }
74
+
75
+ export function defaultState() {
76
+ return {
77
+ offset: 0,
78
+ currentThreadId: "",
79
+ queue: [],
80
+ pendingReplies: [],
81
+ replyOffsets: {},
82
+ replyBuffers: {},
83
83
  lastInbound: null,
84
84
  lastOutbound: null,
85
85
  lastUiNotice: null,
@@ -19,9 +19,10 @@ async function telegramRequest(config, method, body) {
19
19
  }
20
20
 
21
21
  export async function getUpdates(config, offset) {
22
+ const timeout = Math.max(Number.parseInt(process.env.BLUN_TELEGRAM_GETUPDATES_TIMEOUT || "0", 10) || 0, 0);
22
23
  return telegramRequest(config, "getUpdates", {
23
24
  offset,
24
- timeout: 1,
25
+ timeout,
25
26
  allowed_updates: ["message"]
26
27
  });
27
28
  }