@blunking/codexlink 0.1.0 → 0.1.2
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 +233 -115
- package/blun-codex.ps1 +140 -110
- package/package.json +4 -3
- package/start-codex-agent.ps1 +746 -710
- package/telegram-doctor.ps1 +256 -113
- package/telegram-plugin/.env.example +3 -1
- package/telegram-plugin/README.md +7 -3
- package/telegram-plugin/dispatcher.js +2 -2
- package/telegram-plugin/lib/bridge.js +138 -15
- package/telegram-plugin/lib/env.js +15 -4
- package/telegram-plugin/lib/sidecars.js +3 -1
- package/telegram-plugin/lib/storage.js +50 -49
- package/telegram-setup.ps1 +245 -0
- package/telegram-status.ps1 +30 -1
package/telegram-doctor.ps1
CHANGED
|
@@ -1,125 +1,268 @@
|
|
|
1
|
-
param(
|
|
2
|
-
[string]$Profile = "default"
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
$
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
if ($line
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
$
|
|
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
|
+
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."
|
|
17
133
|
}
|
|
18
|
-
return $values
|
|
19
134
|
}
|
|
20
135
|
|
|
21
|
-
function
|
|
136
|
+
function Get-ProfilePath {
|
|
22
137
|
param(
|
|
23
|
-
[
|
|
24
|
-
[string]$
|
|
25
|
-
[string]$Status,
|
|
26
|
-
[string]$Detail
|
|
138
|
+
[string]$RuntimeRoot,
|
|
139
|
+
[string]$ProfileName
|
|
27
140
|
)
|
|
28
|
-
$List.Add([pscustomobject]@{
|
|
29
|
-
name = $Name
|
|
30
|
-
status = $Status
|
|
31
|
-
detail = $Detail
|
|
32
|
-
}) | Out-Null
|
|
33
|
-
}
|
|
34
141
|
|
|
35
|
-
$
|
|
36
|
-
|
|
37
|
-
$
|
|
38
|
-
|
|
39
|
-
if (
|
|
40
|
-
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
$statusRaw = & powershell -ExecutionPolicy Bypass -File (Join-Path $runtimeRoot "telegram-status.ps1") -Profile $Profile
|
|
46
|
-
$status = $statusRaw | ConvertFrom-Json
|
|
47
|
-
|
|
48
|
-
$nodeCommand = Get-Command node -ErrorAction SilentlyContinue
|
|
49
|
-
$codexCommand = Get-Command codex -ErrorAction SilentlyContinue
|
|
50
|
-
Add-Check -List $checks -Name "node" -Status $(if ($nodeCommand) { "ok" } else { "fail" }) -Detail $(if ($nodeCommand) { $nodeCommand.Source } else { "node not found in PATH" })
|
|
51
|
-
Add-Check -List $checks -Name "codex" -Status $(if ($codexCommand) { "ok" } else { "fail" }) -Detail $(if ($codexCommand) { $codexCommand.Source } else { "codex not found in PATH" })
|
|
52
|
-
|
|
53
|
-
if ($status.plugin_root) {
|
|
54
|
-
Add-Check -List $checks -Name "telegram_plugin_root" -Status "ok" -Detail $status.plugin_root
|
|
55
|
-
} else {
|
|
56
|
-
Add-Check -List $checks -Name "telegram_plugin_root" -Status "fail" -Detail "Telegram plugin root could not be resolved."
|
|
57
|
-
}
|
|
142
|
+
$normalized = [string]$ProfileName
|
|
143
|
+
if (-not $normalized) { $normalized = "" }
|
|
144
|
+
$normalized = $normalized.ToLower()
|
|
145
|
+
$candidates = @()
|
|
146
|
+
if ($env:BLUN_CODEX_PROFILE_ROOT) {
|
|
147
|
+
$candidates += (Join-Path $env:BLUN_CODEX_PROFILE_ROOT ($normalized + ".json"))
|
|
148
|
+
}
|
|
149
|
+
$candidates += (Join-Path $env:USERPROFILE (".codex\\profiles\\codexlink\\" + $normalized + ".json"))
|
|
150
|
+
$candidates += (Join-Path $RuntimeRoot ("profiles\\" + $normalized + ".json"))
|
|
58
151
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
}
|
|
152
|
+
foreach ($candidate in $candidates) {
|
|
153
|
+
if ($candidate -and (Test-Path $candidate)) {
|
|
154
|
+
return $candidate
|
|
155
|
+
}
|
|
156
|
+
}
|
|
64
157
|
|
|
65
|
-
$
|
|
66
|
-
$legacyEnv = Read-DotEnvFile -Path (Join-Path $env:USERPROFILE ".codex\channels\codexlink-telegram\.env")
|
|
67
|
-
$tokenSource = if ($activeEnv["BLUN_TELEGRAM_BOT_TOKEN"]) {
|
|
68
|
-
"active_state_env"
|
|
69
|
-
} elseif ($legacyEnv["BLUN_TELEGRAM_BOT_TOKEN"]) {
|
|
70
|
-
"legacy_env_fallback"
|
|
71
|
-
} else {
|
|
72
|
-
""
|
|
158
|
+
return $candidates[-1]
|
|
73
159
|
}
|
|
74
|
-
Add-Check -List $checks -Name "bot_token" -Status $(if ($tokenSource) { "ok" } else { "fail" }) -Detail $(if ($tokenSource) { $tokenSource } else { "No BLUN_TELEGRAM_BOT_TOKEN found in active or legacy env files." })
|
|
75
160
|
|
|
161
|
+
$runtimeRoot = Split-Path -Parent $MyInvocation.MyCommand.Path
|
|
162
|
+
$profilePath = Get-ProfilePath -RuntimeRoot $runtimeRoot -ProfileName $Profile
|
|
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 = ""
|
|
198
|
+
if (Test-TelegramTokenFormat -Value $activeEnv["BLUN_TELEGRAM_BOT_TOKEN"]) {
|
|
199
|
+
$tokenValue = [string]$activeEnv["BLUN_TELEGRAM_BOT_TOKEN"]
|
|
200
|
+
$tokenSource = "state env"
|
|
201
|
+
} elseif (Test-TelegramTokenFormat -Value $legacyEnv["BLUN_TELEGRAM_BOT_TOKEN"]) {
|
|
202
|
+
$tokenValue = [string]$legacyEnv["BLUN_TELEGRAM_BOT_TOKEN"]
|
|
203
|
+
$tokenSource = "legacy env fallback"
|
|
204
|
+
}
|
|
205
|
+
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." })
|
|
206
|
+
|
|
207
|
+
$allowedChatIds = ""
|
|
208
|
+
$allowedChatSource = ""
|
|
209
|
+
if (Test-AllowedChatIdsFormat -Value $activeEnv["BLUN_TELEGRAM_ALLOWED_CHAT_ID"]) {
|
|
210
|
+
$allowedChatIds = [string]$activeEnv["BLUN_TELEGRAM_ALLOWED_CHAT_ID"]
|
|
211
|
+
$allowedChatSource = "state env"
|
|
212
|
+
} elseif (Test-AllowedChatIdsFormat -Value $legacyEnv["BLUN_TELEGRAM_ALLOWED_CHAT_ID"]) {
|
|
213
|
+
$allowedChatIds = [string]$legacyEnv["BLUN_TELEGRAM_ALLOWED_CHAT_ID"]
|
|
214
|
+
$allowedChatSource = "legacy env fallback"
|
|
215
|
+
}
|
|
216
|
+
Add-Check -List $checks -Name "allowed_chat_ids" -Status $(if ($allowedChatIds) { "ok" } else { "fail" }) -Detail $(if ($allowedChatIds) { $allowedChatIds } else { "No valid BLUN_TELEGRAM_ALLOWED_CHAT_ID found." })
|
|
217
|
+
|
|
76
218
|
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." })
|
|
219
|
+
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)
|
|
77
220
|
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." })
|
|
78
|
-
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)
|
|
79
|
-
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)
|
|
80
|
-
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)
|
|
81
|
-
|
|
82
|
-
if ($status.last_inbound) {
|
|
83
|
-
$lastInboundSummary = [string]::Format(
|
|
84
|
-
"chat={0} message={1} type={2} thread={3}",
|
|
85
|
-
$status.last_inbound.chatId,
|
|
86
|
-
$status.last_inbound.messageId,
|
|
87
|
-
$status.last_inbound.chatType,
|
|
88
|
-
$(if ($status.last_inbound.telegramThreadId) { $status.last_inbound.telegramThreadId } else { "-" })
|
|
89
|
-
)
|
|
90
|
-
Add-Check -List $checks -Name "last_inbound" -Status "ok" -Detail $lastInboundSummary
|
|
91
|
-
} else {
|
|
92
|
-
Add-Check -List $checks -Name "last_inbound" -Status "warn" -Detail "No inbound Telegram message recorded yet."
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
if ($status.last_outbound) {
|
|
96
|
-
$lastOutboundSummary = [string]::Format(
|
|
97
|
-
"chat={0} message={1} reply_to={2} thread={3}",
|
|
98
|
-
$status.last_outbound.chatId,
|
|
99
|
-
$status.last_outbound.messageId,
|
|
100
|
-
$(if ($status.last_outbound.replyToMessageId) { $status.last_outbound.replyToMessageId } else { "-" }),
|
|
101
|
-
$(if ($status.last_outbound.telegramThreadId) { $status.last_outbound.telegramThreadId } else { "-" })
|
|
102
|
-
)
|
|
103
|
-
Add-Check -List $checks -Name "last_outbound" -Status "ok" -Detail $lastOutboundSummary
|
|
104
|
-
} else {
|
|
105
|
-
Add-Check -List $checks -Name "last_outbound" -Status "warn" -Detail "No outbound Telegram message recorded yet."
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
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 + " submitted=" + $status.submitted_depth + " pending_replies=" + $status.pending_reply_depth)
|
|
109
|
-
|
|
110
|
-
$
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
221
|
+
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)
|
|
222
|
+
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)
|
|
223
|
+
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)
|
|
224
|
+
|
|
225
|
+
if ($status.last_inbound) {
|
|
226
|
+
$lastInboundSummary = [string]::Format(
|
|
227
|
+
"chat={0} message={1} type={2} thread={3}",
|
|
228
|
+
$status.last_inbound.chatId,
|
|
229
|
+
$status.last_inbound.messageId,
|
|
230
|
+
$status.last_inbound.chatType,
|
|
231
|
+
$(if ($status.last_inbound.telegramThreadId) { $status.last_inbound.telegramThreadId } else { "-" })
|
|
232
|
+
)
|
|
233
|
+
Add-Check -List $checks -Name "last_inbound" -Status "ok" -Detail $lastInboundSummary
|
|
234
|
+
} else {
|
|
235
|
+
Add-Check -List $checks -Name "last_inbound" -Status "warn" -Detail "No inbound Telegram message recorded yet."
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
if ($status.last_outbound) {
|
|
239
|
+
$lastOutboundSummary = [string]::Format(
|
|
240
|
+
"chat={0} message={1} reply_to={2} thread={3}",
|
|
241
|
+
$status.last_outbound.chatId,
|
|
242
|
+
$status.last_outbound.messageId,
|
|
243
|
+
$(if ($status.last_outbound.replyToMessageId) { $status.last_outbound.replyToMessageId } else { "-" }),
|
|
244
|
+
$(if ($status.last_outbound.telegramThreadId) { $status.last_outbound.telegramThreadId } else { "-" })
|
|
245
|
+
)
|
|
246
|
+
Add-Check -List $checks -Name "last_outbound" -Status "ok" -Detail $lastOutboundSummary
|
|
247
|
+
} else {
|
|
248
|
+
Add-Check -List $checks -Name "last_outbound" -Status "warn" -Detail "No outbound Telegram message recorded yet."
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
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 + " submitted=" + $status.submitted_depth + " pending_replies=" + $status.pending_reply_depth)
|
|
252
|
+
|
|
253
|
+
$result = [ordered]@{
|
|
254
|
+
profile = $status.profile
|
|
255
|
+
overall = Get-OverallStatus -Checks $checks
|
|
256
|
+
runtime_root = $runtimeRoot
|
|
257
|
+
state_dir = $status.state_dir
|
|
258
|
+
plugin_root = $status.plugin_root
|
|
259
|
+
checks = $checks
|
|
260
|
+
status = $status
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
if ($Json) {
|
|
264
|
+
$result | ConvertTo-Json -Depth 8
|
|
265
|
+
exit 0
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
Write-DoctorReport -Result $result -TokenSource $tokenSource -AllowedChatSource $allowedChatSource
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
BLUN_TELEGRAM_AGENT_NAME=default
|
|
2
2
|
BLUN_TELEGRAM_BOT_TOKEN=123456789:replace_me
|
|
3
|
-
BLUN_TELEGRAM_ALLOWED_CHAT_ID=1605241602
|
|
3
|
+
BLUN_TELEGRAM_ALLOWED_CHAT_ID=1605241602,-1003927574737
|
|
4
4
|
BLUN_TELEGRAM_CODEX_BIN=codex
|
|
5
5
|
BLUN_TELEGRAM_STATE_DIR=
|
|
6
6
|
BLUN_TELEGRAM_THREAD_ID=
|
|
7
7
|
BLUN_TELEGRAM_RESUME_TIMEOUT_MS=15000
|
|
8
|
+
BLUN_TELEGRAM_IDLE_COOLDOWN_MS=15000
|
|
9
|
+
BLUN_TELEGRAM_DISPATCH_MODE=deferred
|
|
8
10
|
BLUN_TELEGRAM_POLL_INTERVAL_MS=5000
|
|
9
11
|
BLUN_TELEGRAM_INJECT_INTERVAL_MS=15000
|
|
@@ -10,8 +10,10 @@ It is intentionally **not** an autonomous answer bot.
|
|
|
10
10
|
- stores inbound and outbound history under a local state directory
|
|
11
11
|
- keeps private chats and group threads separated
|
|
12
12
|
- binds a live thread id
|
|
13
|
-
- injects the next queued Telegram message into that exact live thread
|
|
13
|
+
- injects the next queued Telegram message into that exact live thread only after the session is idle
|
|
14
14
|
- sends explicit manual replies from the visible operator session
|
|
15
|
+
- keeps ambient group noise queued unless it is relevant to that operator
|
|
16
|
+
- lets escalation-style messages bypass the normal idle queue
|
|
15
17
|
|
|
16
18
|
## What it does not do
|
|
17
19
|
|
|
@@ -44,10 +46,12 @@ Copy `.env.example` to `.env` in the state directory or export env vars:
|
|
|
44
46
|
|
|
45
47
|
- `BLUN_TELEGRAM_AGENT_NAME`
|
|
46
48
|
- `BLUN_TELEGRAM_BOT_TOKEN`
|
|
47
|
-
- `BLUN_TELEGRAM_ALLOWED_CHAT_ID`
|
|
49
|
+
- `BLUN_TELEGRAM_ALLOWED_CHAT_ID` (`chatId` or comma-separated list like `1605241602,-1003927574737`)
|
|
48
50
|
- `BLUN_TELEGRAM_CODEX_BIN`
|
|
49
51
|
- `BLUN_TELEGRAM_THREAD_ID`
|
|
50
52
|
- `BLUN_TELEGRAM_RESUME_TIMEOUT_MS`
|
|
53
|
+
- `BLUN_TELEGRAM_IDLE_COOLDOWN_MS`
|
|
54
|
+
- `BLUN_TELEGRAM_DISPATCH_MODE` (`deferred` by default, `legacy` to restore eager dispatch)
|
|
51
55
|
|
|
52
56
|
## Tools
|
|
53
57
|
|
|
@@ -63,6 +67,6 @@ Copy `.env.example` to `.env` in the state directory or export env vars:
|
|
|
63
67
|
## Runtime split
|
|
64
68
|
|
|
65
69
|
- `poller.js` only fetches Telegram updates into the queue
|
|
66
|
-
- `dispatcher.js` only retries queue delivery into the bound live thread
|
|
70
|
+
- `dispatcher.js` only retries queue delivery into the bound live thread after the current run is quiet
|
|
67
71
|
- `responder.js` only relays finished answers back out
|
|
68
72
|
- none of them are allowed to invent an answer on their own
|
|
@@ -19,8 +19,8 @@ process.on("SIGTERM", () => {
|
|
|
19
19
|
async function main() {
|
|
20
20
|
while (!stopping) {
|
|
21
21
|
try {
|
|
22
|
-
const result = await injectNext("");
|
|
23
|
-
if (result.status
|
|
22
|
+
const result = await injectNext("", { auto: true });
|
|
23
|
+
if (!["empty", "deferred"].includes(result.status)) {
|
|
24
24
|
console.log(JSON.stringify({ ts: new Date().toISOString(), kind: "inject", result }));
|
|
25
25
|
}
|
|
26
26
|
} catch (error) {
|