@xiaoqiong0v0/opencode-ssh-tool 0.6.16 → 0.6.18
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 +20 -12
- package/dist/base-session.d.ts +5 -1
- package/dist/base-session.d.ts.map +1 -1
- package/dist/base-session.js +4 -2
- package/dist/base-session.js.map +1 -1
- package/dist/cli-args.d.ts +19 -0
- package/dist/cli-args.d.ts.map +1 -0
- package/dist/cli-args.js +88 -0
- package/dist/cli-args.js.map +1 -0
- package/dist/i18n.d.ts +52 -10
- package/dist/i18n.d.ts.map +1 -1
- package/dist/i18n.js +52 -10
- package/dist/i18n.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +76 -14
- package/dist/index.js.map +1 -1
- package/dist/payload.d.ts +49 -0
- package/dist/payload.d.ts.map +1 -0
- package/dist/payload.js +114 -0
- package/dist/payload.js.map +1 -0
- package/package.json +2 -2
package/dist/payload.js
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
// 载荷解析:term_cli exec / send 的输入来源(内联文本 / base64 / 本地文件)
|
|
2
|
+
// 目的:复杂引号、目标 shell 转义(PowerShell/cmd)以及以 "-" 开头的载荷,
|
|
3
|
+
// 无法靠 CLI 外层分词安全表达时,用 base64 或本地文件把 payload 原样带进来。
|
|
4
|
+
import { readFile } from "node:fs/promises";
|
|
5
|
+
import { homedir } from "node:os";
|
|
6
|
+
import { join } from "node:path";
|
|
7
|
+
/**
|
|
8
|
+
* 载荷解析错误:只携带机器可读 code,展示文案由调用方按语言生成。
|
|
9
|
+
*/
|
|
10
|
+
export class PayloadError extends Error {
|
|
11
|
+
code;
|
|
12
|
+
/**
|
|
13
|
+
* @param code 错误码(conflict / base64 / file)
|
|
14
|
+
* @param message 内部诊断信息(不直接展示给用户)
|
|
15
|
+
*/
|
|
16
|
+
constructor(code, message) {
|
|
17
|
+
super(message);
|
|
18
|
+
this.name = "PayloadError";
|
|
19
|
+
this.code = code;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
/** 严格 base64 字符集:仅标准字母表、末尾最多两个补位符(长度另校验 4 的倍数) */
|
|
23
|
+
const BASE64_RE = /^[A-Za-z0-9+/]*={0,2}$/;
|
|
24
|
+
/**
|
|
25
|
+
* 展开开头的 ~("~" / "~/" / "~\\")为当前用户 home 目录;其他路径原样返回。
|
|
26
|
+
* @param path 用户传入的文件路径
|
|
27
|
+
* @returns 展开后的路径
|
|
28
|
+
*/
|
|
29
|
+
function expandHome(path) {
|
|
30
|
+
if (path === "~")
|
|
31
|
+
return homedir();
|
|
32
|
+
if (path.startsWith("~/") || path.startsWith("~\\"))
|
|
33
|
+
return join(homedir(), path.slice(2));
|
|
34
|
+
return path;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* 严格解码 UTF-8 载荷:拒绝非法字节序列与 NUL,剥掉开头的 UTF-8 BOM。
|
|
38
|
+
* 目的:避免 UTF-16LE(PowerShell `Out-File` 默认)等非 UTF-8 文件被当成“带 NUL 的乱码”静默发送,
|
|
39
|
+
* 也避免 BOM 变成载荷首字符 U+FEFF。
|
|
40
|
+
* @param bytes 原始字节
|
|
41
|
+
* @param code 出错时归属的错误码(base64 / file)
|
|
42
|
+
* @returns 解码后的文本(无 BOM)
|
|
43
|
+
* @throws {PayloadError} 非法 UTF-8 或含 NUL
|
|
44
|
+
*/
|
|
45
|
+
function decodeUtf8(bytes, code) {
|
|
46
|
+
let text;
|
|
47
|
+
try {
|
|
48
|
+
// ignoreBOM=true:保留 BOM 由下面统一剥离,避免依赖 TextDecoder 的默认行为
|
|
49
|
+
text = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(bytes);
|
|
50
|
+
}
|
|
51
|
+
catch (e) {
|
|
52
|
+
const reason = e instanceof Error ? e.message : String(e);
|
|
53
|
+
throw new PayloadError("decode", `invalid UTF-8 ${code} payload: ${reason}`);
|
|
54
|
+
}
|
|
55
|
+
if (text.charCodeAt(0) === 0xfeff)
|
|
56
|
+
text = text.slice(1);
|
|
57
|
+
if (text.includes("\u0000")) {
|
|
58
|
+
throw new PayloadError("decode", `payload ${code} contains NUL (likely UTF-16 or binary); save it as UTF-8`);
|
|
59
|
+
}
|
|
60
|
+
return text;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* 归一 exec 载荷的换行:仅把 CRLF(\r\n)折成 \n。
|
|
64
|
+
* 仅用于 exec 的 base64/file 载荷;send raw 必须保持原样,故不调用本函数。
|
|
65
|
+
* @param text 载荷文本
|
|
66
|
+
* @returns CRLF 已归一为 LF 的文本
|
|
67
|
+
*/
|
|
68
|
+
export function normalizeExecText(text) {
|
|
69
|
+
return text.replace(/\r\n/g, "\n");
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* 解析 term_cli 的载荷来源。
|
|
73
|
+
* - 默认:positional 原样作为文本,raw=false(send 仍会解释 \n / \xHH 等转义);
|
|
74
|
+
* - --base64:忽略空白后严格校验并解码为 UTF-8 文本,raw=true;
|
|
75
|
+
* - --file:读取插件进程本机的 UTF-8 文件,raw=true(不读远程文件);
|
|
76
|
+
* 两者都严格解码:剥开头的 U+FEFF,非法 UTF-8 或含 NUL 时报错(不静默发送);
|
|
77
|
+
* - --base64 与 --file 互斥。
|
|
78
|
+
* 空载荷不在此报错(由调用方按 cli_need_* 文案处理)。
|
|
79
|
+
* @param positional 位置参数拼接后的内联文本
|
|
80
|
+
* @param opts base64 / file 选项
|
|
81
|
+
* @returns 解析结果(文本、是否原样、来源)
|
|
82
|
+
* @throws {PayloadError} 选项冲突、base64 非法或文件读取失败
|
|
83
|
+
*/
|
|
84
|
+
export async function resolvePayload(positional, opts) {
|
|
85
|
+
if (opts.base64 && opts.file) {
|
|
86
|
+
throw new PayloadError("conflict", "--base64 and --file are mutually exclusive");
|
|
87
|
+
}
|
|
88
|
+
if (opts.base64) {
|
|
89
|
+
// 允许用户把 base64 按行/空格折行传入;去掉所有空白后再严格校验
|
|
90
|
+
const compact = positional.replace(/\s+/g, "");
|
|
91
|
+
if (!BASE64_RE.test(compact) || compact.length % 4 !== 0) {
|
|
92
|
+
throw new PayloadError("base64", "invalid base64 payload");
|
|
93
|
+
}
|
|
94
|
+
return {
|
|
95
|
+
text: decodeUtf8(Buffer.from(compact, "base64"), "base64"),
|
|
96
|
+
raw: true,
|
|
97
|
+
source: "base64",
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
if (opts.file) {
|
|
101
|
+
const resolved = expandHome(opts.file);
|
|
102
|
+
let bytes;
|
|
103
|
+
try {
|
|
104
|
+
bytes = await readFile(resolved);
|
|
105
|
+
}
|
|
106
|
+
catch (e) {
|
|
107
|
+
const reason = e instanceof Error ? e.message : String(e);
|
|
108
|
+
throw new PayloadError("file", `cannot read ${resolved}: ${reason}`);
|
|
109
|
+
}
|
|
110
|
+
return { text: decodeUtf8(bytes, "file"), raw: true, source: "file" };
|
|
111
|
+
}
|
|
112
|
+
return { text: positional, raw: false, source: "text" };
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=payload.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"payload.js","sourceRoot":"","sources":["../src/payload.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xiaoqiong0v0/opencode-ssh-tool",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.18",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "长驻交互式 SSH 会话工具插件,远程命令受 opencode 权限管控",
|
|
6
6
|
"files": [
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
},
|
|
15
15
|
"scripts": {
|
|
16
16
|
"build": "tsc && node web/build.mjs",
|
|
17
|
+
"test": "npm run build && node --test test/cli-args.test.mjs test/payload.test.mjs",
|
|
17
18
|
"prepublishOnly": "npm run build"
|
|
18
19
|
},
|
|
19
20
|
"repository": {
|
|
@@ -40,7 +41,6 @@
|
|
|
40
41
|
"@xterm/headless": "^5.5.0",
|
|
41
42
|
"@xterm/xterm": "^5.5.0",
|
|
42
43
|
"ssh2": "^1.16.0",
|
|
43
|
-
"string-argv": "^0.3.2",
|
|
44
44
|
"ws": "^8.21.3"
|
|
45
45
|
},
|
|
46
46
|
"peerDependencies": {
|