clone3 commited on
Commit
5ac74dc
·
verified ·
1 Parent(s): 1856f3a
Files changed (6) hide show
  1. Dockerfile +25 -0
  2. README.md +11 -11
  3. package.json +14 -0
  4. public/index.html +32 -0
  5. public/script.js +48 -0
  6. src/server.js +87 -0
Dockerfile ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Use Node.js 18 base image
2
+ FROM node:18
3
+
4
+ # Install build tools for node-pty
5
+ RUN apt-get update && apt-get install -y \
6
+ python3 \
7
+ make \
8
+ g++ \
9
+ && rm -rf /var/lib/apt/lists/*
10
+
11
+ # Set working directory
12
+ WORKDIR /app
13
+
14
+ # Copy package.json and install dependencies
15
+ COPY package.json .
16
+ RUN npm install
17
+
18
+ # Copy application files
19
+ COPY . .
20
+
21
+ # Expose port 7860
22
+ EXPOSE 7860
23
+
24
+ # Start the application
25
+ CMD ["npm", "start"]
README.md CHANGED
@@ -1,11 +1,11 @@
1
- ---
2
- title: Terminal
3
- emoji: 📚
4
- colorFrom: purple
5
- colorTo: blue
6
- sdk: docker
7
- pinned: false
8
- license: mit
9
- ---
10
-
11
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
+ ---
2
+ title: Terminal
3
+ emoji: 💻
4
+ colorFrom: gray
5
+ colorTo: gray
6
+ sdk: docker
7
+ pinned: false
8
+ license: mit
9
+ ---
10
+
11
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
package.json ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "web-terminal",
3
+ "version": "1.0.1",
4
+ "description": "Web terminal using WebSocket in Hugging Face Space",
5
+ "main": "src/server.js",
6
+ "scripts": {
7
+ "start": "node src/server.js"
8
+ },
9
+ "dependencies": {
10
+ "express": "^4.21.0",
11
+ "ws": "^8.18.0",
12
+ "node-pty": "^1.0.0"
13
+ }
14
+ }
public/index.html ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Web Terminal</title>
7
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/css/xterm.css" />
8
+ <script src="https://cdn.jsdelivr.net/npm/[email protected]/lib/xterm.js"></script>
9
+ <script src="https://cdn.jsdelivr.net/npm/[email protected]/lib/xterm-addon-fit.js"></script>
10
+ <style>
11
+ /* Ensure the HTML and body take up the full viewport */
12
+ html, body {
13
+ margin: 0;
14
+ padding: 0;
15
+ width: 100%;
16
+ height: 100vh;
17
+ overflow: hidden;
18
+ background: #000; /* Ensure background is black to match terminal */
19
+ }
20
+ /* Make the terminal container full-screen */
21
+ #terminal {
22
+ width: 100%;
23
+ height: 100vh;
24
+ display: block;
25
+ }
26
+ </style>
27
+ </head>
28
+ <body>
29
+ <div id="terminal"></div>
30
+ <script src="/script.js"></script>
31
+ </body>
32
+ </html>
public/script.js ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ document.addEventListener('DOMContentLoaded', () => {
2
+ const term = new Terminal({
3
+ cursorBlink: true,
4
+ theme: {
5
+ background: '#000000', // Ensure terminal background is black
6
+ },
7
+ });
8
+ const fitAddon = new FitAddon.FitAddon();
9
+ term.loadAddon(fitAddon);
10
+ term.open(document.getElementById('terminal'));
11
+ term.write('Connecting to server...\r\n');
12
+
13
+ // Connect to WebSocket server
14
+ const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
15
+ const ws = new WebSocket(`${protocol}//${window.location.host}/`);
16
+
17
+ ws.onopen = () => {
18
+ console.log('Connected to WebSocket server');
19
+ term.write('Connected! Type commands below.\r\n');
20
+ fitAddon.fit(); // Fit terminal to container on connection
21
+ };
22
+
23
+ ws.onmessage = (event) => {
24
+ term.write(event.data);
25
+ };
26
+
27
+ term.onData((data) => {
28
+ ws.send(data);
29
+ });
30
+
31
+ ws.onclose = () => {
32
+ console.log('WebSocket connection closed');
33
+ term.write('\r\nConnection closed.\r\n');
34
+ };
35
+
36
+ ws.onerror = (error) => {
37
+ console.error('WebSocket error:', error);
38
+ term.write('\r\nWebSocket error occurred.\r\n');
39
+ };
40
+
41
+ // Resize terminal on window resize
42
+ window.addEventListener('resize', () => {
43
+ fitAddon.fit();
44
+ });
45
+
46
+ // Initial fit after opening terminal
47
+ fitAddon.fit();
48
+ });
src/server.js ADDED
@@ -0,0 +1,87 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const WebSocket = require('ws');
2
+ const pty = require('node-pty');
3
+ const http = require('http');
4
+ const express = require('express');
5
+ const path = require('path');
6
+
7
+ console.log('===== Application Startup =====');
8
+
9
+ // Initialize Express for serving static files and health check
10
+ const app = express();
11
+
12
+ // Serve static files from the public directory
13
+ app.use(express.static(path.join(__dirname, '../public')));
14
+
15
+ // Health check endpoint
16
+ app.get('/health', (req, res) => {
17
+ res.status(200).json({ status: 'OK', timestamp: new Date().toISOString() });
18
+ });
19
+
20
+ // Create HTTP server
21
+ const server = http.createServer(app);
22
+
23
+ // Initialize WebSocket server
24
+ const wss = new WebSocket.Server({ server });
25
+
26
+ wss.on('connection', (ws) => {
27
+ console.log('New WebSocket client connected');
28
+
29
+ try {
30
+ // Spawn a new shell (bash) for the connection
31
+ const shell = pty.spawn('bash', [], {
32
+ name: 'xterm-color',
33
+ env: process.env,
34
+ });
35
+
36
+ // Relay terminal output to the client
37
+ shell.onData((data) => {
38
+ ws.send(data);
39
+ });
40
+
41
+ // Handle input from the client
42
+ ws.on('message', (message) => {
43
+ shell.write(message.toString());
44
+ });
45
+
46
+ // Handle terminal exit
47
+ shell.onExit(({ exitCode }) => {
48
+ console.log(`Shell exited with code ${exitCode}`);
49
+ ws.close();
50
+ });
51
+
52
+ // Handle WebSocket close
53
+ ws.on('close', () => {
54
+ shell.kill();
55
+ console.log('WebSocket client disconnected');
56
+ });
57
+ } catch (error) {
58
+ console.error('Error spawning shell:', error.message);
59
+ ws.send(`Error: Failed to spawn shell - ${error.message}\r\n`);
60
+ ws.close();
61
+ }
62
+ });
63
+
64
+ wss.on('error', (error) => {
65
+ console.error('WebSocket server error:', error.message);
66
+ });
67
+
68
+ // Start the server on port 7860
69
+ const PORT = process.env.PORT || 7860;
70
+ server.listen(PORT, () => {
71
+ console.log(`Server running on port ${PORT}`);
72
+ console.log(`Health check available at http://localhost:${PORT}/health`);
73
+ });
74
+
75
+ server.on('error', (error) => {
76
+ console.error('HTTP server error:', error.message);
77
+ });
78
+
79
+ // Log Node.js and dependency versions for debugging
80
+ console.log(`Node.js version: ${process.version}`);
81
+ try {
82
+ console.log(`ws version: ${require('ws/package.json').version}`);
83
+ console.log(`node-pty version: ${require('node-pty/package.json').version}`);
84
+ console.log(`express version: ${require('express/package.json').version}`);
85
+ } catch (error) {
86
+ console.error('Error loading dependency versions:', error.message);
87
+ };