@neoline/hostbridge 2.0.1 → 2.0.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neoline/hostbridge",
3
- "version": "2.0.1",
3
+ "version": "2.0.2",
4
4
  "description": "HostBridge MCP server for persistent allowlisted SSH and shell sessions.",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/pyproject.toml CHANGED
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "hostbridge"
7
- version = "2.0.1"
7
+ version = "2.0.2"
8
8
  description = "HostBridge MCP server for persistent allowlisted SSH and shell sessions."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -8,7 +8,7 @@ import sys
8
8
  from typing import NoReturn
9
9
 
10
10
  __all__ = ["__version__", "ensure_runtime"]
11
- __version__ = "2.0.1"
11
+ __version__ = "2.0.2"
12
12
 
13
13
  # (import_name, pip_name) pairs for runtime dependency probing.
14
14
  _REQUIRED_DEPENDENCIES = (
@@ -60,7 +60,7 @@ def _version_diagnostic() -> DiagnosticResult:
60
60
  "npm": npm.get("version"),
61
61
  "python": pyproject.get("project", {}).get("version"),
62
62
  }
63
- status = "pass" if len(set(versions.values())) == 1 and __version__ == "2.0.1" else "fail"
63
+ status = "pass" if len(set(versions.values())) == 1 and __version__ == "2.0.2" else "fail"
64
64
  return DiagnosticResult("version", status, "version sources agree" if status == "pass" else "version drift detected", versions)
65
65
 
66
66
 
@@ -121,9 +121,9 @@ class HostBridgeSSHProcess:
121
121
  process.exit(status)
122
122
 
123
123
  async def _read_exec_stdin(self, process: asyncssh.SSHServerProcess[bytes], command: str) -> bytes | None:
124
- if process.stdin.at_eof():
125
- return b""
126
124
  waits_for_eof = _command_reads_stdin(command)
125
+ if process.stdin.at_eof():
126
+ return b"" if waits_for_eof else None
127
127
  try:
128
128
  first_chunk = await asyncio.wait_for(
129
129
  process.stdin.read(4096),
@@ -134,7 +134,7 @@ class HostBridgeSSHProcess:
134
134
  raise HostBridgeError("timeout", "timed out waiting for mock-ssh exec stdin") from None
135
135
  return None
136
136
  if not first_chunk:
137
- return b""
137
+ return b"" if waits_for_eof else None
138
138
 
139
139
  first_chunk_bytes = _to_bytes(first_chunk)
140
140
  total = len(first_chunk_bytes)
@@ -163,8 +163,7 @@ class PtyTransport:
163
163
  raise TransportError("PTY transport is not alive")
164
164
  if stdin is not None:
165
165
  stdin_path = self._stage_stdin_locked(stdin, timeout=timeout)
166
- command = f"{command} < {shlex.quote(stdin_path)}"
167
- return self._exec_locked(command, timeout=timeout, output_limit=output_limit)
166
+ return self._exec_locked(command, timeout=timeout, output_limit=output_limit, stdin_path=stdin_path)
168
167
  finally:
169
168
  if stdin_path is not None and self.child.isalive():
170
169
  with suppress(Exception):
@@ -205,15 +204,23 @@ class PtyTransport:
205
204
  raise TransportError("failed to stage PTY stdin")
206
205
  return path
207
206
 
208
- def _exec_locked(self, command: str, *, timeout: float, output_limit: int) -> ExecResult:
207
+ def _exec_locked(
208
+ self,
209
+ command: str,
210
+ *,
211
+ timeout: float,
212
+ output_limit: int,
213
+ stdin_path: str | None = None,
214
+ ) -> ExecResult:
209
215
  token = self._token_factory()
210
216
  start_marker = f"__{self._marker_prefix}_START_{token}__"
211
217
  exit_pattern = re.compile(rf"__{self._marker_prefix}_EXIT_{re.escape(token)}__:(\d+)")
212
218
  encoded_command = base64.b64encode(command.encode("utf-8")).decode("ascii")
219
+ stdin_redirect = f" < {shlex.quote(stdin_path)}" if stdin_path is not None else ""
213
220
  remote_line = (
214
221
  f"__scm_cmd=$(printf %s {shlex.quote(encoded_command)} | {_REMOTE_BASE64_DECODE}); "
215
222
  f"printf '\n{start_marker}\n'; "
216
- f"{self.remote_shell} -lc \"$__scm_cmd\"; "
223
+ f"{self.remote_shell} -lc \"$__scm_cmd\"{stdin_redirect}; "
217
224
  "__scm_status=$?; "
218
225
  f"printf '\n__{self._marker_prefix}_EXIT_{token}__:%s\n' \"$__scm_status\""
219
226
  )