@blunking/codexlink 0.1.18 → 0.1.20
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 +239 -230
- package/blun-codex.ps1 +140 -140
- package/package.json +37 -37
- package/start-codex-agent.ps1 +715 -715
- package/telegram-doctor.ps1 +330 -213
- package/telegram-plugin/lib/bridge.js +20 -5
- package/telegram-plugin/lib/storage.js +25 -25
- package/telegram-setup.ps1 +143 -143
- package/telegram-status.ps1 +256 -256
- package/telegram-title-embed.ps1 +98 -98
- package/telegram-title-watcher.ps1 +102 -102
package/telegram-doctor.ps1
CHANGED
|
@@ -1,138 +1,228 @@
|
|
|
1
|
-
param(
|
|
2
|
-
[string]$Profile = "default",
|
|
3
|
-
[switch]$Json
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
$
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
if ($line
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
$
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
[
|
|
26
|
-
[string]$
|
|
27
|
-
[string]$
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
1
|
+
param(
|
|
2
|
+
[string]$Profile = "default",
|
|
3
|
+
[switch]$Json,
|
|
4
|
+
[switch]$Fix
|
|
5
|
+
)
|
|
6
|
+
|
|
7
|
+
$ErrorActionPreference = "Stop"
|
|
8
|
+
|
|
9
|
+
function Read-DotEnvFile {
|
|
10
|
+
param([string]$Path)
|
|
11
|
+
$values = @{}
|
|
12
|
+
if (-not (Test-Path $Path)) { return $values }
|
|
13
|
+
foreach ($line in (Get-Content -Path $Path)) {
|
|
14
|
+
if (-not $line) { continue }
|
|
15
|
+
if ($line.Trim().StartsWith("#")) { continue }
|
|
16
|
+
$parts = $line -split "=", 2
|
|
17
|
+
if ($parts.Count -ne 2) { continue }
|
|
18
|
+
$values[$parts[0].Trim()] = $parts[1]
|
|
19
|
+
}
|
|
20
|
+
return $values
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function Add-Check {
|
|
24
|
+
param(
|
|
25
|
+
[System.Collections.Generic.List[object]]$List,
|
|
26
|
+
[string]$Name,
|
|
27
|
+
[string]$Status,
|
|
28
|
+
[string]$Detail
|
|
29
|
+
)
|
|
30
|
+
$List.Add([pscustomobject]@{
|
|
31
|
+
name = $Name
|
|
32
|
+
status = $Status
|
|
33
|
+
detail = $Detail
|
|
34
|
+
}) | Out-Null
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function Test-TelegramTokenFormat {
|
|
38
|
+
param([string]$Value)
|
|
39
|
+
if (-not $Value) { return $false }
|
|
40
|
+
return $Value -match '^\d{6,}:[A-Za-z0-9_-]{20,}$'
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function Test-AllowedChatIdsFormat {
|
|
44
|
+
param([string]$Value)
|
|
45
|
+
if (-not $Value) { return $false }
|
|
46
|
+
$parts = @($Value -split "," | ForEach-Object { $_.Trim() } | Where-Object { $_ })
|
|
47
|
+
if ($parts.Count -eq 0) { return $false }
|
|
48
|
+
foreach ($part in $parts) {
|
|
49
|
+
if ($part -notmatch '^-?\d+$') {
|
|
50
|
+
return $false
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return $true
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function Get-OverallStatus {
|
|
57
|
+
param([System.Collections.Generic.List[object]]$Checks)
|
|
58
|
+
if (@($Checks | Where-Object { $_.status -eq "fail" }).Count -gt 0) {
|
|
59
|
+
return "fail"
|
|
60
|
+
}
|
|
61
|
+
if (@($Checks | Where-Object { $_.status -eq "warn" }).Count -gt 0) {
|
|
62
|
+
return "warn"
|
|
63
|
+
}
|
|
64
|
+
return "ok"
|
|
65
|
+
}
|
|
66
|
+
|
|
66
67
|
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" }
|
|
68
|
+
param(
|
|
69
|
+
[object]$Result,
|
|
70
|
+
[string]$TokenSource,
|
|
71
|
+
[string]$AllowedChatSource
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
$emoji = switch ($Result.overall) {
|
|
75
|
+
"ok" { "[OK]" }
|
|
76
|
+
"warn" { "[WARN]" }
|
|
77
|
+
default { "[FAIL]" }
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
Write-Host ""
|
|
81
|
+
Write-Host "CodexLink Telegram Doctor $emoji" -ForegroundColor Cyan
|
|
82
|
+
Write-Host "Profil: $($Result.profile)"
|
|
83
|
+
Write-Host "State-Ordner: $($Result.state_dir)"
|
|
84
|
+
Write-Host ""
|
|
85
|
+
|
|
86
|
+
foreach ($check in $Result.checks) {
|
|
87
|
+
$prefix = switch ($check.status) {
|
|
88
|
+
"ok" { "[OK]" }
|
|
89
|
+
"warn" { "[WARN]" }
|
|
90
|
+
default { "[FAIL]" }
|
|
91
|
+
}
|
|
92
|
+
$color = switch ($check.status) {
|
|
93
|
+
"ok" { "Green" }
|
|
94
|
+
"warn" { "Yellow" }
|
|
95
|
+
default { "Red" }
|
|
96
|
+
}
|
|
97
|
+
Write-Host "$prefix $($check.name): $($check.detail)" -ForegroundColor $color
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
Write-Host ""
|
|
101
|
+
if ($TokenSource) {
|
|
102
|
+
Write-Host "Bot-Token gefunden aus: $TokenSource" -ForegroundColor DarkGray
|
|
103
|
+
}
|
|
104
|
+
if ($AllowedChatSource) {
|
|
105
|
+
Write-Host "Erlaubte Chat-ID(s) gefunden aus: $AllowedChatSource" -ForegroundColor DarkGray
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
$failed = @($Result.checks | Where-Object { $_.status -eq "fail" })
|
|
109
|
+
if ($failed.Count -gt 0) {
|
|
110
|
+
Write-Host ""
|
|
111
|
+
Write-Host "Was jetzt fehlt:" -ForegroundColor Yellow
|
|
112
|
+
foreach ($item in $failed) {
|
|
113
|
+
switch ($item.name) {
|
|
114
|
+
"bot_token" { Write-Host " - Telegram Bot Token fehlt. Starte: blun-codex --profile $($Result.profile) telegram-setup" }
|
|
114
115
|
"allowed_chat_ids" { Write-Host " - Chat-ID ist optional. Zum automatischen Koppeln: 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."
|
|
116
|
+
"state_dir" { Write-Host " - Der lokale Telegram-State-Ordner fehlt noch. Ein Setup-Lauf legt ihn automatisch an." }
|
|
117
|
+
"profile_file" { Write-Host " - Das angegebene Profil existiert nicht." }
|
|
118
|
+
"node" { Write-Host " - Node.js fehlt in PATH." }
|
|
119
|
+
"codex" { Write-Host " - Der lokale codex-Befehl fehlt in PATH." }
|
|
120
|
+
"telegram_plugin_root" { Write-Host " - Der Telegram-Plugin-Ordner konnte nicht gefunden werden." }
|
|
121
|
+
default { Write-Host " - $($item.detail)" }
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if ($Result.overall -eq "ok") {
|
|
127
|
+
Write-Host ""
|
|
128
|
+
Write-Host "Telegram ist sauber eingerichtet." -ForegroundColor Green
|
|
129
|
+
Write-Host "Starten: blun-codex --profile $($Result.profile) telegram-plugin"
|
|
130
|
+
} elseif ($Result.overall -eq "warn") {
|
|
131
|
+
Write-Host ""
|
|
132
|
+
Write-Host "Die Grundkonfiguration steht, aber es gibt noch Laufzeit-Hinweise." -ForegroundColor Yellow
|
|
133
|
+
Write-Host "Das ist oft normal, wenn Telegram noch nicht aktiv gestartet wurde oder noch keine Nachricht durchlief."
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function Write-DotEnvFile {
|
|
138
|
+
param(
|
|
139
|
+
[string]$Path,
|
|
140
|
+
[hashtable]$Values
|
|
141
|
+
)
|
|
142
|
+
$dir = Split-Path -Parent $Path
|
|
143
|
+
if ($dir -and -not (Test-Path $dir)) {
|
|
144
|
+
New-Item -ItemType Directory -Path $dir -Force | Out-Null
|
|
145
|
+
}
|
|
146
|
+
$lines = New-Object 'System.Collections.Generic.List[string]'
|
|
147
|
+
foreach ($key in ($Values.Keys | Sort-Object)) {
|
|
148
|
+
if ([string]::IsNullOrWhiteSpace([string]$key)) { continue }
|
|
149
|
+
$value = [string]$Values[$key]
|
|
150
|
+
$lines.Add($key + "=" + $value) | Out-Null
|
|
151
|
+
}
|
|
152
|
+
Set-Content -Path $Path -Value $lines -Encoding UTF8
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function Stop-PidQuiet {
|
|
156
|
+
param([object]$PidValue)
|
|
157
|
+
$pidText = [string]$PidValue
|
|
158
|
+
if (-not $pidText) { return $false }
|
|
159
|
+
$pidInt = 0
|
|
160
|
+
if (-not [int]::TryParse($pidText, [ref]$pidInt)) { return $false }
|
|
161
|
+
if ($pidInt -le 0) { return $false }
|
|
162
|
+
try {
|
|
163
|
+
Stop-Process -Id $pidInt -Force -ErrorAction Stop
|
|
164
|
+
return $true
|
|
165
|
+
} catch {
|
|
166
|
+
return $false
|
|
133
167
|
}
|
|
134
168
|
}
|
|
135
169
|
|
|
170
|
+
function Invoke-RuntimeFix {
|
|
171
|
+
param(
|
|
172
|
+
[object]$Status,
|
|
173
|
+
[string]$RuntimeRoot
|
|
174
|
+
)
|
|
175
|
+
$actions = New-Object 'System.Collections.Generic.List[string]'
|
|
176
|
+
$runtime = $Status.current_runtime
|
|
177
|
+
if ($runtime) {
|
|
178
|
+
$pids = @(
|
|
179
|
+
$runtime.frontend_host_pid,
|
|
180
|
+
$runtime.app_server_pid,
|
|
181
|
+
$runtime.queue_notifier_pid,
|
|
182
|
+
$runtime.poller_pid,
|
|
183
|
+
$runtime.dispatcher_pid,
|
|
184
|
+
$runtime.responder_pid
|
|
185
|
+
) | Where-Object { $_ } | Select-Object -Unique
|
|
186
|
+
foreach ($pidValue in $pids) {
|
|
187
|
+
if (Stop-PidQuiet -PidValue $pidValue) {
|
|
188
|
+
$actions.Add("stopped_pid=" + [string]$pidValue) | Out-Null
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
$runtimeFile = Join-Path $env:USERPROFILE (".codex\runtimes\" + [string]$Status.profile + "\current-remote-runtime.json")
|
|
194
|
+
if (Test-Path $runtimeFile) {
|
|
195
|
+
Remove-Item -LiteralPath $runtimeFile -Force -ErrorAction SilentlyContinue
|
|
196
|
+
$actions.Add("removed_runtime_file") | Out-Null
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
$envFile = Join-Path ([string]$Status.state_dir) ".env"
|
|
200
|
+
$envValues = Read-DotEnvFile -Path $envFile
|
|
201
|
+
if ($envValues.ContainsKey("BLUN_TELEGRAM_THREAD_ID")) {
|
|
202
|
+
$envValues["BLUN_TELEGRAM_THREAD_ID"] = ""
|
|
203
|
+
Write-DotEnvFile -Path $envFile -Values $envValues
|
|
204
|
+
$actions.Add("cleared_env_thread") | Out-Null
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
$stateFile = Join-Path ([string]$Status.state_dir) "state.json"
|
|
208
|
+
if (Test-Path $stateFile) {
|
|
209
|
+
try {
|
|
210
|
+
$state = Get-Content -Raw -Path $stateFile | ConvertFrom-Json
|
|
211
|
+
if ($state.PSObject.Properties.Name.Contains("currentThreadId")) {
|
|
212
|
+
$state.currentThreadId = ""
|
|
213
|
+
} else {
|
|
214
|
+
$state | Add-Member -NotePropertyName "currentThreadId" -NotePropertyValue ""
|
|
215
|
+
}
|
|
216
|
+
$state | ConvertTo-Json -Depth 10 | Set-Content -Path $stateFile -Encoding UTF8
|
|
217
|
+
$actions.Add("cleared_state_thread") | Out-Null
|
|
218
|
+
} catch {
|
|
219
|
+
$actions.Add("state_thread_clear_failed") | Out-Null
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
return @($actions)
|
|
224
|
+
}
|
|
225
|
+
|
|
136
226
|
function Get-ProfilePath {
|
|
137
227
|
param(
|
|
138
228
|
[string]$RuntimeRoot,
|
|
@@ -161,40 +251,40 @@ function Get-ProfilePath {
|
|
|
161
251
|
$runtimeRoot = Split-Path -Parent $MyInvocation.MyCommand.Path
|
|
162
252
|
$profilePath = Get-ProfilePath -RuntimeRoot $runtimeRoot -ProfileName $Profile
|
|
163
253
|
$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 = ""
|
|
254
|
+
|
|
255
|
+
if (Test-Path $profilePath) {
|
|
256
|
+
Add-Check -List $checks -Name "profile_file" -Status "ok" -Detail $profilePath
|
|
257
|
+
} else {
|
|
258
|
+
Add-Check -List $checks -Name "profile_file" -Status "fail" -Detail ("Missing profile: " + $profilePath)
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
$statusRaw = & powershell -ExecutionPolicy Bypass -File (Join-Path $runtimeRoot "telegram-status.ps1") -Profile $Profile
|
|
262
|
+
$status = $statusRaw | ConvertFrom-Json
|
|
263
|
+
|
|
264
|
+
$nodeCommand = Get-Command node -ErrorAction SilentlyContinue
|
|
265
|
+
$codexCommand = Get-Command codex -ErrorAction SilentlyContinue
|
|
266
|
+
Add-Check -List $checks -Name "node" -Status $(if ($nodeCommand) { "ok" } else { "fail" }) -Detail $(if ($nodeCommand) { $nodeCommand.Source } else { "node not found in PATH" })
|
|
267
|
+
Add-Check -List $checks -Name "codex" -Status $(if ($codexCommand) { "ok" } else { "fail" }) -Detail $(if ($codexCommand) { $codexCommand.Source } else { "codex not found in PATH" })
|
|
268
|
+
|
|
269
|
+
if ($status.plugin_root) {
|
|
270
|
+
Add-Check -List $checks -Name "telegram_plugin_root" -Status "ok" -Detail $status.plugin_root
|
|
271
|
+
} else {
|
|
272
|
+
Add-Check -List $checks -Name "telegram_plugin_root" -Status "fail" -Detail "Telegram plugin root could not be resolved."
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
if ($status.state_dir -and (Test-Path $status.state_dir)) {
|
|
276
|
+
Add-Check -List $checks -Name "state_dir" -Status "ok" -Detail $status.state_dir
|
|
277
|
+
} else {
|
|
278
|
+
Add-Check -List $checks -Name "state_dir" -Status "fail" -Detail ("Missing state dir: " + $status.state_dir)
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
$activeEnvPath = Join-Path $status.state_dir ".env"
|
|
282
|
+
$activeEnv = Read-DotEnvFile -Path $activeEnvPath
|
|
283
|
+
$legacyEnvPath = Join-Path $env:USERPROFILE ".codex\channels\codexlink-telegram\.env"
|
|
284
|
+
$legacyEnv = Read-DotEnvFile -Path $legacyEnvPath
|
|
285
|
+
|
|
286
|
+
$tokenValue = ""
|
|
287
|
+
$tokenSource = ""
|
|
198
288
|
if (Test-TelegramTokenFormat -Value $activeEnv["BLUN_TELEGRAM_BOT_TOKEN"]) {
|
|
199
289
|
$tokenValue = [string]$activeEnv["BLUN_TELEGRAM_BOT_TOKEN"]
|
|
200
290
|
$tokenSource = "state env"
|
|
@@ -208,10 +298,10 @@ if (Test-TelegramTokenFormat -Value $activeEnv["BLUN_TELEGRAM_BOT_TOKEN"]) {
|
|
|
208
298
|
$tokenValue = [string]$legacyEnv["TELEGRAM_BOT_TOKEN"]
|
|
209
299
|
$tokenSource = "legacy env fallback legacy key"
|
|
210
300
|
}
|
|
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 = ""
|
|
301
|
+
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." })
|
|
302
|
+
|
|
303
|
+
$allowedChatIds = ""
|
|
304
|
+
$allowedChatSource = ""
|
|
215
305
|
if (Test-AllowedChatIdsFormat -Value $activeEnv["BLUN_TELEGRAM_ALLOWED_CHAT_ID"]) {
|
|
216
306
|
$allowedChatIds = [string]$activeEnv["BLUN_TELEGRAM_ALLOWED_CHAT_ID"]
|
|
217
307
|
$allowedChatSource = "state env"
|
|
@@ -226,57 +316,84 @@ if (Test-AllowedChatIdsFormat -Value $activeEnv["BLUN_TELEGRAM_ALLOWED_CHAT_ID"]
|
|
|
226
316
|
$allowedChatSource = "legacy env fallback legacy key"
|
|
227
317
|
}
|
|
228
318
|
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
|
-
|
|
319
|
+
|
|
230
320
|
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
321
|
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
322
|
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." })
|
|
323
|
+
$loadedThreads = @($status.loaded_threads | Where-Object { $_ })
|
|
324
|
+
$threadVisibilityStatus = "ok"
|
|
325
|
+
$threadVisibilityDetail = "loaded=" + [string]$loadedThreads.Count
|
|
326
|
+
if ($loadedThreads.Count -gt 1) {
|
|
327
|
+
$threadVisibilityStatus = "warn"
|
|
328
|
+
$threadVisibilityDetail = "multiple loaded threads: " + (($loadedThreads | ForEach-Object { [string]$_ }) -join ",") + ". Run telegram-doctor --fix, then restart telegram-plugin."
|
|
329
|
+
} elseif ($status.active_thread_id -and $loadedThreads.Count -eq 1 -and ([string]$loadedThreads[0]) -ne ([string]$status.active_thread_id)) {
|
|
330
|
+
$threadVisibilityStatus = "warn"
|
|
331
|
+
$threadVisibilityDetail = "bound thread differs from loaded visible thread. Run telegram-doctor --fix, then restart telegram-plugin."
|
|
332
|
+
}
|
|
333
|
+
Add-Check -List $checks -Name "thread_visibility" -Status $threadVisibilityStatus -Detail $threadVisibilityDetail
|
|
233
334
|
Add-Check -List $checks -Name "frontend_owner" -Status $(if ($status.frontend_owner_alive) { "ok" } else { "warn" }) -Detail ("pid=" + [string]$status.frontend_owner_pid + " alive=" + [string]$status.frontend_owner_alive)
|
|
234
335
|
Add-Check -List $checks -Name "queue_notifier" -Status $(if (($null -eq $status.queue_notifier_pid) -or ($status.queue_notifier_alive)) { "ok" } else { "warn" }) -Detail ("pid=" + [string]$status.queue_notifier_pid + " alive=" + [string]$status.queue_notifier_alive)
|
|
235
336
|
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
337
|
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
338
|
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
|
-
|
|
339
|
+
|
|
340
|
+
if ($status.last_inbound) {
|
|
341
|
+
$lastInboundSummary = [string]::Format(
|
|
342
|
+
"chat={0} message={1} type={2} thread={3}",
|
|
343
|
+
$status.last_inbound.chatId,
|
|
344
|
+
$status.last_inbound.messageId,
|
|
345
|
+
$status.last_inbound.chatType,
|
|
346
|
+
$(if ($status.last_inbound.telegramThreadId) { $status.last_inbound.telegramThreadId } else { "-" })
|
|
347
|
+
)
|
|
348
|
+
Add-Check -List $checks -Name "last_inbound" -Status "ok" -Detail $lastInboundSummary
|
|
349
|
+
} else {
|
|
350
|
+
Add-Check -List $checks -Name "last_inbound" -Status "warn" -Detail "No inbound Telegram message recorded yet."
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
if ($status.last_outbound) {
|
|
354
|
+
$lastOutboundSummary = [string]::Format(
|
|
355
|
+
"chat={0} message={1} reply_to={2} thread={3}",
|
|
356
|
+
$status.last_outbound.chatId,
|
|
357
|
+
$status.last_outbound.messageId,
|
|
358
|
+
$(if ($status.last_outbound.replyToMessageId) { $status.last_outbound.replyToMessageId } else { "-" }),
|
|
359
|
+
$(if ($status.last_outbound.telegramThreadId) { $status.last_outbound.telegramThreadId } else { "-" })
|
|
360
|
+
)
|
|
361
|
+
Add-Check -List $checks -Name "last_outbound" -Status "ok" -Detail $lastOutboundSummary
|
|
362
|
+
} else {
|
|
363
|
+
Add-Check -List $checks -Name "last_outbound" -Status "warn" -Detail "No outbound Telegram message recorded yet."
|
|
364
|
+
}
|
|
365
|
+
|
|
265
366
|
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 ($
|
|
278
|
-
$
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
367
|
+
|
|
368
|
+
$result = [ordered]@{
|
|
369
|
+
profile = $status.profile
|
|
370
|
+
overall = Get-OverallStatus -Checks $checks
|
|
371
|
+
runtime_root = $runtimeRoot
|
|
372
|
+
state_dir = $status.state_dir
|
|
373
|
+
plugin_root = $status.plugin_root
|
|
374
|
+
checks = $checks
|
|
375
|
+
status = $status
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
if ($Fix) {
|
|
379
|
+
$fixActions = Invoke-RuntimeFix -Status $status -RuntimeRoot $runtimeRoot
|
|
380
|
+
$result["fix_actions"] = $fixActions
|
|
381
|
+
if ($Json) {
|
|
382
|
+
$result | ConvertTo-Json -Depth 8
|
|
383
|
+
exit 0
|
|
384
|
+
}
|
|
385
|
+
Write-DoctorReport -Result $result -TokenSource $tokenSource -AllowedChatSource $allowedChatSource
|
|
386
|
+
Write-Host ""
|
|
387
|
+
Write-Host "Fix angewendet. Starte danach neu: blun-codex --profile $($result.profile) telegram-plugin" -ForegroundColor Yellow
|
|
388
|
+
if ($fixActions.Count -gt 0) {
|
|
389
|
+
Write-Host ("Aktionen: " + ($fixActions -join ", ")) -ForegroundColor DarkGray
|
|
390
|
+
}
|
|
391
|
+
exit 0
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
if ($Json) {
|
|
395
|
+
$result | ConvertTo-Json -Depth 8
|
|
396
|
+
exit 0
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
Write-DoctorReport -Result $result -TokenSource $tokenSource -AllowedChatSource $allowedChatSource
|
|
@@ -79,6 +79,10 @@ function containsToken(text, token) {
|
|
|
79
79
|
return new RegExp(`(^|[^a-z0-9_])${escaped}([^a-z0-9_]|$)`, "i").test(String(text || ""));
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
+
function shouldAckOnlyAddressPing() {
|
|
83
|
+
return String(process.env.BLUN_TELEGRAM_PING_ACK_ONLY || "").trim() === "1";
|
|
84
|
+
}
|
|
85
|
+
|
|
82
86
|
function looksLikeEscalation(text) {
|
|
83
87
|
const value = foldTriggerText(text);
|
|
84
88
|
if (!value) {
|
|
@@ -445,6 +449,10 @@ function isAgentAddressed(config, text) {
|
|
|
445
449
|
if (workDirective) {
|
|
446
450
|
return true;
|
|
447
451
|
}
|
|
452
|
+
|
|
453
|
+
if (containsToken(normalized, name)) {
|
|
454
|
+
return true;
|
|
455
|
+
}
|
|
448
456
|
}
|
|
449
457
|
|
|
450
458
|
return false;
|
|
@@ -1238,12 +1246,14 @@ function normalizeTelegramThreadId(value) {
|
|
|
1238
1246
|
return String(value || "").trim();
|
|
1239
1247
|
}
|
|
1240
1248
|
|
|
1241
|
-
function isAllowedChat(config,
|
|
1249
|
+
function isAllowedChat(config, inbound) {
|
|
1242
1250
|
const allowed = Array.isArray(config.allowedChatIds) ? config.allowedChatIds : [];
|
|
1243
1251
|
if (allowed.length === 0) {
|
|
1244
1252
|
return true;
|
|
1245
1253
|
}
|
|
1246
|
-
|
|
1254
|
+
const chatId = String(inbound?.chatId || inbound || "").trim();
|
|
1255
|
+
const userId = String(inbound?.userId || "").trim();
|
|
1256
|
+
return allowed.includes(chatId) || (userId && allowed.includes(userId));
|
|
1247
1257
|
}
|
|
1248
1258
|
|
|
1249
1259
|
function splitTelegramText(text, maxLength = 3500) {
|
|
@@ -2125,9 +2135,9 @@ export async function pollOnce() {
|
|
|
2125
2135
|
continue;
|
|
2126
2136
|
}
|
|
2127
2137
|
const inbound = normalizeInbound(update.message);
|
|
2128
|
-
if (!isAllowedChat(config, inbound
|
|
2138
|
+
if (!isAllowedChat(config, inbound)) {
|
|
2129
2139
|
ignored += 1;
|
|
2130
|
-
appendLog(config.paths.activityFile, `IGNORED chat=${inbound.chatId} message=${inbound.messageId}`);
|
|
2140
|
+
appendLog(config.paths.activityFile, `IGNORED chat=${inbound.chatId} user=${inbound.userId || "-"} message=${inbound.messageId}`);
|
|
2131
2141
|
continue;
|
|
2132
2142
|
}
|
|
2133
2143
|
if (String(inbound.chatType || "") === "private" && looksLikeMnemoIdleLoopBrief(inbound.text)) {
|
|
@@ -2268,6 +2278,11 @@ function isRealtimeAppServerEntry(entry) {
|
|
|
2268
2278
|
return chatType === "private" || relevance === "direct" || relevance === "lane";
|
|
2269
2279
|
}
|
|
2270
2280
|
|
|
2281
|
+
function isGroupChatEntry(entry) {
|
|
2282
|
+
const chatType = String(entry?.chatType || "").trim().toLowerCase();
|
|
2283
|
+
return chatType === "group" || chatType === "supergroup";
|
|
2284
|
+
}
|
|
2285
|
+
|
|
2271
2286
|
export async function injectNext(threadId, options = {}) {
|
|
2272
2287
|
const config = loadConfig();
|
|
2273
2288
|
const state = loadState(config);
|
|
@@ -2311,7 +2326,7 @@ export async function injectNext(threadId, options = {}) {
|
|
|
2311
2326
|
};
|
|
2312
2327
|
}
|
|
2313
2328
|
|
|
2314
|
-
if (isAddressOnlyPing(config, next.text) && !looksLikeBotSender(next)) {
|
|
2329
|
+
if (shouldAckOnlyAddressPing() && isAddressOnlyPing(config, next.text) && !looksLikeBotSender(next) && !isGroupChatEntry(next)) {
|
|
2315
2330
|
next.status = "delivered";
|
|
2316
2331
|
next.deliveredAt = nowIso();
|
|
2317
2332
|
next.threadId = null;
|