bonzai-burn 1.0.16 → 1.0.17

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": "bonzai-burn",
3
- "version": "1.0.16",
3
+ "version": "1.0.17",
4
4
  "description": "Git branch-based cleanup tool with bburn, baccept, and brevert commands",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -8,7 +8,8 @@ function indexHandler(req, res) {
8
8
  'POST /delete': 'Delete file or directory (body: {path})',
9
9
  'POST /open-cursor': 'Open Cursor (body: {path, line?})',
10
10
  'POST /shutdown': 'Gracefully shutdown the server',
11
- 'POST /scan_code_quality': 'Scan code quality (body: {projectPath})'
11
+ 'POST /scan_code_quality': 'Scan code quality (body: {projectPath})',
12
+ 'WS /terminal': 'Interactive terminal via WebSocket'
12
13
  },
13
14
  example: 'Try: /list or /read?path=README.md'
14
15
  });
@@ -0,0 +1,162 @@
1
+ console.log(`[Terminal] Node version: ${process.version}`);
2
+ console.log(`[Terminal] Platform: ${process.platform} ${process.arch}`);
3
+
4
+ let pty;
5
+ try {
6
+ pty = require('node-pty');
7
+ console.log('[Terminal] node-pty loaded successfully');
8
+ } catch (err) {
9
+ console.error('[Terminal] Failed to load node-pty:', err.message);
10
+ console.error('[Terminal] This usually means node-pty native binaries failed to install.');
11
+ console.error('[Terminal] Try running: cd bonzai && npm rebuild node-pty');
12
+ pty = null;
13
+ }
14
+
15
+ // Store active terminal sessions
16
+ const terminals = new Map();
17
+
18
+ // Get default shell based on platform
19
+ function getDefaultShell() {
20
+ if (process.platform === 'win32') {
21
+ return process.env.COMSPEC || 'powershell.exe';
22
+ }
23
+ // Prefer SHELL env var, but always use full path
24
+ const shell = process.env.SHELL || '/bin/bash';
25
+ // Ensure we have a full path
26
+ if (!shell.startsWith('/')) {
27
+ return '/bin/bash';
28
+ }
29
+ return shell;
30
+ }
31
+
32
+ // Create a new terminal session
33
+ function createTerminal(sessionId, cols = 80, rows = 24) {
34
+ if (!pty) {
35
+ throw new Error('node-pty is not available. Native binaries may not have installed correctly.');
36
+ }
37
+
38
+ const shell = getDefaultShell();
39
+ const cwd = process.env.HOME || process.cwd();
40
+
41
+ console.log(`[Terminal] Spawning shell: ${shell}`);
42
+ console.log(`[Terminal] Working directory: ${cwd}`);
43
+ console.log(`[Terminal] PATH: ${process.env.PATH}`);
44
+
45
+ let ptyProcess;
46
+ try {
47
+ ptyProcess = pty.spawn(shell, [], {
48
+ name: 'xterm-256color',
49
+ cols,
50
+ rows,
51
+ cwd,
52
+ env: {
53
+ ...process.env,
54
+ TERM: 'xterm-256color',
55
+ PATH: process.env.PATH || '/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin'
56
+ }
57
+ });
58
+ } catch (spawnErr) {
59
+ console.error('[Terminal] pty.spawn failed:', spawnErr);
60
+ console.error('[Terminal] Error code:', spawnErr.code);
61
+ console.error('[Terminal] Error errno:', spawnErr.errno);
62
+ // Try with /bin/bash as fallback
63
+ console.log('[Terminal] Retrying with /bin/bash...');
64
+ try {
65
+ ptyProcess = pty.spawn('/bin/bash', [], {
66
+ name: 'xterm-256color',
67
+ cols,
68
+ rows,
69
+ cwd,
70
+ env: {
71
+ ...process.env,
72
+ TERM: 'xterm-256color',
73
+ PATH: process.env.PATH || '/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin'
74
+ }
75
+ });
76
+ } catch (bashErr) {
77
+ console.error('[Terminal] /bin/bash also failed:', bashErr);
78
+ throw spawnErr; // throw original error
79
+ }
80
+ }
81
+
82
+ terminals.set(sessionId, {
83
+ pty: ptyProcess,
84
+ buffer: ''
85
+ });
86
+
87
+ return ptyProcess;
88
+ }
89
+
90
+ // HTTP handler for terminal info
91
+ function terminalHandler(req, res) {
92
+ res.json({
93
+ message: 'Terminal WebSocket API',
94
+ usage: {
95
+ websocket: 'ws://localhost:3001/terminal',
96
+ events: {
97
+ 'input': 'Send terminal input (data: string)',
98
+ 'resize': 'Resize terminal (cols: number, rows: number)',
99
+ 'output': 'Receive terminal output'
100
+ }
101
+ }
102
+ });
103
+ }
104
+
105
+ // WebSocket handler for terminal sessions
106
+ function setupTerminalWebSocket(wss) {
107
+ wss.on('connection', (ws) => {
108
+ const sessionId = Date.now().toString();
109
+ let ptyProcess;
110
+
111
+ try {
112
+ ptyProcess = createTerminal(sessionId);
113
+ console.log(`Terminal session ${sessionId} started`);
114
+ } catch (err) {
115
+ console.error('Failed to create terminal:', err.message);
116
+ ws.send(JSON.stringify({ type: 'error', message: 'Failed to create terminal: ' + err.message }));
117
+ ws.close();
118
+ return;
119
+ }
120
+
121
+ // Send terminal output to WebSocket client
122
+ ptyProcess.onData((data) => {
123
+ try {
124
+ ws.send(JSON.stringify({ type: 'output', data }));
125
+ } catch (e) {
126
+ // Client disconnected
127
+ }
128
+ });
129
+
130
+ ptyProcess.onExit(({ exitCode }) => {
131
+ ws.send(JSON.stringify({ type: 'exit', exitCode }));
132
+ ws.close();
133
+ });
134
+
135
+ // Handle incoming messages from client
136
+ ws.on('message', (message) => {
137
+ try {
138
+ const msg = JSON.parse(message);
139
+
140
+ switch (msg.type) {
141
+ case 'input':
142
+ ptyProcess.write(msg.data);
143
+ break;
144
+ case 'resize':
145
+ ptyProcess.resize(msg.cols, msg.rows);
146
+ break;
147
+ }
148
+ } catch (e) {
149
+ console.error('Terminal message error:', e);
150
+ }
151
+ });
152
+
153
+ // Cleanup on disconnect
154
+ ws.on('close', () => {
155
+ console.log(`Terminal session ${sessionId} closed`);
156
+ ptyProcess.kill();
157
+ terminals.delete(sessionId);
158
+ });
159
+ });
160
+ }
161
+
162
+ module.exports = { terminalHandler, setupTerminalWebSocket };
@@ -2,6 +2,8 @@
2
2
 
3
3
  const express = require('./node_modules/express');
4
4
  const cors = require('./node_modules/cors');
5
+ const http = require('http');
6
+ const { WebSocketServer } = require('./node_modules/ws');
5
7
 
6
8
  // Import handlers
7
9
  const indexHandler = require('./handlers/index');
@@ -11,8 +13,14 @@ const deleteHandler = require('./handlers/delete');
11
13
  const openCursorHandler = require('./handlers/open-cursor');
12
14
  const shutdownHandler = require('./handlers/shutdown');
13
15
  const scanCodeQualityHandler = require('./handlers/scan_code_quality');
16
+ const { terminalHandler, setupTerminalWebSocket } = require('./handlers/terminal');
14
17
 
15
18
  const app = express();
19
+ const server = http.createServer(app);
20
+
21
+ // WebSocket server for terminal
22
+ const wss = new WebSocketServer({ server, path: '/terminal' });
23
+ setupTerminalWebSocket(wss);
16
24
 
17
25
  app.use(cors());
18
26
  app.use(express.json());
@@ -25,8 +33,10 @@ app.post('/delete', deleteHandler);
25
33
  app.post('/open-cursor', openCursorHandler);
26
34
  app.post('/shutdown', shutdownHandler);
27
35
  app.post('/scan_code_quality', scanCodeQualityHandler);
36
+ app.get('/terminal', terminalHandler);
28
37
 
29
38
  const port = 3001;
30
- app.listen(port, () => {
39
+ server.listen(port, () => {
31
40
  console.log('📂 File server running on http://localhost:' + port);
41
+ console.log('🖥️ Terminal WebSocket available at ws://localhost:' + port + '/terminal');
32
42
  });
package/src/bgraph.js CHANGED
@@ -101,6 +101,8 @@ async function main() {
101
101
  packageJson.dependencies.express = "^4.18.2";
102
102
  packageJson.dependencies.cors = "^2.8.5";
103
103
  packageJson.dependencies["@babel/parser"] = "^7.23.0";
104
+ packageJson.dependencies.ws = "^8.14.2";
105
+ packageJson.dependencies["node-pty"] = "^1.0.0";
104
106
 
105
107
  // Add script to run receiver
106
108
  if (!packageJson.scripts) {
@@ -121,9 +123,27 @@ async function main() {
121
123
 
122
124
  npm.on('close', (code) => {
123
125
  if (code === 0) {
126
+ // Fix node-pty spawn-helper permissions (npm doesn't preserve execute bits)
127
+ const nodePtyPrebuilds = path.join(bonzaiDir, 'node_modules', 'node-pty', 'prebuilds');
128
+ if (fs.existsSync(nodePtyPrebuilds)) {
129
+ const archDirs = ['darwin-arm64', 'darwin-x64', 'linux-x64', 'linux-arm64'];
130
+ for (const arch of archDirs) {
131
+ const spawnHelperPath = path.join(nodePtyPrebuilds, arch, 'spawn-helper');
132
+ if (fs.existsSync(spawnHelperPath)) {
133
+ try {
134
+ fs.chmodSync(spawnHelperPath, '755');
135
+ console.log(`Fixed node-pty spawn-helper permissions (${arch})`);
136
+ } catch (e) {
137
+ console.warn(`Warning: Could not fix spawn-helper permissions for ${arch}:`, e.message);
138
+ }
139
+ }
140
+ }
141
+ }
142
+
124
143
  console.log('\nListener endpoints successfully deployed');
125
144
  console.log('All code stays on your machine\n');
126
145
  console.log('Relay server running on localhost:3001');
146
+ console.log('Terminal WebSocket available at ws://localhost:3001/terminal');
127
147
  console.log('Diagram available at https://bonzai.dev/\n');
128
148
 
129
149
  // Start the server automatically