dsh-remote 0.8.39 → 0.8.41

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/lib/paths.js CHANGED
@@ -203,8 +203,16 @@ export function relPathUnder(root, p) {
203
203
  }
204
204
 
205
205
  export function truncate(s, max) {
206
- if (s.length <= max) return s
207
- return s.slice(0, max) + `\n…[truncated: ${s.length - max} more chars]`
206
+ // A missing/NaN cap is a CONFIG BUG (measured: a bare `new SshPool({...})`
207
+ // with no maxOutputChars turned EVERY exec result into a 29-char truncation
208
+ // marker — `'x'.length <= undefined` is false, so the "cap" branch always ran
209
+ // and `s.length - undefined` printed NaN — silently destroying every remote
210
+ // output, including the web-attach startup token). A bad cap must disable
211
+ // truncation, not destroy the string.
212
+ const limit = Number(max)
213
+ if (!Number.isFinite(limit) || limit <= 0) return s
214
+ if (s.length <= limit) return s
215
+ return s.slice(0, limit) + `\n…[truncated: ${s.length - limit} more chars]`
208
216
  }
209
217
 
210
218
  /** 32-bit string hash rendered in base36 (short, collision-safe enough). */
package/lib/web-attach.js CHANGED
@@ -301,12 +301,24 @@ export class WebAttach {
301
301
  // Both failure shapes carry the same diagnostics: the process either died
302
302
  // (its output is the whole story) or is still booting after the deadline.
303
303
  if (launchFailed(out)) {
304
+ // The remote process is already gone — nothing to clean up.
304
305
  throw new Error('web-attach: the remote DSH exited during startup.'
305
306
  + explainLaunchFailure(tail || out)
306
307
  + ` ${tail || 'no output'}`)
307
308
  }
308
309
  const parsed = parseWebStart(out)
309
310
  if (!parsed) {
311
+ // A timed-out launch may STILL be booting: the deadline expiring is not
312
+ // evidence the process died. Kill it before throwing, or every retry
313
+ // leaks another remote server (measured: three leftover `dsh --port 0`
314
+ // processes after three failed attaches). A PID of 0 means the launcher
315
+ // could not report one — nothing to target.
316
+ if (this.remotePid > 0) {
317
+ try {
318
+ await exec(buildStopCommand(this.remotePid, logPath), { timeoutMs: 20000 })
319
+ this.remotePid = 0
320
+ } catch { /* the process may already be gone; a leftover is worse than a failed kill attempt only if it repeats, and the next attach replaces this session */ }
321
+ }
310
322
  throw new Error(
311
323
  `web-attach: the remote DSH did not report a startup token within ${waitSeconds}s.`
312
324
  + ` Check that \`${command}\` exists on the remote and can boot a web profile.`
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "dsh-remote",
3
- "version": "0.8.39",
4
- "description": "Remote-work assistant for DeepSeek Harness: connect SSH (password/key/agent/keyboard-interactive/proxy jump), pick a remote workspace, operate on it with 25 rw_* tools (incl. rw_edit/rw_stat/rw_mkdir/rw_remove/rw_move/rw_forward), conflict-aware mirror sync, workspace backup/restore, port forwarding, optional side-bar remote file editing, audit log, keychain passwords.",
3
+ "version": "0.8.41",
4
+ "description": "Remote-work assistant for DeepSeek Harness: connect SSH (password/key/agent/keyboard-interactive/proxy jump), pick a remote workspace, operate on it with 22 rw_* tools (incl. rw_edit/rw_stat/rw_mkdir/rw_remove/rw_move/rw_forward), conflict-aware mirror sync, one-click remote dsh deploy + web UI attach, port forwarding, optional side-bar remote file editing, audit log, keychain passwords.",
5
5
  "license": "MIT",
6
6
  "author": "flymysql <flyphp@outlook.com>",
7
7
  "readme": "README.md",