@blunking/codexlink 0.1.18 → 0.1.19

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 CHANGED
@@ -124,16 +124,23 @@ BLUN_TELEGRAM_OTHER_AGENT_NAMES=frida,angel,dieter,alfred
124
124
  ```
125
125
 
126
126
  Doctor:
127
-
128
- ```powershell
129
- blun-codex telegram-doctor
130
- ```
131
-
132
- JSON doctor output:
133
-
134
- ```powershell
135
- blun-codex telegram-doctor --json
136
- ```
127
+
128
+ ```powershell
129
+ blun-codex telegram-doctor
130
+ ```
131
+
132
+ Runtime automatisch bereinigen, wenn mehrere Threads geladen sind oder eine alte Bindung klemmt:
133
+
134
+ ```powershell
135
+ blun-codex telegram-doctor --fix
136
+ blun-codex telegram-plugin
137
+ ```
138
+
139
+ JSON doctor output:
140
+
141
+ ```powershell
142
+ blun-codex telegram-doctor --json
143
+ ```
137
144
 
138
145
  Dry run:
139
146
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blunking/codexlink",
3
- "version": "0.1.18",
3
+ "version": "0.1.19",
4
4
  "description": "BLUN CLI launcher with Telegram channel support for one visible session.",
5
5
  "license": "MIT",
6
6
  "private": false,
@@ -1,11 +1,12 @@
1
- param(
2
- [string]$Profile = "default",
3
- [switch]$Json
4
- )
1
+ param(
2
+ [string]$Profile = "default",
3
+ [switch]$Json,
4
+ [switch]$Fix
5
+ )
5
6
 
6
7
  $ErrorActionPreference = "Stop"
7
8
 
8
- function Read-DotEnvFile {
9
+ function Read-DotEnvFile {
9
10
  param([string]$Path)
10
11
  $values = @{}
11
12
  if (-not (Test-Path $Path)) { return $values }
@@ -133,6 +134,95 @@ function Write-DoctorReport {
133
134
  }
134
135
  }
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
167
+ }
168
+ }
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,
@@ -230,6 +320,17 @@ Add-Check -List $checks -Name "allowed_chat_ids" -Status $(if ($allowedChatIds)
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)
@@ -264,15 +365,31 @@ if ($status.last_outbound) {
264
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
367
 
267
- $result = [ordered]@{
368
+ $result = [ordered]@{
268
369
  profile = $status.profile
269
370
  overall = Get-OverallStatus -Checks $checks
270
371
  runtime_root = $runtimeRoot
271
372
  state_dir = $status.state_dir
272
373
  plugin_root = $status.plugin_root
273
374
  checks = $checks
274
- status = $status
275
- }
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
+ }
276
393
 
277
394
  if ($Json) {
278
395
  $result | ConvertTo-Json -Depth 8
@@ -1238,12 +1238,14 @@ function normalizeTelegramThreadId(value) {
1238
1238
  return String(value || "").trim();
1239
1239
  }
1240
1240
 
1241
- function isAllowedChat(config, chatId) {
1241
+ function isAllowedChat(config, inbound) {
1242
1242
  const allowed = Array.isArray(config.allowedChatIds) ? config.allowedChatIds : [];
1243
1243
  if (allowed.length === 0) {
1244
1244
  return true;
1245
1245
  }
1246
- return allowed.includes(String(chatId || "").trim());
1246
+ const chatId = String(inbound?.chatId || inbound || "").trim();
1247
+ const userId = String(inbound?.userId || "").trim();
1248
+ return allowed.includes(chatId) || (userId && allowed.includes(userId));
1247
1249
  }
1248
1250
 
1249
1251
  function splitTelegramText(text, maxLength = 3500) {
@@ -2125,9 +2127,9 @@ export async function pollOnce() {
2125
2127
  continue;
2126
2128
  }
2127
2129
  const inbound = normalizeInbound(update.message);
2128
- if (!isAllowedChat(config, inbound.chatId)) {
2130
+ if (!isAllowedChat(config, inbound)) {
2129
2131
  ignored += 1;
2130
- appendLog(config.paths.activityFile, `IGNORED chat=${inbound.chatId} message=${inbound.messageId}`);
2132
+ appendLog(config.paths.activityFile, `IGNORED chat=${inbound.chatId} user=${inbound.userId || "-"} message=${inbound.messageId}`);
2131
2133
  continue;
2132
2134
  }
2133
2135
  if (String(inbound.chatType || "") === "private" && looksLikeMnemoIdleLoopBrief(inbound.text)) {