auvezy-terminal-remote 0.3.1 → 0.4.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.
- package/dist/cli.js +130 -13
- package/frontend-dist/assets/eruda-Djm-IyEG.js +366 -0
- package/frontend-dist/assets/index-CmMtEAyI.js +335 -0
- package/frontend-dist/assets/index-UEuOXzz9.css +32 -0
- package/frontend-dist/index.html +8 -6
- package/frontend-dist/sw.js +1 -1
- package/package.json +1 -1
- package/frontend-dist/assets/index-BALTbT9e.js +0 -325
- package/frontend-dist/assets/index-DUpRzupd.css +0 -32
package/dist/cli.js
CHANGED
|
@@ -61,9 +61,11 @@ function ensureDefaultUserConfig(input) {
|
|
|
61
61
|
const userCommands = Array.isArray(src.commands) && src.commands.length > 0 ? src.commands : null;
|
|
62
62
|
const commandsLegacy = userCommands !== null && userCommands.some((c) => typeof c.group !== "string" || c.group.length === 0);
|
|
63
63
|
const commands = userCommands === null || commandsLegacy ? DEFAULT_COMMANDS : userCommands;
|
|
64
|
-
|
|
64
|
+
const useInputBarValue = typeof src.input?.useInputBar === "boolean" ? src.input.useInputBar : DEFAULT_INPUT.useInputBar;
|
|
65
|
+
const inputPrefs = { useInputBar: useInputBarValue };
|
|
66
|
+
return { ...src, shortcuts, commands, input: inputPrefs };
|
|
65
67
|
}
|
|
66
|
-
var SHORTCUT_GROUPS, DEFAULT_SHORTCUTS, COMMAND_GROUPS, DEFAULT_COMMANDS;
|
|
68
|
+
var SHORTCUT_GROUPS, DEFAULT_SHORTCUTS, COMMAND_GROUPS, DEFAULT_COMMANDS, DEFAULT_INPUT;
|
|
67
69
|
var init_defaults = __esm({
|
|
68
70
|
"shared/dist/defaults.js"() {
|
|
69
71
|
"use strict";
|
|
@@ -253,6 +255,9 @@ var init_defaults = __esm({
|
|
|
253
255
|
}
|
|
254
256
|
];
|
|
255
257
|
DEFAULT_COMMANDS = COMMAND_GROUPS.flatMap((g) => g.items.map((c) => ({ ...c, group: g.id })));
|
|
258
|
+
DEFAULT_INPUT = {
|
|
259
|
+
useInputBar: true
|
|
260
|
+
};
|
|
256
261
|
}
|
|
257
262
|
});
|
|
258
263
|
|
|
@@ -818,7 +823,7 @@ var init_config_routes = __esm({
|
|
|
818
823
|
});
|
|
819
824
|
|
|
820
825
|
// backend/dist/constants.js
|
|
821
|
-
var WS_FLUSH_INTERVAL_MS, WS_MAX_CHUNK_BYTES, WS_HIGH_WATERMARK_BYTES, FILE_LOCK_RETRIES, FILE_LOCK_RETRY_INTERVAL_MS, FILE_LOCK_STALE_MS, IP_MONITOR_INTERVAL_MS, IP_MONITOR_STABILITY_THRESHOLD, PTY_DEFAULT_COLS, PTY_DEFAULT_ROWS, PTY_TERM_NAME, SHUTDOWN_WS_FLUSH_DELAY_MS, SHUTDOWN_FORCE_EXIT_MS, DOUBLE_CTRL_C_WINDOW_MS, PORT_FINDER_MAX_ATTEMPTS, STOP_INSTANCE_GRACE_MS, STOP_INSTANCE_POLL_INTERVAL_MS, ATTACH_RECONNECT_DELAYS_MS;
|
|
826
|
+
var WS_FLUSH_INTERVAL_MS, WS_MAX_CHUNK_BYTES, WS_HIGH_WATERMARK_BYTES, FILE_LOCK_RETRIES, FILE_LOCK_RETRY_INTERVAL_MS, FILE_LOCK_STALE_MS, IP_MONITOR_INTERVAL_MS, IP_MONITOR_STABILITY_THRESHOLD, PTY_DEFAULT_COLS, PTY_DEFAULT_ROWS, PTY_TERM_NAME, DOUBLE_PULSE_DELAY_MS, SHUTDOWN_WS_FLUSH_DELAY_MS, SHUTDOWN_FORCE_EXIT_MS, DOUBLE_CTRL_C_WINDOW_MS, PORT_FINDER_MAX_ATTEMPTS, STOP_INSTANCE_GRACE_MS, STOP_INSTANCE_POLL_INTERVAL_MS, ATTACH_RECONNECT_DELAYS_MS;
|
|
822
827
|
var init_constants2 = __esm({
|
|
823
828
|
"backend/dist/constants.js"() {
|
|
824
829
|
"use strict";
|
|
@@ -833,6 +838,7 @@ var init_constants2 = __esm({
|
|
|
833
838
|
PTY_DEFAULT_COLS = 80;
|
|
834
839
|
PTY_DEFAULT_ROWS = 24;
|
|
835
840
|
PTY_TERM_NAME = "xterm-256color";
|
|
841
|
+
DOUBLE_PULSE_DELAY_MS = 50;
|
|
836
842
|
SHUTDOWN_WS_FLUSH_DELAY_MS = 500;
|
|
837
843
|
SHUTDOWN_FORCE_EXIT_MS = 2e3;
|
|
838
844
|
DOUBLE_CTRL_C_WINDOW_MS = 500;
|
|
@@ -1437,7 +1443,9 @@ function detectDisplayIp(hostHint) {
|
|
|
1437
1443
|
return hostHint;
|
|
1438
1444
|
}
|
|
1439
1445
|
const ifaces = networkInterfaces();
|
|
1440
|
-
const
|
|
1446
|
+
const tailscale = [];
|
|
1447
|
+
const lanReal = [];
|
|
1448
|
+
const lanVirtual = [];
|
|
1441
1449
|
const linkLocals = [];
|
|
1442
1450
|
for (const list of Object.values(ifaces)) {
|
|
1443
1451
|
if (!list)
|
|
@@ -1448,14 +1456,20 @@ function detectDisplayIp(hostHint) {
|
|
|
1448
1456
|
if (info.family !== "IPv4")
|
|
1449
1457
|
continue;
|
|
1450
1458
|
const ip = info.address;
|
|
1451
|
-
if (
|
|
1452
|
-
|
|
1459
|
+
if (isTailscaleIp(ip)) {
|
|
1460
|
+
tailscale.push(ip);
|
|
1461
|
+
} else if (isPrivateIp(ip)) {
|
|
1462
|
+
const first = Number(ip.split(".")[0]);
|
|
1463
|
+
if (first === 172)
|
|
1464
|
+
lanVirtual.push(ip);
|
|
1465
|
+
else
|
|
1466
|
+
lanReal.push(ip);
|
|
1453
1467
|
} else if (isLinkLocal(ip)) {
|
|
1454
1468
|
linkLocals.push(ip);
|
|
1455
1469
|
}
|
|
1456
1470
|
}
|
|
1457
1471
|
}
|
|
1458
|
-
return
|
|
1472
|
+
return tailscale[0] ?? lanReal[0] ?? lanVirtual[0] ?? linkLocals[0] ?? "127.0.0.1";
|
|
1459
1473
|
}
|
|
1460
1474
|
function isShareableIpv6(ip, info) {
|
|
1461
1475
|
if (!ip.includes(":"))
|
|
@@ -1627,6 +1641,16 @@ var init_pty_manager = __esm({
|
|
|
1627
1641
|
_exited = false;
|
|
1628
1642
|
_cols = PTY_DEFAULT_COLS;
|
|
1629
1643
|
_rows = PTY_DEFAULT_ROWS;
|
|
1644
|
+
/**
|
|
1645
|
+
* 当前是否处于 alt-screen(DECSET 1049 / 1047 / 47)。
|
|
1646
|
+
* 通过扫描 PTY 输出序列实时维护:
|
|
1647
|
+
* - vim/htop/tmux/less 等全屏 TUI 进入时切 true(它们自己整屏重画,对
|
|
1648
|
+
* resize 反应正常,不需要 double-pulse hack)
|
|
1649
|
+
* - claude/zsh prompt 等增量重画 TUI 始终为 false → 需要 double-pulse
|
|
1650
|
+
*/
|
|
1651
|
+
_inAltScreen = false;
|
|
1652
|
+
/** double-pulse resize 的中间帧定时器 */
|
|
1653
|
+
_doublePulseTimer = null;
|
|
1630
1654
|
get cols() {
|
|
1631
1655
|
return this._cols;
|
|
1632
1656
|
}
|
|
@@ -1662,6 +1686,7 @@ var init_pty_manager = __esm({
|
|
|
1662
1686
|
env: { ...process.env, ...opts.env }
|
|
1663
1687
|
});
|
|
1664
1688
|
this.process.onData((data) => {
|
|
1689
|
+
this.scanAltScreenToggle(data);
|
|
1665
1690
|
this.emit("data", data);
|
|
1666
1691
|
});
|
|
1667
1692
|
this.process.onExit(({ exitCode, signal }) => {
|
|
@@ -1699,6 +1724,18 @@ var init_pty_manager = __esm({
|
|
|
1699
1724
|
* webapp resize → ws → PTY.resize → emit 'resize' → broadcast →
|
|
1700
1725
|
* webapp 收到 terminal_resize → 触发 fit → 又算出同尺寸 → 再发 resize → ...
|
|
1701
1726
|
* 同尺寸跳过让链路在第二步就断掉
|
|
1727
|
+
*
|
|
1728
|
+
* Double-pulse hack(仅 normal-screen / 增量重画 TUI):
|
|
1729
|
+
* Claude Code (Ink) / blessed / prompt-toolkit / readline REPL 等用相对
|
|
1730
|
+
* 坐标增量重画的程序,收到 SIGWINCH 后只对"宽度变窄"分支才会清屏 +
|
|
1731
|
+
* 重新 layout(Ink 源码:`if (currentWidth < lastTerminalWidth) clear()`)。
|
|
1732
|
+
* 宽度变宽时既不清前帧也不重排已渲染历史 → 视觉上"没响应"。
|
|
1733
|
+
*
|
|
1734
|
+
* workaround:先 resize(cols-1) 让 Ink 走 width-shrink 分支强制清屏,
|
|
1735
|
+
* 50ms 后再 resize(cols) 回到目标尺寸。代价是程序会多一帧重画。
|
|
1736
|
+
*
|
|
1737
|
+
* alt-screen 程序(vim/tmux/htop/less)自己会在 SIGWINCH 时整屏重画,
|
|
1738
|
+
* 不需要也不应该 double-pulse(多余 SIGWINCH 让它们闪一下)→ 短路。
|
|
1702
1739
|
*/
|
|
1703
1740
|
resize(cols, rows) {
|
|
1704
1741
|
if (!this.process || this._exited)
|
|
@@ -1707,8 +1744,30 @@ var init_pty_manager = __esm({
|
|
|
1707
1744
|
logger.debug({ cols, rows }, "PTY resize \u8DF3\u8FC7\uFF08\u540C\u5C3A\u5BF8\uFF09");
|
|
1708
1745
|
return;
|
|
1709
1746
|
}
|
|
1710
|
-
logger.info({ cols, rows, prevCols: this._cols, prevRows: this._rows }, "PTY resize \u6267\u884C");
|
|
1747
|
+
logger.info({ cols, rows, prevCols: this._cols, prevRows: this._rows, alt: this._inAltScreen }, "PTY resize \u6267\u884C");
|
|
1711
1748
|
try {
|
|
1749
|
+
if (this._doublePulseTimer) {
|
|
1750
|
+
clearTimeout(this._doublePulseTimer);
|
|
1751
|
+
this._doublePulseTimer = null;
|
|
1752
|
+
}
|
|
1753
|
+
const shouldDoublePulse = !this._inAltScreen && cols > this._cols && cols > 2;
|
|
1754
|
+
if (shouldDoublePulse) {
|
|
1755
|
+
this.process.resize(cols - 1, rows);
|
|
1756
|
+
this._doublePulseTimer = setTimeout(() => {
|
|
1757
|
+
this._doublePulseTimer = null;
|
|
1758
|
+
if (!this.process || this._exited)
|
|
1759
|
+
return;
|
|
1760
|
+
try {
|
|
1761
|
+
this.process.resize(cols, rows);
|
|
1762
|
+
this._cols = cols;
|
|
1763
|
+
this._rows = rows;
|
|
1764
|
+
this.emit("resize", cols, rows);
|
|
1765
|
+
} catch (err) {
|
|
1766
|
+
logger.error({ err, cols, rows }, "PTY resize \u7B2C\u4E8C\u8109\u51B2\u5931\u8D25");
|
|
1767
|
+
}
|
|
1768
|
+
}, DOUBLE_PULSE_DELAY_MS);
|
|
1769
|
+
return;
|
|
1770
|
+
}
|
|
1712
1771
|
this.process.resize(cols, rows);
|
|
1713
1772
|
this._cols = cols;
|
|
1714
1773
|
this._rows = rows;
|
|
@@ -1723,6 +1782,10 @@ var init_pty_manager = __esm({
|
|
|
1723
1782
|
* 幂等:多次调用安全
|
|
1724
1783
|
*/
|
|
1725
1784
|
destroy() {
|
|
1785
|
+
if (this._doublePulseTimer) {
|
|
1786
|
+
clearTimeout(this._doublePulseTimer);
|
|
1787
|
+
this._doublePulseTimer = null;
|
|
1788
|
+
}
|
|
1726
1789
|
if (!this.process)
|
|
1727
1790
|
return;
|
|
1728
1791
|
try {
|
|
@@ -1733,6 +1796,32 @@ var init_pty_manager = __esm({
|
|
|
1733
1796
|
}
|
|
1734
1797
|
this.process = null;
|
|
1735
1798
|
}
|
|
1799
|
+
/**
|
|
1800
|
+
* 当前是否处于 alt-screen(仅供 resize 决策用,不暴露给上层)
|
|
1801
|
+
*/
|
|
1802
|
+
get inAltScreen() {
|
|
1803
|
+
return this._inAltScreen;
|
|
1804
|
+
}
|
|
1805
|
+
/**
|
|
1806
|
+
* 扫描 PTY 输出,识别 alt-screen 切换序列:
|
|
1807
|
+
* - DECSET 1049(最常用,xterm 标准 + 保存光标位置):进入 / 退出
|
|
1808
|
+
* - DECSET 1047(仅 alt buffer 切换):进入 / 退出
|
|
1809
|
+
* - DECSET 47(最老的 alt-buffer,无光标保存):进入 / 退出
|
|
1810
|
+
*
|
|
1811
|
+
* 不需要完整 ANSI 解析器——这三个序列形态固定,正则简单匹配即可。
|
|
1812
|
+
* 同一 chunk 内可能既有 enter 又有 exit(罕见,但可能),按顺序处理。
|
|
1813
|
+
*/
|
|
1814
|
+
scanAltScreenToggle(data) {
|
|
1815
|
+
const re = /\x1b\[\?(1049|1047|47)([hl])/g;
|
|
1816
|
+
let m;
|
|
1817
|
+
while ((m = re.exec(data)) !== null) {
|
|
1818
|
+
const isEnter = m[2] === "h";
|
|
1819
|
+
if (this._inAltScreen !== isEnter) {
|
|
1820
|
+
this._inAltScreen = isEnter;
|
|
1821
|
+
logger.debug({ inAltScreen: isEnter }, "PTY alt-screen \u72B6\u6001\u5207\u6362");
|
|
1822
|
+
}
|
|
1823
|
+
}
|
|
1824
|
+
}
|
|
1736
1825
|
};
|
|
1737
1826
|
}
|
|
1738
1827
|
});
|
|
@@ -2008,7 +2097,7 @@ function handleWsMessage(ws, raw, cb) {
|
|
|
2008
2097
|
break;
|
|
2009
2098
|
case "resize":
|
|
2010
2099
|
if (typeof msg.cols === "number" && typeof msg.rows === "number") {
|
|
2011
|
-
cb.onResize(msg.cols, msg.rows);
|
|
2100
|
+
cb.onResize(msg.cols, msg.rows, ws, msg.master === true);
|
|
2012
2101
|
} else {
|
|
2013
2102
|
logger.warn({ msg }, "resize \u7F3A cols/rows \u5B57\u6BB5\u6216\u7C7B\u578B\u9519\u8BEF");
|
|
2014
2103
|
}
|
|
@@ -2018,6 +2107,13 @@ function handleWsMessage(ws, raw, cb) {
|
|
|
2018
2107
|
ws.send(JSON.stringify({ type: "heartbeat", timestamp: Date.now() }));
|
|
2019
2108
|
}
|
|
2020
2109
|
break;
|
|
2110
|
+
case "client_log":
|
|
2111
|
+
if (typeof msg.message === "string" && typeof msg.level === "string") {
|
|
2112
|
+
const ts = typeof msg.ts === "number" ? new Date(msg.ts).toISOString().slice(11, 23) : "?";
|
|
2113
|
+
process.stderr.write(`[client ${ts} ${msg.level}] ${msg.message}
|
|
2114
|
+
`);
|
|
2115
|
+
}
|
|
2116
|
+
break;
|
|
2021
2117
|
default: {
|
|
2022
2118
|
const t = msg.type;
|
|
2023
2119
|
logger.warn({ type: t }, "\u672A\u77E5 WS \u6D88\u606F\u7C7B\u578B\uFF0C\u5DF2\u5FFD\u7565");
|
|
@@ -2124,6 +2220,7 @@ var init_ansi_filter = __esm({
|
|
|
2124
2220
|
});
|
|
2125
2221
|
|
|
2126
2222
|
// backend/dist/session/session-controller.js
|
|
2223
|
+
import { WebSocket as WebSocket3 } from "ws";
|
|
2127
2224
|
var SessionController;
|
|
2128
2225
|
var init_session_controller = __esm({
|
|
2129
2226
|
"backend/dist/session/session-controller.js"() {
|
|
@@ -2157,6 +2254,12 @@ var init_session_controller = __esm({
|
|
|
2157
2254
|
wsBackpressureEvents = 0;
|
|
2158
2255
|
/** 可选的 hook 接收器(阶段 3 启用) */
|
|
2159
2256
|
hookReceiver = null;
|
|
2257
|
+
/**
|
|
2258
|
+
* PTY 尺寸主控连接:声明 master=true 的客户端 WebSocket 引用。
|
|
2259
|
+
* 仅它能改 PTY cols/rows;其他客户端的 resize 被忽略。断开时自动释放
|
|
2260
|
+
* (onDisconnect 处理)。null = 无主控,先到先得
|
|
2261
|
+
*/
|
|
2262
|
+
masterClient = null;
|
|
2160
2263
|
/** ANSI 过滤器(阶段 8 启用;null = 关闭过滤直接透传) */
|
|
2161
2264
|
ansiFilter;
|
|
2162
2265
|
/** 可选的 PushService(阶段 9 启用;用于 hook 触发时推送通知) */
|
|
@@ -2340,8 +2443,18 @@ var init_session_controller = __esm({
|
|
|
2340
2443
|
onUserInput: (data) => {
|
|
2341
2444
|
this.pty.write(data);
|
|
2342
2445
|
},
|
|
2343
|
-
onResize: (cols, rows) => {
|
|
2446
|
+
onResize: (cols, rows, source, master) => {
|
|
2344
2447
|
const counts = this.ws.getClientCounts();
|
|
2448
|
+
if (master) {
|
|
2449
|
+
this.masterClient = source;
|
|
2450
|
+
logger.info({ cols, rows, type }, "PTY \u4E3B\u63A7\u5207\u6362\uFF1A\u5BA2\u6237\u7AEF\u58F0\u660E master");
|
|
2451
|
+
this.pty.resize(cols, rows);
|
|
2452
|
+
return;
|
|
2453
|
+
}
|
|
2454
|
+
if (this.masterClient && this.masterClient !== source) {
|
|
2455
|
+
logger.debug({ cols, rows }, "\u4E3B\u63A7\u88AB\u5176\u4ED6\u5BA2\u6237\u7AEF\u6301\u6709\uFF0C\u5FFD\u7565\u6B64 resize");
|
|
2456
|
+
return;
|
|
2457
|
+
}
|
|
2345
2458
|
if (counts.webapp > 0 && type === "attach") {
|
|
2346
2459
|
logger.debug({ type, cols, rows, counts }, "webapp \u5728\u7EBF\uFF0Cattach \u7684 resize \u88AB\u5FFD\u7565");
|
|
2347
2460
|
return;
|
|
@@ -2363,6 +2476,10 @@ var init_session_controller = __esm({
|
|
|
2363
2476
|
});
|
|
2364
2477
|
this.ws.onDisconnect((counts) => {
|
|
2365
2478
|
logger.debug(counts, "\u5BA2\u6237\u7AEF\u65AD\u5F00\u540E\u5269\u4F59\u7EDF\u8BA1");
|
|
2479
|
+
if (this.masterClient && this.masterClient.readyState !== WebSocket3.OPEN) {
|
|
2480
|
+
logger.info("PTY \u4E3B\u63A7\u8FDE\u63A5\u5DF2\u65AD\u5F00\uFF0C\u91CA\u653E\u4E3B\u63A7\u9501");
|
|
2481
|
+
this.masterClient = null;
|
|
2482
|
+
}
|
|
2366
2483
|
if (counts.webapp === 0 && counts.attach > 0) {
|
|
2367
2484
|
this.ws.broadcast({
|
|
2368
2485
|
type: "terminal_resize",
|
|
@@ -4490,7 +4607,7 @@ var init_cli_stop = __esm({
|
|
|
4490
4607
|
});
|
|
4491
4608
|
|
|
4492
4609
|
// backend/dist/attach/attach-client.js
|
|
4493
|
-
import
|
|
4610
|
+
import WebSocket4 from "ws";
|
|
4494
4611
|
import { EventEmitter as EventEmitter4 } from "node:events";
|
|
4495
4612
|
function normalizeAttachUrl(input) {
|
|
4496
4613
|
let url;
|
|
@@ -4545,7 +4662,7 @@ var init_attach_client = __esm({
|
|
|
4545
4662
|
if (this.destroyed)
|
|
4546
4663
|
return;
|
|
4547
4664
|
this.setStatus("connecting");
|
|
4548
|
-
const ws = new
|
|
4665
|
+
const ws = new WebSocket4(this.url);
|
|
4549
4666
|
this.ws = ws;
|
|
4550
4667
|
ws.on("open", () => {
|
|
4551
4668
|
this.reconnectAttempt = 0;
|
|
@@ -4610,7 +4727,7 @@ var init_attach_client = __esm({
|
|
|
4610
4727
|
}
|
|
4611
4728
|
// ────────────────── 内部 ──────────────────
|
|
4612
4729
|
send(msg) {
|
|
4613
|
-
if (!this.ws || this.ws.readyState !==
|
|
4730
|
+
if (!this.ws || this.ws.readyState !== WebSocket4.OPEN)
|
|
4614
4731
|
return;
|
|
4615
4732
|
try {
|
|
4616
4733
|
this.ws.send(JSON.stringify(msg));
|