@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/README.md +127 -0
- package/bin/geniclaw.js +323 -0
- package/dist/config-manager.d.ts +20 -0
- package/dist/config-manager.d.ts.map +1 -0
- package/dist/config-manager.js +52 -0
- package/dist/config-manager.js.map +1 -0
- package/dist/migrate-config.d.ts +2 -0
- package/dist/migrate-config.d.ts.map +1 -0
- package/dist/migrate-config.js +57 -0
- package/dist/migrate-config.js.map +1 -0
- package/dist/queue-processor.d.ts +7 -0
- package/dist/queue-processor.d.ts.map +1 -0
- package/dist/queue-processor.js +311 -0
- package/dist/queue-processor.js.map +1 -0
- package/dist/session-manager.d.ts +15 -0
- package/dist/session-manager.d.ts.map +1 -0
- package/dist/session-manager.js +84 -0
- package/dist/session-manager.js.map +1 -0
- package/dist/setup.d.ts +2 -0
- package/dist/setup.d.ts.map +1 -0
- package/dist/setup.js +67 -0
- package/dist/setup.js.map +1 -0
- package/dist/telegram-client.d.ts +7 -0
- package/dist/telegram-client.d.ts.map +1 -0
- package/dist/telegram-client.js +229 -0
- package/dist/telegram-client.js.map +1 -0
- package/dist/whatsapp-client.d.ts +7 -0
- package/dist/whatsapp-client.d.ts.map +1 -0
- package/dist/whatsapp-client.js +173 -0
- package/dist/whatsapp-client.js.map +1 -0
- package/geniclaw.sh +388 -0
- package/heartbeat-cron.sh +71 -0
- package/package.json +38 -0
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
|
+
}
|