devbonzai 2.2.307 → 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 +11 -1
- package/package.json +5 -3
- package/templates/handlers/index.js +2 -1
- package/templates/handlers/terminal.js +106 -0
- package/templates/receiver.js +14 -3
- package/templates/terminal-test.html +104 -0
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,6 +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";
|
|
120
|
+
packageJson.dependencies.ws = "^8.16.0";
|
|
121
|
+
packageJson.dependencies["node-pty"] = "^1.1.0";
|
|
112
122
|
|
|
113
123
|
// Add script to run receiver
|
|
114
124
|
if (!packageJson.scripts) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "devbonzai",
|
|
3
|
-
"version": "2.2.
|
|
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,8 +22,10 @@
|
|
|
22
22
|
"author": "",
|
|
23
23
|
"license": "ISC",
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"
|
|
25
|
+
"@babel/parser": "^7.23.0",
|
|
26
26
|
"cors": "^2.8.5",
|
|
27
|
-
"
|
|
27
|
+
"express": "^4.18.2",
|
|
28
|
+
"node-pty": "^1.1.0",
|
|
29
|
+
"ws": "^8.16.0"
|
|
28
30
|
}
|
|
29
31
|
}
|
|
@@ -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,106 @@
|
|
|
1
|
+
const pty = require('node-pty');
|
|
2
|
+
|
|
3
|
+
// Store active terminal sessions
|
|
4
|
+
const terminals = new Map();
|
|
5
|
+
|
|
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
|
+
|
|
18
|
+
const ptyProcess = pty.spawn(shell, [], {
|
|
19
|
+
name: 'xterm-256color',
|
|
20
|
+
cols,
|
|
21
|
+
rows,
|
|
22
|
+
cwd: process.env.HOME || process.cwd(),
|
|
23
|
+
env: { ...process.env, TERM: 'xterm-256color' }
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
terminals.set(sessionId, {
|
|
27
|
+
pty: ptyProcess,
|
|
28
|
+
buffer: ''
|
|
29
|
+
});
|
|
30
|
+
|
|
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'
|
|
44
|
+
}
|
|
45
|
+
}
|
|
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;
|
|
54
|
+
|
|
55
|
+
try {
|
|
56
|
+
ptyProcess = createTerminal(sessionId);
|
|
57
|
+
console.log(`Terminal session ${sessionId} started`);
|
|
58
|
+
} catch (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;
|
|
63
|
+
}
|
|
64
|
+
|
|
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
|
+
});
|
|
73
|
+
|
|
74
|
+
ptyProcess.onExit(({ exitCode }) => {
|
|
75
|
+
ws.send(JSON.stringify({ type: 'exit', exitCode }));
|
|
76
|
+
ws.close();
|
|
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
|
+
});
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
module.exports = { terminalHandler, setupTerminalWebSocket };
|
package/templates/receiver.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
const express = require('
|
|
4
|
-
const cors = require('
|
|
3
|
+
const express = require('express');
|
|
4
|
+
const cors = require('cors');
|
|
5
|
+
const http = require('http');
|
|
6
|
+
const { WebSocketServer } = require('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,11 @@ 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
|
});
|
|
43
|
+
|
|
@@ -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>
|