@blunking/codexlink 0.1.2 → 0.1.10
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 +67 -49
- package/package.json +40 -38
- package/start-codex-agent.ps1 +179 -71
- package/telegram-doctor.ps1 +34 -20
- package/telegram-plugin/.env.example +1 -0
- package/telegram-plugin/README.md +3 -0
- package/telegram-plugin/dispatcher.js +4 -0
- package/telegram-plugin/lib/bridge.js +1550 -86
- package/telegram-plugin/lib/codex.js +142 -21
- package/telegram-plugin/lib/env.js +29 -1
- package/telegram-plugin/lib/paths.js +7 -1
- package/telegram-plugin/lib/sidecars.js +12 -1
- package/telegram-plugin/lib/singleton.js +66 -0
- package/telegram-plugin/lib/storage.js +66 -25
- package/telegram-plugin/lib/telegram.js +8 -0
- package/telegram-plugin/poller.js +4 -0
- package/telegram-plugin/responder.js +4 -0
- package/telegram-setup.ps1 +217 -58
- package/telegram-status.ps1 +292 -182
- package/telegram-title-embed.ps1 +442 -0
- package/telegram-title-watcher.ps1 +454 -0
package/README.md
CHANGED
|
@@ -11,15 +11,16 @@
|
|
|
11
11
|
|
|
12
12
|
CodexLink is the BLUN launcher for one visible CLI session with optional Telegram delivery.
|
|
13
13
|
|
|
14
|
-
It keeps transport and queueing around the operator, without spinning up a hidden second session.
|
|
15
|
-
|
|
16
|
-
Telegram delivery is serial by default:
|
|
17
|
-
|
|
18
|
-
- inbound messages land in queue first
|
|
19
|
-
- active work is not interrupted immediately
|
|
20
|
-
- direct messages wait until the visible session is quiet
|
|
21
|
-
- ambient group noise stays queued until it is relevant or manually drained
|
|
22
|
-
- escalation-style messages can still jump the line
|
|
14
|
+
It keeps transport and queueing around the operator, without spinning up a hidden second session.
|
|
15
|
+
|
|
16
|
+
Telegram delivery is serial by default:
|
|
17
|
+
|
|
18
|
+
- inbound messages land in queue first
|
|
19
|
+
- active work is not interrupted immediately
|
|
20
|
+
- direct messages wait until the visible session is quiet
|
|
21
|
+
- ambient group noise stays queued until it is relevant or manually drained
|
|
22
|
+
- escalation-style messages can still jump the line
|
|
23
|
+
- stale pending replies time out automatically, so the queue cannot block forever
|
|
23
24
|
|
|
24
25
|
## Install
|
|
25
26
|
|
|
@@ -57,12 +58,11 @@ Telegram aktivieren:
|
|
|
57
58
|
blun-codex telegram-plugin
|
|
58
59
|
```
|
|
59
60
|
|
|
60
|
-
Wenn Telegram noch nicht eingerichtet ist, startet automatisch ein kurzer Setup-Flow und fragt:
|
|
61
|
-
|
|
62
|
-
- Telegram Bot Token
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
Die Werte werden automatisch lokal an die richtige Stelle geschrieben. Du musst keine `.env`-Datei suchen.
|
|
61
|
+
Wenn Telegram noch nicht eingerichtet ist, startet automatisch ein kurzer Setup-Flow und fragt:
|
|
62
|
+
|
|
63
|
+
- Telegram Bot Token
|
|
64
|
+
|
|
65
|
+
Danach oeffnest du Telegram und sendest eine Nachricht an den Bot. CodexLink erkennt Chat oder Gruppe automatisch und schreibt alles lokal an die richtige Stelle. Du musst keine Chat-ID suchen und keine `.env`-Datei bearbeiten.
|
|
66
66
|
|
|
67
67
|
Pruefen:
|
|
68
68
|
|
|
@@ -108,7 +108,18 @@ Status:
|
|
|
108
108
|
blun-codex telegram-status
|
|
109
109
|
```
|
|
110
110
|
|
|
111
|
-
|
|
111
|
+
Wenn waehrend einer laufenden Arbeit Telegram-Nachrichten gepuffert werden, bleibt die sichtbare CLI-Eingabe unberuehrt. Pending-Nachrichten bleiben im Fenstertitel/Status sichtbar, bis die Antwort raus ist oder sie wirklich ablaufen. Den Queue-Stand kannst du jederzeit mit `blun-codex telegram-status` pruefen.
|
|
112
|
+
|
|
113
|
+
Der automatische Progress-Hinweis ist bewusst defensiv: standardmaessig sendet Telegram nur finale Antworten plus bei laengeren echten Arbeitslaeufen einen neutralen Status. Interne Commentary-Texte werden nicht als zweite fachliche Antwort gespiegelt. Wer das alte Verhalten will, kann `BLUN_TELEGRAM_PROGRESS_RELAY=commentary` setzen; mit `off` werden Progress-Hinweise ganz deaktiviert.
|
|
114
|
+
|
|
115
|
+
Wenn mehrere Agents denselben Gruppenchat nutzen, kann ein Agent andere Agent-Namen als Fremdroute markieren. Dann werden Owner-Nachrichten wie `Frida mach weiter` nicht in Ottos Session gezogen:
|
|
116
|
+
|
|
117
|
+
```text
|
|
118
|
+
BLUN_TELEGRAM_MENTION_NAMES=otto
|
|
119
|
+
BLUN_TELEGRAM_OTHER_AGENT_NAMES=frida,angel,dieter,alfred
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Doctor:
|
|
112
123
|
|
|
113
124
|
```powershell
|
|
114
125
|
blun-codex telegram-doctor
|
|
@@ -160,39 +171,39 @@ Why this matters:
|
|
|
160
171
|
- starting a second operator on `default` will replace the first `default` runtime
|
|
161
172
|
- a private profile gives that operator a separate runtime slot, state directory, and Mnemo binding
|
|
162
173
|
|
|
163
|
-
For internal/private profiles:
|
|
164
|
-
|
|
165
|
-
- keep the profile local on the machine
|
|
166
|
-
- give it its own `agent_name`
|
|
167
|
-
- give it its own Telegram state directory
|
|
168
|
-
- do not ship internal agent profiles in the public package
|
|
169
|
-
|
|
170
|
-
Local private profiles are loaded from:
|
|
171
|
-
|
|
172
|
-
```text
|
|
173
|
-
%USERPROFILE%\.codex\profiles\codexlink\<name>.json
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
Example:
|
|
177
|
-
|
|
178
|
-
```powershell
|
|
179
|
-
blun-codex --profile frida telegram-plugin
|
|
180
|
-
```
|
|
181
|
-
|
|
182
|
-
looks for:
|
|
183
|
-
|
|
184
|
-
```text
|
|
185
|
-
%USERPROFILE%\.codex\profiles\codexlink\frida.json
|
|
186
|
-
```
|
|
174
|
+
For internal/private profiles:
|
|
175
|
+
|
|
176
|
+
- keep the profile local on the machine
|
|
177
|
+
- give it its own `agent_name`
|
|
178
|
+
- give it its own Telegram state directory
|
|
179
|
+
- do not ship internal agent profiles in the public package
|
|
180
|
+
|
|
181
|
+
Local private profiles are loaded from:
|
|
182
|
+
|
|
183
|
+
```text
|
|
184
|
+
%USERPROFILE%\.codex\profiles\codexlink\<name>.json
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
Example:
|
|
188
|
+
|
|
189
|
+
```powershell
|
|
190
|
+
blun-codex --profile frida telegram-plugin
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
looks for:
|
|
194
|
+
|
|
195
|
+
```text
|
|
196
|
+
%USERPROFILE%\.codex\profiles\codexlink\frida.json
|
|
197
|
+
```
|
|
187
198
|
|
|
188
199
|
## What it does
|
|
189
200
|
|
|
190
201
|
- starts one consistent local CLI runtime
|
|
191
202
|
- writes a launch record into `.codex/runtimes/default/`
|
|
192
203
|
- keeps Telegram queue state under `.codex/channels/telegram-default/`
|
|
193
|
-
- attaches Telegram delivery to the same visible session
|
|
194
|
-
- defers automatic Telegram delivery until the foreground session is idle
|
|
195
|
-
- keeps poller, dispatcher, and reply relay separate from the foreground operator
|
|
204
|
+
- attaches Telegram delivery to the same visible session
|
|
205
|
+
- defers automatic Telegram delivery until the foreground session is idle
|
|
206
|
+
- keeps poller, dispatcher, and reply relay separate from the foreground operator
|
|
196
207
|
|
|
197
208
|
## What it does not do
|
|
198
209
|
|
|
@@ -229,12 +240,19 @@ The bundled plugin lives under `telegram-plugin/` and contains:
|
|
|
229
240
|
|
|
230
241
|
## First-run behavior
|
|
231
242
|
|
|
232
|
-
`blun-codex telegram-plugin` now behaves like a guided setup for normal users:
|
|
233
|
-
|
|
234
|
-
1. check whether Telegram is already configured
|
|
235
|
-
2. ask for missing Bot Token
|
|
236
|
-
3.
|
|
237
|
-
4.
|
|
243
|
+
`blun-codex telegram-plugin` now behaves like a guided setup for normal users:
|
|
244
|
+
|
|
245
|
+
1. check whether Telegram is already configured
|
|
246
|
+
2. ask only for a missing Bot Token
|
|
247
|
+
3. wait for one Telegram message to the bot
|
|
248
|
+
4. detect and store the chat/group ID automatically
|
|
249
|
+
5. continue into Telegram mode
|
|
250
|
+
|
|
251
|
+
Allowed Chat ID(s) are no longer typed by hand. To pair a different chat or group later, run:
|
|
252
|
+
|
|
253
|
+
```powershell
|
|
254
|
+
blun-codex telegram-setup
|
|
255
|
+
```
|
|
238
256
|
|
|
239
257
|
If something is missing later, `blun-codex telegram-doctor` tells you exactly what is missing and what to run next.
|
|
240
258
|
|
package/package.json
CHANGED
|
@@ -1,38 +1,40 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@blunking/codexlink",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "BLUN CLI launcher with Telegram channel support for one visible session.",
|
|
5
|
-
"license": "MIT",
|
|
6
|
-
"private": false,
|
|
7
|
-
"bin": {
|
|
8
|
-
"blun-codex": "
|
|
9
|
-
"codexlink": "
|
|
10
|
-
},
|
|
11
|
-
"files": [
|
|
12
|
-
"bin/",
|
|
13
|
-
"profiles/default.json",
|
|
14
|
-
"telegram-plugin/",
|
|
15
|
-
"README.md",
|
|
16
|
-
"LICENSE",
|
|
17
|
-
"blun-codex.cmd",
|
|
18
|
-
"blun-codex.ps1",
|
|
19
|
-
"start-codex-agent.ps1",
|
|
20
|
-
"start-codex.cmd",
|
|
21
|
-
"telegram-
|
|
22
|
-
"telegram-
|
|
23
|
-
"telegram-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "@blunking/codexlink",
|
|
3
|
+
"version": "0.1.10",
|
|
4
|
+
"description": "BLUN CLI launcher with Telegram channel support for one visible session.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"private": false,
|
|
7
|
+
"bin": {
|
|
8
|
+
"blun-codex": "bin/blun-codex.js",
|
|
9
|
+
"codexlink": "bin/blun-codex.js"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"bin/",
|
|
13
|
+
"profiles/default.json",
|
|
14
|
+
"telegram-plugin/",
|
|
15
|
+
"README.md",
|
|
16
|
+
"LICENSE",
|
|
17
|
+
"blun-codex.cmd",
|
|
18
|
+
"blun-codex.ps1",
|
|
19
|
+
"start-codex-agent.ps1",
|
|
20
|
+
"start-codex.cmd",
|
|
21
|
+
"telegram-title-embed.ps1",
|
|
22
|
+
"telegram-status.ps1",
|
|
23
|
+
"telegram-doctor.ps1",
|
|
24
|
+
"telegram-setup.ps1",
|
|
25
|
+
"telegram-title-watcher.ps1"
|
|
26
|
+
],
|
|
27
|
+
"keywords": [
|
|
28
|
+
"blun",
|
|
29
|
+
"codexlink",
|
|
30
|
+
"telegram",
|
|
31
|
+
"runtime",
|
|
32
|
+
"cli"
|
|
33
|
+
],
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"@modelcontextprotocol/sdk": "^1.0.0"
|
|
36
|
+
},
|
|
37
|
+
"engines": {
|
|
38
|
+
"node": ">=20"
|
|
39
|
+
}
|
|
40
|
+
}
|
package/start-codex-agent.ps1
CHANGED
|
@@ -21,42 +21,42 @@ function Get-JsonFile {
|
|
|
21
21
|
return Get-Content -Raw -Path $Path | ConvertFrom-Json
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
function Try-GetJsonFile {
|
|
25
|
-
param([string]$Path)
|
|
26
|
-
if (-not (Test-Path $Path)) {
|
|
27
|
-
return $null
|
|
24
|
+
function Try-GetJsonFile {
|
|
25
|
+
param([string]$Path)
|
|
26
|
+
if (-not (Test-Path $Path)) {
|
|
27
|
+
return $null
|
|
28
28
|
}
|
|
29
29
|
try {
|
|
30
30
|
return Get-Content -Raw -Path $Path | ConvertFrom-Json
|
|
31
31
|
} catch {
|
|
32
32
|
return $null
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function Get-ProfilePath {
|
|
37
|
-
param(
|
|
38
|
-
[string]$RuntimeRoot,
|
|
39
|
-
[string]$ProfileName
|
|
40
|
-
)
|
|
41
|
-
|
|
42
|
-
$normalized = [string]$ProfileName
|
|
43
|
-
if (-not $normalized) { $normalized = "" }
|
|
44
|
-
$normalized = $normalized.ToLower()
|
|
45
|
-
$candidates = @()
|
|
46
|
-
if ($env:BLUN_CODEX_PROFILE_ROOT) {
|
|
47
|
-
$candidates += (Join-Path $env:BLUN_CODEX_PROFILE_ROOT ($normalized + ".json"))
|
|
48
|
-
}
|
|
49
|
-
$candidates += (Join-Path $env:USERPROFILE (".codex\\profiles\\codexlink\\" + $normalized + ".json"))
|
|
50
|
-
$candidates += (Join-Path $RuntimeRoot ("profiles\\" + $normalized + ".json"))
|
|
51
|
-
|
|
52
|
-
foreach ($candidate in $candidates) {
|
|
53
|
-
if ($candidate -and (Test-Path $candidate)) {
|
|
54
|
-
return $candidate
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
return $candidates[-1]
|
|
59
|
-
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function Get-ProfilePath {
|
|
37
|
+
param(
|
|
38
|
+
[string]$RuntimeRoot,
|
|
39
|
+
[string]$ProfileName
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
$normalized = [string]$ProfileName
|
|
43
|
+
if (-not $normalized) { $normalized = "" }
|
|
44
|
+
$normalized = $normalized.ToLower()
|
|
45
|
+
$candidates = @()
|
|
46
|
+
if ($env:BLUN_CODEX_PROFILE_ROOT) {
|
|
47
|
+
$candidates += (Join-Path $env:BLUN_CODEX_PROFILE_ROOT ($normalized + ".json"))
|
|
48
|
+
}
|
|
49
|
+
$candidates += (Join-Path $env:USERPROFILE (".codex\\profiles\\codexlink\\" + $normalized + ".json"))
|
|
50
|
+
$candidates += (Join-Path $RuntimeRoot ("profiles\\" + $normalized + ".json"))
|
|
51
|
+
|
|
52
|
+
foreach ($candidate in $candidates) {
|
|
53
|
+
if ($candidate -and (Test-Path $candidate)) {
|
|
54
|
+
return $candidate
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return $candidates[-1]
|
|
59
|
+
}
|
|
60
60
|
|
|
61
61
|
function Ensure-Dir {
|
|
62
62
|
param([string]$Path)
|
|
@@ -113,7 +113,7 @@ function Get-FreeTcpPort {
|
|
|
113
113
|
}
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
-
function Stop-ProcessTree {
|
|
116
|
+
function Stop-ProcessTree {
|
|
117
117
|
param([int[]]$RootIds)
|
|
118
118
|
|
|
119
119
|
$all = @(Get-CimInstance Win32_Process)
|
|
@@ -144,9 +144,9 @@ function Stop-ProcessTree {
|
|
|
144
144
|
} catch {
|
|
145
145
|
}
|
|
146
146
|
}
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
function Wait-TcpPort {
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function Wait-TcpPort {
|
|
150
150
|
param(
|
|
151
151
|
[string]$HostName,
|
|
152
152
|
[int]$Port,
|
|
@@ -301,8 +301,8 @@ function Quote-PowerShellLiteral {
|
|
|
301
301
|
}
|
|
302
302
|
|
|
303
303
|
$runtimeRoot = Split-Path -Parent $MyInvocation.MyCommand.Path
|
|
304
|
-
$profilePath = Get-ProfilePath -RuntimeRoot $runtimeRoot -ProfileName $Agent
|
|
305
|
-
$profile = Get-JsonFile -Path $profilePath
|
|
304
|
+
$profilePath = Get-ProfilePath -RuntimeRoot $runtimeRoot -ProfileName $Agent
|
|
305
|
+
$profile = Get-JsonFile -Path $profilePath
|
|
306
306
|
|
|
307
307
|
$resolvedWorkspace = if ($Workspace) { $Workspace } elseif ($profile.workspace) { $profile.workspace } else { (Get-Location).Path }
|
|
308
308
|
$resolvedWorkspace = Resolve-ConfiguredPath -Value $resolvedWorkspace
|
|
@@ -527,13 +527,24 @@ if ($useRemoteAppServer) {
|
|
|
527
527
|
if ($telegramAllowedChatId) {
|
|
528
528
|
$stateEnv["BLUN_TELEGRAM_ALLOWED_CHAT_ID"] = $telegramAllowedChatId
|
|
529
529
|
}
|
|
530
|
-
$stateEnv["BLUN_TELEGRAM_PLUGIN_MODE"] = $TelegramMode
|
|
531
|
-
$stateEnv["BLUN_TELEGRAM_APP_SERVER_WS_URL"] = $telegramAppServerWsUrl
|
|
532
|
-
$stateEnv["BLUN_TELEGRAM_THREAD_ID"] = ""
|
|
533
|
-
Write-DotEnvFile -Path $envFilePath -Values $stateEnv
|
|
534
|
-
Write-DebugStage -Path $debugLogPath -Message ("ENV_WRITTEN ws_url=" + $telegramAppServerWsUrl + " env_file=" + $envFilePath)
|
|
535
|
-
|
|
536
|
-
|
|
530
|
+
$stateEnv["BLUN_TELEGRAM_PLUGIN_MODE"] = $TelegramMode
|
|
531
|
+
$stateEnv["BLUN_TELEGRAM_APP_SERVER_WS_URL"] = $telegramAppServerWsUrl
|
|
532
|
+
$stateEnv["BLUN_TELEGRAM_THREAD_ID"] = ""
|
|
533
|
+
Write-DotEnvFile -Path $envFilePath -Values $stateEnv
|
|
534
|
+
Write-DebugStage -Path $debugLogPath -Message ("ENV_WRITTEN ws_url=" + $telegramAppServerWsUrl + " env_file=" + $envFilePath)
|
|
535
|
+
$telegramStateFile = Join-Path $telegramStateDir "state.json"
|
|
536
|
+
$telegramState = Try-GetJsonFile -Path $telegramStateFile
|
|
537
|
+
if ($null -ne $telegramState) {
|
|
538
|
+
if ($telegramState.PSObject.Properties.Name.Contains("currentThreadId")) {
|
|
539
|
+
$telegramState.currentThreadId = ""
|
|
540
|
+
} else {
|
|
541
|
+
$telegramState | Add-Member -NotePropertyName "currentThreadId" -NotePropertyValue ""
|
|
542
|
+
}
|
|
543
|
+
Write-TextFileWithRetry -Path $telegramStateFile -Content ($telegramState | ConvertTo-Json -Depth 10)
|
|
544
|
+
Write-DebugStage -Path $debugLogPath -Message "STATE_THREAD_CLEARED"
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
Set-EnvVar "BLUN_TELEGRAM_APP_SERVER_WS_URL" $telegramAppServerWsUrl
|
|
537
548
|
|
|
538
549
|
$appServerPidFile = Join-Path $agentRuntimeDir "app-server.pid"
|
|
539
550
|
$appServerInfoFile = Join-Path $agentRuntimeDir "app-server.json"
|
|
@@ -545,6 +556,7 @@ if ($useRemoteAppServer) {
|
|
|
545
556
|
$oldPids = @()
|
|
546
557
|
if ($previousRuntime.frontend_host_pid) { $oldPids += [int]$previousRuntime.frontend_host_pid }
|
|
547
558
|
if ($previousRuntime.app_server_pid) { $oldPids += [int]$previousRuntime.app_server_pid }
|
|
559
|
+
if ($previousRuntime.queue_notifier_pid) { $oldPids += [int]$previousRuntime.queue_notifier_pid }
|
|
548
560
|
if ($previousRuntime.poller_pid) { $oldPids += [int]$previousRuntime.poller_pid }
|
|
549
561
|
if ($previousRuntime.dispatcher_pid) { $oldPids += [int]$previousRuntime.dispatcher_pid }
|
|
550
562
|
if ($previousRuntime.responder_pid) { $oldPids += [int]$previousRuntime.responder_pid }
|
|
@@ -666,26 +678,57 @@ if ($PrintOnly) {
|
|
|
666
678
|
if ($useRemoteAppServer) {
|
|
667
679
|
$codexScript = (Get-Command codex).Source
|
|
668
680
|
$currentRuntimeFile = Join-Path $agentRuntimeDir "current-remote-runtime.json"
|
|
669
|
-
$windowTitle = "BLUN Codex Telegram [" + $profile.agent_name + "] " + $telegramAppServerWsUrl
|
|
670
|
-
$
|
|
681
|
+
$windowTitle = "BLUN Codex Telegram [" + $profile.agent_name + "] " + $telegramAppServerWsUrl
|
|
682
|
+
$titleEmbedScript = Join-Path $runtimeRoot "telegram-title-embed.ps1"
|
|
683
|
+
$titleEmbedLog = Join-Path $agentRuntimeDir "title-embed.log"
|
|
684
|
+
$resumeCommand = ""
|
|
685
|
+
if (Test-Path $titleEmbedScript) {
|
|
686
|
+
$resumeCommand += "& " + (Quote-PowerShellLiteral $titleEmbedScript)
|
|
687
|
+
$resumeCommand += " -StateFile " + (Quote-PowerShellLiteral (Join-Path $telegramStateDir "state.json"))
|
|
688
|
+
$resumeCommand += " -BaseTitle " + (Quote-PowerShellLiteral $windowTitle)
|
|
689
|
+
$resumeCommand += " -LogFile " + (Quote-PowerShellLiteral $titleEmbedLog)
|
|
690
|
+
$resumeCommand += "; "
|
|
691
|
+
}
|
|
692
|
+
$resumeCommand += "$host.UI.RawUI.WindowTitle = " + (Quote-PowerShellLiteral $windowTitle) + "; & " + (Quote-PowerShellLiteral $codexScript) + " " + (($codexArgs | ForEach-Object { Quote-PowerShellLiteral $_ }) -join " ")
|
|
671
693
|
Write-DebugStage -Path $debugLogPath -Message ("FRONTEND_SPAWN command=" + $resumeCommand)
|
|
672
694
|
$frontendProcess = Start-Process -FilePath "powershell" -WorkingDirectory $resolvedWorkspace -ArgumentList @(
|
|
673
695
|
"-NoExit",
|
|
674
696
|
"-Command",
|
|
675
697
|
$resumeCommand
|
|
676
698
|
) -PassThru
|
|
677
|
-
$currentRuntime = [ordered]@{
|
|
678
|
-
ws_url = $telegramAppServerWsUrl
|
|
679
|
-
app_server_pid = $backendProcess.Id
|
|
680
|
-
frontend_host_pid = $frontendProcess.Id
|
|
681
|
-
profile = $profile.agent_name
|
|
699
|
+
$currentRuntime = [ordered]@{
|
|
700
|
+
ws_url = $telegramAppServerWsUrl
|
|
701
|
+
app_server_pid = $backendProcess.Id
|
|
702
|
+
frontend_host_pid = $frontendProcess.Id
|
|
703
|
+
profile = $profile.agent_name
|
|
682
704
|
started_at = (Get-Date).ToUniversalTime().ToString("o")
|
|
683
|
-
}
|
|
684
|
-
Write-TextFileWithRetry -Path $currentRuntimeFile -Content ($currentRuntime | ConvertTo-Json -Depth 4)
|
|
685
|
-
Write-DebugStage -Path $debugLogPath -Message "FRONTEND_SPAWNED"
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
705
|
+
}
|
|
706
|
+
Write-TextFileWithRetry -Path $currentRuntimeFile -Content ($currentRuntime | ConvertTo-Json -Depth 4)
|
|
707
|
+
Write-DebugStage -Path $debugLogPath -Message "FRONTEND_SPAWNED"
|
|
708
|
+
$sidecarsStartedEarly = $false
|
|
709
|
+
if ($sidecarManager) {
|
|
710
|
+
try {
|
|
711
|
+
& node $sidecarManager | Out-Null
|
|
712
|
+
if ($LASTEXITCODE -ne 0) {
|
|
713
|
+
Write-DebugStage -Path $debugLogPath -Message "SIDECAR_MANAGER_EARLY_FAILED"
|
|
714
|
+
} else {
|
|
715
|
+
$sidecarsStartedEarly = $true
|
|
716
|
+
Write-DebugStage -Path $debugLogPath -Message "SIDECAR_MANAGER_EARLY_OK"
|
|
717
|
+
$pollerPid = Read-PidFileValue -Path (Join-Path $telegramStateDir "poller.pid")
|
|
718
|
+
$dispatcherPid = Read-PidFileValue -Path (Join-Path $telegramStateDir "dispatcher.pid")
|
|
719
|
+
$responderPid = Read-PidFileValue -Path (Join-Path $telegramStateDir "responder.pid")
|
|
720
|
+
if ($pollerPid -gt 0) { $currentRuntime["poller_pid"] = $pollerPid }
|
|
721
|
+
if ($dispatcherPid -gt 0) { $currentRuntime["dispatcher_pid"] = $dispatcherPid }
|
|
722
|
+
if ($responderPid -gt 0) { $currentRuntime["responder_pid"] = $responderPid }
|
|
723
|
+
Write-TextFileWithRetry -Path $currentRuntimeFile -Content ($currentRuntime | ConvertTo-Json -Depth 6)
|
|
724
|
+
}
|
|
725
|
+
} catch {
|
|
726
|
+
Write-DebugStage -Path $debugLogPath -Message ("SIDECAR_MANAGER_EARLY_ERROR " + $_.Exception.Message)
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
if ($envFilePath -and $bootstrapScript) {
|
|
730
|
+
try {
|
|
731
|
+
Write-DebugStage -Path $debugLogPath -Message "LOADED_THREAD_WAIT_START"
|
|
689
732
|
$loadedIds = @()
|
|
690
733
|
for ($attempt = 1; $attempt -le 40; $attempt++) {
|
|
691
734
|
$loaded = Invoke-NodeJsonWithRetry -NodeArgs @($bootstrapScript, "list-loaded", "--ws-url", $telegramAppServerWsUrl) -Attempts 1 -DelayMs 0
|
|
@@ -697,17 +740,67 @@ if ($useRemoteAppServer) {
|
|
|
697
740
|
if ($attempt -lt 40) {
|
|
698
741
|
Start-Sleep -Milliseconds 500
|
|
699
742
|
}
|
|
700
|
-
}
|
|
701
|
-
if ($loadedIds.Count -gt 0) {
|
|
702
|
-
$activeThreadId = [string]$loadedIds[$loadedIds.Count - 1]
|
|
703
|
-
|
|
704
|
-
$
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
743
|
+
}
|
|
744
|
+
if ($loadedIds.Count -gt 0) {
|
|
745
|
+
$activeThreadId = [string]$loadedIds[$loadedIds.Count - 1]
|
|
746
|
+
$bestThreadScore = [double]::NegativeInfinity
|
|
747
|
+
foreach ($candidate in $loadedIds) {
|
|
748
|
+
$candidateThreadId = [string]$candidate
|
|
749
|
+
if ([string]::IsNullOrWhiteSpace($candidateThreadId)) {
|
|
750
|
+
continue
|
|
751
|
+
}
|
|
752
|
+
$threadScore = 0.0
|
|
753
|
+
try {
|
|
754
|
+
$candidateInfo = Invoke-NodeJsonWithRetry -NodeArgs @($bootstrapScript, "read-thread", "--ws-url", $telegramAppServerWsUrl, "--thread-id", $candidateThreadId) -Attempts 1 -DelayMs 0
|
|
755
|
+
$thread = $candidateInfo.response.result.thread
|
|
756
|
+
if ($null -ne $thread -and $null -ne $thread.createdAt) {
|
|
757
|
+
$threadScore = [double]$thread.createdAt
|
|
758
|
+
if ($threadScore -gt 0 -and $threadScore -lt 1000000000000) {
|
|
759
|
+
$threadScore = $threadScore * 1000
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
$threadSource = ""
|
|
763
|
+
$threadStatusType = ""
|
|
764
|
+
if ($null -ne $thread -and $null -ne $thread.source) {
|
|
765
|
+
$threadSource = ([string]$thread.source).ToLowerInvariant()
|
|
766
|
+
}
|
|
767
|
+
if ($null -ne $thread -and $null -ne $thread.status -and $null -ne $thread.status.type) {
|
|
768
|
+
$threadStatusType = ([string]$thread.status.type).ToLowerInvariant()
|
|
769
|
+
}
|
|
770
|
+
if ($threadSource -eq "cli" -and $threadStatusType -eq "active") {
|
|
771
|
+
$threadScore += 1000000000000000
|
|
772
|
+
} elseif ($threadStatusType -eq "active") {
|
|
773
|
+
$threadScore += 900000000000000
|
|
774
|
+
} elseif ($threadSource -eq "cli") {
|
|
775
|
+
$threadScore += 800000000000000
|
|
776
|
+
}
|
|
777
|
+
} catch {
|
|
778
|
+
$threadScore = 0.0
|
|
779
|
+
}
|
|
780
|
+
if ($threadScore -ge $bestThreadScore) {
|
|
781
|
+
$bestThreadScore = $threadScore
|
|
782
|
+
$activeThreadId = $candidateThreadId
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
Write-DebugStage -Path $debugLogPath -Message ("LOADED_THREAD_PICKED thread_id=" + $activeThreadId + " count=" + $loadedIds.Count + " score=" + $bestThreadScore)
|
|
786
|
+
$stateEnv = Read-DotEnvFile -Path $envFilePath
|
|
787
|
+
$stateEnv["BLUN_TELEGRAM_THREAD_ID"] = $activeThreadId
|
|
788
|
+
Write-DotEnvFile -Path $envFilePath -Values $stateEnv
|
|
789
|
+
Set-EnvVar "BLUN_TELEGRAM_THREAD_ID" $activeThreadId
|
|
790
|
+
Write-DebugStage -Path $debugLogPath -Message "ENV_THREAD_WRITTEN"
|
|
791
|
+
|
|
792
|
+
$telegramState = Try-GetJsonFile -Path (Join-Path $telegramStateDir "state.json")
|
|
793
|
+
if ($null -ne $telegramState) {
|
|
794
|
+
if ($telegramState.PSObject.Properties.Name.Contains("currentThreadId")) {
|
|
795
|
+
$telegramState.currentThreadId = $activeThreadId
|
|
796
|
+
} else {
|
|
797
|
+
$telegramState | Add-Member -NotePropertyName "currentThreadId" -NotePropertyValue $activeThreadId
|
|
798
|
+
}
|
|
799
|
+
Write-TextFileWithRetry -Path (Join-Path $telegramStateDir "state.json") -Content ($telegramState | ConvertTo-Json -Depth 10)
|
|
800
|
+
Write-DebugStage -Path $debugLogPath -Message "STATE_THREAD_WRITTEN"
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
$currentRuntime["thread_id"] = $activeThreadId
|
|
711
804
|
Write-TextFileWithRetry -Path $currentRuntimeFile -Content ($currentRuntime | ConvertTo-Json -Depth 6)
|
|
712
805
|
Write-DebugStage -Path $debugLogPath -Message "CURRENT_RUNTIME_THREAD_WRITTEN"
|
|
713
806
|
|
|
@@ -749,9 +842,24 @@ if ($useRemoteAppServer) {
|
|
|
749
842
|
if ($responderPid -gt 0) { $currentRuntime["responder_pid"] = $responderPid }
|
|
750
843
|
Write-TextFileWithRetry -Path $currentRuntimeFile -Content ($currentRuntime | ConvertTo-Json -Depth 6)
|
|
751
844
|
Write-DebugStage -Path $debugLogPath -Message "APP_SERVER_INFO_WRITTEN"
|
|
752
|
-
} else {
|
|
753
|
-
Write-DebugStage -Path $debugLogPath -Message "LOADED_THREAD_NONE"
|
|
754
|
-
|
|
845
|
+
} else {
|
|
846
|
+
Write-DebugStage -Path $debugLogPath -Message "LOADED_THREAD_NONE"
|
|
847
|
+
if ($sidecarsStartedEarly) {
|
|
848
|
+
$remoteSessionInfo = [ordered]@{
|
|
849
|
+
ws_url = $telegramAppServerWsUrl
|
|
850
|
+
thread_id = ""
|
|
851
|
+
pid = $backendProcess.Id
|
|
852
|
+
started_at = (Get-Date).ToUniversalTime().ToString("o")
|
|
853
|
+
sidecars = [ordered]@{
|
|
854
|
+
poller = [ordered]@{ pid = Read-PidFileValue -Path (Join-Path $telegramStateDir "poller.pid") }
|
|
855
|
+
dispatcher = [ordered]@{ pid = Read-PidFileValue -Path (Join-Path $telegramStateDir "dispatcher.pid") }
|
|
856
|
+
responder = [ordered]@{ pid = Read-PidFileValue -Path (Join-Path $telegramStateDir "responder.pid") }
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
Write-TextFileWithRetry -Path $appServerInfoFile -Content ($remoteSessionInfo | ConvertTo-Json -Depth 6)
|
|
860
|
+
Write-DebugStage -Path $debugLogPath -Message "APP_SERVER_INFO_WRITTEN_UNBOUND"
|
|
861
|
+
}
|
|
862
|
+
}
|
|
755
863
|
} catch {
|
|
756
864
|
Write-DebugStage -Path $debugLogPath -Message ("LOADED_THREAD_ERROR " + $_.Exception.Message)
|
|
757
865
|
}
|
package/telegram-doctor.ps1
CHANGED
|
@@ -195,32 +195,46 @@ $legacyEnv = Read-DotEnvFile -Path $legacyEnvPath
|
|
|
195
195
|
|
|
196
196
|
$tokenValue = ""
|
|
197
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 $
|
|
202
|
-
$tokenValue = [string]$
|
|
203
|
-
$tokenSource = "
|
|
204
|
-
}
|
|
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 $activeEnv["TELEGRAM_BOT_TOKEN"]) {
|
|
202
|
+
$tokenValue = [string]$activeEnv["TELEGRAM_BOT_TOKEN"]
|
|
203
|
+
$tokenSource = "state env legacy key"
|
|
204
|
+
} elseif (Test-TelegramTokenFormat -Value $legacyEnv["BLUN_TELEGRAM_BOT_TOKEN"]) {
|
|
205
|
+
$tokenValue = [string]$legacyEnv["BLUN_TELEGRAM_BOT_TOKEN"]
|
|
206
|
+
$tokenSource = "legacy env fallback"
|
|
207
|
+
} elseif (Test-TelegramTokenFormat -Value $legacyEnv["TELEGRAM_BOT_TOKEN"]) {
|
|
208
|
+
$tokenValue = [string]$legacyEnv["TELEGRAM_BOT_TOKEN"]
|
|
209
|
+
$tokenSource = "legacy env fallback legacy key"
|
|
210
|
+
}
|
|
205
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." })
|
|
206
212
|
|
|
207
213
|
$allowedChatIds = ""
|
|
208
214
|
$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 $
|
|
213
|
-
$allowedChatIds = [string]$
|
|
214
|
-
$allowedChatSource = "
|
|
215
|
-
}
|
|
216
|
-
|
|
215
|
+
if (Test-AllowedChatIdsFormat -Value $activeEnv["BLUN_TELEGRAM_ALLOWED_CHAT_ID"]) {
|
|
216
|
+
$allowedChatIds = [string]$activeEnv["BLUN_TELEGRAM_ALLOWED_CHAT_ID"]
|
|
217
|
+
$allowedChatSource = "state env"
|
|
218
|
+
} elseif (Test-AllowedChatIdsFormat -Value $activeEnv["TELEGRAM_ALLOWED_CHAT_ID"]) {
|
|
219
|
+
$allowedChatIds = [string]$activeEnv["TELEGRAM_ALLOWED_CHAT_ID"]
|
|
220
|
+
$allowedChatSource = "state env legacy key"
|
|
221
|
+
} elseif (Test-AllowedChatIdsFormat -Value $legacyEnv["BLUN_TELEGRAM_ALLOWED_CHAT_ID"]) {
|
|
222
|
+
$allowedChatIds = [string]$legacyEnv["BLUN_TELEGRAM_ALLOWED_CHAT_ID"]
|
|
223
|
+
$allowedChatSource = "legacy env fallback"
|
|
224
|
+
} elseif (Test-AllowedChatIdsFormat -Value $legacyEnv["TELEGRAM_ALLOWED_CHAT_ID"]) {
|
|
225
|
+
$allowedChatIds = [string]$legacyEnv["TELEGRAM_ALLOWED_CHAT_ID"]
|
|
226
|
+
$allowedChatSource = "legacy env fallback legacy key"
|
|
227
|
+
}
|
|
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." })
|
|
217
229
|
|
|
218
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." })
|
|
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)
|
|
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)
|
|
220
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." })
|
|
221
|
-
Add-Check -List $checks -Name "
|
|
222
|
-
Add-Check -List $checks -Name "
|
|
223
|
-
Add-Check -List $checks -Name "
|
|
233
|
+
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
|
+
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
|
+
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
|
+
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
|
+
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
238
|
|
|
225
239
|
if ($status.last_inbound) {
|
|
226
240
|
$lastInboundSummary = [string]::Format(
|
|
@@ -248,7 +262,7 @@ if ($status.last_outbound) {
|
|
|
248
262
|
Add-Check -List $checks -Name "last_outbound" -Status "warn" -Detail "No outbound Telegram message recorded yet."
|
|
249
263
|
}
|
|
250
264
|
|
|
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)
|
|
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)
|
|
252
266
|
|
|
253
267
|
$result = [ordered]@{
|
|
254
268
|
profile = $status.profile
|