Files
LazyEnter/src/lazy_enter/pty_manager.py

82 lines
2.7 KiB
Python

"""기존 tmux 세션에 attach하여 CLI 입출력을 중계한다."""
from __future__ import annotations
import logging
import subprocess
import pexpect
logger = logging.getLogger(__name__)
class PtyManager:
"""기존 tmux 세션에 attach하고 입출력을 제어한다."""
def __init__(self, session_name: str = "claude", cli_name: str = "claude") -> None:
self.session_name = session_name
self.cli_name = cli_name
self._process: pexpect.spawn | None = None
@property
def is_alive(self) -> bool:
return self._process is not None and self._process.isalive()
def _ensure_session_exists(self) -> None:
"""attach 대상 tmux 세션 존재 여부를 검증한다."""
result = subprocess.run(
["tmux", "has-session", "-t", self.session_name],
check=False,
capture_output=True,
text=True,
)
if result.returncode != 0:
raise RuntimeError(
"tmux 세션이 없습니다. 먼저 실행하세요: "
f"tmux new -s {self.session_name} {self.cli_name}"
)
def start(self) -> None:
"""기존 tmux 세션에 attach한다."""
self._ensure_session_exists()
logger.info("tmux 세션 attach: %s", self.session_name)
self._process = pexpect.spawn(
"tmux",
["attach-session", "-t", self.session_name],
encoding="utf-8",
timeout=None,
)
def send(self, text: str, submit: bool = False) -> None:
"""프로세스에 텍스트 입력을 전달한다."""
if not self.is_alive:
raise RuntimeError("프로세스가 실행 중이 아닙니다.")
assert self._process is not None
logger.debug("입력 전송: %s", text)
if submit:
self._process.sendline(text)
return
self._process.send(text)
def send_enter(self) -> None:
"""엔터 키 입력만 전송한다."""
self.send("", submit=True)
def read_output(self, timeout: float = 5) -> str:
"""프로세스의 출력을 읽는다."""
if not self.is_alive:
raise RuntimeError("프로세스가 실행 중이 아닙니다.")
assert self._process is not None
try:
self._process.expect(pexpect.TIMEOUT, timeout=timeout)
except pexpect.TIMEOUT:
pass
return self._process.before or ""
def stop(self) -> None:
"""attach 연결만 종료한다(원격 세션은 유지)."""
if self._process is not None:
logger.info("tmux attach 종료")
self._process.close(force=True)
self._process = None