Skip to content
Closed
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ logs/
.codex
.copilot
.env
*.child.pid
121 changes: 118 additions & 3 deletions README.de.md

Large diffs are not rendered by default.

121 changes: 118 additions & 3 deletions README.fr.md

Large diffs are not rendered by default.

121 changes: 118 additions & 3 deletions README.ja.md

Large diffs are not rendered by default.

121 changes: 118 additions & 3 deletions README.ko.md

Large diffs are not rendered by default.

123 changes: 120 additions & 3 deletions README.md

Large diffs are not rendered by default.

121 changes: 118 additions & 3 deletions README.nl.md

Large diffs are not rendered by default.

121 changes: 118 additions & 3 deletions README.th.md

Large diffs are not rendered by default.

121 changes: 118 additions & 3 deletions README.vi.md

Large diffs are not rendered by default.

121 changes: 118 additions & 3 deletions README.zh-CN.md

Large diffs are not rendered by default.

121 changes: 118 additions & 3 deletions README.zh-HK.md

Large diffs are not rendered by default.

121 changes: 118 additions & 3 deletions README.zh-TW.md

Large diffs are not rendered by default.

225 changes: 225 additions & 0 deletions bootstrap.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
#!/usr/bin/env bash
# One-time environment setup: creates/activates the venv, installs the
# package, creates the .env file if missing, and validates required
# settings. Does not start the bot itself -- ./startup.sh runs this once
# and then supervises the actual bot process (restarting it across crashes
# and network outages). Run this directly only if you want to (re)prepare
# the environment without starting anything.

set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR"

DEFAULT_ENV_FILE=".env_coding_agent_telegram"
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}"

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"
LOCAL_PRETEND_VERSION="${SETUPTOOLS_SCM_PRETEND_VERSION_FOR_CODING_AGENT_TELEGRAM:-0.0.dev0}"
INSTALL_STATE_FILE_NAME=".coding-agent-telegram-install-state"
FORCE_REINSTALL="${FORCE_REINSTALL:-0}"

compute_install_fingerprint() {
local files=()
local file
for file in pyproject.toml setup.py; do
if [[ -f "$file" ]]; then
files+=("$file")
fi
done
if [[ "${#files[@]}" -eq 0 ]]; then
printf 'no-packaging-files\n'
return
fi
shasum -a 256 "${files[@]}" | shasum -a 256 | awk '{print $1}'
}

if [[ -z "$ENV_FILE" ]]; then
if [[ -f "$HOME_ENV_FILE" ]]; then
ENV_FILE="$HOME_ENV_FILE"
elif [[ -f "$DEFAULT_ENV_FILE" ]]; then
ENV_FILE="$DEFAULT_ENV_FILE"
else
ENV_FILE="$HOME_ENV_FILE"
fi
fi

NEW_ENV_CREATED=0
if [[ ! -f "$ENV_FILE" ]]; then
if [[ -f "$ENV_TEMPLATE_FILE" ]]; then
ENV_FILE_TARGET="$ENV_FILE" ENV_TEMPLATE_SOURCE="$ENV_TEMPLATE_FILE" PYTHONPATH="$SCRIPT_DIR/src${PYTHONPATH:+:$PYTHONPATH}" "$PYTHON_BIN" - <<'PY'
from pathlib import Path
import os
from coding_agent_telegram.config import create_initial_env_file
from coding_agent_telegram.i18n import translate

env_path = Path(os.environ["ENV_FILE_TARGET"]).expanduser()
template_path = Path(os.environ["ENV_TEMPLATE_SOURCE"]).expanduser()
app_locale = create_initial_env_file(env_path, template_path)
print(translate(app_locale, "bootstrap.env_created_locale_line", env_path=env_path, app_locale=app_locale))
print(translate(app_locale, "bootstrap.env_created_change_line", env_path=env_path))
PY
NEW_ENV_CREATED=1
else
echo "Error: $ENV_FILE is missing and $ENV_TEMPLATE_FILE was not found." >&2
exit 1
fi
fi

STATE_FILE="$STATE_FILE_DEFAULT"
STATE_BACKUP_FILE="$STATE_BACKUP_FILE_DEFAULT"
if [[ -f "$APP_HOME_DIR/state.json" ]]; then
STATE_FILE="$APP_HOME_DIR/state.json"
elif [[ -f "./state.json" ]]; then
STATE_FILE="./state.json"
fi
if [[ -f "$APP_HOME_DIR/state.json.bak" ]]; then
STATE_BACKUP_FILE="$APP_HOME_DIR/state.json.bak"
elif [[ -f "./state.json.bak" ]]; then
STATE_BACKUP_FILE="./state.json.bak"
fi
LOG_DIR="$LOG_DIR_DEFAULT"

mkdir -p "$(dirname "$STATE_FILE")" "$(dirname "$STATE_BACKUP_FILE")" "$LOG_DIR"
touch "$STATE_FILE" "$STATE_BACKUP_FILE"

if [[ ! -d "$VENV_DIR" ]]; then
"$PYTHON_BIN" -m venv "$VENV_DIR"
fi

source "$VENV_DIR/bin/activate"

python -m pip install --upgrade pip >/dev/null
INSTALL_STATE_FILE="$VENV_DIR/$INSTALL_STATE_FILE_NAME"
CURRENT_INSTALL_FINGERPRINT="$(compute_install_fingerprint)"
STORED_INSTALL_FINGERPRINT=""
if [[ -f "$INSTALL_STATE_FILE" ]]; then
STORED_INSTALL_FINGERPRINT="$(<"$INSTALL_STATE_FILE")"
fi

NEEDS_REINSTALL=0
if [[ "$FORCE_REINSTALL" == "1" ]]; then
NEEDS_REINSTALL=1
elif ! python -c "import coding_agent_telegram" >/dev/null 2>&1; then
NEEDS_REINSTALL=1
elif [[ "$CURRENT_INSTALL_FINGERPRINT" != "$STORED_INSTALL_FINGERPRINT" ]]; then
NEEDS_REINSTALL=1
fi

if [[ "$NEEDS_REINSTALL" == "1" ]]; then
echo "Installing local package into $VENV_DIR."
SETUPTOOLS_SCM_PRETEND_VERSION_FOR_CODING_AGENT_TELEGRAM="$LOCAL_PRETEND_VERSION" \
python -m pip install -e .
printf '%s\n' "$CURRENT_INSTALL_FINGERPRINT" > "$INSTALL_STATE_FILE"
else
echo "Existing editable install detected; skipping reinstall."
fi

if [[ "$NEW_ENV_CREATED" == "1" ]]; then
python -m coding_agent_telegram.stt_setup offer \
--env-file "$ENV_FILE" \
--python-bin "$VENV_DIR/bin/python" \
--installer-label "./install-stt.sh"
fi

set -a
source "$ENV_FILE"
set +a

required_vars=(
WORKSPACE_ROOT
TELEGRAM_BOT_TOKENS
)

for var_name in "${required_vars[@]}"; do
if [[ -z "${!var_name:-}" ]]; then
echo "Error: $var_name must be set in $ENV_FILE." >&2
echo "Post-installation checklist:"
echo "1. Edit $ENV_FILE"
echo "2. Set WORKSPACE_ROOT to the parent folder containing your projects"
echo "3. Set TELEGRAM_BOT_TOKENS to one or more bot tokens"
echo "4. Set ALLOWED_CHAT_IDS to your Telegram chat id(s)"
echo "5. Run: ./startup.sh"
exit 1
fi
done

if [[ -z "${ALLOWED_CHAT_IDS:-}" ]]; then
echo "Error: set ALLOWED_CHAT_IDS in $ENV_FILE." >&2
echo "Run: ./startup.sh after updating $ENV_FILE."
exit 1
fi

DEFAULT_AGENT_PROVIDER="${DEFAULT_AGENT_PROVIDER:-codex}"
CODEX_BIN="${CODEX_BIN:-codex}"
COPILOT_BIN="${COPILOT_BIN:-copilot}"
CLAUDE_BIN="${CLAUDE_BIN:-claude}"

case "$DEFAULT_AGENT_PROVIDER" in
codex)
if ! command -v "$CODEX_BIN" >/dev/null 2>&1; then
echo "Error: Codex CLI not found: $CODEX_BIN" >&2
echo "Check DEFAULT_AGENT_PROVIDER and CODEX_BIN in $ENV_FILE." >&2
echo "If this machine only has Copilot or Claude Code, set DEFAULT_AGENT_PROVIDER=copilot or claude." >&2
exit 1
fi
;;
copilot)
if ! command -v "$COPILOT_BIN" >/dev/null 2>&1; then
echo "Error: Copilot CLI not found: $COPILOT_BIN" >&2
echo "Check DEFAULT_AGENT_PROVIDER and COPILOT_BIN in $ENV_FILE." >&2
echo "If this machine only has Codex or Claude Code, set DEFAULT_AGENT_PROVIDER=codex or claude." >&2
exit 1
fi
;;
claude)
if ! command -v "$CLAUDE_BIN" >/dev/null 2>&1; then
echo "Error: Claude Code CLI not found: $CLAUDE_BIN" >&2
echo "Check DEFAULT_AGENT_PROVIDER and CLAUDE_BIN in $ENV_FILE." >&2
echo "If this machine only has Codex or Copilot, set DEFAULT_AGENT_PROVIDER=codex or copilot." >&2
exit 1
fi
;;
*)
echo "Error: DEFAULT_AGENT_PROVIDER must be codex, copilot, or claude." >&2
exit 1
;;
esac

echo "Post-installation guide:"
echo "1. Confirm $ENV_FILE contains WORKSPACE_ROOT, TELEGRAM_BOT_TOKENS, and ALLOWED_CHAT_IDS."
echo "2. State files are ready at $STATE_FILE and $STATE_BACKUP_FILE."
echo "3. Application logs will be written under $LOG_DIR."
echo "4. Optional voice-to-text: run ./install-stt.sh if you want local Whisper support."
echo "5. Start the server with: ./startup.sh"
echo "6. In Telegram, start conversations."
echo "Environment ready."
2 changes: 1 addition & 1 deletion install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,5 @@ if [[ -z "$COMMAND_PATH" && ":$PATH:" != *":$SCRIPT_DIR:"* ]]; then
echo "Note: $SCRIPT_DIR is not currently on PATH."
fi

echo "Starting coding-agent-telegram..."
echo "Starting coding-agent-telegram with its recovery supervisor..."
exec "$PYTHON_BIN" -m coding_agent_telegram
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ dependencies = [
]

[project.scripts]
coding-agent-telegram = "coding_agent_telegram.cli:main"
coding-agent-telegram = "coding_agent_telegram.supervisor:main"
coding-agent-telegram-stt-install = "coding_agent_telegram.stt_setup:main"

[tool.setuptools]
Expand Down
2 changes: 1 addition & 1 deletion src/coding_agent_telegram/__main__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from coding_agent_telegram.cli import main
from coding_agent_telegram.supervisor import main


if __name__ == "__main__":
Expand Down
Loading
Loading