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
|
@@ -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
|
-
|
|
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
|