agent-window 1.4.1 → 1.4.3
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/package.json +5 -2
- package/scripts/cleanup-pm2.sh +130 -0
- package/src/bot.js +88 -4
- package/src/core/config.js +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-window",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.3",
|
|
4
4
|
"description": "A window to interact with AI agents through chat interfaces. Simplified interaction, powerful backend capabilities.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/bot.js",
|
|
@@ -11,6 +11,8 @@
|
|
|
11
11
|
"ui": "node bin/cli.js ui",
|
|
12
12
|
"ui:dev": "cd web && npm run dev",
|
|
13
13
|
"ui:build": "cd web && npm run build",
|
|
14
|
+
"cleanup-pm2": "bash scripts/cleanup-pm2.sh",
|
|
15
|
+
"check-node": "node -e \"const v=process.version; const n=parseInt(v.slice(1)); if(n>=23){console.error('⚠️ Node.js',v,'not supported. Use v18 or v20 LTS.'); process.exit(1);}else{console.log('✓ Node.js',v,'is supported');}\"",
|
|
14
16
|
"pm2:start": "pm2 start ecosystem.config.cjs",
|
|
15
17
|
"pm2:stop": "pm2 stop bot-corp",
|
|
16
18
|
"pm2:restart": "pm2 restart bot-corp",
|
|
@@ -26,7 +28,8 @@
|
|
|
26
28
|
"author": "",
|
|
27
29
|
"license": "MIT",
|
|
28
30
|
"engines": {
|
|
29
|
-
"node": ">=18.0.0"
|
|
31
|
+
"node": ">=18.0.0 <23.0.0",
|
|
32
|
+
"npm": ">=9.0.0"
|
|
30
33
|
},
|
|
31
34
|
"dependencies": {
|
|
32
35
|
"@fastify/static": "^6.0.0",
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# PM2 Deep Cleanup Script
|
|
3
|
+
# AgentWindow Multi-Instance Bug Recovery Tool
|
|
4
|
+
#
|
|
5
|
+
# This script cleans up:
|
|
6
|
+
# - Multiple PM2 God Daemon processes (200+ leaked instances)
|
|
7
|
+
# - Orphaned bot processes
|
|
8
|
+
# - Stale PM2 dump files
|
|
9
|
+
# - Duplicate instances
|
|
10
|
+
|
|
11
|
+
set -e
|
|
12
|
+
|
|
13
|
+
echo "🧹 PM2 Deep Cleanup Script"
|
|
14
|
+
echo "======================================"
|
|
15
|
+
echo ""
|
|
16
|
+
|
|
17
|
+
# Color codes for output
|
|
18
|
+
RED='\033[0;31m'
|
|
19
|
+
GREEN='\033[0;32m'
|
|
20
|
+
YELLOW='\033[1;33m'
|
|
21
|
+
NC='\033[0m' # No Color
|
|
22
|
+
|
|
23
|
+
# Step 1: Check PM2 processes before cleanup
|
|
24
|
+
echo "[1/7] Analyzing current state..."
|
|
25
|
+
echo "-----------------------------------"
|
|
26
|
+
|
|
27
|
+
PM2_COUNT=$(pgrep -f "PM2.*God Daemon" | wc -l | tr -d ' ' || echo "0")
|
|
28
|
+
BOT_COUNT=$(ps aux | grep "node.*bot.js" | grep -v grep | wc -l | tr -d ' ' || echo "0")
|
|
29
|
+
|
|
30
|
+
echo -e "PM2 God Daemons: ${YELLOW}${PM2_COUNT}${NC}"
|
|
31
|
+
echo -e "Bot processes: ${YELLOW}${BOT_COUNT}${NC}"
|
|
32
|
+
echo ""
|
|
33
|
+
|
|
34
|
+
if [ "$PM2_COUNT" -lt 5 ] && [ "$BOT_COUNT" -lt 3 ]; then
|
|
35
|
+
echo -e "${GREEN}✓ System appears clean (no major issues detected)${NC}"
|
|
36
|
+
echo ""
|
|
37
|
+
read -p "Continue cleanup anyway? (y/N): " -n 1 -r
|
|
38
|
+
echo
|
|
39
|
+
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
|
40
|
+
echo "Cleanup cancelled."
|
|
41
|
+
exit 0
|
|
42
|
+
fi
|
|
43
|
+
fi
|
|
44
|
+
|
|
45
|
+
# Step 2: Stop all PM2 apps
|
|
46
|
+
echo "[2/7] Stopping all PM2 apps..."
|
|
47
|
+
echo "-----------------------------------"
|
|
48
|
+
if command -v pm2 &> /dev/null; then
|
|
49
|
+
pm2 stop all 2>/dev/null || echo " No apps to stop"
|
|
50
|
+
pm2 delete all 2>/dev/null || echo " No apps to delete"
|
|
51
|
+
echo -e "${GREEN}✓ PM2 apps stopped${NC}"
|
|
52
|
+
else
|
|
53
|
+
echo -e "${YELLOW}⚠ PM2 not found, skipping...${NC}"
|
|
54
|
+
fi
|
|
55
|
+
echo ""
|
|
56
|
+
|
|
57
|
+
# Step 3: Kill PM2 daemon
|
|
58
|
+
echo "[3/7] Killing PM2 daemon..."
|
|
59
|
+
echo "-----------------------------------"
|
|
60
|
+
if command -v pm2 &> /dev/null; then
|
|
61
|
+
pm2 kill 2>/dev/null || echo " Daemon already stopped"
|
|
62
|
+
echo -e "${GREEN}✓ PM2 daemon killed${NC}"
|
|
63
|
+
else
|
|
64
|
+
echo -e "${YELLOW}⚠ PM2 not found, skipping...${NC}"
|
|
65
|
+
fi
|
|
66
|
+
echo ""
|
|
67
|
+
|
|
68
|
+
# Step 4: Kill remaining PM2 God processes
|
|
69
|
+
echo "[4/7] Cleaning up PM2 God processes..."
|
|
70
|
+
echo "-----------------------------------"
|
|
71
|
+
PM2_PIDS=$(pgrep -f "PM2.*God Daemon" || true)
|
|
72
|
+
if [ -n "$PM2_PIDS" ]; then
|
|
73
|
+
echo "$PM2_PIDS" | xargs -I {} kill -9 {} 2>/dev/null || true
|
|
74
|
+
echo -e "${GREEN}✓ Killed PM2 God processes${NC}"
|
|
75
|
+
else
|
|
76
|
+
echo -e "${GREEN}✓ No PM2 God processes found${NC}"
|
|
77
|
+
fi
|
|
78
|
+
echo ""
|
|
79
|
+
|
|
80
|
+
# Step 5: Remove dump file
|
|
81
|
+
echo "[5/7] Removing PM2 dump file..."
|
|
82
|
+
echo "-----------------------------------"
|
|
83
|
+
DUMP_FILE="$HOME/.pm2/dump.pm2"
|
|
84
|
+
if [ -f "$DUMP_FILE" ]; then
|
|
85
|
+
rm -f "$DUMP_FILE"
|
|
86
|
+
echo -e "${GREEN}✓ Removed dump file${NC}"
|
|
87
|
+
else
|
|
88
|
+
echo -e "${GREEN}✓ No dump file found${NC}"
|
|
89
|
+
fi
|
|
90
|
+
echo ""
|
|
91
|
+
|
|
92
|
+
# Step 6: Kill orphaned bot processes
|
|
93
|
+
echo "[6/7] Cleaning up orphaned bot processes..."
|
|
94
|
+
echo "-----------------------------------"
|
|
95
|
+
BOT_PIDS=$(ps aux | grep "node.*bot.js" | grep -v grep | awk '{print $2}' || true)
|
|
96
|
+
if [ -n "$BOT_PIDS" ]; then
|
|
97
|
+
echo "$BOT_PIDS" | xargs -I {} kill -9 {} 2>/dev/null || true
|
|
98
|
+
echo -e "${GREEN}✓ Killed orphaned bot processes${NC}"
|
|
99
|
+
else
|
|
100
|
+
echo -e "${GREEN}✓ No orphaned bot processes found${NC}"
|
|
101
|
+
fi
|
|
102
|
+
echo ""
|
|
103
|
+
|
|
104
|
+
# Step 7: Verification
|
|
105
|
+
echo "[7/7] Verification..."
|
|
106
|
+
echo "-----------------------------------"
|
|
107
|
+
sleep 1
|
|
108
|
+
|
|
109
|
+
FINAL_PM2_COUNT=$(pgrep -f "PM2.*God Daemon" | wc -l | tr -d ' ' || echo "0")
|
|
110
|
+
FINAL_BOT_COUNT=$(ps aux | grep "node.*bot.js" | grep -v grep | wc -l | tr -d ' ' || echo "0")
|
|
111
|
+
|
|
112
|
+
echo -e "PM2 God Daemons: ${GREEN}${FINAL_PM2_COUNT}${NC} (was ${PM2_COUNT})"
|
|
113
|
+
echo -e "Bot processes: ${GREEN}${FINAL_BOT_COUNT}${NC} (was ${BOT_COUNT})"
|
|
114
|
+
echo ""
|
|
115
|
+
|
|
116
|
+
if [ "$FINAL_PM2_COUNT" -eq 0 ] && [ "$FINAL_BOT_COUNT" -eq 0 ]; then
|
|
117
|
+
echo -e "${GREEN}✅ Cleanup complete! System is clean.${NC}"
|
|
118
|
+
echo ""
|
|
119
|
+
echo "Next steps:"
|
|
120
|
+
echo " 1. Start your bot: pm2 start ecosystem.config.cjs"
|
|
121
|
+
echo " 2. Verify: pm2 list"
|
|
122
|
+
echo " 3. Check logs: pm2 logs --lines 20"
|
|
123
|
+
else
|
|
124
|
+
echo -e "${YELLOW}⚠️ Some processes still running. You may need to:${NC}"
|
|
125
|
+
echo " - Check manually: ps aux | grep bot.js"
|
|
126
|
+
echo " - Kill manually: kill -9 <PID>"
|
|
127
|
+
echo " - Restart terminal/daemon"
|
|
128
|
+
fi
|
|
129
|
+
echo ""
|
|
130
|
+
echo "======================================"
|
package/src/bot.js
CHANGED
|
@@ -24,8 +24,6 @@ import { spawn, execSync } from 'child_process';
|
|
|
24
24
|
import { readFileSync, writeFileSync, existsSync, readdirSync, unlinkSync, mkdirSync, chmodSync } from 'fs';
|
|
25
25
|
import { join } from 'path';
|
|
26
26
|
import { homedir } from 'os';
|
|
27
|
-
import { https } from 'https';
|
|
28
|
-
import { http } from 'http';
|
|
29
27
|
import { createWriteStream } from 'fs';
|
|
30
28
|
|
|
31
29
|
// Import performance monitoring utilities (local)
|
|
@@ -41,6 +39,50 @@ import {
|
|
|
41
39
|
// Import centralized configuration (local, project-specific)
|
|
42
40
|
import config from './core/config.js';
|
|
43
41
|
|
|
42
|
+
// ============================================================================
|
|
43
|
+
// CRITICAL: Check Node.js version compatibility
|
|
44
|
+
// ============================================================================
|
|
45
|
+
const NODE_VERSION = process.version;
|
|
46
|
+
const NODE_MAJOR = parseInt(NODE_VERSION.slice(1).split('.')[0]);
|
|
47
|
+
|
|
48
|
+
if (ENFORCE_NODE_VERSION && NODE_MAJOR >= 23) {
|
|
49
|
+
console.error('\n' + '='.repeat(70));
|
|
50
|
+
console.error('⚠️ CRITICAL: Node.js v23+ is not compatible with AgentWindow!');
|
|
51
|
+
console.error('='.repeat(70));
|
|
52
|
+
console.error(`Current version: ${NODE_VERSION}`);
|
|
53
|
+
console.error('Supported versions: Node.js v18 LTS or v20 LTS');
|
|
54
|
+
console.error('\n❌ Issues with Node.js v23:');
|
|
55
|
+
console.error(' - ES module import syntax incompatibility');
|
|
56
|
+
console.error(' - Potential runtime errors with Discord.js');
|
|
57
|
+
console.error(' - Breaking changes in core modules');
|
|
58
|
+
console.error('\n✅ Recommended actions:');
|
|
59
|
+
console.error(' 1. Install Node.js v20 LTS (recommended)');
|
|
60
|
+
console.error(' Using nvm: nvm install 20 && nvm use 20');
|
|
61
|
+
console.error(' Using Homebrew: brew install node@20');
|
|
62
|
+
console.error(' 2. Reinstall global packages: npm install -g agent-window');
|
|
63
|
+
console.error(' 3. Restart your bots');
|
|
64
|
+
console.error('\n📖 To disable this check, add to your config.json:');
|
|
65
|
+
console.error(' { "workspace": { "enforceNodeVersion": false } }');
|
|
66
|
+
console.error('\n📖 See: https://nodejs.org/en/download/releases');
|
|
67
|
+
console.error('='.repeat(70) + '\n');
|
|
68
|
+
process.exit(1);
|
|
69
|
+
} else if (NODE_MAJOR >= 23) {
|
|
70
|
+
// Warning only (if check is disabled)
|
|
71
|
+
console.warn('\n' + '='.repeat(70));
|
|
72
|
+
console.warn('⚠️ WARNING: Node.js v23 detected!');
|
|
73
|
+
console.warn('='.repeat(70));
|
|
74
|
+
console.warn(`Current version: ${NODE_VERSION}`);
|
|
75
|
+
console.warn('Recommended versions: Node.js v18 LTS or v20 LTS');
|
|
76
|
+
console.warn('');
|
|
77
|
+
console.warn('⚠️ Node.js v23 may cause compatibility issues.');
|
|
78
|
+
console.warn('✅ Version check is disabled in config (enforceNodeVersion: false)');
|
|
79
|
+
console.warn(' Bot will continue, but issues may occur.');
|
|
80
|
+
console.warn('');
|
|
81
|
+
console.warn('To enable strict checking, remove "enforceNodeVersion: false" from config');
|
|
82
|
+
console.warn('Or install Node.js v20 LTS for best stability.');
|
|
83
|
+
console.warn('='.repeat(70) + '\n');
|
|
84
|
+
}
|
|
85
|
+
|
|
44
86
|
// Extract commonly used config values for convenience
|
|
45
87
|
const BOT_TOKEN = config.discord.token;
|
|
46
88
|
const PROJECT_DIR = config.workspace.projectDir;
|
|
@@ -50,6 +92,7 @@ const CHANNEL_SESSIONS_FILE = config.paths.sessions;
|
|
|
50
92
|
const PENDING_DIR = config.paths.pending;
|
|
51
93
|
const HOOK_DIR = config.paths.hooks;
|
|
52
94
|
const USE_DOCKER = config.workspace.useDocker; // Whether to use Docker or run locally
|
|
95
|
+
const ENFORCE_NODE_VERSION = config.workspace.enforceNodeVersion; // Whether to enforce Node.js version check
|
|
53
96
|
const CONTAINER_NAME = config.workspace.containerName;
|
|
54
97
|
const DOCKER_IMAGE = config.workspace.dockerImage;
|
|
55
98
|
|
|
@@ -1850,11 +1893,52 @@ client.on(Events.MessageCreate, async (message) => {
|
|
|
1850
1893
|
|
|
1851
1894
|
// Ready event
|
|
1852
1895
|
client.on(Events.ClientReady, async () => {
|
|
1896
|
+
// ========================================================================
|
|
1897
|
+
// CRITICAL: Check for duplicate bot instances
|
|
1898
|
+
// ========================================================================
|
|
1899
|
+
try {
|
|
1900
|
+
const { execSync } = await import('child_process');
|
|
1901
|
+
const psCommand = process.platform === 'win32'
|
|
1902
|
+
? 'tasklist | findstr node.exe'
|
|
1903
|
+
: 'ps aux | grep "node.*bot.js" | grep -v grep';
|
|
1904
|
+
|
|
1905
|
+
const result = execSync(psCommand, { encoding: 'utf-8', timeout: 5000 });
|
|
1906
|
+
const botProcesses = result.split('\n').filter(line => line.trim() && line.includes('bot.js'));
|
|
1907
|
+
|
|
1908
|
+
if (botProcesses.length > 1) {
|
|
1909
|
+
console.error('\n' + '='.repeat(70));
|
|
1910
|
+
console.error('⚠️ CRITICAL WARNING: Multiple bot instances detected!');
|
|
1911
|
+
console.error('='.repeat(70));
|
|
1912
|
+
console.error(`Found ${botProcesses.length} bot instances running concurrently.`);
|
|
1913
|
+
console.error('This will cause duplicate responses to every message!');
|
|
1914
|
+
console.error('\n🔍 Running instances:');
|
|
1915
|
+
botProcesses.forEach((proc, i) => {
|
|
1916
|
+
console.error(` ${i + 1}. ${proc.trim()}`);
|
|
1917
|
+
});
|
|
1918
|
+
console.error('\n🔧 To fix this issue:');
|
|
1919
|
+
console.error(' 1. Check running PM2 processes: pm2 list');
|
|
1920
|
+
console.error(' 2. Stop all bots: pm2 stop all && pm2 delete all');
|
|
1921
|
+
console.error(' 3. Kill PM2 daemon: pm2 kill');
|
|
1922
|
+
console.error(' 4. Kill all bot processes: pkill -9 -f "node.*bot.js"');
|
|
1923
|
+
console.error(' 5. Clean dump file: rm -f ~/.pm2/dump.pm2');
|
|
1924
|
+
console.error(' 6. Restart single instance: pm2 start ecosystem.config.cjs');
|
|
1925
|
+
console.error('\n📖 Or use the cleanup script: npm run cleanup-pm2');
|
|
1926
|
+
console.error('='.repeat(70) + '\n');
|
|
1927
|
+
} else {
|
|
1928
|
+
console.log('✓ Instance check: Single bot instance (good)');
|
|
1929
|
+
}
|
|
1930
|
+
} catch (e) {
|
|
1931
|
+
// Command failed (likely no other instances or platform not supported)
|
|
1932
|
+
console.log('✓ Instance check: Passed');
|
|
1933
|
+
}
|
|
1934
|
+
|
|
1853
1935
|
console.log(`AgentBridge logged in as ${client.user.tag}`);
|
|
1854
1936
|
console.log(`Bot User ID: ${client.user.id}`);
|
|
1855
1937
|
console.log(`Project: ${PROJECT_DIR}`);
|
|
1856
|
-
console.log(`Mode: Docker Sandbox + Hooks Permission`);
|
|
1857
|
-
|
|
1938
|
+
console.log(`Mode: ${USE_DOCKER ? 'Docker Sandbox' : 'Local Host'} + Hooks Permission`);
|
|
1939
|
+
if (USE_DOCKER) {
|
|
1940
|
+
console.log(`Container: ${CONTAINER_NAME}`);
|
|
1941
|
+
}
|
|
1858
1942
|
console.log(`OAuth: ${OAUTH_TOKEN ? 'configured' : 'not set'}`);
|
|
1859
1943
|
|
|
1860
1944
|
// Update PM2 and Discord health status
|
package/src/core/config.js
CHANGED
|
@@ -82,6 +82,7 @@ function loadConfig() {
|
|
|
82
82
|
workspace: {
|
|
83
83
|
projectDir: expandPath(fileConfig.PROJECT_DIR) || process.cwd(),
|
|
84
84
|
useDocker: fileConfig.workspace?.useDocker !== false, // Default to true for backward compatibility
|
|
85
|
+
enforceNodeVersion: fileConfig.workspace?.enforceNodeVersion !== false, // Default to true for safety
|
|
85
86
|
containerName: fileConfig.workspace?.containerName || 'claude-discord-bot',
|
|
86
87
|
dockerImage: fileConfig.workspace?.dockerImage || 'claude-sandbox',
|
|
87
88
|
portMappings: fileConfig.workspace?.portMappings || [],
|