agent-window 1.4.0 → 1.4.2
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 +162 -42
- 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.2",
|
|
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,33 @@ 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 (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📖 See: https://nodejs.org/en/download/releases');
|
|
65
|
+
console.error('='.repeat(70) + '\n');
|
|
66
|
+
process.exit(1);
|
|
67
|
+
}
|
|
68
|
+
|
|
44
69
|
// Extract commonly used config values for convenience
|
|
45
70
|
const BOT_TOKEN = config.discord.token;
|
|
46
71
|
const PROJECT_DIR = config.workspace.projectDir;
|
|
@@ -49,6 +74,7 @@ const ALLOWED_CHANNELS = config.discord.allowedChannels;
|
|
|
49
74
|
const CHANNEL_SESSIONS_FILE = config.paths.sessions;
|
|
50
75
|
const PENDING_DIR = config.paths.pending;
|
|
51
76
|
const HOOK_DIR = config.paths.hooks;
|
|
77
|
+
const USE_DOCKER = config.workspace.useDocker; // Whether to use Docker or run locally
|
|
52
78
|
const CONTAINER_NAME = config.workspace.containerName;
|
|
53
79
|
const DOCKER_IMAGE = config.workspace.dockerImage;
|
|
54
80
|
|
|
@@ -118,20 +144,37 @@ function updateHealthStatus(component, status) {
|
|
|
118
144
|
|
|
119
145
|
// Get overall health status
|
|
120
146
|
function getOverallHealth() {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
147
|
+
// Build health check string
|
|
148
|
+
const checks = [healthStatus.pm2 ? '✓' : '✗', healthStatus.discord ? '✓' : '✗'];
|
|
149
|
+
|
|
150
|
+
// Only check Docker if enabled
|
|
151
|
+
if (USE_DOCKER) {
|
|
152
|
+
checks.push(healthStatus.docker ? '✓' : '✗');
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const healthString = checks.join(' ');
|
|
156
|
+
|
|
157
|
+
// Determine overall status
|
|
158
|
+
if (USE_DOCKER) {
|
|
159
|
+
// Docker mode: all components must be healthy
|
|
160
|
+
if (healthStatus.pm2 && healthStatus.discord && healthStatus.docker) {
|
|
161
|
+
return 'healthy';
|
|
162
|
+
} else if (healthStatus.pm2 && healthStatus.discord) {
|
|
163
|
+
return 'degraded'; // Running but Docker failed
|
|
164
|
+
} else if (healthStatus.pm2) {
|
|
165
|
+
return 'unhealthy'; // PM2 running but Discord disconnected
|
|
166
|
+
} else {
|
|
167
|
+
return 'failed';
|
|
168
|
+
}
|
|
133
169
|
} else {
|
|
134
|
-
|
|
170
|
+
// Non-Docker mode: only PM2 and Discord required
|
|
171
|
+
if (healthStatus.pm2 && healthStatus.discord) {
|
|
172
|
+
return 'healthy';
|
|
173
|
+
} else if (healthStatus.pm2) {
|
|
174
|
+
return 'unhealthy'; // PM2 running but Discord disconnected
|
|
175
|
+
} else {
|
|
176
|
+
return 'failed';
|
|
177
|
+
}
|
|
135
178
|
}
|
|
136
179
|
}
|
|
137
180
|
|
|
@@ -346,6 +389,13 @@ function isContainerRunning() {
|
|
|
346
389
|
|
|
347
390
|
// Start or ensure persistent container is running
|
|
348
391
|
function ensureContainer() {
|
|
392
|
+
// Skip Docker operations if not using Docker
|
|
393
|
+
if (!USE_DOCKER) {
|
|
394
|
+
console.log('[Docker] Docker is disabled (useDocker: false). Running in local mode.');
|
|
395
|
+
updateHealthStatus('docker', true); // Mark as "healthy" since we don't need Docker
|
|
396
|
+
return true;
|
|
397
|
+
}
|
|
398
|
+
|
|
349
399
|
// Log the paths being used
|
|
350
400
|
console.log('[Docker] PENDING_DIR (host):', PENDING_DIR);
|
|
351
401
|
console.log('[Docker] HOOK_DIR (host):', HOOK_DIR);
|
|
@@ -437,6 +487,11 @@ function ensureContainer() {
|
|
|
437
487
|
|
|
438
488
|
// Stop persistent container (call on bot shutdown)
|
|
439
489
|
function stopContainer() {
|
|
490
|
+
if (!USE_DOCKER) {
|
|
491
|
+
console.log('[Docker] Docker is disabled. No container to stop.');
|
|
492
|
+
return;
|
|
493
|
+
}
|
|
494
|
+
|
|
440
495
|
try {
|
|
441
496
|
execSync(`docker stop ${CONTAINER_NAME} 2>/dev/null`);
|
|
442
497
|
execSync(`docker rm ${CONTAINER_NAME} 2>/dev/null`);
|
|
@@ -1445,21 +1500,42 @@ const commands = {
|
|
|
1445
1500
|
let lastStatusUpdate = 0;
|
|
1446
1501
|
const task = activeTasks.get(channelId);
|
|
1447
1502
|
|
|
1448
|
-
//
|
|
1449
|
-
|
|
1450
|
-
'exec', '-i',
|
|
1451
|
-
CONTAINER_NAME,
|
|
1452
|
-
CLI_COMMAND,
|
|
1453
|
-
...cliArgs
|
|
1454
|
-
];
|
|
1503
|
+
// Execute CLI command (either in Docker or locally based on configuration)
|
|
1504
|
+
let child;
|
|
1455
1505
|
|
|
1456
|
-
|
|
1506
|
+
if (USE_DOCKER) {
|
|
1507
|
+
// Use docker exec to run CLI in the persistent container
|
|
1508
|
+
const dockerArgs = [
|
|
1509
|
+
'exec', '-i',
|
|
1510
|
+
CONTAINER_NAME,
|
|
1511
|
+
CLI_COMMAND,
|
|
1512
|
+
...cliArgs
|
|
1513
|
+
];
|
|
1457
1514
|
|
|
1458
|
-
|
|
1459
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
1460
|
-
});
|
|
1515
|
+
console.log('[Docker] Executing in container:', CONTAINER_NAME);
|
|
1461
1516
|
|
|
1462
|
-
|
|
1517
|
+
child = spawn('docker', dockerArgs, {
|
|
1518
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
1519
|
+
});
|
|
1520
|
+
|
|
1521
|
+
child.stdin.end();
|
|
1522
|
+
} else {
|
|
1523
|
+
// Run CLI directly on local host
|
|
1524
|
+
console.log('[Local] Executing locally:', CLI_COMMAND, ...cliArgs);
|
|
1525
|
+
|
|
1526
|
+
child = spawn(CLI_COMMAND, cliArgs, {
|
|
1527
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
1528
|
+
cwd: PROJECT_DIR,
|
|
1529
|
+
env: {
|
|
1530
|
+
...process.env,
|
|
1531
|
+
CLAUDE_CODE_OAUTH_TOKEN: OAUTH_TOKEN || '',
|
|
1532
|
+
HOME: process.env.HOME,
|
|
1533
|
+
PATH: process.env.PATH
|
|
1534
|
+
}
|
|
1535
|
+
});
|
|
1536
|
+
|
|
1537
|
+
child.stdin.end();
|
|
1538
|
+
}
|
|
1463
1539
|
|
|
1464
1540
|
// Update status periodically (with stop button)
|
|
1465
1541
|
// forceUpdate=true bypasses throttle for important state changes
|
|
@@ -1762,18 +1838,9 @@ client.on(Events.MessageCreate, async (message) => {
|
|
|
1762
1838
|
return;
|
|
1763
1839
|
}
|
|
1764
1840
|
|
|
1765
|
-
|
|
1766
|
-
// - If ALLOWED_CHANNELS is set, only allow those channels
|
|
1767
|
-
// - If ALLOWED_CHANNELS is missing (should not happen due to config validation), reject ALL non-DM messages
|
|
1768
|
-
if (!isDM) {
|
|
1769
|
-
if (!ALLOWED_CHANNELS || ALLOWED_CHANNELS.length === 0) {
|
|
1770
|
-
// Defensive: If no channels are configured, reject all non-DM messages
|
|
1771
|
-
console.error('[MSG] SECURITY: No ALLOWED_CHANNELS configured. Rejecting message for safety.');
|
|
1772
|
-
console.error('[MSG] Please configure ALLOWED_CHANNELS in your config.json');
|
|
1773
|
-
return;
|
|
1774
|
-
}
|
|
1841
|
+
if (ALLOWED_CHANNELS && !isDM) {
|
|
1775
1842
|
if (!ALLOWED_CHANNELS.includes(message.channel.id)) {
|
|
1776
|
-
console.log(
|
|
1843
|
+
console.log('[MSG] Ignored: channel not allowed');
|
|
1777
1844
|
return;
|
|
1778
1845
|
}
|
|
1779
1846
|
}
|
|
@@ -1808,11 +1875,52 @@ client.on(Events.MessageCreate, async (message) => {
|
|
|
1808
1875
|
|
|
1809
1876
|
// Ready event
|
|
1810
1877
|
client.on(Events.ClientReady, async () => {
|
|
1878
|
+
// ========================================================================
|
|
1879
|
+
// CRITICAL: Check for duplicate bot instances
|
|
1880
|
+
// ========================================================================
|
|
1881
|
+
try {
|
|
1882
|
+
const { execSync } = await import('child_process');
|
|
1883
|
+
const psCommand = process.platform === 'win32'
|
|
1884
|
+
? 'tasklist | findstr node.exe'
|
|
1885
|
+
: 'ps aux | grep "node.*bot.js" | grep -v grep';
|
|
1886
|
+
|
|
1887
|
+
const result = execSync(psCommand, { encoding: 'utf-8', timeout: 5000 });
|
|
1888
|
+
const botProcesses = result.split('\n').filter(line => line.trim() && line.includes('bot.js'));
|
|
1889
|
+
|
|
1890
|
+
if (botProcesses.length > 1) {
|
|
1891
|
+
console.error('\n' + '='.repeat(70));
|
|
1892
|
+
console.error('⚠️ CRITICAL WARNING: Multiple bot instances detected!');
|
|
1893
|
+
console.error('='.repeat(70));
|
|
1894
|
+
console.error(`Found ${botProcesses.length} bot instances running concurrently.`);
|
|
1895
|
+
console.error('This will cause duplicate responses to every message!');
|
|
1896
|
+
console.error('\n🔍 Running instances:');
|
|
1897
|
+
botProcesses.forEach((proc, i) => {
|
|
1898
|
+
console.error(` ${i + 1}. ${proc.trim()}`);
|
|
1899
|
+
});
|
|
1900
|
+
console.error('\n🔧 To fix this issue:');
|
|
1901
|
+
console.error(' 1. Check running PM2 processes: pm2 list');
|
|
1902
|
+
console.error(' 2. Stop all bots: pm2 stop all && pm2 delete all');
|
|
1903
|
+
console.error(' 3. Kill PM2 daemon: pm2 kill');
|
|
1904
|
+
console.error(' 4. Kill all bot processes: pkill -9 -f "node.*bot.js"');
|
|
1905
|
+
console.error(' 5. Clean dump file: rm -f ~/.pm2/dump.pm2');
|
|
1906
|
+
console.error(' 6. Restart single instance: pm2 start ecosystem.config.cjs');
|
|
1907
|
+
console.error('\n📖 Or use the cleanup script: npm run cleanup-pm2');
|
|
1908
|
+
console.error('='.repeat(70) + '\n');
|
|
1909
|
+
} else {
|
|
1910
|
+
console.log('✓ Instance check: Single bot instance (good)');
|
|
1911
|
+
}
|
|
1912
|
+
} catch (e) {
|
|
1913
|
+
// Command failed (likely no other instances or platform not supported)
|
|
1914
|
+
console.log('✓ Instance check: Passed');
|
|
1915
|
+
}
|
|
1916
|
+
|
|
1811
1917
|
console.log(`AgentBridge logged in as ${client.user.tag}`);
|
|
1812
1918
|
console.log(`Bot User ID: ${client.user.id}`);
|
|
1813
1919
|
console.log(`Project: ${PROJECT_DIR}`);
|
|
1814
|
-
console.log(`Mode: Docker Sandbox + Hooks Permission`);
|
|
1815
|
-
|
|
1920
|
+
console.log(`Mode: ${USE_DOCKER ? 'Docker Sandbox' : 'Local Host'} + Hooks Permission`);
|
|
1921
|
+
if (USE_DOCKER) {
|
|
1922
|
+
console.log(`Container: ${CONTAINER_NAME}`);
|
|
1923
|
+
}
|
|
1816
1924
|
console.log(`OAuth: ${OAUTH_TOKEN ? 'configured' : 'not set'}`);
|
|
1817
1925
|
|
|
1818
1926
|
// Update PM2 and Discord health status
|
|
@@ -1853,9 +1961,14 @@ client.on(Events.ClientReady, async () => {
|
|
|
1853
1961
|
// Send startup notification to Discord channels
|
|
1854
1962
|
try {
|
|
1855
1963
|
const uptime = Math.floor((Date.now() - healthStatus.startTime) / 1000);
|
|
1856
|
-
const dockerStatus = healthStatus.docker ? '✓ Connected' : '✗ Not Available';
|
|
1857
1964
|
const overallStatus = overallHealth.toUpperCase();
|
|
1858
1965
|
|
|
1966
|
+
// Build execution mode info
|
|
1967
|
+
const execMode = USE_DOCKER ? 'Docker Container' : 'Local Host';
|
|
1968
|
+
const dockerStatus = USE_DOCKER
|
|
1969
|
+
? (healthStatus.docker ? '✓ Connected' : '✗ Not Available')
|
|
1970
|
+
: '⊘ Disabled (Local Mode)';
|
|
1971
|
+
|
|
1859
1972
|
// Build startup message
|
|
1860
1973
|
const startupMessage = {
|
|
1861
1974
|
embeds: [{
|
|
@@ -1867,6 +1980,11 @@ client.on(Events.ClientReady, async () => {
|
|
|
1867
1980
|
value: overallStatus,
|
|
1868
1981
|
inline: true
|
|
1869
1982
|
},
|
|
1983
|
+
{
|
|
1984
|
+
name: 'Execution Mode',
|
|
1985
|
+
value: execMode,
|
|
1986
|
+
inline: true
|
|
1987
|
+
},
|
|
1870
1988
|
{
|
|
1871
1989
|
name: 'Docker',
|
|
1872
1990
|
value: dockerStatus,
|
|
@@ -1879,7 +1997,9 @@ client.on(Events.ClientReady, async () => {
|
|
|
1879
1997
|
},
|
|
1880
1998
|
{
|
|
1881
1999
|
name: 'Components',
|
|
1882
|
-
value:
|
|
2000
|
+
value: USE_DOCKER
|
|
2001
|
+
? `PM2: ✓\nDiscord: ✓\nDocker: ${healthStatus.docker ? '✓' : '✗'}`
|
|
2002
|
+
: `PM2: ✓\nDiscord: ✓\nDocker: ⊘ (Local Mode)`,
|
|
1883
2003
|
inline: false
|
|
1884
2004
|
}
|
|
1885
2005
|
],
|
package/src/core/config.js
CHANGED
|
@@ -81,6 +81,7 @@ function loadConfig() {
|
|
|
81
81
|
// === Workspace Configuration ===
|
|
82
82
|
workspace: {
|
|
83
83
|
projectDir: expandPath(fileConfig.PROJECT_DIR) || process.cwd(),
|
|
84
|
+
useDocker: fileConfig.workspace?.useDocker !== false, // Default to true for backward compatibility
|
|
84
85
|
containerName: fileConfig.workspace?.containerName || 'claude-discord-bot',
|
|
85
86
|
dockerImage: fileConfig.workspace?.dockerImage || 'claude-sandbox',
|
|
86
87
|
portMappings: fileConfig.workspace?.portMappings || [],
|