@xiaoqiong0v0/opencode-ssh-tool 0.4.0 → 0.5.1

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.
Files changed (56) hide show
  1. package/README.md +22 -22
  2. package/dist/base-session.d.ts +128 -0
  3. package/dist/base-session.d.ts.map +1 -0
  4. package/dist/base-session.js +414 -0
  5. package/dist/base-session.js.map +1 -0
  6. package/dist/constants.d.ts +27 -3
  7. package/dist/constants.d.ts.map +1 -1
  8. package/dist/constants.js +28 -3
  9. package/dist/constants.js.map +1 -1
  10. package/dist/history.d.ts +14 -5
  11. package/dist/history.d.ts.map +1 -1
  12. package/dist/history.js +25 -9
  13. package/dist/history.js.map +1 -1
  14. package/dist/i18n.d.ts +75 -227
  15. package/dist/i18n.d.ts.map +1 -1
  16. package/dist/i18n.js +65 -211
  17. package/dist/i18n.js.map +1 -1
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +83 -31
  20. package/dist/index.js.map +1 -1
  21. package/dist/last-match.d.ts +17 -0
  22. package/dist/last-match.d.ts.map +1 -0
  23. package/dist/last-match.js +63 -0
  24. package/dist/last-match.js.map +1 -0
  25. package/dist/local-session.d.ts +9 -91
  26. package/dist/local-session.d.ts.map +1 -1
  27. package/dist/local-session.js +46 -308
  28. package/dist/local-session.js.map +1 -1
  29. package/dist/log.d.ts +3 -0
  30. package/dist/log.d.ts.map +1 -0
  31. package/dist/log.js +5 -0
  32. package/dist/log.js.map +1 -0
  33. package/dist/server.d.ts +32 -5
  34. package/dist/server.d.ts.map +1 -1
  35. package/dist/server.js +417 -349
  36. package/dist/server.js.map +1 -1
  37. package/dist/session.d.ts +15 -108
  38. package/dist/session.d.ts.map +1 -1
  39. package/dist/session.js +69 -361
  40. package/dist/session.js.map +1 -1
  41. package/dist/shell-adapter.d.ts +20 -0
  42. package/dist/shell-adapter.d.ts.map +1 -0
  43. package/dist/shell-adapter.js +70 -0
  44. package/dist/shell-adapter.js.map +1 -0
  45. package/dist/ssh-auth.d.ts.map +1 -1
  46. package/dist/ssh-auth.js +1 -2
  47. package/dist/ssh-auth.js.map +1 -1
  48. package/dist/utils.d.ts +25 -24
  49. package/dist/utils.d.ts.map +1 -1
  50. package/dist/utils.js +279 -45
  51. package/dist/utils.js.map +1 -1
  52. package/dist/web/app.css +43 -0
  53. package/dist/web/app.js +2 -0
  54. package/dist/web/fonts/CaskaydiaCoveNerdFontMono-Regular.ttf +0 -0
  55. package/dist/web/index.html +28 -0
  56. package/package.json +6 -3
package/dist/utils.d.ts CHANGED
@@ -1,34 +1,35 @@
1
1
  /**
2
- * 生成命令完成哨兵标记(随机后缀防撞词)
3
- * @returns 哨兵字符串
2
+ * 定位命令回显行在原始流中的结束位置(供流式增量定位使用):
3
+ * 丢弃窗口开头到"命令回显行结束"之前的一切内容(提示符、PS1 填充、光标定位噪音均在内)。
4
+ * 命令回显行 = 命令文本首次出现所在行(命令本身可能含 \033 等转义)。
5
+ * @param raw 原始终端流(含 ANSI)
6
+ * @param command 本次执行的命令文本
7
+ * @returns 命令回显结束后的字节偏移;未命中命令文本返回 0(此时应视作输出尚未开始)
4
8
  */
5
- export declare function genSentinel(): string;
9
+ export declare function extractOutputStart(raw: string, command: string): number;
6
10
  /**
7
- * 剔除 PTY 回显的命令行(含提示符前缀,如 "user@host:~$ cmd"),并清理残留 CR
8
- * 同时剔除含哨兵标记的行(上次命令在哨兵之后到达的回显残留,避免污染下一条输出)
9
- * @param raw 原始输出
10
- * @param cmd 已发送命令(组合串)
11
- * @returns 剔除回显后的输出
11
+ * 剥离"提示符 + 命令回显",只保留程序纯输出:
12
+ * shell 提示符(含背景填充、zle 重绘序列)依赖其自身终端屏幕状态,字节流脱离该状态无法忠实还原,
13
+ * 故丢弃窗口开头到"命令回显行结束"之前的一切内容(提示符、PS1 填充、光标定位噪音均在内)。
14
+ * 命令回显行 = 命令文本首次出现所在行(命令本身可能含 \033 等转义)。
15
+ * 命中后切到"命令文本结束处":zsh 下程序输出可能直接粘连在回显尾(无换行),不能按换行截断。
16
+ * @param raw 原始终端流(含 ANSI)
17
+ * @param command 本次执行的命令文本
18
+ * @returns 纯程序输出原始流(保留程序自身的 ANSI 颜色/进度条,供 web TermScreen 与模型 toModelText)
12
19
  */
13
- export declare function stripEcho(raw: string, cmd: string): string;
20
+ export declare function extractOutput(raw: string, command: string): string;
14
21
  /**
15
- * 剥离 ANSI 转义序列(颜色、光标控制等)
16
- * @param s 原始字符串
17
- * @returns 清洗后的纯文本
22
+ * 把原始终端流转为给模型看的干净文本:模拟终端屏幕(处理光标移动/清行/覆盖/退格/SGR/OSC),
23
+ * 输出最终屏幕各行文本,再逐行去尾空白并剔除空行。比纯文本正则更鲁棒地处理 PSReadLine
24
+ * 行内重绘、进度条 \r 覆盖等场景。
25
+ * @param raw 原始终端流(含 ANSI)
26
+ * @returns 纯文本输出(供模型消费 / exec 返回值 / read 命令)
18
27
  */
19
- export declare function cleanAnsi(s: string): string;
28
+ export declare function toModelText(raw: string): string;
20
29
  /**
21
- * 合并同一行内的 \r / ANSI 清行覆盖(进度条/旋转动画):保留每行最后一次刷新状态,
22
- * 避免 "下载 1%\r下载 2%\r...\r下载 100%" 累积成巨量碎片输出
23
- * 识别 \r 与 ANSI 清行(ESC[2K / ESC[K)+ 光标归位(ESC[G)作为行覆盖分隔符
24
- * @param s 原始输出(含 ANSI 或已清洗均可)
25
- * @returns 合并覆盖后的文本(\r\n 正常换行保留)
26
- */
27
- export declare function collapseCarriage(s: string): string;
28
- /**
29
- * 剥离尾部 shell 提示符残留(如 "5cb08b77db01:~$ "、"user@host:~/dir$ "、"root@server:/etc#")
30
- * @param s 清洗后文本
31
- * @returns 剥离尾部提示符后的文本
30
+ * 末尾空白清理(原为提示符剥离,现仅做清理,不依赖正则匹配具体提示符形状)
31
+ * @param s 文本
32
+ * @returns 清理后文本
32
33
  */
33
34
  export declare function stripPrompt(s: string): string;
34
35
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAIA;;;GAGG;AACH,wBAAgB,WAAW,IAAI,MAAM,CAEpC;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAI1D;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAG3C;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAYlD;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAI7C;AAED;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAI1C"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAWA;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAavE;AAED;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAIlE;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAO/C;AAgJD;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAE7C;AAED;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAI1C"}
package/dist/utils.js CHANGED
@@ -1,63 +1,297 @@
1
- // 工具函数:哨兵生成、回显剔除、ANSI 清洗、CR 覆盖合并、提示符剥离
2
- import { randomBytes } from "node:crypto";
1
+ // 工具函数:命令回显定位、模型文本清理、ANSI 清洗、CR 覆盖合并、完成标记剥离
2
+ import { stripMarkers } from "./shell-adapter.js";
3
+ import { findLastEndOf } from "./last-match.js";
4
+ /** 正则转义文本 */
5
+ const escRe = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
6
+ /** 命令回显匹配的宽松字符间隙(ANSI 序列或普通空白/换行,可插在命令字符之间;容忍终端列宽自动换行) */
7
+ const ECHO_WIDE = "(?:\\x1b\\[[0-9;?]*[a-zA-Z]|\\x1b\\][^\\x07\\x1b]*(?:\\x07|\\x1b\\\\)|\\x1b[^\\x1b]|[ \\t\\r\\n])*";
3
8
  /**
4
- * 生成命令完成哨兵标记(随机后缀防撞词)
5
- * @returns 哨兵字符串
9
+ * 定位命令回显行在原始流中的结束位置(供流式增量定位使用):
10
+ * 丢弃窗口开头到"命令回显行结束"之前的一切内容(提示符、PS1 填充、光标定位噪音均在内)。
11
+ * 命令回显行 = 命令文本首次出现所在行(命令本身可能含 \033 等转义)。
12
+ * @param raw 原始终端流(含 ANSI)
13
+ * @param command 本次执行的命令文本
14
+ * @returns 命令回显结束后的字节偏移;未命中命令文本返回 0(此时应视作输出尚未开始)
6
15
  */
7
- export function genSentinel() {
8
- return `__SSH_DONE_${randomBytes(6).toString("hex")}__`;
16
+ export function extractOutputStart(raw, command) {
17
+ const cmd = command.trim();
18
+ if (!cmd)
19
+ return 0;
20
+ // 单条宽松正则一次性匹配命令回显(命令文本 + 字符间 ANSI 间隙):
21
+ // 1) 字符间容忍 ANSI 间隙(WIDE)2) 命令字面 \033 回显可能变真实 ESC(二选一)
22
+ const norm = cmd.replace(/\\033/gi, "\x1b");
23
+ const frags = [...norm].map((c) => (c === "\x1b" ? "(?:\\x1b|\\\\033)" : escRe(c)));
24
+ // 要求命令文本(及尾部 ANSI 重置序列)后紧跟行尾(\r?\n 或串尾),避免子 shell 输出内命令字(如 zsh: not found)误匹配
25
+ const TAIL_ANSI = "(?:\\x1b\\[[0-9;?]*[a-zA-Z]|\\x1b\\][^\\x07\\x1b]*(?:\\x07|\\x1b\\\\)|\\x1b[^\\x1b])*";
26
+ const re = new RegExp(frags.join(ECHO_WIDE) + TAIL_ANSI + "(?=\\r?\\n|$)", "g");
27
+ // 取最后一次匹配:备屏退出(如 cmatrix)会重放主屏历史,其中含旧命令回显,
28
+ // 只有最后一次出现的回显之后才是本次命令的真实输出
29
+ return findLastEndOf(re, raw);
9
30
  }
10
31
  /**
11
- * 剔除 PTY 回显的命令行(含提示符前缀,如 "user@host:~$ cmd"),并清理残留 CR
12
- * 同时剔除含哨兵标记的行(上次命令在哨兵之后到达的回显残留,避免污染下一条输出)
13
- * @param raw 原始输出
14
- * @param cmd 已发送命令(组合串)
15
- * @returns 剔除回显后的输出
32
+ * 剥离"提示符 + 命令回显",只保留程序纯输出:
33
+ * shell 提示符(含背景填充、zle 重绘序列)依赖其自身终端屏幕状态,字节流脱离该状态无法忠实还原,
34
+ * 故丢弃窗口开头到"命令回显行结束"之前的一切内容(提示符、PS1 填充、光标定位噪音均在内)。
35
+ * 命令回显行 = 命令文本首次出现所在行(命令本身可能含 \033 等转义)。
36
+ * 命中后切到"命令文本结束处":zsh 下程序输出可能直接粘连在回显尾(无换行),不能按换行截断。
37
+ * @param raw 原始终端流(含 ANSI)
38
+ * @param command 本次执行的命令文本
39
+ * @returns 纯程序输出原始流(保留程序自身的 ANSI 颜色/进度条,供 web TermScreen 与模型 toModelText)
16
40
  */
17
- export function stripEcho(raw, cmd) {
18
- const SENTINEL_LINE = /__SSH_DONE_[0-9a-f]+__/;
19
- const lines = raw.split(/\r?\n/).map((line) => line.replace(/\r/g, "").trimEnd());
20
- return lines.filter((line) => line !== "" && !line.includes(cmd) && !SENTINEL_LINE.test(line)).join("\n");
41
+ export function extractOutput(raw, command) {
42
+ const end = extractOutputStart(raw, command);
43
+ if (end <= 0)
44
+ return raw;
45
+ return raw.slice(end).replace(/^[\r\n]+/, "");
21
46
  }
22
47
  /**
23
- * 剥离 ANSI 转义序列(颜色、光标控制等)
24
- * @param s 原始字符串
25
- * @returns 清洗后的纯文本
48
+ * 把原始终端流转为给模型看的干净文本:模拟终端屏幕(处理光标移动/清行/覆盖/退格/SGR/OSC),
49
+ * 输出最终屏幕各行文本,再逐行去尾空白并剔除空行。比纯文本正则更鲁棒地处理 PSReadLine
50
+ * 行内重绘、进度条 \r 覆盖等场景。
51
+ * @param raw 原始终端流(含 ANSI)
52
+ * @returns 纯文本输出(供模型消费 / exec 返回值 / read 命令)
26
53
  */
27
- export function cleanAnsi(s) {
28
- // eslint-disable-next-line no-control-regex
29
- return s.replace(/\x1b\[[0-9;?]*[ -/]*[@-~]/g, "").replace(/\x1b\][^\x07]*\x07/g, "");
54
+ export function toModelText(raw) {
55
+ const screen = simulateScreen(stripMarkers(raw));
56
+ return screen
57
+ .split(/\r?\n/)
58
+ .map((l) => l.replace(/\r/g, "").trimEnd())
59
+ .filter((l) => l !== "")
60
+ .join("\n");
30
61
  }
31
62
  /**
32
- * 合并同一行内的 \r / ANSI 清行覆盖(进度条/旋转动画):保留每行最后一次刷新状态,
33
- * 避免 "下载 1%\r下载 2%\r...\r下载 100%" 累积成巨量碎片输出
34
- * 识别 \r 与 ANSI 清行(ESC[2K / ESC[K)+ 光标归位(ESC[G)作为行覆盖分隔符
35
- * @param s 原始输出(含 ANSI 或已清洗均可)
36
- * @returns 合并覆盖后的文本(\r\n 正常换行保留)
63
+ * 模拟终端屏幕渲染:处理 ANSI 光标/清行/清屏/退格/覆盖,输出最终各行的文本。
64
+ * 不保留颜色/SGR,仅提取各行已写字符的最右列,确保最终屏幕文本与真实终端显示一致。
65
+ * @param s 原始字节流(含 ANSI、\b、\r 等)
66
+ * @returns 按行拼接的纯文本(行间 \n 分隔,行内保留空格)
37
67
  */
38
- export function collapseCarriage(s) {
39
- // 覆盖分隔符:裸 \r 或 ANSI 清行/光标归位序列(ESC[2K、ESC[K、ESC[G)
40
- const OVERWRITE = /\r|\x1b\[[0-9]*[KG]/;
41
- // 按 \r\n 拆成行(保留正常 CRLF 换行),行内按覆盖分隔符拆分取最后一段
42
- return s
43
- .split(/\r\n/)
44
- .map((line) => {
45
- const segs = line.split(OVERWRITE).filter((seg) => seg !== "");
46
- if (segs.length <= 1)
47
- return line;
48
- return segs[segs.length - 1];
49
- })
50
- .join("\n");
68
+ function simulateScreen(s) {
69
+ // 网格:每行 { maxCol: 已写最右列, cols: Map<col, char> }
70
+ const rows = [];
71
+ let r = 0; // 当前行
72
+ let c = 0; // 当前列
73
+ let sr = 0; // 保存的光标行(ESC 7 / CSI s)
74
+ let sc = 0; // 保存的光标列
75
+ let altRows = null; // 备屏保存(CSI ?1049h/l)
76
+ const COLS = 240; // 足够宽
77
+ const row = (i) => {
78
+ while (rows.length <= i)
79
+ rows.push({ maxCol: 0, cols: new Map() });
80
+ return rows[i];
81
+ };
82
+ const put = (ch) => {
83
+ const cur = row(r);
84
+ if (c < COLS) {
85
+ cur.cols.set(c, ch);
86
+ if (c >= cur.maxCol)
87
+ cur.maxCol = c + 1;
88
+ }
89
+ c++;
90
+ };
91
+ let i = 0;
92
+ const n = s.length;
93
+ while (i < n) {
94
+ const ch = s[i];
95
+ if (ch === "\x1b") {
96
+ // OSC: ESC ] ... BEL/ESC\ — 整段丢弃
97
+ if (i + 1 < n && s[i + 1] === "]") {
98
+ let j = i + 2;
99
+ while (j < n && s[j] !== "\x07" && !(s[j] === "\x1b" && j + 1 < n && s[j + 1] === "\\"))
100
+ j++;
101
+ if (j >= n)
102
+ break;
103
+ i = s[j] === "\x07" ? j + 1 : j + 2;
104
+ continue;
105
+ }
106
+ // CSI: ESC [ params... final
107
+ if (i + 1 < n && s[i + 1] === "[") {
108
+ let j = i + 2;
109
+ const start = j;
110
+ while (j < n && !/[A-Za-z@]/.test(s[j]))
111
+ j++;
112
+ if (j >= n)
113
+ break;
114
+ const body = s.slice(start, j);
115
+ const final = s[j];
116
+ const p = (d) => { const v = parseInt(d, 10); return Number.isFinite(v) && v > 0 ? v : 1; };
117
+ const va = (d) => { const v = parseInt(d, 10); return Number.isFinite(v) && v >= 0 ? v : 0; };
118
+ if (final === "m") {
119
+ // SGR:忽略颜色
120
+ }
121
+ else if (final === "A") {
122
+ r = Math.max(0, r - p(body));
123
+ }
124
+ else if (final === "B") {
125
+ r += p(body);
126
+ }
127
+ else if (final === "C") {
128
+ c = Math.min(COLS - 1, c + p(body));
129
+ }
130
+ else if (final === "D") {
131
+ c = Math.max(0, c - p(body));
132
+ }
133
+ else if (final === "H" || final === "f") {
134
+ const m = body.split(";");
135
+ r = Math.max(0, (p(m[0]) - 1));
136
+ c = Math.max(0, (p(m[1]) - 1));
137
+ }
138
+ else if (final === "G" || final.charCodeAt(0) === 96) {
139
+ c = Math.max(0, p(body) - 1);
140
+ }
141
+ else if (final === "d") {
142
+ r = Math.max(0, p(body) - 1);
143
+ }
144
+ else if (final === "s") {
145
+ sr = r;
146
+ sc = c;
147
+ }
148
+ else if (final === "u") {
149
+ r = Math.min(sr, rows.length - 1);
150
+ c = sc;
151
+ }
152
+ else if (final === "h" && body.startsWith("?")) {
153
+ // 私有模式 set:?1049h 进入备屏(保存主屏+光标,清屏)
154
+ if (va(body.slice(1)) === 1049) {
155
+ altRows = rows.map((x) => ({ maxCol: x.maxCol, cols: new Map(x.cols) }));
156
+ sr = r;
157
+ sc = c;
158
+ rows.length = 0;
159
+ r = 0;
160
+ c = 0;
161
+ }
162
+ }
163
+ else if (final === "l" && body.startsWith("?")) {
164
+ // 私有模式 reset:?1049l 退出备屏(恢复主屏+光标)
165
+ if (va(body.slice(1)) === 1049 && altRows) {
166
+ rows.length = 0;
167
+ rows.push(...altRows);
168
+ altRows = null;
169
+ r = Math.min(sr, rows.length - 1);
170
+ c = sc;
171
+ }
172
+ }
173
+ else if (final === "K") {
174
+ const mode = va(body);
175
+ const curRow = row(r);
176
+ if (mode === 2) {
177
+ curRow.cols.clear();
178
+ curRow.maxCol = 0;
179
+ }
180
+ else if (mode === 1) {
181
+ for (let ci = 0; ci <= c; ci++) {
182
+ curRow.cols.delete(ci);
183
+ }
184
+ ;
185
+ curRow.maxCol = 0;
186
+ }
187
+ else {
188
+ for (let ci = c; ci < COLS; ci++) {
189
+ curRow.cols.delete(ci);
190
+ }
191
+ ;
192
+ curRow.maxCol = Math.min(curRow.maxCol, c);
193
+ }
194
+ }
195
+ else if (final === "J") {
196
+ const mode = va(body);
197
+ if (mode === 2 || mode === 3) {
198
+ rows.length = 0;
199
+ r = 0;
200
+ c = 0;
201
+ }
202
+ else if (mode === 1) {
203
+ rows.length = 0;
204
+ r = 0;
205
+ c = 0;
206
+ }
207
+ else {
208
+ const cur = row(r);
209
+ for (let ci = c; ci < COLS; ci++)
210
+ cur.cols.delete(ci);
211
+ cur.maxCol = Math.min(cur.maxCol, c);
212
+ rows.length = r + 1;
213
+ }
214
+ }
215
+ // 其余 CSI(滚动/插入/删除等)忽略
216
+ i = j + 1;
217
+ continue;
218
+ }
219
+ // ESC 7 保存光标 / ESC 8 恢复光标
220
+ if (s[i + 1] === "7") {
221
+ sr = r;
222
+ sc = c;
223
+ i += 2;
224
+ continue;
225
+ }
226
+ if (s[i + 1] === "8") {
227
+ r = Math.min(sr, rows.length - 1);
228
+ c = sc;
229
+ i += 2;
230
+ continue;
231
+ }
232
+ // 其他 ESC 序列忽略
233
+ i += 2;
234
+ continue;
235
+ }
236
+ if (ch === "\r") {
237
+ c = 0;
238
+ i++;
239
+ continue;
240
+ }
241
+ if (ch === "\n") {
242
+ r++;
243
+ c = 0;
244
+ i++;
245
+ continue;
246
+ }
247
+ if (ch === "\b") {
248
+ if (c > 0)
249
+ c--;
250
+ i++;
251
+ continue;
252
+ }
253
+ if (ch === "\t") {
254
+ const next = (Math.floor(c / 8) + 1) * 8;
255
+ if (next >= COLS) {
256
+ r++;
257
+ c = 0;
258
+ }
259
+ else
260
+ c = next;
261
+ i++;
262
+ continue;
263
+ }
264
+ // 其他控制字符(\x07 BEL 等)跳过
265
+ if (ch.charCodeAt(0) < 32) {
266
+ i++;
267
+ continue;
268
+ }
269
+ put(ch);
270
+ i++;
271
+ }
272
+ // 构建输出行
273
+ const lines = [];
274
+ for (let ri = 0; ri < rows.length; ri++) {
275
+ const cur = rows[ri];
276
+ if (cur.maxCol === 0) {
277
+ lines.push("");
278
+ continue;
279
+ }
280
+ let line = "";
281
+ for (let ci = 0; ci < cur.maxCol; ci++) {
282
+ line += cur.cols.get(ci) ?? " ";
283
+ }
284
+ lines.push(line);
285
+ }
286
+ return lines.join("\n");
51
287
  }
52
288
  /**
53
- * 剥离尾部 shell 提示符残留(如 "5cb08b77db01:~$ "、"user@host:~/dir$ "、"root@server:/etc#")
54
- * @param s 清洗后文本
55
- * @returns 剥离尾部提示符后的文本
289
+ * 末尾空白清理(原为提示符剥离,现仅做清理,不依赖正则匹配具体提示符形状)
290
+ * @param s 文本
291
+ * @returns 清理后文本
56
292
  */
57
293
  export function stripPrompt(s) {
58
- return s
59
- .replace(/(?:^|[\r\n])\s*(?:[\w.-]+@)?[\w.-]+:[^\r\n]*[$#%] ?(?=[\r\n]|$)/g, "")
60
- .trim();
294
+ return s.trimEnd();
61
295
  }
62
296
  /**
63
297
  * 时间戳格式化为 yyyy-MM-dd HH:mm:ss
package/dist/utils.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,uCAAuC;AAEvC,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAEzC;;;GAGG;AACH,MAAM,UAAU,WAAW;IACzB,OAAO,cAAc,WAAW,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAA;AACzD,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,SAAS,CAAC,GAAW,EAAE,GAAW;IAChD,MAAM,aAAa,GAAG,wBAAwB,CAAA;IAC9C,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAA;IACjF,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,KAAK,EAAE,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AAC3G,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,SAAS,CAAC,CAAS;IACjC,4CAA4C;IAC5C,OAAO,CAAC,CAAC,OAAO,CAAC,4BAA4B,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,qBAAqB,EAAE,EAAE,CAAC,CAAA;AACvF,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,gBAAgB,CAAC,CAAS;IACxC,kDAAkD;IAClD,MAAM,SAAS,GAAG,qBAAqB,CAAA;IACvC,2CAA2C;IAC3C,OAAO,CAAC;SACL,KAAK,CAAC,MAAM,CAAC;SACb,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;QACZ,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,CAAA;QAC9D,IAAI,IAAI,CAAC,MAAM,IAAI,CAAC;YAAE,OAAO,IAAI,CAAA;QACjC,OAAO,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAA;IAC9B,CAAC,CAAC;SACD,IAAI,CAAC,IAAI,CAAC,CAAA;AACf,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAAC,CAAS;IACnC,OAAO,CAAC;SACL,OAAO,CAAC,kEAAkE,EAAE,EAAE,CAAC;SAC/E,IAAI,EAAE,CAAA;AACX,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,OAAO,CAAC,EAAU;IAChC,MAAM,CAAC,GAAG,IAAI,IAAI,CAAC,EAAE,CAAC,CAAA;IACtB,MAAM,CAAC,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAA;IACnD,OAAO,GAAG,CAAC,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC,EAAE,CAAA;AACnI,CAAC"}
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ @font-face {
2
+ font-family: "CaskaydiaCove Nerd Font Mono";
3
+ src: url("/web/fonts/CaskaydiaCoveNerdFontMono-Regular.ttf") format("truetype");
4
+ font-display: swap;
5
+ }
6
+
7
+ html, body { margin: 0; height: 100%; background: #0d1117; color: #c9d1d9; font-family: system-ui, sans-serif; }
8
+ header { position: sticky; top: 0; display: flex; align-items: center; gap: 12px; padding: 10px 16px; background: #161b22; border-bottom: 1px solid #30363d; }
9
+ header h1 { margin: 0; font-size: 14px; color: #e6edf3; font-weight: 600; }
10
+ select { background: #21262d; color: #c9d1d9; border: 1px solid #30363d; padding: 4px 8px; border-radius: 6px; }
11
+ pre { margin: 0; padding: 16px 16px 52px; font-family: "CaskaydiaCove Nerd Font Mono", "Cascadia Code", "Fira Code", "JetBrains Mono", "Noto Sans Mono", "Hack", Consolas, "Courier New", monospace; font-size: 13px; line-height: 1.5; white-space: pre-wrap; overflow: auto; height: calc(100% - 52px); box-sizing: border-box; color: #c9d1d9; }
12
+ .row { display: flex; align-items: stretch; }
13
+ .row .t { box-sizing: border-box; flex: 0 0 172px; color: #8b949e; padding: 0 10px 0 6px; border-right: 1px solid #30363d; white-space: pre; }
14
+ .row .c { flex: 1; padding-left: 12px; white-space: pre; }
15
+ body:not(.show-time) .row .t { display: none; }
16
+ .row.cmdline { background: #161b22; box-shadow: inset 3px 0 0 #58a6ff; font-weight: 600; }
17
+ .tgl { font-size: 12px; color: #8b949e; display: flex; align-items: center; gap: 4px; white-space: nowrap; }
18
+ #meta { font-size: 12px; color: #8b949e; }
19
+ ::-webkit-scrollbar { width: 10px; height: 10px; }
20
+ ::-webkit-scrollbar-track { background: #161b22; }
21
+ ::-webkit-scrollbar-thumb { background: #30363d; border-radius: 5px; border: 2px solid #161b22; }
22
+ ::-webkit-scrollbar-thumb:hover { background: #484f58; }
23
+ ::-webkit-scrollbar-corner { background: #161b22; }
24
+ * { scrollbar-width: thin; scrollbar-color: #30363d #161b22; }
25
+ .to-bottom-btn {
26
+ position: fixed; right: 24px; bottom: 24px; display: none;
27
+ background: #238636; color: #fff; border: none; border-radius: 20px;
28
+ padding: 8px 16px; font-size: 13px; cursor: pointer; z-index: 10;
29
+ box-shadow: 0 4px 12px rgba(0,0,0,.4);
30
+ }
31
+ .to-bottom-btn:hover { background: #2ea043; }
32
+ .sep hr { border: none; border-top: 1px dashed #30363d; margin: 8px 0; }
33
+ .del-btn { background: none; border: 1px solid #30363d; color: #f85149; border-radius: 4px; cursor: pointer; padding: 2px 8px; font-size: 14px; line-height: 1; display: none; }
34
+ .del-btn:hover { background: #f85149; color: #fff; }
35
+ .del-btn.show { display: inline-block; }
36
+ #cmdbar { position: fixed; bottom: 8px; left: 16px; right: 16px; display: flex; gap: 6px; }
37
+ #cmdInput { flex: 1; box-sizing: border-box; background: #0d1117; color: #c9d1d9; border: 1px solid #30363d; border-radius: 6px; padding: 8px 12px; font-family: "CaskaydiaCove Nerd Font Mono", "Cascadia Code", "Fira Code", "JetBrains Mono", "Noto Sans Mono", "Hack", Consolas, "Courier New", monospace; font-size: 13px; outline: none; resize: none; max-height: 120px; overflow-y: auto; }
38
+ #cmdInput:focus { border-color: #58a6ff; }
39
+ #cmdInput:disabled { opacity: 0.4; }
40
+ #cmdKey { flex: 0 0 auto; width: 72px; text-align: center; background: #21262d; color: #c9d1d9; border: 1px solid #30363d; border-radius: 6px; padding: 8px 6px; font-size: 12px; white-space: nowrap; overflow: hidden; cursor: crosshair; user-select: none; display: flex; align-items: center; justify-content: center; transition: border-color .15s; }
41
+ #cmdKey.capture { border-color: #e67e22; animation: pulse 0.6s ease-in-out infinite alternate; }
42
+ #cmdKey.disabled { opacity: 0.35; cursor: default; }
43
+ @keyframes pulse { from { opacity: 0.6; } to { opacity: 1; } }
@@ -0,0 +1,2 @@
1
+ "use strict";(()=>{var q=Object.defineProperty;var z=(n,e,t)=>e in n?q(n,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):n[e]=t;var g=(n,e,t)=>z(n,typeof e!="symbol"?e+"":e,t);function Z(n){let e=n.match(P);if(e&&e.length>0){let t=parseInt(e[e.length-1].replace(/<SSH_DONE:|>/g,""),10);return{text:n.replace(P,""),exitCode:isNaN(t)?void 0:t}}return{text:n}}var B=window.__I18N__,k=window.__PTY_COLS__||120,W=["#010101","#de382b","#39b54a","#ffc005","#006fb8","#762671","#2cb3e9","#c9d1d9"],j=["#666666","#ff7b72","#3fb950","#d29922","#58a6ff","#bc8cff","#39c5cf","#f0f6fc"],M=class{constructor(e){g(this,"cols");g(this,"grid",[]);g(this,"r",0);g(this,"c",0);g(this,"fg",null);g(this,"bg",null);g(this,"bold",!1);g(this,"sr",0);g(this,"sc",0);g(this,"altGrid",null);this.cols=e}_row(e){for(;this.grid.length<=e;){let t=[];for(let i=0;i<this.cols;i++)t.push({ch:" ",fg:null,bg:null,bold:!1});this.grid.push(t)}return this.grid[e]}_put(e){let t=this._row(this.r);t[this.c]={ch:e,fg:this.fg,bg:this.bg,bold:this.bold},this.c++,this.c>=this.cols&&(this.c=0,this.r++)}write(e){let t=0,i=e.length;for(;t<i;){let l=e[t];if(l==="\x1B"){if(t+1<i&&e[t+1]==="]"){let r=t+2;for(;r<i&&e[r]!=="\x07"&&!(e[r]==="\x1B"&&e[r+1]==="\\");)r++;if(r>=i)break;t=e[r]==="\x07"?r+1:r+2;continue}if(t+1<i&&e[t+1]==="["){let r=t+2,o=r;for(;r<i&&!/[A-Za-z@]/.test(e[r]);)r++;if(r>=i)break;this._csi(e.slice(o,r),e[r]),t=r+1;continue}if(e[t+1]==="7"){this.sr=this.r,this.sc=this.c,t+=2;continue}if(e[t+1]==="8"){this.r=Math.min(this.sr,this.grid.length-1),this.c=this.sc,t+=2;continue}t+=2;continue}if(l==="\r"){this.c=0,t++;continue}if(l===`
2
+ `){this.r++,this.c=0,t++;continue}if(l==="\b"){this.c>0&&this.c--,t++;continue}if(l===" "){this.c=(Math.floor(this.c/8)+1)*8,this.c>=this.cols&&(this.c=0,this.r++),t++;continue}if(l.charCodeAt(0)<32){t++;continue}this._put(l),t++}}_csi(e,t){let i=e.replace(/^[?]/,"");if(t==="m"){let o=i?i.split(";").map(s=>parseInt(s,10)):[0];(!i||o.indexOf(0)>=0)&&(this.fg=null,this.bg=null,this.bold=!1);for(let s of o)s===1?this.bold=!0:s===22?this.bold=!1:s>=30&&s<=37?this.fg=W[s-30]:s===39?this.fg=null:s>=90&&s<=97?this.fg=j[s-90]:s>=40&&s<=47?this.bg=W[s-40]:s===49?this.bg=null:s>=100&&s<=107&&(this.bg=j[s-100]);return}let l=o=>{let s=parseInt(o,10);return Number.isFinite(s)&&s>0?s:1},r=o=>{let s=parseInt(o,10);return Number.isFinite(s)&&s>=0?s:0};if(t==="A")this.r=Math.max(0,this.r-l(i));else if(t==="B")this.r+=l(i);else if(t==="C")this.c=Math.min(this.cols-1,this.c+l(i));else if(t==="D")this.c=Math.max(0,this.c-l(i));else if(t==="H"||t==="f"){let o=i.split(";");this.r=l(o[0])-1,this.c=l(o[1])-1}else if(t==="G"||t.charCodeAt(0)===96)this.c=Math.max(0,l(i)-1);else if(t==="d")this.r=Math.max(0,l(i)-1);else if(t==="J"){let o=r(i);if(o===2||o===3){for(let s=0;s<this.grid.length;s++){let c=this._row(s);for(let d=0;d<this.cols;d++)c[d]={ch:" ",fg:null,bg:null,bold:!1}}this.r=0,this.c=0}else if(o===1)for(let s=0;s<=this.r;s++){let c=this._row(s),d=s===this.r?this.c:this.cols;for(let u=0;u<d;u++)c[u]={ch:" ",fg:null,bg:null,bold:!1}}else for(let s=this.r;s<this.grid.length;s++){let c=this._row(s),d=s===this.r?this.c:0;for(let u=d;u<this.cols;u++)c[u]={ch:" ",fg:null,bg:null,bold:!1}}}else if(t==="K"){let o=r(i),s=this._row(this.r);if(o===2)for(let c=0;c<this.cols;c++)s[c]={ch:" ",fg:null,bg:null,bold:!1};else if(o===1)for(let c=0;c<=this.c;c++)s[c]={ch:" ",fg:null,bg:null,bold:!1};else for(let c=this.c;c<this.cols;c++)s[c]={ch:" ",fg:null,bg:null,bold:!1}}else t==="s"?(this.sr=this.r,this.sc=this.c):t==="u"?(this.r=Math.min(this.sr,this.grid.length-1),this.c=this.sc):(t==="h"||t==="l")&&e.startsWith("?")&&r(e.slice(1).split(";")[0])===1049&&(t==="h"?(this.altGrid=this.grid.map(s=>s.slice()),this.sr=this.r,this.sc=this.c,this.grid=[],this.r=0,this.c=0):this.altGrid&&(this.grid=this.altGrid,this.altGrid=null,this.r=Math.min(this.sr,this.grid.length-1),this.c=this.sc))}render(){let e=i=>i.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;"),t=[];for(let i=0;i<this.grid.length;i++){let l=this.grid[i],r=this.cols;for(;r>0&&l[r-1].ch===" ";)r--;if(r===0)continue;let o="",s=null;for(let c=0;c<r;c++){let d=l[c],u=[];d.bold&&u.push("font-weight:bold"),d.fg&&u.push("color:"+d.fg),d.bg&&u.push("background-color:"+d.bg);let E=u.join(";");E!==s&&(s&&(o+="</span>"),s=E,E&&(o+='<span style="'+E+'">')),o+=e(d.ch)}s&&(o+="</span>"),t.push(o)}return t}},a=null,f=[],y=!0,h=null,b=!1;function Q(){a=new WebSocket("ws://"+window.location.host+"/ws"),a.onopen=()=>{a.send(JSON.stringify({type:"list"})),G()},a.onmessage=n=>{let e;try{e=JSON.parse(n.data)}catch{return}switch(e.type){case"sessions":V(e);break;case"snapshot":$(e);break;case"run":ee(e);break;case"runEnd":break;case"cmdStart":ne(e);break;case"raw":te(e);break}},a.onclose=()=>{let n=document.getElementById("term");n.textContent="WebSocket disconnected"}}function V(n){let e=n.sessions||[],t=document.getElementById("session"),i=e.map(o=>o.sessionID+"|"+o.terminals.length).join(","),l=t.value,r=document.getElementById("terminal").value;f=e,t.innerHTML="";for(let o of f){let s=document.createElement("option");s.value=o.sessionID,s.textContent=R(o)+" ("+o.terminals.length+" "+B.terminals+")",t.appendChild(s)}l&&[...t.options].some(o=>o.value===l)?t.value=l:t.selectedIndex=f.length?0:-1,O(r)}function X(){let n=document.getElementById("session").value,e=document.getElementById("terminal").value,i=f.find(o=>o.sessionID===n)?.terminals.find(o=>(o.name||"default")===e),l=!i||!i.connected,r=document.getElementById("cmdKey");r.classList.toggle("disabled",l),r.title=l?"":B.sendCtrlC||""}function O(n){let e=document.getElementById("session"),t=document.getElementById("terminal"),i=document.getElementById("delTerm"),l=e.value,r=f.find(s=>s.sessionID===l);t.innerHTML="";for(let s of r?r.terminals:[]){let c=document.createElement("option");c.value=s.name||"default",c.textContent=(s.name||"default")+(s.kind==="local"?" ["+B.local+"]":"")+" "+(s.connected?"\u25CF":"\u25CB")+(s.busy?" \u23F3":""),t.appendChild(c)}n&&[...t.options].some(s=>s.value===n)?t.value=n:t.selectedIndex=t.options.length?0:-1;let o=r?.terminals.find(s=>(s.name||"default")===t.value);i.classList.toggle("show",!!(o&&!o.connected)),X(),J(),w="",S="",G()}var w="",S="",x=!1;function G(){let n=document.getElementById("session").value,e=document.getElementById("terminal").value;!n||!e||!a||a.readyState!==WebSocket.OPEN||n===w&&e===S||(w=n,S=e,h=null,x=!1,a.send(JSON.stringify({type:"subscribe",sessionID:n,name:e,raw:b})))}function $(n){let e=document.getElementById("term"),t=document.getElementById("toBottom");if(n.notFound){e.innerHTML='<div class="row"><span class="t"></span><span class="c">'+B.sessionGone+"</span></div>",document.getElementById("meta").textContent="",oe(n.sessionID,n.name);return}let i=n.pairs||[],l=!b&&document.getElementById("showTime").checked;document.body.classList.toggle("show-time",l);let r=i.filter(o=>o.type==="cmd").length;J(r),!(b&&x)&&(h=null,e.innerHTML=se(i,l),y?(e.scrollTop=e.scrollHeight,t.style.display="none"):t.style.display=e.scrollHeight>e.clientHeight?"block":"none")}function J(n){let e=document.getElementById("session").value,t=document.getElementById("terminal").value;if(!e){document.getElementById("meta").textContent="";return}let l=f.find(c=>c.sessionID===e)?.terminals.find(c=>(c.name||"default")===t),r=l?l.host?l.host+(l.port?":"+l.port:""):l.program||"":"",o=r?" ["+r+"]":"",s=n!==void 0?" \xB7 "+n+" "+B.commands:"";document.getElementById("meta").textContent=e+"/"+t+o+s}function ee(n){if(!n.data||b)return;let e=document.getElementById("term"),t=document.getElementById("toBottom"),i=document.getElementById("runBlock");h||(h=new M(k),i=document.createElement("div"),i.id="runBlock",e.appendChild(i)),h.write(K(n.data));let l=h.render().map(r=>'<div class="row"><span class="t"></span><span class="c">'+r+"</span></div>").join("");i.innerHTML=l,y?(e.scrollTop=e.scrollHeight,t.style.display="none"):t.style.display="block"}var I=null;function te(n){if(!b)return;let e=document.getElementById("term"),t=document.getElementById("toBottom");if(x||(x=!0,e.innerHTML="",I=null),n.reset?(I=new M(k),I.write(n.data||"")):n.data&&(I||(I=new M(k)),I.write(n.data)),I){let i=I.render().map(l=>'<div class="row"><span class="c">'+l+"</span></div>").join("");e.innerHTML=i}y?(e.scrollTop=e.scrollHeight,t.style.display="none"):t.style.display=e.scrollHeight>e.clientHeight?"block":"none"}function ne(n){if(b||!n.command)return;let e=document.getElementById("term"),t=document.getElementById("toBottom"),i=document.getElementById("runBlock");i&&i.remove(),h=null;let r=document.getElementById("showTime").checked?Y(Date.now()):"",o=document.createElement("div");o.className="row cmdline",o.innerHTML='<span class="t">'+r+'</span><span class="c">'+F(n.command)+"</span>",e.appendChild(o),y&&(e.scrollTop=e.scrollHeight,t.style.display="none")}function F(n){return n.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}var P=/<SSH_DONE:(-?\d+)>/g;function K(n){return n.replace(P,"")}function se(n,e){let t=[],i=null;for(let l of n){if(l.type==="sep"){t.push('<div class="sep"><hr></div>');continue}if(l.type==="cmd"){i={ts:l.ts,endTs:l.endTs};let r=l.ts?Y(l.ts):"";t.push('<div class="row cmdline"><span class="t">'+r+'</span><span class="c">'+F(l.text)+"</span></div>")}else if(l.type==="run"){h=new M(k),h.write(K(l.text));let r=h.render().map(o=>'<div class="row"><span class="t"></span><span class="c">'+o+"</span></div>").join("");t.push('<div id="runBlock">'+r+"</div>")}else{let r=Z(l.text),o=new M(k);o.write(r.text);let s=o.render(),c=e?ie(i,r.exitCode):"",d=(u,E)=>'<div class="row"><span class="t">'+u+'</span><span class="c">'+E+"</span></div>";s.length===0?t.push(d(c,"&nbsp;")):s.forEach((u,E)=>t.push(d(E===s.length-1?c:"",u))),i=null}}return t.join("")}function ie(n,e){let t=[];if(n&&n.ts&&n.endTs&&t.push("+"+((n.endTs-n.ts)/1e3).toFixed(3)+"s"),e!==void 0){let i=e===0?"#3fb950":"#ff7b72";t.push('<span style="color:'+i+'">'+(e===0?"\u2713":"\u2717 "+e)+"</span>")}return t.join(" ")}function R(n){if(n.title&&n.title.trim())return n.title;let e=n.terminals&&n.terminals[0];return e&&e.host?(e.user?e.user+"@":"")+e.host:n.sessionID.slice(0,8)+"\u2026"}function Y(n){let e=new Date(n),t=i=>String(i).padStart(2,"0");return e.getFullYear()+"-"+t(e.getMonth()+1)+"-"+t(e.getDate())+" "+t(e.getHours())+":"+t(e.getMinutes())+":"+t(e.getSeconds())}function oe(n,e){let t=f.find(o=>o.sessionID===n);t&&(t.terminals=t.terminals.filter(o=>(o.name||"default")!==e),t.terminals.length===0&&(f=f.filter(o=>o.sessionID!==n)));let i=document.getElementById("session"),l=i.value;i.innerHTML="";for(let o of f){let s=document.createElement("option");s.value=o.sessionID,s.textContent=R(o)+" ("+o.terminals.length+" "+B.terminals+")",i.appendChild(s)}l&&[...i.options].some(o=>o.value===l)?i.value=l:i.selectedIndex=f.length?0:-1;let r=document.getElementById("terminal").value;O(r)}function le(n){n&&(y=!0);let e=document.getElementById("terminal").value;O(e)}function re(){let n=document.getElementById("showTime");localStorage.setItem("showTime",n.checked?"1":"0"),document.body.classList.toggle("show-time",n.checked)}function ce(){let n=document.getElementById("debugMode");b=n.checked,localStorage.setItem("debugMode",n.checked?"1":"0"),document.body.classList.toggle("debug",n.checked),w="",S="",G()}function ae(){let n=document.getElementById("session").value,e=document.getElementById("terminal").value;if(!e||!a||a.readyState!==WebSocket.OPEN)return;let t=f.find(r=>r.sessionID===n),i=t?.terminals.find(r=>(r.name||"default")===e);if(!i||i.connected)return;a.send(JSON.stringify({type:"deleteTerminal",sessionID:n,name:e})),t&&(t.terminals=t.terminals.filter(r=>(r.name||"default")!==e),t.terminals.length===0&&(f=f.filter(r=>r.sessionID!==n)));let l=document.getElementById("terminal").value;O(l!==e?l:void 0)}function de(){let n=document.getElementById("term"),e=document.getElementById("toBottom");n.scrollTop=n.scrollHeight,y=!0,e.style.display="none"}var U=document.getElementById("showTime");U.checked=localStorage.getItem("showTime")==="1";document.body.classList.toggle("show-time",U.checked);var ue=document.getElementById("debugMode");b=localStorage.getItem("debugMode")==="1";ue.checked=b;document.body.classList.toggle("debug",b);var C=document.getElementById("term"),me=document.getElementById("toBottom");C.addEventListener("scroll",()=>{C.scrollTop+C.clientHeight>=C.scrollHeight-30?(y=!0,me.style.display="none"):y=!1});var L=[],T=-1,D=-1,_="",m=document.getElementById("cmdInput");m.placeholder=B.cmdPlaceholder||"";m.addEventListener("keydown",n=>{if(n.key==="Tab"){n.preventDefault();let o=m.value.trim();if(!o)return;_!==o&&(D=-1,_=o);let s=L.filter(c=>c.startsWith(_));if(s.length===0)return;D=(D+1)%s.length,m.value=s[D],v();return}if(_="",n.key==="ArrowUp"){n.preventDefault(),T>0&&(T--,m.value=L[T],v());return}if(n.key==="ArrowDown"){n.preventDefault(),T<L.length-1?(T++,m.value=L[T],v()):(T=L.length,m.value="",v());return}if(n.key!=="Enter")return;if(n.shiftKey){v();return}n.preventDefault();let e=document.getElementById("session").value,t=document.getElementById("terminal").value,l=f.find(o=>o.sessionID===e)?.terminals.find(o=>(o.name||"default")===t);if(!l||!l.connected)return;if(l.busy){let o=m.value;if(m.value="",v(),!o||!a||a.readyState!==WebSocket.OPEN)return;(w!==e||S!==t)&&(w=e,S=t,a.send(JSON.stringify({type:"subscribe",sessionID:e,name:t}))),a.send(JSON.stringify({type:"send",sessionID:e,name:t,text:o+"\r"})),y=!0;return}let r=m.value.trim();r&&(m.value="",v(),L.push(r),T=L.length,!(!a||a.readyState!==WebSocket.OPEN)&&((w!==e||S!==t)&&(w=e,S=t,a.send(JSON.stringify({type:"subscribe",sessionID:e,name:t}))),a.send(JSON.stringify({type:"exec",sessionID:e,name:t,command:r})),y=!0))});function v(){m.style.height="auto",m.style.height=Math.min(m.scrollHeight,120)+"px"}m.addEventListener("input",v);var p=document.getElementById("cmdKey"),N=B.sendCtrlC||"Ctrl-C",H=!1;p.textContent=N;function A(n){if(!H)return;H=!1,p.classList.remove("capture"),document.removeEventListener("keyup",A);let e=document.getElementById("session").value,t=document.getElementById("terminal").value;if(!e||!a||a.readyState!==WebSocket.OPEN)return;let i,l;if(n.ctrlKey&&n.key!=="Control"){let r=n.key.toLowerCase().charCodeAt(0)-96;r>=1&&r<=26?(i="\\x"+r.toString(16).padStart(2,"0"),l="Ctrl-"+n.key.toUpperCase()):(i="",l="")}else n.key==="Enter"?(i="\\r",l="\u21B5"):n.key==="Escape"?(i="\\x1b",l="Esc"):n.key==="Tab"?(i="\\t",l="Tab"):n.key==="Backspace"?(i="\\x7f",l="\u232B"):n.key===" "||n.key==="Space"?(i=" ",l="Space"):n.key.length===1?(i=n.key,l=n.key):(i="\\x03",l="Ctrl-C");i&&a.send(JSON.stringify({type:"send",sessionID:e,name:t,text:i})),p.textContent=l||N,setTimeout(()=>{H||(p.textContent=N)},1500)}p.addEventListener("mouseenter",()=>{let n=document.getElementById("session").value,e=document.getElementById("terminal").value;n&&(H=!0,p.classList.add("capture"),p.textContent="...",document.addEventListener("keyup",A))});p.addEventListener("mouseleave",()=>{H&&(H=!1,p.classList.remove("capture"),document.removeEventListener("keyup",A),p.textContent=N)});Object.assign(window,{onSessionChange:le,onShowTimeChange:re,onDebugModeChange:ce,deleteTerminal:ae,scrollToNewest:de});Q();})();
@@ -0,0 +1,28 @@
1
+ <!DOCTYPE html>
2
+ <html lang="{{lang}}">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <title>{{web_title}}</title>
6
+ <link rel="stylesheet" href="/web/app.css">
7
+ </head>
8
+ <body>
9
+ <header>
10
+ <h1>{{web_title}}</h1>
11
+ <select id="session" onchange="onSessionChange(true)"></select>
12
+ <select id="terminal" onchange="onSessionChange(true)"></select>
13
+ <button id="delTerm" class="del-btn" onclick="deleteTerminal()" title="{{web_delete_terminal}}">×</button>
14
+ <label class="tgl"><input type="checkbox" id="showTime" onchange="onShowTimeChange()"> {{web_time}}</label>
15
+ <label class="tgl"><input type="checkbox" id="debugMode" onchange="onDebugModeChange()"> Raw</label>
16
+ <span id="meta"></span>
17
+ </header>
18
+ <pre id="term">{{web_loading}}</pre>
19
+ <button id="toBottom" class="to-bottom-btn" onclick="scrollToNewest()">{{web_new_messages}}</button>
20
+ <div id="cmdbar">
21
+ <span id="cmdKey" title="{{web_send_ctrlc}}">Ctrl-C</span>
22
+ <textarea id="cmdInput" rows="1" placeholder="{{web_cmd_placeholder}}"></textarea>
23
+ </div>
24
+ <script>window.__I18N__ = __I18N_JSON__;</script>
25
+ <script>window.__PTY_COLS__ = __PTY_COLS_VAL__;</script>
26
+ <script src="/web/app.js"></script>
27
+ </body>
28
+ </html>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xiaoqiong0v0/opencode-ssh-tool",
3
- "version": "0.4.0",
3
+ "version": "0.5.1",
4
4
  "type": "module",
5
5
  "description": "长驻交互式 SSH 会话工具插件,远程命令受 opencode 权限管控",
6
6
  "files": [
@@ -13,7 +13,7 @@
13
13
  ".": "./dist/index.js"
14
14
  },
15
15
  "scripts": {
16
- "build": "tsc",
16
+ "build": "tsc && node web/build.mjs",
17
17
  "prepublishOnly": "npm run build"
18
18
  },
19
19
  "repository": {
@@ -38,7 +38,8 @@
38
38
  "dependencies": {
39
39
  "@xiaoqiong0v0/opencode-plugin-logger": "^1.0.4",
40
40
  "ssh2": "^1.16.0",
41
- "string-argv": "^0.3.2"
41
+ "string-argv": "^0.3.2",
42
+ "ws": "^8.21.3"
42
43
  },
43
44
  "peerDependencies": {
44
45
  "@opencode-ai/plugin": "^1.18.4"
@@ -46,6 +47,8 @@
46
47
  "devDependencies": {
47
48
  "@types/node": "^26.1.1",
48
49
  "@types/ssh2": "^1.15.4",
50
+ "@types/ws": "^8.18.1",
51
+ "esbuild": "^0.28.2",
49
52
  "typescript": "^7.0.2"
50
53
  }
51
54
  }