pty-shell 1.2.2 → 1.3.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 +12 -14
- package/dist/index.js +147 -151
- package/dist/type.d.ts +5 -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;
|
|
@@ -88,6 +87,7 @@ export declare class PtyShell implements PtyShellUserMethod {
|
|
|
88
87
|
*/
|
|
89
88
|
reset_option(param: Param): void;
|
|
90
89
|
add_cmd_handle(exe_cmd: string, handle: CmdHandler): void;
|
|
90
|
+
add_js_child(name: string, child: child_func_constructor): void;
|
|
91
91
|
close(): void;
|
|
92
92
|
kill(): void;
|
|
93
93
|
/**
|
|
@@ -100,12 +100,6 @@ export declare class PtyShell implements PtyShellUserMethod {
|
|
|
100
100
|
* @param data
|
|
101
101
|
*/
|
|
102
102
|
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
103
|
/**
|
|
110
104
|
* private method
|
|
111
105
|
*/
|
|
@@ -116,6 +110,7 @@ export declare class PtyShell implements PtyShellUserMethod {
|
|
|
116
110
|
private insert_line;
|
|
117
111
|
private close_child;
|
|
118
112
|
private next_not_enter;
|
|
113
|
+
private have_child;
|
|
119
114
|
private send_and_enter;
|
|
120
115
|
private update_line;
|
|
121
116
|
private cancel_selected;
|
|
@@ -139,4 +134,7 @@ export declare class PtyShell implements PtyShellUserMethod {
|
|
|
139
134
|
private removeCharacterAt;
|
|
140
135
|
private delete_all_enter;
|
|
141
136
|
}
|
|
142
|
-
export
|
|
137
|
+
export * from "./type";
|
|
138
|
+
export * from "./char.util";
|
|
139
|
+
export * from "./path_util";
|
|
140
|
+
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 = "";
|
|
@@ -102,20 +140,30 @@ class PtyShell {
|
|
|
102
140
|
this.history_line_max = 20;
|
|
103
141
|
this.next_not_enter = false;
|
|
104
142
|
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
143
|
this.on_call(this.raw_prompt);
|
|
144
|
+
// 添加自定义处理方法
|
|
145
|
+
this.add_cmd_handle("pwd", (params, send) => __awaiter(this, void 0, void 0, function* () {
|
|
146
|
+
this.send_and_enter(`${this.cwd}`);
|
|
147
|
+
}));
|
|
148
|
+
this.add_cmd_handle("cd", (params, send) => __awaiter(this, void 0, void 0, function* () {
|
|
149
|
+
const p = path.isAbsolute(params[0]) ? params[0] : path.join(this.cwd, params[0]);
|
|
150
|
+
if (!fs.existsSync(p)) {
|
|
151
|
+
this.send_and_enter(`not directory ${p}`);
|
|
152
|
+
}
|
|
153
|
+
this.cwd = p;
|
|
154
|
+
this.send_and_enter(``);
|
|
155
|
+
this.word_detection = undefined; // 清空检测器
|
|
156
|
+
}));
|
|
157
|
+
this.add_cmd_handle("ls", (params, send) => __awaiter(this, void 0, void 0, function* () {
|
|
158
|
+
var _a;
|
|
159
|
+
const items = fs.readdirSync((_a = params === null || params === void 0 ? void 0 : params[0]) !== null && _a !== void 0 ? _a : this.cwd); // 读取目录内容
|
|
160
|
+
const v = this.cols_handle(" " + items.join(" "));
|
|
161
|
+
this.send_and_enter(v);
|
|
162
|
+
}));
|
|
111
163
|
}
|
|
112
164
|
// cmd 命令 参数预测 只要是参数都可能是本目录下的文件名所以可以检测一下
|
|
113
165
|
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); // 读取目录内容
|
|
166
|
+
const items = fs.readdirSync(this.cwd); // 读取目录内容
|
|
119
167
|
if (!this.word_detection) {
|
|
120
168
|
this.word_detection = new word_detection_js_1.word_detection_js();
|
|
121
169
|
for (const item of items) {
|
|
@@ -143,7 +191,9 @@ class PtyShell {
|
|
|
143
191
|
}
|
|
144
192
|
add_cmd_handle(exe_cmd, handle) {
|
|
145
193
|
this.cmd_exec_map.set(exe_cmd, handle);
|
|
146
|
-
|
|
194
|
+
}
|
|
195
|
+
add_js_child(name, child) {
|
|
196
|
+
this.js_child_map.set(name, child);
|
|
147
197
|
}
|
|
148
198
|
close() {
|
|
149
199
|
this.is_running = false;
|
|
@@ -162,7 +212,7 @@ class PtyShell {
|
|
|
162
212
|
const max_index = str.length - 1;
|
|
163
213
|
const list = [];
|
|
164
214
|
let last_index = 0; // 上一次位置
|
|
165
|
-
let index =
|
|
215
|
+
let index = char_util_1.CharUtil.readFullCharIndex(str, 0, this.cols);
|
|
166
216
|
let count = 0;
|
|
167
217
|
while (index < max_index) {
|
|
168
218
|
if (count > max_index)
|
|
@@ -174,21 +224,21 @@ class PtyShell {
|
|
|
174
224
|
if (this.is_empty(str[f])) {
|
|
175
225
|
list.push(str.substring(last_index, f + 1));
|
|
176
226
|
last_index = f + 1;
|
|
177
|
-
index = f + 1 +
|
|
227
|
+
index = f + 1 + char_util_1.CharUtil.readFullCharIndex(str, f + 1, this.cols);
|
|
178
228
|
continue;
|
|
179
229
|
}
|
|
180
230
|
if (f === last_index) {
|
|
181
231
|
// 最后一位 直接把本行全部添加进去
|
|
182
232
|
list.push(str.substring(last_index, index));
|
|
183
233
|
last_index = index;
|
|
184
|
-
index = index +
|
|
234
|
+
index = index + char_util_1.CharUtil.readFullCharIndex(str, index, this.cols);
|
|
185
235
|
}
|
|
186
236
|
}
|
|
187
237
|
}
|
|
188
238
|
else {
|
|
189
239
|
list.push(str.substring(last_index, index));
|
|
190
240
|
last_index = index;
|
|
191
|
-
index = index +
|
|
241
|
+
index = index + char_util_1.CharUtil.readFullCharIndex(str, index + 1, this.cols);
|
|
192
242
|
}
|
|
193
243
|
}
|
|
194
244
|
if (index >= max_index) {
|
|
@@ -202,7 +252,7 @@ class PtyShell {
|
|
|
202
252
|
*/
|
|
203
253
|
write(data) {
|
|
204
254
|
return __awaiter(this, void 0, void 0, function* () {
|
|
205
|
-
if (this.
|
|
255
|
+
if (this.have_child()) {
|
|
206
256
|
// 终端shell 完全 托管给 别的程序
|
|
207
257
|
this.spawn_write(data);
|
|
208
258
|
return;
|
|
@@ -252,52 +302,6 @@ class PtyShell {
|
|
|
252
302
|
}
|
|
253
303
|
});
|
|
254
304
|
}
|
|
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
305
|
/**
|
|
302
306
|
* private method
|
|
303
307
|
*/
|
|
@@ -332,17 +336,29 @@ class PtyShell {
|
|
|
332
336
|
}
|
|
333
337
|
close_child(code) {
|
|
334
338
|
this.child_now_line = '';
|
|
335
|
-
|
|
339
|
+
let child = this.child;
|
|
340
|
+
if (this.node_pty_child) {
|
|
341
|
+
child = this.node_pty_child;
|
|
342
|
+
}
|
|
343
|
+
if (child) {
|
|
336
344
|
// SystemUtil.killProcess(this.child.pid);
|
|
337
|
-
const pid =
|
|
345
|
+
const pid = child.pid;
|
|
338
346
|
if (this.on_child_kill) {
|
|
339
347
|
this.exec_end_call(code, pid);
|
|
340
348
|
}
|
|
341
349
|
else {
|
|
342
|
-
|
|
350
|
+
child.kill(); // 不同平台信号不同 win 默认 SIGHUP
|
|
343
351
|
}
|
|
344
|
-
this.child = undefined;
|
|
345
352
|
}
|
|
353
|
+
if (this.js_func_child) {
|
|
354
|
+
this.js_func_child.kill();
|
|
355
|
+
}
|
|
356
|
+
this.child = undefined;
|
|
357
|
+
this.node_pty_child = null;
|
|
358
|
+
this.js_func_child = null;
|
|
359
|
+
}
|
|
360
|
+
have_child() {
|
|
361
|
+
return this.child != null || this.node_pty_child != null || this.js_func_child != null;
|
|
346
362
|
}
|
|
347
363
|
send_and_enter(str, send_prompt = false) {
|
|
348
364
|
try {
|
|
@@ -365,7 +381,7 @@ class PtyShell {
|
|
|
365
381
|
}
|
|
366
382
|
if (i === 0 || i !== last_i)
|
|
367
383
|
list.push(str.substring(i));
|
|
368
|
-
if (this.
|
|
384
|
+
if (this.have_child()) {
|
|
369
385
|
// 添加子进程的提示换行
|
|
370
386
|
this.child_now_line = list[list.length - 1];
|
|
371
387
|
}
|
|
@@ -380,7 +396,7 @@ class PtyShell {
|
|
|
380
396
|
}
|
|
381
397
|
this.next_not_enter = str.endsWith('\n\r') || str.endsWith('\r\n'); // 下一次不用换行了
|
|
382
398
|
}
|
|
383
|
-
if (!this.
|
|
399
|
+
if (!this.have_child() || send_prompt) {
|
|
384
400
|
this.on_call(`${this.enter_prompt}`);
|
|
385
401
|
}
|
|
386
402
|
this.clear_line();
|
|
@@ -393,7 +409,7 @@ class PtyShell {
|
|
|
393
409
|
update_line(param) {
|
|
394
410
|
var _a, _b;
|
|
395
411
|
const prompt = !this.child ? this.raw_prompt : this.child_now_line;
|
|
396
|
-
let len = (!this.child ? this.prompt_call_len :
|
|
412
|
+
let len = (!this.child ? this.prompt_call_len : char_util_1.CharUtil.get_full_char_num(prompt)) + this.line_char_index; // 字符串前面的字符数量
|
|
397
413
|
if (param && param.line_add_num) {
|
|
398
414
|
len += param.line_add_num;
|
|
399
415
|
}
|
|
@@ -421,7 +437,7 @@ class PtyShell {
|
|
|
421
437
|
get line_char_index() {
|
|
422
438
|
if (this.line_index === -1)
|
|
423
439
|
return 0;
|
|
424
|
-
return
|
|
440
|
+
return char_util_1.CharUtil.get_full_char_num(this.line.substring(0, this.line_index + 1));
|
|
425
441
|
}
|
|
426
442
|
ctrl_exec(str) {
|
|
427
443
|
let cancel_selected = true; // 取消选中
|
|
@@ -483,7 +499,7 @@ class PtyShell {
|
|
|
483
499
|
break;
|
|
484
500
|
}
|
|
485
501
|
this.line_index++;
|
|
486
|
-
const len =
|
|
502
|
+
const len = char_util_1.CharUtil.get_full_char_num(this.line[this.line_index]);
|
|
487
503
|
if (this.select_line) {
|
|
488
504
|
this.update_line({ line_add_num: 1 });
|
|
489
505
|
break;
|
|
@@ -497,7 +513,7 @@ class PtyShell {
|
|
|
497
513
|
if (this.line_index === -1) {
|
|
498
514
|
break;
|
|
499
515
|
}
|
|
500
|
-
const len =
|
|
516
|
+
const len = char_util_1.CharUtil.get_full_char_num(this.line[this.line_index]);
|
|
501
517
|
this.line_index--;
|
|
502
518
|
if (this.select_line) {
|
|
503
519
|
this.update_line({ line_add_num: 1 });
|
|
@@ -726,23 +742,12 @@ class PtyShell {
|
|
|
726
742
|
parse_exec() {
|
|
727
743
|
return __awaiter(this, void 0, void 0, function* () {
|
|
728
744
|
// const line = this.delete_all_enter(this.line);
|
|
729
|
-
if (!this.line && !this.
|
|
745
|
+
if (!this.line && !this.have_child()) {
|
|
730
746
|
this.send_and_enter("");
|
|
731
747
|
this.clear_line();
|
|
732
748
|
return;
|
|
733
749
|
}
|
|
734
750
|
this.push_history_line(this.line);
|
|
735
|
-
if (this.child && this.is_pty) {
|
|
736
|
-
// 终端shell 完全 托管给 别的程序
|
|
737
|
-
this.spawn_write(`${this.line}\r`);
|
|
738
|
-
return;
|
|
739
|
-
}
|
|
740
|
-
else if (this.child) {
|
|
741
|
-
// 把数据给正在运行的别的程序
|
|
742
|
-
this.spawn_write(`${this.line}\n`);
|
|
743
|
-
this.clear_line();
|
|
744
|
-
return;
|
|
745
|
-
}
|
|
746
751
|
let { exe, params } = this.get_exec(this.line);
|
|
747
752
|
this.history_line_index = -1;
|
|
748
753
|
try {
|
|
@@ -772,7 +777,7 @@ class PtyShell {
|
|
|
772
777
|
exe = r.exe_cmd;
|
|
773
778
|
params = r.params;
|
|
774
779
|
}
|
|
775
|
-
if (this.
|
|
780
|
+
if (this.cmd_exec_map.has(exe)) {
|
|
776
781
|
// 检测某个已经有预处理的命令 包括用户自定义的
|
|
777
782
|
yield this.exec_cmd(exe, params);
|
|
778
783
|
// 不用再继续了
|
|
@@ -780,6 +785,17 @@ class PtyShell {
|
|
|
780
785
|
this.exec_end_call(0);
|
|
781
786
|
return;
|
|
782
787
|
}
|
|
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;
|
|
798
|
+
}
|
|
783
799
|
this.spawn(exe, params, use_noe_pty);
|
|
784
800
|
this.clear_line();
|
|
785
801
|
}
|
|
@@ -813,18 +829,22 @@ class PtyShell {
|
|
|
813
829
|
});
|
|
814
830
|
}
|
|
815
831
|
spawn_write(str) {
|
|
816
|
-
if (this.
|
|
817
|
-
this.
|
|
832
|
+
if (this.node_pty_child) {
|
|
833
|
+
this.node_pty_child.write(str);
|
|
834
|
+
// this.child.write(str);
|
|
818
835
|
}
|
|
819
|
-
else {
|
|
836
|
+
else if (this.child) {
|
|
820
837
|
this.child.stdin.write(str);
|
|
821
838
|
}
|
|
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;
|
|
839
|
+
else if (this.js_func_child) {
|
|
840
|
+
this.js_func_child.write(str);
|
|
827
841
|
}
|
|
842
|
+
}
|
|
843
|
+
spawn(exe, params, use_noe_pty = true) {
|
|
844
|
+
// if (this.not_use_node_pre_cmd_exec) {
|
|
845
|
+
// this.send_and_enter(`not_use_node_pre_cmd_exec is true`);
|
|
846
|
+
// return;
|
|
847
|
+
// }
|
|
828
848
|
// this.send_and_enter(""); //
|
|
829
849
|
// if (!this.child) {
|
|
830
850
|
// this.on_call(`\n\r`); // 先换个行
|
|
@@ -834,30 +854,40 @@ class PtyShell {
|
|
|
834
854
|
// exe += '.exe';
|
|
835
855
|
// }
|
|
836
856
|
this.on_call(`\n\r`); // 先换个行
|
|
837
|
-
this.
|
|
838
|
-
|
|
857
|
+
this.node_pty_child = this.node_pty.spawn(exe, params, {
|
|
858
|
+
name: 'xterm-color',
|
|
859
|
+
cols: this.cols,
|
|
860
|
+
rows: this.rows,
|
|
861
|
+
cwd: this.cwd, // 设置子进程的工作目录
|
|
862
|
+
useConptyDll: false,
|
|
863
|
+
useConpty: process.env.NODE_ENV !== "production" ? false : undefined, // conpty 可以支持 bash 等命令 从 Windows 10 版本 1809 开始提供 , 但是如果使用了 powershell 这个也就没有必要了,而且设置为false才能使用debug模式运行
|
|
864
|
+
env: Object.assign(Object.assign({}, process.env), this.env), // 传递环境变量
|
|
865
|
+
});
|
|
866
|
+
this.node_pty_child.onData((data) => {
|
|
839
867
|
this.on_call(data.toString());
|
|
840
868
|
if (this.on_call_child_raw) {
|
|
841
869
|
this.on_call_child_raw(data);
|
|
842
870
|
}
|
|
843
871
|
});
|
|
844
|
-
this.
|
|
872
|
+
this.node_pty_child.onExit(({ exitCode, signal }) => {
|
|
845
873
|
this.close_child(exitCode);
|
|
846
874
|
this.send_and_enter("");
|
|
847
875
|
// this.send_and_enter(`pty with ${exitCode}`);
|
|
848
876
|
this.next_not_enter = false; // 下一次的换行输出 上一次没有换行
|
|
849
877
|
});
|
|
850
|
-
this.is_pty = true;
|
|
878
|
+
// this.is_pty = true;
|
|
851
879
|
}
|
|
852
880
|
else {
|
|
853
|
-
this.is_pty = false;
|
|
881
|
+
// this.is_pty = false;
|
|
854
882
|
// 其他的没有必要再创建一个 tty 都是资源消耗
|
|
855
|
-
this.child =
|
|
883
|
+
this.child = child_process.spawn(exe, params, {
|
|
856
884
|
// shell:getShell(),
|
|
857
|
-
cwd: this.cwd,
|
|
885
|
+
cwd: this.cwd, // 设置子进程的工作目录
|
|
886
|
+
env: Object.assign(Object.assign(Object.assign({}, process.env), this.env), { LANG: 'en_US.UTF-8' }), // 传递环境变量
|
|
858
887
|
// stdio: 'inherit' // 让子进程的输入输出与父进程共享 pipe ignore inherit
|
|
859
888
|
// timeout: 5000, // 设置超时为 5 秒
|
|
860
|
-
maxBuffer: 1024 * 1024 * 10
|
|
889
|
+
// maxBuffer: 1024 * 1024 * 10// 设置缓冲区为 10 MB
|
|
890
|
+
});
|
|
861
891
|
// 设置编码为 'utf8',确保输出按 UTF-8 编码解析
|
|
862
892
|
this.child.stdout.setEncoding('utf8');
|
|
863
893
|
this.child.stdout.on('data', (data) => {
|
|
@@ -893,58 +923,20 @@ class PtyShell {
|
|
|
893
923
|
}
|
|
894
924
|
exec_cmd(exe, params) {
|
|
895
925
|
return __awaiter(this, void 0, void 0, function* () {
|
|
926
|
+
var _a;
|
|
896
927
|
try {
|
|
897
928
|
const handle = this.cmd_exec_map.get(exe);
|
|
898
|
-
if (
|
|
899
|
-
// 如果用户有了就用用户的 不用系统自己的
|
|
900
|
-
yield handle(params, (data) => {
|
|
901
|
-
this.send_and_enter(data,
|
|
929
|
+
if (handle) {
|
|
930
|
+
// 如果用户有了就用用户的 不用系统自己的
|
|
931
|
+
yield handle(params, (data, enter) => {
|
|
932
|
+
this.send_and_enter(data, enter);
|
|
902
933
|
});
|
|
903
934
|
return true;
|
|
904
935
|
}
|
|
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
936
|
}
|
|
944
937
|
catch (e) {
|
|
945
|
-
this.send_and_enter(
|
|
938
|
+
this.send_and_enter((_a = e === null || e === void 0 ? void 0 : e.message) !== null && _a !== void 0 ? _a : e);
|
|
946
939
|
}
|
|
947
|
-
return false; // 让其它方式处理
|
|
948
940
|
});
|
|
949
941
|
}
|
|
950
942
|
// 解析命令与参数
|
|
@@ -1062,3 +1054,7 @@ class PtyShell {
|
|
|
1062
1054
|
}
|
|
1063
1055
|
}
|
|
1064
1056
|
exports.PtyShell = PtyShell;
|
|
1057
|
+
__exportStar(require("./type"), exports);
|
|
1058
|
+
__exportStar(require("./char.util"), exports);
|
|
1059
|
+
__exportStar(require("./path_util"), exports);
|
|
1060
|
+
__exportStar(require("./word_detection_js"), exports);
|
package/dist/type.d.ts
ADDED
package/dist/type.js
ADDED