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.
Files changed (2) hide show
  1. package/dist/index.js +27 -98
  2. 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 = "0.1.10";
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
- // Clean environment
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 shell directly with interactive flag
711
- let proc;
712
- const isWindows = os.platform() === 'win32';
713
- console.log(`[Terminal] Spawning shell: ${shell} in ${ROOT_DIR}`);
714
- if (isWindows) {
715
- proc = spawn(shell, [], {
716
- cwd: ROOT_DIR,
717
- env: cleanEnv,
718
- stdio: ['pipe', 'pipe', 'pipe'],
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
- proc,
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
- const processOutput = (data) => {
752
- const str = data.toString();
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
- proc.on('close', (code) => {
779
- console.log(`[Terminal] Process closed with code: ${code}`);
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: code || 0 },
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.proc.stdin?.write(data);
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.proc.kill('SIGKILL');
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.proc.kill();
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.11",
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",