pty-shell 1.3.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
@@ -82,6 +82,9 @@ export declare class PtyShell implements PtyShellUserMethod {
82
82
  private history_line;
83
83
  private history_line_index;
84
84
  private history_line_max;
85
+ private _not_write;
86
+ get not_write(): boolean;
87
+ set not_write(value: boolean);
85
88
  /**
86
89
  * public method
87
90
  */
@@ -119,6 +122,15 @@ export declare class PtyShell implements PtyShellUserMethod {
119
122
  private push_history_line;
120
123
  private exec_end_call;
121
124
  private parse_exec;
125
+ /**
126
+ * 解析组合命令,按 ; && || 分隔,返回命令列表
127
+ * 支持引号内的分隔符不被拆分
128
+ */
129
+ private parse_combined_commands;
130
+ /**
131
+ * 同步方式执行子进程(用于组合命令),返回退出码
132
+ */
133
+ private spawn_sync;
122
134
  private multiple_line;
123
135
  private spawn_write;
124
136
  private spawn;
package/dist/index.js CHANGED
@@ -138,6 +138,7 @@ class PtyShell {
138
138
  this.history_line = [];
139
139
  this.history_line_index = -1;
140
140
  this.history_line_max = 20;
141
+ this._not_write = false;
141
142
  this.next_not_enter = false;
142
143
  this.reset_option(param);
143
144
  this.on_call(this.raw_prompt);
@@ -149,6 +150,7 @@ class PtyShell {
149
150
  const p = path.isAbsolute(params[0]) ? params[0] : path.join(this.cwd, params[0]);
150
151
  if (!fs.existsSync(p)) {
151
152
  this.send_and_enter(`not directory ${p}`);
153
+ return;
152
154
  }
153
155
  this.cwd = p;
154
156
  this.send_and_enter(``);
@@ -178,6 +180,12 @@ class PtyShell {
178
180
  }
179
181
  return v;
180
182
  }
183
+ get not_write() {
184
+ return this._not_write;
185
+ }
186
+ set not_write(value) {
187
+ this._not_write = value;
188
+ }
181
189
  /**
182
190
  * public method
183
191
  */
@@ -252,8 +260,10 @@ class PtyShell {
252
260
  */
253
261
  write(data) {
254
262
  return __awaiter(this, void 0, void 0, function* () {
255
- if (this.have_child()) {
256
- // 终端shell 完全 托管给 别的程序
263
+ if (this._not_write)
264
+ return;
265
+ if (this.node_pty_child != null) {
266
+ // 终端shell 完全 托管给 别的程序 只有 pty 的子程序可以 其他 需要pty-shell充当编辑器
257
267
  this.spawn_write(data);
258
268
  return;
259
269
  }
@@ -362,6 +372,7 @@ class PtyShell {
362
372
  }
363
373
  send_and_enter(str, send_prompt = false) {
364
374
  try {
375
+ const have_child = this.have_child();
365
376
  if (typeof str == "string") {
366
377
  const list = [];
367
378
  let i = 0;
@@ -381,7 +392,7 @@ class PtyShell {
381
392
  }
382
393
  if (i === 0 || i !== last_i)
383
394
  list.push(str.substring(i));
384
- if (this.have_child()) {
395
+ if (have_child) {
385
396
  // 添加子进程的提示换行
386
397
  this.child_now_line = list[list.length - 1];
387
398
  }
@@ -396,7 +407,7 @@ class PtyShell {
396
407
  }
397
408
  this.next_not_enter = str.endsWith('\n\r') || str.endsWith('\r\n'); // 下一次不用换行了
398
409
  }
399
- if (!this.have_child() || send_prompt) {
410
+ if (!have_child || send_prompt) {
400
411
  this.on_call(`${this.enter_prompt}`);
401
412
  }
402
413
  this.clear_line();
@@ -408,8 +419,9 @@ class PtyShell {
408
419
  // 重新更新显示本行 也许可以更节省的更新 文本 powershell 这样的每次都是全部更新 暂时和他一样
409
420
  update_line(param) {
410
421
  var _a, _b;
411
- const prompt = !this.child ? this.raw_prompt : this.child_now_line;
412
- let len = (!this.child ? this.prompt_call_len : char_util_1.CharUtil.get_full_char_num(prompt)) + this.line_char_index; // 字符串前面的字符数量
422
+ const have_child = this.have_child();
423
+ const prompt = !have_child ? this.raw_prompt : this.child_now_line;
424
+ let len = (!have_child ? this.prompt_call_len : char_util_1.CharUtil.get_full_char_num(prompt)) + this.line_char_index; // 字符串前面的字符数量
413
425
  if (param && param.line_add_num) {
414
426
  len += param.line_add_num;
415
427
  }
@@ -566,7 +578,7 @@ class PtyShell {
566
578
  this.cancel_selected();
567
579
  this.update_line({ line_add_num: 1 });
568
580
  }
569
- else if (this.child) {
581
+ else if (this.have_child()) {
570
582
  this.close_child(0);
571
583
  return;
572
584
  }
@@ -741,6 +753,7 @@ class PtyShell {
741
753
  // 解析和执行命令 执行完会自动换行的
742
754
  parse_exec() {
743
755
  return __awaiter(this, void 0, void 0, function* () {
756
+ var _a;
744
757
  // const line = this.delete_all_enter(this.line);
745
758
  if (!this.line && !this.have_child()) {
746
759
  this.send_and_enter("");
@@ -748,61 +761,227 @@ class PtyShell {
748
761
  return;
749
762
  }
750
763
  this.push_history_line(this.line);
751
- let { exe, params } = this.get_exec(this.line);
764
+ if (this.have_child()) {
765
+ // 把数据给正在运行的别的程序 但是肯定不是 pty 的pty 不需要pty-shell 做命令编辑器
766
+ this.spawn_write(`${this.line}\n`);
767
+ this.clear_line();
768
+ return;
769
+ }
752
770
  this.history_line_index = -1;
753
- 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;
754
781
  let use_noe_pty = false;
782
+ // 1. check_exe_cmd(只调一次)
755
783
  if (this.check_exe_cmd) {
756
- // 检查外部自定义的 是否能执行某个命令
757
- const v = yield this.check_exe_cmd(exe, params);
758
- switch (v) {
759
- case exec_type.not:
760
- this.send_and_enter(`not have permission to execute ${exe}`);
761
- this.clear_line();
762
- this.exec_end_call(-1);
763
- return;
764
- case exec_type.auto_child_process:
765
- break;
766
- case exec_type.not_pty:
767
- use_noe_pty = true;
768
- break;
769
- default:
770
- // 未知的不报错也不执行
771
- this.exec_end_call(0);
772
- 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;
773
793
  }
774
794
  }
795
+ // 2. cmd_replace(只调一次)
775
796
  if (this.cmd_replace) {
776
- const r = yield this.cmd_replace(exe, params);
777
- exe = r.exe_cmd;
797
+ const r = yield this.cmd_replace(exe_cmd, params);
798
+ exe_cmd = r.exe_cmd;
778
799
  params = r.params;
779
800
  }
780
- if (this.cmd_exec_map.has(exe)) {
781
- // 检测某个已经有预处理的命令 包括用户自定义的
782
- yield this.exec_cmd(exe, params);
783
- // 不用再继续了
784
- this.clear_line();
785
- this.exec_end_call(0);
786
- 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; // 前面成功,跳过
819
+ }
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
+ }
853
+ }
854
+ catch (e) {
855
+ this.send_and_enter((_a = e.message) !== null && _a !== void 0 ? _a : e);
856
+ lastExitCode = -1;
787
857
  }
788
- if (this.js_child_map.has(exe)) {
789
- const c_ = this.js_child_map.get(exe);
790
- this.js_func_child = new c_(() => {
791
- this.send_and_enter("");
792
- this.next_not_enter = false; // 下一次的换行输出 上一次没有换行
793
- this.close_child();
794
- }, (str) => {
795
- this.on_call(str);
796
- }, params);
797
- return;
858
+ }
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;
798
879
  }
799
- this.spawn(exe, params, use_noe_pty);
800
- this.clear_line();
880
+ else if (quote === ch) {
881
+ quote = null;
882
+ }
883
+ current += ch;
884
+ i++;
885
+ continue;
801
886
  }
802
- catch (e) {
803
- // console.log("子线程执行异常", e);
804
- this.send_and_enter(e.message);
805
- this.exec_end_call(-1);
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
+ });
806
985
  }
807
986
  });
808
987
  }
package/dist/type.d.ts CHANGED
@@ -1,5 +1,7 @@
1
+ import { PtyShell } from "./index";
1
2
  export interface child_func_type {
2
3
  write(str: string): void;
3
4
  kill(): void;
5
+ init(): void;
4
6
  }
5
- export type child_func_constructor = new (exit_on: () => void, send: (str: string) => void, params: string[]) => child_func_type;
7
+ export type child_func_constructor = new (pty_shell: PtyShell, exit_on: () => void, send: (str: string) => void, params: string[]) => child_func_type;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pty-shell",
3
- "version": "1.3.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",