dsh-ssh-tui 0.7.0-rc.1 → 0.7.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/README.en.md +134 -64
- package/README.md +81 -75
- package/docs/remote-ops.md +154 -0
- package/docs/screenshots/compare.png +0 -0
- package/docs/screenshots/headless.png +0 -0
- package/docs/screenshots/workspace.png +0 -0
- package/lib/attach.js +66 -7
- package/lib/attach.js.map +1 -1
- package/lib/color-depth.js +226 -0
- package/lib/color-depth.js.map +1 -0
- package/lib/copy-text.js +1 -0
- package/lib/copy-text.js.map +1 -1
- package/lib/dialogs.js +73 -1
- package/lib/dialogs.js.map +1 -1
- package/lib/display-sock.js +1 -1
- package/lib/display-sock.js.map +1 -1
- package/lib/footer.js +199 -22
- package/lib/footer.js.map +1 -1
- package/lib/i18n/en.js +23 -0
- package/lib/i18n/en.js.map +1 -1
- package/lib/i18n/zh.js +24 -1
- package/lib/i18n/zh.js.map +1 -1
- package/lib/index.js +4 -1
- package/lib/index.js.map +1 -1
- package/lib/keymap.js +172 -0
- package/lib/keymap.js.map +1 -0
- package/lib/line-diff.js +224 -0
- package/lib/line-diff.js.map +1 -0
- package/lib/line-mode.js +86 -0
- package/lib/line-mode.js.map +1 -0
- package/lib/paint.js +17 -2
- package/lib/paint.js.map +1 -1
- package/lib/plan.js +51 -8
- package/lib/plan.js.map +1 -1
- package/lib/preset-picker.js +105 -0
- package/lib/preset-picker.js.map +1 -0
- package/lib/quota.js +23 -2
- package/lib/quota.js.map +1 -1
- package/lib/selection.js +154 -0
- package/lib/selection.js.map +1 -0
- package/lib/term-text.js +106 -1
- package/lib/term-text.js.map +1 -1
- package/lib/tool-present.js +157 -21
- package/lib/tool-present.js.map +1 -1
- package/lib/tui.js +825 -91
- package/lib/tui.js.map +1 -1
- package/lib/types/attach.d.ts +29 -0
- package/lib/types/color-depth.d.ts +50 -0
- package/lib/types/dialogs.d.ts +30 -0
- package/lib/types/footer.d.ts +102 -7
- package/lib/types/keymap.d.ts +68 -0
- package/lib/types/line-diff.d.ts +107 -0
- package/lib/types/line-mode.d.ts +36 -0
- package/lib/types/paint.d.ts +9 -1
- package/lib/types/plan.d.ts +7 -0
- package/lib/types/preset-picker.d.ts +64 -0
- package/lib/types/quota.d.ts +17 -0
- package/lib/types/selection.d.ts +81 -0
- package/lib/types/term-text.d.ts +29 -0
- package/lib/types/tool-present.d.ts +53 -3
- package/lib/types/transcript-types.d.ts +20 -2
- package/lib/types/tui.d.ts +157 -4
- package/package.json +2 -1
package/README.en.md
CHANGED
|
@@ -66,6 +66,16 @@ Reproducible, no model in the loop: `npm run screenshots:slow` writes
|
|
|
66
66
|
`docs/screenshots/slow-link.json`. This capture is 14 paints, about
|
|
67
67
|
**18.0 KB**, **8.8 s** at 2 kB/s. Byte ledger for this event sequence.
|
|
68
68
|
|
|
69
|
+
0.7 highlights: drag-select any part of a model reply to copy it (OSC 52 into your local
|
|
70
|
+
clipboard; a tool card still expands on click) · the footer is one priority-ordered chip
|
|
71
|
+
strip, and `⚠` opens `/doctor` · the quota bar is on screen from the first frame and names
|
|
72
|
+
its window (`5Hr`/`1Wk`/`1Mo`, smallest window by default, `?%` with a 15-second retry until
|
|
73
|
+
a reading arrives) · `/mode` groups presets and filters with `/` · the compact view names
|
|
74
|
+
every changed file · tool diffs are line-level, emphasise only what changed, and go
|
|
75
|
+
side-by-side at 100 columns or more · `DSH_TUI_LINE_MODE=1` appends plain lines for screen
|
|
76
|
+
readers and `tee` · `ssh-tui.keys` rebinds keys, refusing conflicts · `DSH_TUI_COLOR_DEPTH`
|
|
77
|
+
pins the palette (truecolor / 256 / 8 / none).
|
|
78
|
+
|
|
69
79
|
## Requirements
|
|
70
80
|
|
|
71
81
|
- Node.js >= 22.19
|
|
@@ -135,6 +145,7 @@ milliseconds; `0`/`off` restores the old behavior) and then exits, handing the
|
|
|
135
145
|
lock back — long enough for the old window to reattach, after which `--resume`
|
|
136
146
|
reopens the flushed log. A Host that never finishes its turn still falls back to
|
|
137
147
|
`DSH_TUI_DETACHED_IDLE_MS` (6h). Optional: wrap the TUI in tmux.
|
|
148
|
+
Copy-paste recipes for keeping a session reachable (tmux, screen, systemd --user, long turns) are in [`docs/remote-ops.md`](docs/remote-ops.md) (Chinese for now), together with what the `reconnected N times · away X` and `away …` transcript lines mean.
|
|
138
149
|
|
|
139
150
|
New sessions inherit the directory you launched from. Resuming a session
|
|
140
151
|
`chdir`s into that session's recorded working directory. The footer shows
|
|
@@ -285,68 +296,28 @@ You can reopen the wizard at any time with:
|
|
|
285
296
|
|
|
286
297
|
## Cross-platform support
|
|
287
298
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
and
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
(reasoning or tool-call fragments included) to the settled step, and rebuilt
|
|
311
|
-
when a session is replayed on `--resume` — from the durable `assistant/chunk`
|
|
312
|
-
events on 0.1.2 hosts, or from the packed stream inside `assistant/message`
|
|
313
|
-
on 0.1.5. Steps with no usable timing fall back to `首字 1.2s`.
|
|
314
|
-
- Emoji width: BMP symbols carrying the Unicode `Emoji` property are budgeted two cells. When the monospace font lacks the glyph, the terminal falls back to a colour emoji about 1.6 cells wide that still advances one cell — it covers its neighbours, and the second budgeted cell goes unspent so the row comes up short. `pinEmojiCells()` does both halves at paint time: VS15 asks for the narrow text form, and a reserving space spends the second cell (emitted unconditionally, so "two cells plus every space in the text" equals "one cell of glyph advance, one reserving space, and those same spaces" for every shape). Symbols that already default to emoji presentation (`✅` `⚡` …) and astral emoji (`😀` …) advance two cells and are left alone.
|
|
315
|
-
- Reconnect: channel readiness is a real connect, so a `.sock` file left behind by a killed Host is no longer listed as attachable (it used to fail the first attach with `write EPIPE`); a Host still cancelling/flushing keeps itself alive when a relay HELLOs mid-hangup; a launcher that hits a vanished peer retries once automatically — silently, with the TTY kept in raw mode and queued bytes dropped so a stale cursor reply is never echoed as `^[[17;1R`; and a probe that misses its window is retried while the Host keeps the last measurement, so the footer chip does not fall back to four hollow circles.
|
|
316
|
-
- Reporting a problem: `/diag` prints a local, read-only snapshot — plugin/dsh/node
|
|
317
|
-
versions, platform, session id, whether this process is the launcher or the detached
|
|
318
|
-
Host, `DSH_HOME`, the display channel address and whether it answers (including the
|
|
319
|
-
leftover-socket-file verdict), the Host pid/identity/lock state, the lock file path, link
|
|
320
|
-
RTT and paint interval, the session log format and size, other locks on the machine, and
|
|
321
|
-
a **verdict chain** ("attaches to the leftover Host (pid N), do not open a second
|
|
322
|
-
window", "the pid was recycled", "a leftover socket file that does not answer — the
|
|
323
|
-
source of the first-attach EPIPE"). Nothing leaves the machine; paste it into an issue.
|
|
324
|
-
- When `/mode` reports a missing service or the preset tools vanish after an upgrade
|
|
325
|
-
(the 0.6.3 class of configuration regression), run `/doctor`. It judges the
|
|
326
|
-
**deployment composition** item by item: whether the profile patch parses, whether the
|
|
327
|
-
agent-presets roster and code-runtime are composed (naming the missing row), whether the
|
|
328
|
-
subagent model-selection settings row is mounted, whether any row is mounted twice (with
|
|
329
|
-
its line), whether the profile layer and the bundle layer both mount a row, whether the
|
|
330
|
-
host version is in the plugin's declared compatibility table, whether two
|
|
331
|
-
`@deepseek-ai/dsh-scope` installs exist (the nested-install copy trap), and whether the
|
|
332
|
-
default route and the subagent model are self-consistent. Every item carries its evidence
|
|
333
|
-
and the command that acts on it. `/doctor --fix` mounts the missing rows and merges
|
|
334
|
-
duplicates into `cordis.patch.yml`, keeping a `.bak-<timestamp>` beside it and never
|
|
335
|
-
touching an override or disable you wrote; `/fix code-runtime` repairs one row. A restart
|
|
336
|
-
picks it up, and like `/diag` nothing leaves the machine.
|
|
337
|
-
- Leftover Host lifetime: the write lock a dropped-but-still-running Host holds is what makes the Web UI refuse the same session (`resume failed for session … is already owned by an active write handle`), and a drop that raced a reattach used to leave the honored-reattach flag set, so the *next* drop was ignored and the session lock kept its stale state. The flag is cleared once a turn runs again, and once the turn the Host stayed for settles it exits within `DSH_TUI_IDLE_EXIT_MS` (default 60s, `0` disables) instead of holding the session for six hours.
|
|
338
|
-
- Subagent route: the identity row always carries `sub:<model>` — the route
|
|
339
|
-
every child inherits — suffixed with the effort when `/subeffort` set one,
|
|
340
|
-
and prefixed with the provider when `settings.yaml` pinned one
|
|
341
|
-
(`ssh-tui-subagent.provider`), e.g. `sub:xai/grok-4.5(xhigh)`.
|
|
342
|
-
- Resize: the launcher picker unregisters its own `resize` listener when it
|
|
343
|
-
settles and refuses to paint afterwards, so resizing the window can no longer
|
|
344
|
-
redraw the finished picker over the running TUI (the launcher process keeps
|
|
345
|
-
owning the TTY as the display relay for the whole session).
|
|
346
|
-
- Legacy Windows consoles without VT support: set `DSH_TUI_NO_ALT_SCREEN=1`
|
|
347
|
-
(and `--no-color` if needed) to skip the alternate-screen escape sequences.
|
|
348
|
-
- Keyboard input accepts both `\x7f` and `\x08` backspace, and both `\r` /
|
|
349
|
-
`\r\n` line endings.
|
|
299
|
+
- **Windows**: the display channel is a named pipe
|
|
300
|
+
(`\\.\pipe\dsh-tui-<8-hex DSH_HOME>-<session name>-<8-hex session id>`) — the only
|
|
301
|
+
local socket Windows can listen on — and it is reclaimed when the Host exits. Readiness
|
|
302
|
+
is probed with a real connect, and a Host that exits early is reported at once with its
|
|
303
|
+
stderr (`%USERPROFILE%\.dsh\tui-socks\<session>.err`). Session locks check a live pid
|
|
304
|
+
with `Get-Process` (image name plus creation time), so a recycled pid is recognised as
|
|
305
|
+
stale instead of reported as a phantom zombie Host.
|
|
306
|
+
- **Legacy Windows consoles without VT support**: set `DSH_TUI_NO_ALT_SCREEN=1` (and
|
|
307
|
+
`--no-color` if needed) to skip the alternate-screen escape sequences.
|
|
308
|
+
- **Footer speed** (`135 tok/s`) is measured from the first token the model emits to the
|
|
309
|
+
settled step, and is rebuilt when a session is replayed with `--resume`. A step with no
|
|
310
|
+
usable timing shows `首字 1.2s` instead.
|
|
311
|
+
- **Emoji / CJK width**: BMP symbols carrying the Unicode `Emoji` property are budgeted two
|
|
312
|
+
cells, and the painter asks for the narrow text form (VS15) plus a reserving space. A
|
|
313
|
+
monospace font that lacks the glyph still makes the terminal fall back to a wider colour
|
|
314
|
+
emoji, so pick a font that covers what you use (Noto Sans Mono CJK, for instance).
|
|
315
|
+
- **Keyboard input** accepts both `\x7f` and `\x08` backspace, and both `\r` / `\r\n`
|
|
316
|
+
line endings.
|
|
317
|
+
- **Subagent route**: the identity row always carries `sub:<model>` — the route every child
|
|
318
|
+
inherits — with the effort in parentheses when `/subeffort` set one, e.g.
|
|
319
|
+
`sub:grok-4.5(xhigh)`. Only the model name is shown; the provider and the full route are
|
|
320
|
+
in `/status`.
|
|
350
321
|
|
|
351
322
|
## Usage
|
|
352
323
|
|
|
@@ -506,8 +477,8 @@ completion (`DSH_TUI_NO_BELL=1` disables it).
|
|
|
506
477
|
|
|
507
478
|
Tool calls render as compact cards instead of raw argument JSON. The title
|
|
508
479
|
stays the default foreground; the status dot is yellow / green / red for
|
|
509
|
-
running / ok / error.
|
|
510
|
-
|
|
480
|
+
running / ok / error. A successful call shows the dot alone — no `[ok]`
|
|
481
|
+
suffix — while a failure still carries `[error]` and an in-flight call
|
|
511
482
|
`[running…]` (plus `[退出码 N]` / `[信号 X]` when a shell exits). Consecutive
|
|
512
483
|
reads or edits of the same path fold into one card (`×N`, cumulative
|
|
513
484
|
chars/lines, appended diffs, a brief flip animation).
|
|
@@ -597,6 +568,105 @@ does not write into the transcript.
|
|
|
597
568
|
Idle hangup exits the Host. A busy turn keeps it; `--resume` attaches to that
|
|
598
569
|
process. Do not start a second Host.
|
|
599
570
|
|
|
571
|
+
## Troubleshooting (Q&A)
|
|
572
|
+
|
|
573
|
+
Find your symptom; each answer is what to do, not a change log.
|
|
574
|
+
|
|
575
|
+
### Start-up and install
|
|
576
|
+
|
|
577
|
+
- **`dsh-ssh-tui: both stdin and stdout must be TTYs`** — start it from a real terminal or
|
|
578
|
+
SSH session; a pipe, CI, or `&` background job will not do.
|
|
579
|
+
- **Windows: `host display socket did not appear`** — upgrade
|
|
580
|
+
(`dsh plugin --profile tui add dsh-ssh-tui@latest`); older builds waited 15 seconds and
|
|
581
|
+
timed out on the named pipe. If it still fails, attach `/diag` to an issue.
|
|
582
|
+
- **pnpm refuses to run the build script of a git dependency** — add the key pnpm prints to
|
|
583
|
+
`allowBuilds` in the profile's `pnpm-workspace.yaml`, then reinstall.
|
|
584
|
+
- **After an upgrade `/mode` reports a missing service, or the preset tools vanish** — run
|
|
585
|
+
`/doctor`: it judges the deployment composition item by item (patch parses, roster and
|
|
586
|
+
code-runtime composed, no row mounted twice, host version inside the compatibility table,
|
|
587
|
+
two `@deepseek-ai/dsh-scope` installs) and gives a verdict, its evidence and the command
|
|
588
|
+
that acts on it. `/doctor --fix` writes the missing rows into `cordis.patch.yml`, keeping
|
|
589
|
+
a `.bak-<timestamp>`; a restart picks it up.
|
|
590
|
+
- **"session is already running on pid N / attachable"** — that Host is alive: attach with
|
|
591
|
+
`dsh --profile tui --resume`. Do not open a second window; clear `$DSH_HOME/tui-locks/`
|
|
592
|
+
only once the pid is really gone.
|
|
593
|
+
|
|
594
|
+
### Sessions and locks
|
|
595
|
+
|
|
596
|
+
- **The Web UI refuses a session (`already owned by an active write handle`)** — the Host
|
|
597
|
+
left over from an SSH drop still holds the write lock; it exits within a minute of the
|
|
598
|
+
turn settling (`DSH_TUI_IDLE_EXIT_MS` / `ssh-tui.idleExit`). You can also attach to it
|
|
599
|
+
with `--resume` and keep working.
|
|
600
|
+
- **The first `--resume` fails with `write EPIPE`, the second works** — the launcher waits
|
|
601
|
+
and retries once by itself. If it keeps failing, `/diag` prints the channel and lock
|
|
602
|
+
verdict chain, including the leftover-socket-file case.
|
|
603
|
+
- **Too many history sessions to tell apart** — filter the `--resume` picker by title,
|
|
604
|
+
session id or working directory (`/` or `Ctrl+F`); older history is read on demand when
|
|
605
|
+
you filter or page to the end, and the counter shows how many are still unloaded.
|
|
606
|
+
|
|
607
|
+
### Display and terminal
|
|
608
|
+
|
|
609
|
+
- **Colours look wrong, or a diff is one solid block you cannot read** — pin the palette
|
|
610
|
+
with `DSH_TUI_COLOR_DEPTH=truecolor|256|8|none`. At `256` a diff is dark grey with green
|
|
611
|
+
or red text; at `none` there is no colour at all, but `+`/`-`, `●`, `⚠` and `✖` remain —
|
|
612
|
+
status is never carried by colour alone.
|
|
613
|
+
- **CJK or emoji crowd the characters next to them** — use a monospace font that covers
|
|
614
|
+
them (Noto Sans Mono CJK, for instance). Those symbols are budgeted two cells and the
|
|
615
|
+
painter asks for the narrow text form; a font without the glyph still makes the terminal
|
|
616
|
+
fall back to a wider colour emoji.
|
|
617
|
+
- **The screen cannot keep up on a slow link** — `DSH_TUI_PAINT_MS` sets the paint interval
|
|
618
|
+
(40–1000 ms: smaller is snappier and sends more); unset, it follows the round-trip
|
|
619
|
+
measured at start-up (80 / 160 / 250 / 400 ms).
|
|
620
|
+
- **Screen reader, or you want a log** — start with `DSH_TUI_LINE_MODE=1`: plain appended
|
|
621
|
+
lines, no cursor control, safe to `tee`.
|
|
622
|
+
- **Title bar or bell does nothing** — the terminal needs OSC 0 and BEL; `DSH_TUI_NO_BELL=1`
|
|
623
|
+
turns the bell off.
|
|
624
|
+
- **Whole-row backgrounds are too loud on a dark terminal** — `DSH_TUI_COLOR_DEPTH=none`
|
|
625
|
+
drops them; diffs still read through `+`/`-`.
|
|
626
|
+
|
|
627
|
+
### Status line and quota
|
|
628
|
+
|
|
629
|
+
- **The quota widget shows `░░░░░░░░ ?%`** — no reading has arrived yet (the API is slow or
|
|
630
|
+
unreachable). It is not `0%`. The TUI asks again every 15 seconds and replaces it with the
|
|
631
|
+
real number and window; if it never does, `/quota` reports the error.
|
|
632
|
+
- **There is no quota widget** — only SuperGrok, OpenCode Go and Command Code report quota;
|
|
633
|
+
DeepSeek shows a balance line and a metered Zen route shows none.
|
|
634
|
+
- **What do `5Hr` / `1Wk` / `1Mo` mean?** — the window the number belongs to. The smallest
|
|
635
|
+
window is shown by default (5-hour → weekly → monthly); `/quota` lists every window with
|
|
636
|
+
its remaining share and reset time. Threshold alerts still fire on the tightest window.
|
|
637
|
+
- **The model name has no provider prefix** — the status line shows the model alone
|
|
638
|
+
(`provider/model` is truncated to the model); the full route is in the header and
|
|
639
|
+
`/status`, and a `sub:` chip shows the child's model the same way.
|
|
640
|
+
- **The subagent model is not what you want** — `/submodel` picks the model, `/subeffort`
|
|
641
|
+
the reasoning effort, `/status` shows the current pair.
|
|
642
|
+
- **No `tok/s`** — that turn had no measurable model tokens; a step with only a first-token
|
|
643
|
+
time shows `首字 1.2s`.
|
|
644
|
+
- **The plan strip asked for leftover todos only once** — that is deliberate: one reminder
|
|
645
|
+
per open list, so a turn end never spawns another turn forever. To see it again, complete
|
|
646
|
+
the list and start a new one.
|
|
647
|
+
|
|
648
|
+
### Drops and proxies
|
|
649
|
+
|
|
650
|
+
- **What happens when SSH drops** — idle: flush and exit. Busy (thinking, replying, a tool,
|
|
651
|
+
a subagent): the turn is cancelled by default and the Host stays, so
|
|
652
|
+
`dsh --profile tui --resume` attaches to it; `/disconnect continue` lets it run to
|
|
653
|
+
completion instead. Do not start a second Host.
|
|
654
|
+
- **After reconnecting there is an extra notice line, `^[[17;1R` flashes, and the link chip
|
|
655
|
+
goes hollow** — upgrade; the automatic retry stays in raw mode and drops queued bytes, so
|
|
656
|
+
those replies are no longer echoed.
|
|
657
|
+
- **Model requests must go through a proxy** — start dsh with `--use-env-proxy` (not via
|
|
658
|
+
`NODE_OPTIONS`) and split by domain with `NO_PROXY`, keeping domestic hosts that are faster
|
|
659
|
+
direct and every local/intranet address in the list. The recipe and its revert are in
|
|
660
|
+
[docs/remote-ops.md §4.7](docs/remote-ops.md).
|
|
661
|
+
|
|
662
|
+
### Asking for help
|
|
663
|
+
|
|
664
|
+
- **You need a readable report for a supporter** — type **`/diag`** in the session (local and
|
|
665
|
+
read-only): versions, platform, session id, `DSH_HOME`, the display channel and whether it
|
|
666
|
+
answers, the Host pid and lock state, link RTT, log format and size, and a verdict chain
|
|
667
|
+
("attaches to the leftover Host (pid N), do not open a second window"). Deployment
|
|
668
|
+
questions go to **`/doctor`**. Paste both into an issue.
|
|
669
|
+
|
|
600
670
|
## Development
|
|
601
671
|
|
|
602
672
|
```text
|
package/README.md
CHANGED
|
@@ -67,8 +67,8 @@ dsh --profile tui
|
|
|
67
67
|
- 转录区滚动回看(`PgUp`/`PgDn`、鼠标滚轮),点击思考/工具标题行直接展开收起;
|
|
68
68
|
- 输入框下方两行底栏:第一行链路芯片 + 按宽度丢组的会话数字(轮次、入/出 token、速度);
|
|
69
69
|
第二行只留一个活动词(运行中 / 工具 N / 子代理 N / 压缩中…),身份收到右侧(含 `目录:srv`;点击打印完整工作目录);
|
|
70
|
-
身份里始终带 `sub
|
|
71
|
-
|
|
70
|
+
身份里始终带 `sub:<子代理模型>`(如 `sub:grok-4.5(xhigh)`):`/submodel` 选模型、`/subeffort` 选档位(带括号后缀);
|
|
71
|
+
只显示模型名——提供商与完整路由在顶栏与 `/status`;
|
|
72
72
|
- 恢复旧会话会切到该会话记录的工作目录;新建会话用启动时的当前目录;
|
|
73
73
|
- 历史会话启动选择器:`dsh --profile tui --resume`(或 `resume`)先选会话再进入;
|
|
74
74
|
- 终端窗口标题栏:运行中旋转图标 + `运行中 · 工具 N`,完成后 `✓ 已完成`,并响
|
|
@@ -87,6 +87,13 @@ dsh --profile tui
|
|
|
87
87
|
标题(还没出现就保持「处理中」),运行中的工具摘要在 `└` 下自动折行(最多 3 行,末行
|
|
88
88
|
加省略号),带计时和 Esc 中断;回复开始流式输出时自动让位。
|
|
89
89
|
|
|
90
|
+
- 0.7 起:模型回复可**拖选自由复制**(按住拖过一段,走 OSC 52 写回本机剪贴板;工具卡仍是点击展开);
|
|
91
|
+
底栏收敛成一条带优先级的芯片带(先丢文字后丢组,`⚠` 可点击打开 `/doctor`);**额度条常驻**并标注窗口
|
|
92
|
+
(`5Hr`/`1Wk`/`1Mo`,默认显示最小窗口,未取到时显示 `?%` 并每 15 秒重试);`/mode` 分组显示并可用 `/` 过滤;
|
|
93
|
+
极简视图逐文件列 `+/-`;工具 diff 为**行级**、只高亮变化字符、≥100 列时并排显示;
|
|
94
|
+
`DSH_TUI_LINE_MODE=1` 纯行模式(屏幕阅读器 / `tee`);`ssh-tui.keys` 可改键位(冲突会明确拒绝);
|
|
95
|
+
`DSH_TUI_COLOR_DEPTH` 指定色深(truecolor / 256 / 8 / none)。
|
|
96
|
+
|
|
90
97
|
## 环境要求
|
|
91
98
|
|
|
92
99
|
- Node.js ≥ 22.19
|
|
@@ -160,8 +167,14 @@ Host 在后台跑完这一轮;审批和提问等接上后再弹。空闲断线
|
|
|
160
167
|
留下的 Host 持有该会话的内核写锁(`session.lock`),而 Web 端打开同一会话时正是被这把锁挡下的
|
|
161
168
|
(`resume failed for session … is already owned by an active write handle`)。所以它**跑完留下来的那一轮后最多再等 1 分钟**
|
|
162
169
|
(`DSH_TUI_IDLE_EXIT_MS`,或 settings.yaml 的 `ssh-tui.idleExit`,毫秒;设 `0`/`off` 恢复旧行为)
|
|
170
|
+
|
|
171
|
+
按键可改:`ssh-tui.keys`(动作 `pageUp` / `pageDown` / `toggleCard` / `copy` / `cancel`,如 `keys: { pageUp: ctrl+b }`)。
|
|
172
|
+
冲突或未知的名字**不会静默生效**:启动时提示,且该键保持默认或变成无操作。
|
|
173
|
+
纯行模式:`DSH_TUI_LINE_MODE=1`(或 `ssh-tui.lineMode: true`)——不画帧,逐事件追加纯文本行,
|
|
174
|
+
适合屏幕阅读器、`tee` 与录屏;代价是全屏交互(鼠标拖选、卡片展开、`/find` 高亮)不可用。
|
|
163
175
|
就自行退出并让出锁:这段时间够原窗口重连接入,之后 `--resume` 重新打开已落盘的日志。
|
|
164
176
|
完全没有显示器且一直空闲的兜底仍由 `DSH_TUI_DETACHED_IDLE_MS`(默认 6 小时)负责。可选:用 tmux 包一层。
|
|
177
|
+
常驻与接管的可复制配方(tmux / screen / systemd --user / 长任务)见 [`docs/remote-ops.md`](docs/remote-ops.md);重连后转录里的「已重连 N 次 · 断开 Xs」与「离开 …」两行的语义也在那里。
|
|
165
178
|
|
|
166
179
|
启动时若 npm 上有更新,会弹出选单(类似 Codex / Claude Code 首启):**现在更新 / 稍后 / 跳过此版本**。选「现在更新」会运行 `dsh plugin --profile tui add dsh-ssh-tui@latest`,完成后提示退出再启动。`DSH_TUI_NO_UPDATE_CHECK=1` 可关掉。`/status` 里也能看到当前插件版本、链路芯片、额度窗口,以及子代理模型是否与父路由同族。
|
|
167
180
|
|
|
@@ -500,79 +513,72 @@ npm run typecheck
|
|
|
500
513
|
npm run build
|
|
501
514
|
```
|
|
502
515
|
|
|
503
|
-
##
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
- **pnpm 拒绝 git
|
|
513
|
-
|
|
514
|
-
-
|
|
515
|
-
`
|
|
516
|
-
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
并找回提供商前缀与 `/subeffort` 的括号后缀(提供商要先在 `settings.yaml` 的 `ssh-tui-subagent.provider`
|
|
570
|
-
里固定过)。查看或修改:`/status`、`/submodel`、`/subeffort`。
|
|
571
|
-
- **缩放终端窗口时整屏闪烁、还闪出历史会话选择器**:启动选择器注册 `resize` 监听时用的是匿名函数,
|
|
572
|
-
退出时却按 `render` 这个引用去注销,于是监听器一直留在中继进程里(中继要活整个会话)。之后每次
|
|
573
|
-
缩放终端,那张已经结束的选择器都会被整屏重画一遍(`\x1b[H\x1b[J` + 整屏内容),紧接着再被 TUI
|
|
574
|
-
的整屏重画覆盖——看起来就是闪两下并闪出选择器内容。0.5.8 起改用命名监听器注销,并给已结束的
|
|
575
|
-
选择器加 `done` 守卫:结算后再触发 `resize` 一个字节都不会写(有回归测试)。
|
|
516
|
+
## 常见问题(QA)
|
|
517
|
+
|
|
518
|
+
按现象查;每条只讲怎么办,不讲版本历史。
|
|
519
|
+
|
|
520
|
+
### 启动与安装
|
|
521
|
+
|
|
522
|
+
- **`dsh-ssh-tui: both stdin and stdout must be TTYs`**:必须在真实终端 / SSH 会话里启动;管道、CI、`&` 后台都不行。
|
|
523
|
+
- **Windows 报 `host display socket did not appear`**:升级到最新版:
|
|
524
|
+
`dsh plugin --profile tui add dsh-ssh-tui@latest`。仍失败请附 `/diag` 输出提 issue。
|
|
525
|
+
- **pnpm 拒绝 git 依赖的构建脚本**:把 pnpm 打印的 key 加进 profile 的 `pnpm-workspace.yaml` 的 `allowBuilds`,再重装。
|
|
526
|
+
- **升级后 `/mode` 报「服务不可用」、preset 工具消失**:敲 `/doctor`。它逐项判定部署组合(补丁能否解析、
|
|
527
|
+
名单与 code-runtime 是否组合、有没有行被挂载两次、dsh 版本是否在兼容表内、是否装着两份 `@deepseek-ai/dsh-scope`),
|
|
528
|
+
每项给「结论 + 证据 + 修复命令」;`/doctor --fix` 会补齐 patch、写前留 `.bak-<时间戳>` 备份,重启 TUI 生效。
|
|
529
|
+
- **提示「会话已在 pid 运行 / 可接入」**:那个 Host 还活着,用 `dsh --profile tui --resume` 接入。
|
|
530
|
+
**不要**再开第二个窗口;只有 pid 确实已死时才清 `$DSH_HOME/tui-locks/` 再从日志恢复。
|
|
531
|
+
|
|
532
|
+
### 会话与锁
|
|
533
|
+
|
|
534
|
+
- **Web 端打不开同一会话(`already owned by an active write handle`)**:SSH 断开时仍在跑轮的 Host 会持有写锁,
|
|
535
|
+
轮次结束后最多 1 分钟自动退出并放锁(`DSH_TUI_IDLE_EXIT_MS` / `ssh-tui.idleExit` 可调);也可以直接 `--resume` 接入它继续。
|
|
536
|
+
- **`--resume` 第一次报 `write EPIPE`、第二次才接上**:launcher 会自动等待并重试一次。若持续失败,
|
|
537
|
+
用 `/diag` 看显示通道与锁的判定链(含"残留 socket 文件"这一常见来源)。
|
|
538
|
+
- **历史会话太多、认不出哪条是哪个**:在 `--resume` 选择器里按标题 / session id / 工作目录筛选(`/` 或 `Ctrl+F`),
|
|
539
|
+
更早的历史会在筛选或翻到末尾时按需读取;计数行会显示还有多少条未加载。
|
|
540
|
+
|
|
541
|
+
### 显示与终端
|
|
542
|
+
|
|
543
|
+
- **颜色不对,或 diff 整块一个颜色、看不清字**:显式指定色深 `DSH_TUI_COLOR_DEPTH=truecolor|256|8|none`。
|
|
544
|
+
`256` 下 diff 是深灰底 + 绿/红字;`none` 完全没有颜色,但 `+`/`-`、`●`、`⚠`、`✖` 仍在,状态不只靠颜色表达。
|
|
545
|
+
- **中文 / emoji 挤压相邻字符**:换一款覆盖这些字形的等宽字体(如 Noto Sans Mono CJK)。程序按 2 格预算这些符号
|
|
546
|
+
并请求文本字形;字体缺字时终端会回退到彩色 emoji 字形,视觉上仍可能偏宽。
|
|
547
|
+
- **弱网下画面跟不上**:`DSH_TUI_PAINT_MS` 控制发画间隔(40–1000 ms,越小越快也越费流量);
|
|
548
|
+
不设时按启动测得的 RTT 自动取 80 / 160 / 250 / 400 ms。
|
|
549
|
+
- **要给屏幕阅读器或日志用**:`DSH_TUI_LINE_MODE=1` 启动纯行模式——只追加纯文本行、不发光标控制,可直接 `tee` 存档。
|
|
550
|
+
- **标题栏 / 铃声不生效**:终端需支持 OSC 0 与 BEL;`DSH_TUI_NO_BELL=1` 可关闭铃声。
|
|
551
|
+
- **深色终端下整行底色太抢眼**:`DSH_TUI_COLOR_DEPTH=none` 去掉底色,diff 仍用 `+`/`-` 区分。
|
|
552
|
+
|
|
553
|
+
### 状态栏与额度
|
|
554
|
+
|
|
555
|
+
- **额度条显示 `░░░░░░░░ ?%`**:**还没拿到读数**(接口慢或不通),不是 0%。TUI 每 15 秒重试一次,拿到后自动替换成
|
|
556
|
+
真实数值与窗口;一直不变成数值时用 `/quota` 看具体报错。
|
|
557
|
+
- **没有额度条**:只有 SuperGrok / OpenCode Go / Command Code 有额度;DeepSeek 显示的是余额行,Zen 是计量制。
|
|
558
|
+
- **`5Hr` / `1Wk` / `1Mo` 是什么**:该数值所属的额度窗口。默认显示**最小窗口**(5 小时 → 周 → 月),
|
|
559
|
+
`/quota` 列出全部窗口、剩余比例与重置时间。告警仍按"最紧的那个窗口"触发。
|
|
560
|
+
- **状态栏模型名没有提供商前缀**:状态行只显示模型名(`provider/model` 会截断成模型名),完整路由在顶栏与 `/status`;
|
|
561
|
+
`sub:` 子代理同理,只显示模型名。
|
|
562
|
+
- **看不到子代理模型 / 想换子代理**:`/submodel` 选模型、`/subeffort` 选思考档位,`/status` 查看当前值。
|
|
563
|
+
- **没有 `tok/s`**:该轮没有可统计的模型 token;只有首字耗时时显示 `首字 1.2s`。
|
|
564
|
+
- **计划条只提醒一次"补待办"**:有意如此——一条待办列表只问一次,避免每轮结束都开一个新回合。
|
|
565
|
+
想再次触发,先把列表全部标成完成,再新开一列待办。
|
|
566
|
+
|
|
567
|
+
### 断线与代理
|
|
568
|
+
|
|
569
|
+
- **SSH 断了会怎样**:空闲则落盘后退出;忙碌(思考 / 回复 / 工具 / 子代理)默认取消当轮、保留 Host,
|
|
570
|
+
回来 `dsh --profile tui --resume` 接入;`/disconnect continue` 则让它在后台跑完当前轮。**不要**再开第二个 Host。
|
|
571
|
+
- **重连后多出一行提示、闪出 `^[[17;1R` 之类的字符、链路芯片变空心**:升级到最新版;自动重试期间保持 raw 模式
|
|
572
|
+
并丢弃排队字节,这些字符不会再被回显。
|
|
573
|
+
- **模型请求要走公司 / 本机代理**:给 dsh 进程加 `--use-env-proxy`(不要用 `NODE_OPTIONS`),并用 `NO_PROXY`
|
|
574
|
+
按域名分流——国内直连更快的域名放进去,本地 / 内网地址务必保留在列表里。做法与回退见
|
|
575
|
+
[docs/remote-ops.md §4.7](docs/remote-ops.md)。
|
|
576
|
+
|
|
577
|
+
### 排障入口
|
|
578
|
+
|
|
579
|
+
- **要给支持者一份可读的排障信息**:在会话里敲 **`/diag`**(只读本地信息、不外传):版本、平台、会话 id、
|
|
580
|
+
`DSH_HOME`、显示通道与可连接性、Host 的 pid / 锁状态、链路 RTT、日志格式与大小,最后是**判定链**
|
|
581
|
+
(例如"会接入后台 Host(pid N),不要另起第二个窗口")。部署组合问题用 **`/doctor`**。报 issue 时贴这两段即可。
|
|
576
582
|
|
|
577
583
|
## License
|
|
578
584
|
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# 远程运维手册:断线、接管与常驻
|
|
2
|
+
|
|
3
|
+
这份文档回答一个问题:**在一条会断的 SSH 链路上,怎么让会话按你的预期活下来、又怎么把它接回来。**
|
|
4
|
+
它只描述当前实现的行为与可复制的配方;机制本身写在 README 的排障一节。
|
|
5
|
+
|
|
6
|
+
> 语言:本手册目前只有中文。README 的中英两版都指向这里,若要英文版请提 issue。
|
|
7
|
+
|
|
8
|
+
## 1. 断线时会发生什么
|
|
9
|
+
|
|
10
|
+
| 掉线时的状态 | 行为 |
|
|
11
|
+
|---|---|
|
|
12
|
+
| **空闲**(没有轮次在跑) | launcher 交还终端并退出,会话日志已落盘。回来 `--resume` 从日志恢复,**没有 Host 在等你** |
|
|
13
|
+
| **忙碌**(思考 / 回复 / 工具 / 子代理在跑),默认策略 `pause` | 取消当前轮次、flush 日志,**Host 留下**并持有会话写锁,等你回来接入 |
|
|
14
|
+
| **忙碌**,`/disconnect continue` | 不取消,Host 在后台把这一轮跑完;你回来时结果已经在转录里(可能还带着子代理的进度) |
|
|
15
|
+
| 你开了第二个窗口去接同一个会话 | 新窗口接管显示,旧窗口退出(`replaced`)。**不要为同一个会话起第二份 Host** |
|
|
16
|
+
|
|
17
|
+
会话写锁是内核锁:同一时刻只有一个写者。这是为什么"再开一个窗口"和"再起一份 Host"是两件不同的事——前者是接管,后者会失败(`already owned by an active write handle`)。
|
|
18
|
+
|
|
19
|
+
## 2. 三个时间旋钮
|
|
20
|
+
|
|
21
|
+
| 旋钮 | 默认 | 作用 |
|
|
22
|
+
|---|---|---|
|
|
23
|
+
| `DSH_TUI_IDLE_EXIT_MS`(或 settings.yaml 的 `ssh-tui.idleExit`) | `60000`(60s) | 忙碌掉线留下的 Host,在**轮次结束后**再等多久无人接入就自行退出并让出锁。`0`/`off` 恢复旧的"长时间保留"行为 |
|
|
24
|
+
| `DSH_TUI_DETACHED_IDLE_MS` | `21600000`(6h) | 完全没有显示器、且一直空闲的兜底上限;轮次在跑时会自动续期 |
|
|
25
|
+
| `/disconnect pause\|continue` | `pause` | 掉线时是否取消当前轮次(见上表) |
|
|
26
|
+
|
|
27
|
+
**推荐**:留在默认。60s 足够你重连;把它调大只在一种场景下有意义——你确定自己会离开很久、且希望**轮次继续跑完**,那就配 `/disconnect continue`,而不是无限期留住锁。
|
|
28
|
+
|
|
29
|
+
`DSH_TUI_NO_SESSION_LOCK=1` 会关掉锁,仅用于调试:两个 Host 同时写一个会话日志会互相覆盖。
|
|
30
|
+
|
|
31
|
+
## 3. 回来后你会看到什么
|
|
32
|
+
|
|
33
|
+
重连成功时,转录里会多出两行(都是 Host 侧写的,launcher 写不了——屏幕由 TUI 拥有并会立刻重绘):
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
已重连 1 次 · 断开 1m
|
|
37
|
+
离开 1m:自动审批 1 次放行 / 1 次拒绝 · 其中 1 次因无人确认被拒 · 1 条提问待回答
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
- **计数按 Host 计**:新起的 Host 从 1 开始,它确实不知道更早的 Host。
|
|
41
|
+
- **摘要是增量**:只统计这次离开期间发生的事,不重复会话的总量。
|
|
42
|
+
- 只有真发生过的部分才会出现;链路只是抖一下,就只有第一行。
|
|
43
|
+
- 断线期间到达的**未识别审批会被拒绝**并把理由写进转录(模型据此调整)。这是有意的:挂起的轮次会撞上 idle-exit 兜底,最后既没跑完也没人确认。
|
|
44
|
+
|
|
45
|
+
## 4. 配方
|
|
46
|
+
|
|
47
|
+
### 4.1 tmux(最省事)
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
tmux new -As dsh # 有就接入,没有就新建
|
|
51
|
+
# 会话里:
|
|
52
|
+
dsh --profile tui --resume # 选择器;活着的 Host 优先接入
|
|
53
|
+
# 断开终端:Ctrl-b d 下次 SSH 回来:tmux new -As dsh
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
tmux 保证的是**终端会话**不断;dsh 保证的是**会话日志与(忙碌时的)Host**不断。两者叠加,链路抖动就不再等于丢进度。
|
|
57
|
+
|
|
58
|
+
### 4.2 screen
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
screen -S dsh
|
|
62
|
+
dsh --profile tui --resume
|
|
63
|
+
# Ctrl-a d 断开;回来 screen -r dsh
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### 4.3 systemd --user:登录即有 tmux
|
|
67
|
+
|
|
68
|
+
`~/.config/systemd/user/dsh-tui.service`:
|
|
69
|
+
|
|
70
|
+
```ini
|
|
71
|
+
[Unit]
|
|
72
|
+
Description=Persistent tmux session for the dsh TUI
|
|
73
|
+
After=default.target
|
|
74
|
+
|
|
75
|
+
[Service]
|
|
76
|
+
Type=forking
|
|
77
|
+
Environment=DSH_HOME=%h/.dsh
|
|
78
|
+
ExecStart=/usr/bin/tmux new-session -d -s dsh 'dsh --profile tui --resume'
|
|
79
|
+
ExecStop=/usr/bin/tmux kill-session -t dsh
|
|
80
|
+
Restart=no
|
|
81
|
+
|
|
82
|
+
[Install]
|
|
83
|
+
WantedBy=default.target
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
systemctl --user daemon-reload
|
|
88
|
+
systemctl --user enable --now dsh-tui.service
|
|
89
|
+
loginctl enable-linger "$USER" # 未登录时也保留(按需)
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
注意:unit 只是把 tmux 会话带起来,**dsh 的断线语义不变**——空闲掉线仍然会退出 Host,`--resume` 从日志恢复。不要把 `Restart=always` 配上:那会在你退出 `/exit` 后不断重开一个会话。
|
|
93
|
+
|
|
94
|
+
### 4.4 长任务跨休眠 / 跨通勤
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
/disconnect continue # 掉线不取消当前轮次
|
|
98
|
+
DSH_TUI_IDLE_EXIT_MS=600000 dsh --profile tui --resume
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
轮次在跑时 `DSH_TUI_DETACHED_IDLE_MS` 会自动续期,空闲后按 `DSH_TUI_IDLE_EXIT_MS` 退出并让锁——把上限设大是为了"回来还能接上",不是为了让 Host 常驻。
|
|
102
|
+
|
|
103
|
+
### 4.5 一次性任务:官方 headless
|
|
104
|
+
|
|
105
|
+
不需要交互、只要最后一条回复时,用官方 `dsh --profile headless "…"`:跑完把最终文本打到 stdout 就退出。思考、工具调用、子代理都在会话日志里,终端上看不到——这正是本 TUI 存在的理由(同一条任务的对照图见 README)。
|
|
106
|
+
|
|
107
|
+
### 4.6 纯行模式(屏幕阅读器与日志录制)
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
DSH_TUI_LINE_MODE=1 dsh --profile tui --resume # 每个事件追加若干纯文本行
|
|
111
|
+
DSH_TUI_LINE_MODE=1 ssh host 'dsh --profile tui --resume' | tee session.log
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
行模式**不画帧**:不进备用屏、不做绝对光标寻址、不用回车原地重绘、不出动画。每个事件按顺序追加一次,
|
|
115
|
+
多行内容(报告、diff、工具输出)按其自身结构展开,因此 `tee`、`script(1)`、屏幕阅读器都能完整读到。
|
|
116
|
+
状态符号(● ⚠ ✖、diff 的 `+`/`-`)仍然保留,语义不依赖颜色。全屏交互(鼠标拖选、卡片展开、
|
|
117
|
+
`/find` 高亮)在行模式下不可用——那是画帧的代价,需要时用普通模式。
|
|
118
|
+
|
|
119
|
+
## 4.7 让 dsh 自己走网络代理
|
|
120
|
+
|
|
121
|
+
插件跑在 dsh 进程里,所以"TUI 的网络"就是"dsh 的网络"。Node **不会**自动读 `HTTP_PROXY` /
|
|
122
|
+
`HTTPS_PROXY`:`NODE_USE_ENV_PROXY=1` 单独在 v24.19 上不生效,必须给 Node 传
|
|
123
|
+
**`--use-env-proxy`**(启动包装脚本里加,别用 `NODE_OPTIONS`——那会把模型跑测试、跑工具的
|
|
124
|
+
每个 Node 子进程也一起塞进代理)。
|
|
125
|
+
|
|
126
|
+
按域名分流用 `NO_PROXY`(后缀匹配,`deepseek.com` 覆盖整域)。国内直连更快、或直连比代理更稳的域名放这里,
|
|
127
|
+
例如:
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
export NO_PROXY="${NO_PROXY:+$NO_PROXY,}deepseek.com,api.deepseek.com,commandcode.ai,api.commandcode.ai"
|
|
131
|
+
exec /path/to/node --use-env-proxy /path/to/@deepseek-ai/dsh/lib/bin.js "$@"
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
`cli-chat-proxy.grok.com` / `api.x.ai` / `auth.x.ai` **不要**放进 `NO_PROXY`:这台机器上 Grok 直连会被 RST,必须走代理。
|
|
135
|
+
|
|
136
|
+
容器/沙箱里如果只有代理出口,直连会成片超时——那种"直连失败"是出口的噪音,不能当成
|
|
137
|
+
"某家必须走代理"的证据;判断标准应该是**该域名直连是否稳定**,而不是一次超时。
|
|
138
|
+
`127.0.0.1` / 本机网段务必留在 `NO_PROXY` 里:显示通道虽然走 AF_UNIX,但别把本地流量也绕出去。
|
|
139
|
+
|
|
140
|
+
## 5. 明确不做(以及为什么)
|
|
141
|
+
|
|
142
|
+
- **内置 `--daemon` 常驻模式**:Host 不是服务。它是"某个会话的写者",靠 idle-exit 把写锁交还;把它变成常驻服务会让 Web UI 与其它窗口长期打不开同一会话。
|
|
143
|
+
- **`detachedApproval = pause`(断线时挂起审批等用户回来)**:看似更"尊重用户",实际会与第 2 节的兜底打架——轮次停在一个没人能确认的审批上,直到 idle-exit 把它带走,结果既没执行也没拒绝。当前行为(拒绝 + 写清理由)让轮次能收尾,模型会在你回来前调整做法。
|
|
144
|
+
- **多窗口同屏 / 只读旁观**:锁语义与收益都未验证,不做。
|
|
145
|
+
|
|
146
|
+
## 6. 排障入口
|
|
147
|
+
|
|
148
|
+
- `/diag`:这条会话的通道、锁、Host 身份、判定链("会接入后台 Host,不要另开第二个窗口")。
|
|
149
|
+
- `/doctor`:profile 组合、依赖、兼容与 `dsh-scope` 副本数;`/doctor --fix` 修 profile 补丁(写前备份)。
|
|
150
|
+
- 真机验收脚本:`node scripts/tui-probe.mjs`(启动/缩放//diag//doctor//copy error//preset/鼠标模式/退出)、
|
|
151
|
+
`node scripts/tui-drop-probe.mjs`(杀掉窗口再接管,断言转录保留、可输入、无乱码)与
|
|
152
|
+
`node scripts/tui-mock-probe.mjs`(**合成临时 profile + 脚本化模型**,跑一个真实轮次后验证"拖选复制"
|
|
153
|
+
与 `/find` 高亮;不碰你的 profile、不花额度)、同一脚本的 `--busy`(**忙碌断线**:轮次在飞时杀掉窗口,
|
|
154
|
+
断言 Host 仍持有会话、重连窗口打出「已重连 N 次」);`node scripts/verify-batch.mjs --batch <批>` 一次跑完全部证据。
|
|
Binary file
|
|
Binary file
|
|
Binary file
|