abbasyk60 commited on
Commit
68cd3b2
·
1 Parent(s): 6be7ab4

Deploy OpenCode with Web UI and Telegram bot integration

Browse files
Files changed (7) hide show
  1. .gitignore +5 -0
  2. Dockerfile +43 -0
  3. README.md +30 -10
  4. bot.js +140 -0
  5. entrypoint.sh +75 -0
  6. opencode.json +73 -0
  7. package.json +12 -0
.gitignore ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ node_modules/
2
+ data/
3
+ .env
4
+ *.log
5
+ .DS_Store
Dockerfile ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM node:20-slim
2
+
3
+ # Install system dependencies
4
+ RUN apt-get update && apt-get install -y \
5
+ git \
6
+ curl \
7
+ procps \
8
+ lsof \
9
+ python3 \
10
+ && rm -rf /var/lib/apt/lists/*
11
+
12
+ # Install OpenCode
13
+ RUN npm install -g opencode-ai
14
+
15
+ # Create app directory
16
+ WORKDIR /app
17
+
18
+ # Copy package.json and install dependencies
19
+ COPY package.json ./
20
+ RUN npm install --production
21
+
22
+ # Copy OpenCode config
23
+ COPY opencode.json /root/.config/opencode/opencode.json
24
+
25
+ # Copy bot code
26
+ COPY bot.js ./
27
+
28
+ # Create entrypoint script
29
+ COPY entrypoint.sh /entrypoint.sh
30
+ RUN chmod +x /entrypoint.sh
31
+
32
+ # Create data directories
33
+ RUN mkdir -p /app/data /root/.local/share/opencode
34
+
35
+ # Expose port 7860 for HF Spaces
36
+ EXPOSE 7860
37
+
38
+ # Health check
39
+ HEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \
40
+ CMD curl -f http://localhost:7860/ || exit 1
41
+
42
+ # Run entrypoint
43
+ ENTRYPOINT ["/entrypoint.sh"]
README.md CHANGED
@@ -1,13 +1,33 @@
1
  ---
2
- title: Opencode
3
- emoji: 🏃
4
- colorFrom: green
5
- colorTo: yellow
6
- sdk: gradio
7
- sdk_version: 6.20.0
8
- python_version: '3.12'
9
- app_file: app.py
10
- pinned: false
11
  ---
12
 
13
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ title: OpenCode AI Agent
3
+ emoji: 🤖
4
+ colorFrom: blue
5
+ colorTo: purple
6
+ sdk: docker
7
+ app_port: 7860
8
+ pinned: true
 
 
9
  ---
10
 
11
+ # OpenCode AI Agent on HuggingFace Spaces
12
+
13
+ AI coding agent with Web UI and Telegram integration powered by OpenCode Zen.
14
+
15
+ ## Features
16
+
17
+ - **Web UI**: Access OpenCode directly in your browser
18
+ - **Telegram Bot**: Chat with OpenCode via Telegram
19
+ - **Multiple Models**: GPT-5, Claude, Gemini, DeepSeek and more
20
+ - **Free Models Available**: DeepSeek V4 Flash Free, MiMo-V2.5 Free
21
+
22
+ ## Access
23
+
24
+ - **Web UI**: [https://abbasyk60-opencode.hf.space](https://abbasyk60-opencode.hf.space)
25
+ - **Telegram**: Send messages to your bot in the configured supergroup
26
+
27
+ ## Configuration
28
+
29
+ Environment variables (set in HF Space secrets):
30
+
31
+ - `OPENCODE_ZEN_KEY` - OpenCode Zen API key
32
+ - `TELEGRAM_BOT_TOKEN` - Telegram bot token
33
+ - `TELEGRAM_CHAT_ID` - Telegram supergroup chat ID
bot.js ADDED
@@ -0,0 +1,140 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const { Bot } = require("grammy");
2
+
3
+ // Get environment variables
4
+ const BOT_TOKEN = process.env.TELEGRAM_BOT_TOKEN;
5
+ const CHAT_ID = process.env.TELEGRAM_CHAT_ID;
6
+ const OPENCODE_URL = process.env.OPENCODE_URL || "http://localhost:7860";
7
+
8
+ if (!BOT_TOKEN) {
9
+ console.error("[ERROR] TELEGRAM_BOT_TOKEN not set");
10
+ process.exit(1);
11
+ }
12
+
13
+ // Create bot
14
+ const bot = new Bot(BOT_TOKEN);
15
+
16
+ // Store sessions
17
+ const sessions = new Map();
18
+
19
+ // Helper: Call OpenCode API
20
+ async function callOpenCode(message, sessionId) {
21
+ try {
22
+ // Create a new session or use existing
23
+ const sessionResponse = await fetch(`${OPENCODE_URL}/session`, {
24
+ method: "POST",
25
+ headers: { "Content-Type": "application/json" },
26
+ body: JSON.stringify({}),
27
+ });
28
+
29
+ if (!sessionResponse.ok) {
30
+ throw new Error("Failed to create session");
31
+ }
32
+
33
+ const session = await sessionResponse.json();
34
+ const sid = session.id || sessionId;
35
+
36
+ // Send message to OpenCode
37
+ const response = await fetch(`${OPENCODE_URL}/session/${sid}/message`, {
38
+ method: "POST",
39
+ headers: { "Content-Type": "application/json" },
40
+ body: JSON.stringify({ content: message }),
41
+ });
42
+
43
+ if (!response.ok) {
44
+ throw new Error("Failed to send message");
45
+ }
46
+
47
+ const result = await response.json();
48
+ return result.content || result.message || "No response from OpenCode";
49
+ } catch (error) {
50
+ console.error("[ERROR] OpenCode API call failed:", error.message);
51
+ return `Error communicating with OpenCode: ${error.message}`;
52
+ }
53
+ }
54
+
55
+ // Handle /start command
56
+ bot.command("start", async (ctx) => {
57
+ await ctx.reply(
58
+ "Welcome to OpenCode AI Agent!\n\n" +
59
+ "Send me any message and I'll respond using AI.\n\n" +
60
+ "Commands:\n" +
61
+ "/start - Show this message\n" +
62
+ "/help - Show help\n" +
63
+ "/new - Start a new session"
64
+ );
65
+ });
66
+
67
+ // Handle /help command
68
+ bot.command("help", async (ctx) => {
69
+ await ctx.reply(
70
+ "OpenCode AI Agent Help\n\n" +
71
+ "Simply send me a message and I'll process it using OpenCode AI.\n\n" +
72
+ "Features:\n" +
73
+ "- AI-powered responses\n" +
74
+ "- Code assistance\n" +
75
+ "- Multiple AI models available\n\n" +
76
+ "Use /new to start a fresh conversation."
77
+ );
78
+ });
79
+
80
+ // Handle /new command
81
+ bot.command("new", async (ctx) => {
82
+ const chatId = ctx.chat.id;
83
+ sessions.delete(chatId);
84
+ await ctx.reply("New session started. Send me a message!");
85
+ });
86
+
87
+ // Handle text messages
88
+ bot.on("message:text", async (ctx) => {
89
+ const chatId = ctx.chat.id;
90
+ const message = ctx.message.text;
91
+
92
+ // Check if it's the allowed chat
93
+ if (CHAT_ID && chatId.toString() !== CHAT_ID.toString()) {
94
+ await ctx.reply("Sorry, I'm only available in the designated group.");
95
+ return;
96
+ }
97
+
98
+ // Show typing indicator
99
+ await ctx.replyWithChatAction("typing");
100
+
101
+ // Get or create session
102
+ let sessionId = sessions.get(chatId);
103
+ if (!sessionId) {
104
+ sessionId = `telegram_${chatId}`;
105
+ sessions.set(chatId, sessionId);
106
+ }
107
+
108
+ // Call OpenCode
109
+ const response = await callOpenCode(message, sessionId);
110
+
111
+ // Send response
112
+ try {
113
+ // Telegram has a 4096 character limit
114
+ if (response.length > 4000) {
115
+ // Split into multiple messages
116
+ const chunks = response.match(/.{1,4000}/gs);
117
+ for (const chunk of chunks) {
118
+ await ctx.reply(chunk);
119
+ }
120
+ } else {
121
+ await ctx.reply(response);
122
+ }
123
+ } catch (error) {
124
+ console.error("[ERROR] Failed to send message:", error.message);
125
+ await ctx.reply("Failed to send response. Please try again.");
126
+ }
127
+ });
128
+
129
+ // Error handler
130
+ bot.catch((err) => {
131
+ console.error("[ERROR] Bot error:", err);
132
+ });
133
+
134
+ // Start bot
135
+ console.log("[OK] Starting Telegram bot...");
136
+ bot.start({
137
+ onStart: () => {
138
+ console.log("[OK] Telegram bot is running!");
139
+ },
140
+ });
entrypoint.sh ADDED
@@ -0,0 +1,75 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+ set -e
3
+
4
+ echo "========================================="
5
+ echo " OpenCode AI Agent - Starting"
6
+ echo "========================================="
7
+
8
+ # Create OpenCode config directory
9
+ mkdir -p /root/.config/opencode
10
+
11
+ # Write OpenCode config if OPENCODE_ZEN_KEY is provided
12
+ if [ -n "$OPENCODE_ZEN_KEY" ]; then
13
+ cat > /root/.config/opencode/opencode.json <<EOF
14
+ {
15
+ "\$schema": "https://opencode.ai/config.json",
16
+ "model": "opencode/gpt-5.4",
17
+ "small_model": "opencode/gpt-5-nano",
18
+ "provider": {
19
+ "opencode": {
20
+ "npm": "@ai-sdk/openai-compatible",
21
+ "name": "OpenCode Zen",
22
+ "options": {
23
+ "baseURL": "https://opencode.ai/zen/v1",
24
+ "apiKey": "$OPENCODE_ZEN_KEY"
25
+ }
26
+ }
27
+ },
28
+ "server": {
29
+ "port": 7860,
30
+ "hostname": "0.0.0.0"
31
+ }
32
+ }
33
+ EOF
34
+ echo "[OK] OpenCode config written"
35
+ else
36
+ echo "[WARN] OPENCODE_ZEN_KEY not set - using bundled config"
37
+ cp /app/opencode.json /root/.config/opencode/opencode.json 2>/dev/null || true
38
+ fi
39
+
40
+ # Create data directories
41
+ mkdir -p /root/.local/share/opencode
42
+ mkdir -p /app/data
43
+
44
+ # Start OpenCode web server in background
45
+ echo "[OK] Starting OpenCode web server on port 7860..."
46
+ opencode web --port 7860 --hostname 0.0.0.0 &
47
+ OPENCODE_PID=$!
48
+
49
+ # Wait for OpenCode to be ready
50
+ echo "[OK] Waiting for OpenCode to start..."
51
+ for i in $(seq 1 30); do
52
+ if curl -s http://localhost:7860 > /dev/null 2>&1; then
53
+ echo "[OK] OpenCode is ready!"
54
+ break
55
+ fi
56
+ sleep 2
57
+ done
58
+
59
+ # Start Telegram bot if token is provided
60
+ if [ -n "$TELEGRAM_BOT_TOKEN" ]; then
61
+ echo "[OK] Starting Telegram bot..."
62
+ node /app/bot.js &
63
+ TELEGRAM_PID=$!
64
+ echo "[OK] Telegram bot started!"
65
+ else
66
+ echo "[WARN] TELEGRAM_BOT_TOKEN not set - Telegram bot disabled"
67
+ fi
68
+
69
+ echo "========================================="
70
+ echo " All services started!"
71
+ echo " Web UI: http://localhost:7860"
72
+ echo "========================================="
73
+
74
+ # Keep the container running
75
+ wait -n $OPENCODE_PID ${TELEGRAM_PID:-}
opencode.json ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "$schema": "https://opencode.ai/config.json",
3
+ "model": "opencode/gpt-5.4",
4
+ "small_model": "opencode/gpt-5-nano",
5
+ "provider": {
6
+ "opencode": {
7
+ "npm": "@ai-sdk/openai-compatible",
8
+ "name": "OpenCode Zen",
9
+ "options": {
10
+ "baseURL": "https://opencode.ai/zen/v1",
11
+ "apiKey": "{env:OPENCODE_ZEN_KEY}"
12
+ },
13
+ "models": {
14
+ "gpt-5.4": { "name": "GPT 5.4" },
15
+ "gpt-5.4-pro": { "name": "GPT 5.4 Pro" },
16
+ "gpt-5.4-mini": { "name": "GPT 5.4 Mini" },
17
+ "gpt-5.4-nano": { "name": "GPT 5.4 Nano" },
18
+ "gpt-5.5": { "name": "GPT 5.5" },
19
+ "gpt-5.5-pro": { "name": "GPT 5.5 Pro" },
20
+ "gpt-5.3-codex": { "name": "GPT 5.3 Codex" },
21
+ "gpt-5.3-codex-spark": { "name": "GPT 5.3 Codex Spark" },
22
+ "gpt-5.2": { "name": "GPT 5.2" },
23
+ "gpt-5.1": { "name": "GPT 5.1" },
24
+ "gpt-5": { "name": "GPT 5" },
25
+ "gpt-5-nano": { "name": "GPT 5 Nano" },
26
+ "gpt-5.6-sol": { "name": "GPT 5.6 Sol" },
27
+ "gpt-5.6-terra": { "name": "GPT 5.6 Terra" },
28
+ "gpt-5.6-luna": { "name": "GPT 5.6 Luna" },
29
+ "claude-sonnet-5": { "name": "Claude Sonnet 5" },
30
+ "claude-opus-5": { "name": "Claude Opus 5" },
31
+ "claude-opus-4-8": { "name": "Claude Opus 4.8" },
32
+ "claude-opus-4-7": { "name": "Claude Opus 4.7" },
33
+ "claude-opus-4-5": { "name": "Claude Opus 4.5" },
34
+ "claude-sonnet-4-6": { "name": "Claude Sonnet 4.6" },
35
+ "claude-sonnet-4-5": { "name": "Claude Sonnet 4.5" },
36
+ "claude-haiku-4-5": { "name": "Claude Haiku 4.5" },
37
+ "claude-fable-5": { "name": "Claude Fable 5" },
38
+ "gemini-3.6-flash": { "name": "Gemini 3.6 Flash" },
39
+ "gemini-3.5-flash": { "name": "Gemini 3.5 Flash" },
40
+ "gemini-3.5-flash-lite": { "name": "Gemini 3.5 Flash Lite" },
41
+ "gemini-3.1-pro": { "name": "Gemini 3.1 Pro" },
42
+ "gemini-3-flash": { "name": "Gemini 3 Flash" },
43
+ "grok-4.5": { "name": "Grok 4.5" },
44
+ "grok-build-0.1": { "name": "Grok Build 0.1" },
45
+ "qwen3.7-max": { "name": "Qwen3.7 Max" },
46
+ "qwen3.7-plus": { "name": "Qwen3.7 Plus" },
47
+ "qwen3.6-plus": { "name": "Qwen3.6 Plus" },
48
+ "qwen3.5-plus": { "name": "Qwen3.5 Plus" },
49
+ "deepseek-v4-pro": { "name": "DeepSeek V4 Pro" },
50
+ "deepseek-v4-flash": { "name": "DeepSeek V4 Flash" },
51
+ "deepseek-v4-flash-free": { "name": "DeepSeek V4 Flash Free" },
52
+ "minimax-m3": { "name": "MiniMax M3" },
53
+ "minimax-m2.7": { "name": "MiniMax M2.7" },
54
+ "glm-5.2": { "name": "GLM 5.2" },
55
+ "glm-5.1": { "name": "GLM 5.1" },
56
+ "kimi-k2.5": { "name": "Kimi K2.5" },
57
+ "kimi-k2.6": { "name": "Kimi K2.6" },
58
+ "kimi-k2.7-code": { "name": "Kimi K2.7 Code" },
59
+ "big-pickle": { "name": "Big Pickle" },
60
+ "mimo-v2.5-free": { "name": "MiMo-V2.5 Free" },
61
+ "laguna-s-2.1-free": { "name": "Laguna S 2.1 Free" },
62
+ "ling-3.0-flash-free": { "name": "Ling-3.0 Flash Free" },
63
+ "north-mini-code-free": { "name": "North Mini Code Free" },
64
+ "nemotron-3-ultra-free": { "name": "Nemotron 3 Ultra Free" }
65
+ }
66
+ }
67
+ },
68
+ "server": {
69
+ "port": 7860,
70
+ "hostname": "0.0.0.0",
71
+ "cors": ["*"]
72
+ }
73
+ }
package.json ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "opencode-telegram-bot",
3
+ "version": "1.0.0",
4
+ "description": "Telegram bot for OpenCode AI Agent",
5
+ "scripts": {
6
+ "start": "node bot.js",
7
+ "dev": "node --watch bot.js"
8
+ },
9
+ "dependencies": {
10
+ "grammy": "^1.21.0"
11
+ }
12
+ }