@xiaoqiong0v0/opencode-ssh-tool 0.6.15 → 0.6.16
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/agent.d.ts +4 -0
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +17 -2
- package/dist/agent.js.map +1 -1
- package/dist/base-session.d.ts +64 -32
- package/dist/base-session.d.ts.map +1 -1
- package/dist/base-session.js +235 -106
- package/dist/base-session.js.map +1 -1
- package/dist/constants.d.ts +7 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +7 -1
- package/dist/constants.js.map +1 -1
- package/dist/i18n.d.ts +20 -0
- package/dist/i18n.d.ts.map +1 -1
- package/dist/i18n.js +20 -0
- package/dist/i18n.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +28 -4
- package/dist/index.js.map +1 -1
- package/dist/local-session.d.ts +3 -1
- package/dist/local-session.d.ts.map +1 -1
- package/dist/local-session.js +82 -4
- package/dist/local-session.js.map +1 -1
- package/dist/log.d.ts +8 -0
- package/dist/log.d.ts.map +1 -1
- package/dist/log.js +28 -1
- package/dist/log.js.map +1 -1
- package/dist/parse-check.d.ts +20 -0
- package/dist/parse-check.d.ts.map +1 -0
- package/dist/parse-check.js +505 -0
- package/dist/parse-check.js.map +1 -0
- package/dist/precheck.d.ts +32 -0
- package/dist/precheck.d.ts.map +1 -0
- package/dist/precheck.js +105 -0
- package/dist/precheck.js.map +1 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +41 -5
- package/dist/server.js.map +1 -1
- package/dist/session-store.d.ts +33 -0
- package/dist/session-store.d.ts.map +1 -1
- package/dist/session-store.js +55 -0
- package/dist/session-store.js.map +1 -1
- package/dist/session.js +1 -1
- package/dist/session.js.map +1 -1
- package/dist/shell-adapter.d.ts +14 -0
- package/dist/shell-adapter.d.ts.map +1 -1
- package/dist/shell-adapter.js +58 -10
- package/dist/shell-adapter.js.map +1 -1
- package/dist/utils.d.ts +8 -34
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +22 -64
- package/dist/utils.js.map +1 -1
- package/dist/web/app.js +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,505 @@
|
|
|
1
|
+
// 发送前语法预检(只解析、绝不执行命令):
|
|
2
|
+
// - PowerShell:常驻解析侧车(优先 pwsh 7,回落 Windows PowerShell 5.1)。命令文本一律
|
|
3
|
+
// base64(UTF-8) 传输,解析器返回结构化 ErrorId + 起止行/列 + 出错片段(Extent.Text 也 base64)。
|
|
4
|
+
// - bash/zsh:每次 spawn `bash -n`/`zsh -n` 作粗闸门(只给行号、无语言无关 ErrorId),钉 LC_ALL=C。
|
|
5
|
+
// - 不支持的 shell,或解析器不可用/超时/崩溃:一律返回 { ok: true }(不支持就不拦,绝不误拦)。
|
|
6
|
+
//
|
|
7
|
+
// 关键实测约束(见 .tmp/scratch/probe-precheck-20261016.md):
|
|
8
|
+
// 1) 判定只看 errorCount>0;IncompleteInput 对截断单行恒 true,不能当闸门。
|
|
9
|
+
// 2) 命令文本/片段不可做任何归一化——字符串内 `$(...)` 的子表达式错误会被检出,归一化会漏报。
|
|
10
|
+
// 3) 侧车脚本必须 ASCII-only:5.1 读无 BOM 的 UTF-8 脚本会按 ANSI 解码,含非 ASCII 会把脚本自身解析炸掉;
|
|
11
|
+
// 非 ASCII 数据只经 base64 进出。此处改用 -EncodedCommand(UTF-16LE base64),同样只含 ASCII 字符。
|
|
12
|
+
// 4) 侧车必须常驻(一次性启动 0.6~1.4s,之后每条往返 0~1ms;每条冷启动约 1s,不可接受)。
|
|
13
|
+
import { spawn } from "node:child_process";
|
|
14
|
+
/** 通过结果的共享单例(无错误时复用,语义明确且避免重复分配) */
|
|
15
|
+
const PASS = { ok: true };
|
|
16
|
+
/** 侧车启动超时(毫秒):pwsh 7 实测 ~0.6s、5.1 ~1.4s,留足余量;超时即换下一个候选 */
|
|
17
|
+
const SIDECAR_START_TIMEOUT_MS = 5_000;
|
|
18
|
+
/** 单次解析请求往返超时(毫秒):实测 0~1ms;超时视为侧车异常,杀进程并降级放行 */
|
|
19
|
+
const SIDECAR_REQUEST_TIMEOUT_MS = 5_000;
|
|
20
|
+
/** 侧车运行中崩溃后允许的自动重启次数:超过则本会话内永久降级为放行 */
|
|
21
|
+
const SIDECAR_MAX_RESTARTS = 1;
|
|
22
|
+
/** bash/zsh 预检超时(毫秒):`-n` 纯解析不执行,正常 <50ms;超时按放行处理 */
|
|
23
|
+
const POSIX_CHECK_TIMEOUT_MS = 10_000;
|
|
24
|
+
/** bash 错误消息中的行号(如 `bash: line 2: syntax error ...`);解析不到时回退第 1 行 */
|
|
25
|
+
const BASH_LINE_RE = /line (\d+)/;
|
|
26
|
+
/** bash 粗错误的固定 ErrorId(bash 无语言无关 ErrorId,仅作占位标记) */
|
|
27
|
+
const BASH_ERR_ID = "bash-syntax";
|
|
28
|
+
/**
|
|
29
|
+
* POSIX 语法错误消息特征(LC_ALL=C 英文;bash/zsh 措辞不随版本稳定,故只做"是否像语法错误"的存在性判断):
|
|
30
|
+
* 用于把「命令不存在 / WSL 无发行版」等**非语法错误**的失败(如 exit 127)与真语法错误区分开——
|
|
31
|
+
* 前者必须放行,绝不误拦。
|
|
32
|
+
*/
|
|
33
|
+
const POSIX_SYNTAX_RE = /(syntax error|unexpected EOF|unexpected end of file|parse error)/i;
|
|
34
|
+
/** PowerShell 侧车候选解释器(按优先级):Windows 上优先 pwsh 7,回落 5.1;非 Windows 仅 pwsh */
|
|
35
|
+
const PS_CANDIDATES = process.platform === "win32" ? ["pwsh", "powershell.exe"] : ["pwsh"];
|
|
36
|
+
/**
|
|
37
|
+
* PowerShell 常驻侧车脚本(**必须 ASCII-only**,见文件头约束 3)。
|
|
38
|
+
* 协议:父写一行 base64(UTF-8 命令文本) → 子回一行 `ERRCOUNT=<n>[|ErrorId:line:col:b64(text)]...`;
|
|
39
|
+
* 首行 `READY` 表示就绪;收 `!QUIT` 或 stdin EOF 即干净退出(`!` 不是 base64 字符,避免 payload 碰撞)。
|
|
40
|
+
* 输出行整体为 ASCII(ErrorId 为 ASCII、片段走 base64),故不受控制台代码页影响。
|
|
41
|
+
*/
|
|
42
|
+
const PS_SIDECAR_SCRIPT = [
|
|
43
|
+
"$ErrorActionPreference = 'Stop'",
|
|
44
|
+
"[Console]::OutputEncoding = [System.Text.Encoding]::UTF8",
|
|
45
|
+
"$out = [Console]::Out",
|
|
46
|
+
"$out.WriteLine('READY')",
|
|
47
|
+
"$out.Flush()",
|
|
48
|
+
"while ($true) {",
|
|
49
|
+
" $line = [Console]::In.ReadLine()",
|
|
50
|
+
" if ($null -eq $line) { break }",
|
|
51
|
+
" if ($line -eq '!QUIT') { break }",
|
|
52
|
+
" try {",
|
|
53
|
+
" $bytes = [Convert]::FromBase64String($line)",
|
|
54
|
+
" $text = [System.Text.Encoding]::UTF8.GetString($bytes)",
|
|
55
|
+
" $tokens = $null",
|
|
56
|
+
" $errors = $null",
|
|
57
|
+
" [void][System.Management.Automation.Language.Parser]::ParseInput($text, [ref]$tokens, [ref]$errors)",
|
|
58
|
+
" if ($null -eq $errors) { $errors = @() }",
|
|
59
|
+
" $sb = New-Object System.Text.StringBuilder",
|
|
60
|
+
" [void]$sb.Append('ERRCOUNT=').Append($errors.Count)",
|
|
61
|
+
" foreach ($e in $errors) {",
|
|
62
|
+
" $tb = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($e.Extent.Text))",
|
|
63
|
+
" [void]$sb.Append('|').Append($e.ErrorId).Append(':').Append($e.Extent.StartLineNumber).Append(':').Append($e.Extent.StartColumnNumber).Append(':').Append($tb)",
|
|
64
|
+
" }",
|
|
65
|
+
" $out.WriteLine($sb.ToString())",
|
|
66
|
+
" $out.Flush()",
|
|
67
|
+
" } catch {",
|
|
68
|
+
" $out.WriteLine('ERROR=1')",
|
|
69
|
+
" $out.Flush()",
|
|
70
|
+
" }",
|
|
71
|
+
"}",
|
|
72
|
+
].join("\n");
|
|
73
|
+
/** 侧车启动参数:-EncodedCommand 传脚本,规避引号与代码页问题(脚本 ASCII-only) */
|
|
74
|
+
const PS_SIDECAR_ARGS = [
|
|
75
|
+
"-NoProfile",
|
|
76
|
+
"-NonInteractive",
|
|
77
|
+
"-NoLogo",
|
|
78
|
+
"-EncodedCommand",
|
|
79
|
+
encodeUtf16leBase64(PS_SIDECAR_SCRIPT),
|
|
80
|
+
];
|
|
81
|
+
/**
|
|
82
|
+
* bash/zsh 运行候选(按序尝试):本机无 bash 时回落 WSL 里的 bash。
|
|
83
|
+
* 不为 zsh 配 WSL 回落——发行版多半没装 zsh,`wsl zsh -n` 会以"命令不存在"失败,
|
|
84
|
+
* 易被误当成语法错误而**误拦**合法命令;zsh 本机不可用即放行。
|
|
85
|
+
*/
|
|
86
|
+
const POSIX_RUNNERS = {
|
|
87
|
+
bash: [["bash", "-n"], ["wsl", "bash", "-n"]],
|
|
88
|
+
zsh: [["zsh", "-f", "-n"]],
|
|
89
|
+
};
|
|
90
|
+
/**
|
|
91
|
+
* 发送前校验命令语法(不执行命令)。
|
|
92
|
+
* @param shell 目标 shell 标识(pwsh/powershell → PowerShell;bash/zsh → `-n`;含 wsl 视为 bash 走 WSL)
|
|
93
|
+
* @param text 待校验的命令文本(原样,不做任何归一化)
|
|
94
|
+
* @returns 通过 `{ok:true}`;有语法错误 `{ok:false, errors}`;不支持或解析器不可用一律 `{ok:true}`
|
|
95
|
+
*/
|
|
96
|
+
export async function validateCommand(shell, text) {
|
|
97
|
+
if (!text)
|
|
98
|
+
return PASS;
|
|
99
|
+
const kind = classifyShell(shell);
|
|
100
|
+
if (kind === "pwsh") {
|
|
101
|
+
const errors = await pwshSidecar.check(text);
|
|
102
|
+
if (errors === null || errors.length === 0)
|
|
103
|
+
return PASS;
|
|
104
|
+
return { ok: false, errors };
|
|
105
|
+
}
|
|
106
|
+
if (kind === "bash" || kind === "zsh") {
|
|
107
|
+
const issue = await checkPosix(kind, text);
|
|
108
|
+
return issue ? { ok: false, errors: [issue] } : PASS;
|
|
109
|
+
}
|
|
110
|
+
return PASS;
|
|
111
|
+
}
|
|
112
|
+
/** UTF-16LE + base64 编码(供 `-EncodedCommand` 使用) */
|
|
113
|
+
function encodeUtf16leBase64(s) {
|
|
114
|
+
return Buffer.from(s, "utf16le").toString("base64");
|
|
115
|
+
}
|
|
116
|
+
/** 把 shell 标识归类:仅识别 PowerShell 与 bash/zsh;其余归为 other(放行) */
|
|
117
|
+
function classifyShell(shell) {
|
|
118
|
+
const s = shell.trim().toLowerCase();
|
|
119
|
+
if (s.includes("pwsh") || s.includes("powershell"))
|
|
120
|
+
return "pwsh";
|
|
121
|
+
if (s.includes("zsh"))
|
|
122
|
+
return "zsh";
|
|
123
|
+
if (s.includes("bash") || s.includes("wsl"))
|
|
124
|
+
return "bash";
|
|
125
|
+
return "other";
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* bash/zsh 粗闸门:按候选顺序 spawn `-n` 解析(不执行)。
|
|
129
|
+
* 首个二进制可用的候选给出结果:exit 0 → 通过;确为语法错误 → 返回一条粗错误。
|
|
130
|
+
* 「非语法错误的失败」(命令不存在/无发行版等,如 exit 127)与候选全部不可用/超时 → 返回 null(放行)。
|
|
131
|
+
* @param kind "bash" | "zsh"
|
|
132
|
+
* @param text 命令文本
|
|
133
|
+
* @returns 语法错误条目;通过或不可用时为 null
|
|
134
|
+
*/
|
|
135
|
+
async function checkPosix(kind, text) {
|
|
136
|
+
for (const argv of POSIX_RUNNERS[kind]) {
|
|
137
|
+
const outcome = await runPosixCheck(argv, text);
|
|
138
|
+
if (outcome === null)
|
|
139
|
+
continue; // 二进制不存在/超时:试下一个候选
|
|
140
|
+
if (outcome.code === 0)
|
|
141
|
+
return null;
|
|
142
|
+
// 只有 stderr 确实像语法错误才拦:不能单凭 exit=2(包装脚本/WSL 异常也可能返回 2);
|
|
143
|
+
// 其余非 0(如 127 command not found、WSL 无发行版)视为"不可用"→ 放行,绝不误拦
|
|
144
|
+
if (!POSIX_SYNTAX_RE.test(outcome.stderr))
|
|
145
|
+
return null;
|
|
146
|
+
const stderr = outcome.stderr.trim();
|
|
147
|
+
return { errorId: BASH_ERR_ID, line: parseBashLine(outcome.stderr), col: 0, text: stderr };
|
|
148
|
+
}
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* 运行一次 `bash -n`/`zsh -n`:把命令文本经 **stdin** 喂入(规避命令行引号/换行问题),
|
|
153
|
+
* 钉 LC_ALL/LANG=C 保证消息为英文(bash 消息会按 locale 本地化,不可机读),捕获退出码与 stderr。
|
|
154
|
+
* @param argv 完整 argv(如 ["bash","-n"] / ["wsl","bash","-n"])
|
|
155
|
+
* @param text 命令文本(内部补尾换行,与实际提交语义一致)
|
|
156
|
+
* @returns 正常结束的退出码与 stderr;二进制不可用/超时返回 null
|
|
157
|
+
*/
|
|
158
|
+
function runPosixCheck(argv, text) {
|
|
159
|
+
return new Promise((resolve) => {
|
|
160
|
+
let proc;
|
|
161
|
+
try {
|
|
162
|
+
// 清掉 BASH_ENV/ENV:否则用户启动文件里的语法错误会让 `bash -n` 把**所有**合法命令判错
|
|
163
|
+
const env = { ...process.env, LC_ALL: "C", LANG: "C" };
|
|
164
|
+
delete env.BASH_ENV;
|
|
165
|
+
delete env.ENV;
|
|
166
|
+
proc = spawn(argv[0], argv.slice(1), {
|
|
167
|
+
stdio: ["pipe", "ignore", "pipe"],
|
|
168
|
+
env,
|
|
169
|
+
windowsHide: true,
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
catch {
|
|
173
|
+
resolve(null);
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
let stderr = "";
|
|
177
|
+
let settled = false;
|
|
178
|
+
let timer;
|
|
179
|
+
const done = (v) => {
|
|
180
|
+
if (settled)
|
|
181
|
+
return;
|
|
182
|
+
settled = true;
|
|
183
|
+
clearTimeout(timer);
|
|
184
|
+
resolve(v);
|
|
185
|
+
};
|
|
186
|
+
proc.stderr?.setEncoding("utf8");
|
|
187
|
+
proc.stderr?.on("data", (chunk) => {
|
|
188
|
+
stderr += chunk;
|
|
189
|
+
});
|
|
190
|
+
// 子进程在写入前/写入中退出时,流层会发 EPIPE;无监听会变成未捕获异常。
|
|
191
|
+
// 这里一律视为该候选不可用(结束本次校验,由调用方降级放行)。
|
|
192
|
+
proc.stdin?.on("error", () => done(null));
|
|
193
|
+
proc.stdout?.on("error", () => done(null));
|
|
194
|
+
proc.stderr?.on("error", () => done(null));
|
|
195
|
+
proc.once("error", () => done(null)); // ENOENT 等:该候选不可用
|
|
196
|
+
proc.once("close", (code) => done({ code: code ?? 1, stderr }));
|
|
197
|
+
timer = setTimeout(() => {
|
|
198
|
+
try {
|
|
199
|
+
proc.kill();
|
|
200
|
+
}
|
|
201
|
+
catch {
|
|
202
|
+
/* ignore */
|
|
203
|
+
}
|
|
204
|
+
done(null);
|
|
205
|
+
}, POSIX_CHECK_TIMEOUT_MS);
|
|
206
|
+
try {
|
|
207
|
+
proc.stdin?.end(text + "\n");
|
|
208
|
+
}
|
|
209
|
+
catch {
|
|
210
|
+
/* ignore:close/error 会给出结果 */
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
/** 从 bash 错误消息解析行号;解析不到或非正数时回退 1 */
|
|
215
|
+
function parseBashLine(stderr) {
|
|
216
|
+
const m = BASH_LINE_RE.exec(stderr);
|
|
217
|
+
const n = m ? Number.parseInt(m[1], 10) : Number.NaN;
|
|
218
|
+
return Number.isFinite(n) && n > 0 ? n : 1;
|
|
219
|
+
}
|
|
220
|
+
/** 解析侧车响应行 `ERRCOUNT=<n>[|ErrorId:line:col:b64]...` */
|
|
221
|
+
function parsePsResponse(line) {
|
|
222
|
+
const prefix = "ERRCOUNT=";
|
|
223
|
+
if (!line.startsWith(prefix))
|
|
224
|
+
return null;
|
|
225
|
+
const head = line.indexOf("|");
|
|
226
|
+
const countText = head < 0 ? line.slice(prefix.length) : line.slice(prefix.length, head);
|
|
227
|
+
const count = Number.parseInt(countText, 10);
|
|
228
|
+
if (!Number.isFinite(count))
|
|
229
|
+
return null;
|
|
230
|
+
const errors = [];
|
|
231
|
+
if (head >= 0) {
|
|
232
|
+
for (const seg of line.slice(head + 1).split("|")) {
|
|
233
|
+
const issue = parsePsEntry(seg);
|
|
234
|
+
if (issue)
|
|
235
|
+
errors.push(issue);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
// errorCount>0 但条目未解出时的防御:仍判为有错(不放过确实存在的语法错误)
|
|
239
|
+
if (errors.length === 0 && count > 0)
|
|
240
|
+
errors.push({ errorId: "ParseError", line: 0, col: 0, text: line });
|
|
241
|
+
return errors;
|
|
242
|
+
}
|
|
243
|
+
/** 解析单条侧车错误条目 `ErrorId:line:col:b64(text)`(b64 不含 `:`,故按前 3 个冒号切分) */
|
|
244
|
+
function parsePsEntry(seg) {
|
|
245
|
+
const c1 = seg.indexOf(":");
|
|
246
|
+
const c2 = seg.indexOf(":", c1 + 1);
|
|
247
|
+
const c3 = seg.indexOf(":", c2 + 1);
|
|
248
|
+
if (c1 < 0 || c2 < 0 || c3 < 0)
|
|
249
|
+
return null;
|
|
250
|
+
return {
|
|
251
|
+
errorId: seg.slice(0, c1),
|
|
252
|
+
line: toInt(seg.slice(c1 + 1, c2)),
|
|
253
|
+
col: toInt(seg.slice(c2 + 1, c3)),
|
|
254
|
+
text: Buffer.from(seg.slice(c3 + 1), "base64").toString("utf8"),
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
/** 十进制解析;非法返回 0 */
|
|
258
|
+
function toInt(s) {
|
|
259
|
+
const n = Number.parseInt(s, 10);
|
|
260
|
+
return Number.isFinite(n) ? n : 0;
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* 常驻 PowerShell 解析侧车(进程内单例,懒启动、崩溃自愈、空闲不阻塞父进程退出)。
|
|
264
|
+
* 请求按到达顺序排队:协议本就是"一行请求 → 一行响应",故响应顺序与请求顺序一致。
|
|
265
|
+
*/
|
|
266
|
+
class PwshSidecar {
|
|
267
|
+
_proc = null;
|
|
268
|
+
_ready = false;
|
|
269
|
+
_disabled = false;
|
|
270
|
+
_crashes = 0;
|
|
271
|
+
/** 挂接代次:新进程挂接后递增,旧进程迟到的 data/exit 一律忽略,防串扰新进程队列 */
|
|
272
|
+
_attachEpoch = 0;
|
|
273
|
+
_starting = null;
|
|
274
|
+
_queue = [];
|
|
275
|
+
_buf = "";
|
|
276
|
+
/**
|
|
277
|
+
* 解析一段命令文本。
|
|
278
|
+
* @param text 命令文本
|
|
279
|
+
* @returns 解析错误列表(空数组=通过);侧车不可用/异常时返回 null(调用方降级放行)
|
|
280
|
+
*/
|
|
281
|
+
async check(text) {
|
|
282
|
+
const proc = await this._ensure();
|
|
283
|
+
if (proc === null)
|
|
284
|
+
return null;
|
|
285
|
+
const payload = Buffer.from(text, "utf8").toString("base64");
|
|
286
|
+
let response;
|
|
287
|
+
try {
|
|
288
|
+
response = await this._request(proc, payload);
|
|
289
|
+
}
|
|
290
|
+
catch {
|
|
291
|
+
return null; // 侧车异常/超时 → 降级放行
|
|
292
|
+
}
|
|
293
|
+
return parsePsResponse(response);
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* 取得就绪的侧车进程;不可用(找不到解释器)/已达重启上限时返回 null。
|
|
297
|
+
* 懒启动:首次调用才 spawn;并发启动共享同一 Promise(只起一个进程)。
|
|
298
|
+
*/
|
|
299
|
+
async _ensure() {
|
|
300
|
+
if (this._disabled)
|
|
301
|
+
return null;
|
|
302
|
+
if (this._proc !== null && this._ready)
|
|
303
|
+
return this._proc;
|
|
304
|
+
if (this._starting === null)
|
|
305
|
+
this._starting = this._start();
|
|
306
|
+
const ok = await this._starting;
|
|
307
|
+
this._starting = null;
|
|
308
|
+
return ok ? this._proc : null;
|
|
309
|
+
}
|
|
310
|
+
/** 按候选顺序尝试启动;全部失败则永久降级。成功后置空闲不阻塞退出(崩溃计数在成功响应后重置) */
|
|
311
|
+
async _start() {
|
|
312
|
+
for (const bin of PS_CANDIDATES) {
|
|
313
|
+
const proc = await this._spawnCandidate(bin);
|
|
314
|
+
if (proc === null)
|
|
315
|
+
continue;
|
|
316
|
+
// READY 之后、赋值前已退出:不要装入死进程(否则 _ensure 会一直返回它并等超时)
|
|
317
|
+
if (!this._ready) {
|
|
318
|
+
try {
|
|
319
|
+
proc.kill();
|
|
320
|
+
}
|
|
321
|
+
catch {
|
|
322
|
+
/* ignore */
|
|
323
|
+
}
|
|
324
|
+
continue;
|
|
325
|
+
}
|
|
326
|
+
this._proc = proc;
|
|
327
|
+
this._unref(proc);
|
|
328
|
+
return true;
|
|
329
|
+
}
|
|
330
|
+
this._disabled = true;
|
|
331
|
+
return false;
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* 启动一个候选解释器并等待首行 `READY`。
|
|
335
|
+
* @param bin 二进制名(PATH 解析)
|
|
336
|
+
* @returns 就绪的进程;spawn 失败/早退/超时返回 null
|
|
337
|
+
*/
|
|
338
|
+
_spawnCandidate(bin) {
|
|
339
|
+
return new Promise((resolve) => {
|
|
340
|
+
let proc;
|
|
341
|
+
try {
|
|
342
|
+
proc = spawn(bin, PS_SIDECAR_ARGS, {
|
|
343
|
+
stdio: ["pipe", "pipe", "ignore"],
|
|
344
|
+
windowsHide: true,
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
catch {
|
|
348
|
+
resolve(null);
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
let settled = false;
|
|
352
|
+
let timer;
|
|
353
|
+
const onError = () => done(false);
|
|
354
|
+
const onFailExit = () => done(false);
|
|
355
|
+
const done = (ok) => {
|
|
356
|
+
if (settled)
|
|
357
|
+
return;
|
|
358
|
+
settled = true;
|
|
359
|
+
clearTimeout(timer);
|
|
360
|
+
proc.removeListener("error", onError);
|
|
361
|
+
proc.removeListener("exit", onFailExit);
|
|
362
|
+
if (!ok) {
|
|
363
|
+
try {
|
|
364
|
+
proc.kill();
|
|
365
|
+
}
|
|
366
|
+
catch {
|
|
367
|
+
/* ignore */
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
resolve(ok ? proc : null);
|
|
371
|
+
};
|
|
372
|
+
proc.once("error", onError);
|
|
373
|
+
proc.once("exit", onFailExit);
|
|
374
|
+
timer = setTimeout(() => done(false), SIDECAR_START_TIMEOUT_MS);
|
|
375
|
+
this._attach(proc, () => done(true));
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
/** 挂接侧车输出:按行解析(首行 READY 触发就绪回调,其后逐行兑现队首请求),并注册退出清理 */
|
|
379
|
+
_attach(proc, onReady) {
|
|
380
|
+
const epoch = ++this._attachEpoch;
|
|
381
|
+
this._buf = "";
|
|
382
|
+
this._ready = false;
|
|
383
|
+
const current = () => epoch === this._attachEpoch;
|
|
384
|
+
// 侧车提前退出/管道断开时的流级异常:走统一退出清理(拒绝待决请求,下次调用重新拉起);
|
|
385
|
+
// 不监听会变成未捕获 EPIPE。旧进程的异常/退出由 epoch 过滤,不污染新进程。
|
|
386
|
+
proc.stdin?.on("error", () => { if (current())
|
|
387
|
+
this._onExit(proc); });
|
|
388
|
+
proc.stdout?.on("error", () => { if (current())
|
|
389
|
+
this._onExit(proc); });
|
|
390
|
+
proc.stderr?.on("error", () => { if (current())
|
|
391
|
+
this._onExit(proc); });
|
|
392
|
+
proc.stdout?.setEncoding("utf8");
|
|
393
|
+
proc.stdout?.on("data", (chunk) => {
|
|
394
|
+
if (!current())
|
|
395
|
+
return;
|
|
396
|
+
this._buf += chunk;
|
|
397
|
+
let idx;
|
|
398
|
+
while ((idx = this._buf.indexOf("\n")) >= 0) {
|
|
399
|
+
const line = this._buf.slice(0, idx).replace(/\r$/, "");
|
|
400
|
+
this._buf = this._buf.slice(idx + 1);
|
|
401
|
+
if (!this._ready) {
|
|
402
|
+
if (line === "READY") {
|
|
403
|
+
this._ready = true;
|
|
404
|
+
onReady();
|
|
405
|
+
}
|
|
406
|
+
continue;
|
|
407
|
+
}
|
|
408
|
+
const pending = this._queue.shift();
|
|
409
|
+
if (pending) {
|
|
410
|
+
clearTimeout(pending.timer);
|
|
411
|
+
// 成功响应才重置连续崩溃计数:避免 _start 每次都清零导致重启上限失效
|
|
412
|
+
this._crashes = 0;
|
|
413
|
+
pending.resolve(line);
|
|
414
|
+
}
|
|
415
|
+
if (this._queue.length === 0)
|
|
416
|
+
this._unref(proc);
|
|
417
|
+
}
|
|
418
|
+
});
|
|
419
|
+
proc.once("exit", () => { if (current())
|
|
420
|
+
this._onExit(proc); });
|
|
421
|
+
}
|
|
422
|
+
/** 侧车退出:运行中崩溃计入重启次数(超限永久降级),并拒绝所有待决请求 */
|
|
423
|
+
_onExit(proc) {
|
|
424
|
+
// 旧进程迟到退出:新进程已接管,忽略,避免清掉新进程的 _ready/队列
|
|
425
|
+
if (this._proc !== null && this._proc !== proc)
|
|
426
|
+
return;
|
|
427
|
+
if (this._proc === proc) {
|
|
428
|
+
if (this._ready) {
|
|
429
|
+
this._crashes += 1;
|
|
430
|
+
if (this._crashes > SIDECAR_MAX_RESTARTS)
|
|
431
|
+
this._disabled = true;
|
|
432
|
+
}
|
|
433
|
+
this._proc = null;
|
|
434
|
+
this._ready = false;
|
|
435
|
+
this._buf = "";
|
|
436
|
+
}
|
|
437
|
+
const err = new Error("pwsh sidecar exited");
|
|
438
|
+
while (this._queue.length > 0) {
|
|
439
|
+
const pending = this._queue.shift();
|
|
440
|
+
clearTimeout(pending.timer);
|
|
441
|
+
pending.reject(err);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
/**
|
|
445
|
+
* 发送一行请求并等待一行响应。
|
|
446
|
+
* @param proc 就绪的侧车进程
|
|
447
|
+
* @param payload base64(UTF-8 命令文本)
|
|
448
|
+
* @returns 响应行;超时/写失败时 reject(调用方降级放行)
|
|
449
|
+
*/
|
|
450
|
+
_request(proc, payload) {
|
|
451
|
+
return new Promise((resolve, reject) => {
|
|
452
|
+
let pending;
|
|
453
|
+
const timer = setTimeout(() => {
|
|
454
|
+
const i = this._queue.indexOf(pending);
|
|
455
|
+
if (i >= 0)
|
|
456
|
+
this._queue.splice(i, 1);
|
|
457
|
+
try {
|
|
458
|
+
proc.kill();
|
|
459
|
+
}
|
|
460
|
+
catch {
|
|
461
|
+
/* ignore */
|
|
462
|
+
}
|
|
463
|
+
reject(new Error("pwsh sidecar request timeout"));
|
|
464
|
+
}, SIDECAR_REQUEST_TIMEOUT_MS);
|
|
465
|
+
pending = { resolve, reject, timer };
|
|
466
|
+
this._queue.push(pending);
|
|
467
|
+
this._ref(proc);
|
|
468
|
+
try {
|
|
469
|
+
proc.stdin?.write(payload + "\n");
|
|
470
|
+
}
|
|
471
|
+
catch {
|
|
472
|
+
const i = this._queue.indexOf(pending);
|
|
473
|
+
if (i >= 0)
|
|
474
|
+
this._queue.splice(i, 1);
|
|
475
|
+
clearTimeout(pending.timer);
|
|
476
|
+
reject(new Error("pwsh sidecar write failed"));
|
|
477
|
+
}
|
|
478
|
+
});
|
|
479
|
+
}
|
|
480
|
+
/** 请求期间保持事件循环引用,确保极简进程里也能等到响应 */
|
|
481
|
+
_ref(proc) {
|
|
482
|
+
proc.ref();
|
|
483
|
+
refStream(proc.stdin);
|
|
484
|
+
refStream(proc.stdout);
|
|
485
|
+
}
|
|
486
|
+
/** 空闲时解除引用:侧车不阻止父进程正常退出(父退出→管道关闭→侧车自退) */
|
|
487
|
+
_unref(proc) {
|
|
488
|
+
proc.unref();
|
|
489
|
+
unrefStream(proc.stdin);
|
|
490
|
+
unrefStream(proc.stdout);
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
/** 解除某流的引用(尽力而为) */
|
|
494
|
+
function unrefStream(s) {
|
|
495
|
+
;
|
|
496
|
+
s?.unref?.();
|
|
497
|
+
}
|
|
498
|
+
/** 恢复某流的引用(尽力而为) */
|
|
499
|
+
function refStream(s) {
|
|
500
|
+
;
|
|
501
|
+
s?.ref?.();
|
|
502
|
+
}
|
|
503
|
+
/** 进程内单例侧车 */
|
|
504
|
+
const pwshSidecar = new PwshSidecar();
|
|
505
|
+
//# sourceMappingURL=parse-check.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parse-check.js","sourceRoot":"","sources":["../src/parse-check.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { type ParseCheckResult, type ParseIssue } from "./parse-check.js";
|
|
2
|
+
import { type Lang } from "./i18n.js";
|
|
3
|
+
/** 语法校验函数签名(默认取 parse-check 的 validateCommand;第三参仅供单测注入异常实现) */
|
|
4
|
+
export type CommandValidator = (shell: string, text: string) => Promise<ParseCheckResult>;
|
|
5
|
+
/** 预检所需的会话能力(SshSession / LocalSession / AgentSession 均满足) */
|
|
6
|
+
export interface PrecheckTarget {
|
|
7
|
+
/** 探测到的 shell 适配器名;未探测到为 null */
|
|
8
|
+
readonly shellName: string | null;
|
|
9
|
+
/** 是否本地会话(SSH 远程本步不接) */
|
|
10
|
+
readonly isLocal: boolean;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* 把解析错误列表拼成面向模型的拦截文案(含 ErrorId + 行 + 列 + 出错片段)
|
|
14
|
+
* @param command 原始命令文本(供 extent 为空时回退取片段)
|
|
15
|
+
* @param errors 解析错误列表(调用方保证非空)
|
|
16
|
+
* @param lang 文案语言
|
|
17
|
+
* @returns 拦截文案,如 `发送前语法校验失败:ErrorId=X 位置=行1列3 片段='if'`
|
|
18
|
+
*/
|
|
19
|
+
export declare function formatSyntaxRejection(command: string, errors: ParseIssue[], lang: Lang): string;
|
|
20
|
+
/**
|
|
21
|
+
* 发送前语法预检(只解析、绝不执行):命中语法错误即拦下并返回文案,放行返回 null。
|
|
22
|
+
* - 只对本地会话生效:SSH 远程需会话内往返且涉及 `$?` 污染,本步不接。
|
|
23
|
+
* - shell 映射:pwsh/bash/zsh → 对应校验器;cmd 等 → 跳过。
|
|
24
|
+
* - 校验模块异常一律放行,绝不因预检故障阻塞命令。
|
|
25
|
+
* @param session 已解析的会话(需提供 shellName/isLocal)
|
|
26
|
+
* @param command 待发送的命令文本
|
|
27
|
+
* @param validate 校验函数(默认 validateCommand;仅供单测注入异常实现)
|
|
28
|
+
* @param lang 文案语言(默认取当前工具语言)
|
|
29
|
+
* @returns null=放行;非 null=拦截文案(调用方直接返回,不写终端)
|
|
30
|
+
*/
|
|
31
|
+
export declare function precheckBeforeExec(session: PrecheckTarget, command: string, validate?: CommandValidator, lang?: Lang): Promise<string | null>;
|
|
32
|
+
//# sourceMappingURL=precheck.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"precheck.d.ts","sourceRoot":"","sources":["../src/precheck.ts"],"names":[],"mappings":"AAKA,OAAO,EAAmB,KAAK,gBAAgB,EAAE,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAE1F,OAAO,EAAmB,KAAK,IAAI,EAAE,MAAM,WAAW,CAAA;AAmBtD,gEAAgE;AAChE,MAAM,MAAM,gBAAgB,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,gBAAgB,CAAC,CAAA;AAEzF,8DAA8D;AAC9D,MAAM,WAAW,cAAc;IAC7B,iCAAiC;IACjC,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACjC,yBAAyB;IACzB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;CAC1B;AAoCD;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,EAAE,IAAI,EAAE,IAAI,GAAG,MAAM,CAa/F;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,cAAc,EACvB,OAAO,EAAE,MAAM,EACf,QAAQ,GAAE,gBAAkC,EAC5C,IAAI,GAAE,IAAoB,GACzB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAcxB"}
|