@thelapyae/geniclaw 1.0.0

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/geniclaw.sh ADDED
@@ -0,0 +1,388 @@
1
+ #!/bin/bash
2
+ # Geniclaw - Main daemon using tmux + Google Gemini + Telegram
3
+
4
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5
+ TMUX_SESSION="geniclaw"
6
+
7
+ # Determine Config/Work Directory
8
+ # Check for local .env or .geniclaw directory to determine mode
9
+ if [ -f "$SCRIPT_DIR/.env" ] || [ -d "$SCRIPT_DIR/.geniclaw" ]; then
10
+ # Local Dev Mode
11
+ GENICLAW_WORK_DIR="$SCRIPT_DIR/.geniclaw"
12
+ ENV_FILE="$SCRIPT_DIR/.env"
13
+ TMUX_SESSION="geniclaw-dev"
14
+ else
15
+ # Global/Production Mode
16
+ GENICLAW_WORK_DIR="$HOME/.geniclaw"
17
+ ENV_FILE="$GENICLAW_WORK_DIR/.env"
18
+ fi
19
+
20
+ export GENICLAW_WORK_DIR
21
+ export GENICLAW_ENV_FILE="$ENV_FILE"
22
+
23
+ LOG_DIR="$GENICLAW_WORK_DIR/logs"
24
+
25
+ # Ensure directories exist
26
+ mkdir -p "$LOG_DIR"
27
+ mkdir -p "$GENICLAW_WORK_DIR/queue/incoming"
28
+ mkdir -p "$GENICLAW_WORK_DIR/queue/outgoing"
29
+ mkdir -p "$GENICLAW_WORK_DIR/channels"
30
+
31
+ GREEN='\033[0;32m'
32
+ YELLOW='\033[1;33m'
33
+ RED='\033[0;31m'
34
+ BLUE='\033[0;34m'
35
+ NC='\033[0m'
36
+
37
+ log() {
38
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOG_DIR/daemon.log"
39
+ }
40
+
41
+ # Check if session exists
42
+ session_exists() {
43
+ tmux has-session -t "$TMUX_SESSION" 2>/dev/null
44
+ }
45
+
46
+ # Check Environment using Node.js
47
+ check_env() {
48
+ # Ensure config exists or run setup
49
+ if ! node -e 'const fs=require("fs"); const c=JSON.parse(fs.readFileSync(require("path").join(process.env.GENICLAW_WORK_DIR || require("os").homedir()+"/.geniclaw", "config.json"))); if(!c.geminiApiKey || !c.telegramBotToken) process.exit(1)' 2>/dev/null; then
50
+ echo -e "${YELLOW}Configuration missing or incomplete.${NC}"
51
+ echo "Running setup..."
52
+ npm run build >/dev/null 2>&1
53
+ node dist/setup.js
54
+ fi
55
+ }
56
+
57
+ # Start daemon
58
+ start_daemon() {
59
+ if session_exists; then
60
+ echo -e "${YELLOW}Session already running${NC}"
61
+ return 1
62
+ fi
63
+
64
+ check_env
65
+
66
+ log "Starting Geniclaw daemon..."
67
+
68
+ # Check if Node.js dependencies are installed
69
+ if [ ! -d "$SCRIPT_DIR/node_modules" ]; then
70
+ echo -e "${YELLOW}Installing Node.js dependencies...${NC}"
71
+ cd "$SCRIPT_DIR"
72
+ PUPPETEER_SKIP_DOWNLOAD=true npm install
73
+ fi
74
+
75
+ # Build TypeScript if needed
76
+ if [ ! -d "$SCRIPT_DIR/dist" ] || [ "$SCRIPT_DIR/src/telegram-client.ts" -nt "$SCRIPT_DIR/dist/telegram-client.js" ] || [ "$SCRIPT_DIR/src/queue-processor.ts" -nt "$SCRIPT_DIR/dist/queue-processor.js" ]; then
77
+ echo -e "${YELLOW}Building TypeScript...${NC}"
78
+ cd "$SCRIPT_DIR"
79
+ npm run build
80
+ fi
81
+
82
+ # Run session start hook
83
+ HOOK_SCRIPT="$SCRIPT_DIR/.gemini/hooks/session-start.sh"
84
+ if [ -f "$HOOK_SCRIPT" ]; then
85
+ chmod +x "$HOOK_SCRIPT"
86
+ echo -e "${BLUE}Running startup hook...${NC}"
87
+ "$HOOK_SCRIPT"
88
+ echo ""
89
+ fi
90
+
91
+ # Create detached tmux session with 4 panes
92
+ if command -v tmux &> /dev/null; then
93
+ tmux new-session -d -s "$TMUX_SESSION" -n "geniclaw" -c "$SCRIPT_DIR"
94
+
95
+ # Split into 4 panes: 2 rows, 2 columns
96
+ tmux split-window -v -t "$TMUX_SESSION" -c "$SCRIPT_DIR"
97
+ tmux split-window -h -t "$TMUX_SESSION:0.0" -c "$SCRIPT_DIR"
98
+ tmux split-window -h -t "$TMUX_SESSION:0.2" -c "$SCRIPT_DIR"
99
+
100
+ # Pane 0 (top-left): Telegram client
101
+ tmux send-keys -t "$TMUX_SESSION:0.0" "cd '$SCRIPT_DIR' && GENICLAW_HOME='$GENICLAW_HOME' node dist/telegram-client.js" C-m
102
+
103
+ # Pane 1 (top-right): Queue processor
104
+ tmux send-keys -t "$TMUX_SESSION:0.1" "cd '$SCRIPT_DIR' && GENICLAW_HOME='$GENICLAW_HOME' node dist/queue-processor.js" C-m
105
+
106
+ # Pane 2 (bottom-left): Heartbeat
107
+ tmux send-keys -t "$TMUX_SESSION:0.2" "cd '$SCRIPT_DIR' && GENICLAW_HOME='$GENICLAW_HOME' ./heartbeat-cron.sh" C-m
108
+
109
+ # Pane 3 (bottom-right): Logs
110
+ tmux send-keys -t "$TMUX_SESSION:0.3" "cd '$SCRIPT_DIR' && tail -f $LOG_DIR/queue.log" C-m
111
+
112
+ # Set pane titles
113
+ tmux select-pane -t "$TMUX_SESSION:0.0" -T "Telegram"
114
+ tmux select-pane -t "$TMUX_SESSION:0.1" -T "Queue"
115
+ tmux select-pane -t "$TMUX_SESSION:0.2" -T "Heartbeat"
116
+ tmux select-pane -t "$TMUX_SESSION:0.3" -T "Logs"
117
+
118
+ echo ""
119
+ echo -e "${GREEN}✓ Geniclaw started (tmux)${NC}"
120
+ echo ""
121
+ echo -e "${YELLOW}🤖 Start a chat with your bot to begin!${NC}"
122
+ echo ""
123
+ echo -e "${BLUE}Tmux Session Layout:${NC}"
124
+ echo " ┌──────────────┬──────────────┐"
125
+ echo " │ Telegram │ Queue │"
126
+ echo " ├──────────────┼──────────────┤"
127
+ echo " │ Heartbeat │ Logs │"
128
+ echo " └──────────────┴──────────────┘"
129
+ echo ""
130
+ log "Daemon started with tmux"
131
+
132
+ elif command -v pm2 &> /dev/null; then
133
+ echo -e "${YELLOW}Tmux not found. Using PM2...${NC}"
134
+ export GENICLAW_WORK_DIR
135
+ export GENICLAW_ENV_FILE
136
+ cd "$SCRIPT_DIR"
137
+ pm2 start ecosystem.config.js
138
+ pm2 save
139
+
140
+ echo ""
141
+ echo -e "${GREEN}✓ Geniclaw started (PM2)${NC}"
142
+ echo ""
143
+ echo -e "${YELLOW}🤖 Start a chat with your bot to begin!${NC}"
144
+ log "Daemon started with PM2"
145
+ else
146
+ echo -e "${RED}Error: Neither tmux nor pm2 found.${NC}"
147
+ echo "Please install tmux (brew install tmux) or pm2 (npm install -g pm2)"
148
+ exit 1
149
+ fi
150
+ }
151
+
152
+ # Stop daemon
153
+ stop_daemon() {
154
+ log "Stopping Geniclaw..."
155
+
156
+ if session_exists; then
157
+ tmux kill-session -t "$TMUX_SESSION"
158
+ echo -e "${GREEN}✓ Geniclaw stopped (tmux)${NC}"
159
+ elif command -v pm2 &> /dev/null && pm2 describe geniclaw-queue &> /dev/null; then
160
+ cd "$SCRIPT_DIR"
161
+ pm2 stop ecosystem.config.js
162
+ pm2 delete ecosystem.config.js
163
+ pm2 save
164
+ echo -e "${GREEN}✓ Geniclaw stopped (PM2)${NC}"
165
+ else
166
+ echo -e "${YELLOW}Geniclaw not running (tmux/pm2)${NC}"
167
+ fi
168
+
169
+ # Kill any remaining processes (fallback)
170
+ pkill -f "dist/telegram-client.js" || true
171
+ pkill -f "dist/queue-processor.js" || true
172
+ pkill -f "heartbeat-cron.sh" || true
173
+
174
+ log "Daemon stopped"
175
+ }
176
+
177
+ # Send message to Gemini via Queue
178
+ send_message() {
179
+ local message="$1"
180
+ local source="${2:-cli}"
181
+
182
+ check_env
183
+
184
+ log "[$source] Queuing: ${message:0:50}..."
185
+
186
+ if ! pgrep -f "dist/queue-processor.js" > /dev/null; then
187
+ echo -e "${YELLOW}Warning: Queue processor is not running. Message will be queued but not processed until started.${NC}"
188
+ echo "Start with: ./geniclaw.sh start"
189
+ fi
190
+
191
+ # Create message file
192
+ # Use Node.js for cross-platform millisecond timestamp (macOS date doesn't support %N)
193
+ if command -v node &> /dev/null; then
194
+ TIMESTAMP=$(node -e 'console.log(Date.now())')
195
+ else
196
+ # Fallback to seconds if node missing (unlikely since this is a node app)
197
+ TIMESTAMP=$(date +%s)000
198
+ fi
199
+
200
+ MESSAGE_ID="cli_${TIMESTAMP}"
201
+ QUEUE_INCOMING="$GENICLAW_WORK_DIR/queue/incoming"
202
+ QUEUE_OUTGOING="$GENICLAW_WORK_DIR/queue/outgoing"
203
+
204
+ mkdir -p "$QUEUE_INCOMING" "$QUEUE_OUTGOING"
205
+
206
+ cat > "$QUEUE_INCOMING/${source}_${MESSAGE_ID}.json" <<EOF
207
+ {
208
+ "channel": "${source}",
209
+ "sender": "User",
210
+ "message": "${message}",
211
+ "timestamp": ${TIMESTAMP},
212
+ "messageId": "${MESSAGE_ID}"
213
+ }
214
+ EOF
215
+
216
+ echo -e "${BLUE}Message queued. Waiting for response...${NC}"
217
+
218
+ # Wait for response (poll outgoing queue)
219
+ for i in {1..60}; do
220
+ RESPONSE_FILE=$(ls "$QUEUE_OUTGOING"/${source}_${MESSAGE_ID}*.json 2>/dev/null | head -n 1)
221
+
222
+ if [ -n "$RESPONSE_FILE" ]; then
223
+ if command -v jq >/dev/null 2>&1; then
224
+ RESPONSE=$(jq -r '.message' "$RESPONSE_FILE")
225
+ else
226
+ RESPONSE=$(python3 -c "import sys, json; print(json.load(open('$RESPONSE_FILE'))['message'])" 2>/dev/null)
227
+ fi
228
+
229
+ echo ""
230
+ echo -e "${GREEN}Gemini:${NC}"
231
+ echo "$RESPONSE"
232
+ echo ""
233
+
234
+ rm "$RESPONSE_FILE"
235
+ log "[$source] Response received (${#RESPONSE} chars)"
236
+ return 0
237
+ fi
238
+
239
+ sleep 1
240
+ echo -n "."
241
+ done
242
+
243
+ echo ""
244
+ echo -e "${RED}Timeout waiting for response.${NC}"
245
+ log "[$source] Timeout waiting for response"
246
+ return 1
247
+ }
248
+
249
+ # Status
250
+ status_daemon() {
251
+ echo -e "${BLUE}Geniclaw Status${NC}"
252
+ echo "==============="
253
+ echo ""
254
+
255
+ if session_exists; then
256
+ echo -e "Tmux Session: ${GREEN}Running${NC}"
257
+ echo " Attach: tmux attach -t $TMUX_SESSION"
258
+ elif command -v pm2 &> /dev/null && pm2 describe geniclaw-queue &> /dev/null; then
259
+ echo -e "PM2 Status: ${GREEN}Running${NC}"
260
+ echo " Monitor: pm2 monit"
261
+ echo " Logs: pm2 logs geniclaw-telegram"
262
+ else
263
+ echo -e "Process Manager: ${RED}Not Running (tmux/pm2)${NC}"
264
+ echo " Start: geniclaw start"
265
+ fi
266
+
267
+ echo ""
268
+
269
+ if pgrep -f "dist/telegram-client.js" > /dev/null; then
270
+ echo -e "Telegram Client: ${GREEN}Running${NC}"
271
+ else
272
+ echo -e "Telegram Client: ${RED}Not Running${NC}"
273
+ fi
274
+
275
+ if pgrep -f "dist/queue-processor.js" > /dev/null; then
276
+ echo -e "Queue Processor: ${GREEN}Running${NC}"
277
+ else
278
+ echo -e "Queue Processor: ${RED}Not Running${NC}"
279
+ fi
280
+
281
+ if pgrep -f "heartbeat-cron.sh" > /dev/null; then
282
+ echo -e "Heartbeat: ${GREEN}Running${NC}"
283
+ else
284
+ echo -e "Heartbeat: ${RED}Not Running${NC}"
285
+ fi
286
+
287
+ echo ""
288
+ echo "Recent Activity:"
289
+ echo "───────────────"
290
+ tail -n 5 "$LOG_DIR/telegram.log" 2>/dev/null || echo " No Telegram activity yet"
291
+
292
+ echo ""
293
+ echo "Recent Heartbeats:"
294
+ echo "─────────────────"
295
+ tail -n 3 "$LOG_DIR/heartbeat.log" 2>/dev/null || echo " No heartbeat logs logs yet"
296
+
297
+ echo ""
298
+ echo "Logs:"
299
+ echo " Telegram: tail -f $LOG_DIR/telegram.log"
300
+ echo " Heartbeat: tail -f $LOG_DIR/heartbeat.log"
301
+ echo " Daemon: tail -f $LOG_DIR/daemon.log"
302
+ echo " Queue: tail -f $LOG_DIR/queue.log"
303
+ }
304
+
305
+ # View logs
306
+ logs() {
307
+ case "${1:-telegram}" in
308
+ telegram|tg)
309
+ tail -f "$LOG_DIR/telegram.log"
310
+ ;;
311
+ heartbeat|hb)
312
+ tail -f "$LOG_DIR/heartbeat.log"
313
+ ;;
314
+ daemon|all)
315
+ tail -f "$LOG_DIR/daemon.log"
316
+ ;;
317
+ queue)
318
+ tail -f "$LOG_DIR/queue.log"
319
+ ;;
320
+ *)
321
+ echo "Usage: $0 logs [telegram|heartbeat|daemon|queue]"
322
+ ;;
323
+ esac
324
+ }
325
+
326
+ case "${1:-}" in
327
+ start)
328
+ start_daemon
329
+ ;;
330
+ stop)
331
+ stop_daemon
332
+ ;;
333
+ restart)
334
+ stop_daemon
335
+ sleep 2
336
+ start_daemon
337
+ ;;
338
+ status)
339
+ status_daemon
340
+ ;;
341
+ send)
342
+ if [ -z "$2" ]; then
343
+ echo "Usage: $0 send <message>"
344
+ exit 1
345
+ fi
346
+ send_message "$2" "cli"
347
+ ;;
348
+ logs)
349
+ logs "$2"
350
+ ;;
351
+ reset)
352
+ echo -e "${YELLOW}🔄 Resetting conversation...${NC}"
353
+ touch "$GENICLAW_WORK_DIR/reset_flag"
354
+ echo -e "${GREEN}✓ Reset flag set${NC}"
355
+ echo ""
356
+ echo "The next message will start a fresh conversation history."
357
+ ;;
358
+ setup)
359
+ echo -e "${BLUE}Welcome to Geniclaw Setup!${NC}"
360
+ npm run build >/dev/null 2>&1
361
+ node dist/setup.js
362
+ echo ""
363
+ echo -e "${GREEN}✓ Setup complete!${NC}"
364
+ echo "Run 'geniclaw start' to launch the daemon."
365
+ ;;
366
+
367
+ attach)
368
+ tmux attach -t "$TMUX_SESSION"
369
+ ;;
370
+ *)
371
+ echo -e "${BLUE}Geniclaw - Google Gemini + Telegram${NC}"
372
+ echo ""
373
+ echo "Usage: $0 {start|stop|restart|status|send|logs|reset|attach}"
374
+ echo ""
375
+ echo "Commands:"
376
+ echo " start Start Geniclaw"
377
+ echo " stop Stop all processes"
378
+ echo " restart Restart Geniclaw"
379
+ echo " status Show current status"
380
+ echo " setup Run interactive setup (API keys)"
381
+ echo " send <msg> Send message to Gemini manually via queue"
382
+ echo " logs [type] View logs (telegram|heartbeat|daemon|queue)"
383
+ echo " reset Reset conversation history"
384
+ echo " attach Attach to tmux session"
385
+ echo ""
386
+ exit 1
387
+ ;;
388
+ esac
@@ -0,0 +1,71 @@
1
+ #!/bin/bash
2
+ # Heartbeat - Periodically prompts Claude via queue system
3
+
4
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5
+
6
+ # Ensure GENICLAW_WORK_DIR is set (fallback for cron/manual run)
7
+ if [ -z "$GENICLAW_WORK_DIR" ]; then
8
+ if [ -f "$SCRIPT_DIR/.env" ]; then
9
+ GENICLAW_WORK_DIR="$SCRIPT_DIR/.geniclaw"
10
+ else
11
+ GENICLAW_WORK_DIR="$HOME/.geniclaw"
12
+ fi
13
+ fi
14
+
15
+ HEARTBEAT_FILE="$GENICLAW_WORK_DIR/heartbeat.md"
16
+ LOG_FILE="$GENICLAW_WORK_DIR/logs/heartbeat.log"
17
+ QUEUE_INCOMING="$GENICLAW_WORK_DIR/queue/incoming"
18
+ QUEUE_OUTGOING="$GENICLAW_WORK_DIR/queue/outgoing"
19
+ INTERVAL=300 # 5 minutes
20
+
21
+ mkdir -p "$QUEUE_INCOMING" "$QUEUE_OUTGOING" "$(dirname "$LOG_FILE")"
22
+
23
+ log() {
24
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOG_FILE"
25
+ }
26
+
27
+ log "Heartbeat started (interval: ${INTERVAL}s)"
28
+
29
+ while true; do
30
+ sleep "$INTERVAL"
31
+
32
+ log "Heartbeat check..."
33
+
34
+ # Read heartbeat prompt
35
+ if [ -f "$HEARTBEAT_FILE" ]; then
36
+ PROMPT=$(cat "$HEARTBEAT_FILE")
37
+ else
38
+ PROMPT="Quick status check: Any pending tasks? Keep response brief."
39
+ fi
40
+
41
+ # Generate unique message ID
42
+ MESSAGE_ID="heartbeat_$(date +%s)_$$"
43
+
44
+ # Write to queue (like any other channel)
45
+ cat > "$QUEUE_INCOMING/${MESSAGE_ID}.json" << EOF
46
+ {
47
+ "channel": "heartbeat",
48
+ "sender": "System",
49
+ "senderId": "heartbeat",
50
+ "message": "$PROMPT",
51
+ "timestamp": $(date +%s)000,
52
+ "messageId": "$MESSAGE_ID"
53
+ }
54
+ EOF
55
+
56
+ log "✓ Heartbeat queued: $MESSAGE_ID"
57
+
58
+ # Optional: wait a bit and check if response was created
59
+ sleep 10
60
+
61
+ # Check for response (optional logging)
62
+ RESPONSE_FILE="$QUEUE_OUTGOING/${MESSAGE_ID}.json"
63
+ if [ -f "$RESPONSE_FILE" ]; then
64
+ RESPONSE=$(cat "$RESPONSE_FILE" | jq -r '.message' 2>/dev/null || echo "")
65
+ if [ -n "$RESPONSE" ]; then
66
+ log "Response: ${RESPONSE:0:100}..."
67
+ # Clean up response file (we don't need to send it anywhere)
68
+ rm "$RESPONSE_FILE"
69
+ fi
70
+ fi
71
+ done
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "@thelapyae/geniclaw",
3
+ "version": "1.0.0",
4
+ "description": "Geniclaw - Gemini + Telegram CLI Assistant",
5
+ "main": "dist/telegram-client.js",
6
+ "bin": {
7
+ "geniclaw": "./bin/geniclaw.js"
8
+ },
9
+ "files": [
10
+ "dist",
11
+ "bin",
12
+ "geniclaw.sh",
13
+ "heartbeat-cron.sh",
14
+ "README.md",
15
+ "LICENSE"
16
+ ],
17
+ "scripts": {
18
+ "build": "tsc",
19
+ "prepublishOnly": "npm run build",
20
+ "telegram": "node dist/telegram-client.js",
21
+ "queue": "node dist/queue-processor.js"
22
+ },
23
+ "dependencies": {
24
+ "@google/generative-ai": "^0.24.1",
25
+ "commander": "^14.0.3",
26
+ "dotenv": "^16.3.1",
27
+ "google-auth-library": "^10.5.0",
28
+ "inquirer": "^13.2.2",
29
+ "telegraf": "^4.16.3"
30
+ },
31
+ "devDependencies": {
32
+ "@types/dotenv": "^6.1.1",
33
+ "@types/express": "^5.0.6",
34
+ "@types/node": "^25.2.2",
35
+ "@types/qrcode-terminal": "^0.12.2",
36
+ "typescript": "^5.9.3"
37
+ }
38
+ }