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 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
- On a remote box with `dsh` and pnpm already installed (no clone):
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
- `Tab` completes, `Enter` runs. `/help` lists everything.
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`) works when the current provider is an OpenCode
238
- source and keeps the two billing models distinct:
278
+ `/usage` (alias `/quota`) follows the **current** provider:
239
279
 
240
- - **OpenCode Go** queries the official quota endpoint and shows rolling
241
- 5-hour / weekly / monthly usage percentages, limit state, and reset times;
242
- - **OpenCode Zen** is metered per API bill and has no fixed quota, so the TUI
243
- points to `https://opencode.ai/zen` for balance/billing and shows the
244
- session token usage it has recorded instead of inventing a quota.
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
- 远程机器最短路径(已有 `dsh` pnpm,无需 clone):
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 的 npm 安装
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` 等)。harness 命令若声明
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` 在当前提供商为 OpenCode 源时可用,并区分两种计费方式:
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
- - **OpenCode Go**:调用官方额度接口,显示滚动 5 小时 / 本周 / 本月用量
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
- handle = resume
110
- ? await agents.resume({ resumeSessionId: sessionId, agentOptions, setup })
111
- : await agents.create({ sessionId, meta: { cwd: process.cwd() }, agentOptions, setup });
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
- process.stderr.write(`dsh-ssh-tui: session "${bootingSessionId}" failed to start: ${errorChain(error)}\n`);
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":"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"}
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;AAC7D,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAA;AAE/F,MAAM,CAAC,MAAM,IAAI,GAAG,SAAS,CAAA;AAE7B,6EAA6E;AAC7E,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,QAAQ,EAAE,mBAAmB,CAAC,CAAA;AAqBrD;;;;;GAKG;AACH,MAAM,UAAU,KAAK,CAAC,GAAY,EAAE,MAAc;IAChD,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;QAClD,MAAM,IAAI,KAAK,CAAC,6EAA6E,CAAC,CAAA;IAChG,CAAC;IACD,MAAM,iBAAiB,GAAG,uBAAuB,CAAC,GAAG,CAAC,CAAA;IACtD,GAAG,CAAC,MAAM,CAAC,GAAG,EAAE;QACd,IAAI,QAAQ,GAAG,KAAK,CAAA;QACpB,IAAI,SAAS,GAAG,KAAK,CAAA;QACrB,IAAI,MAA+B,CAAA;QACnC,IAAI,UAAqC,CAAA;QACzC,IAAI,mBAAuC,CAAA;QAE3C,MAAM,eAAe,GAAG,KAAK,IAAmB,EAAE;YAChD,MAAM,IAAI,GAAG,mBAAmB,CAAA;YAChC,mBAAmB,GAAG,SAAS,CAAA;YAC/B,IAAI,IAAI,KAAK,SAAS;gBAAE,MAAM,kBAAkB,CAAC,IAAI,CAAC,CAAA;QACxD,CAAC,CAAA;QAED,MAAM,eAAe,GAAG,KAAK,EAAE,SAAiB,EAAiB,EAAE;YACjE,IAAI,mBAAmB,EAAE;gBAAE,OAAM;YACjC,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,kBAAkB,CAAC,SAAS,EAAE;gBACnD,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC,OAAO,IAAI,OAAO,CAAC,GAAG,CAAC,GAAG;aAC5C,CAAC,CAAA;YACF,mBAAmB,GAAG,IAAI,CAAA;QAC5B,CAAC,CAAA;QACD,yEAAyE;QACzE,4EAA4E;QAC5E,IAAI,aAAyC,CAAA;QAE7C,iEAAiE;QACjE,MAAM,UAAU,GAAG,CAAC,SAAiB,EAAU,EAAE;YAC/C,MAAM,QAAQ,GAAG,GAAG,CAAC,GAAG,CAAC,mBAAmB,CAAuB,CAAA;YACnE,MAAM,MAAM,GAAG,WAAW,CAAA;YAC1B,MAAM,WAAW,GAAG,QAAQ,EAAE,WAAW,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAA;YACvD,IAAI,QAAQ,KAAK,SAAS,IAAI,WAAW,KAAK,CAAC,CAAC,EAAE,CAAC;gBACjD,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,WAAW,GAAG,MAAM,CAAC,MAAM,CAAC,CAAA;gBACxD,IAAI,IAAI,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;oBACrC,OAAO,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,WAAW,GAAG,MAAM,CAAC,MAAM,CAAC,GAAG,SAAS,EAAE,CAAA;gBACxE,CAAC;YACH,CAAC;YACD,OAAO,sDAAsD,SAAS,EAAE,CAAA;QAC1E,CAAC,CAAA;QAED,MAAM,KAAK,GAAG,KAAK,EAAE,SAAoB,EAAE,MAAe,EAAiB,EAAE;YAC3E,MAAM,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,KAAK,EAAE,CAAA;YAChC,IAAI,QAAQ;gBAAE,OAAM;YACpB,MAAM,MAAM,GAAG,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;YAChC,IAAI,MAAM,KAAK,SAAS;gBAAE,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAA;YACvF,MAAM,YAAY,GAAG,GAAG,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAA;YACjD,MAAM,cAAc,GAAG,YAAY,EAAE,gBAAgB,EAAE,CAAA;YAEvD,yEAAyE;YACzE,4EAA4E;YAC5E,MAAM,QAAQ,GAAG,aAAa,EAAE,QAAQ;mBACnC,MAAM,CAAC,QAAQ;mBACf,cAAc,EAAE,QAAQ;mBACxB,mBAAmB,CAAA;YACxB,MAAM,KAAK,GAAG,aAAa,EAAE,KAAK;mBAC7B,MAAM,CAAC,KAAK;mBACZ,cAAc,EAAE,KAAK;mBACrB,mBAAmB,CAAA;YAExB,sEAAsE;YACtE,0EAA0E;YAC1E,qCAAqC;YACrC,IAAI,eAAe,GAAG,aAAa,EAAE,eAAe,CAAA;YACpD,IAAI,eAAe,KAAK,SAAS,IAAI,aAAa,KAAK,SAAS,EAAE,CAAC;gBACjE,MAAM,cAAc,GAAG,cAAc,KAAK,SAAS;uBAC9C,CAAC,MAAM,CAAC,QAAQ,KAAK,SAAS,IAAI,MAAM,CAAC,QAAQ,KAAK,cAAc,CAAC,QAAQ,CAAC;uBAC9E,CAAC,MAAM,CAAC,KAAK,KAAK,SAAS,IAAI,MAAM,CAAC,KAAK,KAAK,cAAc,CAAC,KAAK,CAAC,CAAA;gBAC1E,IAAI,cAAc;oBAAE,eAAe,GAAG,cAAc,CAAC,eAAe,CAAA;YACtE,CAAC;YACD,mEAAmE;YACnE,0EAA0E;YAC1E,wEAAwE;YACxE,iDAAiD;YACjD,IAAI,eAAe,KAAK,SAAS,IAAI,QAAQ,KAAK,mBAAmB,EAAE,CAAC;gBACtE,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;gBAC1B,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;oBACtB,eAAe,GAAG,MAAM,sBAAsB,CAAC,GAAG,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAA;gBACtE,CAAC;YACH,CAAC;YAED,MAAM,kBAAkB,GAAmB;gBACzC,QAAQ;gBACR,KAAK;gBACL,GAAG,CAAC,eAAe,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,eAAe,EAAE,CAAC;aAC9D,CAAA;YACD,MAAM,YAAY,GAAsB;gBACtC,OAAO,EAAE,kBAAkB;gBAC3B,SAAS,EAAE,SAAS;aACrB,CAAA;YACD,MAAM,YAAY,GAAG,GAAG,CAAC,GAAG,CAAC,cAAc,CAAC,CAAA;YAC5C,MAAM,YAAY,GAAG;gBACnB,QAAQ;gBACR,KAAK;gBACL,GAAG,CAAC,eAAe,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,eAAe,EAAE,CAAC;aAC9D,CAAA;YACD,MAAM,KAAK,GAAG,KAAK,EAAE,QAAiB,EAAiB,EAAE;gBACvD,qBAAqB,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAA;gBAC7C,MAAM,YAAY,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAA;YACrC,CAAC,CAAA;YACD,MAAM,eAAe,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAA;YACxC,IAAI,CAAC;gBACH,MAAM,GAAG,MAAM;oBACb,CAAC,CAAC,MAAM,MAAM,CAAC,MAAM,CAAC,EAAE,eAAe,EAAE,SAAS,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC;oBAC1E,CAAC,CAAC,MAAM,MAAM,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,EAAE,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC,CAAA;YAC3F,CAAC;YAAC,OAAO,KAAc,EAAE,CAAC;gBACxB,MAAM,eAAe,EAAE,CAAA;gBACvB,MAAM,KAAK,CAAA;YACb,CAAC;YACD,IAAI,QAAQ,EAAE,CAAC;gBACb,MAAM,MAAM,CAAC,OAAO,EAAE,CAAA;gBACtB,MAAM,eAAe,EAAE,CAAA;gBACvB,OAAM;YACR,CAAC;YACD,MAAM,QAAQ,GAAG,YAAY,EAAE,cAAc,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,YAAY,EAAE,SAAS,CAAA;YAC1F,IAAI,UAAU,GAAG,QAAQ,CAAA;YACzB,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;gBAC3B,IAAI,CAAC;oBACH,MAAM,MAAM,GAAG,MAAM,YAAY,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAA;oBACpD,IAAI,MAAM,EAAE,IAAI,KAAK,SAAS;wBAAE,UAAU,GAAG,MAAM,CAAC,IAAI,CAAA;gBAC1D,CAAC;gBAAC,MAAM,CAAC;oBACP,uBAAuB;gBACzB,CAAC;YACH,CAAC;YACD,UAAU,GAAG,QAAQ,CAAC,GAAG,EAAE;gBACzB,GAAG,MAAM;gBACT,YAAY,EAAE,KAAK;gBACnB,SAAS,EAAE,MAAM,CAAC,SAAS,CAAC;gBAC5B,MAAM;gBACN,QAAQ;gBACR,KAAK;gBACL,YAAY;gBACZ,iBAAiB;gBACjB,QAAQ;gBACR,UAAU;gBACV,OAAO,EAAE,UAAU,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;gBACtC,eAAe,EAAE,QAAQ;gBACzB,kBAAkB,EAAE,CAAC,IAAI,EAAE,EAAE;oBAC3B,aAAa,GAAG,IAAI,CAAA;gBACtB,CAAC;aACF,CAAC,CAAA;QACJ,CAAC,CAAA;QAED,6EAA6E;QAC7E,MAAM,QAAQ,GAAG,KAAK,EAAE,MAAc,EAAiB,EAAE;YACvD,IAAI,SAAS,IAAI,QAAQ;gBAAE,OAAM;YACjC,SAAS,GAAG,IAAI,CAAA;YAChB,IAAI,CAAC;gBACH,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;oBAC7B,MAAM,UAAU,CAAC,OAAO,EAAE,CAAA;oBAC1B,UAAU,GAAG,SAAS,CAAA;gBACxB,CAAC;gBACD,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;oBACzB,MAAM,MAAM,CAAC,OAAO,EAAE,CAAA;oBACtB,MAAM,GAAG,SAAS,CAAA;gBACpB,CAAC;gBACD,MAAM,eAAe,EAAE,CAAA;gBACvB,MAAM,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,CAAA;YACtC,CAAC;YAAC,OAAO,KAAc,EAAE,CAAC;gBACxB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,6CAA6C,MAAM,MAAM,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;gBACpG,MAAM,IAAI,GAAG,GAAG,CAAC,GAAG,CAAC,SAAS,CAAC,CAAA;gBAC/B,IAAI,IAAI,KAAK,SAAS;oBAAE,IAAI,CAAC,CAAC,CAAC,CAAA;;oBAC1B,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;YACtB,CAAC;oBAAS,CAAC;gBACT,SAAS,GAAG,KAAK,CAAA;YACnB,CAAC;QACH,CAAC,CAAA;QAED,MAAM,WAAW,GAAG,IAAI,eAAe,EAAE,CAAA;QACzC,IAAI,gBAAgB,GAAG,MAAM,CAAC,SAAS,CAAA;QACvC,MAAM,IAAI,GAAG,KAAK,IAAmB,EAAE;YACrC,IAAI,MAAM,CAAC,YAAY,KAAK,IAAI,EAAE,CAAC;gBACjC,MAAM,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,KAAK,EAAE,CAAA;gBAChC,MAAM,MAAM,GAAG,MAAM,iBAAiB,CAAC,GAAG,EAAE,MAAM,CAAC,KAAK,KAAK,KAAK,EAAE,WAAW,CAAC,MAAM,CAAC,CAAA;gBACvF,IAAI,QAAQ;oBAAE,OAAM;gBACpB,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;oBACpB,MAAM,IAAI,GAAG,GAAG,CAAC,GAAG,CAAC,SAAS,CAAC,CAAA;oBAC/B,IAAI,IAAI,KAAK,SAAS;wBAAE,IAAI,CAAC,CAAC,CAAC,CAAA;;wBAC1B,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;oBACpB,OAAM;gBACR,CAAC;gBACD,IAAI,MAAM,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;oBAC7B,gBAAgB,GAAG,MAAM,CAAC,EAAE,CAAA;oBAC5B,MAAM,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,CAAA;gBACzC,CAAC;qBAAM,CAAC;oBACN,oEAAoE;oBACpE,mEAAmE;oBACnE,sBAAsB;oBACtB,MAAM,SAAS,GAAG,MAAM,CAAC,SAAS,KAAK,EAAE;wBACvC,CAAC,CAAC,gBAAgB,UAAU,EAAE,EAAE;wBAChC,CAAC,CAAC,MAAM,CAAC,SAAS,CAAA;oBACpB,gBAAgB,GAAG,SAAS,CAAA;oBAC5B,MAAM,KAAK,CAAC,SAAS,CAAC,SAAS,CAAC,EAAE,KAAK,CAAC,CAAA;gBAC1C,CAAC;gBACD,OAAM;YACR,CAAC;YACD,MAAM,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,KAAK,IAAI,CAAC,CAAA;QAClE,CAAC,CAAA;QAED,KAAK,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;YACnC,MAAM,MAAM,GAAG,KAAK,YAAY,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,sBAAsB;gBAC5E,CAAC,CAAC,KAAK,CAAC,OAAO;gBACf,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,CAAA;YACrB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,yBAAyB,gBAAgB,uBAAuB,MAAM,IAAI,CAAC,CAAA;YAChG,MAAM,IAAI,GAAG,GAAG,CAAC,GAAG,CAAC,SAAS,CAAC,CAAA;YAC/B,IAAI,IAAI,KAAK,SAAS;gBAAE,IAAI,CAAC,CAAC,CAAC,CAAA;;gBAC1B,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;QACtB,CAAC,CAAC,CAAA;QAEF,OAAO,KAAK,IAAmB,EAAE;YAC/B,QAAQ,GAAG,IAAI,CAAA;YACf,WAAW,CAAC,KAAK,EAAE,CAAA;YACnB,MAAM,UAAU,EAAE,OAAO,EAAE,CAAA;YAC3B,MAAM,MAAM,EAAE,OAAO,EAAE,CAAA;YACvB,MAAM,eAAe,EAAE,CAAA;QACzB,CAAC,CAAA;IACH,CAAC,EAAE,SAAS,CAAC,CAAA;AACf,CAAC"}
@@ -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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"}