diff --git a/install.sh b/install.sh old mode 100755 new mode 100644 index d685b78..75587d2 --- a/install.sh +++ b/install.sh @@ -2,8 +2,6 @@ set -euo pipefail -APP_HOME_DIR="${HOME}/.coding-agent-telegram" -VENV_DIR="${VENV_DIR:-$APP_HOME_DIR/.venv}" PYTHON_BIN="${PYTHON_BIN:-python3}" PACKAGE_SPEC="${PACKAGE_SPEC:-coding-agent-telegram}" @@ -12,17 +10,17 @@ if ! command -v "$PYTHON_BIN" >/dev/null 2>&1; then exit 1 fi -mkdir -p "$APP_HOME_DIR" - -if [[ ! -d "$VENV_DIR" ]]; then - "$PYTHON_BIN" -m venv "$VENV_DIR" +"$PYTHON_BIN" -m pip install --upgrade "$PACKAGE_SPEC" +SCRIPT_DIR="$("$PYTHON_BIN" - <<'PY' +import sysconfig +print(sysconfig.get_path("scripts")) +PY +)" +echo "Installed $PACKAGE_SPEC using $PYTHON_BIN." +echo "Command path: $SCRIPT_DIR/coding-agent-telegram" +if [[ ":$PATH:" != *":$SCRIPT_DIR:"* ]]; then + echo "Note: $SCRIPT_DIR is not currently on PATH." fi -source "$VENV_DIR/bin/activate" - -python -m pip install --upgrade pip >/dev/null -python -m pip install --upgrade "$PACKAGE_SPEC" - -echo "Installed $PACKAGE_SPEC into $VENV_DIR." echo "Starting coding-agent-telegram..." -exec "$VENV_DIR/bin/coding-agent-telegram" +exec "$PYTHON_BIN" -m coding_agent_telegram.cli diff --git a/src/coding_agent_telegram/config.py b/src/coding_agent_telegram/config.py index c3ec738..59c435a 100644 --- a/src/coding_agent_telegram/config.py +++ b/src/coding_agent_telegram/config.py @@ -4,6 +4,7 @@ import importlib.resources import os +import pwd import re import locale as system_locale from dataclasses import dataclass @@ -83,7 +84,17 @@ def _parse_bot_tokens() -> tuple[str, ...]: def default_app_internal_root() -> Path: - return Path.home() / DEFAULT_INTERNAL_APP_DIR_NAME + return resolve_user_home() / DEFAULT_INTERNAL_APP_DIR_NAME + + +def resolve_user_home() -> Path: + sudo_user = os.getenv("SUDO_USER", "").strip() + if sudo_user and sudo_user != "root": + try: + return Path(pwd.getpwnam(sudo_user).pw_dir) + except KeyError: + pass + return Path.home() def detect_system_locale() -> str: diff --git a/startup.sh b/startup.sh index 717dd7d..1e212cc 100755 --- a/startup.sh +++ b/startup.sh @@ -6,12 +6,35 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" cd "$SCRIPT_DIR" DEFAULT_ENV_FILE=".env_coding_agent_telegram" -APP_HOME_DIR="${HOME}/.coding-agent-telegram" -HOME_ENV_FILE="$APP_HOME_DIR/$DEFAULT_ENV_FILE" +PYTHON_BIN="${PYTHON_BIN:-python3}" ENV_FILE="${ENV_FILE:-}" ENV_TEMPLATE_FILE="${ENV_TEMPLATE_FILE:-src/coding_agent_telegram/resources/.env.example}" VENV_DIR="${VENV_DIR:-.venv}" -PYTHON_BIN="${PYTHON_BIN:-python3}" + +resolve_user_home() { + "$PYTHON_BIN" - <<'PY' +from pathlib import Path +import os +import pwd + +sudo_user = os.getenv("SUDO_USER", "").strip() +if sudo_user and sudo_user != "root": + try: + print(pwd.getpwnam(sudo_user).pw_dir) + except KeyError: + print(Path.home()) +else: + print(Path.home()) +PY +} + +if ! command -v "$PYTHON_BIN" >/dev/null 2>&1; then + echo "Error: $PYTHON_BIN was not found in PATH." >&2 + exit 1 +fi + +APP_HOME_DIR="$(resolve_user_home)/.coding-agent-telegram" +HOME_ENV_FILE="$APP_HOME_DIR/$DEFAULT_ENV_FILE" STATE_FILE_DEFAULT="$APP_HOME_DIR/state.json" STATE_BACKUP_FILE_DEFAULT="$APP_HOME_DIR/state.json.bak" LOG_DIR_DEFAULT="$APP_HOME_DIR/logs" @@ -34,11 +57,6 @@ compute_install_fingerprint() { shasum -a 256 "${files[@]}" | shasum -a 256 | awk '{print $1}' } -if ! command -v "$PYTHON_BIN" >/dev/null 2>&1; then - echo "Error: $PYTHON_BIN was not found in PATH." >&2 - exit 1 -fi - if [[ -z "$ENV_FILE" ]]; then if [[ -f "$HOME_ENV_FILE" ]]; then ENV_FILE="$HOME_ENV_FILE" diff --git a/tests/test_config.py b/tests/test_config.py index ed32abd..447fdaf 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -1,4 +1,5 @@ from pathlib import Path +from types import SimpleNamespace import pytest @@ -12,6 +13,7 @@ resolve_app_internal_root, resolve_default_state_file_path, resolve_env_file_path, + resolve_user_home, ) @@ -243,6 +245,37 @@ def test_load_config_prefers_home_internal_app_root(monkeypatch, tmp_path): assert cfg.app_internal_root == home_internal_root +def test_resolve_user_home_uses_current_user_when_not_running_under_sudo(monkeypatch, tmp_path): + real_home = tmp_path / "real-home" + monkeypatch.delenv("SUDO_USER", raising=False) + monkeypatch.setattr(Path, "home", lambda: real_home) + + assert resolve_user_home() == real_home + + +def test_default_app_internal_root_uses_sudo_user_home(monkeypatch, tmp_path): + root_home = tmp_path / "root-home" + sudo_home = tmp_path / "sudo-home" + monkeypatch.setattr(Path, "home", lambda: root_home) + monkeypatch.setenv("SUDO_USER", "exampleuser") + monkeypatch.setattr(config_module.pwd, "getpwnam", lambda _user: SimpleNamespace(pw_dir=str(sudo_home))) + + assert config_module.default_app_internal_root() == sudo_home / ".coding-agent-telegram" + + +def test_resolve_user_home_falls_back_to_current_user_when_sudo_user_is_missing(monkeypatch, tmp_path): + root_home = tmp_path / "root-home" + monkeypatch.setattr(Path, "home", lambda: root_home) + monkeypatch.setenv("SUDO_USER", "missinguser") + + def _raise_key_error(_user): + raise KeyError("missing user") + + monkeypatch.setattr(config_module.pwd, "getpwnam", _raise_key_error) + + assert resolve_user_home() == root_home + + def test_load_config_falls_back_to_workspace_internal_app_root(monkeypatch, tmp_path): _isolate_env(monkeypatch, tmp_path) home = tmp_path / "home" diff --git a/tests/test_session_runtime_security.py b/tests/test_session_runtime_security.py index eef8829..1dbcec8 100644 --- a/tests/test_session_runtime_security.py +++ b/tests/test_session_runtime_security.py @@ -175,7 +175,7 @@ def test_sanitize_agent_error_leaves_plain_messages_unchanged(): def test_sanitize_agent_error_normalizes_abort_message_with_path(): - text = "Agent run aborted by /Users/daocha/.coding-agent-telegram/queued_questions/abc.txt" + text = "Agent run aborted by /Users/exampleuser/.coding-agent-telegram/queued_questions/abc.txt" assert _sanitize_agent_error(text, error_code="agent_aborted") == "Agent run aborted by /abort."