Comprehensive step-by-step guide covering: - ccproxy installation from source (patched fork required for Claude 4+) - ccproxy OAuth login via `ccproxy auth login claude-api` - EvoScientist install with telegram + stt extras using uv - STT model pre-download to avoid first-message delay - launchd plist setup for auto-start on login with KeepAlive - Full troubleshooting section based on real deployment experience: - OAuth token not found (.credentials.json location) - Packages installed in wrong Python environment (conda vs .venv) - Shell glob eating brackets in pip install 'pkg[extra]' - Whisper hallucination / VAD filter - ffmpeg approval prompts → --auto-approve - heredoc variable expansion gotcha - External drive mount timing with launchd Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: Xi Zhang <106144707+X-iZhang@users.noreply.github.com>
9.3 KiB
Running EvoScientist 24/7 on macOS (Telegram Bot + STT + ccproxy)
This guide covers deploying EvoScientist as a fully automated 24/7 service on macOS (tested on Mac Mini with Apple Silicon), using:
- EvoScientist — the AI research agent
- ccproxy — local proxy that routes API calls through your Claude Code subscription
- Telegram channel — messaging interface
- STT — automatic voice message transcription (faster-whisper)
Prerequisites
- macOS 13+ (Apple Silicon or Intel)
- Claude Code installed and subscription active
- uv installed
- A Telegram bot token (from @BotFather)
- Your Telegram user ID (from @userinfobot)
1. Clone repositories
mkdir -p ~/Dev/tools
cd ~/Dev/tools
git clone git@github.com:jhfnetboy/EvoScientist.git
git clone git@github.com:jhfnetboy/ccproxy-api.git
Both repos should sit as siblings in the same parent directory.
2. Install ccproxy from source
Do not
pip install ccproxy-apifrom PyPI — it lacks the adaptive thinking and OAuth header fixes required for Claude 4+ models. Install from the patched fork.
cd ~/Dev/tools/ccproxy-api
git checkout jhf-research
pip install -e .
which ccproxy # verify: should print a path
3. Authenticate ccproxy with your Claude subscription
This step opens a browser window to log in with your Claude account. It only needs to be done once per machine.
ccproxy auth login claude-api
Verify the token was saved:
ls ~/.claude/.credentials.json # note the leading dot
4. Install EvoScientist from source
cd ~/Dev/tools/EvoScientist
git checkout jhf-research
# Install with Telegram and STT support
uv pip install -e '.[telegram,stt]'
Important: always use
uv pip install, notpip install, to ensure packages land in the correct.venvthat the service will use.
Verify:
which EvoSci
EvoSci -h
5. Pre-download the STT model
The STT model (~250 MB) downloads from HuggingFace on first use. Pre-download it now to avoid a delay on the first voice message:
uv run python -c "
from faster_whisper import WhisperModel
WhisperModel('Systran/faster-whisper-small', device='cpu', compute_type='int8')
print('STT model ready')
"
6. Configure EvoScientist
cd ~/Dev/tools/EvoScientist
EvoSci config set anthropic_base_url "http://localhost:8000/claude"
EvoSci config set anthropic_api_key "sk-dummy"
EvoSci config set model "claude-sonnet-4-6"
# Telegram
EvoSci config set telegram_bot_token "YOUR_BOT_TOKEN"
EvoSci config set telegram_allowed_senders "YOUR_TELEGRAM_USER_ID"
# STT voice transcription
EvoSci config set stt_enabled true
EvoSci config set stt_language zh # zh / en / auto
# Optional: set a default workspace directory
EvoSci config set default_workdir "/absolute/path/to/your/workspace"
7. Smoke test before creating services
Open two terminals:
Terminal 1 — ccproxy:
ccproxy serve --port 8000
Terminal 2 — EvoSci:
cd ~/Dev/tools/EvoScientist
EvoSci serve --auto-approve
Send a text message and a voice message to your Telegram bot. If both work, proceed to set up the background services.
8. Create launchd services (auto-start on login)
Run this script once to create both plist files:
CCPROXY=$(which ccproxy)
EVOSCI_DIR="$HOME/Dev/tools/EvoScientist" # adjust if your path differs
EVOSCI_BIN="${EVOSCI_DIR}/.venv/bin/EvoSci"
# ── ccproxy service ──────────────────────────────────────────────────
cat > ~/Library/LaunchAgents/com.evosci.ccproxy.plist << EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key><string>com.evosci.ccproxy</string>
<key>ProgramArguments</key>
<array>
<string>${CCPROXY}</string>
<string>serve</string>
<string>--port</string>
<string>8000</string>
</array>
<key>RunAtLoad</key><true/>
<key>KeepAlive</key><true/>
<key>StandardOutPath</key><string>/tmp/ccproxy.log</string>
<key>StandardErrorPath</key><string>/tmp/ccproxy.log</string>
</dict>
</plist>
EOF
# ── EvoSci serve service ─────────────────────────────────────────────
cat > ~/Library/LaunchAgents/com.evosci.serve.plist << EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key><string>com.evosci.serve</string>
<key>ProgramArguments</key>
<array>
<string>${EVOSCI_BIN}</string>
<string>serve</string>
<string>--auto-approve</string>
</array>
<key>WorkingDirectory</key><string>${EVOSCI_DIR}</string>
<key>RunAtLoad</key><true/>
<key>KeepAlive</key><true/>
<key>StandardOutPath</key><string>/tmp/evosci.log</string>
<key>StandardErrorPath</key><string>/tmp/evosci.log</string>
</dict>
</plist>
EOF
# ── Load both services ───────────────────────────────────────────────
launchctl load ~/Library/LaunchAgents/com.evosci.ccproxy.plist
launchctl load ~/Library/LaunchAgents/com.evosci.serve.plist
echo "Services loaded:"
launchctl list | grep evosci
9. Check status and logs
# Are both services running?
launchctl list | grep evosci
# Live logs
tail -f /tmp/evosci.log
tail -f /tmp/ccproxy.log
Managing services
| Action | Command |
|---|---|
| Restart EvoSci | launchctl kickstart -k gui/$(id -u)/com.evosci.serve |
| Restart ccproxy | launchctl kickstart -k gui/$(id -u)/com.evosci.ccproxy |
| Stop EvoSci | launchctl unload ~/Library/LaunchAgents/com.evosci.serve.plist |
| Stop ccproxy | launchctl unload ~/Library/LaunchAgents/com.evosci.ccproxy.plist |
| Reload after plist edit | launchctl unload <plist> && launchctl load <plist> |
Troubleshooting
ccproxy not found
ccproxy is not installed. Run:
cd ~/Dev/tools/ccproxy-api && pip install -e .
ValueError: No valid OAuth access token available
ccproxy cannot find your Claude login token. Run:
ccproxy auth login claude-api
A browser window will open. Log in with your Claude subscription account.
Verify the token was saved at ~/.claude/.credentials.json (hidden file, note the leading dot).
Channel telegram fatal error: python-telegram-bot not installed
The package was installed in the wrong Python environment. Use uv pip:
cd ~/Dev/tools/EvoScientist
uv pip install 'python-telegram-bot>=21.0'
zsh: no matches found: evoscientist[telegram]
Shell interprets [ as a glob. Always quote the package name:
uv pip install 'EvoScientist[telegram,stt]' # correct
uv pip install EvoScientist[telegram,stt] # wrong — shell eats the brackets
Agent keeps asking to approve ffmpeg / shell commands
Add --auto-approve to the EvoSci serve command in the plist (see step 8).
Voice messages not transcribed / agent receives [voice: ...] annotation
STT is disabled or not installed:
EvoSci config set stt_enabled true
uv pip install faster-whisper
Whisper outputs gibberish (字幕by索兰娅 etc.)
This is a known Whisper hallucination on silent/very short audio. It is filtered
automatically by the built-in VAD filter and no_speech_prob threshold introduced
in PR #28. Make sure you are on the jhf-research branch or a release that includes
that fix.
Could not find service "com.evosci.*" in domain
The plist was never loaded. Run:
launchctl load ~/Library/LaunchAgents/com.evosci.ccproxy.plist
launchctl load ~/Library/LaunchAgents/com.evosci.serve.plist
heredoc cat > file << EOF hangs in terminal
The shell variable inside the heredoc was not expanded because EOF was quoted
(<< 'EOF'). Use unquoted << EOF when the content contains $VARIABLES that
should be expanded, or pre-set the variables before running the command.
Services on external drive not starting at boot
/Volumes/... paths are only available after the external drive mounts.
With KeepAlive: true, launchd will keep retrying every few seconds and
the services will start automatically once the drive is available.
This is normal behaviour for a Mac Mini where the drive is always connected.
pip install installs to conda base instead of .venv
Always use uv pip install inside the EvoScientist project directory,
or explicitly target the venv:
cd ~/Dev/tools/EvoScientist
uv pip install 'some-package'
Architecture overview
Telegram app
│ voice / text message
▼
python-telegram-bot (polling)
│
▼
EvoScientist channel layer
│ STT hook in _enqueue_raw()
│ .ogg → faster-whisper → plain text
▼
EvoScientist agent
│ tool calls / LLM requests
▼
ccproxy (localhost:8000)
│ OAuth token from ~/.claude/.credentials.json
▼
Anthropic Claude API (claude.ai subscription)