@neoline/hostbridge 1.3.0 → 1.4.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/bin/hostbridge.js CHANGED
@@ -27,6 +27,7 @@ const env = { ...process.env };
27
27
  env.PYTHONPATH = env.PYTHONPATH ? `${bundledSrc}${delimiter}${env.PYTHONPATH}` : bundledSrc;
28
28
  env.HOSTBRIDGE_DEFAULT_MCP_COMMAND = env.HOSTBRIDGE_DEFAULT_MCP_COMMAND || 'npx';
29
29
  env.HOSTBRIDGE_DEFAULT_MCP_ARGS = env.HOSTBRIDGE_DEFAULT_MCP_ARGS || JSON.stringify(['-y', '@neoline/hostbridge']);
30
+ env.HOSTBRIDGE_NPM_LAUNCHER = env.HOSTBRIDGE_NPM_LAUNCHER || '1';
30
31
  if (!env.HOSTBRIDGE_DAEMON_SOCKET && !env.HOSTBRIDGE_DAEMON_DIR) {
31
32
  const username = env.USER || env.LOGNAME || userInfo().username || 'user';
32
33
  env.HOSTBRIDGE_DAEMON_SOCKET = join(hostbridgeRuntimeDir(username), 'daemon.sock');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neoline/hostbridge",
3
- "version": "1.3.0",
3
+ "version": "1.4.0",
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 = "1.3.0"
7
+ version = "1.4.0"
8
8
  description = "HostBridge MCP server for persistent allowlisted SSH and shell sessions."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -3,10 +3,12 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import sys
6
+ import os
7
+ import subprocess
6
8
  from typing import NoReturn
7
9
 
8
10
  __all__ = ["__version__", "ensure_runtime"]
9
- __version__ = "1.3.0"
11
+ __version__ = "1.4.0"
10
12
 
11
13
  # (import_name, pip_name) pairs for runtime dependency probing.
12
14
  _REQUIRED_DEPENDENCIES = (
@@ -38,6 +40,9 @@ def ensure_runtime() -> None:
38
40
  missing = _missing_dependencies()
39
41
  if not missing:
40
42
  return
43
+ if _auto_install_enabled() and _install_missing_dependencies(missing):
44
+ if not _missing_dependencies():
45
+ return
41
46
 
42
47
  install_command = "pip install " + " ".join(missing)
43
48
  message = (
@@ -52,3 +57,17 @@ def ensure_runtime() -> None:
52
57
  def _abort_with_error(message: str) -> NoReturn:
53
58
  print(message, file=sys.stderr)
54
59
  sys.exit(2)
60
+
61
+
62
+ def _auto_install_enabled() -> bool:
63
+ return (os.environ.get("HOSTBRIDGE_AUTO_INSTALL_DEPS") or os.environ.get("HOSTBRIDGE_NPM_LAUNCHER") or "").lower() in {
64
+ "1",
65
+ "true",
66
+ "yes",
67
+ }
68
+
69
+
70
+ def _install_missing_dependencies(missing: list[str]) -> bool:
71
+ print(f"hostbridge: installing missing Python dependencies: {', '.join(missing)}", file=sys.stderr)
72
+ result = subprocess.run([sys.executable, "-m", "pip", "install", *missing], stdout=sys.stderr, stderr=sys.stderr, check=False)
73
+ return result.returncode == 0
@@ -281,7 +281,13 @@ def build_manager() -> SessionManager:
281
281
 
282
282
 
283
283
  def daemon_dir(path: Path | str | None = None) -> Path:
284
- configured = path or os.environ.get("HOSTBRIDGE_DAEMON_DIR") or DEFAULT_DAEMON_DIR
284
+ configured = path or os.environ.get("HOSTBRIDGE_DAEMON_DIR")
285
+ if configured:
286
+ return Path(configured).expanduser()
287
+ socket_override = os.environ.get("HOSTBRIDGE_DAEMON_SOCKET")
288
+ if socket_override:
289
+ return Path(socket_override).expanduser().parent
290
+ configured = DEFAULT_DAEMON_DIR
285
291
  return Path(configured).expanduser()
286
292
 
287
293
 
@@ -303,6 +309,8 @@ def log_file() -> Path:
303
309
 
304
310
  def _prepare_socket_path(path: Path) -> None:
305
311
  path.parent.mkdir(parents=True, exist_ok=True)
312
+ log_file().parent.mkdir(parents=True, exist_ok=True)
313
+ pid_file().parent.mkdir(parents=True, exist_ok=True)
306
314
  with suppress(OSError):
307
315
  path.parent.chmod(0o700)
308
316
  if path.exists():