lunel-cli 0.1.12 → 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 -106
- 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,58 +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
|
-
proc = spawn(shell, [], {
|
|
720
|
-
cwd: ROOT_DIR,
|
|
721
|
-
env: cleanEnv,
|
|
722
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
723
|
-
shell: true,
|
|
724
|
-
});
|
|
725
|
-
}
|
|
726
|
-
else {
|
|
727
|
-
// Spawn shell with flags to disable rc files and work non-interactively
|
|
728
|
-
const shellName = path.basename(shell);
|
|
729
|
-
let args = [];
|
|
730
|
-
if (shellName === 'zsh') {
|
|
731
|
-
// -f: no rc files, -s: read from stdin
|
|
732
|
-
args = ['-f', '-s'];
|
|
733
|
-
}
|
|
734
|
-
else if (shellName === 'bash') {
|
|
735
|
-
// --norc --noprofile: no rc files, -s: read from stdin
|
|
736
|
-
args = ['--norc', '--noprofile', '-s'];
|
|
737
|
-
}
|
|
738
|
-
else {
|
|
739
|
-
args = ['-s'];
|
|
740
|
-
}
|
|
741
|
-
proc = spawn(shell, args, {
|
|
742
|
-
cwd: ROOT_DIR,
|
|
743
|
-
env: cleanEnv,
|
|
744
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
745
|
-
});
|
|
746
|
-
}
|
|
747
|
-
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}`);
|
|
748
715
|
terminals.set(terminalId, {
|
|
749
|
-
|
|
716
|
+
ptyProcess,
|
|
750
717
|
shell,
|
|
751
718
|
cols,
|
|
752
719
|
rows,
|
|
@@ -756,9 +723,8 @@ function handleTerminalSpawn(payload) {
|
|
|
756
723
|
parser,
|
|
757
724
|
});
|
|
758
725
|
// Process output and send grid state
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
parser.write(str);
|
|
726
|
+
ptyProcess.onData((data) => {
|
|
727
|
+
parser.write(data);
|
|
762
728
|
const state = parser.getState();
|
|
763
729
|
if (dataChannel && dataChannel.readyState === WebSocket.OPEN) {
|
|
764
730
|
const msg = {
|
|
@@ -777,14 +743,9 @@ function handleTerminalSpawn(payload) {
|
|
|
777
743
|
};
|
|
778
744
|
dataChannel.send(JSON.stringify(msg));
|
|
779
745
|
}
|
|
780
|
-
};
|
|
781
|
-
proc.stdout?.on('data', processOutput);
|
|
782
|
-
proc.stderr?.on('data', processOutput);
|
|
783
|
-
proc.on('error', (err) => {
|
|
784
|
-
console.error(`[Terminal] Process error:`, err);
|
|
785
746
|
});
|
|
786
|
-
|
|
787
|
-
console.log(`[Terminal]
|
|
747
|
+
ptyProcess.onExit(({ exitCode }) => {
|
|
748
|
+
console.log(`[Terminal] PTY exited with code: ${exitCode}`);
|
|
788
749
|
terminals.delete(terminalId);
|
|
789
750
|
if (dataChannel && dataChannel.readyState === WebSocket.OPEN) {
|
|
790
751
|
const msg = {
|
|
@@ -792,32 +753,11 @@ function handleTerminalSpawn(payload) {
|
|
|
792
753
|
id: `evt-${Date.now()}`,
|
|
793
754
|
ns: "terminal",
|
|
794
755
|
action: "exit",
|
|
795
|
-
payload: { terminalId, code:
|
|
756
|
+
payload: { terminalId, code: exitCode },
|
|
796
757
|
};
|
|
797
758
|
dataChannel.send(JSON.stringify(msg));
|
|
798
759
|
}
|
|
799
760
|
});
|
|
800
|
-
// Send initial state
|
|
801
|
-
setTimeout(() => {
|
|
802
|
-
const state = parser.getState();
|
|
803
|
-
if (dataChannel && dataChannel.readyState === WebSocket.OPEN) {
|
|
804
|
-
const msg = {
|
|
805
|
-
v: 1,
|
|
806
|
-
id: `evt-${Date.now()}`,
|
|
807
|
-
ns: "terminal",
|
|
808
|
-
action: "state",
|
|
809
|
-
payload: {
|
|
810
|
-
terminalId,
|
|
811
|
-
buffer: state.buffer,
|
|
812
|
-
cursorX: state.cursorX,
|
|
813
|
-
cursorY: state.cursorY,
|
|
814
|
-
cols: state.cols,
|
|
815
|
-
rows: state.rows,
|
|
816
|
-
},
|
|
817
|
-
};
|
|
818
|
-
dataChannel.send(JSON.stringify(msg));
|
|
819
|
-
}
|
|
820
|
-
}, 100);
|
|
821
761
|
return { terminalId, shell, cols, rows };
|
|
822
762
|
}
|
|
823
763
|
function handleTerminalWrite(payload) {
|
|
@@ -830,7 +770,7 @@ function handleTerminalWrite(payload) {
|
|
|
830
770
|
const session = terminals.get(terminalId);
|
|
831
771
|
if (!session)
|
|
832
772
|
throw Object.assign(new Error("Terminal not found"), { code: "ENOTERM" });
|
|
833
|
-
session.
|
|
773
|
+
session.ptyProcess.write(data);
|
|
834
774
|
return {};
|
|
835
775
|
}
|
|
836
776
|
function handleTerminalResize(payload) {
|
|
@@ -844,31 +784,12 @@ function handleTerminalResize(payload) {
|
|
|
844
784
|
const session = terminals.get(terminalId);
|
|
845
785
|
if (!session)
|
|
846
786
|
throw Object.assign(new Error("Terminal not found"), { code: "ENOTERM" });
|
|
787
|
+
// Resize PTY (sends SIGWINCH)
|
|
788
|
+
session.ptyProcess.resize(cols, rows);
|
|
847
789
|
// Resize parser buffer
|
|
848
790
|
session.parser.resize(cols, rows);
|
|
849
791
|
session.cols = cols;
|
|
850
792
|
session.rows = rows;
|
|
851
|
-
// Send SIGWINCH to process (resize signal) - won't work without real PTY
|
|
852
|
-
// But we update our internal state
|
|
853
|
-
// Send updated state
|
|
854
|
-
const state = session.parser.getState();
|
|
855
|
-
if (dataChannel && dataChannel.readyState === WebSocket.OPEN) {
|
|
856
|
-
const msg = {
|
|
857
|
-
v: 1,
|
|
858
|
-
id: `evt-${Date.now()}`,
|
|
859
|
-
ns: "terminal",
|
|
860
|
-
action: "state",
|
|
861
|
-
payload: {
|
|
862
|
-
terminalId,
|
|
863
|
-
buffer: state.buffer,
|
|
864
|
-
cursorX: state.cursorX,
|
|
865
|
-
cursorY: state.cursorY,
|
|
866
|
-
cols: state.cols,
|
|
867
|
-
rows: state.rows,
|
|
868
|
-
},
|
|
869
|
-
};
|
|
870
|
-
dataChannel.send(JSON.stringify(msg));
|
|
871
|
-
}
|
|
872
793
|
return {};
|
|
873
794
|
}
|
|
874
795
|
function handleTerminalKill(payload) {
|
|
@@ -878,7 +799,7 @@ function handleTerminalKill(payload) {
|
|
|
878
799
|
const session = terminals.get(terminalId);
|
|
879
800
|
if (!session)
|
|
880
801
|
throw Object.assign(new Error("Terminal not found"), { code: "ENOTERM" });
|
|
881
|
-
session.
|
|
802
|
+
session.ptyProcess.kill();
|
|
882
803
|
terminals.delete(terminalId);
|
|
883
804
|
return {};
|
|
884
805
|
}
|
|
@@ -1675,7 +1596,7 @@ function connectWebSocket(code) {
|
|
|
1675
1596
|
console.log("\nShutting down...");
|
|
1676
1597
|
// Kill all terminals
|
|
1677
1598
|
for (const [id, session] of terminals) {
|
|
1678
|
-
session.
|
|
1599
|
+
session.ptyProcess.kill();
|
|
1679
1600
|
}
|
|
1680
1601
|
terminals.clear();
|
|
1681
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",
|