pty-shell 1.4.0 → 1.5.1

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.d.ts CHANGED
@@ -122,6 +122,11 @@ export declare class PtyShell implements PtyShellUserMethod {
122
122
  private push_history_line;
123
123
  private exec_end_call;
124
124
  private parse_exec;
125
+ /**
126
+ * 解析组合命令,按 ; && || 分隔,返回命令列表
127
+ * 支持引号内的分隔符不被拆分
128
+ */
129
+ private parse_combined_commands;
125
130
  private multiple_line;
126
131
  private spawn_write;
127
132
  private spawn;
package/dist/index.js CHANGED
@@ -767,16 +767,24 @@ class PtyShell {
767
767
  this.clear_line();
768
768
  return;
769
769
  }
770
- let { exe, params } = this.get_exec(this.line);
771
770
  this.history_line_index = -1;
772
- try {
771
+ // 解析组合命令(支持 ; && ||),解析出的 exe_cmd/params 来自 get_exec
772
+ const commands = this.parse_combined_commands(this.line);
773
+ if (commands.length === 0) {
774
+ this.send_and_enter("");
775
+ this.clear_line();
776
+ return;
777
+ }
778
+ const resolved_list = [];
779
+ for (const cmd of commands) {
780
+ let { exe_cmd, params } = cmd;
773
781
  let use_noe_pty = false;
782
+ // 1. check_exe_cmd(只调一次)
774
783
  if (this.check_exe_cmd) {
775
- // 检查外部自定义的 是否能执行某个命令
776
- const v = yield this.check_exe_cmd(exe, params);
784
+ const v = yield this.check_exe_cmd(exe_cmd, params);
777
785
  switch (v) {
778
786
  case exec_type.not:
779
- this.send_and_enter(`not have permission to execute ${exe}`);
787
+ this.send_and_enter(`not have permission to execute ${exe_cmd}`);
780
788
  this.clear_line();
781
789
  this.exec_end_call(-1);
782
790
  return;
@@ -791,43 +799,136 @@ class PtyShell {
791
799
  return;
792
800
  }
793
801
  }
802
+ // 2. cmd_replace(只调一次)
794
803
  if (this.cmd_replace) {
795
- const r = yield this.cmd_replace(exe, params);
796
- exe = r.exe_cmd;
804
+ const r = yield this.cmd_replace(exe_cmd, params);
805
+ exe_cmd = r.exe_cmd;
797
806
  params = r.params;
798
807
  }
799
- if (this.cmd_exec_map.has(exe)) {
800
- // 检测某个已经有预处理的命令 包括用户自定义的
801
- yield this.exec_cmd(exe, params);
802
- // 不用再继续了
803
- this.clear_line();
804
- this.exec_end_call(0);
805
- return;
808
+ resolved_list.push({
809
+ exe_cmd,
810
+ params,
811
+ separator: cmd.separator,
812
+ use_noe_pty,
813
+ });
814
+ }
815
+ // 全部校验通过,按分隔符语义串行执行
816
+ let lastExitCode = 0;
817
+ for (let i = 0; i < resolved_list.length; i++) {
818
+ const cmd = resolved_list[i];
819
+ // 根据前一个命令的退出码和分隔符决定是否跳过当前命令
820
+ if (i > 0) {
821
+ const prevSep = resolved_list[i - 1].separator;
822
+ if (prevSep === '&&' && lastExitCode !== 0)
823
+ continue; // 前面失败,跳过
824
+ if (prevSep === '||' && lastExitCode === 0)
825
+ continue; // 前面成功,跳过
806
826
  }
807
- if (this.js_child_map.has(exe)) {
808
- const c_ = this.js_child_map.get(exe);
809
- this.js_func_child = new c_(this, () => {
810
- this.send_and_enter("", true);
811
- this.next_not_enter = false; // 下一次的换行输出 上一次没有换行
812
- this.close_child();
813
- }, (str) => {
814
- this.send_and_enter(str);
815
- }, params);
816
- this.js_func_child.init();
817
- return;
827
+ try {
828
+ if (this.cmd_exec_map.has(cmd.exe_cmd)) {
829
+ yield this.exec_cmd(cmd.exe_cmd, cmd.params);
830
+ lastExitCode = 0;
831
+ }
832
+ else if (this.js_child_map.has(cmd.exe_cmd)) {
833
+ // js_child 异步模式,不支持在组合命令中等待
834
+ if (resolved_list.length > 1) {
835
+ this.send_and_enter(`\x1b[31mjs_child not supported in combined commands: ${cmd.exe_cmd}\x1b[0m`);
836
+ lastExitCode = -1;
837
+ continue;
838
+ }
839
+ const c_ = this.js_child_map.get(cmd.exe_cmd);
840
+ this.js_func_child = new c_(this, () => {
841
+ this.send_and_enter("", true);
842
+ this.next_not_enter = false;
843
+ this.close_child();
844
+ }, (str) => {
845
+ this.send_and_enter(str);
846
+ }, cmd.params);
847
+ this.js_func_child.init();
848
+ this.clear_line();
849
+ return;
850
+ }
851
+ else {
852
+ // 子进程:组合命令需要同步等待,单命令走原有异步
853
+ lastExitCode = yield this.spawn(cmd.exe_cmd, cmd.params, cmd.use_noe_pty);
854
+ }
818
855
  }
819
- else {
820
- this.spawn(exe, params, use_noe_pty);
856
+ catch (e) {
857
+ this.send_and_enter((_a = e.message) !== null && _a !== void 0 ? _a : e);
858
+ lastExitCode = -1;
821
859
  }
822
- this.clear_line();
823
- }
824
- catch (e) {
825
- // console.log("子线程执行异常", e);
826
- this.send_and_enter((_a = e.message) !== null && _a !== void 0 ? _a : e);
827
- this.exec_end_call(-1);
828
860
  }
861
+ this.clear_line();
862
+ this.next_not_enter = false; // 下一次的换行输出 上一次没有换行
863
+ this.close_child(lastExitCode);
829
864
  });
830
865
  }
866
+ /**
867
+ * 解析组合命令,按 ; && || 分隔,返回命令列表
868
+ * 支持引号内的分隔符不被拆分
869
+ */
870
+ parse_combined_commands(line) {
871
+ const result = [];
872
+ let current = '';
873
+ let quote = null;
874
+ let i = 0;
875
+ while (i < line.length) {
876
+ const ch = line[i];
877
+ if (ch === '"' || ch === "'") {
878
+ if (quote === null) {
879
+ quote = ch;
880
+ }
881
+ else if (quote === ch) {
882
+ quote = null;
883
+ }
884
+ current += ch;
885
+ i++;
886
+ continue;
887
+ }
888
+ if (quote !== null) {
889
+ current += ch;
890
+ i++;
891
+ continue;
892
+ }
893
+ // &&
894
+ if (ch === '&' && i + 1 < line.length && line[i + 1] === '&') {
895
+ if (current.trim()) {
896
+ const r = this.get_exec(current.trim());
897
+ result.push({ exe_cmd: r.exe, params: r.params, separator: '&&' });
898
+ current = '';
899
+ }
900
+ i += 2;
901
+ continue;
902
+ }
903
+ // ||
904
+ if (ch === '|' && i + 1 < line.length && line[i + 1] === '|') {
905
+ if (current.trim()) {
906
+ const r = this.get_exec(current.trim());
907
+ result.push({ exe_cmd: r.exe, params: r.params, separator: '||' });
908
+ current = '';
909
+ }
910
+ i += 2;
911
+ continue;
912
+ }
913
+ // ;
914
+ if (ch === ';') {
915
+ if (current.trim()) {
916
+ const r = this.get_exec(current.trim());
917
+ result.push({ exe_cmd: r.exe, params: r.params, separator: ';' });
918
+ current = '';
919
+ }
920
+ i++;
921
+ continue;
922
+ }
923
+ current += ch;
924
+ i++;
925
+ }
926
+ if (current.trim()) {
927
+ const r = this.get_exec(current.trim());
928
+ result.push({ exe_cmd: r.exe, params: r.params, separator: '' });
929
+ }
930
+ return result;
931
+ }
831
932
  multiple_line(data, enter_index) {
832
933
  return __awaiter(this, void 0, void 0, function* () {
833
934
  if (!data)
@@ -862,86 +963,94 @@ class PtyShell {
862
963
  this.js_func_child.write(str);
863
964
  }
864
965
  }
865
- spawn(exe, params, use_noe_pty = true) {
866
- // if (this.not_use_node_pre_cmd_exec) {
867
- // this.send_and_enter(`not_use_node_pre_cmd_exec is true`);
868
- // return;
869
- // }
870
- // this.send_and_enter(""); //
871
- // if (!this.child) {
872
- // this.on_call(`\n\r`); // 先换个行
873
- // }
874
- if ((use_noe_pty || this.shell_set.has("*") || this.shell_set.has(exe)) && this.node_pty) {
875
- // if (!exe.includes('exe') && exe !== 'bash' && exe !== 'sh') {
876
- // exe += '.exe';
966
+ spawn(exe_1, params_1) {
967
+ return __awaiter(this, arguments, void 0, function* (exe, params, use_noe_pty = true) {
968
+ // if (this.not_use_node_pre_cmd_exec) {
969
+ // this.send_and_enter(`not_use_node_pre_cmd_exec is true`);
970
+ // return;
877
971
  // }
878
- this.on_call(`\n\r`); // 先换个行
879
- this.node_pty_child = this.node_pty.spawn(exe, params, {
880
- name: 'xterm-color',
881
- cols: this.cols,
882
- rows: this.rows,
883
- cwd: this.cwd, // 设置子进程的工作目录
884
- useConptyDll: false,
885
- useConpty: process.env.NODE_ENV !== "production" ? false : undefined, // conpty 可以支持 bash 等命令 从 Windows 10 版本 1809 开始提供 , 但是如果使用了 powershell 这个也就没有必要了,而且设置为false才能使用debug模式运行
886
- env: Object.assign(Object.assign({}, process.env), this.env), // 传递环境变量
887
- });
888
- this.node_pty_child.onData((data) => {
889
- this.on_call(data.toString());
890
- if (this.on_call_child_raw) {
891
- this.on_call_child_raw(data);
892
- }
893
- });
894
- this.node_pty_child.onExit(({ exitCode, signal }) => {
895
- this.close_child(exitCode);
896
- this.send_and_enter("");
897
- // this.send_and_enter(`pty with ${exitCode}`);
898
- this.next_not_enter = false; // 下一次的换行输出 上一次没有换行
899
- });
900
- // this.is_pty = true;
901
- }
902
- else {
903
- // this.is_pty = false;
904
- // 其他的没有必要再创建一个 tty 都是资源消耗
905
- this.child = child_process.spawn(exe, params, {
906
- // shell:getShell(),
907
- cwd: this.cwd, // 设置子进程的工作目录
908
- env: Object.assign(Object.assign(Object.assign({}, process.env), this.env), { LANG: 'en_US.UTF-8' }), // 传递环境变量
909
- // stdio: 'inherit' // 让子进程的输入输出与父进程共享 pipe ignore inherit
910
- // timeout: 5000, // 设置超时为 5 秒
911
- // maxBuffer: 1024 * 1024 * 10// 设置缓冲区为 10 MB
912
- });
913
- // 设置编码为 'utf8',确保输出按 UTF-8 编码解析
914
- this.child.stdout.setEncoding('utf8');
915
- this.child.stdout.on('data', (data) => {
916
- // const v = data.toString(); // 子程序没有换行等符号不会立即输出 有缓冲区
917
- this.send_and_enter(data.toString());
918
- if (this.on_call_child_raw) {
919
- this.on_call_child_raw(data);
972
+ // this.send_and_enter(""); //
973
+ // if (!this.child) {
974
+ // this.on_call(`\n\r`); // 先换个行
975
+ // }
976
+ return new Promise(resolve => {
977
+ if ((use_noe_pty || this.shell_set.has("*") || this.shell_set.has(exe)) && this.node_pty) {
978
+ // if (!exe.includes('exe') && exe !== 'bash' && exe !== 'sh') {
979
+ // exe += '.exe';
980
+ // }
981
+ this.on_call(`\n\r`); // 先换个行
982
+ this.node_pty_child = this.node_pty.spawn(exe, params, {
983
+ name: 'xterm-color',
984
+ cols: this.cols,
985
+ rows: this.rows,
986
+ cwd: this.cwd, // 设置子进程的工作目录
987
+ useConptyDll: false,
988
+ useConpty: process.env.NODE_ENV !== "production" ? false : undefined, // conpty 可以支持 bash 等命令 从 Windows 10 版本 1809 开始提供 , 但是如果使用了 powershell 这个也就没有必要了,而且设置为false才能使用debug模式运行
989
+ env: Object.assign(Object.assign({}, process.env), this.env), // 传递环境变量
990
+ });
991
+ this.node_pty_child.onData((data) => {
992
+ this.on_call(data.toString());
993
+ if (this.on_call_child_raw) {
994
+ this.on_call_child_raw(data);
995
+ }
996
+ });
997
+ this.node_pty_child.onExit(({ exitCode, signal }) => {
998
+ // this.close_child(exitCode);
999
+ // this.send_and_enter("");
1000
+ this.send_and_enter(`pty with ${exitCode}`);
1001
+ // this.next_not_enter = false; // 下一次的换行输出 上一次没有换行
1002
+ resolve(exitCode !== null && exitCode !== void 0 ? exitCode : -1);
1003
+ });
1004
+ // this.is_pty = true;
920
1005
  }
921
- });
922
- this.child.stderr.on('data', (data) => {
923
- // const v = data.toString();
924
- this.send_and_enter(data.toString());
925
- this.next_not_enter = false; // 下一次的换行输出 上一次没有换行
926
- if (this.on_call_child_raw) {
927
- this.on_call_child_raw(data);
1006
+ else {
1007
+ // this.is_pty = false;
1008
+ // 其他的没有必要再创建一个 tty 都是资源消耗
1009
+ this.child = child_process.spawn(exe, params, {
1010
+ // shell:getShell(),
1011
+ cwd: this.cwd, // 设置子进程的工作目录
1012
+ env: Object.assign(Object.assign(Object.assign({}, process.env), this.env), { LANG: 'en_US.UTF-8' }), // 传递环境变量
1013
+ // stdio: 'inherit' // 让子进程的输入输出与父进程共享 pipe ignore inherit
1014
+ // timeout: 5000, // 设置超时为 5 秒
1015
+ // maxBuffer: 1024 * 1024 * 10// 设置缓冲区为 10 MB
1016
+ });
1017
+ // 设置编码为 'utf8',确保输出按 UTF-8 编码解析
1018
+ this.child.stdout.setEncoding('utf8');
1019
+ this.child.stdout.on('data', (data) => {
1020
+ // const v = data.toString(); // 子程序没有换行等符号不会立即输出 有缓冲区
1021
+ this.send_and_enter(data.toString());
1022
+ if (this.on_call_child_raw) {
1023
+ this.on_call_child_raw(data);
1024
+ }
1025
+ });
1026
+ this.child.stderr.on('data', (data) => {
1027
+ // const v = data.toString();
1028
+ this.send_and_enter(data.toString());
1029
+ this.next_not_enter = false; // 下一次的换行输出 上一次没有换行
1030
+ if (this.on_call_child_raw) {
1031
+ this.on_call_child_raw(data);
1032
+ }
1033
+ });
1034
+ this.child.on('exit', (code) => {
1035
+ // this.close_child(code);
1036
+ // if (code !== 1) {
1037
+ this.send_and_enter(`process exited with code ${code}`);
1038
+ // }
1039
+ // else {
1040
+ // this.send_and_enter(``);
1041
+ // }
1042
+ // this.next_not_enter = false; // 下一次的换行输出 上一次没有换行
1043
+ resolve(code !== null && code !== void 0 ? code : -1);
1044
+ });
1045
+ this.child.on('error', (error) => {
1046
+ // this.next_not_enter = false; // 下一次的换行输出 上一次没有换行
1047
+ // this.close_child(-1);
1048
+ this.send_and_enter(error.message);
1049
+ resolve(-1);
1050
+ });
928
1051
  }
929
1052
  });
930
- this.child.on('exit', (code) => {
931
- this.close_child(code);
932
- // if (code !== 1) {
933
- // this.send_and_enter(`process exited with code ${code}`);
934
- // } else {
935
- this.send_and_enter(``);
936
- // }
937
- this.next_not_enter = false; // 下一次的换行输出 上一次没有换行
938
- });
939
- this.child.on('error', (error) => {
940
- this.next_not_enter = false; // 下一次的换行输出 上一次没有换行
941
- this.close_child(-1);
942
- this.send_and_enter(error.message);
943
- });
944
- }
1053
+ });
945
1054
  }
946
1055
  exec_cmd(exe, params) {
947
1056
  return __awaiter(this, void 0, void 0, function* () {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pty-shell",
3
- "version": "1.4.0",
3
+ "version": "1.5.1",
4
4
  "description": "a virtual PTY shell for javaScript",
5
5
  "author": "xiaobaidadada",
6
6
  "main": "dist/index.js",