lunel-cli 0.1.6 → 0.1.7
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 +49 -152
- package/package.json +1 -2
package/dist/index.js
CHANGED
|
@@ -8,9 +8,8 @@ import * as path from "path";
|
|
|
8
8
|
import * as os from "os";
|
|
9
9
|
import { spawn, execSync } from "child_process";
|
|
10
10
|
import { createServer } from "net";
|
|
11
|
-
import * as pty from "node-pty";
|
|
12
11
|
const PROXY_URL = process.env.LUNEL_PROXY_URL || "https://gateway.lunel.dev";
|
|
13
|
-
const VERSION = "0.1.
|
|
12
|
+
const VERSION = "0.1.6";
|
|
14
13
|
// Root directory - sandbox all file operations to this
|
|
15
14
|
const ROOT_DIR = process.cwd();
|
|
16
15
|
const terminals = new Map();
|
|
@@ -481,9 +480,8 @@ async function handleGitDiscard(payload) {
|
|
|
481
480
|
// ============================================================================
|
|
482
481
|
let dataChannel = null;
|
|
483
482
|
function handleTerminalSpawn(payload) {
|
|
484
|
-
//
|
|
485
|
-
let shell = payload.shell || process.env.SHELL;
|
|
486
|
-
// Validate and find a working shell
|
|
483
|
+
// Find a working shell
|
|
484
|
+
let shell = payload.shell || process.env.SHELL || '';
|
|
487
485
|
const possibleShells = os.platform() === 'win32'
|
|
488
486
|
? ['powershell.exe', 'cmd.exe']
|
|
489
487
|
: [shell, '/bin/zsh', '/bin/bash', '/bin/sh'].filter(Boolean);
|
|
@@ -492,152 +490,64 @@ function handleTerminalSpawn(payload) {
|
|
|
492
490
|
if (!candidate)
|
|
493
491
|
continue;
|
|
494
492
|
try {
|
|
495
|
-
if (os.platform()
|
|
496
|
-
// On Windows, just use the shell name
|
|
497
|
-
shell = candidate;
|
|
498
|
-
break;
|
|
499
|
-
}
|
|
500
|
-
else {
|
|
501
|
-
// On Unix, check if file exists and is executable
|
|
493
|
+
if (os.platform() !== 'win32') {
|
|
502
494
|
execSync(`test -x "${candidate}"`, { stdio: 'ignore' });
|
|
503
|
-
shell = candidate;
|
|
504
|
-
break;
|
|
505
495
|
}
|
|
496
|
+
shell = candidate;
|
|
497
|
+
break;
|
|
506
498
|
}
|
|
507
499
|
catch {
|
|
508
|
-
// Try next
|
|
500
|
+
// Try next
|
|
509
501
|
}
|
|
510
502
|
}
|
|
511
503
|
if (!shell) {
|
|
512
504
|
throw Object.assign(new Error('No valid shell found'), { code: 'ENOSHELL' });
|
|
513
505
|
}
|
|
514
|
-
const cols = payload.cols || 80;
|
|
515
|
-
const rows = payload.rows || 24;
|
|
516
506
|
const terminalId = `term-${Date.now()}-${Math.random().toString(36).substring(2, 8)}`;
|
|
517
|
-
//
|
|
507
|
+
// Clean environment
|
|
518
508
|
const cleanEnv = {};
|
|
519
509
|
for (const [key, value] of Object.entries(process.env)) {
|
|
520
510
|
if (value !== undefined) {
|
|
521
511
|
cleanEnv[key] = value;
|
|
522
512
|
}
|
|
523
513
|
}
|
|
524
|
-
cleanEnv['TERM'] = '
|
|
525
|
-
//
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
};
|
|
562
|
-
dataChannel.send(JSON.stringify(msg));
|
|
563
|
-
}
|
|
564
|
-
});
|
|
565
|
-
ptyProcess.onExit(({ exitCode }) => {
|
|
566
|
-
terminals.delete(terminalId);
|
|
567
|
-
if (dataChannel && dataChannel.readyState === WebSocket.OPEN) {
|
|
568
|
-
const msg = {
|
|
569
|
-
v: 1,
|
|
570
|
-
id: `evt-${Date.now()}`,
|
|
571
|
-
ns: "terminal",
|
|
572
|
-
action: "exit",
|
|
573
|
-
payload: { terminalId, code: exitCode },
|
|
574
|
-
};
|
|
575
|
-
dataChannel.send(JSON.stringify(msg));
|
|
576
|
-
}
|
|
577
|
-
});
|
|
578
|
-
}
|
|
579
|
-
else if (fallbackProc) {
|
|
580
|
-
// Store fallback process with a wrapper that mimics PTY interface
|
|
581
|
-
const wrapperPty = {
|
|
582
|
-
write: (data) => fallbackProc.stdin?.write(data),
|
|
583
|
-
resize: (_cols, _rows) => { },
|
|
584
|
-
kill: (signal) => fallbackProc.kill(signal),
|
|
585
|
-
pid: fallbackProc.pid || 0,
|
|
586
|
-
cols,
|
|
587
|
-
rows,
|
|
588
|
-
process: shell,
|
|
589
|
-
handleFlowControl: false,
|
|
590
|
-
onData: (cb) => {
|
|
591
|
-
fallbackProc.stdout?.on('data', (d) => cb(d.toString()));
|
|
592
|
-
fallbackProc.stderr?.on('data', (d) => cb(d.toString()));
|
|
593
|
-
return { dispose: () => { } };
|
|
594
|
-
},
|
|
595
|
-
onExit: (cb) => {
|
|
596
|
-
fallbackProc.on('close', (code) => cb({ exitCode: code || 0 }));
|
|
597
|
-
return { dispose: () => { } };
|
|
598
|
-
},
|
|
599
|
-
clear: () => { },
|
|
600
|
-
pause: () => { },
|
|
601
|
-
resume: () => { },
|
|
602
|
-
};
|
|
603
|
-
terminals.set(terminalId, {
|
|
604
|
-
pty: wrapperPty,
|
|
605
|
-
cols,
|
|
606
|
-
rows,
|
|
607
|
-
});
|
|
608
|
-
// Stream output
|
|
609
|
-
const sendOutput = (data) => {
|
|
610
|
-
if (dataChannel && dataChannel.readyState === WebSocket.OPEN) {
|
|
611
|
-
const msg = {
|
|
612
|
-
v: 1,
|
|
613
|
-
id: `evt-${Date.now()}`,
|
|
614
|
-
ns: "terminal",
|
|
615
|
-
action: "output",
|
|
616
|
-
payload: { terminalId, data: data.toString() },
|
|
617
|
-
};
|
|
618
|
-
dataChannel.send(JSON.stringify(msg));
|
|
619
|
-
}
|
|
620
|
-
};
|
|
621
|
-
fallbackProc.stdout?.on('data', sendOutput);
|
|
622
|
-
fallbackProc.stderr?.on('data', sendOutput);
|
|
623
|
-
fallbackProc.on('close', (code) => {
|
|
624
|
-
terminals.delete(terminalId);
|
|
625
|
-
if (dataChannel && dataChannel.readyState === WebSocket.OPEN) {
|
|
626
|
-
const msg = {
|
|
627
|
-
v: 1,
|
|
628
|
-
id: `evt-${Date.now()}`,
|
|
629
|
-
ns: "terminal",
|
|
630
|
-
action: "exit",
|
|
631
|
-
payload: { terminalId, code: code || 0 },
|
|
632
|
-
};
|
|
633
|
-
dataChannel.send(JSON.stringify(msg));
|
|
634
|
-
}
|
|
635
|
-
});
|
|
636
|
-
}
|
|
637
|
-
else {
|
|
638
|
-
throw Object.assign(new Error('Failed to spawn terminal'), { code: 'ESPAWN' });
|
|
639
|
-
}
|
|
640
|
-
return { terminalId };
|
|
514
|
+
cleanEnv['TERM'] = 'dumb'; // Simple terminal - no escape codes
|
|
515
|
+
// Spawn shell process
|
|
516
|
+
const proc = spawn(shell, ['-i'], {
|
|
517
|
+
cwd: ROOT_DIR,
|
|
518
|
+
env: cleanEnv,
|
|
519
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
520
|
+
});
|
|
521
|
+
terminals.set(terminalId, { proc, shell });
|
|
522
|
+
// Stream output to app
|
|
523
|
+
const sendOutput = (data) => {
|
|
524
|
+
if (dataChannel && dataChannel.readyState === WebSocket.OPEN) {
|
|
525
|
+
const msg = {
|
|
526
|
+
v: 1,
|
|
527
|
+
id: `evt-${Date.now()}`,
|
|
528
|
+
ns: "terminal",
|
|
529
|
+
action: "output",
|
|
530
|
+
payload: { terminalId, data: data.toString() },
|
|
531
|
+
};
|
|
532
|
+
dataChannel.send(JSON.stringify(msg));
|
|
533
|
+
}
|
|
534
|
+
};
|
|
535
|
+
proc.stdout?.on('data', sendOutput);
|
|
536
|
+
proc.stderr?.on('data', sendOutput);
|
|
537
|
+
proc.on('close', (code) => {
|
|
538
|
+
terminals.delete(terminalId);
|
|
539
|
+
if (dataChannel && dataChannel.readyState === WebSocket.OPEN) {
|
|
540
|
+
const msg = {
|
|
541
|
+
v: 1,
|
|
542
|
+
id: `evt-${Date.now()}`,
|
|
543
|
+
ns: "terminal",
|
|
544
|
+
action: "exit",
|
|
545
|
+
payload: { terminalId, code: code || 0 },
|
|
546
|
+
};
|
|
547
|
+
dataChannel.send(JSON.stringify(msg));
|
|
548
|
+
}
|
|
549
|
+
});
|
|
550
|
+
return { terminalId, shell };
|
|
641
551
|
}
|
|
642
552
|
function handleTerminalWrite(payload) {
|
|
643
553
|
const terminalId = payload.terminalId;
|
|
@@ -649,24 +559,11 @@ function handleTerminalWrite(payload) {
|
|
|
649
559
|
const session = terminals.get(terminalId);
|
|
650
560
|
if (!session)
|
|
651
561
|
throw Object.assign(new Error("Terminal not found"), { code: "ENOTERM" });
|
|
652
|
-
session.
|
|
562
|
+
session.proc.stdin?.write(data);
|
|
653
563
|
return {};
|
|
654
564
|
}
|
|
655
565
|
function handleTerminalResize(payload) {
|
|
656
|
-
|
|
657
|
-
const cols = payload.cols;
|
|
658
|
-
const rows = payload.rows;
|
|
659
|
-
if (!terminalId)
|
|
660
|
-
throw Object.assign(new Error("terminalId is required"), { code: "EINVAL" });
|
|
661
|
-
if (!cols || !rows)
|
|
662
|
-
throw Object.assign(new Error("cols and rows are required"), { code: "EINVAL" });
|
|
663
|
-
const session = terminals.get(terminalId);
|
|
664
|
-
if (!session)
|
|
665
|
-
throw Object.assign(new Error("Terminal not found"), { code: "ENOTERM" });
|
|
666
|
-
// Resize PTY
|
|
667
|
-
session.pty.resize(cols, rows);
|
|
668
|
-
session.cols = cols;
|
|
669
|
-
session.rows = rows;
|
|
566
|
+
// No-op for simple terminal - resize not supported without PTY
|
|
670
567
|
return {};
|
|
671
568
|
}
|
|
672
569
|
function handleTerminalKill(payload) {
|
|
@@ -676,7 +573,7 @@ function handleTerminalKill(payload) {
|
|
|
676
573
|
const session = terminals.get(terminalId);
|
|
677
574
|
if (!session)
|
|
678
575
|
throw Object.assign(new Error("Terminal not found"), { code: "ENOTERM" });
|
|
679
|
-
session.
|
|
576
|
+
session.proc.kill();
|
|
680
577
|
terminals.delete(terminalId);
|
|
681
578
|
return {};
|
|
682
579
|
}
|
|
@@ -1473,7 +1370,7 @@ function connectWebSocket(code) {
|
|
|
1473
1370
|
console.log("\nShutting down...");
|
|
1474
1371
|
// Kill all terminals
|
|
1475
1372
|
for (const [id, session] of terminals) {
|
|
1476
|
-
session.
|
|
1373
|
+
session.proc.kill();
|
|
1477
1374
|
}
|
|
1478
1375
|
terminals.clear();
|
|
1479
1376
|
// 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.7",
|
|
4
4
|
"author": [
|
|
5
5
|
{
|
|
6
6
|
"name": "Soham Bharambe",
|
|
@@ -26,7 +26,6 @@
|
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
28
|
"ignore": "^6.0.2",
|
|
29
|
-
"node-pty": "^1.0.0",
|
|
30
29
|
"qrcode-terminal": "^0.12.0",
|
|
31
30
|
"ws": "^8.18.0"
|
|
32
31
|
},
|