dsh-ssh-tui 0.5.9 → 0.6.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.
Files changed (56) hide show
  1. package/README.en.md +28 -5
  2. package/README.md +76 -6
  3. package/lib/attach.js +156 -0
  4. package/lib/attach.js.map +1 -0
  5. package/lib/commands.js +73 -0
  6. package/lib/commands.js.map +1 -0
  7. package/lib/diag.js +292 -0
  8. package/lib/diag.js.map +1 -0
  9. package/lib/dialogs.js +82 -0
  10. package/lib/dialogs.js.map +1 -0
  11. package/lib/display-sock.js +304 -86
  12. package/lib/display-sock.js.map +1 -1
  13. package/lib/dsh-compat.js +25 -4
  14. package/lib/dsh-compat.js.map +1 -1
  15. package/lib/i18n/en.js +55 -0
  16. package/lib/i18n/en.js.map +1 -1
  17. package/lib/i18n/index.js +2 -0
  18. package/lib/i18n/index.js.map +1 -1
  19. package/lib/i18n/zh.js +55 -0
  20. package/lib/i18n/zh.js.map +1 -1
  21. package/lib/index.js +75 -73
  22. package/lib/index.js.map +1 -1
  23. package/lib/paint.js +20 -38
  24. package/lib/paint.js.map +1 -1
  25. package/lib/picker.js +152 -37
  26. package/lib/picker.js.map +1 -1
  27. package/lib/rows.js +120 -0
  28. package/lib/rows.js.map +1 -0
  29. package/lib/session-index.js +4 -1
  30. package/lib/session-index.js.map +1 -1
  31. package/lib/session-list.js +372 -167
  32. package/lib/session-list.js.map +1 -1
  33. package/lib/session-lock.js +20 -9
  34. package/lib/session-lock.js.map +1 -1
  35. package/lib/stats.js +136 -0
  36. package/lib/stats.js.map +1 -0
  37. package/lib/terminal-input.js +470 -0
  38. package/lib/terminal-input.js.map +1 -0
  39. package/lib/tui.js +257 -311
  40. package/lib/tui.js.map +1 -1
  41. package/lib/types/attach.d.ts +106 -0
  42. package/lib/types/commands.d.ts +103 -0
  43. package/lib/types/diag.d.ts +78 -0
  44. package/lib/types/dialogs.d.ts +79 -0
  45. package/lib/types/display-sock.d.ts +59 -3
  46. package/lib/types/dsh-compat.d.ts +7 -0
  47. package/lib/types/i18n/index.d.ts +4 -0
  48. package/lib/types/index.d.ts +8 -4
  49. package/lib/types/paint.d.ts +6 -3
  50. package/lib/types/picker.d.ts +27 -7
  51. package/lib/types/rows.d.ts +69 -0
  52. package/lib/types/session-list.d.ts +43 -3
  53. package/lib/types/stats.d.ts +98 -0
  54. package/lib/types/terminal-input.d.ts +164 -0
  55. package/lib/types/tui.d.ts +33 -8
  56. package/package.json +11 -5
package/lib/diag.js ADDED
@@ -0,0 +1,292 @@
1
+ /**
2
+ * `/diag`: one command that answers "why can't I get in?" with local facts only.
3
+ *
4
+ * Every reconnect/zombie/first-attach report so far took a hand-written probe to
5
+ * explain: which lock file exists, whether its pid is really the Host, whether
6
+ * the display channel answers, whether the artifact is a dead socket file left
7
+ * by a killed Host. This module turns that probe into a product surface.
8
+ *
9
+ * The renderer is pure — it takes one already-collected snapshot and produces
10
+ * the transcript lines, including the decision chain — so it is unit-testable
11
+ * without a Host, a lock, or a filesystem. Collection (which does touch the
12
+ * filesystem) lives in `collectDiag`, and every probe is best-effort: a failing
13
+ * probe reports its failure rather than breaking the command.
14
+ *
15
+ * Nothing here leaves the machine: it prints versions, paths, and states.
16
+ */
17
+ import { readFile, readdir, stat } from 'node:fs/promises';
18
+ import { join } from 'node:path';
19
+ import { t } from './i18n/index.js';
20
+ import { displaySockExists, isPipePath, resolveDshHome, sessionErrPath, sessionSockPath, } from './display-sock.js';
21
+ import { inspectLiveHost, sessionLockDisabled, sessionLockPath } from './session-lock.js';
22
+ import { sessionIndexPath } from './session-index.js';
23
+ /** How much of a Host stderr log the report carries. */
24
+ export const DIAG_ERR_TAIL_BYTES = 2000;
25
+ /** Tail of a Host stderr log, or undefined when there is nothing to read. */
26
+ export async function readErrTail(sessionId, dshHome = resolveDshHome()) {
27
+ try {
28
+ const raw = await readFile(sessionErrPath(sessionId, dshHome), 'utf8');
29
+ const trimmed = raw.trim();
30
+ if (trimmed === '')
31
+ return undefined;
32
+ return trimmed.length <= DIAG_ERR_TAIL_BYTES ? trimmed : `…${trimmed.slice(-DIAG_ERR_TAIL_BYTES)}`;
33
+ }
34
+ catch {
35
+ return undefined;
36
+ }
37
+ }
38
+ /**
39
+ * Why `--resume` would behave the way it does, as an ordered chain of verdicts.
40
+ * The first entry is the actionable one; the rest is supporting context.
41
+ */
42
+ export function diagVerdicts(snapshot) {
43
+ const verdicts = [];
44
+ const { host } = snapshot;
45
+ if (host.kind === 'zombie') {
46
+ verdicts.push(t('diag.v.zombie', { pid: String(host.pid ?? '?') }));
47
+ verdicts.push(t('diag.v.zombieHint'));
48
+ }
49
+ else if (host.kind === 'live') {
50
+ verdicts.push(t('diag.v.live', { pid: String(host.pid ?? '?') }));
51
+ verdicts.push(t('diag.v.liveHint'));
52
+ }
53
+ else if (host.kind === 'stale') {
54
+ verdicts.push(t('diag.v.stale', { pid: String(host.pid ?? '?') }));
55
+ verdicts.push(t('diag.v.staleHint'));
56
+ }
57
+ else {
58
+ verdicts.push(t('diag.v.free'));
59
+ }
60
+ if (host.identity === 'mismatch') {
61
+ verdicts.push(t('diag.v.pidReused', { pid: String(host.pid ?? '?') }));
62
+ }
63
+ else if (host.identity === 'unverifiable' && host.pid !== undefined) {
64
+ verdicts.push(t('diag.v.pidUnverified', { pid: String(host.pid) }));
65
+ }
66
+ // A socket file that exists but does not answer is the classic "first attach
67
+ // dies with EPIPE, the second works" residue of a killed Host.
68
+ if (snapshot.sockFilePresent === true && !snapshot.sockReachable) {
69
+ verdicts.push(t('diag.v.deadSock'));
70
+ }
71
+ if (snapshot.errTail !== undefined && host.kind !== 'none') {
72
+ verdicts.push(t('diag.v.errTail'));
73
+ }
74
+ if (snapshot.hostProcess) {
75
+ verdicts.push(t('diag.v.thisIsHost'));
76
+ }
77
+ if (snapshot.locksDisabled) {
78
+ verdicts.push(t('diag.v.locksOff'));
79
+ }
80
+ if (snapshot.link.probeState === 'unknown' && snapshot.link.kind === 'ssh') {
81
+ verdicts.push(t('diag.v.rttUnknown'));
82
+ }
83
+ if (snapshot.host.state === 'running-detached') {
84
+ verdicts.push(t('diag.v.detachedBusy'));
85
+ }
86
+ return verdicts;
87
+ }
88
+ /** The whole report as transcript lines. Pure. */
89
+ export function formatDiag(snapshot) {
90
+ const yes = t('diag.yes');
91
+ const no = t('diag.no');
92
+ const lines = [];
93
+ lines.push(t('diag.title'));
94
+ lines.push(t('diag.rowVersions', {
95
+ plugin: snapshot.pluginVersion,
96
+ dsh: snapshot.hostVersion,
97
+ node: snapshot.nodeVersion,
98
+ }));
99
+ lines.push(t('diag.rowPlatform', { platform: snapshot.platform }));
100
+ lines.push(t('diag.rowSession', { session: snapshot.sessionId }));
101
+ lines.push(t('diag.rowRole', {
102
+ role: snapshot.hostProcess ? t('diag.roleHost') : t('diag.roleLauncher'),
103
+ home: snapshot.dshHome,
104
+ }));
105
+ lines.push(t('diag.rowChannel', {
106
+ path: snapshot.sockPath,
107
+ kind: isPipePath(snapshot.sockPath) ? t('diag.channelPipe') : t('diag.channelSocket'),
108
+ reachable: snapshot.sockReachable ? yes : no,
109
+ file: snapshot.sockFilePresent === undefined
110
+ ? t('diag.fileNotApplicable')
111
+ : snapshot.sockFilePresent ? yes : no,
112
+ }));
113
+ lines.push(t('diag.rowHost', {
114
+ kind: t(`diag.host.${snapshot.host.kind}`),
115
+ pid: snapshot.host.pid === undefined ? '—' : String(snapshot.host.pid),
116
+ identity: snapshot.host.identity === undefined ? '—' : t(`diag.identity.${snapshot.host.identity}`),
117
+ state: snapshot.host.state ?? '—',
118
+ agent: snapshot.host.agentStatus ?? '—',
119
+ }));
120
+ if (snapshot.host.lockPath !== undefined) {
121
+ lines.push(t('diag.rowLockPath', { path: snapshot.host.lockPath }));
122
+ }
123
+ lines.push(t('diag.rowLink', {
124
+ kind: snapshot.link.kind === 'ssh' ? 'SSH' : t('diag.linkLocal'),
125
+ rtt: snapshot.link.rttMs === undefined ? '—' : `${snapshot.link.rttMs}ms`,
126
+ probe: t(`diag.probe.${snapshot.link.probeState}`),
127
+ paint: snapshot.link.paintIntervalMs === undefined ? '—' : `${snapshot.link.paintIntervalMs}ms`,
128
+ }));
129
+ if (snapshot.sessionLog === undefined) {
130
+ lines.push(t('diag.rowLog', { format: t('diag.logMissing') }));
131
+ }
132
+ else {
133
+ lines.push(t('diag.rowLog', {
134
+ format: t('diag.logPresent', {
135
+ format: snapshot.sessionLog.format,
136
+ bytes: String(snapshot.sessionLog.bytes),
137
+ events: snapshot.sessionLog.events === undefined ? '?' : String(snapshot.sessionLog.events),
138
+ seq: snapshot.sessionLog.seq === undefined ? '?' : String(snapshot.sessionLog.seq),
139
+ }),
140
+ }));
141
+ }
142
+ lines.push(t('diag.rowLocks', {
143
+ held: snapshot.lockHeldByThisProcess ? yes : no,
144
+ others: String(snapshot.otherLocks.length),
145
+ }));
146
+ for (const other of snapshot.otherLocks.slice(0, 5)) {
147
+ lines.push(t('diag.rowOtherLock', {
148
+ session: other.sessionId,
149
+ pid: String(other.pid),
150
+ state: other.state ?? '—',
151
+ }));
152
+ }
153
+ lines.push('');
154
+ lines.push(t('diag.verdictsTitle'));
155
+ for (const verdict of diagVerdicts(snapshot))
156
+ lines.push(` · ${verdict}`);
157
+ if (snapshot.errTail !== undefined) {
158
+ lines.push('');
159
+ lines.push(t('diag.errTitle'));
160
+ for (const line of snapshot.errTail.split('\n').slice(-12))
161
+ lines.push(` ${line}`);
162
+ }
163
+ return lines;
164
+ }
165
+ /** Gather everything the report needs. Every probe is best-effort. */
166
+ export async function collectDiag(options) {
167
+ const dshHome = options.dshHome ?? resolveDshHome();
168
+ const sockPath = sessionSockPath(options.sessionId, dshHome);
169
+ const lockPath = sessionLockPath(options.sessionId, dshHome);
170
+ const reachable = await displaySockExists(sockPath);
171
+ let sockFilePresent;
172
+ if (!isPipePath(sockPath)) {
173
+ sockFilePresent = await stat(sockPath).then(() => true).catch(() => false);
174
+ }
175
+ const live = await inspectLiveHost(options.sessionId, dshHome).catch(() => undefined);
176
+ const host = live === undefined
177
+ ? { kind: 'none', path: lockPath }
178
+ : {
179
+ kind: live.kind === 'attachable' ? 'live' : 'zombie',
180
+ pid: live.lock.pid,
181
+ ...(live.lock.state === undefined ? {} : { state: live.lock.state }),
182
+ ...(live.lock.agentStatus === undefined ? {} : { agentStatus: live.lock.agentStatus }),
183
+ ...(live.lock.disconnectPolicy === undefined ? {} : { disconnectPolicy: live.lock.disconnectPolicy }),
184
+ ...(live.lock.tty === undefined ? {} : { tty: live.lock.tty }),
185
+ identity: 'verified',
186
+ lockPath: live.path,
187
+ path: live.sock,
188
+ };
189
+ // `inspectLiveHost` steals a dead lock, so a lock file that exists while the
190
+ // inspection found nothing is the stale case.
191
+ if (host.kind === 'none') {
192
+ const raw = await readFile(lockPath, 'utf8').then(text => text, () => undefined);
193
+ if (raw !== undefined) {
194
+ try {
195
+ const parsed = JSON.parse(raw);
196
+ host.kind = 'stale';
197
+ if (typeof parsed.pid === 'number')
198
+ host.pid = parsed.pid;
199
+ if (typeof parsed.state === 'string')
200
+ host.state = parsed.state;
201
+ if (typeof parsed.agentStatus === 'string')
202
+ host.agentStatus = parsed.agentStatus;
203
+ host.lockPath = lockPath;
204
+ }
205
+ catch {
206
+ // An unreadable lock is reported as no lock.
207
+ }
208
+ }
209
+ }
210
+ const log = await sessionLogInfo(options.sessionId);
211
+ const errTail = await readErrTail(options.sessionId, dshHome);
212
+ return {
213
+ ...(log === undefined ? {} : { sessionLog: log }),
214
+ ...(errTail === undefined ? {} : { errTail }),
215
+ pluginVersion: options.pluginVersion,
216
+ hostVersion: options.hostVersion,
217
+ nodeVersion: process.version,
218
+ platform: `${process.platform} ${process.arch}`,
219
+ sessionId: options.sessionId,
220
+ hostProcess: options.hostProcess,
221
+ locksDisabled: sessionLockDisabled(),
222
+ dshHome,
223
+ sockPath,
224
+ sockReachable: reachable,
225
+ ...(sockFilePresent === undefined ? {} : { sockFilePresent }),
226
+ host,
227
+ link: options.paintIntervalMs === undefined
228
+ ? options.link
229
+ : { ...options.link, paintIntervalMs: options.paintIntervalMs },
230
+ lockHeldByThisProcess: host.kind !== 'none' && host.pid === process.pid,
231
+ otherLocks: await listOtherLocks(options.sessionId, dshHome),
232
+ };
233
+ }
234
+ /** Locks under DSH_HOME belonging to other sessions, for the report's context. */
235
+ async function listOtherLocks(sessionId, dshHome) {
236
+ const dir = join(dshHome, 'tui-locks');
237
+ const names = await readdir(dir).catch(() => []);
238
+ const others = [];
239
+ for (const name of names) {
240
+ if (!name.endsWith('.json'))
241
+ continue;
242
+ try {
243
+ const parsed = JSON.parse(await readFile(join(dir, name), 'utf8'));
244
+ if (typeof parsed.sessionId !== 'string' || parsed.sessionId === sessionId)
245
+ continue;
246
+ if (typeof parsed.pid !== 'number')
247
+ continue;
248
+ others.push({
249
+ sessionId: parsed.sessionId,
250
+ pid: parsed.pid,
251
+ ...(typeof parsed.state === 'string' ? { state: parsed.state } : {}),
252
+ });
253
+ }
254
+ catch {
255
+ // A corrupt lock is not this report's business.
256
+ }
257
+ }
258
+ return others;
259
+ }
260
+ /** Directory holding the per-session index, for callers that report it. */
261
+ export function diagIndexPath(dshHome = resolveDshHome()) {
262
+ return sessionIndexPath(dshHome);
263
+ }
264
+ /** Size and last-seq of one session artifact, when it has materialized. */
265
+ async function sessionLogInfo(sessionId) {
266
+ const roots = await readdir(join(dshHomeDir(), 'sessions')).catch(() => []);
267
+ for (const project of roots) {
268
+ const dir = join(dshHomeDir(), 'sessions', project, sessionId);
269
+ const names = await readdir(dir).catch(() => []);
270
+ const log = names.find(name => name.startsWith('session.v') && name.includes('.jsonl'));
271
+ if (log === undefined)
272
+ continue;
273
+ const info = await stat(join(dir, log)).catch(() => undefined);
274
+ if (info === undefined)
275
+ continue;
276
+ // The last persisted seq is read from the tail of the decompressed log by
277
+ // the caller's own persistence layer; here the durable file facts are
278
+ // enough to answer "did this session ever materialize, and how big".
279
+ const format = log.replace(/^session\./, '').replace(/\.jsonl(\.zstd)?$/, '');
280
+ return {
281
+ format: `${format}${log.endsWith('.zstd') ? ' zstd' : ''}`,
282
+ bytes: info.size,
283
+ modifiedAt: info.mtimeMs,
284
+ };
285
+ }
286
+ return undefined;
287
+ }
288
+ /** DSH_HOME resolved once, so the probes below cannot disagree about it. */
289
+ function dshHomeDir() {
290
+ return resolveDshHome();
291
+ }
292
+ //# sourceMappingURL=diag.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diag.js","sourceRoot":"","sources":["../src/diag.ts"],"names":[],"mappings":"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"}
package/lib/dialogs.js ADDED
@@ -0,0 +1,82 @@
1
+ /** Digits then letters: the hotkeys painted next to question options. */
2
+ export const QUESTION_OPTION_KEYS = '123456789abcdefghijklmnopqrstuvwxyz';
3
+ /** Options a dialog can answer with; only a question list has any. */
4
+ export function optionsLength(dialog) {
5
+ return dialog.kind === 'questions' ? (dialog.question.options?.length ?? 0) : 0;
6
+ }
7
+ /**
8
+ * The option index a hotkey selects, or undefined when the key is not one of
9
+ * the painted keys or names no option.
10
+ */
11
+ export function questionOptionIndex(key, optionCount) {
12
+ const index = QUESTION_OPTION_KEYS.indexOf(key.toLowerCase());
13
+ if (index < 0 || index >= optionCount)
14
+ return undefined;
15
+ return index;
16
+ }
17
+ /** Move the question highlight, clamped to its options. Returns false if not a question dialog. */
18
+ export function moveQuestionCursor(dialog, delta) {
19
+ const count = dialog.question.options?.length ?? 0;
20
+ if (count === 0)
21
+ return false;
22
+ dialog.cursor = Math.max(0, Math.min(count - 1, dialog.cursor + delta));
23
+ if (dialog.question.multiSelect !== true) {
24
+ dialog.selected.clear();
25
+ dialog.selected.add(dialog.cursor);
26
+ }
27
+ return true;
28
+ }
29
+ /** Select option `index`: toggles in a multi-select list, replaces otherwise. */
30
+ export function selectQuestionOption(dialog, index) {
31
+ dialog.cursor = index;
32
+ if (dialog.question.multiSelect === true) {
33
+ if (dialog.selected.has(index))
34
+ dialog.selected.delete(index);
35
+ else
36
+ dialog.selected.add(index);
37
+ return;
38
+ }
39
+ dialog.selected.clear();
40
+ dialog.selected.add(index);
41
+ }
42
+ /** Handle a hotkey: select its option and report whether it applied. */
43
+ export function selectQuestionOptionByKey(dialog, key) {
44
+ const index = questionOptionIndex(key, dialog.question.options?.length ?? 0);
45
+ if (index === undefined)
46
+ return false;
47
+ selectQuestionOption(dialog, index);
48
+ return true;
49
+ }
50
+ /**
51
+ * What Enter means for a question dialog. A single-select list with nothing
52
+ * highlighted cancels (the user must not accidentally answer with the first
53
+ * option); a multi-select list may answer with an empty selection. With no
54
+ * options at all, the typed text is the answer.
55
+ */
56
+ export function questionSubmit(dialog, input) {
57
+ const options = dialog.question.options ?? [];
58
+ const selected = [...dialog.selected]
59
+ .map(index => options[index]?.label)
60
+ .filter((label) => label !== undefined);
61
+ if (selected.length === 0 && options.length > 0 && dialog.question.multiSelect !== true) {
62
+ return { kind: 'reject' };
63
+ }
64
+ if (options.length === 0)
65
+ return { kind: 'resolve', selected: [], custom: input };
66
+ return { kind: 'resolve', selected };
67
+ }
68
+ /** y/n/ctrl-c/esc answers for a confirm prompt. */
69
+ export function confirmAnswer(text) {
70
+ if (text === 'y' || text === 'Y')
71
+ return 'y';
72
+ if (text === 'n' || text === 'N')
73
+ return 'n';
74
+ if (text === '\x03' || text === '\x1b')
75
+ return 'cancel';
76
+ return undefined;
77
+ }
78
+ /** The inspect overlay closes on any of these, and ignores the rest. */
79
+ export function inspectClosesOn(text) {
80
+ return text === '\x1b' || text === '\x03' || text === 'q' || text === 'Q' || text === '\r' || text === '\n';
81
+ }
82
+ //# sourceMappingURL=dialogs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dialogs.js","sourceRoot":"","sources":["../src/dialogs.ts"],"names":[],"mappings":"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"}