tmex-cli 0.14.0 → 0.15.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/CHANGELOG.md +12 -24
- package/dist/runtime/server.js +158 -14
- package/package.json +1 -1
- package/resources/fe-dist/assets/{DevicePage-Ctq31eWb.js → DevicePage-kndrXVHE.js} +1 -1
- package/resources/fe-dist/assets/{DevicesPage-DGo44NW0.js → DevicesPage-CZLzOS04.js} +1 -1
- package/resources/fe-dist/assets/{FilePage-BpubOqjN.js → FilePage-CxVqdlbt.js} +1 -1
- package/resources/fe-dist/assets/{SettingsPage-QT6fy6XA.js → SettingsPage-C50JRe91.js} +1 -1
- package/resources/fe-dist/assets/{agent-tab-A2FeMPwL.js → agent-tab-BVWxou-d.js} +1 -1
- package/resources/fe-dist/assets/{api-CGfOwFKm.js → api-CrHrQflz.js} +1 -1
- package/resources/fe-dist/assets/{arc-B2dphCVi.js → arc-fsY_kDTa.js} +1 -1
- package/resources/fe-dist/assets/{architectureDiagram-3BPJPVTR-BpvEhd8M.js → architectureDiagram-3BPJPVTR-Vsg8Cd3e.js} +1 -1
- package/resources/fe-dist/assets/{blockDiagram-GPEHLZMM-CI_NnIJz.js → blockDiagram-GPEHLZMM-G7sC3DZO.js} +1 -1
- package/resources/fe-dist/assets/{c4Diagram-AAUBKEIU-Y6RrOc7Y.js → c4Diagram-AAUBKEIU-C1AfLOva.js} +1 -1
- package/resources/fe-dist/assets/{card-CydUB3N3.js → card-CXa-2u8i.js} +1 -1
- package/resources/fe-dist/assets/channel-DS4kWkSb.js +1 -0
- package/resources/fe-dist/assets/{chunk-2J33WTMH-BQN_fYaa.js → chunk-2J33WTMH-C1p02eGI.js} +1 -1
- package/resources/fe-dist/assets/{chunk-4BX2VUAB-BExarxjk.js → chunk-4BX2VUAB-C8n2FURp.js} +1 -1
- package/resources/fe-dist/assets/{chunk-55IACEB6-DeOEgcn_.js → chunk-55IACEB6-Da0nuNCD.js} +1 -1
- package/resources/fe-dist/assets/{chunk-727SXJPM-CwkxCtPA.js → chunk-727SXJPM-C1TXyDEt.js} +1 -1
- package/resources/fe-dist/assets/{chunk-AQP2D5EJ-_IoTtvOb.js → chunk-AQP2D5EJ-CnRKlbXo.js} +1 -1
- package/resources/fe-dist/assets/{chunk-FMBD7UC4-6pvmQ3Lj.js → chunk-FMBD7UC4-B8HGrJ3t.js} +1 -1
- package/resources/fe-dist/assets/{chunk-ND2GUHAM-CxpGf5zM.js → chunk-ND2GUHAM-CcN2Ou-o.js} +1 -1
- package/resources/fe-dist/assets/{chunk-QZHKN3VN-DOMH0v_e.js → chunk-QZHKN3VN-DfWJ_tl5.js} +1 -1
- package/resources/fe-dist/assets/classDiagram-4FO5ZUOK-BfsrnUi9.js +1 -0
- package/resources/fe-dist/assets/classDiagram-v2-Q7XG4LA2-BfsrnUi9.js +1 -0
- package/resources/fe-dist/assets/{copy-DnlW35bA.js → copy-BveLgJe5.js} +1 -1
- package/resources/fe-dist/assets/{cose-bilkent-S5V4N54A-D03NElzH.js → cose-bilkent-S5V4N54A-BkmsTzwU.js} +1 -1
- package/resources/fe-dist/assets/{dagre-BM42HDAG-CVOxLhKQ.js → dagre-BM42HDAG-pGgtc4EE.js} +1 -1
- package/resources/fe-dist/assets/{diagram-2AECGRRQ-p564GAo-.js → diagram-2AECGRRQ-DJxixZvm.js} +1 -1
- package/resources/fe-dist/assets/{diagram-5GNKFQAL-C_pgnQLt.js → diagram-5GNKFQAL-2ITViev7.js} +1 -1
- package/resources/fe-dist/assets/{diagram-KO2AKTUF-B36jioML.js → diagram-KO2AKTUF-EMcadaeo.js} +1 -1
- package/resources/fe-dist/assets/{diagram-LMA3HP47-B8P4WWeq.js → diagram-LMA3HP47-w3sLU7zY.js} +1 -1
- package/resources/fe-dist/assets/{diagram-OG6HWLK6-CTL4zJlz.js → diagram-OG6HWLK6-B4KGMQr7.js} +1 -1
- package/resources/fe-dist/assets/{en_US-DRPd4vPi.js → en_US-Chxeay8F.js} +1 -1
- package/resources/fe-dist/assets/{erDiagram-TEJ5UH35-DbKPL3Fa.js → erDiagram-TEJ5UH35-CB0P-Wz7.js} +1 -1
- package/resources/fe-dist/assets/{files-tab-CeUM00x8.js → files-tab-lNenDPFy.js} +1 -1
- package/resources/fe-dist/assets/{flowDiagram-I6XJVG4X-DvOU6wtN.js → flowDiagram-I6XJVG4X-CmBc7GAX.js} +1 -1
- package/resources/fe-dist/assets/{ganttDiagram-6RSMTGT7-CwgNky8L.js → ganttDiagram-6RSMTGT7-CHNf7S_K.js} +1 -1
- package/resources/fe-dist/assets/{gitGraphDiagram-PVQCEYII-B3U-hNGe.js → gitGraphDiagram-PVQCEYII-CabspIF1.js} +1 -1
- package/resources/fe-dist/assets/{index-DBHh6Xu1.js → index-BQbq6q_-.js} +1 -1
- package/resources/fe-dist/assets/{index-DAElYydw.js → index-DOaZjm8K.js} +79 -79
- package/resources/fe-dist/assets/index-DaOR3c0u.css +1 -0
- package/resources/fe-dist/assets/{infoDiagram-5YYISTIA-BmcWF7v1.js → infoDiagram-5YYISTIA-ny6wqvHH.js} +1 -1
- package/resources/fe-dist/assets/{ishikawaDiagram-YF4QCWOH-Dr8C0MtS.js → ishikawaDiagram-YF4QCWOH-EFCdGVsV.js} +1 -1
- package/resources/fe-dist/assets/{ja_JP-DHREHMZZ.js → ja_JP-BI-C8I9X.js} +1 -1
- package/resources/fe-dist/assets/{journeyDiagram-JHISSGLW-DzlvXb4K.js → journeyDiagram-JHISSGLW-Cvi8jO9a.js} +1 -1
- package/resources/fe-dist/assets/{kanban-definition-UN3LZRKU-4aBWZlcm.js → kanban-definition-UN3LZRKU-nKej8tSo.js} +1 -1
- package/resources/fe-dist/assets/{linear-_wXLepjd.js → linear-leOPyu29.js} +1 -1
- package/resources/fe-dist/assets/{markdown-preview-BX0o83rG.js → markdown-preview-rqUSs5w8.js} +3 -3
- package/resources/fe-dist/assets/{mermaid.core-DoE3dx_X.js → mermaid.core-QvhJBi8f.js} +5 -5
- package/resources/fe-dist/assets/{mindmap-definition-RKZ34NQL-CKpq2s43.js → mindmap-definition-RKZ34NQL-xWje0B_-.js} +1 -1
- package/resources/fe-dist/assets/{pieDiagram-4H26LBE5-DlW1TLza.js → pieDiagram-4H26LBE5-DT1i53ec.js} +1 -1
- package/resources/fe-dist/assets/{quadrantDiagram-W4KKPZXB-NEwxpMVe.js → quadrantDiagram-W4KKPZXB-C1zbS4Tx.js} +1 -1
- package/resources/fe-dist/assets/{requirementDiagram-4Y6WPE33-CGysr6uH.js → requirementDiagram-4Y6WPE33-By_eNRll.js} +1 -1
- package/resources/fe-dist/assets/{sankeyDiagram-5OEKKPKP-j8yXOgys.js → sankeyDiagram-5OEKKPKP-CG7jC05N.js} +1 -1
- package/resources/fe-dist/assets/{send-BtOPYsPT.js → send-CcFyyX2G.js} +1 -1
- package/resources/fe-dist/assets/{sequenceDiagram-3UESZ5HK-BRPWK9ZU.js → sequenceDiagram-3UESZ5HK-C9yK_EKN.js} +1 -1
- package/resources/fe-dist/assets/{stateDiagram-AJRCARHV-C8ckQlA7.js → stateDiagram-AJRCARHV-B-ROUwGP.js} +1 -1
- package/resources/fe-dist/assets/stateDiagram-v2-BHNVJYJU-iopOcale.js +1 -0
- package/resources/fe-dist/assets/{terminal-settings-panel-ZnL_cBER.js → terminal-settings-panel-CNwWqOMV.js} +1 -1
- package/resources/fe-dist/assets/{timeline-definition-PNZ67QCA-BXMFPa99.js → timeline-definition-PNZ67QCA-yMM8VB4U.js} +1 -1
- package/resources/fe-dist/assets/{transfer-toast-CUkPc73G.js → transfer-toast-mQs0hgyg.js} +1 -1
- package/resources/fe-dist/assets/{triangle-alert-DyM9tU7d.js → triangle-alert-DMp7kMmJ.js} +1 -1
- package/resources/fe-dist/assets/{vennDiagram-CIIHVFJN-Bk8oPY9E.js → vennDiagram-CIIHVFJN-CE08llZ3.js} +1 -1
- package/resources/fe-dist/assets/{wardley-L42UT6IY-Bi_GTP2_.js → wardley-L42UT6IY-BKLGCmLJ.js} +1 -1
- package/resources/fe-dist/assets/{wardleyDiagram-YWT4CUSO-CaPDmxFM.js → wardleyDiagram-YWT4CUSO-BsjwfO3P.js} +1 -1
- package/resources/fe-dist/assets/{xychartDiagram-2RQKCTM6-EHogasXy.js → xychartDiagram-2RQKCTM6-Bhz6AI7w.js} +1 -1
- package/resources/fe-dist/assets/{zap-CBgYyGBV.js → zap-DPwn8lbv.js} +1 -1
- package/resources/fe-dist/assets/{zh_CN-DMEKXuFd.js → zh_CN-DE-BaQ3P.js} +1 -1
- package/resources/fe-dist/index.html +2 -2
- package/resources/fe-dist/assets/channel-Ce3VMo9B.js +0 -1
- package/resources/fe-dist/assets/classDiagram-4FO5ZUOK-BjK-bnh-.js +0 -1
- package/resources/fe-dist/assets/classDiagram-v2-Q7XG4LA2-BjK-bnh-.js +0 -1
- package/resources/fe-dist/assets/index-D-q7dOhH.css +0 -1
- package/resources/fe-dist/assets/stateDiagram-v2-BHNVJYJU-Cm0vyyJy.js +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,41 +1,29 @@
|
|
|
1
|
-
# 0.
|
|
1
|
+
# 0.15.0
|
|
2
2
|
|
|
3
3
|
_2026-06-29_
|
|
4
4
|
|
|
5
5
|
## English
|
|
6
6
|
|
|
7
|
-
###
|
|
7
|
+
### Bug Fixes
|
|
8
8
|
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
- Installer now guides you through installing missing dependencies (tmux, bun) during setup, with distro-specific commands for common Linux distributions.
|
|
9
|
+
- Fix iOS PWA losing WebSocket connection permanently after backgrounding. The client now detects zombie connections via pong timeout and automatically reconnects when the page returns to foreground. (`42c8249`)
|
|
10
|
+
- Fix stdin heartbeat and `%pause` handling in control mode; improve pump recovery on unexpected disconnects. (`a208371`)
|
|
12
11
|
|
|
13
12
|
### Improvements
|
|
14
13
|
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
- The installer detects whether systemd is available on Linux before proceeding, instead of failing midway on container or WSL environments without it.
|
|
18
|
-
|
|
19
|
-
### Fixes
|
|
20
|
-
|
|
21
|
-
- Fixed non-ASCII filenames (Chinese, Japanese, Korean, etc.) showing as garbled escape sequences in the file browser on Linux servers.
|
|
14
|
+
- Add a floating connection status indicator (bottom-right corner) that shows reconnecting state and provides a manual reconnect button when all retries are exhausted. Replaces the previous unhelpful error toast.
|
|
15
|
+
- Display WebSocket latency next to the dark mode toggle in the sidebar. Values >= 150 ms are highlighted in red.
|
|
22
16
|
|
|
23
17
|
---
|
|
24
18
|
|
|
25
19
|
## 中文
|
|
26
20
|
|
|
27
|
-
###
|
|
28
|
-
|
|
29
|
-
- 终端程序剪贴板支持:vim、Claude Code 等 TUI 程序现在可以通过终端直接复制文本到系统剪贴板(OSC 52),复制成功时会显示提示。
|
|
30
|
-
- 可配置默认工作目录:可为每台设备设置新终端窗口的默认路径,新窗口将在该目录下打开而非主目录。修改后立即生效,无需断开连接。
|
|
31
|
-
- 安装器现在会在安装过程中引导用户安装缺失的依赖(tmux、bun),并为常见 Linux 发行版提供专属的安装命令。
|
|
32
|
-
|
|
33
|
-
### 改进
|
|
21
|
+
### 问题修复
|
|
34
22
|
|
|
35
|
-
-
|
|
36
|
-
-
|
|
37
|
-
- 安装器在 Linux 上会先检测 systemd 是否可用,在容器或无 systemd 的 WSL 环境中提前给出明确错误,而非中途失败。
|
|
23
|
+
- 修复 iOS PWA 后台化后 WebSocket 永久断连的问题。客户端现在通过 pong 超时检测僵尸连接,页面回到前台时自动恢复连接。(`42c8249`)
|
|
24
|
+
- 修复控制模式下 stdin 心跳和 `%pause` 处理问题,改善意外断连时的 pump 恢复。(`a208371`)
|
|
38
25
|
|
|
39
|
-
###
|
|
26
|
+
### 体验优化
|
|
40
27
|
|
|
41
|
-
-
|
|
28
|
+
- 新增浮动连接状态指示器(右下角),显示重连状态,重试耗尽后提供手动重连按钮。替换了之前不友好的错误 toast。
|
|
29
|
+
- 在侧边栏深色模式开关旁显示 WebSocket 延迟,延迟 >= 150 ms 时标红。
|
package/dist/runtime/server.js
CHANGED
|
@@ -23122,7 +23122,9 @@ Time: {{time}}`,
|
|
|
23122
23122
|
error: "WebSocket connection error",
|
|
23123
23123
|
checkGateway: "Please check Gateway status",
|
|
23124
23124
|
upgradeFailed: "Upgrade failed",
|
|
23125
|
-
invalidMessage: "Invalid message format"
|
|
23125
|
+
invalidMessage: "Invalid message format",
|
|
23126
|
+
reconnecting: "Reconnecting",
|
|
23127
|
+
reconnect: "Reconnect"
|
|
23126
23128
|
},
|
|
23127
23129
|
wsError: {
|
|
23128
23130
|
checkGateway: "Please check Gateway status"
|
|
@@ -24118,7 +24120,9 @@ Bot\uFF1A{{botName}}
|
|
|
24118
24120
|
error: "WebSocket \u8FDE\u63A5\u9519\u8BEF",
|
|
24119
24121
|
checkGateway: "\u8BF7\u68C0\u67E5 Gateway \u72B6\u6001",
|
|
24120
24122
|
upgradeFailed: "Upgrade failed",
|
|
24121
|
-
invalidMessage: "Invalid message format"
|
|
24123
|
+
invalidMessage: "Invalid message format",
|
|
24124
|
+
reconnecting: "\u91CD\u8FDE\u4E2D",
|
|
24125
|
+
reconnect: "\u91CD\u65B0\u8FDE\u63A5"
|
|
24122
24126
|
},
|
|
24123
24127
|
wsError: {
|
|
24124
24128
|
checkGateway: "\u8BF7\u68C0\u67E5 Gateway \u72B6\u6001"
|
|
@@ -25114,7 +25118,9 @@ Bot\uFF1A{{botName}}
|
|
|
25114
25118
|
error: "WebSocket \u63A5\u7D9A\u30A8\u30E9\u30FC",
|
|
25115
25119
|
checkGateway: "Gateway \u72B6\u614B\u3092\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044",
|
|
25116
25120
|
upgradeFailed: "\u30A2\u30C3\u30D7\u30B0\u30EC\u30FC\u30C9\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
|
|
25117
|
-
invalidMessage: "\u7121\u52B9\u306A\u30E1\u30C3\u30BB\u30FC\u30B8\u5F62\u5F0F"
|
|
25121
|
+
invalidMessage: "\u7121\u52B9\u306A\u30E1\u30C3\u30BB\u30FC\u30B8\u5F62\u5F0F",
|
|
25122
|
+
reconnecting: "\u518D\u63A5\u7D9A\u4E2D",
|
|
25123
|
+
reconnect: "\u518D\u63A5\u7D9A"
|
|
25118
25124
|
},
|
|
25119
25125
|
wsError: {
|
|
25120
25126
|
checkGateway: "Gateway \u72B6\u614B\u3092\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044"
|
|
@@ -98613,6 +98619,11 @@ function createControlModeSubscription(callbacks) {
|
|
|
98613
98619
|
if (STRUCTURE_NOTIFICATION_TYPES.has(notification.type)) {
|
|
98614
98620
|
scheduleStructureChanged();
|
|
98615
98621
|
}
|
|
98622
|
+
if (notification.type === "pause") {
|
|
98623
|
+
callbacks.onPause?.(notification.args.trim());
|
|
98624
|
+
} else if (notification.type === "continue") {
|
|
98625
|
+
callbacks.onContinue?.(notification.args.trim());
|
|
98626
|
+
}
|
|
98616
98627
|
}
|
|
98617
98628
|
const parser = createControlModeParser({
|
|
98618
98629
|
onOutput: (paneId, data) => {
|
|
@@ -98888,6 +98899,8 @@ var CONTROL_RESTART_DELAY_MS = 500;
|
|
|
98888
98899
|
var CONTROL_STABLE_RESET_MS = 1e4;
|
|
98889
98900
|
var CONTROL_STDERR_TAIL_LIMIT = 2048;
|
|
98890
98901
|
var CONTROL_ATTACH_READY_TIMEOUT_MS = 3000;
|
|
98902
|
+
var HEARTBEAT_INTERVAL_MS = 30000;
|
|
98903
|
+
var HEARTBEAT_TIMEOUT_MS = 1e4;
|
|
98891
98904
|
var PARKING_WINDOW_NAME = "tmex-park";
|
|
98892
98905
|
function hasRenderableTerminalContent(value) {
|
|
98893
98906
|
return value.trim().length > 0;
|
|
@@ -98946,6 +98959,11 @@ function defaultSpawnControlClient(argv) {
|
|
|
98946
98959
|
stdin?.end();
|
|
98947
98960
|
} catch {}
|
|
98948
98961
|
subprocess.kill();
|
|
98962
|
+
},
|
|
98963
|
+
write: (data) => {
|
|
98964
|
+
try {
|
|
98965
|
+
stdin?.write(data);
|
|
98966
|
+
} catch {}
|
|
98949
98967
|
}
|
|
98950
98968
|
};
|
|
98951
98969
|
}
|
|
@@ -98973,6 +98991,9 @@ class LocalExternalTmuxConnection {
|
|
|
98973
98991
|
controlRestartCount = 0;
|
|
98974
98992
|
controlStderrTail = "";
|
|
98975
98993
|
spawnUnavailableNotified = false;
|
|
98994
|
+
heartbeatTimer = null;
|
|
98995
|
+
heartbeatPending = false;
|
|
98996
|
+
heartbeatTimeoutTimer = null;
|
|
98976
98997
|
constructor(options, inputDeps = {}) {
|
|
98977
98998
|
this.deviceId = options.deviceId;
|
|
98978
98999
|
this.callbacks = options;
|
|
@@ -99305,6 +99326,7 @@ class LocalExternalTmuxConnection {
|
|
|
99305
99326
|
await this.runTmuxAllowFailure(["kill-window", "-t", windowId]);
|
|
99306
99327
|
}
|
|
99307
99328
|
async startControlClient() {
|
|
99329
|
+
this.stopHeartbeat();
|
|
99308
99330
|
let attachReadyResolve = null;
|
|
99309
99331
|
const attachReady = new Promise((resolve3) => {
|
|
99310
99332
|
attachReadyResolve = resolve3;
|
|
@@ -99328,6 +99350,7 @@ class LocalExternalTmuxConnection {
|
|
|
99328
99350
|
console.warn(`[local] tmux control client died during attach on ${this.deviceId}: ${message}`);
|
|
99329
99351
|
throw new Error(message);
|
|
99330
99352
|
}
|
|
99353
|
+
this.startHeartbeat();
|
|
99331
99354
|
}
|
|
99332
99355
|
spawnControlClientProcess(onAttachReady) {
|
|
99333
99356
|
const subscription = createControlModeSubscription({
|
|
@@ -99353,9 +99376,16 @@ class LocalExternalTmuxConnection {
|
|
|
99353
99376
|
onStructureChanged: () => {
|
|
99354
99377
|
this.requestSnapshot();
|
|
99355
99378
|
},
|
|
99379
|
+
onPause: (paneId) => {
|
|
99380
|
+
this.controlProcess?.write("refresh-client -A " + paneId + `:continue
|
|
99381
|
+
`);
|
|
99382
|
+
},
|
|
99356
99383
|
onExit: () => {},
|
|
99357
|
-
onBlockEnd: () => {
|
|
99384
|
+
onBlockEnd: (block) => {
|
|
99358
99385
|
onAttachReady();
|
|
99386
|
+
if (!block.isError && block.lines.length === 1 && block.lines[0] === "tmex-hb") {
|
|
99387
|
+
this.onHeartbeatResponse();
|
|
99388
|
+
}
|
|
99359
99389
|
}
|
|
99360
99390
|
});
|
|
99361
99391
|
const proc = this.deps.spawnControlClient([
|
|
@@ -99395,6 +99425,10 @@ class LocalExternalTmuxConnection {
|
|
|
99395
99425
|
}
|
|
99396
99426
|
}
|
|
99397
99427
|
subscription.end();
|
|
99428
|
+
if (this.controlProcess === proc) {
|
|
99429
|
+
console.warn("[local] control client stdout ended unexpectedly on " + this.deviceId + ", killing process");
|
|
99430
|
+
proc.kill();
|
|
99431
|
+
}
|
|
99398
99432
|
}
|
|
99399
99433
|
async pumpControlStderr(proc) {
|
|
99400
99434
|
const reader = proc.stderr.getReader();
|
|
@@ -99412,12 +99446,55 @@ class LocalExternalTmuxConnection {
|
|
|
99412
99446
|
} catch {}
|
|
99413
99447
|
}
|
|
99414
99448
|
stopControlClient() {
|
|
99449
|
+
this.stopHeartbeat();
|
|
99415
99450
|
const proc = this.controlProcess;
|
|
99416
99451
|
this.controlProcess = null;
|
|
99417
99452
|
this.controlSubscription?.dispose();
|
|
99418
99453
|
this.controlSubscription = null;
|
|
99419
99454
|
proc?.kill();
|
|
99420
99455
|
}
|
|
99456
|
+
startHeartbeat() {
|
|
99457
|
+
this.stopHeartbeat();
|
|
99458
|
+
this.heartbeatTimer = setInterval(() => {
|
|
99459
|
+
this.sendHeartbeat();
|
|
99460
|
+
}, HEARTBEAT_INTERVAL_MS);
|
|
99461
|
+
}
|
|
99462
|
+
stopHeartbeat() {
|
|
99463
|
+
if (this.heartbeatTimer) {
|
|
99464
|
+
clearInterval(this.heartbeatTimer);
|
|
99465
|
+
this.heartbeatTimer = null;
|
|
99466
|
+
}
|
|
99467
|
+
if (this.heartbeatTimeoutTimer) {
|
|
99468
|
+
clearTimeout(this.heartbeatTimeoutTimer);
|
|
99469
|
+
this.heartbeatTimeoutTimer = null;
|
|
99470
|
+
}
|
|
99471
|
+
this.heartbeatPending = false;
|
|
99472
|
+
}
|
|
99473
|
+
sendHeartbeat() {
|
|
99474
|
+
if (!this.controlProcess || this.heartbeatPending || !this.connected || this.manualDisconnect) {
|
|
99475
|
+
return;
|
|
99476
|
+
}
|
|
99477
|
+
this.heartbeatPending = true;
|
|
99478
|
+
this.controlProcess.write(`display-message -p "tmex-hb"
|
|
99479
|
+
`);
|
|
99480
|
+
this.heartbeatTimeoutTimer = setTimeout(() => {
|
|
99481
|
+
if (!this.heartbeatPending || !this.connected || this.manualDisconnect) {
|
|
99482
|
+
return;
|
|
99483
|
+
}
|
|
99484
|
+
console.warn(`[local] tmux control client heartbeat timeout on ${this.deviceId}, killing stalled process`);
|
|
99485
|
+
this.controlProcess?.kill();
|
|
99486
|
+
}, HEARTBEAT_TIMEOUT_MS);
|
|
99487
|
+
}
|
|
99488
|
+
onHeartbeatResponse() {
|
|
99489
|
+
if (!this.heartbeatPending) {
|
|
99490
|
+
return;
|
|
99491
|
+
}
|
|
99492
|
+
this.heartbeatPending = false;
|
|
99493
|
+
if (this.heartbeatTimeoutTimer) {
|
|
99494
|
+
clearTimeout(this.heartbeatTimeoutTimer);
|
|
99495
|
+
this.heartbeatTimeoutTimer = null;
|
|
99496
|
+
}
|
|
99497
|
+
}
|
|
99421
99498
|
handleControlClientExit(proc, exitCode) {
|
|
99422
99499
|
if (this.controlProcess !== proc) {
|
|
99423
99500
|
return;
|
|
@@ -100197,6 +100274,8 @@ var CONTROL_RESTART_DELAY_MS2 = 500;
|
|
|
100197
100274
|
var CONTROL_STABLE_RESET_MS2 = 1e4;
|
|
100198
100275
|
var CONTROL_STDERR_TAIL_LIMIT2 = 2048;
|
|
100199
100276
|
var CONTROL_ATTACH_READY_TIMEOUT_MS2 = 3000;
|
|
100277
|
+
var HEARTBEAT_INTERVAL_MS2 = 30000;
|
|
100278
|
+
var HEARTBEAT_TIMEOUT_MS2 = 1e4;
|
|
100200
100279
|
var PARKING_WINDOW_NAME2 = "tmex-park";
|
|
100201
100280
|
|
|
100202
100281
|
class SshExternalTmuxConnection {
|
|
@@ -100220,6 +100299,9 @@ class SshExternalTmuxConnection {
|
|
|
100220
100299
|
controlStartedAt = 0;
|
|
100221
100300
|
controlRestartCount = 0;
|
|
100222
100301
|
controlStderrTail = "";
|
|
100302
|
+
heartbeatTimer = null;
|
|
100303
|
+
heartbeatPending = false;
|
|
100304
|
+
heartbeatTimeoutTimer = null;
|
|
100223
100305
|
sshClient = null;
|
|
100224
100306
|
commandStream = null;
|
|
100225
100307
|
commandStdoutBuffer = "";
|
|
@@ -100641,6 +100723,7 @@ class SshExternalTmuxConnection {
|
|
|
100641
100723
|
await this.runTmuxAllowFailure(["kill-window", "-t", windowId]);
|
|
100642
100724
|
}
|
|
100643
100725
|
async startControlClient() {
|
|
100726
|
+
this.stopHeartbeat();
|
|
100644
100727
|
let attachReadyResolve = null;
|
|
100645
100728
|
const attachReady = new Promise((resolve3) => {
|
|
100646
100729
|
attachReadyResolve = resolve3;
|
|
@@ -100662,6 +100745,7 @@ class SshExternalTmuxConnection {
|
|
|
100662
100745
|
if (this.controlChannel !== handle) {
|
|
100663
100746
|
throw new Error(this.controlStderrTail.trim() || "tmux control client channel closed during attach");
|
|
100664
100747
|
}
|
|
100748
|
+
this.startHeartbeat();
|
|
100665
100749
|
}
|
|
100666
100750
|
async openControlChannel(onAttachReady) {
|
|
100667
100751
|
const subscription = createControlModeSubscription({
|
|
@@ -100688,16 +100772,25 @@ class SshExternalTmuxConnection {
|
|
|
100688
100772
|
this.requestSnapshot();
|
|
100689
100773
|
},
|
|
100690
100774
|
onExit: () => {},
|
|
100691
|
-
|
|
100775
|
+
onPause: (paneId) => {
|
|
100776
|
+
if (this.controlChannel === handle) {
|
|
100777
|
+
handle.write("refresh-client -A " + paneId + ":continue" + `
|
|
100778
|
+
`);
|
|
100779
|
+
}
|
|
100780
|
+
},
|
|
100781
|
+
onBlockEnd: (block) => {
|
|
100692
100782
|
onAttachReady();
|
|
100783
|
+
if (!block.isError && block.lines.length === 1 && block.lines[0] === "tmex-hb") {
|
|
100784
|
+
this.onHeartbeatResponse();
|
|
100785
|
+
}
|
|
100693
100786
|
}
|
|
100694
100787
|
});
|
|
100695
|
-
const handle = { stop: () => {} };
|
|
100788
|
+
const handle = { stop: () => {}, write: () => {} };
|
|
100696
100789
|
this.controlChannel = handle;
|
|
100697
100790
|
this.controlSubscription = subscription;
|
|
100698
100791
|
this.controlStartedAt = Date.now();
|
|
100699
100792
|
this.controlStderrTail = "";
|
|
100700
|
-
const
|
|
100793
|
+
const reader = await this.openReaderChannel(`exec ${quoteShellArg(this.tmuxBin)} -C attach-session -t ${quoteShellArg(this.sessionName)}`, {
|
|
100701
100794
|
onData: (data) => {
|
|
100702
100795
|
if (this.controlChannel === handle) {
|
|
100703
100796
|
subscription.push(new Uint8Array(data.buffer, data.byteOffset, data.byteLength));
|
|
@@ -100712,16 +100805,60 @@ class SshExternalTmuxConnection {
|
|
|
100712
100805
|
this.handleControlChannelClose(handle);
|
|
100713
100806
|
}
|
|
100714
100807
|
});
|
|
100715
|
-
handle.stop =
|
|
100808
|
+
handle.stop = reader.stop;
|
|
100809
|
+
handle.write = reader.write;
|
|
100716
100810
|
return handle;
|
|
100717
100811
|
}
|
|
100718
100812
|
stopControlClient() {
|
|
100813
|
+
this.stopHeartbeat();
|
|
100719
100814
|
const handle = this.controlChannel;
|
|
100720
100815
|
this.controlChannel = null;
|
|
100721
100816
|
this.controlSubscription?.dispose();
|
|
100722
100817
|
this.controlSubscription = null;
|
|
100723
100818
|
handle?.stop();
|
|
100724
100819
|
}
|
|
100820
|
+
startHeartbeat() {
|
|
100821
|
+
if (this.heartbeatTimer) {
|
|
100822
|
+
clearInterval(this.heartbeatTimer);
|
|
100823
|
+
}
|
|
100824
|
+
if (this.heartbeatTimeoutTimer) {
|
|
100825
|
+
clearTimeout(this.heartbeatTimeoutTimer);
|
|
100826
|
+
}
|
|
100827
|
+
this.heartbeatPending = false;
|
|
100828
|
+
this.heartbeatTimer = setInterval(() => this.sendHeartbeat(), HEARTBEAT_INTERVAL_MS2);
|
|
100829
|
+
}
|
|
100830
|
+
stopHeartbeat() {
|
|
100831
|
+
if (this.heartbeatTimer) {
|
|
100832
|
+
clearInterval(this.heartbeatTimer);
|
|
100833
|
+
this.heartbeatTimer = null;
|
|
100834
|
+
}
|
|
100835
|
+
if (this.heartbeatTimeoutTimer) {
|
|
100836
|
+
clearTimeout(this.heartbeatTimeoutTimer);
|
|
100837
|
+
this.heartbeatTimeoutTimer = null;
|
|
100838
|
+
}
|
|
100839
|
+
this.heartbeatPending = false;
|
|
100840
|
+
}
|
|
100841
|
+
sendHeartbeat() {
|
|
100842
|
+
if (!this.controlChannel || this.heartbeatPending || !this.connected || this.manualDisconnect) {
|
|
100843
|
+
return;
|
|
100844
|
+
}
|
|
100845
|
+
this.heartbeatPending = true;
|
|
100846
|
+
this.controlChannel.write('display-message -p "tmex-hb"' + `
|
|
100847
|
+
`);
|
|
100848
|
+
this.heartbeatTimeoutTimer = setTimeout(() => {
|
|
100849
|
+
if (this.heartbeatPending && this.connected && !this.manualDisconnect) {
|
|
100850
|
+
console.warn(`[ssh] tmux control client heartbeat timeout on ${this.deviceId}, killing stalled channel`);
|
|
100851
|
+
this.controlChannel?.stop();
|
|
100852
|
+
}
|
|
100853
|
+
}, HEARTBEAT_TIMEOUT_MS2);
|
|
100854
|
+
}
|
|
100855
|
+
onHeartbeatResponse() {
|
|
100856
|
+
this.heartbeatPending = false;
|
|
100857
|
+
if (this.heartbeatTimeoutTimer) {
|
|
100858
|
+
clearTimeout(this.heartbeatTimeoutTimer);
|
|
100859
|
+
this.heartbeatTimeoutTimer = null;
|
|
100860
|
+
}
|
|
100861
|
+
}
|
|
100725
100862
|
handleControlChannelClose(handle) {
|
|
100726
100863
|
if (this.controlChannel !== handle) {
|
|
100727
100864
|
return;
|
|
@@ -101172,10 +101309,17 @@ printf '\\036TMEX_END %s %d\\036\\n' ${quoteShellArg(commandId)} $?
|
|
|
101172
101309
|
});
|
|
101173
101310
|
stream.write(`${command}
|
|
101174
101311
|
`);
|
|
101175
|
-
return
|
|
101176
|
-
|
|
101177
|
-
|
|
101178
|
-
|
|
101312
|
+
return {
|
|
101313
|
+
stop: () => {
|
|
101314
|
+
stream.end();
|
|
101315
|
+
stream.close();
|
|
101316
|
+
stream.destroy();
|
|
101317
|
+
},
|
|
101318
|
+
write: (data) => {
|
|
101319
|
+
try {
|
|
101320
|
+
stream.write(data);
|
|
101321
|
+
} catch {}
|
|
101322
|
+
}
|
|
101179
101323
|
};
|
|
101180
101324
|
}
|
|
101181
101325
|
isTmuxServerGoneMessage(message) {
|
|
@@ -106601,8 +106745,8 @@ function getBaseVersion() {
|
|
|
106601
106745
|
if (cachedBase !== undefined)
|
|
106602
106746
|
return cachedBase;
|
|
106603
106747
|
let base = null;
|
|
106604
|
-
if ("0.
|
|
106605
|
-
base = "0.
|
|
106748
|
+
if ("0.15.0") {
|
|
106749
|
+
base = "0.15.0";
|
|
106606
106750
|
}
|
|
106607
106751
|
if (!base && config.isProd) {
|
|
106608
106752
|
base = readInstallMeta()?.cliVersion ?? null;
|
package/package.json
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{Y as Fe,u as je,j as o,$ as tt,a0 as nt,a1 as rt,a2 as st,a3 as ot,a4 as it,r,d as H,a5 as J,a6 as Xe,a7 as Ke,a8 as ct,a9 as at,aa as lt,ab as Oe,o as xe,ac as ut,ad as Be,ae as dt,b as Ge,af as We,ag as ft,c as $e,ah as Ve,ai as Ee,aj as mt,ak as ht,al as ze,y as wt,am as pt,B as me,T as xt,an as vt,ao as gt,ap as St,aq as bt,A as Et,f as Rt,g as Tt,i as yt,k as jt,l as It,m as Ct,n as Nt}from"./index-
|
|
1
|
+
import{Y as Fe,u as je,j as o,$ as tt,a0 as nt,a1 as rt,a2 as st,a3 as ot,a4 as it,r,d as H,a5 as J,a6 as Xe,a7 as Ke,a8 as ct,a9 as at,aa as lt,ab as Oe,o as xe,ac as ut,ad as Be,ae as dt,b as Ge,af as We,ag as ft,c as $e,ah as Ve,ai as Ee,aj as mt,ak as ht,al as ze,y as wt,am as pt,B as me,T as xt,an as vt,ao as gt,ap as St,aq as bt,A as Et,f as Rt,g as Tt,i as yt,k as jt,l as It,m as Ct,n as Nt}from"./index-DOaZjm8K.js";import{T as kt,C as Mt,c as Pt,F as At,a as Qe,R as Lt,A as _t,f as zt,t as Dt,S as Ft}from"./terminal-settings-panel-CNwWqOMV.js";import{w as Bt}from"./selection-clipboard-Dq6Zemfd.js";import{C as Wt}from"./copy-BveLgJe5.js";import{S as qe,K as $t}from"./send-CcFyyX2G.js";/**
|
|
2
2
|
* @license lucide-react v0.564.0 - ISC
|
|
3
3
|
*
|
|
4
4
|
* This source code is licensed under the ISC license.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{u as b,r as v,a as _,b as ie,c as re,d as ne,e as I,t as oe,j as s,M as X,B as C,P as Y,A as ce,f as de,g as le,h as ue,T as ee,i as me,k as he,l as pe,m as xe,n as ve,G as fe,o as f,D as ge,p as ye,E as je,q as we,s as $,v as Ne,w as De,x as Q,y as Ce,S as be,L as Se,z as ke,C as Ee,F as Me,H as Pe,I as Ie,J as Ke,K as x,N as z,O as V,Q as U,R as J,U as w,V as Re,W as $e}from"./index-DAElYydw.js";import{C as M,a as P,b as Ae,c as Te,d as Fe}from"./card-CydUB3N3.js";import{Z as We}from"./zap-CBgYyGBV.js";function g(e){const o=e.trim();return o||void 0}function qe(e){return e?{name:e.name,type:e.type,host:e.host??"",port:e.port??22,username:e.username??"",sshConfigRef:e.sshConfigRef??"",session:e.session??"tmex",defaultWorkingDir:e.defaultWorkingDir??"",authMode:e.type==="local"?"auto":e.authMode,password:"",privateKey:"",privateKeyPassphrase:""}:{name:"",type:"local",host:"",port:22,username:"root",sshConfigRef:"",session:"tmex",defaultWorkingDir:"",authMode:"auto",password:"",privateKey:"",privateKeyPassphrase:""}}function Le(e){if(e.type==="local")return{name:e.name.trim(),type:"local",session:g(e.session)??"tmex",defaultWorkingDir:g(e.defaultWorkingDir),authMode:"auto"};const o={name:e.name.trim(),type:"ssh",host:e.host.trim(),port:e.port,username:e.username.trim(),session:g(e.session)??"tmex",defaultWorkingDir:g(e.defaultWorkingDir),authMode:e.authMode};return e.authMode==="configRef"&&(o.sshConfigRef=e.sshConfigRef.trim()),e.authMode==="password"&&(o.password=e.password),e.authMode==="key"&&(o.privateKey=e.privateKey,o.privateKeyPassphrase=e.privateKeyPassphrase||void 0),o}function He(e){if(e.type==="local")return{name:e.name.trim(),session:g(e.session)??"tmex",defaultWorkingDir:g(e.defaultWorkingDir)??"",authMode:"auto"};const o={name:e.name.trim(),host:e.host.trim(),port:e.port,username:e.username.trim(),sshConfigRef:e.authMode==="configRef"?e.sshConfigRef.trim():"",session:g(e.session)??"tmex",defaultWorkingDir:g(e.defaultWorkingDir)??"",authMode:e.authMode};return e.authMode==="password"&&e.password&&(o.password=e.password),e.authMode==="key"&&e.privateKey&&(o.privateKey=e.privateKey,o.privateKeyPassphrase=e.privateKeyPassphrase||void 0),o}function se(e){return Number.isInteger(e)&&e>=1&&e<=65535}function Oe(e){return e.type!=="ssh"?null:e.host.trim()?se(e.port)?e.username.trim()?e.authMode==="configRef"&&!e.sshConfigRef.trim()?"validation.sshConfigRequired":null:"validation.usernameRequired":"validation.portRequired":"validation.hostRequired"}async function G(e,o){try{return(await e.json()).error??o}catch{return o}}function Ue(){const{t:e}=b(),[o,m]=v.useState(!1),[t,y]=v.useState(null),[r,d]=v.useState(null),l=_(),h=ie(n=>{var p;return((p=n.settings)==null?void 0:p.language)??"en_US"});v.useEffect(()=>{const n=()=>m(!0);return window.addEventListener("tmex:open-add-device",n),()=>window.removeEventListener("tmex:open-add-device",n)},[]);const{data:c,isLoading:K,isError:N}=re({queryKey:["devices"],queryFn:async()=>{const n=await fetch("/api/devices");if(!n.ok)throw new Error(e("device.loadFailed"));return n.json()},throwOnError:!1}),S=ne(n=>n.hydrateDeviceErrors);v.useEffect(()=>{c!=null&&c.devices&&S(c.devices.map(n=>({deviceId:n.id,lastError:n.lastError??null,lastErrorType:n.lastErrorType??null})))},[c,S]);const k=I({mutationFn:async n=>{if(!(await fetch(`/api/devices/${n}`,{method:"DELETE"})).ok)throw new Error(e("device.deleteFailed"))},onSuccess:()=>{l.invalidateQueries({queryKey:["devices"]}),f.success(e("common.success"))},onError:n=>{f.error(n instanceof Error?n.message:e("common.error"))}}),E=v.useMemo(()=>[...(c==null?void 0:c.devices)??[]].sort((p,D)=>p.sortOrder-D.sortOrder||p.name.localeCompare(D.name,oe(h),{numeric:!0,sensitivity:"base"})),[c==null?void 0:c.devices,h]);return s.jsxs("div",{className:"mx-auto flex w-full max-w-6xl flex-col gap-3 p-3 pb-[calc(1rem+env(safe-area-inset-bottom))] sm:gap-4 sm:p-5","data-testid":"devices-page",children:[K?s.jsx(M,{children:s.jsx(P,{className:"py-16 text-center text-sm text-muted-foreground",children:e("common.loading")})}):N?s.jsx(M,{children:s.jsx(P,{className:"py-16 text-center text-sm text-destructive",children:e("device.loadFailed")})}):E.length===0?s.jsx(M,{children:s.jsxs(P,{className:"space-y-4 py-14 text-center",children:[s.jsx("div",{className:"mx-auto flex h-12 w-12 items-center justify-center rounded-xl border border-border bg-muted",children:s.jsx(X,{className:"h-6 w-6 text-muted-foreground"})}),s.jsxs("div",{className:"space-y-1",children:[s.jsx("h2",{className:"text-lg font-medium",children:e("device.noDevices")}),s.jsx("p",{className:"text-sm text-muted-foreground",children:e("device.addDevice")})]}),s.jsxs(C,{variant:"default","data-testid":"devices-add-empty",onClick:()=>m(!0),children:[s.jsx(Y,{className:"h-4 w-4"}),e("device.addDevice")]})]})}):s.jsx("div",{className:"grid gap-3 lg:grid-cols-2",children:E.map(n=>s.jsx(Be,{device:n,onEdit:()=>y(n),onDelete:()=>d(n)},n.id))}),o&&s.jsx(Z,{mode:"create",onClose:()=>m(!1)}),t&&s.jsx(Z,{mode:"edit",device:t,onClose:()=>y(null)}),s.jsx(ce,{open:r!==null,onOpenChange:n=>!n&&d(null),children:s.jsxs(de,{children:[s.jsxs(le,{children:[s.jsx(ue,{className:"bg-destructive/10",children:s.jsx(ee,{className:"h-5 w-5 text-destructive"})}),s.jsx(me,{children:e("device.deleteConfirm")}),s.jsx(he,{children:e("device.deleteDescription",{name:(r==null?void 0:r.name)??""})})]}),s.jsxs(pe,{children:[s.jsx(xe,{children:e("common.cancel")}),s.jsx(ve,{variant:"destructive",disabled:!r||k.isPending,onClick:()=>{r&&(k.mutate(r.id),d(null))},children:e("common.delete")})]})]})})]})}function Be({device:e,onEdit:o,onDelete:m}){const{t}=b(),y=e.type==="local"?s.jsx(X,{className:"h-4 w-4"}):s.jsx(fe,{className:"h-4 w-4"}),r=e.type==="local"?t("device.typeLocal"):`${e.username??"-"}@${e.host??"-"}:${e.port??22}`,d=I({mutationFn:async()=>{const l=await fetch(`/api/devices/${e.id}/test-connection`,{method:"POST"});let h=null;try{h=await l.json()}catch{h=null}if(!l.ok){const c=h;throw new Error((c==null?void 0:c.error)??t("common.error"))}return h},onSuccess:l=>{f.success(l.message??t("common.success"))},onError:l=>{f.error(l instanceof Error?l.message:t("common.error"))}});return s.jsxs(M,{"data-testid":"device-card","data-device-id":e.id,"data-device-name":e.name,className:"overflow-hidden border-border/50",children:[s.jsxs(Ae,{className:"space-y-2 pb-2",children:[s.jsxs("div",{className:"flex items-start justify-between gap-2",children:[s.jsxs("div",{className:"flex min-w-0 items-center gap-2.5",children:[s.jsx("div",{className:"flex h-8 w-8 shrink-0 items-center justify-center rounded-md border border-border bg-muted text-muted-foreground",children:y}),s.jsxs("div",{className:"min-w-0 space-y-0.5",children:[s.jsx(Te,{className:"line-clamp-1 text-sm",title:e.name,children:e.name}),s.jsx(Fe,{className:"line-clamp-1 text-xs",children:r})]})]}),s.jsx("div",{className:"flex shrink-0 items-center gap-1",children:s.jsxs(ge,{children:[s.jsx(ye,{render:s.jsx(C,{variant:"ghost",size:"icon-sm","data-testid":`device-card-actions-${e.id}`,"aria-label":t("common.edit"),title:t("common.edit")}),children:s.jsx(je,{className:"h-4 w-4"})}),s.jsxs(we,{align:"end",children:[s.jsxs($,{"data-testid":`device-card-edit-${e.id}`,onClick:o,children:[s.jsx(Ne,{className:"h-4 w-4"}),t("common.edit")]}),e.type==="ssh"&&s.jsxs($,{"data-testid":`device-card-test-${e.id}`,onClick:()=>d.mutate(),disabled:d.isPending,children:[s.jsx(We,{className:"h-4 w-4"}),t("common.test")]}),s.jsx(De,{}),s.jsxs($,{"data-testid":`device-card-delete-${e.id}`,variant:"destructive",onClick:m,children:[s.jsx(ee,{className:"h-4 w-4"}),t("common.delete")]})]})]})})]}),s.jsxs("div",{className:"flex flex-wrap items-center gap-1.5",children:[s.jsx(Q,{variant:"outline",className:"text-[11px] font-normal",children:e.type==="local"?t("device.typeLocal"):t("device.typeSSHBadge")}),e.session&&s.jsx(Q,{variant:"outline",className:"text-[11px] font-normal",children:e.session}),s.jsx(Ce,{deviceId:e.id})]})]}),s.jsxs(P,{className:"pt-0",children:[s.jsx(be,{className:"mb-2"}),s.jsx("div",{className:"flex items-center justify-end",children:s.jsx(Se,{to:`/devices/${e.id}`,"data-testid":`device-card-connect-${e.id}`,className:ke({variant:"outline",size:"sm"}),children:t("device.connect")})})]})]})}function Z({mode:e,device:o,onClose:m}){const{t}=b(),y=_(),[r,d]=v.useState(qe(o)),[l,h]=v.useState(!1),[c,K]=v.useState(!1),N=e==="edit",S=r.type==="ssh",k=I({mutationFn:async a=>{const i=await fetch("/api/devices",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(a)});if(!i.ok)throw new Error(await G(i,t("device.createFailed")));return i.json()},onSuccess:()=>{y.invalidateQueries({queryKey:["devices"]}),f.success(t("common.success")),m()},onError:a=>{f.error(a instanceof Error?a.message:t("common.error"))}}),E=I({mutationFn:async a=>{if(!o)throw new Error(t("apiError.deviceNotFound"));const i=await fetch(`/api/devices/${o.id}`,{method:"PATCH",headers:{"Content-Type":"application/json"},body:JSON.stringify(a)});if(!i.ok)throw new Error(await G(i,t("device.updateFailed")));return i.json()},onSuccess:()=>{y.invalidateQueries({queryKey:["devices"]}),f.success(t("common.success")),m()},onError:a=>{f.error(a instanceof Error?a.message:t("common.error"))}}),n=async a=>{a.preventDefault(),K(!0);const i=Oe(r);if(i){f.error(t(i));return}h(!0);try{e==="create"?await k.mutateAsync(Le(r)):await E.mutateAsync(He(r))}catch{}finally{h(!1)}},p=`${e}-device-name`,D=`${e}-device-type`,A=`${e}-device-host`,T=`${e}-device-port`,F=`${e}-device-username`,W=`${e}-device-session`,q=`${e}-device-default-working-dir`,L=`${e}-device-auth-mode`,H=`${e}-device-password`,O=`${e}-device-private-key`,B=`${e}-device-private-key-passphrase`,te={local:t("device.typeLocal"),ssh:t("device.typeSSH")},ae={password:t("device.authPassword"),key:t("device.authKey"),agent:t("device.authAgent"),configRef:t("device.authConfigRef")},R=a=>s.jsx("div",{className:"text-[11px] font-semibold uppercase tracking-wider text-muted-foreground",children:a}),u=(a,i,j)=>s.jsxs("label",{className:"block text-xs font-medium text-foreground",htmlFor:a,children:[i,j&&s.jsx("span",{className:"ml-0.5 text-destructive",children:"*"})]});return s.jsx(Ee,{open:!0,onOpenChange:a=>!a&&m(),children:s.jsxs(Me,{"data-testid":"device-dialog",className:"w-full max-w-2xl",children:[s.jsxs(Pe,{children:[s.jsx(Ie,{children:t(N?"device.editDevice":"device.addDevice")}),s.jsx(Ke,{children:t(N?"device.editDeviceDescription":"device.addDeviceDescription")})]}),s.jsxs("form",{onSubmit:n,className:"space-y-4",children:[s.jsxs("div",{className:"-mr-2 max-h-[min(70dvh,720px)] space-y-5 overflow-y-auto pr-2",children:[s.jsxs("section",{className:"space-y-2.5",children:[R(t("device.sectionBasic")),s.jsxs("div",{className:"grid gap-3 sm:grid-cols-2",children:[s.jsxs("div",{className:"space-y-1.5 sm:col-span-2",children:[u(p,t("device.name"),!0),s.jsx(x,{id:p,"data-testid":"device-name-input",type:"text",value:r.name,onChange:a=>d(i=>({...i,name:a.target.value})),placeholder:t("device.namePlaceholder"),required:!0})]}),s.jsxs("div",{className:"space-y-1.5",children:[u(D,t("device.type")),s.jsxs(z,{value:r.type,onValueChange:a=>{if(!a)return;const i=a;d(j=>({...j,type:i,authMode:i==="local"?"auto":j.authMode==="auto"?"agent":j.authMode}))},disabled:N,children:[s.jsx(V,{id:D,"data-testid":"device-type-select",className:"w-full",children:s.jsx(U,{placeholder:t("device.type"),children:a=>te[a]??""})}),s.jsxs(J,{children:[s.jsx(w,{value:"local",children:t("device.typeLocal")}),s.jsx(w,{value:"ssh",children:t("device.typeSSH")})]})]})]}),s.jsxs("div",{className:"space-y-1.5",children:[u(W,t("device.session")),s.jsx(x,{id:W,"data-testid":"device-session-input",type:"text",value:r.session,onChange:a=>d(i=>({...i,session:a.target.value})),placeholder:t("device.sessionPlaceholder")})]}),s.jsxs("div",{className:"space-y-1.5 sm:col-span-2",children:[u(q,t("device.defaultWorkingDir")),s.jsx(x,{id:q,"data-testid":"device-default-working-dir-input",type:"text",value:r.defaultWorkingDir,onChange:a=>d(i=>({...i,defaultWorkingDir:a.target.value})),placeholder:t("device.defaultWorkingDirPlaceholder")})]})]})]}),S&&s.jsxs(s.Fragment,{children:[s.jsxs("section",{className:"space-y-2.5",children:[R(t("device.sectionConnection")),s.jsxs("div",{className:"grid gap-3 sm:grid-cols-3",children:[s.jsxs("div",{className:"space-y-1.5 sm:col-span-2",children:[u(A,t("device.host"),!0),s.jsx(x,{id:A,type:"text",value:r.host,onChange:a=>d(i=>({...i,host:a.target.value})),placeholder:t("device.hostPlaceholder"),"aria-invalid":c&&!r.host.trim()})]}),s.jsxs("div",{className:"space-y-1.5",children:[u(T,t("device.port"),!0),s.jsx(x,{id:T,type:"number",value:Number.isNaN(r.port)?"":r.port,onChange:a=>{const i=a.target.value;d(j=>({...j,port:i===""?Number.NaN:Number.parseInt(i,10)}))},min:1,max:65535,"aria-invalid":c&&!se(r.port)})]}),s.jsxs("div",{className:"space-y-1.5 sm:col-span-2",children:[u(F,t("device.username"),!0),s.jsx(x,{id:F,type:"text",value:r.username,onChange:a=>d(i=>({...i,username:a.target.value})),placeholder:t("device.usernamePlaceholder"),"aria-invalid":c&&!r.username.trim()})]})]})]}),s.jsxs("section",{className:"space-y-2.5",children:[R(t("device.sectionAuth")),s.jsxs("div",{className:"space-y-3",children:[s.jsxs("div",{className:"space-y-1.5",children:[u(L,t("device.authMode")),s.jsxs(z,{value:r.authMode,onValueChange:a=>{a&&d(i=>({...i,authMode:a}))},children:[s.jsx(V,{id:L,"data-testid":"device-auth-mode-select",className:"w-full",children:s.jsx(U,{placeholder:t("device.authMode"),children:a=>ae[a]??""})}),s.jsxs(J,{children:[s.jsx(w,{value:"password",children:t("device.authPassword")}),s.jsx(w,{value:"key",children:t("device.authKey")}),s.jsx(w,{value:"agent",children:t("device.authAgent")}),s.jsx(w,{value:"configRef",children:t("device.authConfigRef")})]})]})]}),r.authMode==="password"&&s.jsxs("div",{className:"space-y-1.5",children:[u(H,t("device.password")),s.jsx(x,{id:H,type:"password",value:r.password,onChange:a=>d(i=>({...i,password:a.target.value}))})]}),r.authMode==="key"&&s.jsxs(s.Fragment,{children:[s.jsxs("div",{className:"space-y-1.5",children:[u(O,t("device.privateKey")),s.jsx(Re,{id:O,value:r.privateKey,onChange:a=>d(i=>({...i,privateKey:a.target.value})),className:"h-28 font-mono text-xs",placeholder:t("device.privateKeyPlaceholder")})]}),s.jsxs("div",{className:"space-y-1.5",children:[u(B,t("device.passphrase")),s.jsx(x,{id:B,type:"password",value:r.privateKeyPassphrase,onChange:a=>d(i=>({...i,privateKeyPassphrase:a.target.value}))})]})]}),r.authMode==="configRef"&&s.jsxs("div",{className:"space-y-1.5",children:[u(`${e}-device-ssh-config-ref`,t("device.authConfigRef"),!0),s.jsx(x,{id:`${e}-device-ssh-config-ref`,"data-testid":"device-ssh-config-ref-input",type:"text",value:r.sshConfigRef,onChange:a=>d(i=>({...i,sshConfigRef:a.target.value})),placeholder:t("device.sshConfigRefPlaceholder"),"aria-invalid":c&&!r.sshConfigRef.trim()}),s.jsx("p",{className:"text-[11px] text-muted-foreground",children:t("device.sshConfigRefHint")})]})]})]})]})]}),s.jsxs($e,{children:[s.jsx(C,{type:"button",variant:"outline",onClick:m,children:t("common.cancel")}),s.jsx(C,{type:"submit",variant:"default","data-testid":"device-dialog-save",disabled:l,children:t(l?"common.saving":"common.save")})]})]})]})})}function Je(){const{t:e}=b();return s.jsx(s.Fragment,{children:e("sidebar.manageDevices")})}function Ge(){const{t:e}=b(),o=()=>{window.dispatchEvent(new CustomEvent("tmex:open-add-device"))};return s.jsx(C,{variant:"ghost",size:"icon-sm","data-testid":"devices-add",onClick:o,"aria-label":e("sidebar.addDevice"),title:e("sidebar.addDevice"),children:s.jsx(Y,{className:"h-4 w-4"})})}export{Ge as PageActions,Je as PageTitle,Ue as default};
|
|
1
|
+
import{u as b,r as v,a as _,b as ie,c as re,d as ne,e as I,t as oe,j as s,M as X,B as C,P as Y,A as ce,f as de,g as le,h as ue,T as ee,i as me,k as he,l as pe,m as xe,n as ve,G as fe,o as f,D as ge,p as ye,E as je,q as we,s as $,v as Ne,w as De,x as Q,y as Ce,S as be,L as Se,z as ke,C as Ee,F as Me,H as Pe,I as Ie,J as Ke,K as x,N as z,O as V,Q as U,R as J,U as w,V as Re,W as $e}from"./index-DOaZjm8K.js";import{C as M,a as P,b as Ae,c as Te,d as Fe}from"./card-CXa-2u8i.js";import{Z as We}from"./zap-DPwn8lbv.js";function g(e){const o=e.trim();return o||void 0}function qe(e){return e?{name:e.name,type:e.type,host:e.host??"",port:e.port??22,username:e.username??"",sshConfigRef:e.sshConfigRef??"",session:e.session??"tmex",defaultWorkingDir:e.defaultWorkingDir??"",authMode:e.type==="local"?"auto":e.authMode,password:"",privateKey:"",privateKeyPassphrase:""}:{name:"",type:"local",host:"",port:22,username:"root",sshConfigRef:"",session:"tmex",defaultWorkingDir:"",authMode:"auto",password:"",privateKey:"",privateKeyPassphrase:""}}function Le(e){if(e.type==="local")return{name:e.name.trim(),type:"local",session:g(e.session)??"tmex",defaultWorkingDir:g(e.defaultWorkingDir),authMode:"auto"};const o={name:e.name.trim(),type:"ssh",host:e.host.trim(),port:e.port,username:e.username.trim(),session:g(e.session)??"tmex",defaultWorkingDir:g(e.defaultWorkingDir),authMode:e.authMode};return e.authMode==="configRef"&&(o.sshConfigRef=e.sshConfigRef.trim()),e.authMode==="password"&&(o.password=e.password),e.authMode==="key"&&(o.privateKey=e.privateKey,o.privateKeyPassphrase=e.privateKeyPassphrase||void 0),o}function He(e){if(e.type==="local")return{name:e.name.trim(),session:g(e.session)??"tmex",defaultWorkingDir:g(e.defaultWorkingDir)??"",authMode:"auto"};const o={name:e.name.trim(),host:e.host.trim(),port:e.port,username:e.username.trim(),sshConfigRef:e.authMode==="configRef"?e.sshConfigRef.trim():"",session:g(e.session)??"tmex",defaultWorkingDir:g(e.defaultWorkingDir)??"",authMode:e.authMode};return e.authMode==="password"&&e.password&&(o.password=e.password),e.authMode==="key"&&e.privateKey&&(o.privateKey=e.privateKey,o.privateKeyPassphrase=e.privateKeyPassphrase||void 0),o}function se(e){return Number.isInteger(e)&&e>=1&&e<=65535}function Oe(e){return e.type!=="ssh"?null:e.host.trim()?se(e.port)?e.username.trim()?e.authMode==="configRef"&&!e.sshConfigRef.trim()?"validation.sshConfigRequired":null:"validation.usernameRequired":"validation.portRequired":"validation.hostRequired"}async function G(e,o){try{return(await e.json()).error??o}catch{return o}}function Ue(){const{t:e}=b(),[o,m]=v.useState(!1),[t,y]=v.useState(null),[r,d]=v.useState(null),l=_(),h=ie(n=>{var p;return((p=n.settings)==null?void 0:p.language)??"en_US"});v.useEffect(()=>{const n=()=>m(!0);return window.addEventListener("tmex:open-add-device",n),()=>window.removeEventListener("tmex:open-add-device",n)},[]);const{data:c,isLoading:K,isError:N}=re({queryKey:["devices"],queryFn:async()=>{const n=await fetch("/api/devices");if(!n.ok)throw new Error(e("device.loadFailed"));return n.json()},throwOnError:!1}),S=ne(n=>n.hydrateDeviceErrors);v.useEffect(()=>{c!=null&&c.devices&&S(c.devices.map(n=>({deviceId:n.id,lastError:n.lastError??null,lastErrorType:n.lastErrorType??null})))},[c,S]);const k=I({mutationFn:async n=>{if(!(await fetch(`/api/devices/${n}`,{method:"DELETE"})).ok)throw new Error(e("device.deleteFailed"))},onSuccess:()=>{l.invalidateQueries({queryKey:["devices"]}),f.success(e("common.success"))},onError:n=>{f.error(n instanceof Error?n.message:e("common.error"))}}),E=v.useMemo(()=>[...(c==null?void 0:c.devices)??[]].sort((p,D)=>p.sortOrder-D.sortOrder||p.name.localeCompare(D.name,oe(h),{numeric:!0,sensitivity:"base"})),[c==null?void 0:c.devices,h]);return s.jsxs("div",{className:"mx-auto flex w-full max-w-6xl flex-col gap-3 p-3 pb-[calc(1rem+env(safe-area-inset-bottom))] sm:gap-4 sm:p-5","data-testid":"devices-page",children:[K?s.jsx(M,{children:s.jsx(P,{className:"py-16 text-center text-sm text-muted-foreground",children:e("common.loading")})}):N?s.jsx(M,{children:s.jsx(P,{className:"py-16 text-center text-sm text-destructive",children:e("device.loadFailed")})}):E.length===0?s.jsx(M,{children:s.jsxs(P,{className:"space-y-4 py-14 text-center",children:[s.jsx("div",{className:"mx-auto flex h-12 w-12 items-center justify-center rounded-xl border border-border bg-muted",children:s.jsx(X,{className:"h-6 w-6 text-muted-foreground"})}),s.jsxs("div",{className:"space-y-1",children:[s.jsx("h2",{className:"text-lg font-medium",children:e("device.noDevices")}),s.jsx("p",{className:"text-sm text-muted-foreground",children:e("device.addDevice")})]}),s.jsxs(C,{variant:"default","data-testid":"devices-add-empty",onClick:()=>m(!0),children:[s.jsx(Y,{className:"h-4 w-4"}),e("device.addDevice")]})]})}):s.jsx("div",{className:"grid gap-3 lg:grid-cols-2",children:E.map(n=>s.jsx(Be,{device:n,onEdit:()=>y(n),onDelete:()=>d(n)},n.id))}),o&&s.jsx(Z,{mode:"create",onClose:()=>m(!1)}),t&&s.jsx(Z,{mode:"edit",device:t,onClose:()=>y(null)}),s.jsx(ce,{open:r!==null,onOpenChange:n=>!n&&d(null),children:s.jsxs(de,{children:[s.jsxs(le,{children:[s.jsx(ue,{className:"bg-destructive/10",children:s.jsx(ee,{className:"h-5 w-5 text-destructive"})}),s.jsx(me,{children:e("device.deleteConfirm")}),s.jsx(he,{children:e("device.deleteDescription",{name:(r==null?void 0:r.name)??""})})]}),s.jsxs(pe,{children:[s.jsx(xe,{children:e("common.cancel")}),s.jsx(ve,{variant:"destructive",disabled:!r||k.isPending,onClick:()=>{r&&(k.mutate(r.id),d(null))},children:e("common.delete")})]})]})})]})}function Be({device:e,onEdit:o,onDelete:m}){const{t}=b(),y=e.type==="local"?s.jsx(X,{className:"h-4 w-4"}):s.jsx(fe,{className:"h-4 w-4"}),r=e.type==="local"?t("device.typeLocal"):`${e.username??"-"}@${e.host??"-"}:${e.port??22}`,d=I({mutationFn:async()=>{const l=await fetch(`/api/devices/${e.id}/test-connection`,{method:"POST"});let h=null;try{h=await l.json()}catch{h=null}if(!l.ok){const c=h;throw new Error((c==null?void 0:c.error)??t("common.error"))}return h},onSuccess:l=>{f.success(l.message??t("common.success"))},onError:l=>{f.error(l instanceof Error?l.message:t("common.error"))}});return s.jsxs(M,{"data-testid":"device-card","data-device-id":e.id,"data-device-name":e.name,className:"overflow-hidden border-border/50",children:[s.jsxs(Ae,{className:"space-y-2 pb-2",children:[s.jsxs("div",{className:"flex items-start justify-between gap-2",children:[s.jsxs("div",{className:"flex min-w-0 items-center gap-2.5",children:[s.jsx("div",{className:"flex h-8 w-8 shrink-0 items-center justify-center rounded-md border border-border bg-muted text-muted-foreground",children:y}),s.jsxs("div",{className:"min-w-0 space-y-0.5",children:[s.jsx(Te,{className:"line-clamp-1 text-sm",title:e.name,children:e.name}),s.jsx(Fe,{className:"line-clamp-1 text-xs",children:r})]})]}),s.jsx("div",{className:"flex shrink-0 items-center gap-1",children:s.jsxs(ge,{children:[s.jsx(ye,{render:s.jsx(C,{variant:"ghost",size:"icon-sm","data-testid":`device-card-actions-${e.id}`,"aria-label":t("common.edit"),title:t("common.edit")}),children:s.jsx(je,{className:"h-4 w-4"})}),s.jsxs(we,{align:"end",children:[s.jsxs($,{"data-testid":`device-card-edit-${e.id}`,onClick:o,children:[s.jsx(Ne,{className:"h-4 w-4"}),t("common.edit")]}),e.type==="ssh"&&s.jsxs($,{"data-testid":`device-card-test-${e.id}`,onClick:()=>d.mutate(),disabled:d.isPending,children:[s.jsx(We,{className:"h-4 w-4"}),t("common.test")]}),s.jsx(De,{}),s.jsxs($,{"data-testid":`device-card-delete-${e.id}`,variant:"destructive",onClick:m,children:[s.jsx(ee,{className:"h-4 w-4"}),t("common.delete")]})]})]})})]}),s.jsxs("div",{className:"flex flex-wrap items-center gap-1.5",children:[s.jsx(Q,{variant:"outline",className:"text-[11px] font-normal",children:e.type==="local"?t("device.typeLocal"):t("device.typeSSHBadge")}),e.session&&s.jsx(Q,{variant:"outline",className:"text-[11px] font-normal",children:e.session}),s.jsx(Ce,{deviceId:e.id})]})]}),s.jsxs(P,{className:"pt-0",children:[s.jsx(be,{className:"mb-2"}),s.jsx("div",{className:"flex items-center justify-end",children:s.jsx(Se,{to:`/devices/${e.id}`,"data-testid":`device-card-connect-${e.id}`,className:ke({variant:"outline",size:"sm"}),children:t("device.connect")})})]})]})}function Z({mode:e,device:o,onClose:m}){const{t}=b(),y=_(),[r,d]=v.useState(qe(o)),[l,h]=v.useState(!1),[c,K]=v.useState(!1),N=e==="edit",S=r.type==="ssh",k=I({mutationFn:async a=>{const i=await fetch("/api/devices",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(a)});if(!i.ok)throw new Error(await G(i,t("device.createFailed")));return i.json()},onSuccess:()=>{y.invalidateQueries({queryKey:["devices"]}),f.success(t("common.success")),m()},onError:a=>{f.error(a instanceof Error?a.message:t("common.error"))}}),E=I({mutationFn:async a=>{if(!o)throw new Error(t("apiError.deviceNotFound"));const i=await fetch(`/api/devices/${o.id}`,{method:"PATCH",headers:{"Content-Type":"application/json"},body:JSON.stringify(a)});if(!i.ok)throw new Error(await G(i,t("device.updateFailed")));return i.json()},onSuccess:()=>{y.invalidateQueries({queryKey:["devices"]}),f.success(t("common.success")),m()},onError:a=>{f.error(a instanceof Error?a.message:t("common.error"))}}),n=async a=>{a.preventDefault(),K(!0);const i=Oe(r);if(i){f.error(t(i));return}h(!0);try{e==="create"?await k.mutateAsync(Le(r)):await E.mutateAsync(He(r))}catch{}finally{h(!1)}},p=`${e}-device-name`,D=`${e}-device-type`,A=`${e}-device-host`,T=`${e}-device-port`,F=`${e}-device-username`,W=`${e}-device-session`,q=`${e}-device-default-working-dir`,L=`${e}-device-auth-mode`,H=`${e}-device-password`,O=`${e}-device-private-key`,B=`${e}-device-private-key-passphrase`,te={local:t("device.typeLocal"),ssh:t("device.typeSSH")},ae={password:t("device.authPassword"),key:t("device.authKey"),agent:t("device.authAgent"),configRef:t("device.authConfigRef")},R=a=>s.jsx("div",{className:"text-[11px] font-semibold uppercase tracking-wider text-muted-foreground",children:a}),u=(a,i,j)=>s.jsxs("label",{className:"block text-xs font-medium text-foreground",htmlFor:a,children:[i,j&&s.jsx("span",{className:"ml-0.5 text-destructive",children:"*"})]});return s.jsx(Ee,{open:!0,onOpenChange:a=>!a&&m(),children:s.jsxs(Me,{"data-testid":"device-dialog",className:"w-full max-w-2xl",children:[s.jsxs(Pe,{children:[s.jsx(Ie,{children:t(N?"device.editDevice":"device.addDevice")}),s.jsx(Ke,{children:t(N?"device.editDeviceDescription":"device.addDeviceDescription")})]}),s.jsxs("form",{onSubmit:n,className:"space-y-4",children:[s.jsxs("div",{className:"-mr-2 max-h-[min(70dvh,720px)] space-y-5 overflow-y-auto pr-2",children:[s.jsxs("section",{className:"space-y-2.5",children:[R(t("device.sectionBasic")),s.jsxs("div",{className:"grid gap-3 sm:grid-cols-2",children:[s.jsxs("div",{className:"space-y-1.5 sm:col-span-2",children:[u(p,t("device.name"),!0),s.jsx(x,{id:p,"data-testid":"device-name-input",type:"text",value:r.name,onChange:a=>d(i=>({...i,name:a.target.value})),placeholder:t("device.namePlaceholder"),required:!0})]}),s.jsxs("div",{className:"space-y-1.5",children:[u(D,t("device.type")),s.jsxs(z,{value:r.type,onValueChange:a=>{if(!a)return;const i=a;d(j=>({...j,type:i,authMode:i==="local"?"auto":j.authMode==="auto"?"agent":j.authMode}))},disabled:N,children:[s.jsx(V,{id:D,"data-testid":"device-type-select",className:"w-full",children:s.jsx(U,{placeholder:t("device.type"),children:a=>te[a]??""})}),s.jsxs(J,{children:[s.jsx(w,{value:"local",children:t("device.typeLocal")}),s.jsx(w,{value:"ssh",children:t("device.typeSSH")})]})]})]}),s.jsxs("div",{className:"space-y-1.5",children:[u(W,t("device.session")),s.jsx(x,{id:W,"data-testid":"device-session-input",type:"text",value:r.session,onChange:a=>d(i=>({...i,session:a.target.value})),placeholder:t("device.sessionPlaceholder")})]}),s.jsxs("div",{className:"space-y-1.5 sm:col-span-2",children:[u(q,t("device.defaultWorkingDir")),s.jsx(x,{id:q,"data-testid":"device-default-working-dir-input",type:"text",value:r.defaultWorkingDir,onChange:a=>d(i=>({...i,defaultWorkingDir:a.target.value})),placeholder:t("device.defaultWorkingDirPlaceholder")})]})]})]}),S&&s.jsxs(s.Fragment,{children:[s.jsxs("section",{className:"space-y-2.5",children:[R(t("device.sectionConnection")),s.jsxs("div",{className:"grid gap-3 sm:grid-cols-3",children:[s.jsxs("div",{className:"space-y-1.5 sm:col-span-2",children:[u(A,t("device.host"),!0),s.jsx(x,{id:A,type:"text",value:r.host,onChange:a=>d(i=>({...i,host:a.target.value})),placeholder:t("device.hostPlaceholder"),"aria-invalid":c&&!r.host.trim()})]}),s.jsxs("div",{className:"space-y-1.5",children:[u(T,t("device.port"),!0),s.jsx(x,{id:T,type:"number",value:Number.isNaN(r.port)?"":r.port,onChange:a=>{const i=a.target.value;d(j=>({...j,port:i===""?Number.NaN:Number.parseInt(i,10)}))},min:1,max:65535,"aria-invalid":c&&!se(r.port)})]}),s.jsxs("div",{className:"space-y-1.5 sm:col-span-2",children:[u(F,t("device.username"),!0),s.jsx(x,{id:F,type:"text",value:r.username,onChange:a=>d(i=>({...i,username:a.target.value})),placeholder:t("device.usernamePlaceholder"),"aria-invalid":c&&!r.username.trim()})]})]})]}),s.jsxs("section",{className:"space-y-2.5",children:[R(t("device.sectionAuth")),s.jsxs("div",{className:"space-y-3",children:[s.jsxs("div",{className:"space-y-1.5",children:[u(L,t("device.authMode")),s.jsxs(z,{value:r.authMode,onValueChange:a=>{a&&d(i=>({...i,authMode:a}))},children:[s.jsx(V,{id:L,"data-testid":"device-auth-mode-select",className:"w-full",children:s.jsx(U,{placeholder:t("device.authMode"),children:a=>ae[a]??""})}),s.jsxs(J,{children:[s.jsx(w,{value:"password",children:t("device.authPassword")}),s.jsx(w,{value:"key",children:t("device.authKey")}),s.jsx(w,{value:"agent",children:t("device.authAgent")}),s.jsx(w,{value:"configRef",children:t("device.authConfigRef")})]})]})]}),r.authMode==="password"&&s.jsxs("div",{className:"space-y-1.5",children:[u(H,t("device.password")),s.jsx(x,{id:H,type:"password",value:r.password,onChange:a=>d(i=>({...i,password:a.target.value}))})]}),r.authMode==="key"&&s.jsxs(s.Fragment,{children:[s.jsxs("div",{className:"space-y-1.5",children:[u(O,t("device.privateKey")),s.jsx(Re,{id:O,value:r.privateKey,onChange:a=>d(i=>({...i,privateKey:a.target.value})),className:"h-28 font-mono text-xs",placeholder:t("device.privateKeyPlaceholder")})]}),s.jsxs("div",{className:"space-y-1.5",children:[u(B,t("device.passphrase")),s.jsx(x,{id:B,type:"password",value:r.privateKeyPassphrase,onChange:a=>d(i=>({...i,privateKeyPassphrase:a.target.value}))})]})]}),r.authMode==="configRef"&&s.jsxs("div",{className:"space-y-1.5",children:[u(`${e}-device-ssh-config-ref`,t("device.authConfigRef"),!0),s.jsx(x,{id:`${e}-device-ssh-config-ref`,"data-testid":"device-ssh-config-ref-input",type:"text",value:r.sshConfigRef,onChange:a=>d(i=>({...i,sshConfigRef:a.target.value})),placeholder:t("device.sshConfigRefPlaceholder"),"aria-invalid":c&&!r.sshConfigRef.trim()}),s.jsx("p",{className:"text-[11px] text-muted-foreground",children:t("device.sshConfigRefHint")})]})]})]})]})]}),s.jsxs($e,{children:[s.jsx(C,{type:"button",variant:"outline",onClick:m,children:t("common.cancel")}),s.jsx(C,{type:"submit",variant:"default","data-testid":"device-dialog-save",disabled:l,children:t(l?"common.saving":"common.save")})]})]})]})})}function Je(){const{t:e}=b();return s.jsx(s.Fragment,{children:e("sidebar.manageDevices")})}function Ge(){const{t:e}=b(),o=()=>{window.dispatchEvent(new CustomEvent("tmex:open-add-device"))};return s.jsx(C,{variant:"ghost",size:"icon-sm","data-testid":"devices-add",onClick:o,"aria-label":e("sidebar.addDevice"),title:e("sidebar.addDevice"),children:s.jsx(Y,{className:"h-4 w-4"})})}export{Ge as PageActions,Je as PageTitle,Ue as default};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{Y as zn,_ as ut,r as oe,j as h,X as gt,ad as bt,u as ae,c as $n,al as qn,a as mt,B as se,Z as nn}from"./index-
|
|
1
|
+
import{Y as zn,_ as ut,r as oe,j as h,X as gt,ad as bt,u as ae,c as $n,al as qn,a as mt,B as se,Z as nn}from"./index-DOaZjm8K.js";import{r as pt,M as _t}from"./markdown-preview-rqUSs5w8.js";import{k as ft,d as Ve,l as te,D as Kn,m as Et,f as ht,c as Nt}from"./api-CrHrQflz.js";import{R as yt,s as vt}from"./transfer-toast-mQs0hgyg.js";import"./index-BQbq6q_-.js";/**
|
|
2
2
|
* @license lucide-react v0.564.0 - ISC
|
|
3
3
|
*
|
|
4
4
|
* This source code is licensed under the ISC license.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{Y as ce,u as F,j as e,L as It,M as Ze,z as Lt,r as w,c as Q,B as k,P as ge,a as O,e as M,o as b,am as D,v as De,T as te,A as xe,f as fe,g as pe,h as $t,i as we,k as ye,l as je,m as ve,n as be,C as ae,F as ne,H as ie,I as re,J as Ne,N as Ce,O as Ee,Q as ke,R as Se,U as le,K as B,W as de,al as U,G as Bt,x as xt,a4 as Dt,b as Oe,t as et,bo as $,X as ft,a5 as nt,bp as Ot,bq as _t,aS as zt,br as qt,bs as Kt,bt as Qt,bu as Ut,bv as it,Z as Ht}from"./index-
|
|
1
|
+
import{Y as ce,u as F,j as e,L as It,M as Ze,z as Lt,r as w,c as Q,B as k,P as ge,a as O,e as M,o as b,am as D,v as De,T as te,A as xe,f as fe,g as pe,h as $t,i as we,k as ye,l as je,m as ve,n as be,C as ae,F as ne,H as ie,I as re,J as Ne,N as Ce,O as Ee,Q as ke,R as Se,U as le,K as B,W as de,al as U,G as Bt,x as xt,a4 as Dt,b as Oe,t as et,bo as $,X as ft,a5 as nt,bp as Ot,bq as _t,aS as zt,br as qt,bs as Kt,bt as Qt,bu as Ut,bv as it,Z as Ht}from"./index-DOaZjm8K.js";import{C as H,b as G,c as J,d as pt,a as V}from"./card-CXa-2u8i.js";import{e as Vt,h as wt,F as We,i as Wt,j as Gt,D as Jt}from"./api-CrHrQflz.js";import{R as tt,T as Yt,b as Xt,d as Zt}from"./terminal-settings-panel-CNwWqOMV.js";import{S as yt}from"./send-CcFyyX2G.js";import{M as es}from"./markdown-preview-rqUSs5w8.js";import{T as jt}from"./triangle-alert-DMp7kMmJ.js";import"./selection-clipboard-Dq6Zemfd.js";import"./index-BQbq6q_-.js";/**
|
|
2
2
|
* @license lucide-react v0.564.0 - ISC
|
|
3
3
|
*
|
|
4
4
|
* This source code is licensed under the ISC license.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{Y as _,r as N,j as e,X as C,u as k,aI as Y,aJ as ee,aK as te,al as se,aL as ne,aH as F,aM as B,a4 as H,B as y,G as be,M as ve,x as Ne,c as K,N as ye,O as ke,R as Ie,U as X,aN as Ce,aO as Se,V as re,v as Me,ae as _e,a5 as ze,aP as Te,ak as g,d as Ee,aQ as Be,aR as qe,P as $e,aS as Pe,am as Re,ah as Ae}from"./index-
|
|
1
|
+
import{Y as _,r as N,j as e,X as C,u as k,aI as Y,aJ as ee,aK as te,al as se,aL as ne,aH as F,aM as B,a4 as H,B as y,G as be,M as ve,x as Ne,c as K,N as ye,O as ke,R as Ie,U as X,aN as Ce,aO as Se,V as re,v as Me,ae as _e,a5 as ze,aP as Te,ak as g,d as Ee,aQ as Be,aR as qe,P as $e,aS as Pe,am as Re,ah as Ae}from"./index-DOaZjm8K.js";import{M as Fe,r as He}from"./index-BQbq6q_-.js";import{K as Ke,S as Z}from"./send-CcFyyX2G.js";import{Z as ae}from"./zap-DPwn8lbv.js";/**
|
|
2
2
|
* @license lucide-react v0.564.0 - ISC
|
|
3
3
|
*
|
|
4
4
|
* This source code is licensed under the ISC license.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var N=Object.defineProperty;var P=(t,e,o)=>e in t?N(t,e,{enumerable:!0,configurable:!0,writable:!0,value:o}):t[e]=o;var O=(t,e,o)=>P(t,typeof e!="symbol"?e+"":e,o);import{Y as x}from"./index-
|
|
1
|
+
var N=Object.defineProperty;var P=(t,e,o)=>e in t?N(t,e,{enumerable:!0,configurable:!0,writable:!0,value:o}):t[e]=o;var O=(t,e,o)=>P(t,typeof e!="symbol"?e+"":e,o);import{Y as x}from"./index-DOaZjm8K.js";/**
|
|
2
2
|
* @license lucide-react v0.564.0 - ISC
|
|
3
3
|
*
|
|
4
4
|
* This source code is licensed under the ISC license.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{a0 as ln,a1 as an,a2 as H,a3 as q,a4 as B,a5 as un,a6 as y,a7 as tn,a8 as L,a9 as _,aa as rn,ab as o,ac as on,ad as sn,ae as fn}from"./mermaid.core-
|
|
1
|
+
import{a0 as ln,a1 as an,a2 as H,a3 as q,a4 as B,a5 as un,a6 as y,a7 as tn,a8 as L,a9 as _,aa as rn,ab as o,ac as on,ad as sn,ae as fn}from"./mermaid.core-QvhJBi8f.js";function cn(l){return l.innerRadius}function yn(l){return l.outerRadius}function gn(l){return l.startAngle}function dn(l){return l.endAngle}function mn(l){return l&&l.padAngle}function pn(l,h,I,D,v,A,C,a){var O=I-l,i=D-h,n=C-v,d=a-A,u=d*O-n*i;if(!(u*u<y))return u=(n*(h-A)-d*(l-v))/u,[l+u*O,h+u*i]}function W(l,h,I,D,v,A,C){var a=l-I,O=h-D,i=(C?A:-A)/L(a*a+O*O),n=i*O,d=-i*a,u=l+n,s=h+d,f=I+n,c=D+d,F=(u+f)/2,t=(s+c)/2,m=f-u,g=c-s,R=m*m+g*g,T=v-A,P=u*c-f*s,S=(g<0?-1:1)*L(on(0,T*T*R-P*P)),j=(P*g-m*S)/R,z=(-P*m-g*S)/R,w=(P*g+m*S)/R,p=(-P*m+g*S)/R,x=j-F,e=z-t,r=w-F,G=p-t;return x*x+e*e>r*r+G*G&&(j=w,z=p),{cx:j,cy:z,x01:-n,y01:-d,x11:j*(v/T-1),y11:z*(v/T-1)}}function hn(){var l=cn,h=yn,I=B(0),D=null,v=gn,A=dn,C=mn,a=null,O=ln(i);function i(){var n,d,u=+l.apply(this,arguments),s=+h.apply(this,arguments),f=v.apply(this,arguments)-un,c=A.apply(this,arguments)-un,F=rn(c-f),t=c>f;if(a||(a=n=O()),s<u&&(d=s,s=u,u=d),!(s>y))a.moveTo(0,0);else if(F>tn-y)a.moveTo(s*H(f),s*q(f)),a.arc(0,0,s,f,c,!t),u>y&&(a.moveTo(u*H(c),u*q(c)),a.arc(0,0,u,c,f,t));else{var m=f,g=c,R=f,T=c,P=F,S=F,j=C.apply(this,arguments)/2,z=j>y&&(D?+D.apply(this,arguments):L(u*u+s*s)),w=_(rn(s-u)/2,+I.apply(this,arguments)),p=w,x=w,e,r;if(z>y){var G=sn(z/u*q(j)),M=sn(z/s*q(j));(P-=G*2)>y?(G*=t?1:-1,R+=G,T-=G):(P=0,R=T=(f+c)/2),(S-=M*2)>y?(M*=t?1:-1,m+=M,g-=M):(S=0,m=g=(f+c)/2)}var J=s*H(m),K=s*q(m),N=u*H(T),Q=u*q(T);if(w>y){var U=s*H(g),V=s*q(g),X=u*H(R),Y=u*q(R),E;if(F<an)if(E=pn(J,K,X,Y,U,V,N,Q)){var Z=J-E[0],$=K-E[1],b=U-E[0],k=V-E[1],nn=1/q(fn((Z*b+$*k)/(L(Z*Z+$*$)*L(b*b+k*k)))/2),en=L(E[0]*E[0]+E[1]*E[1]);p=_(w,(u-en)/(nn-1)),x=_(w,(s-en)/(nn+1))}else p=x=0}S>y?x>y?(e=W(X,Y,J,K,s,x,t),r=W(U,V,N,Q,s,x,t),a.moveTo(e.cx+e.x01,e.cy+e.y01),x<w?a.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(r.y01,r.x01),!t):(a.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(e.y11,e.x11),!t),a.arc(0,0,s,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),!t),a.arc(r.cx,r.cy,x,o(r.y11,r.x11),o(r.y01,r.x01),!t))):(a.moveTo(J,K),a.arc(0,0,s,m,g,!t)):a.moveTo(J,K),!(u>y)||!(P>y)?a.lineTo(N,Q):p>y?(e=W(N,Q,U,V,u,-p,t),r=W(J,K,X,Y,u,-p,t),a.lineTo(e.cx+e.x01,e.cy+e.y01),p<w?a.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(r.y01,r.x01),!t):(a.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(e.y11,e.x11),!t),a.arc(0,0,u,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),t),a.arc(r.cx,r.cy,p,o(r.y11,r.x11),o(r.y01,r.x01),!t))):a.arc(0,0,u,T,R,t)}if(a.closePath(),n)return a=null,n+""||null}return i.centroid=function(){var n=(+l.apply(this,arguments)+ +h.apply(this,arguments))/2,d=(+v.apply(this,arguments)+ +A.apply(this,arguments))/2-an/2;return[H(d)*n,q(d)*n]},i.innerRadius=function(n){return arguments.length?(l=typeof n=="function"?n:B(+n),i):l},i.outerRadius=function(n){return arguments.length?(h=typeof n=="function"?n:B(+n),i):h},i.cornerRadius=function(n){return arguments.length?(I=typeof n=="function"?n:B(+n),i):I},i.padRadius=function(n){return arguments.length?(D=n==null?null:typeof n=="function"?n:B(+n),i):D},i.startAngle=function(n){return arguments.length?(v=typeof n=="function"?n:B(+n),i):v},i.endAngle=function(n){return arguments.length?(A=typeof n=="function"?n:B(+n),i):A},i.padAngle=function(n){return arguments.length?(C=typeof n=="function"?n:B(+n),i):C},i.context=function(n){return arguments.length?(a=n??null,i):a},i}export{hn as d};
|