@alexnodeland/claude-telegram 0.1.4 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,27 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.2.1] - 2026-03-22
9
+
10
+ ### Fixed
11
+
12
+ - **`claude` not found in daemon** — orchestrator now reads `CLAUDE_BIN` env var for the CLI path, falling back to `"claude"` from `$PATH`. Fixes "Executable not found" when launchd/systemd don't inherit the user's full `$PATH`.
13
+ - Daemon install script (`daemon.sh`) auto-detects the `claude` binary path and passes `CLAUDE_BIN` into the launchd plist / systemd unit.
14
+
15
+ ## [0.2.0] - 2025-03-22
16
+
17
+ ### Added
18
+
19
+ - **npm distribution** — install via `bun add -g @alexnodeland/claude-telegram`
20
+ - **Daemon script** — `claude-telegram-daemon install` sets up launchd (macOS) or systemd (Linux) automatically
21
+ - **CONTRIBUTING.md** — development setup, code style, testing, and PR guidelines
22
+ - **GitHub Release workflow** — tag-triggered CI, GitHub Release, and npm publish with provenance
23
+
24
+ ### Changed
25
+
26
+ - Quick start defaults to npm install instead of git clone
27
+ - Orchestrator and channel mode docs updated for npm install path
28
+
8
29
  ## [0.1.0] - 2025-03-22
9
30
 
10
31
  ### Added
@@ -23,4 +44,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
23
44
  - **HTML formatting** — all output uses Telegram HTML parse mode for reliable rendering
24
45
  - **Zero Telegram SDK** — all API calls use native `fetch`
25
46
 
47
+ [0.2.1]: https://github.com/alexnodeland/claude-telegram/releases/tag/v0.2.1
48
+ [0.2.0]: https://github.com/alexnodeland/claude-telegram/releases/tag/v0.2.0
26
49
  [0.1.0]: https://github.com/alexnodeland/claude-telegram/releases/tag/v0.1.0
package/README.md CHANGED
@@ -4,6 +4,7 @@
4
4
 
5
5
  **Control Claude Code from Telegram — run tasks, review diffs, and ship code from your phone.**
6
6
 
7
+ [![npm](https://img.shields.io/npm/v/@alexnodeland/claude-telegram?color=cc785c)](https://www.npmjs.com/package/@alexnodeland/claude-telegram)
7
8
  [![GitHub Release](https://img.shields.io/github/v/release/alexnodeland/claude-telegram?style=flat&color=cc785c)](https://github.com/alexnodeland/claude-telegram/releases)
8
9
  [![CI](https://img.shields.io/github/actions/workflow/status/alexnodeland/claude-telegram/ci.yml?branch=main&label=CI)](https://github.com/alexnodeland/claude-telegram/actions/workflows/ci.yml)
9
10
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
@@ -54,19 +55,54 @@ Claude Telegram gives you full Claude Code access from any Telegram client — p
54
55
 
55
56
  ## Quick start
56
57
 
57
- **1. Create a Telegram bot** — message [@BotFather](https://t.me/BotFather), send `/newbot`, copy the token.
58
+ ### Prerequisites
58
59
 
59
- **2. Install and run:**
60
+ - [Bun](https://bun.sh) >= 1.1
61
+ - [Claude Code](https://docs.anthropic.com/en/docs/claude-code) >= 2.1
62
+ - A Telegram bot token (message [@BotFather](https://t.me/BotFather), send `/newbot`, copy the token)
63
+
64
+ ### Install and run
60
65
 
61
66
  ```bash
62
- git clone https://github.com/alexnodeland/claude-telegram.git
63
- cd claude-telegram
64
- bun install
67
+ # Install the package
68
+ bun add -g @alexnodeland/claude-telegram
69
+
70
+ # Set your bot token
65
71
  export TELEGRAM_BOT_TOKEN=your_token_here
66
- bun run start:orchestrator
72
+
73
+ # Start the orchestrator
74
+ claude-telegram-orchestrator
67
75
  ```
68
76
 
69
- **3. Pair your Telegram account** — send `/start` to your bot, then enter the pairing code shown in your terminal.
77
+ Or run directly without installing:
78
+
79
+ ```bash
80
+ TELEGRAM_BOT_TOKEN=your_token_here bunx @alexnodeland/claude-telegram/src/orchestrator.ts
81
+ ```
82
+
83
+ ### Pair your Telegram account
84
+
85
+ 1. Send `/start` to your bot in Telegram
86
+ 2. An already-approved user sends `/approve <CODE>` with the 6-character pairing code
87
+
88
+ To pre-approve users on first run, set `TELEGRAM_ALLOWED_USERS` with comma-separated Telegram user IDs:
89
+
90
+ ```bash
91
+ TELEGRAM_ALLOWED_USERS=783772449 claude-telegram-orchestrator
92
+ ```
93
+
94
+ ### Running as a daemon
95
+
96
+ A built-in script sets up launchd (macOS) or systemd (Linux) automatically:
97
+
98
+ ```bash
99
+ claude-telegram-daemon install # install and start
100
+ claude-telegram-daemon status # check if running
101
+ claude-telegram-daemon logs # tail logs
102
+ claude-telegram-daemon uninstall # stop and remove
103
+ ```
104
+
105
+ Auto-restarts on crash and starts on login. See the [orchestrator docs](docs/orchestrator-mode.md#running-as-a-daemon) for manual setup options.
70
106
 
71
107
  > [!TIP]
72
108
  > The **orchestrator mode** above is standalone and manages its own Claude sessions. There's also a [channel mode](docs/channel-mode.md) that attaches Telegram to an existing Claude Code session as an MCP plugin.
@@ -173,18 +209,11 @@ Anything that isn't a command is sent as a prompt to Claude.
173
209
  | [Channel Mode](docs/channel-mode.md) | MCP channel plugin setup, pairing, access commands, tools |
174
210
  | [Architecture](docs/architecture.md) | System design, module map, security model, data flow |
175
211
  | [Changelog](CHANGELOG.md) | Release history |
212
+ | [Contributing](CONTRIBUTING.md) | Development setup, code style, testing, pull requests |
176
213
 
177
- ## Development
178
-
179
- ```bash
180
- just setup # Install deps + configure git hooks
181
- just dev-orchestrator # Watch mode
182
- just ci # Typecheck + lint + test (112 tests)
183
- just fix # Auto-fix lint/format
184
- just release 1.1.0 # Tag a release
185
- ```
214
+ ## Contributing
186
215
 
187
- See [CLAUDE.md](CLAUDE.md) for architecture details and contributor guidelines.
216
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup and guidelines.
188
217
 
189
218
  ## License
190
219
 
package/package.json CHANGED
@@ -1,13 +1,14 @@
1
1
  {
2
2
  "name": "@alexnodeland/claude-telegram",
3
- "version": "0.1.4",
3
+ "version": "0.2.1",
4
4
  "description": "Claude Code Channel plugin + standalone orchestrator bridging Telegram to Claude Code sessions",
5
5
  "module": "src/index.ts",
6
6
  "main": "src/index.ts",
7
7
  "type": "module",
8
8
  "bin": {
9
9
  "claude-telegram": "./src/index.ts",
10
- "claude-telegram-orchestrator": "./src/orchestrator.ts"
10
+ "claude-telegram-orchestrator": "./src/orchestrator.ts",
11
+ "claude-telegram-daemon": "./scripts/daemon.sh"
11
12
  },
12
13
  "scripts": {
13
14
  "start": "bun run src/index.ts",
@@ -36,6 +37,7 @@
36
37
  },
37
38
  "files": [
38
39
  "src/",
40
+ "scripts/",
39
41
  "LICENSE",
40
42
  "README.md",
41
43
  "CHANGELOG.md"
@@ -0,0 +1,161 @@
1
+ #!/usr/bin/env bash
2
+ # Install/uninstall claude-telegram as a system daemon.
3
+ # Usage: daemon.sh install | uninstall | status | logs
4
+ set -euo pipefail
5
+
6
+ # Resolve paths
7
+ BUN="$(which bun 2>/dev/null || echo "$HOME/.bun/bin/bun")"
8
+ SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
9
+ ORCHESTRATOR="$SCRIPT_DIR/src/orchestrator.ts"
10
+ TOKEN="${TELEGRAM_BOT_TOKEN:-}"
11
+ CLAUDE_BIN="${CLAUDE_BIN:-$(which claude 2>/dev/null || echo "claude")}"
12
+
13
+ if [ -z "$TOKEN" ] && [ "$1" != "uninstall" ] && [ "$1" != "status" ] && [ "$1" != "logs" ]; then
14
+ echo "Error: TELEGRAM_BOT_TOKEN is not set"
15
+ echo " export TELEGRAM_BOT_TOKEN=your_token_here"
16
+ exit 1
17
+ fi
18
+
19
+ # ── macOS (launchd) ─────────────────────────────────────────────────────────
20
+
21
+ PLIST="$HOME/Library/LaunchAgents/com.claude-telegram.plist"
22
+
23
+ install_launchd() {
24
+ cat > "$PLIST" << EOF
25
+ <?xml version="1.0" encoding="UTF-8"?>
26
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
27
+ <plist version="1.0">
28
+ <dict>
29
+ <key>Label</key>
30
+ <string>com.claude-telegram</string>
31
+ <key>ProgramArguments</key>
32
+ <array>
33
+ <string>$BUN</string>
34
+ <string>run</string>
35
+ <string>$ORCHESTRATOR</string>
36
+ </array>
37
+ <key>EnvironmentVariables</key>
38
+ <dict>
39
+ <key>TELEGRAM_BOT_TOKEN</key>
40
+ <string>$TOKEN</string>
41
+ <key>CLAUDE_BIN</key>
42
+ <string>$CLAUDE_BIN</string>
43
+ <key>PATH</key>
44
+ <string>/usr/local/bin:/usr/bin:/bin:$HOME/.bun/bin</string>
45
+ </dict>
46
+ <key>KeepAlive</key>
47
+ <true/>
48
+ <key>StandardOutPath</key>
49
+ <string>/tmp/claude-telegram.log</string>
50
+ <key>StandardErrorPath</key>
51
+ <string>/tmp/claude-telegram.log</string>
52
+ </dict>
53
+ </plist>
54
+ EOF
55
+ launchctl load "$PLIST"
56
+ echo "Daemon installed and started."
57
+ echo " Logs: tail -f /tmp/claude-telegram.log"
58
+ echo " Stop: $0 uninstall"
59
+ }
60
+
61
+ uninstall_launchd() {
62
+ if [ -f "$PLIST" ]; then
63
+ launchctl unload "$PLIST" 2>/dev/null || true
64
+ rm -f "$PLIST"
65
+ echo "Daemon stopped and removed."
66
+ else
67
+ echo "No daemon installed."
68
+ fi
69
+ }
70
+
71
+ status_launchd() {
72
+ if launchctl list com.claude-telegram &>/dev/null; then
73
+ echo "Running"
74
+ launchctl list com.claude-telegram
75
+ else
76
+ echo "Not running"
77
+ fi
78
+ }
79
+
80
+ logs_launchd() {
81
+ tail -f /tmp/claude-telegram.log
82
+ }
83
+
84
+ # ── Linux (systemd) ─────────────────────────────────────────────────────────
85
+
86
+ SERVICE_DIR="$HOME/.config/systemd/user"
87
+ SERVICE="$SERVICE_DIR/claude-telegram.service"
88
+
89
+ install_systemd() {
90
+ mkdir -p "$SERVICE_DIR"
91
+ cat > "$SERVICE" << EOF
92
+ [Unit]
93
+ Description=Claude Telegram Orchestrator
94
+ After=network.target
95
+
96
+ [Service]
97
+ ExecStart=$BUN run $ORCHESTRATOR
98
+ Environment=TELEGRAM_BOT_TOKEN=$TOKEN
99
+ Environment=CLAUDE_BIN=$CLAUDE_BIN
100
+ Restart=on-failure
101
+ RestartSec=5
102
+
103
+ [Install]
104
+ WantedBy=default.target
105
+ EOF
106
+ systemctl --user daemon-reload
107
+ systemctl --user enable --now claude-telegram
108
+ echo "Daemon installed and started."
109
+ echo " Logs: journalctl --user -u claude-telegram -f"
110
+ echo " Stop: $0 uninstall"
111
+ }
112
+
113
+ uninstall_systemd() {
114
+ if [ -f "$SERVICE" ]; then
115
+ systemctl --user disable --now claude-telegram 2>/dev/null || true
116
+ rm -f "$SERVICE"
117
+ systemctl --user daemon-reload
118
+ echo "Daemon stopped and removed."
119
+ else
120
+ echo "No daemon installed."
121
+ fi
122
+ }
123
+
124
+ status_systemd() {
125
+ systemctl --user status claude-telegram 2>/dev/null || echo "Not running"
126
+ }
127
+
128
+ logs_systemd() {
129
+ journalctl --user -u claude-telegram -f
130
+ }
131
+
132
+ # ── Dispatch ─────────────────────────────────────────────────────────────────
133
+
134
+ ACTION="${1:-install}"
135
+ OS="$(uname -s)"
136
+
137
+ case "$OS" in
138
+ Darwin)
139
+ case "$ACTION" in
140
+ install) install_launchd ;;
141
+ uninstall) uninstall_launchd ;;
142
+ status) status_launchd ;;
143
+ logs) logs_launchd ;;
144
+ *) echo "Usage: $0 install|uninstall|status|logs"; exit 1 ;;
145
+ esac
146
+ ;;
147
+ Linux)
148
+ case "$ACTION" in
149
+ install) install_systemd ;;
150
+ uninstall) uninstall_systemd ;;
151
+ status) status_systemd ;;
152
+ logs) logs_systemd ;;
153
+ *) echo "Usage: $0 install|uninstall|status|logs"; exit 1 ;;
154
+ esac
155
+ ;;
156
+ *)
157
+ echo "Unsupported OS: $OS"
158
+ echo "Use tmux instead: tmux new-session -d -s claude 'claude-telegram-orchestrator'"
159
+ exit 1
160
+ ;;
161
+ esac
@@ -348,6 +348,7 @@ const MAX_TURNS = Number(process.env.ORCHESTRATOR_MAX_TURNS ?? "50");
348
348
  let globalModel = process.env.ORCHESTRATOR_MODEL;
349
349
 
350
350
  // Absolute path to the sidecar script
351
+ const CLAUDE_BIN = process.env.CLAUDE_BIN ?? "claude";
351
352
  const RELAY_SCRIPT = resolve(import.meta.dir, "permission-relay.ts");
352
353
 
353
354
  process.stderr.write(
@@ -1258,7 +1259,7 @@ async function runQuery(
1258
1259
  await writeFile(mcpConfigPath, JSON.stringify(mcpConfig));
1259
1260
 
1260
1261
  const args = [
1261
- "claude",
1262
+ CLAUDE_BIN,
1262
1263
  "-p",
1263
1264
  "--output-format",
1264
1265
  "stream-json",