pty-shell 1.4.0 → 1.5.0

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,15 @@ 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;
130
+ /**
131
+ * 同步方式执行子进程(用于组合命令),返回退出码
132
+ */
133
+ private spawn_sync;
125
134
  private multiple_line;
126
135
  private spawn_write;
127
136
  private spawn;
package/dist/index.js CHANGED
@@ -767,64 +767,221 @@ 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);
777
- switch (v) {
778
- case exec_type.not:
779
- this.send_and_enter(`not have permission to execute ${exe}`);
780
- this.clear_line();
781
- this.exec_end_call(-1);
782
- return;
783
- case exec_type.auto_child_process:
784
- break;
785
- case exec_type.not_pty:
786
- use_noe_pty = true;
787
- break;
788
- default:
789
- // 未知的不报错也不执行
790
- this.exec_end_call(0);
791
- return;
784
+ const v = yield this.check_exe_cmd(exe_cmd, params);
785
+ if (v === exec_type.not) {
786
+ this.send_and_enter(`not have permission to execute ${exe_cmd}`);
787
+ this.clear_line();
788
+ this.exec_end_call(-1);
789
+ return;
790
+ }
791
+ if (v === exec_type.not_pty) {
792
+ use_noe_pty = true;
792
793
  }
793
794
  }
795
+ // 2. cmd_replace(只调一次)
794
796
  if (this.cmd_replace) {
795
- const r = yield this.cmd_replace(exe, params);
796
- exe = r.exe_cmd;
797
+ const r = yield this.cmd_replace(exe_cmd, params);
798
+ exe_cmd = r.exe_cmd;
797
799
  params = r.params;
798
800
  }
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;
801
+ resolved_list.push({
802
+ exe_cmd,
803
+ params,
804
+ separator: cmd.separator,
805
+ use_noe_pty,
806
+ });
807
+ }
808
+ // 全部校验通过,按分隔符语义串行执行
809
+ let lastExitCode = 0;
810
+ for (let i = 0; i < resolved_list.length; i++) {
811
+ const cmd = resolved_list[i];
812
+ // 根据前一个命令的退出码和分隔符决定是否跳过当前命令
813
+ if (i > 0) {
814
+ const prevSep = resolved_list[i - 1].separator;
815
+ if (prevSep === '&&' && lastExitCode !== 0)
816
+ continue; // 前面失败,跳过
817
+ if (prevSep === '||' && lastExitCode === 0)
818
+ continue; // 前面成功,跳过
806
819
  }
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;
820
+ try {
821
+ if (this.cmd_exec_map.has(cmd.exe_cmd)) {
822
+ yield this.exec_cmd(cmd.exe_cmd, cmd.params);
823
+ lastExitCode = 0;
824
+ }
825
+ else if (this.js_child_map.has(cmd.exe_cmd)) {
826
+ // js_child 异步模式,不支持在组合命令中等待
827
+ if (resolved_list.length > 1) {
828
+ this.send_and_enter(`\x1b[31mjs_child not supported in combined commands: ${cmd.exe_cmd}\x1b[0m`);
829
+ lastExitCode = -1;
830
+ continue;
831
+ }
832
+ const c_ = this.js_child_map.get(cmd.exe_cmd);
833
+ this.js_func_child = new c_(this, () => {
834
+ this.send_and_enter("", true);
835
+ this.next_not_enter = false;
836
+ this.close_child();
837
+ }, (str) => {
838
+ this.send_and_enter(str);
839
+ }, cmd.params);
840
+ this.js_func_child.init();
841
+ this.clear_line();
842
+ return;
843
+ }
844
+ else {
845
+ // 子进程:组合命令需要同步等待,单命令走原有异步
846
+ if (resolved_list.length > 1) {
847
+ lastExitCode = yield this.spawn_sync(cmd.exe_cmd, cmd.params, cmd.use_noe_pty);
848
+ }
849
+ else {
850
+ this.spawn(cmd.exe_cmd, cmd.params, cmd.use_noe_pty);
851
+ }
852
+ }
818
853
  }
819
- else {
820
- this.spawn(exe, params, use_noe_pty);
854
+ catch (e) {
855
+ this.send_and_enter((_a = e.message) !== null && _a !== void 0 ? _a : e);
856
+ lastExitCode = -1;
821
857
  }
822
- this.clear_line();
823
858
  }
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);
859
+ this.clear_line();
860
+ if (commands.length === 1) {
861
+ this.exec_end_call(lastExitCode === 0 ? 0 : -1);
862
+ }
863
+ });
864
+ }
865
+ /**
866
+ * 解析组合命令,按 ; && || 分隔,返回命令列表
867
+ * 支持引号内的分隔符不被拆分
868
+ */
869
+ parse_combined_commands(line) {
870
+ const result = [];
871
+ let current = '';
872
+ let quote = null;
873
+ let i = 0;
874
+ while (i < line.length) {
875
+ const ch = line[i];
876
+ if (ch === '"' || ch === "'") {
877
+ if (quote === null) {
878
+ quote = ch;
879
+ }
880
+ else if (quote === ch) {
881
+ quote = null;
882
+ }
883
+ current += ch;
884
+ i++;
885
+ continue;
886
+ }
887
+ if (quote !== null) {
888
+ current += ch;
889
+ i++;
890
+ continue;
891
+ }
892
+ // &&
893
+ if (ch === '&' && i + 1 < line.length && line[i + 1] === '&') {
894
+ if (current.trim()) {
895
+ const r = this.get_exec(current.trim());
896
+ result.push({ exe_cmd: r.exe, params: r.params, separator: '&&' });
897
+ current = '';
898
+ }
899
+ i += 2;
900
+ continue;
901
+ }
902
+ // ||
903
+ if (ch === '|' && i + 1 < line.length && line[i + 1] === '|') {
904
+ if (current.trim()) {
905
+ const r = this.get_exec(current.trim());
906
+ result.push({ exe_cmd: r.exe, params: r.params, separator: '||' });
907
+ current = '';
908
+ }
909
+ i += 2;
910
+ continue;
911
+ }
912
+ // ;
913
+ if (ch === ';') {
914
+ if (current.trim()) {
915
+ const r = this.get_exec(current.trim());
916
+ result.push({ exe_cmd: r.exe, params: r.params, separator: ';' });
917
+ current = '';
918
+ }
919
+ i++;
920
+ continue;
921
+ }
922
+ current += ch;
923
+ i++;
924
+ }
925
+ if (current.trim()) {
926
+ const r = this.get_exec(current.trim());
927
+ result.push({ exe_cmd: r.exe, params: r.params, separator: '' });
928
+ }
929
+ return result;
930
+ }
931
+ /**
932
+ * 同步方式执行子进程(用于组合命令),返回退出码
933
+ */
934
+ spawn_sync(exe, params, use_noe_pty = false) {
935
+ return new Promise((resolve) => {
936
+ var _a, _b;
937
+ if ((use_noe_pty || ((_a = this.shell_set) === null || _a === void 0 ? void 0 : _a.has("*")) || ((_b = this.shell_set) === null || _b === void 0 ? void 0 : _b.has(exe))) && this.node_pty) {
938
+ this.on_call(`\n\r`);
939
+ this.node_pty_child = this.node_pty.spawn(exe, params, {
940
+ name: 'xterm-color',
941
+ cols: this.cols,
942
+ rows: this.rows,
943
+ cwd: this.cwd,
944
+ useConptyDll: false,
945
+ useConpty: process.env.NODE_ENV !== "production" ? false : undefined,
946
+ env: Object.assign(Object.assign({}, process.env), this.env),
947
+ });
948
+ this.node_pty_child.onData((data) => {
949
+ this.on_call(data.toString());
950
+ if (this.on_call_child_raw) {
951
+ this.on_call_child_raw(data);
952
+ }
953
+ });
954
+ this.node_pty_child.onExit(({ exitCode, signal }) => {
955
+ this.node_pty_child = null;
956
+ resolve(exitCode !== null && exitCode !== void 0 ? exitCode : -1);
957
+ });
958
+ }
959
+ else {
960
+ const child = child_process.spawn(exe, params, {
961
+ cwd: this.cwd,
962
+ env: Object.assign(Object.assign({}, process.env), this.env),
963
+ });
964
+ child.stdout.setEncoding('utf8');
965
+ child.stdout.on('data', (data) => {
966
+ this.send_and_enter(data.toString());
967
+ if (this.on_call_child_raw) {
968
+ this.on_call_child_raw(data);
969
+ }
970
+ });
971
+ child.stderr.on('data', (data) => {
972
+ this.send_and_enter(data.toString());
973
+ this.next_not_enter = false;
974
+ if (this.on_call_child_raw) {
975
+ this.on_call_child_raw(data);
976
+ }
977
+ });
978
+ child.on('exit', (code) => {
979
+ resolve(code !== null && code !== void 0 ? code : -1);
980
+ });
981
+ child.on('error', (error) => {
982
+ this.send_and_enter(error.message);
983
+ resolve(-1);
984
+ });
828
985
  }
829
986
  });
830
987
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pty-shell",
3
- "version": "1.4.0",
3
+ "version": "1.5.0",
4
4
  "description": "a virtual PTY shell for javaScript",
5
5
  "author": "xiaobaidadada",
6
6
  "main": "dist/index.js",