@linxin666/dsh-ssh 0.2.3 → 0.2.4

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/index.js CHANGED
@@ -2137,11 +2137,11 @@ var SshEngine = class {
2137
2137
  stopAllTunnels(this, alias);
2138
2138
  disposeRecord(this, alias);
2139
2139
  }
2140
- /** Probe connectivity: connect, run `true`, close. */
2140
+ /** Probe connectivity with a cross-platform shell command. */
2141
2141
  async test(alias) {
2142
2142
  const started = Date.now();
2143
2143
  try {
2144
- const result = await this.exec(alias, "true", 1e4);
2144
+ const result = await this.exec(alias, "echo ok", 1e4);
2145
2145
  return result.success ? {
2146
2146
  ok: true,
2147
2147
  latencyMs: result.durationMs
@@ -81,7 +81,7 @@ export declare class SshEngine {
81
81
  * connection — the next operation re-connects from the current config.
82
82
  */
83
83
  dropAlias(alias: string): void;
84
- /** Probe connectivity: connect, run `true`, close. */
84
+ /** Probe connectivity with a cross-platform shell command. */
85
85
  test(alias: string): Promise<TestResult>;
86
86
  /** Close every pooled connection and tunnel. */
87
87
  dispose(): void;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@linxin666/dsh-ssh",
3
3
  "description": "Remote SSH operations for the dsh web GUI: host config store (~/.dsh/dsh-ssh.json, import from ~/.ssh/config), a persistent ssh2 connection pool with jump-host support, exec / PTY web terminal / SFTP transfer / local port-forward tunnels / cluster execution, plus agent tools (ssh_list, ssh_exec, ssh_upload, ssh_download, ssh_tunnel, ssh_cluster). Hot-pluggable — mounted via ~/.dsh/cordis.patch.yml + a profile node_modules symlink, no dsh source changes.",
4
- "version": "0.2.3",
4
+ "version": "0.2.4",
5
5
  "type": "module",
6
6
  "engines": {
7
7
  "node": "^22.19.0 || >=24.0.0"
@@ -49,18 +49,18 @@
49
49
  },
50
50
  "devDependencies": {
51
51
  "@deepseek-ai/cordis": "^4.0.1",
52
- "@deepseek-ai/dsh-client-connection": "^0.1.0-rc.7",
53
- "@deepseek-ai/dsh-client-locale": "^0.1.0-rc.7",
54
- "@deepseek-ai/dsh-client-runtime": "^0.1.0-rc.7",
55
- "@deepseek-ai/dsh-client-ui-settings": "^0.1.0-rc.7",
56
- "@deepseek-ai/dsh-client-ui-slots": "^0.1.0-rc.7",
57
- "@deepseek-ai/dsh-host-webserver": "^0.1.0-rc.7",
58
- "@deepseek-ai/dsh-llm": "^0.1.0-rc.7",
59
- "@deepseek-ai/dsh-scope": "^0.1.0-rc.7",
60
- "@deepseek-ai/dsh-settings": "^0.1.0-rc.7",
61
- "@deepseek-ai/dsh-system-prompt": "^0.1.0-rc.7",
62
- "@deepseek-ai/dsh-timeout": "^0.1.0-rc.7",
63
- "@deepseek-ai/dsh-tools": "^0.1.0-rc.7",
52
+ "@deepseek-ai/dsh-client-connection": "^0.1.0-rc.8",
53
+ "@deepseek-ai/dsh-client-locale": "^0.1.0-rc.8",
54
+ "@deepseek-ai/dsh-client-runtime": "^0.1.0-rc.8",
55
+ "@deepseek-ai/dsh-client-ui-settings": "^0.1.0-rc.8",
56
+ "@deepseek-ai/dsh-client-ui-slots": "^0.1.0-rc.8",
57
+ "@deepseek-ai/dsh-host-webserver": "^0.1.0-rc.8",
58
+ "@deepseek-ai/dsh-llm": "^0.1.0-rc.8",
59
+ "@deepseek-ai/dsh-scope": "^0.1.0-rc.8",
60
+ "@deepseek-ai/dsh-settings": "^0.1.0-rc.8",
61
+ "@deepseek-ai/dsh-system-prompt": "^0.1.0-rc.8",
62
+ "@deepseek-ai/dsh-timeout": "^0.1.0-rc.8",
63
+ "@deepseek-ai/dsh-tools": "^0.1.0-rc.8",
64
64
  "@types/node": "^22.20.0",
65
65
  "@types/react": "~18.3.1",
66
66
  "@types/react-dom": "^18.3.5",
@@ -124,17 +124,38 @@ export function TerminalTab({ api, presetAlias, requestId, terminalFont }: Termi
124
124
  // Unmount cleanup (never touches state on an unmounting component).
125
125
  useEffect(() => () => { teardown() }, [])
126
126
 
127
- // Keep the terminal fitted to its container while connected.
127
+ // Keep the terminal fitted to its container. A window resize is only one
128
+ // trigger: the status banner appearing after connect, panel resizes, and
129
+ // sidebar toggles all change the container without a window resize, so the
130
+ // container itself is observed (otherwise the viewport keeps the pre-banner
131
+ // height and the last line is clipped below the fold). ResizeObserver may
132
+ // be absent (jsdom tests); the window listener then remains the only path.
128
133
  useEffect(() => {
129
- const onResize = (): void => {
134
+ let lastCols = -1
135
+ let lastRows = -1
136
+ const sync = (): void => {
130
137
  const term = termRef.current
131
138
  const fit = fitRef.current
132
139
  if (term === null || fit === null) return
133
140
  fit.fit()
134
- connRef.current?.resize(term.cols, term.rows)
141
+ const conn = connRef.current
142
+ if (conn !== null && (term.cols !== lastCols || term.rows !== lastRows)) {
143
+ lastCols = term.cols
144
+ lastRows = term.rows
145
+ conn.resize(term.cols, term.rows)
146
+ }
147
+ }
148
+ window.addEventListener('resize', sync)
149
+ const container = containerRef.current
150
+ if (container === null || typeof ResizeObserver === 'undefined') {
151
+ return () => { window.removeEventListener('resize', sync) }
152
+ }
153
+ const observer = new ResizeObserver(() => { sync() })
154
+ observer.observe(container)
155
+ return () => {
156
+ observer.disconnect()
157
+ window.removeEventListener('resize', sync)
135
158
  }
136
- window.addEventListener('resize', onResize)
137
- return () => { window.removeEventListener('resize', onResize) }
138
159
  }, [])
139
160
 
140
161
  const connect = (): void => {
@@ -703,8 +703,10 @@ html[data-dsh-ssh-active]:not([data-dsh-taskboard-active]) [class*='centerCol']
703
703
 
704
704
  .termContainer {
705
705
  position: absolute;
706
- inset: 0;
707
- padding: 8px 10px;
706
+ /* Inset offsets, not padding: FitAddon measures this element's height as
707
+ the drawable area, so padding here would be measured as terminal space
708
+ and clip the bottom line under a global border-box reset. */
709
+ inset: 8px 10px;
708
710
  }
709
711
 
710
712
  .termPlaceholder {
package/src/engine.ts CHANGED
@@ -148,11 +148,11 @@ export class SshEngine {
148
148
 
149
149
  // ------------------------------------------------------------- misc
150
150
 
151
- /** Probe connectivity: connect, run `true`, close. */
151
+ /** Probe connectivity with a cross-platform shell command. */
152
152
  async test(alias: string): Promise<TestResult> {
153
153
  const started = Date.now()
154
154
  try {
155
- const result = await this.exec(alias, 'true', 10_000)
155
+ const result = await this.exec(alias, 'echo ok', 10_000)
156
156
  return result.success
157
157
  ? { ok: true, latencyMs: result.durationMs }
158
158
  : { ok: false, latencyMs: result.durationMs, error: 'remote exit code ' + result.exitCode }
@@ -167,4 +167,4 @@ export class SshEngine {
167
167
  for (const id of [...this.tunnels.keys()]) stopTunnel(this, id)
168
168
  for (const alias of [...this.pool.keys()]) disposeRecord(this, alias)
169
169
  }
170
- }
170
+ }