fix(komodo,config): fresh pagerless shell per exec, panel and api injects framed as the user

This commit is contained in:
hh
2026-08-29 17:07:45 +02:00
parent 37217943b5
commit fc0fef734e
2 changed files with 27 additions and 6 deletions
+21
View File
@@ -13,6 +13,7 @@ from beaver_gateway.core.conversations import (
SeedContext, SeedContext,
) )
from beaver_gateway.core.distill import DistillContext, Distiller, LineCap from beaver_gateway.core.distill import DistillContext, Distiller, LineCap
from beaver_gateway.core.injects import INTERRUPTED_TURN, InjectContext
from beaver_gateway.core.prompt import assemble from beaver_gateway.core.prompt import assemble
from beaver_gateway.core.registry import Gateway from beaver_gateway.core.registry import Gateway
from beaver_gateway.core.rotation import HandoutContext, RotationPolicy from beaver_gateway.core.rotation import HandoutContext, RotationPolicy
@@ -435,7 +436,27 @@ def distill_prompt(ctx: DistillContext) -> str:
return DISTILL.format(chat=ctx.chat_name, reason=ctx.reason, day=day, path=path) return DISTILL.format(chat=ctx.chat_name, reason=ctx.reason, day=day, path=path)
# Панель и API - это сам Бобёр (single-user); остальные origin - крон, watch,
# komodo, ротация - не он.
BEAVER_ORIGINS = frozenset({"panel", "api"})
def inject_header(ctx: InjectContext) -> str:
if ctx.origin in BEAVER_ORIGINS:
head = (
"[инжект из панели: это Бобёр, ответ он видит в панели; "
"в телегу - только через say]"
)
else:
head = (
f"[инжект: {ctx.origin} - это не Бобёр, отвечать не нужно, "
"голос не обязателен]"
)
return f"{head}\n{INTERRUPTED_TURN}" if ctx.interrupted_turn else head
texts = ConversationTexts( texts = ConversationTexts(
inject_header=inject_header,
merge_prompt=_read(GRANULES / "слив.md") or ConversationTexts().merge_prompt, merge_prompt=_read(GRANULES / "слив.md") or ConversationTexts().merge_prompt,
seed=seed_body, seed=seed_body,
handout=handout_prompt, handout=handout_prompt,
+6 -6
View File
@@ -46,7 +46,9 @@ class Komodo:
secret: str secret: str
exec_deny: tuple[str, ...] = ("*periphery*", "*komodo*") exec_deny: tuple[str, ...] = ("*periphery*", "*komodo*")
terminal: str = "beaver" terminal: str = "beaver"
shell: str = "sh" shell: str = "env PAGER=cat GIT_PAGER=cat TERM=dumb sh"
"""Terminal is a tty: without ``PAGER=cat`` psql/git open a pager and
wait for a key forever, wedging the terminal."""
timeout: float = 120.0 timeout: float = 120.0
wait: float = 180.0 wait: float = 180.0
max_chars: int = 12_000 max_chars: int = 12_000
@@ -77,7 +79,8 @@ class Komodo:
- deploy / restart: стек целиком или один service; ждёт исхода до 3 мин. - deploy / restart: стек целиком или один service; ждёт исхода до 3 мин.
- exec: команда внутри контейнера (container, command; server, если имя - exec: команда внутри контейнера (container, command; server, если имя
контейнера есть на нескольких серверах). Это `docker exec` от контейнера есть на нескольких серверах). Это `docker exec` от
пользователя контейнера. Долгое - запускай в фон с выводом в файл. пользователя контейнера, свежий шелл на каждый вызов, без tty-пейджера.
Лимит ~2 мин: долгое запускай в фон с выводом в файл и читай файл.
Деплой и рестарт - только когда об этом попросили, не «заодно». Деплой и рестарт - только когда об этом попросили, не «заодно».
""" """
@@ -211,12 +214,9 @@ class Komodo:
}, },
"terminal": self.terminal, "terminal": self.terminal,
"command": command, "command": command,
"init": {"command": self.shell, "recreate": "Never"}, "init": {"command": self.shell, "recreate": "Always"},
} }
status, text = await self._post("terminal/execute", body) status, text = await self._post("terminal/execute", body)
if status != 200:
body["init"]["recreate"] = "Always"
status, text = await self._post("terminal/execute", body)
if status != 200: if status != 200:
return f"exec {container}: Komodo {status}: {text[:500]}" return f"exec {container}: Komodo {status}: {text[:500]}"
output, _, code = ANSI.sub("", text).rpartition(EXIT_MARK) output, _, code = ANSI.sub("", text).rpartition(EXIT_MARK)