bonzai-burn 1.0.16 → 1.0.18

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.18",
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,140 @@
1
+ let pty;
2
+ try {
3
+ pty = require('node-pty');
4
+ } catch (err) {
5
+ pty = null;
6
+ }
7
+
8
+ // Store active terminal sessions
9
+ const terminals = new Map();
10
+
11
+ // Get default shell based on platform
12
+ function getDefaultShell() {
13
+ if (process.platform === 'win32') {
14
+ return process.env.COMSPEC || 'powershell.exe';
15
+ }
16
+ const shell = process.env.SHELL || '/bin/bash';
17
+ if (!shell.startsWith('/')) {
18
+ return '/bin/bash';
19
+ }
20
+ return shell;
21
+ }
22
+
23
+ // Create a new terminal session
24
+ function createTerminal(sessionId, cols = 80, rows = 24) {
25
+ if (!pty) {
26
+ throw new Error('node-pty is not available. Native binaries may not have installed correctly.');
27
+ }
28
+
29
+ const shell = getDefaultShell();
30
+ const cwd = process.env.HOME || process.cwd();
31
+
32
+ let ptyProcess;
33
+ try {
34
+ ptyProcess = pty.spawn(shell, [], {
35
+ name: 'xterm-256color',
36
+ cols,
37
+ rows,
38
+ cwd,
39
+ env: {
40
+ ...process.env,
41
+ TERM: 'xterm-256color',
42
+ PATH: process.env.PATH || '/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin'
43
+ }
44
+ });
45
+ } catch (spawnErr) {
46
+ try {
47
+ ptyProcess = pty.spawn('/bin/bash', [], {
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 (bashErr) {
59
+ throw spawnErr;
60
+ }
61
+ }
62
+
63
+ terminals.set(sessionId, {
64
+ pty: ptyProcess,
65
+ buffer: ''
66
+ });
67
+
68
+ return ptyProcess;
69
+ }
70
+
71
+ // HTTP handler for terminal info
72
+ function terminalHandler(req, res) {
73
+ res.json({
74
+ message: 'Terminal WebSocket API',
75
+ usage: {
76
+ websocket: 'ws://localhost:3001/terminal',
77
+ events: {
78
+ 'input': 'Send terminal input (data: string)',
79
+ 'resize': 'Resize terminal (cols: number, rows: number)',
80
+ 'output': 'Receive terminal output'
81
+ }
82
+ }
83
+ });
84
+ }
85
+
86
+ // WebSocket handler for terminal sessions
87
+ function setupTerminalWebSocket(wss) {
88
+ wss.on('connection', (ws) => {
89
+ const sessionId = Date.now().toString();
90
+ let ptyProcess;
91
+
92
+ try {
93
+ ptyProcess = createTerminal(sessionId);
94
+ } catch (err) {
95
+ ws.send(JSON.stringify({ type: 'error', message: 'Failed to create terminal: ' + err.message }));
96
+ ws.close();
97
+ return;
98
+ }
99
+
100
+ // Send terminal output to WebSocket client
101
+ ptyProcess.onData((data) => {
102
+ try {
103
+ ws.send(JSON.stringify({ type: 'output', data }));
104
+ } catch (e) {
105
+ // Client disconnected
106
+ }
107
+ });
108
+
109
+ ptyProcess.onExit(({ exitCode }) => {
110
+ ws.send(JSON.stringify({ type: 'exit', exitCode }));
111
+ ws.close();
112
+ });
113
+
114
+ // Handle incoming messages from client
115
+ ws.on('message', (message) => {
116
+ try {
117
+ const msg = JSON.parse(message);
118
+
119
+ switch (msg.type) {
120
+ case 'input':
121
+ ptyProcess.write(msg.data);
122
+ break;
123
+ case 'resize':
124
+ ptyProcess.resize(msg.cols, msg.rows);
125
+ break;
126
+ }
127
+ } catch (e) {
128
+ // Parse error
129
+ }
130
+ });
131
+
132
+ // Cleanup on disconnect
133
+ ws.on('close', () => {
134
+ ptyProcess.kill();
135
+ terminals.delete(sessionId);
136
+ });
137
+ });
138
+ }
139
+
140
+ 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