feat: make progress messages always silent
This commit is contained in:
@@ -36,12 +36,13 @@ uv run exec-bridge
|
|||||||
Optional flags:
|
Optional flags:
|
||||||
|
|
||||||
- `--progress-edit-every FLOAT` (default `2.0`)
|
- `--progress-edit-every FLOAT` (default `2.0`)
|
||||||
- `--progress-silent/--no-progress-silent` (default silent)
|
|
||||||
- `--final-notify/--no-final-notify` (default notify via new message)
|
- `--final-notify/--no-final-notify` (default notify via new message)
|
||||||
- `--ignore-backlog/--process-backlog` (default ignore pending updates)
|
- `--ignore-backlog/--process-backlog` (default ignore pending updates)
|
||||||
- `--cd PATH` (pass through to `codex --cd`)
|
- `--cd PATH` (pass through to `codex --cd`)
|
||||||
- `--model NAME` (pass through to `codex exec`)
|
- `--model NAME` (pass through to `codex exec`)
|
||||||
|
|
||||||
|
Progress updates are always sent silently.
|
||||||
|
|
||||||
To resume an existing thread without a database, reply with (or include) the session id shown at the end of the bot response:
|
To resume an existing thread without a database, reply with (or include) the session id shown at the end of the bot response:
|
||||||
|
|
||||||
`resume: \`019b66fc-64c2-7a71-81cd-081c504cfeb2\``
|
`resume: \`019b66fc-64c2-7a71-81cd-081c504cfeb2\``
|
||||||
|
|||||||
@@ -8,12 +8,10 @@ import os
|
|||||||
import re
|
import re
|
||||||
import shlex
|
import shlex
|
||||||
import shutil
|
import shutil
|
||||||
import sys
|
|
||||||
import time
|
import time
|
||||||
from collections import deque
|
from collections import deque
|
||||||
from collections.abc import Awaitable, Callable
|
from collections.abc import Awaitable, Callable
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from logging.handlers import RotatingFileHandler
|
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
import typer
|
import typer
|
||||||
@@ -21,6 +19,7 @@ import typer
|
|||||||
from .config import load_telegram_config
|
from .config import load_telegram_config
|
||||||
from .constants import TELEGRAM_HARD_LIMIT
|
from .constants import TELEGRAM_HARD_LIMIT
|
||||||
from .exec_render import ExecProgressRenderer, render_event_cli
|
from .exec_render import ExecProgressRenderer, render_event_cli
|
||||||
|
from .logging import setup_logging
|
||||||
from .rendering import render_markdown
|
from .rendering import render_markdown
|
||||||
from .telegram_client import TelegramClient
|
from .telegram_client import TelegramClient
|
||||||
|
|
||||||
@@ -53,33 +52,6 @@ async def _drain_stderr(stderr: asyncio.StreamReader | None, tail: deque[str]) -
|
|||||||
logger.debug("[codex][stderr] drain error: %s", e)
|
logger.debug("[codex][stderr] drain error: %s", e)
|
||||||
|
|
||||||
|
|
||||||
def setup_logging(log_file: str | None, *, debug: bool = False) -> None:
|
|
||||||
root_logger = logging.getLogger()
|
|
||||||
root_logger.setLevel(logging.DEBUG)
|
|
||||||
for handler in root_logger.handlers[:]:
|
|
||||||
root_logger.removeHandler(handler)
|
|
||||||
handler.close()
|
|
||||||
|
|
||||||
fmt = logging.Formatter("%(asctime)s %(levelname)s %(name)s: %(message)s")
|
|
||||||
|
|
||||||
console = logging.StreamHandler(sys.stdout)
|
|
||||||
console.setLevel(logging.DEBUG if debug else logging.INFO)
|
|
||||||
console.setFormatter(fmt)
|
|
||||||
root_logger.addHandler(console)
|
|
||||||
|
|
||||||
if log_file:
|
|
||||||
file_handler = RotatingFileHandler(
|
|
||||||
log_file,
|
|
||||||
maxBytes=5 * 1024 * 1024,
|
|
||||||
backupCount=3,
|
|
||||||
encoding="utf-8",
|
|
||||||
)
|
|
||||||
file_handler.setLevel(logging.DEBUG if debug else logging.INFO)
|
|
||||||
file_handler.setFormatter(fmt)
|
|
||||||
root_logger.addHandler(file_handler)
|
|
||||||
logger.debug("[debug] file logger initialized path=%r", log_file)
|
|
||||||
|
|
||||||
|
|
||||||
TELEGRAM_TEXT_LIMIT = TELEGRAM_HARD_LIMIT
|
TELEGRAM_TEXT_LIMIT = TELEGRAM_HARD_LIMIT
|
||||||
TELEGRAM_MARKDOWN_LIMIT = 3500
|
TELEGRAM_MARKDOWN_LIMIT = 3500
|
||||||
|
|
||||||
@@ -338,7 +310,6 @@ class BridgeConfig:
|
|||||||
chat_id: int
|
chat_id: int
|
||||||
ignore_backlog: bool
|
ignore_backlog: bool
|
||||||
progress_edit_every_s: float
|
progress_edit_every_s: float
|
||||||
progress_silent: bool
|
|
||||||
final_notify: bool
|
final_notify: bool
|
||||||
startup_msg: str
|
startup_msg: str
|
||||||
max_concurrency: int
|
max_concurrency: int
|
||||||
@@ -347,7 +318,6 @@ class BridgeConfig:
|
|||||||
def _parse_bridge_config(
|
def _parse_bridge_config(
|
||||||
*,
|
*,
|
||||||
progress_edit_every_s: float,
|
progress_edit_every_s: float,
|
||||||
progress_silent: bool,
|
|
||||||
final_notify: bool,
|
final_notify: bool,
|
||||||
ignore_backlog: bool,
|
ignore_backlog: bool,
|
||||||
cd: str | None,
|
cd: str | None,
|
||||||
@@ -398,7 +368,6 @@ def _parse_bridge_config(
|
|||||||
chat_id=chat_id,
|
chat_id=chat_id,
|
||||||
ignore_backlog=bool(ignore_backlog),
|
ignore_backlog=bool(ignore_backlog),
|
||||||
progress_edit_every_s=progress_edit_every_s,
|
progress_edit_every_s=progress_edit_every_s,
|
||||||
progress_silent=progress_silent,
|
|
||||||
final_notify=final_notify,
|
final_notify=final_notify,
|
||||||
startup_msg=startup_msg,
|
startup_msg=startup_msg,
|
||||||
max_concurrency=16,
|
max_concurrency=16,
|
||||||
@@ -500,7 +469,7 @@ async def _handle_message(
|
|||||||
text=initial_rendered,
|
text=initial_rendered,
|
||||||
entities=initial_entities,
|
entities=initial_entities,
|
||||||
reply_to_message_id=user_msg_id,
|
reply_to_message_id=user_msg_id,
|
||||||
disable_notification=cfg.progress_silent,
|
disable_notification=True,
|
||||||
)
|
)
|
||||||
progress_id = int(progress_msg["message_id"])
|
progress_id = int(progress_msg["message_id"])
|
||||||
last_edit_at = time.monotonic()
|
last_edit_at = time.monotonic()
|
||||||
@@ -556,7 +525,7 @@ async def _handle_message(
|
|||||||
chat_id=chat_id,
|
chat_id=chat_id,
|
||||||
text=err,
|
text=err,
|
||||||
reply_to_message_id=user_msg_id,
|
reply_to_message_id=user_msg_id,
|
||||||
disable_notification=cfg.progress_silent,
|
disable_notification=True,
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -683,11 +652,6 @@ def run(
|
|||||||
help="Minimum seconds between progress message edits.",
|
help="Minimum seconds between progress message edits.",
|
||||||
min=1.0,
|
min=1.0,
|
||||||
),
|
),
|
||||||
progress_silent: bool = typer.Option(
|
|
||||||
True,
|
|
||||||
"--progress-silent/--no-progress-silent",
|
|
||||||
help="Send the progress message without sound/vibration.",
|
|
||||||
),
|
|
||||||
final_notify: bool = typer.Option(
|
final_notify: bool = typer.Option(
|
||||||
True,
|
True,
|
||||||
"--final-notify/--no-final-notify",
|
"--final-notify/--no-final-notify",
|
||||||
@@ -704,9 +668,9 @@ def run(
|
|||||||
help="Log codex JSONL, Telegram requests, and rendered messages.",
|
help="Log codex JSONL, Telegram requests, and rendered messages.",
|
||||||
),
|
),
|
||||||
log_file: str | None = typer.Option(
|
log_file: str | None = typer.Option(
|
||||||
"exec_bridge.log",
|
None,
|
||||||
"--log-file",
|
"--log-file",
|
||||||
help="Write detailed debug logs to this file (set to empty to disable).",
|
help="Write detailed logs to this file.",
|
||||||
),
|
),
|
||||||
cd: str | None = typer.Option(
|
cd: str | None = typer.Option(
|
||||||
None,
|
None,
|
||||||
@@ -722,7 +686,6 @@ def run(
|
|||||||
setup_logging(log_file if log_file else None, debug=debug)
|
setup_logging(log_file if log_file else None, debug=debug)
|
||||||
cfg = _parse_bridge_config(
|
cfg = _parse_bridge_config(
|
||||||
progress_edit_every_s=progress_edit_every_s,
|
progress_edit_every_s=progress_edit_every_s,
|
||||||
progress_silent=progress_silent,
|
|
||||||
final_notify=final_notify,
|
final_notify=final_notify,
|
||||||
ignore_backlog=ignore_backlog,
|
ignore_backlog=ignore_backlog,
|
||||||
cd=cd,
|
cd=cd,
|
||||||
|
|||||||
@@ -0,0 +1,54 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
from logging.handlers import RotatingFileHandler
|
||||||
|
|
||||||
|
TELEGRAM_TOKEN_RE = re.compile(r"bot\d+:[A-Za-z0-9_-]+")
|
||||||
|
TELEGRAM_BARE_TOKEN_RE = re.compile(r"\b\d+:[A-Za-z0-9_-]{10,}\b")
|
||||||
|
|
||||||
|
|
||||||
|
class RedactTokenFilter(logging.Filter):
|
||||||
|
def filter(self, record: logging.LogRecord) -> bool:
|
||||||
|
try:
|
||||||
|
message = record.getMessage()
|
||||||
|
except Exception:
|
||||||
|
return True
|
||||||
|
|
||||||
|
redacted = TELEGRAM_TOKEN_RE.sub("bot[REDACTED]", message)
|
||||||
|
redacted = TELEGRAM_BARE_TOKEN_RE.sub("[REDACTED_TOKEN]", redacted)
|
||||||
|
if redacted != message:
|
||||||
|
record.msg = redacted
|
||||||
|
record.args = ()
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def setup_logging(log_file: str | None, *, debug: bool = False) -> None:
|
||||||
|
root_logger = logging.getLogger()
|
||||||
|
root_logger.setLevel(logging.DEBUG)
|
||||||
|
for handler in root_logger.handlers[:]:
|
||||||
|
root_logger.removeHandler(handler)
|
||||||
|
handler.close()
|
||||||
|
|
||||||
|
fmt = logging.Formatter("%(asctime)s %(levelname)s %(name)s: %(message)s")
|
||||||
|
redactor = RedactTokenFilter()
|
||||||
|
|
||||||
|
console = logging.StreamHandler(sys.stdout)
|
||||||
|
console.setLevel(logging.DEBUG if debug else logging.INFO)
|
||||||
|
console.setFormatter(fmt)
|
||||||
|
console.addFilter(redactor)
|
||||||
|
root_logger.addHandler(console)
|
||||||
|
|
||||||
|
if log_file:
|
||||||
|
file_handler = RotatingFileHandler(
|
||||||
|
log_file,
|
||||||
|
maxBytes=5 * 1024 * 1024,
|
||||||
|
backupCount=3,
|
||||||
|
encoding="utf-8",
|
||||||
|
)
|
||||||
|
file_handler.setLevel(logging.DEBUG if debug else logging.INFO)
|
||||||
|
file_handler.setFormatter(fmt)
|
||||||
|
file_handler.addFilter(redactor)
|
||||||
|
root_logger.addHandler(file_handler)
|
||||||
|
logging.getLogger(__name__).debug("[debug] file logger initialized path=%r", log_file)
|
||||||
@@ -98,7 +98,6 @@ def test_final_notify_sends_loud_final_message() -> None:
|
|||||||
chat_id=123,
|
chat_id=123,
|
||||||
ignore_backlog=True,
|
ignore_backlog=True,
|
||||||
progress_edit_every_s=999.0,
|
progress_edit_every_s=999.0,
|
||||||
progress_silent=True,
|
|
||||||
final_notify=True,
|
final_notify=True,
|
||||||
startup_msg="",
|
startup_msg="",
|
||||||
max_concurrency=1,
|
max_concurrency=1,
|
||||||
@@ -131,7 +130,6 @@ def test_new_final_message_forces_notification_when_too_long_to_edit() -> None:
|
|||||||
chat_id=123,
|
chat_id=123,
|
||||||
ignore_backlog=True,
|
ignore_backlog=True,
|
||||||
progress_edit_every_s=999.0,
|
progress_edit_every_s=999.0,
|
||||||
progress_silent=True,
|
|
||||||
final_notify=False,
|
final_notify=False,
|
||||||
startup_msg="",
|
startup_msg="",
|
||||||
max_concurrency=1,
|
max_concurrency=1,
|
||||||
|
|||||||
Reference in New Issue
Block a user