blun-king-cli 9.1.229 → 9.1.231

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.
@@ -0,0 +1,49 @@
1
+ 'use strict';
2
+
3
+ function normalizePath(value) {
4
+ return String(value || '').trim().replaceAll('\\', '/').replace(/\/+$/u, '').toLowerCase();
5
+ }
6
+
7
+ function windowsBashPath(value) {
8
+ const normalized = normalizePath(value);
9
+ const match = normalized.match(/^([a-z]):(\/.*)$/u);
10
+ return match ? `/${match[1]}${match[2]}` : normalized;
11
+ }
12
+
13
+ function escapeRegExp(value) {
14
+ return value.replace(/[.*+?^${}()|[\]\\]/gu, '\\$&');
15
+ }
16
+
17
+ function findTargetsWholeHome(command, homeDir, isWindowsBash) {
18
+ const normalizedHome = normalizePath(homeDir);
19
+ if (!normalizedHome) return false;
20
+ const homes = new Set([normalizedHome, '~', '$home']);
21
+ if (isWindowsBash) homes.add(windowsBashPath(normalizedHome));
22
+ return [...homes].some((home) => {
23
+ const target = escapeRegExp(home);
24
+ return new RegExp(`(?:^|[;&|]\\s*)find\\s+(?:["']?)${target}(?=["'\\s;|&]|$)`, 'iu').test(command);
25
+ });
26
+ }
27
+
28
+ function recursiveGrepTargetsDot(command) {
29
+ const segments = String(command || '').split(/(?:&&|\|\||[;\n])/u);
30
+ return segments.some((segment) => {
31
+ const match = segment.match(/^\s*grep\s+(.+)$/iu);
32
+ if (!match) return false;
33
+ const args = match[1];
34
+ if (!/(?:^|\s)-[^\s]*[rR][^\s]*(?=\s|$)/u.test(args)) return false;
35
+ return /(?:^|\s)["']?\.["']?(?=\s|$)/u.test(args);
36
+ });
37
+ }
38
+
39
+ function broadRecursiveSearchRefusal({ command, homeDir, isWindowsBash = false }) {
40
+ if (findTargetsWholeHome(String(command || ''), homeDir, isWindowsBash)) {
41
+ return 'Refused broad recursive search over the whole user home before execution. Use Glob with an anchored filename pattern, or narrow Bash find to a known project directory.';
42
+ }
43
+ if (recursiveGrepTargetsDot(command)) {
44
+ return 'Refused broad recursive grep against dot before execution. Use Grep with a path/glob filter, or narrow Bash grep to explicit project subdirectories.';
45
+ }
46
+ return undefined;
47
+ }
48
+
49
+ module.exports = { broadRecursiveSearchRefusal };
package/blun.mjs CHANGED
@@ -262719,7 +262719,7 @@ function windowsPathToPosixPath(path) {
262719
262719
  function rewriteWindowsNullRedirect(command) {
262720
262720
  return command.replace(WINDOWS_NUL_REDIRECT, "$1/dev/null");
262721
262721
  }
262722
- var MS_PER_SECOND, DEFAULT_TIMEOUT_S, MAX_TIMEOUT_S, DEFAULT_BACKGROUND_TIMEOUT_S, MAX_BACKGROUND_TIMEOUT_S, USER_INTERRUPT_REASON, BashInputSchema, SHELL_TIMEOUT_VARS, BashTool, WINDOWS_NUL_REDIRECT;
262722
+ var MS_PER_SECOND, DEFAULT_TIMEOUT_S, MAX_TIMEOUT_S, DEFAULT_BACKGROUND_TIMEOUT_S, MAX_BACKGROUND_TIMEOUT_S, USER_INTERRUPT_REASON, BashInputSchema, SHELL_TIMEOUT_VARS, BashTool, WINDOWS_NUL_REDIRECT, broadRecursiveSearchRefusal;
262723
262723
  var init_bash = __esmMin((() => {
262724
262724
  init_zod$1();
262725
262725
  init_background();
@@ -262728,6 +262728,7 @@ var init_bash = __esmMin((() => {
262728
262728
  init_rule_match();
262729
262729
  init_result_builder();
262730
262730
  init_bash$1();
262731
+ ({ broadRecursiveSearchRefusal } = createRequire(import.meta.url)("./bin/bash-search-scope-policy.cjs"));
262731
262732
  MS_PER_SECOND = 1e3;
262732
262733
  DEFAULT_TIMEOUT_S = 60;
262733
262734
  MAX_TIMEOUT_S = 300;
@@ -262764,7 +262765,7 @@ var init_bash = __esmMin((() => {
262764
262765
  MAX_TIMEOUT_S,
262765
262766
  MAX_BACKGROUND_TIMEOUT_S
262766
262767
  };
262767
- bash_default = "Execute a `{{ SHELL_NAME }}` command for pipes, environment variables, processes, git, package managers, build or test runners, and other genuine shell semantics.\n\nPrefer the dedicated tools when they fit: `Read` for a known file, `Write` to create or replace a file, `Edit` for exact changes, `Glob` to locate files, and `Grep` to search contents. Their output is bounded and their permissions are clearer.\n\nStdout and stderr are combined. Long foreground output is streamed to a private file; capture stops at a safe size while the command continues to its real exit code. A non-zero command ends with its exit code; timeouts and user interrupts are reported separately.\n\nEach call runs in a fresh shell environment. Variables, `cd`, and shell history do not carry into later calls; pass `cwd` or use absolute paths. Foreground commands default to {{ DEFAULT_TIMEOUT_S }}s and allow up to {{ MAX_TIMEOUT_S }}s through `timeout`. Do not run interactive commands or commands that may run forever in the foreground.\n\nWith `run_in_background=true`, provide a short `description`; the tool returns a task ID and notifies you on completion. Background tasks default to {{ DEFAULT_BACKGROUND_TIMEOUT_S }}s and allow up to {{ MAX_BACKGROUND_TIMEOUT_S }}s. Use `disable_timeout=true` only when no deadline is appropriate. Use `TaskOutput` for a status or output snapshot and `TaskStop` only to cancel. After launch, normally return control instead of immediately waiting.\n\nQuote paths containing spaces. Chain dependent commands with `&&`; issue independent read-only checks as parallel tool calls. Use `;`, `||`, pipes, redirects, and shell control flow when their semantics are required.\n\nStay inside the working directory unless the user explicitly requests otherwise. Avoid `..`, and never require superuser privileges without explicit instruction. Command availability depends on the host; verify unfamiliar commands before relying on them.";
262768
+ bash_default = "Execute a `{{ SHELL_NAME }}` command for pipes, environment variables, processes, git, package managers, builds, tests, and other shell semantics.\n\nPrefer dedicated tools: `Read` for a known file, `Write` to create or replace it, `Edit` for exact changes, `Glob` to locate files, and `Grep` to search contents. Whole-home `find` and recursive `grep` against `.` are refused; narrow the path or use those search tools.\n\nStdout and stderr are combined. Long foreground output is streamed to a private file; capture stops at a safe size while the command continues to its real exit code. A non-zero command ends with its exit code; timeouts and user interrupts are reported separately.\n\nEach call runs in a fresh shell environment. Variables, `cd`, and shell history do not carry into later calls; pass `cwd` or use absolute paths. Foreground commands default to {{ DEFAULT_TIMEOUT_S }}s and allow up to {{ MAX_TIMEOUT_S }}s through `timeout`. Do not run interactive commands or commands that may run forever in the foreground.\n\nWith `run_in_background=true`, provide a short `description`; the tool returns a task ID and notifies you on completion. Background tasks default to {{ DEFAULT_BACKGROUND_TIMEOUT_S }}s and allow up to {{ MAX_BACKGROUND_TIMEOUT_S }}s. Use `disable_timeout=true` only when no deadline is appropriate. Use `TaskOutput` for a status or output snapshot and `TaskStop` only to cancel. After launch, normally return control instead of immediately waiting.\n\nQuote paths containing spaces. Chain dependent commands with `&&`; issue independent read-only checks as parallel tool calls. Use `;`, `||`, pipes, redirects, and shell control flow when their semantics are required.\n\nStay inside the working directory unless the user explicitly requests otherwise. Avoid `..`, and never require superuser privileges without explicit instruction. Command availability depends on the host; verify unfamiliar commands before relying on them.";
262768
262769
  BashTool = class {
262769
262770
  kaos;
262770
262771
  cwd;
@@ -262897,6 +262898,16 @@ var init_bash = __esmMin((() => {
262897
262898
  isError: true,
262898
262899
  output: "Command cannot be empty."
262899
262900
  };
262901
+ const searchRefusal = broadRecursiveSearchRefusal({
262902
+ command: args.command,
262903
+ cwd: args.cwd ?? this.cwd,
262904
+ homeDir: process.env["USERPROFILE"] ?? process.env["HOME"],
262905
+ isWindowsBash: this.isWindowsBash
262906
+ });
262907
+ if (searchRefusal !== void 0) return {
262908
+ isError: true,
262909
+ output: searchRefusal
262910
+ };
262900
262911
  if (args.run_in_background !== true) return void 0;
262901
262912
  if (!this.allowBackground) return {
262902
262913
  isError: true,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blun-king-cli",
3
- "version": "9.1.229",
3
+ "version": "9.1.231",
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": {
@@ -9814,6 +9814,29 @@ function isRecentDuplicate(chatId, text) {
9814
9814
  } catch {}
9815
9815
  return false;
9816
9816
  }
9817
+ function telegramErrorMessage(error) {
9818
+ return error instanceof Error ? error.message : String(error);
9819
+ }
9820
+ function isTelegramEntityParseError(error) {
9821
+ return /bad request: can't parse entities:/iu.test(telegramErrorMessage(error));
9822
+ }
9823
+ async function sendTelegramTextChunk(api, chatId, text, options, parseMode) {
9824
+ try {
9825
+ return {
9826
+ sent: await api.sendMessage(chatId, text, {
9827
+ ...options,
9828
+ ...parseMode !== void 0 ? { parse_mode: parseMode } : {}
9829
+ }),
9830
+ plainTextFallback: false
9831
+ };
9832
+ } catch (error) {
9833
+ if (parseMode === void 0 || !isTelegramEntityParseError(error)) throw error;
9834
+ return {
9835
+ sent: await api.sendMessage(chatId, text, options),
9836
+ plainTextFallback: true
9837
+ };
9838
+ }
9839
+ }
9817
9840
  async function runReply(api, args) {
9818
9841
  const chatId = resolveAllowedChatId(args.chat_id);
9819
9842
  const text = args.text;
@@ -9849,13 +9872,15 @@ async function runReply(api, args) {
9849
9872
  const replyMode = access.replyToMode ?? "first";
9850
9873
  const chunks = chunk(text, limit, mode);
9851
9874
  const sentIds = [];
9875
+ let plainTextFallbacks = 0;
9852
9876
  try {
9853
9877
  for (let i = 0; i < chunks.length; i++) {
9854
9878
  const shouldReplyTo = replyTo !== void 0 && replyMode !== "off" && (replyMode === "all" || i === 0);
9855
- const sent = await api.sendMessage(chatId, chunks[i], {
9856
- ...shouldReplyTo ? { reply_parameters: { message_id: replyTo } } : {},
9857
- ...parseMode !== void 0 ? { parse_mode: parseMode } : {}
9858
- });
9879
+ const result = await sendTelegramTextChunk(api, chatId, chunks[i], {
9880
+ ...shouldReplyTo ? { reply_parameters: { message_id: replyTo } } : {}
9881
+ }, parseMode);
9882
+ const sent = result.sent;
9883
+ if (result.plainTextFallback) plainTextFallbacks += 1;
9859
9884
  sentIds.push(sent.message_id);
9860
9885
  }
9861
9886
  } catch (err) {
@@ -9875,6 +9900,7 @@ async function runReply(api, args) {
9875
9900
  chat_id: chatId,
9876
9901
  message_ids: sentIds,
9877
9902
  text,
9903
+ ...plainTextFallbacks > 0 ? { plain_text_fallbacks: plainTextFallbacks } : {},
9878
9904
  ...files.length > 0 ? { files } : {}
9879
9905
  });
9880
9906
  return sentIds.length === 1 ? `sent (id: ${sentIds[0]})` : `sent ${sentIds.length} parts (ids: ${sentIds.join(", ")})`;