index.js 2.5 KB

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