lunel-cli 0.1.11 → 0.1.13
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/dist/index.js +27 -98
- package/package.json +4 -3
package/dist/index.js
CHANGED
|
@@ -4,12 +4,17 @@ import qrcode from "qrcode-terminal";
|
|
|
4
4
|
import Ignore from "ignore";
|
|
5
5
|
const ignore = Ignore.default;
|
|
6
6
|
import * as fs from "fs/promises";
|
|
7
|
+
import * as fsSync from "fs";
|
|
7
8
|
import * as path from "path";
|
|
8
9
|
import * as os from "os";
|
|
9
10
|
import { spawn, execSync } from "child_process";
|
|
11
|
+
import * as pty from "node-pty";
|
|
10
12
|
import { createServer } from "net";
|
|
13
|
+
import { fileURLToPath } from "url";
|
|
14
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
15
|
+
const __dirname = path.dirname(__filename);
|
|
11
16
|
const PROXY_URL = process.env.LUNEL_PROXY_URL || "https://gateway.lunel.dev";
|
|
12
|
-
const VERSION = "
|
|
17
|
+
const VERSION = JSON.parse(fsSync.readFileSync(path.join(__dirname, "../package.json"), "utf-8")).version;
|
|
13
18
|
// Root directory - sandbox all file operations to this
|
|
14
19
|
const ROOT_DIR = process.cwd();
|
|
15
20
|
// Simple ANSI parser for terminal state
|
|
@@ -695,50 +700,20 @@ function handleTerminalSpawn(payload) {
|
|
|
695
700
|
throw Object.assign(new Error('No valid shell found'), { code: 'ENOSHELL' });
|
|
696
701
|
}
|
|
697
702
|
const terminalId = `term-${Date.now()}-${Math.random().toString(36).substring(2, 8)}`;
|
|
698
|
-
|
|
699
|
-
const cleanEnv = {};
|
|
700
|
-
for (const [key, value] of Object.entries(process.env)) {
|
|
701
|
-
if (value !== undefined) {
|
|
702
|
-
cleanEnv[key] = value;
|
|
703
|
-
}
|
|
704
|
-
}
|
|
705
|
-
cleanEnv['TERM'] = 'xterm-256color';
|
|
706
|
-
cleanEnv['COLUMNS'] = cols.toString();
|
|
707
|
-
cleanEnv['LINES'] = rows.toString();
|
|
703
|
+
console.log(`[Terminal] Spawning PTY shell: ${shell} (${cols}x${rows}) in ${ROOT_DIR}`);
|
|
708
704
|
// Create ANSI parser for terminal state
|
|
709
705
|
const parser = new AnsiParser(cols, rows);
|
|
710
|
-
// Spawn
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
shell: true,
|
|
720
|
-
});
|
|
721
|
-
}
|
|
722
|
-
else if (os.platform() === 'darwin') {
|
|
723
|
-
// macOS: use script to create a PTY
|
|
724
|
-
// script -q /dev/null shell - creates PTY, logs to /dev/null, runs shell
|
|
725
|
-
proc = spawn('script', ['-q', '/dev/null', shell], {
|
|
726
|
-
cwd: ROOT_DIR,
|
|
727
|
-
env: cleanEnv,
|
|
728
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
729
|
-
});
|
|
730
|
-
}
|
|
731
|
-
else {
|
|
732
|
-
// Linux: script -q -c shell /dev/null
|
|
733
|
-
proc = spawn('script', ['-qc', shell, '/dev/null'], {
|
|
734
|
-
cwd: ROOT_DIR,
|
|
735
|
-
env: cleanEnv,
|
|
736
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
737
|
-
});
|
|
738
|
-
}
|
|
739
|
-
console.log(`[Terminal] Spawned with PID: ${proc.pid}`);
|
|
706
|
+
// Spawn PTY with node-pty
|
|
707
|
+
const ptyProcess = pty.spawn(shell, [], {
|
|
708
|
+
name: 'xterm-256color',
|
|
709
|
+
cols,
|
|
710
|
+
rows,
|
|
711
|
+
cwd: ROOT_DIR,
|
|
712
|
+
env: process.env,
|
|
713
|
+
});
|
|
714
|
+
console.log(`[Terminal] PTY spawned with PID: ${ptyProcess.pid}`);
|
|
740
715
|
terminals.set(terminalId, {
|
|
741
|
-
|
|
716
|
+
ptyProcess,
|
|
742
717
|
shell,
|
|
743
718
|
cols,
|
|
744
719
|
rows,
|
|
@@ -748,9 +723,8 @@ function handleTerminalSpawn(payload) {
|
|
|
748
723
|
parser,
|
|
749
724
|
});
|
|
750
725
|
// Process output and send grid state
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
parser.write(str);
|
|
726
|
+
ptyProcess.onData((data) => {
|
|
727
|
+
parser.write(data);
|
|
754
728
|
const state = parser.getState();
|
|
755
729
|
if (dataChannel && dataChannel.readyState === WebSocket.OPEN) {
|
|
756
730
|
const msg = {
|
|
@@ -769,14 +743,9 @@ function handleTerminalSpawn(payload) {
|
|
|
769
743
|
};
|
|
770
744
|
dataChannel.send(JSON.stringify(msg));
|
|
771
745
|
}
|
|
772
|
-
};
|
|
773
|
-
proc.stdout?.on('data', processOutput);
|
|
774
|
-
proc.stderr?.on('data', processOutput);
|
|
775
|
-
proc.on('error', (err) => {
|
|
776
|
-
console.error(`[Terminal] Process error:`, err);
|
|
777
746
|
});
|
|
778
|
-
|
|
779
|
-
console.log(`[Terminal]
|
|
747
|
+
ptyProcess.onExit(({ exitCode }) => {
|
|
748
|
+
console.log(`[Terminal] PTY exited with code: ${exitCode}`);
|
|
780
749
|
terminals.delete(terminalId);
|
|
781
750
|
if (dataChannel && dataChannel.readyState === WebSocket.OPEN) {
|
|
782
751
|
const msg = {
|
|
@@ -784,32 +753,11 @@ function handleTerminalSpawn(payload) {
|
|
|
784
753
|
id: `evt-${Date.now()}`,
|
|
785
754
|
ns: "terminal",
|
|
786
755
|
action: "exit",
|
|
787
|
-
payload: { terminalId, code:
|
|
756
|
+
payload: { terminalId, code: exitCode },
|
|
788
757
|
};
|
|
789
758
|
dataChannel.send(JSON.stringify(msg));
|
|
790
759
|
}
|
|
791
760
|
});
|
|
792
|
-
// Send initial state
|
|
793
|
-
setTimeout(() => {
|
|
794
|
-
const state = parser.getState();
|
|
795
|
-
if (dataChannel && dataChannel.readyState === WebSocket.OPEN) {
|
|
796
|
-
const msg = {
|
|
797
|
-
v: 1,
|
|
798
|
-
id: `evt-${Date.now()}`,
|
|
799
|
-
ns: "terminal",
|
|
800
|
-
action: "state",
|
|
801
|
-
payload: {
|
|
802
|
-
terminalId,
|
|
803
|
-
buffer: state.buffer,
|
|
804
|
-
cursorX: state.cursorX,
|
|
805
|
-
cursorY: state.cursorY,
|
|
806
|
-
cols: state.cols,
|
|
807
|
-
rows: state.rows,
|
|
808
|
-
},
|
|
809
|
-
};
|
|
810
|
-
dataChannel.send(JSON.stringify(msg));
|
|
811
|
-
}
|
|
812
|
-
}, 100);
|
|
813
761
|
return { terminalId, shell, cols, rows };
|
|
814
762
|
}
|
|
815
763
|
function handleTerminalWrite(payload) {
|
|
@@ -822,7 +770,7 @@ function handleTerminalWrite(payload) {
|
|
|
822
770
|
const session = terminals.get(terminalId);
|
|
823
771
|
if (!session)
|
|
824
772
|
throw Object.assign(new Error("Terminal not found"), { code: "ENOTERM" });
|
|
825
|
-
session.
|
|
773
|
+
session.ptyProcess.write(data);
|
|
826
774
|
return {};
|
|
827
775
|
}
|
|
828
776
|
function handleTerminalResize(payload) {
|
|
@@ -836,31 +784,12 @@ function handleTerminalResize(payload) {
|
|
|
836
784
|
const session = terminals.get(terminalId);
|
|
837
785
|
if (!session)
|
|
838
786
|
throw Object.assign(new Error("Terminal not found"), { code: "ENOTERM" });
|
|
787
|
+
// Resize PTY (sends SIGWINCH)
|
|
788
|
+
session.ptyProcess.resize(cols, rows);
|
|
839
789
|
// Resize parser buffer
|
|
840
790
|
session.parser.resize(cols, rows);
|
|
841
791
|
session.cols = cols;
|
|
842
792
|
session.rows = rows;
|
|
843
|
-
// Send SIGWINCH to process (resize signal) - won't work without real PTY
|
|
844
|
-
// But we update our internal state
|
|
845
|
-
// Send updated state
|
|
846
|
-
const state = session.parser.getState();
|
|
847
|
-
if (dataChannel && dataChannel.readyState === WebSocket.OPEN) {
|
|
848
|
-
const msg = {
|
|
849
|
-
v: 1,
|
|
850
|
-
id: `evt-${Date.now()}`,
|
|
851
|
-
ns: "terminal",
|
|
852
|
-
action: "state",
|
|
853
|
-
payload: {
|
|
854
|
-
terminalId,
|
|
855
|
-
buffer: state.buffer,
|
|
856
|
-
cursorX: state.cursorX,
|
|
857
|
-
cursorY: state.cursorY,
|
|
858
|
-
cols: state.cols,
|
|
859
|
-
rows: state.rows,
|
|
860
|
-
},
|
|
861
|
-
};
|
|
862
|
-
dataChannel.send(JSON.stringify(msg));
|
|
863
|
-
}
|
|
864
793
|
return {};
|
|
865
794
|
}
|
|
866
795
|
function handleTerminalKill(payload) {
|
|
@@ -870,7 +799,7 @@ function handleTerminalKill(payload) {
|
|
|
870
799
|
const session = terminals.get(terminalId);
|
|
871
800
|
if (!session)
|
|
872
801
|
throw Object.assign(new Error("Terminal not found"), { code: "ENOTERM" });
|
|
873
|
-
session.
|
|
802
|
+
session.ptyProcess.kill();
|
|
874
803
|
terminals.delete(terminalId);
|
|
875
804
|
return {};
|
|
876
805
|
}
|
|
@@ -1667,7 +1596,7 @@ function connectWebSocket(code) {
|
|
|
1667
1596
|
console.log("\nShutting down...");
|
|
1668
1597
|
// Kill all terminals
|
|
1669
1598
|
for (const [id, session] of terminals) {
|
|
1670
|
-
session.
|
|
1599
|
+
session.ptyProcess.kill();
|
|
1671
1600
|
}
|
|
1672
1601
|
terminals.clear();
|
|
1673
1602
|
// Kill all managed processes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lunel-cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.13",
|
|
4
4
|
"author": [
|
|
5
5
|
{
|
|
6
6
|
"name": "Soham Bharambe",
|
|
@@ -25,10 +25,11 @@
|
|
|
25
25
|
"prepublishOnly": "npm run build"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
+
"@xterm/headless": "^5.5.0",
|
|
28
29
|
"ignore": "^6.0.2",
|
|
30
|
+
"node-pty": "^1.1.0",
|
|
29
31
|
"qrcode-terminal": "^0.12.0",
|
|
30
|
-
"ws": "^8.18.0"
|
|
31
|
-
"@xterm/headless": "^5.5.0"
|
|
32
|
+
"ws": "^8.18.0"
|
|
32
33
|
},
|
|
33
34
|
"devDependencies": {
|
|
34
35
|
"@types/node": "^20.0.0",
|