Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 11 additions & 13 deletions install.sh
100755 → 100644
Original file line number Diff line number Diff line change
Expand Up @@ -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}"

Expand All @@ -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
13 changes: 12 additions & 1 deletion src/coding_agent_telegram/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import importlib.resources
import os
import pwd
import re
import locale as system_locale
from dataclasses import dataclass
Expand Down Expand Up @@ -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:
Expand Down
34 changes: 26 additions & 8 deletions startup.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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"
Expand Down
33 changes: 33 additions & 0 deletions tests/test_config.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from pathlib import Path
from types import SimpleNamespace

import pytest

Expand All @@ -12,6 +13,7 @@
resolve_app_internal_root,
resolve_default_state_file_path,
resolve_env_file_path,
resolve_user_home,
)


Expand Down Expand Up @@ -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"
Expand Down
2 changes: 1 addition & 1 deletion tests/test_session_runtime_security.py
Original file line number Diff line number Diff line change
Expand Up @@ -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."


Expand Down
Loading