devbonzai 2.2.306 → 2.2.308

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/cli.js CHANGED
@@ -71,7 +71,15 @@ async function main() {
71
71
  const utilsSrc = path.join(__dirname, 'templates', 'utils');
72
72
  const utilsDest = path.join(bonzaiDir, 'utils');
73
73
  copyDirectory(utilsSrc, utilsDest);
74
-
74
+
75
+ // Copy terminal-test.html
76
+ console.log('📝 Copying terminal-test.html...');
77
+ const terminalTestSrc = path.join(__dirname, 'templates', 'terminal-test.html');
78
+ const terminalTestDest = path.join(bonzaiDir, 'terminal-test.html');
79
+ if (fs.existsSync(terminalTestSrc)) {
80
+ fs.copyFileSync(terminalTestSrc, terminalTestDest);
81
+ }
82
+
75
83
  // Write .ignore file in bonzai directory
76
84
  const ignoreTargetPath = path.join(bonzaiDir, '.ignore');
77
85
 
@@ -109,8 +117,8 @@ async function main() {
109
117
  packageJson.dependencies.express = "^4.18.2";
110
118
  packageJson.dependencies.cors = "^2.8.5";
111
119
  packageJson.dependencies["@babel/parser"] = "^7.23.0";
112
- packageJson.dependencies["node-pty"] = "^1.0.0";
113
120
  packageJson.dependencies.ws = "^8.16.0";
121
+ packageJson.dependencies["node-pty"] = "^1.1.0";
114
122
 
115
123
  // Add script to run receiver
116
124
  if (!packageJson.scripts) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "devbonzai",
3
- "version": "2.2.306",
3
+ "version": "2.2.308",
4
4
  "description": "Quickly set up a local file server in any repository for browser-based file access",
5
5
  "main": "cli.js",
6
6
  "bin": {
@@ -22,10 +22,10 @@
22
22
  "author": "",
23
23
  "license": "ISC",
24
24
  "dependencies": {
25
- "express": "^4.18.2",
26
- "cors": "^2.8.5",
27
25
  "@babel/parser": "^7.23.0",
28
- "node-pty": "^1.0.0",
26
+ "cors": "^2.8.5",
27
+ "express": "^4.18.2",
28
+ "node-pty": "^1.1.0",
29
29
  "ws": "^8.16.0"
30
30
  }
31
31
  }
@@ -9,7 +9,7 @@ function indexHandler(req, res) {
9
9
  'POST /open-cursor': 'Open Cursor (body: {path, line?})',
10
10
  'POST /shutdown': 'Gracefully shutdown the server',
11
11
  'POST /scan_code_quality': 'Scan code quality (body: {projectPath})',
12
- 'WS /terminal?cwd=<path>': 'WebSocket terminal connection (optional cwd query param)'
12
+ 'WS /terminal': 'Interactive terminal via WebSocket'
13
13
  },
14
14
  example: 'Try: /list or /read?path=README.md'
15
15
  });
@@ -1,97 +1,106 @@
1
1
  const pty = require('node-pty');
2
- const fs = require('fs');
3
2
 
4
3
  // Store active terminal sessions
5
4
  const terminals = new Map();
6
5
 
7
- function handleTerminalConnection(ws, workingDirectory) {
8
- // Use user's default shell, fallback to /bin/zsh
9
- const shell = process.platform === 'win32' ? 'powershell.exe' : (process.env.SHELL || '/bin/zsh');
10
- const cwd = workingDirectory || process.env.HOME;
11
-
12
- // Debug logging
13
- console.log('🖥️ Terminal session starting...');
14
- console.log(' Shell:', shell, '| Exists:', fs.existsSync(shell));
15
- console.log(' CWD:', cwd, '| Exists:', fs.existsSync(cwd));
16
-
6
+ // Get default shell based on platform
7
+ function getDefaultShell() {
8
+ if (process.platform === 'win32') {
9
+ return process.env.COMSPEC || 'powershell.exe';
10
+ }
11
+ return process.env.SHELL || '/bin/bash';
12
+ }
13
+
14
+ // Create a new terminal session
15
+ function createTerminal(sessionId, cols = 80, rows = 24) {
16
+ const shell = getDefaultShell();
17
+
17
18
  const ptyProcess = pty.spawn(shell, [], {
18
19
  name: 'xterm-256color',
19
- cols: 80,
20
- rows: 24,
21
- cwd: cwd,
22
- env: {
23
- ...process.env,
24
- TERM: 'xterm-256color',
25
- COLORTERM: 'truecolor',
26
- },
20
+ cols,
21
+ rows,
22
+ cwd: process.env.HOME || process.cwd(),
23
+ env: { ...process.env, TERM: 'xterm-256color' }
27
24
  });
28
25
 
29
- const terminalId = Date.now().toString();
30
- terminals.set(terminalId, { pty: ptyProcess, ws });
26
+ terminals.set(sessionId, {
27
+ pty: ptyProcess,
28
+ buffer: ''
29
+ });
31
30
 
32
- // Send PTY output to WebSocket
33
- ptyProcess.onData((data) => {
34
- try {
35
- if (ws.readyState === 1) { // WebSocket.OPEN
36
- ws.send(data);
31
+ return ptyProcess;
32
+ }
33
+
34
+ // HTTP handler for terminal info
35
+ function terminalHandler(req, res) {
36
+ res.json({
37
+ message: 'Terminal WebSocket API',
38
+ usage: {
39
+ websocket: 'ws://localhost:3001/terminal',
40
+ events: {
41
+ 'input': 'Send terminal input (data: string)',
42
+ 'resize': 'Resize terminal (cols: number, rows: number)',
43
+ 'output': 'Receive terminal output'
37
44
  }
38
- } catch (err) {
39
- console.error('Error sending to WebSocket:', err);
40
45
  }
41
46
  });
47
+ }
48
+
49
+ // WebSocket handler for terminal sessions
50
+ function setupTerminalWebSocket(wss) {
51
+ wss.on('connection', (ws) => {
52
+ const sessionId = Date.now().toString();
53
+ let ptyProcess;
42
54
 
43
- // Handle incoming messages from WebSocket
44
- ws.on('message', (msg) => {
45
55
  try {
46
- const message = msg.toString();
47
-
48
- // Check if it's a resize command
49
- if (message.startsWith('\x1b[8;')) {
50
- // Parse resize: ESC[8;rows;colst
51
- const match = message.match(/\x1b\[8;(\d+);(\d+)t/);
52
- if (match) {
53
- const rows = parseInt(match[1], 10);
54
- const cols = parseInt(match[2], 10);
55
- ptyProcess.resize(cols, rows);
56
- return;
57
- }
58
- }
59
-
60
- // Check for JSON resize command
61
- if (message.startsWith('{')) {
62
- try {
63
- const json = JSON.parse(message);
64
- if (json.type === 'resize' && json.cols && json.rows) {
65
- ptyProcess.resize(json.cols, json.rows);
66
- return;
67
- }
68
- } catch (e) {
69
- // Not JSON, treat as regular input
70
- }
71
- }
72
-
73
- // Regular terminal input
74
- ptyProcess.write(message);
56
+ ptyProcess = createTerminal(sessionId);
57
+ console.log(`Terminal session ${sessionId} started`);
75
58
  } catch (err) {
76
- console.error('Error processing message:', err);
59
+ console.error('Failed to create terminal:', err.message);
60
+ ws.send(JSON.stringify({ type: 'error', message: 'Failed to create terminal: ' + err.message }));
61
+ ws.close();
62
+ return;
77
63
  }
78
- });
79
64
 
80
- // Clean up on WebSocket close
81
- ws.on('close', () => {
82
- console.log('🖥️ Terminal session closed');
83
- ptyProcess.kill();
84
- terminals.delete(terminalId);
85
- });
65
+ // Send terminal output to WebSocket client
66
+ ptyProcess.onData((data) => {
67
+ try {
68
+ ws.send(JSON.stringify({ type: 'output', data }));
69
+ } catch (e) {
70
+ // Client disconnected
71
+ }
72
+ });
86
73
 
87
- // Handle PTY exit
88
- ptyProcess.onExit(({ exitCode, signal }) => {
89
- console.log(`🖥️ Terminal process exited (code: ${exitCode}, signal: ${signal})`);
90
- terminals.delete(terminalId);
91
- if (ws.readyState === 1) {
74
+ ptyProcess.onExit(({ exitCode }) => {
75
+ ws.send(JSON.stringify({ type: 'exit', exitCode }));
92
76
  ws.close();
93
- }
77
+ });
78
+
79
+ // Handle incoming messages from client
80
+ ws.on('message', (message) => {
81
+ try {
82
+ const msg = JSON.parse(message);
83
+
84
+ switch (msg.type) {
85
+ case 'input':
86
+ ptyProcess.write(msg.data);
87
+ break;
88
+ case 'resize':
89
+ ptyProcess.resize(msg.cols, msg.rows);
90
+ break;
91
+ }
92
+ } catch (e) {
93
+ console.error('Terminal message error:', e);
94
+ }
95
+ });
96
+
97
+ // Cleanup on disconnect
98
+ ws.on('close', () => {
99
+ console.log(`Terminal session ${sessionId} closed`);
100
+ ptyProcess.kill();
101
+ terminals.delete(sessionId);
102
+ });
94
103
  });
95
104
  }
96
105
 
97
- module.exports = { handleTerminalConnection };
106
+ module.exports = { terminalHandler, setupTerminalWebSocket };
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- const express = require('./node_modules/express');
4
- const cors = require('./node_modules/cors');
5
- const WebSocket = require('./node_modules/ws');
6
- const { ROOT } = require('./config');
3
+ const express = require('express');
4
+ const cors = require('cors');
5
+ const http = require('http');
6
+ const { WebSocketServer } = require('ws');
7
7
 
8
8
  // Import handlers
9
9
  const indexHandler = require('./handlers/index');
@@ -13,9 +13,14 @@ const deleteHandler = require('./handlers/delete');
13
13
  const openCursorHandler = require('./handlers/open-cursor');
14
14
  const shutdownHandler = require('./handlers/shutdown');
15
15
  const scanCodeQualityHandler = require('./handlers/scan_code_quality');
16
- const { handleTerminalConnection } = require('./handlers/terminal');
16
+ const { terminalHandler, setupTerminalWebSocket } = require('./handlers/terminal');
17
17
 
18
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);
19
24
 
20
25
  app.use(cors());
21
26
  app.use(express.json());
@@ -28,32 +33,11 @@ app.post('/delete', deleteHandler);
28
33
  app.post('/open-cursor', openCursorHandler);
29
34
  app.post('/shutdown', shutdownHandler);
30
35
  app.post('/scan_code_quality', scanCodeQualityHandler);
36
+ app.get('/terminal', terminalHandler);
31
37
 
32
38
  const port = 3001;
33
- const server = app.listen(port, () => {
39
+ server.listen(port, () => {
34
40
  console.log('📂 File server running on http://localhost:' + port);
41
+ console.log('🖥️ Terminal WebSocket available at ws://localhost:' + port + '/terminal');
35
42
  });
36
43
 
37
- // WebSocket server for terminal
38
- const wss = new WebSocket.Server({
39
- server,
40
- path: '/terminal'
41
- });
42
-
43
- wss.on('connection', (ws, req) => {
44
- // Extract working directory from query string if provided
45
- let workingDirectory = ROOT;
46
-
47
- if (req.url) {
48
- const urlMatch = req.url.match(/[?&]cwd=([^&]+)/);
49
- if (urlMatch) {
50
- try {
51
- workingDirectory = decodeURIComponent(urlMatch[1]);
52
- } catch (e) {
53
- console.warn('Invalid cwd parameter, using default:', e.message);
54
- }
55
- }
56
- }
57
-
58
- handleTerminalConnection(ws, workingDirectory);
59
- });
@@ -0,0 +1,104 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Terminal Test</title>
5
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/xterm@5.3.0/css/xterm.css" />
6
+ <style>
7
+ body {
8
+ margin: 0;
9
+ padding: 20px;
10
+ background: #1e1e1e;
11
+ font-family: sans-serif;
12
+ }
13
+ h1 {
14
+ color: #fff;
15
+ margin-bottom: 10px;
16
+ }
17
+ #status {
18
+ color: #888;
19
+ margin-bottom: 10px;
20
+ }
21
+ #terminal {
22
+ height: 400px;
23
+ }
24
+ </style>
25
+ </head>
26
+ <body>
27
+ <h1>Terminal Test</h1>
28
+ <div id="status">Connecting...</div>
29
+ <div id="terminal"></div>
30
+
31
+ <script src="https://cdn.jsdelivr.net/npm/xterm@5.3.0/lib/xterm.min.js"></script>
32
+ <script src="https://cdn.jsdelivr.net/npm/xterm-addon-fit@0.8.0/lib/xterm-addon-fit.min.js"></script>
33
+ <script>
34
+ const term = new Terminal({
35
+ cursorBlink: true,
36
+ fontSize: 14,
37
+ fontFamily: 'Menlo, Monaco, "Courier New", monospace',
38
+ theme: {
39
+ background: '#1e1e1e',
40
+ foreground: '#d4d4d4'
41
+ }
42
+ });
43
+
44
+ const fitAddon = new FitAddon.FitAddon();
45
+ term.loadAddon(fitAddon);
46
+
47
+ const terminalEl = document.getElementById('terminal');
48
+ const statusEl = document.getElementById('status');
49
+
50
+ term.open(terminalEl);
51
+ fitAddon.fit();
52
+
53
+ // Connect to WebSocket
54
+ const ws = new WebSocket('ws://localhost:3001/terminal');
55
+
56
+ ws.onopen = () => {
57
+ statusEl.textContent = 'Connected';
58
+ statusEl.style.color = '#4ec9b0';
59
+
60
+ // Send initial size
61
+ ws.send(JSON.stringify({
62
+ type: 'resize',
63
+ cols: term.cols,
64
+ rows: term.rows
65
+ }));
66
+ };
67
+
68
+ ws.onmessage = (event) => {
69
+ const msg = JSON.parse(event.data);
70
+ if (msg.type === 'output') {
71
+ term.write(msg.data);
72
+ } else if (msg.type === 'exit') {
73
+ statusEl.textContent = 'Session ended (exit code: ' + msg.exitCode + ')';
74
+ statusEl.style.color = '#f48771';
75
+ }
76
+ };
77
+
78
+ ws.onclose = () => {
79
+ statusEl.textContent = 'Disconnected';
80
+ statusEl.style.color = '#f48771';
81
+ };
82
+
83
+ ws.onerror = (err) => {
84
+ statusEl.textContent = 'Connection error';
85
+ statusEl.style.color = '#f48771';
86
+ };
87
+
88
+ // Send input to server
89
+ term.onData((data) => {
90
+ ws.send(JSON.stringify({ type: 'input', data }));
91
+ });
92
+
93
+ // Handle resize
94
+ window.addEventListener('resize', () => {
95
+ fitAddon.fit();
96
+ ws.send(JSON.stringify({
97
+ type: 'resize',
98
+ cols: term.cols,
99
+ rows: term.rows
100
+ }));
101
+ });
102
+ </script>
103
+ </body>
104
+ </html>