blun-king-cli 9.1.68 → 9.1.70

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/LIESMICH.txt CHANGED
@@ -9,7 +9,7 @@ Installation
9
9
  ------------
10
10
  Die geprüfte Version exakt global installieren:
11
11
 
12
- npm install -g blun-king-cli@9.1.68
12
+ npm install -g blun-king-cli@9.1.69
13
13
 
14
14
  Start
15
15
  -----
@@ -20,6 +20,26 @@ Start
20
20
  Beide Startbefehle verwenden dieselbe Version, dasselbe Konto, dasselbe Modell
21
21
  und dieselben Befehle.
22
22
 
23
+ Standard-MCPs und Eingabesteuerung in 9.1.69
24
+ --------------------------------------------
25
+ Beim Start richtet BLUN King agent-browser, blun-language-guard, context7 und
26
+ unter Windows windows-mcp automatisch ein. Erkannte Standardkonfigurationen
27
+ werden aktiviert; eigene, abweichende MCP-Befehle bleiben unverändert.
28
+
29
+ /mcp
30
+ blun tools list
31
+ blun tools enable context7
32
+
33
+ /mcp zeigt den Betriebszustand ohne internen Konfigurationspfad. In der
34
+ Detailansicht bleibt der Pfad für die Fehlersuche verfügbar.
35
+
36
+ Telegram-Nachrichten werden in Eingangsreihenfolge verarbeitet. Im Leerlauf
37
+ startet die Warteschlange selbstständig; während eines Werkzeuglaufs erfolgt
38
+ die Zustellung am nächsten geschützten Übergabepunkt. Strg+C und Esc brechen
39
+ einen aktiven Zug ab, ohne den geschriebenen Entwurf zu löschen. Das gilt auch
40
+ bei Autovervollständigung, Geistervorschlägen, Bash-Eingabe und einer offenen
41
+ Mehrzeileneingabe.
42
+
23
43
  Zuverlässiger King-Start
24
44
  -----------------------
25
45
  Der Windows-Hilfsprozess für private Pfade übernimmt TEMP und TMP aus der
@@ -65,6 +85,16 @@ Ein neuer /loop ersetzt den bisherigen Loop derselben Sitzung. Dabei bleibt
65
85
  genau ein gespeicherter Loop aktiv. /loop stop, /loop pause und /loop status
66
86
  wirken auch dann sofort, wenn der Agent gerade arbeitet.
67
87
 
88
+ Wird ein neuer /loop während eines laufenden Zugs aktiviert, wird sein erster
89
+ Auftrag hinter dem aktuellen Zug eingereiht. Er startet danach automatisch; es
90
+ entsteht weder ein zweiter paralleler Zug noch der Fehler turn.agent_busy.
91
+
92
+ Beispiel:
93
+
94
+ /loop 5min Prüfe den Teststand und arbeite am nächsten offenen Punkt weiter.
95
+
96
+ Der erste Lauf beginnt direkt nach dem aktuellen Zug, danach alle fünf Minuten.
97
+
68
98
  Eine fällige Wiederholung wird als autonomer Aufwecker eingespeist und nicht
69
99
  als neue Nutzernachricht behandelt. Die Statuszeile zeigt den nächsten
70
100
  Aufweckzeitpunkt laufend an. Beim Fortsetzen derselben Sitzung wird der
@@ -134,10 +164,18 @@ Alle Befehle funktionieren identisch mit `king`.
134
164
 
135
165
  Medienerzeugung
136
166
  ---------------
137
- GenerateVideo erzeugt Videos aus einer Textbeschreibung, animiert einen
138
- abgeschlossenen Bildauftrag oder lädt eine lokale PNG- beziehungsweise JPEG-Datei
139
- hoch und animiert genau dieses Bild. Der Auftrag läuft asynchron und wird
140
- anschließend mit GetMedia abgerufen.
167
+ Für Medienaufträge bleiben GenerateImage, GenerateVideo, GenerateSpeech und
168
+ GetMedia auch bei schlanker Werkzeugauswahl unmittelbar verfügbar. Nutzer
169
+ müssen weder Werkzeugnamen noch Modell-Prompts kennen. Eine normale Anweisung
170
+ wie „Erzeuge ein realistisches Produktbild einer schwarzen Armbanduhr auf weißem
171
+ Marmor“ oder „Animiere das letzte Bild als ruhige Kamerafahrt von sechs Sekunden“
172
+ genügt. King wählt den passenden Medienweg und kann das asynchrone Ergebnis mit
173
+ GetMedia abrufen.
174
+
175
+ Angehängte Bilder werden weiterhin mit ReadMediaFile gelesen. Bild-, Video- und
176
+ Spracherzeugung laufen asynchron, sodass die Konsole während der Verarbeitung
177
+ nutzbar bleibt. GenerateVideo kann außerdem einen abgeschlossenen Bildauftrag
178
+ oder eine lokale PNG- beziehungsweise JPEG-Datei animieren.
141
179
 
142
180
  Die Medienleiste erscheint nur bei aktiven Medienaufträgen. Sie zeigt
143
181
  Warteschlangenplatz, Produktionsphase und Laufzeit sowie nur tatsächlich vom
package/README.md CHANGED
@@ -9,7 +9,7 @@ Voraussetzung ist Node.js 24.15 oder neuer. Die geprüfte Version wird exakt
9
9
  installiert:
10
10
 
11
11
  ```powershell
12
- npm install -g blun-king-cli@9.1.68
12
+ npm install -g blun-king-cli@9.1.69
13
13
  ```
14
14
 
15
15
  ## Reproduzierbares Staging und Packen
@@ -40,6 +40,29 @@ erzeugt `/telegram:access connect` einen einmaligen Code. `/connect CODE` im
40
40
  gewünschten privaten Chat oder in einer Gruppe speichert Chat und Absender
41
41
  intern; der Code verfällt nach zehn Minuten und ist nur einmal verwendbar.
42
42
 
43
+ ## Standard-MCPs und Eingabesteuerung in 9.1.69
44
+
45
+ Beim Start richtet BLUN King `agent-browser`, `blun-language-guard`, `context7`
46
+ und unter Windows `windows-mcp` automatisch ein und aktiviert erkannte
47
+ Standardkonfigurationen wieder. Eigene, abweichende MCP-Befehle bleiben
48
+ unverändert. `/mcp` zeigt den Betriebszustand ohne internen Konfigurationspfad;
49
+ in der Detailansicht bleibt der Pfad für die Fehlersuche verfügbar.
50
+
51
+ Beispiele:
52
+
53
+ ```text
54
+ /mcp
55
+ blun tools list
56
+ blun tools enable context7
57
+ ```
58
+
59
+ Telegram-Nachrichten werden in Eingangsreihenfolge verarbeitet. Trifft eine
60
+ Nachricht im Leerlauf ein, startet die Warteschlange selbstständig; während
61
+ eines Werkzeuglaufs wird sie am nächsten geschützten Übergabepunkt eingefügt.
62
+ `Strg+C` und `Esc` brechen einen aktiven Zug zuverlässig ab, ohne den bereits
63
+ geschriebenen Entwurf zu löschen. Das gilt auch bei Autovervollständigung,
64
+ Geistervorschlägen, Bash-Eingabe und einer noch offenen Mehrzeileneingabe.
65
+
43
66
  ## Zuverlässiger King-Start
44
67
 
45
68
  Der Windows-Hilfsprozess für private Pfade übernimmt `TEMP` und `TMP` aus der
@@ -85,6 +108,14 @@ genau ein gespeicherter Loop aktiv; der neue Auftrag, das neue Intervall und
85
108
  der neue Startzeitpunkt gelten vollständig. `/loop stop`, `/loop pause` und
86
109
  `/loop status` wirken auch dann sofort, wenn der Agent gerade arbeitet.
87
110
 
111
+ Wird ein neuer `/loop` während eines laufenden Zugs aktiviert, wird sein erster
112
+ Auftrag hinter dem aktuellen Zug eingereiht. Er startet danach automatisch; es
113
+ entsteht weder ein zweiter paralleler Zug noch der Fehler `turn.agent_busy`.
114
+
115
+ Beispiel: `/loop 5min Prüfe den Teststand und arbeite am nächsten offenen Punkt
116
+ weiter.` führt den ersten Lauf direkt nach dem aktuellen Zug aus und danach alle
117
+ fünf Minuten.
118
+
88
119
  Eine fällige Wiederholung wird als autonomer Aufwecker eingespeist und nicht
89
120
  als neue Nutzernachricht behandelt. Die Statuszeile zeigt den nächsten
90
121
  Aufweckzeitpunkt laufend an. Beim Fortsetzen derselben Sitzung wird der
@@ -161,10 +192,18 @@ Sie stehen unter `blun` und `king` identisch zur Verfügung.
161
192
 
162
193
  ## Medienerzeugung
163
194
 
164
- `GenerateVideo` erzeugt Videos aus einer Textbeschreibung, animiert einen
165
- abgeschlossenen Bildauftrag oder lädt eine lokale PNG- beziehungsweise JPEG-Datei
166
- hoch und animiert genau dieses Bild. Der Auftrag läuft asynchron und wird
167
- anschließend mit `GetMedia` abgerufen.
195
+ Für Medienaufträge bleiben `GenerateImage`, `GenerateVideo`, `GenerateSpeech`
196
+ und `GetMedia` auch bei schlanker Werkzeugauswahl unmittelbar verfügbar. Nutzer
197
+ müssen weder Werkzeugnamen noch Modell-Prompts kennen. Eine normale Anweisung
198
+ wie „Erzeuge ein realistisches Produktbild einer schwarzen Armbanduhr auf weißem
199
+ Marmor“ oder „Animiere das letzte Bild als ruhige Kamerafahrt von sechs Sekunden“
200
+ genügt. King wählt den passenden Medienweg und kann das asynchrone Ergebnis mit
201
+ `GetMedia` abrufen.
202
+
203
+ Angehängte Bilder werden weiterhin mit `ReadMediaFile` gelesen. Bild-, Video-
204
+ und Spracherzeugung laufen asynchron, sodass die Konsole während der Verarbeitung
205
+ nutzbar bleibt. `GenerateVideo` kann außerdem einen abgeschlossenen Bildauftrag
206
+ oder eine lokale PNG- beziehungsweise JPEG-Datei animieren.
168
207
 
169
208
  Die Medienleiste erscheint nur bei aktiven Medienaufträgen. Sie zeigt
170
209
  Warteschlangenplatz, Produktionsphase und Laufzeit sowie nur tatsächlich vom
@@ -431,6 +431,7 @@ async function runLauncher(options = {}) {
431
431
  packageRoot: PKG,
432
432
  blunDir: privatePaths.profilePaths.home,
433
433
  names: mcpNames,
434
+ forceEnable: true,
434
435
  });
435
436
  }
436
437
  if (selected.includes('telegram')) {
@@ -31,6 +31,9 @@ function readCatalogue(packageRoot) {
31
31
  || !tool.platforms.every((platform) => platform === 'darwin' || platform === 'win32'))) {
32
32
  throw new Error(`Ungueltige Standard-Tool-Plattform: ${name}`);
33
33
  }
34
+ if (tool.autoEnable !== undefined && typeof tool.autoEnable !== 'boolean') {
35
+ throw new Error(`Ungueltiger Standard-Tool-Autostart: ${name}`);
36
+ }
34
37
  if (tool.owner === 'user-mcp'
35
38
  && (!isObject(tool.config) || typeof tool.config.enabled !== 'boolean')) {
36
39
  throw new Error(`Standard-Tool braucht einen Aktivierungszustand: ${name}`);
@@ -81,16 +84,41 @@ function sameStringArray(left, right) {
81
84
  && left.every((value, index) => value === right[index]);
82
85
  }
83
86
 
84
- function recognizedBundledServer(current, expected) {
85
- return isObject(current)
86
- && current.transport === expected.transport
87
+ function recognizedBundledServer(name, current, expected) {
88
+ if (!isObject(current)) return false;
89
+ const exact = current.transport === expected.transport
87
90
  && current.command === expected.command
88
91
  && sameStringArray(current.args, expected.args);
92
+ if (exact) return true;
93
+ return name === 'context7'
94
+ && current.transport === 'stdio'
95
+ && current.command === 'npx'
96
+ && Array.isArray(current.args)
97
+ && current.args[0] === '-y'
98
+ && /^@upstash\/context7-mcp(?:@[^\s]+)?$/u.test(current.args[1] ?? '');
89
99
  }
90
100
 
91
101
  function migrateBundledTimeouts(name, current, expected) {
92
- if (!recognizedBundledServer(current, expected)) return false;
102
+ if (!recognizedBundledServer(name, current, expected)) return false;
93
103
  let changed = false;
104
+ if (name === 'context7') {
105
+ if (expected.blunExplicitlyEnabled === true) {
106
+ if (current.enabled !== true) {
107
+ current.enabled = true;
108
+ changed = true;
109
+ }
110
+ if (current.blunExplicitlyEnabled !== true) {
111
+ current.blunExplicitlyEnabled = true;
112
+ changed = true;
113
+ }
114
+ } else if (current.blunExplicitlyEnabled !== true && current.enabled !== false) {
115
+ current.enabled = false;
116
+ changed = true;
117
+ }
118
+ } else if (current.enabled === false) {
119
+ current.enabled = true;
120
+ changed = true;
121
+ }
94
122
  if (name === 'agent-browser') {
95
123
  if (current.startupTimeoutMs === 30_000) {
96
124
  current.startupTimeoutMs = expected.startupTimeoutMs;
@@ -123,9 +151,16 @@ function writeMcpDocument(configPath, document) {
123
151
  }
124
152
  }
125
153
 
126
- function bundledToolConfig(name, tool, { packageRoot, blunDir, platform, env }) {
154
+ function bundledToolConfig(name, tool, {
155
+ packageRoot,
156
+ blunDir,
157
+ platform,
158
+ env,
159
+ forceEnable,
160
+ }) {
127
161
  const config = structuredClone(tool.config);
128
- config.enabled = true;
162
+ config.enabled = forceEnable || tool.autoEnable !== false;
163
+ if (forceEnable && tool.autoEnable === false) config.blunExplicitlyEnabled = true;
129
164
  if (name === 'blun-language-guard') {
130
165
  config.command = platform === 'win32' ? 'python' : 'python3';
131
166
  config.args = [
@@ -174,6 +209,7 @@ function seedStandardTools({
174
209
  names,
175
210
  platform = process.platform,
176
211
  env = process.env,
212
+ forceEnable = false,
177
213
  }) {
178
214
  const catalogue = readCatalogue(packageRoot);
179
215
  if (!Array.isArray(names) || names.length === 0) {
@@ -204,6 +240,7 @@ function seedStandardTools({
204
240
  blunDir,
205
241
  platform,
206
242
  env,
243
+ forceEnable,
207
244
  });
208
245
  if (Object.hasOwn(servers, name)) {
209
246
  if (migrateBundledTimeouts(name, servers[name], expected)) updated.push(name);
@@ -18,6 +18,10 @@ const CORE_TOOL_NAMES = Object.freeze([
18
18
  'ReadMediaFile',
19
19
  'TaskOutput',
20
20
  'Skill',
21
+ 'GenerateImage',
22
+ 'GenerateVideo',
23
+ 'GenerateSpeech',
24
+ 'GetMedia',
21
25
  'mcp__plugin-telegram_telegram__reply',
22
26
  'mcp__plugin-telegram_telegram__react',
23
27
  'mcp__plugin-telegram_telegram__edit_message',
package/blun.mjs CHANGED
@@ -421036,7 +421036,8 @@ async function handleLoopCommand(host, args) {
421036
421036
  host.setAppState?.({ loop });
421037
421037
  host.track("loop_start", { interval: loop.interval });
421038
421038
  host.showStatus(`/loop ${loop.status}`);
421039
- host.sendNormalUserInput(loop.prompt);
421039
+ if (isStreaming(host) || host.state.appState.isCompacting) host.enqueueNormalUserInput(loop.prompt);
421040
+ else host.sendNormalUserInput(loop.prompt);
421040
421041
  return;
421041
421042
  }
421042
421043
  case "pause": {
@@ -421937,8 +421938,6 @@ var McpManagerComponent = class extends Container {
421937
421938
  const name = selected ? currentTheme.boldFg("primary", server.name) : currentTheme.fg("text", server.name);
421938
421939
  const status = currentTheme.fg(statusTone(server.status), statusLabel$2(server.status));
421939
421940
  lines.push(` ${pointer} ${name} ${status} ${currentTheme.fg("textMuted", toolCount(server))}`);
421940
- const origin = originDescription(server);
421941
- if (origin !== void 0) lines.push(currentTheme.fg("textMuted", ` ${origin}`));
421942
421941
  }
421943
421942
  if (view.items.length === 0) lines.push(currentTheme.fg("textMuted", ` ${uiText("common.noMatches")}`));
421944
421943
  lines.push("");
@@ -422055,10 +422054,6 @@ function groupLabel(group) {
422055
422054
  function sourceLabel(group) {
422056
422055
  return uiText(`mcp.source.${group}`);
422057
422056
  }
422058
- function originDescription(server) {
422059
- if (server.source === "plugin") return uiText("mcp.origin.plugin", { id: server.pluginId ?? uiText("mcp.unknown") });
422060
- return server.configPath === void 0 ? void 0 : sanitizeMcpDisplayText(server.configPath);
422061
- }
422062
422057
  function detailRows(server) {
422063
422058
  const rows = [
422064
422059
  [uiText("mcp.detail.status"), statusLabel$2(server.status)],
@@ -514662,7 +514657,6 @@ var BlunTUI = class {
514662
514657
  ...envelope.meta["image_path"] !== void 0 ? { channelImagePath: envelope.meta["image_path"] } : {}
514663
514658
  });
514664
514659
  this.syncChannelQueueDeadline();
514665
- this.channelQueueDeadline.requestDeliveryNow();
514666
514660
  this.scheduleQueueDrain();
514667
514661
  this.track("input_queue");
514668
514662
  this.updateQueueDisplay();
@@ -515021,6 +515015,12 @@ var BlunTUI = class {
515021
515015
  this.updateQueueDisplay();
515022
515016
  this.state.ui.requestRender();
515023
515017
  }
515018
+ enqueueNormalUserInput(text) {
515019
+ this.enqueueMessage(text);
515020
+ this.updateQueueDisplay();
515021
+ this.scheduleQueueDrain();
515022
+ this.state.ui.requestRender();
515023
+ }
515024
515024
  beginSessionRequest() {
515025
515025
  this.streamingUI.setTurnId(void 0);
515026
515026
  this.streamingUI.resetLiveText();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blun-king-cli",
3
- "version": "9.1.68",
3
+ "version": "9.1.70",
4
4
  "description": "BLUN CLI - your own AI agent with a Telegram channel. Get it done. With BLUN.",
5
5
  "license": "MIT",
6
6
  "bin": {
@@ -1,4 +1,4 @@
1
1
  {
2
- "version": "9.1.63",
2
+ "version": "9.1.69",
3
3
  "removals": []
4
4
  }
@@ -31,6 +31,20 @@
31
31
  "toolTimeoutMs": 120000
32
32
  }
33
33
  },
34
+ "context7": {
35
+ "owner": "user-mcp",
36
+ "version": "latest",
37
+ "autoEnable": false,
38
+ "detectTokens": ["context7", "@upstash/context7-mcp"],
39
+ "config": {
40
+ "transport": "stdio",
41
+ "command": "npx",
42
+ "args": ["-y", "@upstash/context7-mcp@latest"],
43
+ "enabled": false,
44
+ "startupTimeoutMs": 40000,
45
+ "toolTimeoutMs": 120000
46
+ }
47
+ },
34
48
  "telegram": {
35
49
  "owner": "managed-plugin",
36
50
  "version": "bundled",
@@ -4372,21 +4372,36 @@ var KingSession = class {
4372
4372
  * orphan means permanent 409 Conflict), pairing-approval confirmations,
4373
4373
  * and the backoff polling loop.
4374
4374
  */
4375
- /** Kill any stale poller holding the token, then claim the pid file. */
4375
+ /** Atomically claim the token poller without interrupting a live owner. */
4376
4376
  function claimPollerPid() {
4377
4377
  mkdirSync(stateDir(), {
4378
4378
  recursive: true,
4379
4379
  mode: 448
4380
4380
  });
4381
- try {
4382
- const stale = parseInt(readFileSync(pidFile(), "utf8"), 10);
4383
- if (stale > 1 && stale !== process.pid) {
4384
- process.kill(stale, 0);
4385
- process.stderr.write(`telegram channel: replacing stale poller pid=${stale}\n`);
4386
- process.kill(stale, "SIGTERM");
4381
+ for (let attempt = 0; attempt < 3; attempt += 1) {
4382
+ try {
4383
+ writeFileSync(pidFile(), String(process.pid), { flag: "wx" });
4384
+ return true;
4385
+ } catch (error) {
4386
+ if (error?.code !== "EEXIST") throw error;
4387
4387
  }
4388
- } catch {}
4389
- writeFileSync(pidFile(), String(process.pid));
4388
+ let ownerPid;
4389
+ try {
4390
+ ownerPid = parseInt(readFileSync(pidFile(), "utf8"), 10);
4391
+ } catch {}
4392
+ if (ownerPid === process.pid) return true;
4393
+ if (ownerPid !== void 0 && ownerPid > 1) try {
4394
+ process.kill(ownerPid, 0);
4395
+ process.stderr.write(`telegram channel: live poller pid=${ownerPid} already owns the bot token\n`);
4396
+ return false;
4397
+ } catch {}
4398
+ try {
4399
+ rmSync(pidFile());
4400
+ } catch {
4401
+ return false;
4402
+ }
4403
+ }
4404
+ return false;
4390
4405
  }
4391
4406
  function releasePollerPid() {
4392
4407
  try {
@@ -4546,7 +4561,10 @@ process.on("uncaughtException", (err) => {
4546
4561
  });
4547
4562
  ensureStateDir();
4548
4563
  rotateStaleTuiQueue();
4549
- claimPollerPid();
4564
+ if (!claimPollerPid()) {
4565
+ process.stderr.write("telegram channel: another live poller owns the bot token; exiting\n");
4566
+ process.exit(0);
4567
+ }
4550
4568
  const bot = new import_out.Bot(TOKEN);
4551
4569
  let botUsername = "";
4552
4570
  const session = new KingSession(KING_ENTRY, (msg) => {