pty-shell 1.2.2 → 1.4.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/README.md +1 -1
- package/dist/char.util.d.ts +5 -0
- package/dist/char.util.js +49 -0
- package/dist/index.d.ts +15 -14
- package/dist/index.js +170 -152
- package/dist/type.d.ts +7 -0
- package/dist/type.js +2 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# pty-shell
|
|
2
2
|
This is a virtual PTY shell that can be used to filter or log executable commands. It can also be used in a browser environment. Currently, it does not support features like piping. It supports the use of `node-pty`. For more usage examples, you can refer to the code in the [filecat](https://github.com/xiaobaidadada/filecat) project.
|
|
3
3
|
It needs to be used in combination with projects similar to xterm.js.
|
|
4
|
-
这是一个虚拟的pty shell ,可以用来过滤或者记录可执行命令,
|
|
4
|
+
这是一个虚拟的pty shell ,可以用来过滤或者记录可执行命令, 只支持node环境,不支持浏览器, 目前并不支持管道等功能,支持使用 node-pty ,更多使用例子目前可以参考 filecat(https://github.com/xiaobaidadada/filecat)项目中的代码 。
|
|
5
5
|
需要配合 xterm.js 类似的项目组合使用
|
|
6
6
|
# Sample Example
|
|
7
7
|
```js
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CharUtil = void 0;
|
|
4
|
+
class CharUtil {
|
|
5
|
+
// 判断一个字符是全角还是半角
|
|
6
|
+
static isFullCharWidth(char) {
|
|
7
|
+
// 计算字符的 UTF-8 编码字节长度
|
|
8
|
+
const byteLength = Buffer.byteLength(char, 'utf8');
|
|
9
|
+
// 如果字符的字节长度大于 1,说明是全角字符
|
|
10
|
+
return byteLength > 1;
|
|
11
|
+
}
|
|
12
|
+
// 从start_index往前多少个位置获取指定数量的 半角 字符(宽字符算两个)
|
|
13
|
+
static readFullCharIndex(str, start_index, len) {
|
|
14
|
+
if (!str)
|
|
15
|
+
return 0;
|
|
16
|
+
if (start_index >= str.length)
|
|
17
|
+
return 0;
|
|
18
|
+
let num = 0;
|
|
19
|
+
let char_num = 0;
|
|
20
|
+
for (let i = start_index; i < str.length; i++) {
|
|
21
|
+
if (this.isFullCharWidth(str[i])) {
|
|
22
|
+
num += 2;
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
num++;
|
|
26
|
+
}
|
|
27
|
+
char_num++;
|
|
28
|
+
if (num >= len)
|
|
29
|
+
return char_num;
|
|
30
|
+
}
|
|
31
|
+
return char_num;
|
|
32
|
+
}
|
|
33
|
+
// 获取字符串中有多少个 字符(将宽字符统计成两个)
|
|
34
|
+
static get_full_char_num(str) {
|
|
35
|
+
if (!str)
|
|
36
|
+
return 0;
|
|
37
|
+
let char_num = 0;
|
|
38
|
+
for (let i = 0; i < str.length; i++) {
|
|
39
|
+
if (this.isFullCharWidth(str[i])) {
|
|
40
|
+
char_num += 2;
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
char_num++;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return char_num;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
exports.CharUtil = CharUtil;
|
package/dist/index.d.ts
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* 读取输出与写入输入:捕获输出并发送输入,模拟用户交互。
|
|
9
9
|
* 信号和控制字符支持:处理回车、换行等控制字符,并支持信号转发。
|
|
10
10
|
*/
|
|
11
|
+
import { child_func_constructor } from "./type";
|
|
11
12
|
export declare enum exec_type {
|
|
12
13
|
not = -1,// 不能执行
|
|
13
14
|
auto_child_process = 0,// 使用内置子线程执行(除了cd命令)
|
|
@@ -32,7 +33,6 @@ interface PtyShellUserMethod {
|
|
|
32
33
|
}
|
|
33
34
|
interface Param extends Partial<PtyShellUserMethod> {
|
|
34
35
|
cwd: string;
|
|
35
|
-
not_use_node_pre_cmd_exec?: boolean;
|
|
36
36
|
node_pty?: any;
|
|
37
37
|
cols?: number;
|
|
38
38
|
rows?: number;
|
|
@@ -41,7 +41,7 @@ interface Param extends Partial<PtyShellUserMethod> {
|
|
|
41
41
|
history_line?: string[];
|
|
42
42
|
history_line_max?: number;
|
|
43
43
|
}
|
|
44
|
-
type CmdHandler = (params: string[], send_prompt?: (
|
|
44
|
+
type CmdHandler = (params: string[], send_prompt?: (str: string, send_prompt?: boolean) => void) => Promise<void>;
|
|
45
45
|
export declare class PtyShell implements PtyShellUserMethod {
|
|
46
46
|
rows: number;
|
|
47
47
|
cols: number;
|
|
@@ -49,7 +49,7 @@ export declare class PtyShell implements PtyShellUserMethod {
|
|
|
49
49
|
env: {};
|
|
50
50
|
constructor(param: Param);
|
|
51
51
|
cmd_params_auto_completion(param_str: any): string | undefined;
|
|
52
|
-
cmd_exe_auto_completion:
|
|
52
|
+
cmd_exe_auto_completion: (str: string) => string;
|
|
53
53
|
on_prompt_call: (cwd: any) => {
|
|
54
54
|
str: string;
|
|
55
55
|
char_num: number;
|
|
@@ -63,18 +63,17 @@ export declare class PtyShell implements PtyShellUserMethod {
|
|
|
63
63
|
exe_cmd: string;
|
|
64
64
|
params: string[];
|
|
65
65
|
}>;
|
|
66
|
-
private cmd_set;
|
|
67
66
|
private shell_set;
|
|
68
|
-
private node_require;
|
|
69
|
-
private not_use_node_pre_cmd_exec;
|
|
70
67
|
private cmd_exec_map;
|
|
68
|
+
private js_child_map;
|
|
71
69
|
private prompt_call_len;
|
|
72
70
|
private is_running;
|
|
73
71
|
private child_now_line;
|
|
74
72
|
private word_detection;
|
|
75
73
|
private node_pty;
|
|
76
74
|
private child;
|
|
77
|
-
private
|
|
75
|
+
private node_pty_child;
|
|
76
|
+
private js_func_child;
|
|
78
77
|
private line;
|
|
79
78
|
private line_index;
|
|
80
79
|
private select_line;
|
|
@@ -83,11 +82,15 @@ export declare class PtyShell implements PtyShellUserMethod {
|
|
|
83
82
|
private history_line;
|
|
84
83
|
private history_line_index;
|
|
85
84
|
private history_line_max;
|
|
85
|
+
private _not_write;
|
|
86
|
+
get not_write(): boolean;
|
|
87
|
+
set not_write(value: boolean);
|
|
86
88
|
/**
|
|
87
89
|
* public method
|
|
88
90
|
*/
|
|
89
91
|
reset_option(param: Param): void;
|
|
90
92
|
add_cmd_handle(exe_cmd: string, handle: CmdHandler): void;
|
|
93
|
+
add_js_child(name: string, child: child_func_constructor): void;
|
|
91
94
|
close(): void;
|
|
92
95
|
kill(): void;
|
|
93
96
|
/**
|
|
@@ -100,12 +103,6 @@ export declare class PtyShell implements PtyShellUserMethod {
|
|
|
100
103
|
* @param data
|
|
101
104
|
*/
|
|
102
105
|
write(data: string): Promise<void>;
|
|
103
|
-
/**
|
|
104
|
-
* static method
|
|
105
|
-
*/
|
|
106
|
-
static isFullCharWidth(char: any): boolean;
|
|
107
|
-
static readFullCharIndex(str: string, start_index: number, len: number): number;
|
|
108
|
-
static get_full_char_num(str: string): number;
|
|
109
106
|
/**
|
|
110
107
|
* private method
|
|
111
108
|
*/
|
|
@@ -116,6 +113,7 @@ export declare class PtyShell implements PtyShellUserMethod {
|
|
|
116
113
|
private insert_line;
|
|
117
114
|
private close_child;
|
|
118
115
|
private next_not_enter;
|
|
116
|
+
private have_child;
|
|
119
117
|
private send_and_enter;
|
|
120
118
|
private update_line;
|
|
121
119
|
private cancel_selected;
|
|
@@ -139,4 +137,7 @@ export declare class PtyShell implements PtyShellUserMethod {
|
|
|
139
137
|
private removeCharacterAt;
|
|
140
138
|
private delete_all_enter;
|
|
141
139
|
}
|
|
142
|
-
export
|
|
140
|
+
export * from "./type";
|
|
141
|
+
export * from "./char.util";
|
|
142
|
+
export * from "./path_util";
|
|
143
|
+
export * from "./word_detection_js";
|
package/dist/index.js
CHANGED
|
@@ -9,6 +9,42 @@
|
|
|
9
9
|
* 读取输出与写入输入:捕获输出并发送输入,模拟用户交互。
|
|
10
10
|
* 信号和控制字符支持:处理回车、换行等控制字符,并支持信号转发。
|
|
11
11
|
*/
|
|
12
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
13
|
+
if (k2 === undefined) k2 = k;
|
|
14
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
15
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
16
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
17
|
+
}
|
|
18
|
+
Object.defineProperty(o, k2, desc);
|
|
19
|
+
}) : (function(o, m, k, k2) {
|
|
20
|
+
if (k2 === undefined) k2 = k;
|
|
21
|
+
o[k2] = m[k];
|
|
22
|
+
}));
|
|
23
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
24
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
25
|
+
}) : function(o, v) {
|
|
26
|
+
o["default"] = v;
|
|
27
|
+
});
|
|
28
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
29
|
+
var ownKeys = function(o) {
|
|
30
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
31
|
+
var ar = [];
|
|
32
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
33
|
+
return ar;
|
|
34
|
+
};
|
|
35
|
+
return ownKeys(o);
|
|
36
|
+
};
|
|
37
|
+
return function (mod) {
|
|
38
|
+
if (mod && mod.__esModule) return mod;
|
|
39
|
+
var result = {};
|
|
40
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
41
|
+
__setModuleDefault(result, mod);
|
|
42
|
+
return result;
|
|
43
|
+
};
|
|
44
|
+
})();
|
|
45
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
46
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
47
|
+
};
|
|
12
48
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
13
49
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
14
50
|
return new (P || (P = Promise))(function (resolve, reject) {
|
|
@@ -22,6 +58,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
22
58
|
exports.PtyShell = exports.exec_cmd_type = exports.exec_type = void 0;
|
|
23
59
|
const word_detection_js_1 = require("./word_detection_js");
|
|
24
60
|
const path_util_1 = require("./path_util");
|
|
61
|
+
const char_util_1 = require("./char.util");
|
|
62
|
+
const child_process = __importStar(require("node:child_process"));
|
|
63
|
+
const fs = require("fs");
|
|
64
|
+
const path = require("path");
|
|
65
|
+
// const child_process = require("child_process");
|
|
25
66
|
// import {SystemUtil} from "../sys/sys.utl";
|
|
26
67
|
/**
|
|
27
68
|
* 功能说明:
|
|
@@ -32,7 +73,7 @@ const path_util_1 = require("./path_util");
|
|
|
32
73
|
* 5. 对于特殊的 shell 命令 会使用 node-pty 来执行 并让shell托管所有的输入输出数据
|
|
33
74
|
* 6. 使用了shell: true 参数 系统的默认shell可以支持管道等操作,还可以支持程序路劲查找的功能 但是这样无法支持 | 这样左边命令的校验了, todo 暂时取消这个功能。以后再添加 不能使用原生的shell因为不知道会有什么特殊语法从而跳过命令校验
|
|
34
75
|
*/
|
|
35
|
-
const cmd_list = ['ls', 'cd', 'pwd']; // 仅支持这三个内置命令 cd 命令是唯一支持参数的
|
|
76
|
+
// const cmd_list = ['ls', 'cd', 'pwd']; // 仅支持这三个内置命令 cd 命令是唯一支持参数的
|
|
36
77
|
/**
|
|
37
78
|
* \r 是回车 光标移动到最右边 \n 是换行,当前位置下一行 和\x1b[1B 作用一样
|
|
38
79
|
* \x1b 是 ESC 后面跟着控制字符
|
|
@@ -79,19 +120,16 @@ class PtyShell {
|
|
|
79
120
|
this.env = {};
|
|
80
121
|
this.on_prompt_call = (cwd) => {
|
|
81
122
|
const str = `${cwd}:# `;
|
|
82
|
-
return { str, char_num:
|
|
123
|
+
return { str, char_num: char_util_1.CharUtil.get_full_char_num(str) };
|
|
83
124
|
};
|
|
84
125
|
this.on_call = (data) => {
|
|
85
126
|
};
|
|
86
127
|
this.on_control_cmd = (type, data) => {
|
|
87
128
|
};
|
|
88
|
-
this.cmd_set = new Set(cmd_list);
|
|
89
|
-
this.node_require = {};
|
|
90
|
-
this.not_use_node_pre_cmd_exec = false;
|
|
91
129
|
this.cmd_exec_map = new Map();
|
|
130
|
+
this.js_child_map = new Map();
|
|
92
131
|
this.is_running = true;
|
|
93
132
|
this.child_now_line = '';
|
|
94
|
-
this.is_pty = false;
|
|
95
133
|
this.line = "";
|
|
96
134
|
this.line_index = -1; // 当前指针在 某个字符(后面)
|
|
97
135
|
this.select_line = "";
|
|
@@ -100,22 +138,34 @@ class PtyShell {
|
|
|
100
138
|
this.history_line = [];
|
|
101
139
|
this.history_line_index = -1;
|
|
102
140
|
this.history_line_max = 20;
|
|
141
|
+
this._not_write = false;
|
|
103
142
|
this.next_not_enter = false;
|
|
104
143
|
this.reset_option(param);
|
|
105
|
-
if (!this.not_use_node_pre_cmd_exec) {
|
|
106
|
-
this.node_require.fs = require("fs");
|
|
107
|
-
this.node_require.path = require("path");
|
|
108
|
-
this.node_require.child_process = require('child_process');
|
|
109
|
-
}
|
|
110
144
|
this.on_call(this.raw_prompt);
|
|
145
|
+
// 添加自定义处理方法
|
|
146
|
+
this.add_cmd_handle("pwd", (params, send) => __awaiter(this, void 0, void 0, function* () {
|
|
147
|
+
this.send_and_enter(`${this.cwd}`);
|
|
148
|
+
}));
|
|
149
|
+
this.add_cmd_handle("cd", (params, send) => __awaiter(this, void 0, void 0, function* () {
|
|
150
|
+
const p = path.isAbsolute(params[0]) ? params[0] : path.join(this.cwd, params[0]);
|
|
151
|
+
if (!fs.existsSync(p)) {
|
|
152
|
+
this.send_and_enter(`not directory ${p}`);
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
this.cwd = p;
|
|
156
|
+
this.send_and_enter(``);
|
|
157
|
+
this.word_detection = undefined; // 清空检测器
|
|
158
|
+
}));
|
|
159
|
+
this.add_cmd_handle("ls", (params, send) => __awaiter(this, void 0, void 0, function* () {
|
|
160
|
+
var _a;
|
|
161
|
+
const items = fs.readdirSync((_a = params === null || params === void 0 ? void 0 : params[0]) !== null && _a !== void 0 ? _a : this.cwd); // 读取目录内容
|
|
162
|
+
const v = this.cols_handle(" " + items.join(" "));
|
|
163
|
+
this.send_and_enter(v);
|
|
164
|
+
}));
|
|
111
165
|
}
|
|
112
166
|
// cmd 命令 参数预测 只要是参数都可能是本目录下的文件名所以可以检测一下
|
|
113
167
|
cmd_params_auto_completion(param_str) {
|
|
114
|
-
//
|
|
115
|
-
if (this.not_use_node_pre_cmd_exec) {
|
|
116
|
-
return;
|
|
117
|
-
}
|
|
118
|
-
const items = this.node_require.fs.readdirSync(this.cwd); // 读取目录内容
|
|
168
|
+
const items = fs.readdirSync(this.cwd); // 读取目录内容
|
|
119
169
|
if (!this.word_detection) {
|
|
120
170
|
this.word_detection = new word_detection_js_1.word_detection_js();
|
|
121
171
|
for (const item of items) {
|
|
@@ -130,6 +180,12 @@ class PtyShell {
|
|
|
130
180
|
}
|
|
131
181
|
return v;
|
|
132
182
|
}
|
|
183
|
+
get not_write() {
|
|
184
|
+
return this._not_write;
|
|
185
|
+
}
|
|
186
|
+
set not_write(value) {
|
|
187
|
+
this._not_write = value;
|
|
188
|
+
}
|
|
133
189
|
/**
|
|
134
190
|
* public method
|
|
135
191
|
*/
|
|
@@ -143,7 +199,9 @@ class PtyShell {
|
|
|
143
199
|
}
|
|
144
200
|
add_cmd_handle(exe_cmd, handle) {
|
|
145
201
|
this.cmd_exec_map.set(exe_cmd, handle);
|
|
146
|
-
|
|
202
|
+
}
|
|
203
|
+
add_js_child(name, child) {
|
|
204
|
+
this.js_child_map.set(name, child);
|
|
147
205
|
}
|
|
148
206
|
close() {
|
|
149
207
|
this.is_running = false;
|
|
@@ -162,7 +220,7 @@ class PtyShell {
|
|
|
162
220
|
const max_index = str.length - 1;
|
|
163
221
|
const list = [];
|
|
164
222
|
let last_index = 0; // 上一次位置
|
|
165
|
-
let index =
|
|
223
|
+
let index = char_util_1.CharUtil.readFullCharIndex(str, 0, this.cols);
|
|
166
224
|
let count = 0;
|
|
167
225
|
while (index < max_index) {
|
|
168
226
|
if (count > max_index)
|
|
@@ -174,21 +232,21 @@ class PtyShell {
|
|
|
174
232
|
if (this.is_empty(str[f])) {
|
|
175
233
|
list.push(str.substring(last_index, f + 1));
|
|
176
234
|
last_index = f + 1;
|
|
177
|
-
index = f + 1 +
|
|
235
|
+
index = f + 1 + char_util_1.CharUtil.readFullCharIndex(str, f + 1, this.cols);
|
|
178
236
|
continue;
|
|
179
237
|
}
|
|
180
238
|
if (f === last_index) {
|
|
181
239
|
// 最后一位 直接把本行全部添加进去
|
|
182
240
|
list.push(str.substring(last_index, index));
|
|
183
241
|
last_index = index;
|
|
184
|
-
index = index +
|
|
242
|
+
index = index + char_util_1.CharUtil.readFullCharIndex(str, index, this.cols);
|
|
185
243
|
}
|
|
186
244
|
}
|
|
187
245
|
}
|
|
188
246
|
else {
|
|
189
247
|
list.push(str.substring(last_index, index));
|
|
190
248
|
last_index = index;
|
|
191
|
-
index = index +
|
|
249
|
+
index = index + char_util_1.CharUtil.readFullCharIndex(str, index + 1, this.cols);
|
|
192
250
|
}
|
|
193
251
|
}
|
|
194
252
|
if (index >= max_index) {
|
|
@@ -202,8 +260,10 @@ class PtyShell {
|
|
|
202
260
|
*/
|
|
203
261
|
write(data) {
|
|
204
262
|
return __awaiter(this, void 0, void 0, function* () {
|
|
205
|
-
if (this.
|
|
206
|
-
|
|
263
|
+
if (this._not_write)
|
|
264
|
+
return;
|
|
265
|
+
if (this.node_pty_child != null) {
|
|
266
|
+
// 终端shell 完全 托管给 别的程序 只有 pty 的子程序可以 其他 需要pty-shell充当编辑器
|
|
207
267
|
this.spawn_write(data);
|
|
208
268
|
return;
|
|
209
269
|
}
|
|
@@ -252,52 +312,6 @@ class PtyShell {
|
|
|
252
312
|
}
|
|
253
313
|
});
|
|
254
314
|
}
|
|
255
|
-
/**
|
|
256
|
-
* static method
|
|
257
|
-
*/
|
|
258
|
-
// 判断一个字符是全角还是半角
|
|
259
|
-
static isFullCharWidth(char) {
|
|
260
|
-
// 计算字符的 UTF-8 编码字节长度
|
|
261
|
-
const byteLength = Buffer.byteLength(char, 'utf8');
|
|
262
|
-
// 如果字符的字节长度大于 1,说明是全角字符
|
|
263
|
-
return byteLength > 1;
|
|
264
|
-
}
|
|
265
|
-
// 从start_index往前多少个位置获取指定数量的 半角 字符(宽字符算两个)
|
|
266
|
-
static readFullCharIndex(str, start_index, len) {
|
|
267
|
-
if (!str)
|
|
268
|
-
return 0;
|
|
269
|
-
if (start_index >= str.length)
|
|
270
|
-
return 0;
|
|
271
|
-
let num = 0;
|
|
272
|
-
let char_num = 0;
|
|
273
|
-
for (let i = start_index; i < str.length; i++) {
|
|
274
|
-
if (this.isFullCharWidth(str[i])) {
|
|
275
|
-
num += 2;
|
|
276
|
-
}
|
|
277
|
-
else {
|
|
278
|
-
num++;
|
|
279
|
-
}
|
|
280
|
-
char_num++;
|
|
281
|
-
if (num >= len)
|
|
282
|
-
return char_num;
|
|
283
|
-
}
|
|
284
|
-
return char_num;
|
|
285
|
-
}
|
|
286
|
-
// 获取字符串中有多少个 字符(将宽字符统计成两个)
|
|
287
|
-
static get_full_char_num(str) {
|
|
288
|
-
if (!str)
|
|
289
|
-
return 0;
|
|
290
|
-
let char_num = 0;
|
|
291
|
-
for (let i = 0; i < str.length; i++) {
|
|
292
|
-
if (this.isFullCharWidth(str[i])) {
|
|
293
|
-
char_num += 2;
|
|
294
|
-
}
|
|
295
|
-
else {
|
|
296
|
-
char_num++;
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
return char_num;
|
|
300
|
-
}
|
|
301
315
|
/**
|
|
302
316
|
* private method
|
|
303
317
|
*/
|
|
@@ -332,20 +346,33 @@ class PtyShell {
|
|
|
332
346
|
}
|
|
333
347
|
close_child(code) {
|
|
334
348
|
this.child_now_line = '';
|
|
335
|
-
|
|
349
|
+
let child = this.child;
|
|
350
|
+
if (this.node_pty_child) {
|
|
351
|
+
child = this.node_pty_child;
|
|
352
|
+
}
|
|
353
|
+
if (child) {
|
|
336
354
|
// SystemUtil.killProcess(this.child.pid);
|
|
337
|
-
const pid =
|
|
355
|
+
const pid = child.pid;
|
|
338
356
|
if (this.on_child_kill) {
|
|
339
357
|
this.exec_end_call(code, pid);
|
|
340
358
|
}
|
|
341
359
|
else {
|
|
342
|
-
|
|
360
|
+
child.kill(); // 不同平台信号不同 win 默认 SIGHUP
|
|
343
361
|
}
|
|
344
|
-
this.child = undefined;
|
|
345
362
|
}
|
|
363
|
+
if (this.js_func_child) {
|
|
364
|
+
this.js_func_child.kill();
|
|
365
|
+
}
|
|
366
|
+
this.child = undefined;
|
|
367
|
+
this.node_pty_child = null;
|
|
368
|
+
this.js_func_child = null;
|
|
369
|
+
}
|
|
370
|
+
have_child() {
|
|
371
|
+
return this.child != null || this.node_pty_child != null || this.js_func_child != null;
|
|
346
372
|
}
|
|
347
373
|
send_and_enter(str, send_prompt = false) {
|
|
348
374
|
try {
|
|
375
|
+
const have_child = this.have_child();
|
|
349
376
|
if (typeof str == "string") {
|
|
350
377
|
const list = [];
|
|
351
378
|
let i = 0;
|
|
@@ -365,7 +392,7 @@ class PtyShell {
|
|
|
365
392
|
}
|
|
366
393
|
if (i === 0 || i !== last_i)
|
|
367
394
|
list.push(str.substring(i));
|
|
368
|
-
if (
|
|
395
|
+
if (have_child) {
|
|
369
396
|
// 添加子进程的提示换行
|
|
370
397
|
this.child_now_line = list[list.length - 1];
|
|
371
398
|
}
|
|
@@ -380,7 +407,7 @@ class PtyShell {
|
|
|
380
407
|
}
|
|
381
408
|
this.next_not_enter = str.endsWith('\n\r') || str.endsWith('\r\n'); // 下一次不用换行了
|
|
382
409
|
}
|
|
383
|
-
if (!
|
|
410
|
+
if (!have_child || send_prompt) {
|
|
384
411
|
this.on_call(`${this.enter_prompt}`);
|
|
385
412
|
}
|
|
386
413
|
this.clear_line();
|
|
@@ -392,8 +419,9 @@ class PtyShell {
|
|
|
392
419
|
// 重新更新显示本行 也许可以更节省的更新 文本 powershell 这样的每次都是全部更新 暂时和他一样
|
|
393
420
|
update_line(param) {
|
|
394
421
|
var _a, _b;
|
|
395
|
-
const
|
|
396
|
-
|
|
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; // 字符串前面的字符数量
|
|
397
425
|
if (param && param.line_add_num) {
|
|
398
426
|
len += param.line_add_num;
|
|
399
427
|
}
|
|
@@ -421,7 +449,7 @@ class PtyShell {
|
|
|
421
449
|
get line_char_index() {
|
|
422
450
|
if (this.line_index === -1)
|
|
423
451
|
return 0;
|
|
424
|
-
return
|
|
452
|
+
return char_util_1.CharUtil.get_full_char_num(this.line.substring(0, this.line_index + 1));
|
|
425
453
|
}
|
|
426
454
|
ctrl_exec(str) {
|
|
427
455
|
let cancel_selected = true; // 取消选中
|
|
@@ -483,7 +511,7 @@ class PtyShell {
|
|
|
483
511
|
break;
|
|
484
512
|
}
|
|
485
513
|
this.line_index++;
|
|
486
|
-
const len =
|
|
514
|
+
const len = char_util_1.CharUtil.get_full_char_num(this.line[this.line_index]);
|
|
487
515
|
if (this.select_line) {
|
|
488
516
|
this.update_line({ line_add_num: 1 });
|
|
489
517
|
break;
|
|
@@ -497,7 +525,7 @@ class PtyShell {
|
|
|
497
525
|
if (this.line_index === -1) {
|
|
498
526
|
break;
|
|
499
527
|
}
|
|
500
|
-
const len =
|
|
528
|
+
const len = char_util_1.CharUtil.get_full_char_num(this.line[this.line_index]);
|
|
501
529
|
this.line_index--;
|
|
502
530
|
if (this.select_line) {
|
|
503
531
|
this.update_line({ line_add_num: 1 });
|
|
@@ -550,7 +578,7 @@ class PtyShell {
|
|
|
550
578
|
this.cancel_selected();
|
|
551
579
|
this.update_line({ line_add_num: 1 });
|
|
552
580
|
}
|
|
553
|
-
else if (this.
|
|
581
|
+
else if (this.have_child()) {
|
|
554
582
|
this.close_child(0);
|
|
555
583
|
return;
|
|
556
584
|
}
|
|
@@ -725,20 +753,16 @@ class PtyShell {
|
|
|
725
753
|
// 解析和执行命令 执行完会自动换行的
|
|
726
754
|
parse_exec() {
|
|
727
755
|
return __awaiter(this, void 0, void 0, function* () {
|
|
756
|
+
var _a;
|
|
728
757
|
// const line = this.delete_all_enter(this.line);
|
|
729
|
-
if (!this.line && !this.
|
|
758
|
+
if (!this.line && !this.have_child()) {
|
|
730
759
|
this.send_and_enter("");
|
|
731
760
|
this.clear_line();
|
|
732
761
|
return;
|
|
733
762
|
}
|
|
734
763
|
this.push_history_line(this.line);
|
|
735
|
-
if (this.
|
|
736
|
-
//
|
|
737
|
-
this.spawn_write(`${this.line}\r`);
|
|
738
|
-
return;
|
|
739
|
-
}
|
|
740
|
-
else if (this.child) {
|
|
741
|
-
// 把数据给正在运行的别的程序
|
|
764
|
+
if (this.have_child()) {
|
|
765
|
+
// 把数据给正在运行的别的程序 但是肯定不是 pty 的pty 不需要pty-shell 做命令编辑器
|
|
742
766
|
this.spawn_write(`${this.line}\n`);
|
|
743
767
|
this.clear_line();
|
|
744
768
|
return;
|
|
@@ -772,7 +796,7 @@ class PtyShell {
|
|
|
772
796
|
exe = r.exe_cmd;
|
|
773
797
|
params = r.params;
|
|
774
798
|
}
|
|
775
|
-
if (this.
|
|
799
|
+
if (this.cmd_exec_map.has(exe)) {
|
|
776
800
|
// 检测某个已经有预处理的命令 包括用户自定义的
|
|
777
801
|
yield this.exec_cmd(exe, params);
|
|
778
802
|
// 不用再继续了
|
|
@@ -780,12 +804,26 @@ class PtyShell {
|
|
|
780
804
|
this.exec_end_call(0);
|
|
781
805
|
return;
|
|
782
806
|
}
|
|
783
|
-
this.
|
|
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;
|
|
818
|
+
}
|
|
819
|
+
else {
|
|
820
|
+
this.spawn(exe, params, use_noe_pty);
|
|
821
|
+
}
|
|
784
822
|
this.clear_line();
|
|
785
823
|
}
|
|
786
824
|
catch (e) {
|
|
787
825
|
// console.log("子线程执行异常", e);
|
|
788
|
-
this.send_and_enter(e.message);
|
|
826
|
+
this.send_and_enter((_a = e.message) !== null && _a !== void 0 ? _a : e);
|
|
789
827
|
this.exec_end_call(-1);
|
|
790
828
|
}
|
|
791
829
|
});
|
|
@@ -813,18 +851,22 @@ class PtyShell {
|
|
|
813
851
|
});
|
|
814
852
|
}
|
|
815
853
|
spawn_write(str) {
|
|
816
|
-
if (this.
|
|
817
|
-
this.
|
|
854
|
+
if (this.node_pty_child) {
|
|
855
|
+
this.node_pty_child.write(str);
|
|
856
|
+
// this.child.write(str);
|
|
818
857
|
}
|
|
819
|
-
else {
|
|
858
|
+
else if (this.child) {
|
|
820
859
|
this.child.stdin.write(str);
|
|
821
860
|
}
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
if (this.not_use_node_pre_cmd_exec) {
|
|
825
|
-
this.send_and_enter(`not_use_node_pre_cmd_exec is true`);
|
|
826
|
-
return;
|
|
861
|
+
else if (this.js_func_child) {
|
|
862
|
+
this.js_func_child.write(str);
|
|
827
863
|
}
|
|
864
|
+
}
|
|
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
|
+
// }
|
|
828
870
|
// this.send_and_enter(""); //
|
|
829
871
|
// if (!this.child) {
|
|
830
872
|
// this.on_call(`\n\r`); // 先换个行
|
|
@@ -834,30 +876,40 @@ class PtyShell {
|
|
|
834
876
|
// exe += '.exe';
|
|
835
877
|
// }
|
|
836
878
|
this.on_call(`\n\r`); // 先换个行
|
|
837
|
-
this.
|
|
838
|
-
|
|
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) => {
|
|
839
889
|
this.on_call(data.toString());
|
|
840
890
|
if (this.on_call_child_raw) {
|
|
841
891
|
this.on_call_child_raw(data);
|
|
842
892
|
}
|
|
843
893
|
});
|
|
844
|
-
this.
|
|
894
|
+
this.node_pty_child.onExit(({ exitCode, signal }) => {
|
|
845
895
|
this.close_child(exitCode);
|
|
846
896
|
this.send_and_enter("");
|
|
847
897
|
// this.send_and_enter(`pty with ${exitCode}`);
|
|
848
898
|
this.next_not_enter = false; // 下一次的换行输出 上一次没有换行
|
|
849
899
|
});
|
|
850
|
-
this.is_pty = true;
|
|
900
|
+
// this.is_pty = true;
|
|
851
901
|
}
|
|
852
902
|
else {
|
|
853
|
-
this.is_pty = false;
|
|
903
|
+
// this.is_pty = false;
|
|
854
904
|
// 其他的没有必要再创建一个 tty 都是资源消耗
|
|
855
|
-
this.child =
|
|
905
|
+
this.child = child_process.spawn(exe, params, {
|
|
856
906
|
// shell:getShell(),
|
|
857
|
-
cwd: this.cwd,
|
|
907
|
+
cwd: this.cwd, // 设置子进程的工作目录
|
|
908
|
+
env: Object.assign(Object.assign(Object.assign({}, process.env), this.env), { LANG: 'en_US.UTF-8' }), // 传递环境变量
|
|
858
909
|
// stdio: 'inherit' // 让子进程的输入输出与父进程共享 pipe ignore inherit
|
|
859
910
|
// timeout: 5000, // 设置超时为 5 秒
|
|
860
|
-
maxBuffer: 1024 * 1024 * 10
|
|
911
|
+
// maxBuffer: 1024 * 1024 * 10// 设置缓冲区为 10 MB
|
|
912
|
+
});
|
|
861
913
|
// 设置编码为 'utf8',确保输出按 UTF-8 编码解析
|
|
862
914
|
this.child.stdout.setEncoding('utf8');
|
|
863
915
|
this.child.stdout.on('data', (data) => {
|
|
@@ -893,58 +945,20 @@ class PtyShell {
|
|
|
893
945
|
}
|
|
894
946
|
exec_cmd(exe, params) {
|
|
895
947
|
return __awaiter(this, void 0, void 0, function* () {
|
|
948
|
+
var _a;
|
|
896
949
|
try {
|
|
897
950
|
const handle = this.cmd_exec_map.get(exe);
|
|
898
|
-
if (
|
|
899
|
-
// 如果用户有了就用用户的 不用系统自己的
|
|
900
|
-
yield handle(params, (data) => {
|
|
901
|
-
this.send_and_enter(data,
|
|
951
|
+
if (handle) {
|
|
952
|
+
// 如果用户有了就用用户的 不用系统自己的
|
|
953
|
+
yield handle(params, (data, enter) => {
|
|
954
|
+
this.send_and_enter(data, enter);
|
|
902
955
|
});
|
|
903
956
|
return true;
|
|
904
957
|
}
|
|
905
|
-
switch (exe) {
|
|
906
|
-
case 'pwd':
|
|
907
|
-
{
|
|
908
|
-
this.send_and_enter(`${this.cwd}`);
|
|
909
|
-
}
|
|
910
|
-
return true;
|
|
911
|
-
case 'cd':
|
|
912
|
-
{
|
|
913
|
-
let p;
|
|
914
|
-
if (!this.not_use_node_pre_cmd_exec) {
|
|
915
|
-
// 有node环境可以检测一下
|
|
916
|
-
p = this.node_require.path.isAbsolute(params[0]) ? params[0] : this.node_require.path.join(this.cwd, params[0]);
|
|
917
|
-
if (!this.node_require.fs.existsSync(p)) {
|
|
918
|
-
this.send_and_enter(`not directory ${p}`);
|
|
919
|
-
}
|
|
920
|
-
}
|
|
921
|
-
else {
|
|
922
|
-
// 没有node环境只能这样了 todo 对于 .. 路径有问题
|
|
923
|
-
p = (0, path_util_1.path_join)(this.cwd, params[0]);
|
|
924
|
-
}
|
|
925
|
-
this.cwd = p;
|
|
926
|
-
this.send_and_enter(``);
|
|
927
|
-
this.word_detection = undefined; // 清空检测器
|
|
928
|
-
}
|
|
929
|
-
return true;
|
|
930
|
-
case 'ls':
|
|
931
|
-
{
|
|
932
|
-
if (this.not_use_node_pre_cmd_exec) {
|
|
933
|
-
return false; // 让其它方式处理
|
|
934
|
-
}
|
|
935
|
-
const items = this.node_require.fs.readdirSync(this.cwd); // 读取目录内容
|
|
936
|
-
const v = this.cols_handle(" " + items.join(" "));
|
|
937
|
-
this.send_and_enter(v);
|
|
938
|
-
}
|
|
939
|
-
return true;
|
|
940
|
-
default:
|
|
941
|
-
return false; // 让其它方式处理
|
|
942
|
-
}
|
|
943
958
|
}
|
|
944
959
|
catch (e) {
|
|
945
|
-
this.send_and_enter(
|
|
960
|
+
this.send_and_enter((_a = e === null || e === void 0 ? void 0 : e.message) !== null && _a !== void 0 ? _a : e);
|
|
946
961
|
}
|
|
947
|
-
return false; // 让其它方式处理
|
|
948
962
|
});
|
|
949
963
|
}
|
|
950
964
|
// 解析命令与参数
|
|
@@ -1062,3 +1076,7 @@ class PtyShell {
|
|
|
1062
1076
|
}
|
|
1063
1077
|
}
|
|
1064
1078
|
exports.PtyShell = PtyShell;
|
|
1079
|
+
__exportStar(require("./type"), exports);
|
|
1080
|
+
__exportStar(require("./char.util"), exports);
|
|
1081
|
+
__exportStar(require("./path_util"), exports);
|
|
1082
|
+
__exportStar(require("./word_detection_js"), exports);
|
package/dist/type.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { PtyShell } from "./index";
|
|
2
|
+
export interface child_func_type {
|
|
3
|
+
write(str: string): void;
|
|
4
|
+
kill(): void;
|
|
5
|
+
init(): void;
|
|
6
|
+
}
|
|
7
|
+
export type child_func_constructor = new (pty_shell: PtyShell, exit_on: () => void, send: (str: string) => void, params: string[]) => child_func_type;
|
package/dist/type.js
ADDED