dsh-ssh-tui 0.3.3 → 0.3.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +58 -11
- package/README.md +52 -14
- package/lib/index.js +33 -4
- package/lib/index.js.map +1 -1
- package/lib/session-lock.js +113 -0
- package/lib/session-lock.js.map +1 -0
- package/lib/tui.js +498 -52
- package/lib/tui.js.map +1 -1
- package/lib/types/session-lock.d.ts +26 -0
- package/lib/types/tui.d.ts +63 -1
- package/lib/types/update-check.d.ts +4 -0
- package/lib/update-check.js +49 -0
- package/lib/update-check.js.map +1 -0
- package/package.json +4 -2
package/README.en.md
CHANGED
|
@@ -9,6 +9,17 @@ If you mostly work over SSH — a jump host, a test box, a keyboard-only
|
|
|
9
9
|
session — start here. A local desktop terminal with themes and layout
|
|
10
10
|
you already like can stay as it is.
|
|
11
11
|
|
|
12
|
+
Install with the official CLI (no clone):
|
|
13
|
+
|
|
14
|
+
```sh
|
|
15
|
+
dsh plugin --profile tui add dsh-ssh-tui
|
|
16
|
+
dsh --profile tui
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Current `dsh` requires `--profile` (`dsh plugin add …` errors without it).
|
|
20
|
+
Swap `tui` for another profile name. The same command updates. Remove with
|
|
21
|
+
`dsh plugin --profile tui remove dsh-ssh-tui`.
|
|
22
|
+
|
|
12
23
|
## Official headless vs this TUI
|
|
13
24
|
|
|
14
25
|
There is no shipped TUI. The default terminal entry on a remote box is
|
|
@@ -49,11 +60,14 @@ Reproducible, no model in the loop: `npm run screenshots:slow` writes
|
|
|
49
60
|
|
|
50
61
|
## Install
|
|
51
62
|
|
|
52
|
-
|
|
63
|
+
The recommended install is the command at the top of this README:
|
|
64
|
+
`dsh plugin --profile tui add dsh-ssh-tui`. The CLI pulls npm, writes the
|
|
65
|
+
profile dependency, and appends this package to `dsh.profile.bundles`
|
|
66
|
+
because the manifest declares `dsh.bundle`.
|
|
67
|
+
|
|
68
|
+
Optional: reuse a SuperGrok / grok-bridge token already on the machine:
|
|
53
69
|
|
|
54
70
|
```sh
|
|
55
|
-
dsh plugin --profile tui add dsh-ssh-tui
|
|
56
|
-
# optional: reuse a SuperGrok / grok-bridge token already on the machine
|
|
57
71
|
dsh plugin --profile tui add github:cyjyyd/dsh-llm-xai-oauth
|
|
58
72
|
dsh plugin --profile headless add github:cyjyyd/dsh-llm-xai-oauth
|
|
59
73
|
```
|
|
@@ -67,6 +81,31 @@ dsh --profile tui
|
|
|
67
81
|
|
|
68
82
|
From a checkout: `bash scripts/smoke-headless.sh` (prints an outcome summary, never a token).
|
|
69
83
|
|
|
84
|
+
### Survive an SSH drop (tmux)
|
|
85
|
+
|
|
86
|
+
A slow-link-friendly painter is not the same as surviving a disconnect.
|
|
87
|
+
Closing the laptop or an idle jump host kills the TTY process. Put the TUI
|
|
88
|
+
in tmux so SSH is only the display:
|
|
89
|
+
|
|
90
|
+
```sh
|
|
91
|
+
tmux new -s dsh -- dsh --profile tui
|
|
92
|
+
# after reconnecting
|
|
93
|
+
tmux attach -t dsh
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
A second TUI on the same `sessionId` is refused (it would steal stdin and
|
|
97
|
+
approvals) and prints `tmux attach`. Locks live under `$DSH_HOME/tui-locks/`;
|
|
98
|
+
a leftover file from a crash is stolen if the pid is dead.
|
|
99
|
+
`DSH_TUI_NO_SESSION_LOCK=1` skips this.
|
|
100
|
+
|
|
101
|
+
On start the TUI checks npm for a newer `dsh-ssh-tui` and **notifies only**:
|
|
102
|
+
|
|
103
|
+
```text
|
|
104
|
+
发现新版本 dsh-ssh-tui 0.3.5(当前 0.3.4)。更新:dsh plugin --profile tui add dsh-ssh-tui
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Set `DSH_TUI_NO_UPDATE_CHECK=1` to skip. `/status` also shows the plugin version.
|
|
108
|
+
|
|
70
109
|
From git:
|
|
71
110
|
|
|
72
111
|
```sh
|
|
@@ -189,7 +228,9 @@ The plugin runs on Linux, macOS, and Windows (Node ≥ 22.19):
|
|
|
189
228
|
Type `/` to see slash-command suggestions — the panel merges the TUI's own
|
|
190
229
|
commands (`/find`, `/model`, `/help`, ...) with every command the harness
|
|
191
230
|
registers (`/goal`, `/plan`, `/compact`, `/permission`, `/feedback`, ...).
|
|
192
|
-
`
|
|
231
|
+
`/compact` shows a spinning 「压缩上下文」 card and footer until it
|
|
232
|
+
finishes, then the tokens recovered. `Tab` completes, `Enter` runs.
|
|
233
|
+
`/help` lists everything.
|
|
193
234
|
|
|
194
235
|
`/model` lists models for the **current** provider first. On SuperGrok that
|
|
195
236
|
is `grok-4.6` / `grok-4.5` plus reasoning effort (`xhigh` on 4.6). Switching
|
|
@@ -234,14 +275,17 @@ Interrupted streaming output keeps the already-generated prefix and is marked
|
|
|
234
275
|
system messages. Harness slash commands that accept image attachments are
|
|
235
276
|
labelled `(images ok)` in the command list and completion hints.
|
|
236
277
|
|
|
237
|
-
`/usage` (alias `/quota`)
|
|
238
|
-
source and keeps the two billing models distinct:
|
|
278
|
+
`/usage` (alias `/quota`) follows the **current** provider:
|
|
239
279
|
|
|
240
|
-
- **
|
|
241
|
-
|
|
242
|
-
- **OpenCode Zen** is metered
|
|
243
|
-
|
|
244
|
-
|
|
280
|
+
- **SuperGrok** reads `GET cli-chat-proxy.grok.com/v1/billing` (weekly remaining %);
|
|
281
|
+
- **OpenCode Go** reads the official `/v1/usage` windows (5-hour / week / month);
|
|
282
|
+
- **OpenCode Zen** is metered — the TUI points at `https://opencode.ai/zen`.
|
|
283
|
+
|
|
284
|
+
Quota is fetched at startup. Recheck cadence follows the tightest window:
|
|
285
|
+
every 10 turns for a 5-hour cap (every 4 when near a threshold), every 50
|
|
286
|
+
for weekly (every 10 when near), every 80 for monthly (every 20 when near).
|
|
287
|
+
Crossing 50% / 25% / 10% / 5% remaining posts a ⚠ planning reminder. The
|
|
288
|
+
footer shows the tightest window.
|
|
245
289
|
|
|
246
290
|
The startup screen shows the official DeepSeek whale logo (rendered from the
|
|
247
291
|
harness favicon) in the DeepSeek brand color, with the wordmark below it. The
|
|
@@ -342,6 +386,9 @@ Over SSH the TUI probes CSI 6n once and picks 80 / 160 / 250 / 400 ms from
|
|
|
342
386
|
the round-trip. `DSH_TUI_PAINT_MS` always wins (40–1000). `/status` and the
|
|
343
387
|
footer show the active tier.
|
|
344
388
|
|
|
389
|
+
SSH disconnect still kills a TUI that is not inside tmux. Do not start a
|
|
390
|
+
second copy of the same session from another window.
|
|
391
|
+
|
|
345
392
|
## Development
|
|
346
393
|
|
|
347
394
|
```sh
|
package/README.md
CHANGED
|
@@ -10,6 +10,15 @@ English: [README.en.md](README.en.md)
|
|
|
10
10
|
|
|
11
11
|
SuperGrok / X Premium 订阅走配套插件 [dsh-llm-xai-oauth](https://github.com/cyjyyd/dsh-llm-xai-oauth),复用本机 grok-bridge token,不需要 xAI API Key。
|
|
12
12
|
|
|
13
|
+
安装(官方 CLI,无需 clone):
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
dsh plugin --profile tui add dsh-ssh-tui
|
|
17
|
+
dsh --profile tui
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
当前 `dsh` 必须带 `--profile`(`dsh plugin add …` 会报缺选项)。装进别的 profile 把 `tui` 换成那个名字即可。更新同一条命令。卸载:`dsh plugin --profile tui remove dsh-ssh-tui`。
|
|
21
|
+
|
|
13
22
|
## 官方 headless 和这个 TUI
|
|
14
23
|
|
|
15
24
|
官方没有预置 TUI。远程机器上的默认终端入口是 `dsh --profile headless`:跑完一个任务,把**最后一条助手回复**打到 stdout 就退出。思考、工具调用、子代理、计划都在会话日志里,终端上看不到。
|
|
@@ -61,11 +70,11 @@ SuperGrok / X Premium 订阅走配套插件 [dsh-llm-xai-oauth](https://github.c
|
|
|
61
70
|
|
|
62
71
|
## 部署指南
|
|
63
72
|
|
|
64
|
-
|
|
73
|
+
推荐安装就是文首那条 `dsh plugin --profile tui add dsh-ssh-tui`。CLI 会从 npm 拉包、写入 profile 依赖,并把本插件加入 `dsh.profile.bundles`(因为包内声明了 `dsh.bundle`)。
|
|
74
|
+
|
|
75
|
+
可选:本机已有 SuperGrok / grok-bridge token 时再装配套 OAuth:
|
|
65
76
|
|
|
66
77
|
```bash
|
|
67
|
-
dsh plugin --profile tui add dsh-ssh-tui
|
|
68
|
-
# 可选:本机已有 SuperGrok / grok-bridge token 时
|
|
69
78
|
dsh plugin --profile tui add github:cyjyyd/dsh-llm-xai-oauth
|
|
70
79
|
dsh plugin --profile headless add github:cyjyyd/dsh-llm-xai-oauth
|
|
71
80
|
```
|
|
@@ -77,6 +86,29 @@ dsh --profile headless "Reply with exactly: tui-install-ok. Do not use tools."
|
|
|
77
86
|
dsh --profile tui # 必须在真实终端 / SSH 会话里
|
|
78
87
|
```
|
|
79
88
|
|
|
89
|
+
### SSH 断线后续跑(tmux)
|
|
90
|
+
|
|
91
|
+
弱网友好不等于断线后还能跑。合盖、跳板 idle、换网会杀掉当前 TTY 上的进程。
|
|
92
|
+
把 TUI 放进 tmux,断 SSH 只丢显示器,不丢 Agent:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
tmux new -s dsh -- dsh --profile tui
|
|
96
|
+
# 断线后重新 SSH 进来
|
|
97
|
+
tmux attach -t dsh
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
同一 `sessionId` 不能开第二份 TUI(会抢 stdin 和审批)。第二份启动会退出并提示
|
|
101
|
+
`tmux attach`。锁在 `$DSH_HOME/tui-locks/`;进程异常退出后残留锁会在下次启动时
|
|
102
|
+
核对 pid,已死则自动接管。调试可设 `DSH_TUI_NO_SESSION_LOCK=1`。
|
|
103
|
+
|
|
104
|
+
启动时会查一次 npm 上的 `dsh-ssh-tui` 最新版,有更新只提示、不自动升级:
|
|
105
|
+
|
|
106
|
+
```text
|
|
107
|
+
发现新版本 dsh-ssh-tui 0.3.5(当前 0.3.4)。更新:dsh plugin --profile tui add dsh-ssh-tui
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
`DSH_TUI_NO_UPDATE_CHECK=1` 可关掉。`/status` 里也能看到当前插件版本。
|
|
111
|
+
|
|
80
112
|
仓库内也可:`bash scripts/smoke-headless.sh`(记录出口摘要,不打印 token)。
|
|
81
113
|
|
|
82
114
|
### 方式一:从 git clone 安装
|
|
@@ -105,15 +137,14 @@ npm run build
|
|
|
105
137
|
dsh plugin --profile tui add "link:$(pwd)"
|
|
106
138
|
```
|
|
107
139
|
|
|
108
|
-
### 方式三:指定其它 profile
|
|
140
|
+
### 方式三:指定其它 profile
|
|
109
141
|
|
|
110
142
|
```bash
|
|
143
|
+
dsh plugin --profile work add dsh-ssh-tui
|
|
144
|
+
# 或仓库脚本
|
|
111
145
|
bash scripts/install-npm.sh work
|
|
112
146
|
```
|
|
113
147
|
|
|
114
|
-
`dsh plugin add` 会从 npm 拉取包、写入 profile 依赖,并自动把 `dsh-ssh-tui`
|
|
115
|
-
加入该 profile 的 `dsh.profile.bundles`。
|
|
116
|
-
|
|
117
148
|
### 智能路由模式(dsh-routing-suite)
|
|
118
149
|
|
|
119
150
|
需要“智能路由模式”时,安装 `dsh-routing-suite` 并注册其 preset:
|
|
@@ -165,7 +196,9 @@ dsh --profile tui --no-color
|
|
|
165
196
|
|
|
166
197
|
斜杠命令:`/help`、`/find`、`/model`、`/submodel`、`/subeffort`、`/mode`、`/resume`、
|
|
167
198
|
`/status`、`/subagents`、`/usage`(`/quota` 同义)、`/setup`、`/clear`,
|
|
168
|
-
以及 harness 自带命令(`/goal`、`/plan`、`/compact`
|
|
199
|
+
以及 harness 自带命令(`/goal`、`/plan`、`/compact` 等)。`/compact` 进行中会显示
|
|
200
|
+
「压缩上下文」卡片和底栏转圈,结束时写出回收的 token 数。模型请求失败会显示重试
|
|
201
|
+
进度;会话标题由模型生成后写到窗口标题。harness 命令若声明
|
|
169
202
|
支持图片附件,会在命令列表和补全提示中标注“可附图”。
|
|
170
203
|
|
|
171
204
|
`/model` 默认列出**当前提供商**的模型。已经在 SuperGrok 时,直接选
|
|
@@ -200,12 +233,13 @@ SuperGrok / X Premium 走本机 OAuth,不需要填 Key。`/status` 和底栏
|
|
|
200
233
|
`提问用户` 卡片。`/goal` 是折叠的 `目标` 卡片。`/find 思考 padAnsi` 或 `Alt+1..4`
|
|
201
234
|
可跳到对应类别的最新卡片。
|
|
202
235
|
|
|
203
|
-
`/usage`
|
|
236
|
+
`/usage`(`/quota` 同义)按**当前提供商**查额度:
|
|
237
|
+
|
|
238
|
+
- **SuperGrok**:`GET cli-chat-proxy.grok.com/v1/billing`,显示本周剩余%;
|
|
239
|
+
- **OpenCode Go**:官方 `/v1/usage`,滚动 5 小时 / 本周 / 本月剩余%;
|
|
240
|
+
- **OpenCode Zen**:按量计费、没有固定额度,提示到 `https://opencode.ai/zen`。
|
|
204
241
|
|
|
205
|
-
|
|
206
|
-
百分比、限流状态与重置时间;
|
|
207
|
-
- **OpenCode Zen**:按 API 账单计费、没有固定额度,TUI 不假装查询余额,
|
|
208
|
-
只提示到 `https://opencode.ai/zen` 查看,并附本会话已记录的 token 用量。
|
|
242
|
+
启动时查一次。之后按最紧窗口调查询:5 小时额度每 10 轮(接近阈值改 4 轮),周额度每 50 轮(接近改 10 轮),月额度每 80 轮(接近改 20 轮)。剩余跨过 50% / 25% / 10% / 5% 时用 ⚠ 提示合理规划。底栏显示最紧窗口的剩余百分比。
|
|
209
243
|
|
|
210
244
|
## 配置
|
|
211
245
|
|
|
@@ -275,10 +309,12 @@ bash scripts/uninstall.sh work # 指定 profile
|
|
|
275
309
|
## 开发与目录结构
|
|
276
310
|
|
|
277
311
|
```text
|
|
278
|
-
src/index.ts
|
|
312
|
+
src/index.ts 插件入口:启动选择器、会话创建/恢复/切换、session lock
|
|
279
313
|
src/startup.ts 命令行参数解析(--resume / --new / --model ...)
|
|
280
314
|
src/picker.ts 启动历史会话选择器
|
|
281
315
|
src/session-list.ts 历史会话扫描与标签(共享给 /resume)
|
|
316
|
+
src/session-lock.ts 同会话防双开
|
|
317
|
+
src/update-check.ts npm 最新版提示(不自动升级)
|
|
282
318
|
src/tui.ts 终端渲染、交互、统计、标题/铃声
|
|
283
319
|
cordis.patch.yml dsh bundle patch(挂载 TUI 与 agent-presets)
|
|
284
320
|
scripts/ 安装 / 卸载 / 验证脚本
|
|
@@ -299,6 +335,8 @@ npm run build
|
|
|
299
335
|
`DSH_TUI_NO_BELL=1` 关闭。
|
|
300
336
|
- **滚轮误触取消**:已加入转义序列缓冲,网络拆包也不会把 `ESC` 当取消。
|
|
301
337
|
- **跳板机 / 多层代理 SSH 发画**:每一帧只发脏行,并且拼成一次 `stdout.write`。本机 80 ms;SSH 启动时用 CSI 6n 测往返,按 RTT 选 80/160/250/400 ms。`DSH_TUI_PAINT_MS` 始终优先(40–1000)。`/status` 和底栏显示当前档。
|
|
338
|
+
- **SSH 断了任务也没了**:用 tmux 开 TUI(见上文)。不要在另一个 SSH 窗口再开同一会话。
|
|
339
|
+
- **提示会话已在 pid 运行**:那份 TUI 还活着。`tmux attach -t dsh`;确认进程已死再删 `$DSH_HOME/tui-locks/` 对应文件。
|
|
302
340
|
|
|
303
341
|
## License
|
|
304
342
|
|
package/lib/index.js
CHANGED
|
@@ -12,6 +12,7 @@ import { showSessionPicker } from './picker.js';
|
|
|
12
12
|
import { mountTui } from './tui.js';
|
|
13
13
|
import { defaultReasoningEffort } from './reasoning.js';
|
|
14
14
|
import { createSubagentSelection } from './subagent-model.js';
|
|
15
|
+
import { acquireSessionLock, releaseSessionLock, sessionLockDisabled } from './session-lock.js';
|
|
15
16
|
export const name = 'ssh-tui';
|
|
16
17
|
/** Core services required before the terminal channel can drive an agent. */
|
|
17
18
|
export const inject = ['agents', 'agentDefaultModel'];
|
|
@@ -31,6 +32,21 @@ export function apply(ctx, config) {
|
|
|
31
32
|
let switching = false;
|
|
32
33
|
let handle;
|
|
33
34
|
let controller;
|
|
35
|
+
let sessionLockPathHeld;
|
|
36
|
+
const dropSessionLock = async () => {
|
|
37
|
+
const path = sessionLockPathHeld;
|
|
38
|
+
sessionLockPathHeld = undefined;
|
|
39
|
+
if (path !== undefined)
|
|
40
|
+
await releaseSessionLock(path);
|
|
41
|
+
};
|
|
42
|
+
const takeSessionLock = async (sessionId) => {
|
|
43
|
+
if (sessionLockDisabled())
|
|
44
|
+
return;
|
|
45
|
+
const { path } = await acquireSessionLock(sessionId, {
|
|
46
|
+
tty: process.env.SSH_TTY ?? process.env.TTY,
|
|
47
|
+
});
|
|
48
|
+
sessionLockPathHeld = path;
|
|
49
|
+
};
|
|
34
50
|
// An explicit in-process change (/setup or /model) wins over launch-time
|
|
35
51
|
// CLI overrides for every session created or resumed later in this process.
|
|
36
52
|
let liveSelection;
|
|
@@ -106,11 +122,19 @@ export function apply(ctx, config) {
|
|
|
106
122
|
installModelSelection(agentCtx, selectionRef);
|
|
107
123
|
await agentPresets?.mount(agentCtx);
|
|
108
124
|
};
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
125
|
+
await takeSessionLock(String(sessionId));
|
|
126
|
+
try {
|
|
127
|
+
handle = resume
|
|
128
|
+
? await agents.resume({ resumeSessionId: sessionId, agentOptions, setup })
|
|
129
|
+
: await agents.create({ sessionId, meta: { cwd: process.cwd() }, agentOptions, setup });
|
|
130
|
+
}
|
|
131
|
+
catch (error) {
|
|
132
|
+
await dropSessionLock();
|
|
133
|
+
throw error;
|
|
134
|
+
}
|
|
112
135
|
if (disposed) {
|
|
113
136
|
await handle.dispose();
|
|
137
|
+
await dropSessionLock();
|
|
114
138
|
return;
|
|
115
139
|
}
|
|
116
140
|
const presetId = agentPresets?.composedPreset(handle.agent.ctx) ?? agentPresets?.defaultId;
|
|
@@ -157,6 +181,7 @@ export function apply(ctx, config) {
|
|
|
157
181
|
await handle.dispose();
|
|
158
182
|
handle = undefined;
|
|
159
183
|
}
|
|
184
|
+
await dropSessionLock();
|
|
160
185
|
await start(SessionId(target), true);
|
|
161
186
|
}
|
|
162
187
|
catch (error) {
|
|
@@ -206,7 +231,10 @@ export function apply(ctx, config) {
|
|
|
206
231
|
await start(SessionId(config.sessionId), config.resume === true);
|
|
207
232
|
};
|
|
208
233
|
void boot().catch((error) => {
|
|
209
|
-
|
|
234
|
+
const detail = error instanceof Error && error.name === 'SessionLockHeldError'
|
|
235
|
+
? error.message
|
|
236
|
+
: errorChain(error);
|
|
237
|
+
process.stderr.write(`dsh-ssh-tui: session "${bootingSessionId}" failed to start:\n${detail}\n`);
|
|
210
238
|
const exit = ctx.get('appExit');
|
|
211
239
|
if (exit !== undefined)
|
|
212
240
|
exit(1);
|
|
@@ -218,6 +246,7 @@ export function apply(ctx, config) {
|
|
|
218
246
|
pickerAbort.abort();
|
|
219
247
|
await controller?.dispose();
|
|
220
248
|
await handle?.dispose();
|
|
249
|
+
await dropSessionLock();
|
|
221
250
|
};
|
|
222
251
|
}, 'ssh-tui');
|
|
223
252
|
}
|
package/lib/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,qBAAqB,EAAiE,MAAM,wBAAwB,CAAA;AAG7H,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAA;AAEjD,OAAO,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAA;AACpD,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACxC,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAC/C,OAAO,EAAE,QAAQ,EAAsB,MAAM,UAAU,CAAA;AACvD,OAAO,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAA;AACvD,OAAO,EAAE,uBAAuB,EAAE,MAAM,qBAAqB,CAAA;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Exclusive lock so two TUI processes do not drive the same session.
|
|
3
|
+
* Stale locks (dead pid) are stolen. Live locks tell the user to attach tmux.
|
|
4
|
+
*/
|
|
5
|
+
import { mkdir, readFile, unlink, writeFile } from 'node:fs/promises';
|
|
6
|
+
import { homedir } from 'node:os';
|
|
7
|
+
import { dirname, join } from 'node:path';
|
|
8
|
+
export class SessionLockHeldError extends Error {
|
|
9
|
+
lock;
|
|
10
|
+
path;
|
|
11
|
+
constructor(lock, path) {
|
|
12
|
+
super(formatLockHeldMessage(lock));
|
|
13
|
+
this.name = 'SessionLockHeldError';
|
|
14
|
+
this.lock = lock;
|
|
15
|
+
this.path = path;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export function sessionLockPath(sessionId, dshHome = process.env.DSH_HOME ?? join(homedir(), '.dsh')) {
|
|
19
|
+
const safe = sessionId.replaceAll(/[^A-Za-z0-9._-]/g, '_');
|
|
20
|
+
return join(dshHome, 'tui-locks', `${safe}.json`);
|
|
21
|
+
}
|
|
22
|
+
export function parseSessionLock(raw) {
|
|
23
|
+
try {
|
|
24
|
+
const parsed = JSON.parse(raw);
|
|
25
|
+
const pid = typeof parsed.pid === 'number' && Number.isInteger(parsed.pid) ? parsed.pid : undefined;
|
|
26
|
+
const sessionId = typeof parsed.sessionId === 'string' ? parsed.sessionId : undefined;
|
|
27
|
+
if (pid === undefined || pid <= 0 || sessionId === undefined || sessionId === '')
|
|
28
|
+
return undefined;
|
|
29
|
+
return {
|
|
30
|
+
pid,
|
|
31
|
+
sessionId,
|
|
32
|
+
startedAt: typeof parsed.startedAt === 'string' ? parsed.startedAt : '',
|
|
33
|
+
...(typeof parsed.tty === 'string' && parsed.tty !== '' ? { tty: parsed.tty } : {}),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/** True when `pid` still exists on this machine (best-effort). */
|
|
41
|
+
export function processIsAlive(pid) {
|
|
42
|
+
if (!Number.isInteger(pid) || pid <= 0)
|
|
43
|
+
return false;
|
|
44
|
+
try {
|
|
45
|
+
process.kill(pid, 0);
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
return error.code === 'EPERM';
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
export function formatLockHeldMessage(lock) {
|
|
53
|
+
const tty = lock.tty === undefined ? '' : ` · ${lock.tty}`;
|
|
54
|
+
return [
|
|
55
|
+
`会话 ${lock.sessionId} 已在 pid ${lock.pid}${tty} 运行。`,
|
|
56
|
+
'不要再开第二份 TUI。若 SSH 断过:tmux attach -t dsh',
|
|
57
|
+
'确认进程已死后,删除 $DSH_HOME/tui-locks/ 里对应的锁再启动。',
|
|
58
|
+
].join('\n');
|
|
59
|
+
}
|
|
60
|
+
export function sessionLockDisabled(env = process.env) {
|
|
61
|
+
return env.DSH_TUI_NO_SESSION_LOCK === '1' || env.DSH_TUI_NO_SESSION_LOCK === 'true';
|
|
62
|
+
}
|
|
63
|
+
export async function acquireSessionLock(sessionId, options = {}) {
|
|
64
|
+
const path = sessionLockPath(sessionId, options.dshHome);
|
|
65
|
+
await mkdir(dirname(path), { recursive: true, mode: 0o700 });
|
|
66
|
+
const info = {
|
|
67
|
+
pid: options.pid ?? process.pid,
|
|
68
|
+
sessionId,
|
|
69
|
+
startedAt: new Date().toISOString(),
|
|
70
|
+
...(options.tty ? { tty: options.tty } : {}),
|
|
71
|
+
};
|
|
72
|
+
const payload = `${JSON.stringify(info, null, 2)}\n`;
|
|
73
|
+
for (let attempt = 0; attempt < 4; attempt++) {
|
|
74
|
+
try {
|
|
75
|
+
await writeFile(path, payload, { flag: 'wx', mode: 0o600 });
|
|
76
|
+
return { path, info };
|
|
77
|
+
}
|
|
78
|
+
catch (error) {
|
|
79
|
+
if (error.code !== 'EEXIST')
|
|
80
|
+
throw error;
|
|
81
|
+
let existing;
|
|
82
|
+
try {
|
|
83
|
+
existing = parseSessionLock(await readFile(path, 'utf8'));
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
existing = undefined;
|
|
87
|
+
}
|
|
88
|
+
const ours = options.pid ?? process.pid;
|
|
89
|
+
if (existing !== undefined && processIsAlive(existing.pid) && existing.pid !== ours) {
|
|
90
|
+
throw new SessionLockHeldError(existing, path);
|
|
91
|
+
}
|
|
92
|
+
try {
|
|
93
|
+
await unlink(path);
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
// Raced with another unlock; retry exclusive create.
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
throw new Error(`无法占用会话锁 ${path}`);
|
|
101
|
+
}
|
|
102
|
+
export async function releaseSessionLock(path, pid = process.pid) {
|
|
103
|
+
try {
|
|
104
|
+
const current = parseSessionLock(await readFile(path, 'utf8'));
|
|
105
|
+
if (current !== undefined && current.pid !== pid)
|
|
106
|
+
return;
|
|
107
|
+
await unlink(path);
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
// Missing lock is fine.
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=session-lock.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-lock.js","sourceRoot":"","sources":["../src/session-lock.ts"],"names":[],"mappings":"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"}
|