index.js 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. const express = require('express');
  2. const http = require('http');
  3. const WebSocket = require('ws');
  4. const chokidar = require('chokidar');
  5. const fs = require('fs');
  6. const path = require('path');
  7. const PORT = process.env.BRAINSTORM_PORT || 3333;
  8. const SCREEN_FILE = process.env.BRAINSTORM_SCREEN || '/tmp/brainstorm/screen.html';
  9. const SCREEN_DIR = path.dirname(SCREEN_FILE);
  10. // Ensure screen directory exists
  11. if (!fs.existsSync(SCREEN_DIR)) {
  12. fs.mkdirSync(SCREEN_DIR, { recursive: true });
  13. }
  14. // Create default screen if none exists
  15. if (!fs.existsSync(SCREEN_FILE)) {
  16. fs.writeFileSync(SCREEN_FILE, `<!DOCTYPE html>
  17. <html>
  18. <head>
  19. <title>Brainstorm Companion</title>
  20. <style>
  21. body { font-family: system-ui, sans-serif; padding: 2rem; max-width: 800px; margin: 0 auto; }
  22. h1 { color: #333; }
  23. p { color: #666; }
  24. </style>
  25. </head>
  26. <body>
  27. <h1>Brainstorm Companion</h1>
  28. <p>Waiting for Claude to push a screen...</p>
  29. </body>
  30. </html>`);
  31. }
  32. const app = express();
  33. const server = http.createServer(app);
  34. const wss = new WebSocket.Server({ server });
  35. // Track connected browsers for reload notifications
  36. const clients = new Set();
  37. wss.on('connection', (ws) => {
  38. clients.add(ws);
  39. ws.on('close', () => clients.delete(ws));
  40. ws.on('message', (data) => {
  41. // User interaction event - write to stdout for Claude
  42. const event = JSON.parse(data.toString());
  43. console.log(JSON.stringify({ ...event, type: 'user-event' }));
  44. });
  45. });
  46. // Serve current screen with helper.js injected
  47. app.get('/', (req, res) => {
  48. let html = fs.readFileSync(SCREEN_FILE, 'utf-8');
  49. // Inject helper script before </body>
  50. const helperScript = fs.readFileSync(path.join(__dirname, 'helper.js'), 'utf-8');
  51. const injection = `<script>\n${helperScript}\n</script>`;
  52. if (html.includes('</body>')) {
  53. html = html.replace('</body>', `${injection}\n</body>`);
  54. } else {
  55. html += injection;
  56. }
  57. res.type('html').send(html);
  58. });
  59. // Watch for screen file changes
  60. chokidar.watch(SCREEN_FILE).on('change', () => {
  61. console.log(JSON.stringify({ type: 'screen-updated', file: SCREEN_FILE }));
  62. // Notify all browsers to reload
  63. clients.forEach(ws => {
  64. if (ws.readyState === WebSocket.OPEN) {
  65. ws.send(JSON.stringify({ type: 'reload' }));
  66. }
  67. });
  68. });
  69. server.listen(PORT, '127.0.0.1', () => {
  70. console.log(JSON.stringify({ type: 'server-started', port: PORT, url: `http://localhost:${PORT}` }));
  71. });