termites 1.0.0

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.
@@ -0,0 +1,13 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(npm view shellgate)",
5
+ "Bash(npm view termbridge)",
6
+ "Bash(npm view shellcast)",
7
+ "Bash(npm view ptyshare)",
8
+ "Bash(npm view turtleshell)",
9
+ "Bash(npm view snailshell)",
10
+ "Bash(npm view crabshell)"
11
+ ]
12
+ }
13
+ }
package/README.md ADDED
@@ -0,0 +1,63 @@
1
+ # Termites
2
+
3
+ A web-based terminal with server-client architecture for remote shell access.
4
+
5
+ ## Architecture
6
+
7
+ ```
8
+ ┌─────────────────┐ WebSocket ┌─────────────────┐
9
+ │ Client 1 │◄──────────────────►│ │
10
+ │ (node-pty) │ │ │
11
+ └─────────────────┘ │ Server │
12
+ │ (Web UI) │◄──► Browser
13
+ ┌─────────────────┐ WebSocket │ │
14
+ │ Client 2 │◄──────────────────►│ │
15
+ │ (node-pty) │ └─────────────────┘
16
+ └─────────────────┘
17
+ ```
18
+
19
+ ## Installation
20
+
21
+ ```bash
22
+ npm install -g termites
23
+ ```
24
+
25
+ ## Usage
26
+
27
+ ### Start Server
28
+
29
+ ```bash
30
+ termites server
31
+ ```
32
+
33
+ The server starts on port 3456 by default. Access the web UI at `http://localhost:3456`.
34
+
35
+ To use a different port:
36
+
37
+ ```bash
38
+ PORT=8080 termites server
39
+ ```
40
+
41
+ ### Connect Client
42
+
43
+ ```bash
44
+ # Connect to localhost:3456
45
+ termites client
46
+
47
+ # Connect to a remote server
48
+ termites client 192.168.1.100:3456
49
+ termites client ws://example.com:3456
50
+ ```
51
+
52
+ ## Features
53
+
54
+ - Multi-client support - manage multiple shell sessions from one web interface
55
+ - Mobile-friendly UI with collapsible drawer menu
56
+ - Multiple color themes (Solarized, Monokai, Dracula, Nord, GitHub Dark)
57
+ - Customizable font family and size
58
+ - Auto-reconnect for clients
59
+ - Output buffering for terminal history
60
+
61
+ ## License
62
+
63
+ MIT
@@ -0,0 +1,43 @@
1
+ #!/usr/bin/env node
2
+ const path = require('path');
3
+
4
+ const args = process.argv.slice(2);
5
+ const command = args[0];
6
+
7
+ function showHelp() {
8
+ console.log(`
9
+ Termites - Web terminal with server-client architecture
10
+
11
+ Usage:
12
+ termites server Start the server
13
+ termites client [url] Connect to a server
14
+
15
+ Options:
16
+ PORT=<port> Set server port (default: 3456)
17
+
18
+ Examples:
19
+ termites server Start server on port 3456
20
+ PORT=8080 termites server Start server on port 8080
21
+ termites client Connect to ws://localhost:3456
22
+ termites client 192.168.1.100:3456
23
+ `);
24
+ }
25
+
26
+ if (!command || command === '-h' || command === '--help' || command === 'help') {
27
+ showHelp();
28
+ process.exit(0);
29
+ }
30
+
31
+ const rootDir = path.join(__dirname, '..');
32
+
33
+ if (command === 'server') {
34
+ require(path.join(rootDir, 'server.js'));
35
+ } else if (command === 'client') {
36
+ const serverUrl = args[1] || 'ws://localhost:3456';
37
+ process.argv = [process.argv[0], path.join(rootDir, 'client.js'), serverUrl];
38
+ require(path.join(rootDir, 'client.js'));
39
+ } else {
40
+ console.error(`Unknown command: ${command}`);
41
+ showHelp();
42
+ process.exit(1);
43
+ }
package/client.js ADDED
@@ -0,0 +1,146 @@
1
+ #!/usr/bin/env node
2
+ const os = require('os');
3
+ const WebSocket = require('ws');
4
+
5
+ const SHELL = process.env.SHELL || '/bin/bash';
6
+ const SERVER_URL = process.argv[2] || 'ws://localhost:3456/client';
7
+
8
+ function getSystemInfo() {
9
+ const username = os.userInfo().username;
10
+ const hostname = os.hostname();
11
+ const cwd = process.cwd().replace(os.homedir(), '~');
12
+ const platform = os.platform();
13
+ return { username, hostname, cwd, platform };
14
+ }
15
+
16
+ class WebShellClient {
17
+ constructor(serverUrl) {
18
+ this.serverUrl = serverUrl.endsWith('/client') ? serverUrl : serverUrl + '/client';
19
+ this.ws = null;
20
+ this.pty = null;
21
+ this.reconnectDelay = 1000;
22
+ this.maxReconnectDelay = 30000;
23
+ this.systemInfo = getSystemInfo();
24
+
25
+ this.connect();
26
+ }
27
+
28
+ connect() {
29
+ console.log(`连接服务器: ${this.serverUrl}`);
30
+
31
+ this.ws = new WebSocket(this.serverUrl);
32
+
33
+ this.ws.on('open', () => {
34
+ console.log('已连接到服务器');
35
+ this.reconnectDelay = 1000;
36
+
37
+ // Register with server
38
+ this.ws.send(JSON.stringify({
39
+ type: 'register',
40
+ info: this.systemInfo
41
+ }));
42
+
43
+ // Start shell
44
+ this.startShell();
45
+ });
46
+
47
+ this.ws.on('message', (message) => {
48
+ try {
49
+ const data = JSON.parse(message);
50
+ this.handleServerMessage(data);
51
+ } catch (e) {
52
+ console.error('解析服务器消息失败:', e);
53
+ }
54
+ });
55
+
56
+ this.ws.on('close', () => {
57
+ console.log('与服务器断开连接');
58
+ this.stopShell();
59
+ this.scheduleReconnect();
60
+ });
61
+
62
+ this.ws.on('error', (err) => {
63
+ console.error('WebSocket 错误:', err.message);
64
+ });
65
+ }
66
+
67
+ scheduleReconnect() {
68
+ console.log(`${this.reconnectDelay / 1000} 秒后重连...`);
69
+ setTimeout(() => {
70
+ this.reconnectDelay = Math.min(this.reconnectDelay * 2, this.maxReconnectDelay);
71
+ this.connect();
72
+ }, this.reconnectDelay);
73
+ }
74
+
75
+ handleServerMessage(data) {
76
+ switch (data.type) {
77
+ case 'input':
78
+ if (this.pty) {
79
+ this.pty.write(data.text);
80
+ }
81
+ break;
82
+
83
+ case 'resize':
84
+ if (this.pty) {
85
+ this.pty.resize(data.cols, data.rows);
86
+ }
87
+ break;
88
+ }
89
+ }
90
+
91
+ startShell() {
92
+ if (this.pty) return;
93
+
94
+ const pty = require('node-pty');
95
+ this.pty = pty.spawn(SHELL, ['-l'], {
96
+ name: 'xterm-256color',
97
+ cols: 120,
98
+ rows: 40,
99
+ cwd: process.cwd(),
100
+ env: process.env
101
+ });
102
+
103
+ console.log(`Shell 已启动 (PID: ${this.pty.pid})`);
104
+
105
+ this.pty.onData((data) => {
106
+ if (this.ws && this.ws.readyState === WebSocket.OPEN) {
107
+ this.ws.send(JSON.stringify({
108
+ type: 'output',
109
+ data
110
+ }));
111
+ }
112
+ });
113
+
114
+ this.pty.onExit(() => {
115
+ console.log('Shell 已退出');
116
+ if (this.ws && this.ws.readyState === WebSocket.OPEN) {
117
+ this.ws.send(JSON.stringify({ type: 'exit' }));
118
+ }
119
+ this.pty = null;
120
+ // Restart shell
121
+ setTimeout(() => this.startShell(), 1000);
122
+ });
123
+ }
124
+
125
+ stopShell() {
126
+ if (this.pty) {
127
+ this.pty.kill();
128
+ this.pty = null;
129
+ }
130
+ }
131
+ }
132
+
133
+ // Parse server URL from command line
134
+ let serverUrl = SERVER_URL;
135
+ if (!serverUrl.startsWith('ws://') && !serverUrl.startsWith('wss://')) {
136
+ serverUrl = 'ws://' + serverUrl;
137
+ }
138
+
139
+ console.log('WebShell Client');
140
+ console.log(`系统信息: ${getSystemInfo().username}@${getSystemInfo().hostname}`);
141
+ console.log(`Shell: ${SHELL}`);
142
+
143
+ new WebShellClient(serverUrl);
144
+
145
+ process.on('SIGINT', () => process.exit(0));
146
+ process.on('SIGTERM', () => process.exit(0));