agent-window 1.4.0 → 1.4.1

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-window",
3
- "version": "1.4.0",
3
+ "version": "1.4.1",
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",
package/src/bot.js CHANGED
@@ -49,6 +49,7 @@ const ALLOWED_CHANNELS = config.discord.allowedChannels;
49
49
  const CHANNEL_SESSIONS_FILE = config.paths.sessions;
50
50
  const PENDING_DIR = config.paths.pending;
51
51
  const HOOK_DIR = config.paths.hooks;
52
+ const USE_DOCKER = config.workspace.useDocker; // Whether to use Docker or run locally
52
53
  const CONTAINER_NAME = config.workspace.containerName;
53
54
  const DOCKER_IMAGE = config.workspace.dockerImage;
54
55
 
@@ -118,20 +119,37 @@ function updateHealthStatus(component, status) {
118
119
 
119
120
  // Get overall health status
120
121
  function getOverallHealth() {
121
- const checks = [
122
- healthStatus.pm2 ? '✓' : '✗',
123
- healthStatus.discord ? '✓' : '✗',
124
- healthStatus.docker ? '✓' : '✗'
125
- ].join(' ');
126
-
127
- if (healthStatus.pm2 && healthStatus.discord && healthStatus.docker) {
128
- return 'healthy';
129
- } else if (healthStatus.pm2 && healthStatus.discord) {
130
- return 'degraded'; // Running but Docker failed
131
- } else if (healthStatus.pm2) {
132
- return 'unhealthy'; // PM2 running but Discord disconnected
122
+ // Build health check string
123
+ const checks = [healthStatus.pm2 ? '✓' : '✗', healthStatus.discord ? '✓' : '✗'];
124
+
125
+ // Only check Docker if enabled
126
+ if (USE_DOCKER) {
127
+ checks.push(healthStatus.docker ? '✓' : '✗');
128
+ }
129
+
130
+ const healthString = checks.join(' ');
131
+
132
+ // Determine overall status
133
+ if (USE_DOCKER) {
134
+ // Docker mode: all components must be healthy
135
+ if (healthStatus.pm2 && healthStatus.discord && healthStatus.docker) {
136
+ return 'healthy';
137
+ } else if (healthStatus.pm2 && healthStatus.discord) {
138
+ return 'degraded'; // Running but Docker failed
139
+ } else if (healthStatus.pm2) {
140
+ return 'unhealthy'; // PM2 running but Discord disconnected
141
+ } else {
142
+ return 'failed';
143
+ }
133
144
  } else {
134
- return 'failed';
145
+ // Non-Docker mode: only PM2 and Discord required
146
+ if (healthStatus.pm2 && healthStatus.discord) {
147
+ return 'healthy';
148
+ } else if (healthStatus.pm2) {
149
+ return 'unhealthy'; // PM2 running but Discord disconnected
150
+ } else {
151
+ return 'failed';
152
+ }
135
153
  }
136
154
  }
137
155
 
@@ -346,6 +364,13 @@ function isContainerRunning() {
346
364
 
347
365
  // Start or ensure persistent container is running
348
366
  function ensureContainer() {
367
+ // Skip Docker operations if not using Docker
368
+ if (!USE_DOCKER) {
369
+ console.log('[Docker] Docker is disabled (useDocker: false). Running in local mode.');
370
+ updateHealthStatus('docker', true); // Mark as "healthy" since we don't need Docker
371
+ return true;
372
+ }
373
+
349
374
  // Log the paths being used
350
375
  console.log('[Docker] PENDING_DIR (host):', PENDING_DIR);
351
376
  console.log('[Docker] HOOK_DIR (host):', HOOK_DIR);
@@ -437,6 +462,11 @@ function ensureContainer() {
437
462
 
438
463
  // Stop persistent container (call on bot shutdown)
439
464
  function stopContainer() {
465
+ if (!USE_DOCKER) {
466
+ console.log('[Docker] Docker is disabled. No container to stop.');
467
+ return;
468
+ }
469
+
440
470
  try {
441
471
  execSync(`docker stop ${CONTAINER_NAME} 2>/dev/null`);
442
472
  execSync(`docker rm ${CONTAINER_NAME} 2>/dev/null`);
@@ -1445,21 +1475,42 @@ const commands = {
1445
1475
  let lastStatusUpdate = 0;
1446
1476
  const task = activeTasks.get(channelId);
1447
1477
 
1448
- // Use docker exec to run CLI in the persistent container
1449
- const dockerArgs = [
1450
- 'exec', '-i',
1451
- CONTAINER_NAME,
1452
- CLI_COMMAND,
1453
- ...cliArgs
1454
- ];
1478
+ // Execute CLI command (either in Docker or locally based on configuration)
1479
+ let child;
1455
1480
 
1456
- console.log('[Docker] Executing in container:', CONTAINER_NAME);
1481
+ if (USE_DOCKER) {
1482
+ // Use docker exec to run CLI in the persistent container
1483
+ const dockerArgs = [
1484
+ 'exec', '-i',
1485
+ CONTAINER_NAME,
1486
+ CLI_COMMAND,
1487
+ ...cliArgs
1488
+ ];
1457
1489
 
1458
- const child = spawn('docker', dockerArgs, {
1459
- stdio: ['pipe', 'pipe', 'pipe'],
1460
- });
1490
+ console.log('[Docker] Executing in container:', CONTAINER_NAME);
1491
+
1492
+ child = spawn('docker', dockerArgs, {
1493
+ stdio: ['pipe', 'pipe', 'pipe'],
1494
+ });
1461
1495
 
1462
- child.stdin.end();
1496
+ child.stdin.end();
1497
+ } else {
1498
+ // Run CLI directly on local host
1499
+ console.log('[Local] Executing locally:', CLI_COMMAND, ...cliArgs);
1500
+
1501
+ child = spawn(CLI_COMMAND, cliArgs, {
1502
+ stdio: ['pipe', 'pipe', 'pipe'],
1503
+ cwd: PROJECT_DIR,
1504
+ env: {
1505
+ ...process.env,
1506
+ CLAUDE_CODE_OAUTH_TOKEN: OAUTH_TOKEN || '',
1507
+ HOME: process.env.HOME,
1508
+ PATH: process.env.PATH
1509
+ }
1510
+ });
1511
+
1512
+ child.stdin.end();
1513
+ }
1463
1514
 
1464
1515
  // Update status periodically (with stop button)
1465
1516
  // forceUpdate=true bypasses throttle for important state changes
@@ -1762,18 +1813,9 @@ client.on(Events.MessageCreate, async (message) => {
1762
1813
  return;
1763
1814
  }
1764
1815
 
1765
- // SECURITY: Validate channel access
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
- }
1816
+ if (ALLOWED_CHANNELS && !isDM) {
1775
1817
  if (!ALLOWED_CHANNELS.includes(message.channel.id)) {
1776
- console.log(`[MSG] Ignored: channel ${message.channel.id} not in allowed list`);
1818
+ console.log('[MSG] Ignored: channel not allowed');
1777
1819
  return;
1778
1820
  }
1779
1821
  }
@@ -1853,9 +1895,14 @@ client.on(Events.ClientReady, async () => {
1853
1895
  // Send startup notification to Discord channels
1854
1896
  try {
1855
1897
  const uptime = Math.floor((Date.now() - healthStatus.startTime) / 1000);
1856
- const dockerStatus = healthStatus.docker ? '✓ Connected' : '✗ Not Available';
1857
1898
  const overallStatus = overallHealth.toUpperCase();
1858
1899
 
1900
+ // Build execution mode info
1901
+ const execMode = USE_DOCKER ? 'Docker Container' : 'Local Host';
1902
+ const dockerStatus = USE_DOCKER
1903
+ ? (healthStatus.docker ? '✓ Connected' : '✗ Not Available')
1904
+ : '⊘ Disabled (Local Mode)';
1905
+
1859
1906
  // Build startup message
1860
1907
  const startupMessage = {
1861
1908
  embeds: [{
@@ -1867,6 +1914,11 @@ client.on(Events.ClientReady, async () => {
1867
1914
  value: overallStatus,
1868
1915
  inline: true
1869
1916
  },
1917
+ {
1918
+ name: 'Execution Mode',
1919
+ value: execMode,
1920
+ inline: true
1921
+ },
1870
1922
  {
1871
1923
  name: 'Docker',
1872
1924
  value: dockerStatus,
@@ -1879,7 +1931,9 @@ client.on(Events.ClientReady, async () => {
1879
1931
  },
1880
1932
  {
1881
1933
  name: 'Components',
1882
- value: `PM2: ✓\nDiscord: ✓\nDocker: ${healthStatus.docker ? '✓' : '✗'}`,
1934
+ value: USE_DOCKER
1935
+ ? `PM2: ✓\nDiscord: ✓\nDocker: ${healthStatus.docker ? '✓' : '✗'}`
1936
+ : `PM2: ✓\nDiscord: ✓\nDocker: ⊘ (Local Mode)`,
1883
1937
  inline: false
1884
1938
  }
1885
1939
  ],
@@ -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 || [],