@neoline/hostbridge 1.4.3 → 2.0.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/README.md CHANGED
@@ -25,57 +25,27 @@ This repo is portable: it ships with no personal server paths or built-in hosts.
25
25
 
26
26
  Every tool returns a structured payload with a stable `error_code` on failure (`HOST_NOT_FOUND`, `SESSION_NOT_FOUND`, `POLICY_DENIED`, `TASK_NOT_FOUND`, `TIMEOUT`, `INTERNAL`).
27
27
 
28
- ## Install, connect to agents, and configure hosts
28
+ ## Install and configure v1
29
29
 
30
- The lifecycle mirrors tools like CodeGraph: install the CLI, register the MCP with local agent clients, then configure per-machine host entries. Installing the CLI alone does not connect it to any agent; `hostbridge install` performs that wiring. It does not configure any servers; per-server setup is the separate `hostbridge setup` step.
31
-
32
- The shared distribution path is npm. Users can run the MCP server or setup wizard with `npx`:
33
-
34
- ```bash
35
- npx -y @neoline/hostbridge install
36
- npx -y @neoline/hostbridge setup
37
- npx -y @neoline/hostbridge list
38
- ```
39
-
40
- For a persistent global install:
30
+ Install from npm or Python, then create a schema-v1 configuration and start the daemon:
41
31
 
42
32
  ```bash
43
33
  npm install -g @neoline/hostbridge
44
- hostbridge install
45
34
  hostbridge setup
35
+ hostbridge daemon start
36
+ hostbridge doctor
46
37
  ```
47
38
 
48
- `hostbridge install` detects supported local agents and writes only the hostbridge MCP entry into each detected config, preserving existing MCP servers. Supported targets are:
49
-
50
- - Codex CLI: `~/.codex/config.toml`
51
- - Claude Code: `~/.claude.json`
52
- - Cursor: `~/.cursor/mcp.json`
53
- - Gemini CLI: `~/.gemini/settings.json`
54
- - opencode: `~/.config/opencode/opencode.json`
55
- - Hermes Agent: `~/.hermes/mcp.json`
56
- - Antigravity IDE: `~/.antigravity/mcp.json`
57
- - Kiro: `~/.kiro/settings/mcp.json`
39
+ The public CLI is intentionally small: `setup`, `migrate`, `doctor`, `hosts`, `daemon`, `mock-ssh`, and `version`. Configure MCP clients manually to run `npx -y @neoline/hostbridge`; when no CLI command is supplied the same entrypoint serves MCP over stdio.
58
40
 
59
- Use `--target <name>` for one client only, for example `hostbridge install --target codex`. From a source checkout, the installer registers `python3 <repo>/run_server.py`. From an installed package, it registers the `hostbridge` executable. Restart open agent clients or start new sessions once after installation so the new MCP server is loaded.
60
-
61
- This npm package is a Node launcher around the Python MCP core, so the machine still needs Python 3.11+ and the package's Python dependencies available to that interpreter. To point at a non-default interpreter:
62
-
63
- ```bash
64
- HOSTBRIDGE_PYTHON=/path/to/python3 npx -y @neoline/hostbridge setup
65
- ```
66
-
67
- For local development from this repository:
41
+ Add a host as one validated JSON object:
68
42
 
69
43
  ```bash
70
- git clone <repo-url> hostbridge
71
- cd hostbridge
72
- python3 -m pip install -e .
73
- python3 -m server_control_mcp.server install
74
- npm run test:node
44
+ hostbridge hosts add '{"id":"gpu","label":"GPU","ssh":{"host":"alice@gpu.example.com"},"transport":"auto"}'
45
+ hostbridge hosts list
46
+ hostbridge hosts check gpu
75
47
  ```
76
48
 
77
-
78
-
79
49
  ## File transfer model
80
50
 
81
51
  HostBridge file transfer is shell-based because recorded login paths may be JumpServer menus, OTP flows, wrapper scripts, or `docker exec` sessions rather than standard SSH/SFTP connections. The first-class transfer tools stream chunked base64 through the already-open shell session, verify hashes, and work for text or binary artifacts up to 100MiB:
@@ -92,7 +62,7 @@ Give file tools local and remote paths; do not paste base64 into tool arguments.
92
62
  Some clients, including compute providers, know how to connect to SSH targets but cannot run HostBridge's MCP connector inside their local sandbox. `hostbridge mock-ssh` exposes one configured HostBridge host as a local SSH server while keeping the actual remote access path behind HostBridge. This is useful when the client-side SSH implementation runs outside a restricted sandbox.
93
63
 
94
64
  ```bash
95
- hostbridge mock-ssh a800_95
65
+ hostbridge mock-ssh start a800_95
96
66
  ```
97
67
 
98
68
  By default HostBridge selects a free local port from `2222-2299`, creates host/client keys under `~/.hostbridge/mock-ssh/<host_id>/`, and writes a managed `Host hostbridge-<host_id>` block into `~/.ssh/config`. The command prints the chosen alias, port, config path, and identity file:
@@ -108,7 +78,7 @@ identity file: /Users/alice/.hostbridge/mock-ssh/a800_95/client_ed25519_key
108
78
  Point the compute provider at the printed SSH alias, for example `ssh:hostbridge-a800_95`. To remove the managed SSH config block later:
109
79
 
110
80
  ```bash
111
- hostbridge mock-ssh a800_95 --remove
81
+ hostbridge mock-ssh remove a800_95
112
82
  ```
113
83
 
114
84
  Advanced options are still available for fixed deployments: `--listen-host`, `--port`, `--key-dir`, `--ssh-host-alias`, `--ssh-config`, and `--no-ssh-config`.
@@ -159,81 +129,11 @@ For MCP clients, prefer setting the environment variable instead of relying on s
159
129
 
160
130
  Sessions can outlive a single agent client process. HostBridge records an optional `HOSTBRIDGE_AGENT_ID` owner on sessions and refuses cross-owner operations unless a force-close path is used. Set `HOSTBRIDGE_MAX_SESSIONS_PER_HOST` to cap concurrent live sessions per configured host (default `0`, unlimited).
161
131
 
162
- ## Guided setup wizard
163
-
164
- Run the local setup wizard instead of hand-writing `hosts.json`:
165
-
166
- ```bash
167
- hostbridge setup
168
- ```
169
-
170
- You can also pass the host id up front to reduce prompts:
171
-
172
- ```bash
173
- hostbridge setup gpu_1
174
- ```
175
-
176
- The wizard now records the real login path instead of asking a fixed questionnaire:
177
-
178
- 1. Collect only a stable host id, display label, and the command you already run locally, such as `ssh bastion`, `ssh -J bastion alice@gpu.internal`, `bash ~/.ssh/connect_gpu.sh`, or `docker exec -it training-box bash`.
179
- 2. Start that command in a terminal-style PTY proxy: remote output is printed directly, and user input is forwarded to the setup session.
180
- 3. Record the prompt visible when each line is typed. Secret-looking prompts (`password`, `passphrase`, `token`, `OTP`, `密码`, `验证码`) do not echo locally and are encrypted before saving.
181
- 4. Convert the recorded prompt/input pairs into ordered `login_steps`; type `/ready` once the final remote shell prompt is available.
182
- 5. Send a readiness probe through the same session, then replay the recorded `login_steps` in a fresh connection before saving `~/.hostbridge/hosts.json`.
132
+ ## Configuration lifecycle
183
133
 
184
- Useful helper commands:
134
+ `hostbridge setup` creates an empty schema-v1 file with mode `0600`. It does not run an interactive login recorder. Use `hostbridge hosts add` for new entries and `hostbridge migrate OLD NEW` for legacy files. The daemon loads one validated config at startup; restart it after configuration changes.
185
135
 
186
- ```bash
187
- hostbridge install
188
- hostbridge list
189
- hostbridge check gpu_1
190
- hostbridge remove gpu_1
191
- hostbridge setup --config ./hosts.json
192
- hostbridge reload
193
- ```
194
-
195
- ### Agent-friendly setup
196
-
197
- Agents should prefer the CLI setup flow over searching shell history or hand-editing JSON. A common JumpServer pattern is to clone a known bastion entry and change only the menu-selected host:
198
-
199
- ```bash
200
- hostbridge setup gpu_lab_1 \
201
- --label "Lab GPU server" \
202
- --from-host bastion \
203
- --menu-selection "gpu-lab-01" \
204
- --final-prompt '.*[$#] '
205
- ```
206
-
207
- This reuses the source host's connection command and encrypted secrets, replaces the menu selection, verifies the resulting runtime path, then saves the new host. After setup, running MCP servers that include runtime reload support will pick up the new entry on the next `hosts_list` or `session_open`; no client restart is required. If an already-running MCP process predates this feature, restart it once.
208
-
209
- Recommended agent flow:
210
-
211
- 1. Call `hosts_list`.
212
- 2. If the target host exists, call `session_open`.
213
- 3. If `session_open` times out or fails, run `hostbridge check <host_id>` to verify the recorded login path, then retry or repair setup.
214
- 4. If it does not exist and the user identifies a similar bastion-backed host, run `hostbridge setup <new_id> --from-host <source_id> --menu-selection <ip-or-name> --final-prompt <regex>`.
215
- 5. Call `hosts_list` again, then `session_open(<new_id>)`.
216
- 6. Do not inspect `~/.ssh`, old expect scripts, or shell snapshots unless the user explicitly asks or the setup command lacks required information.
217
-
218
- ### Encrypted login automation
219
-
220
- Some environments require a password prompt, a bastion menu, or a JumpServer host selector before the remote shell is available. The setup wizard can save these as ordered `login_steps`. Secrets are encrypted with a local Fernet key at `~/.hostbridge/key` or `HOSTBRIDGE_KEY_FILE`; the decrypted value is used only in memory while opening a session.
221
-
222
- Equivalent non-interactive commands are available:
223
-
224
- ```bash
225
- hostbridge secret set bastion password
226
- hostbridge step add bastion --expect '(?i)password:' --send-secret password
227
- hostbridge step add bastion --expect 'Opt>' --send 'gpu-lab-01'
228
- hostbridge step add bastion --expect '.*[$#] ' --send ''
229
- ```
230
-
231
- Security model:
232
-
233
- - `hosts.json` never stores plaintext passwords.
234
- - `hostbridge list` and `hosts_list` do not expose secret material.
235
- - `~/.hostbridge/hosts.json` and `~/.hostbridge/key` should be readable only by the local user running the MCP server.
236
- - Local encryption protects config-at-rest, not a fully compromised same-user account. Prefer SSH keys or system keychains when available.
136
+ Use `transport: "auto"` for normal operation, `pty` for JumpServer/menu/wrapper flows, or `ssh` for native AsyncSSH/SFTP-capable targets. Commands receive stdin explicitly through the protocol; HostBridge no longer guesses whether a command reads stdin.
237
137
 
238
138
  ## Configure hosts
239
139
 
@@ -243,6 +143,7 @@ Create `~/.hostbridge/hosts.json`:
243
143
  mkdir -p ~/.hostbridge
244
144
  cat > ~/.hostbridge/hosts.json <<'JSON'
245
145
  {
146
+ "schema_version": 1,
246
147
  "hosts": [
247
148
  {
248
149
  "id": "gpu_direct",
@@ -256,7 +157,7 @@ cat > ~/.hostbridge/hosts.json <<'JSON'
256
157
  JSON
257
158
  ```
258
159
 
259
- Running MCP servers reload this file during `hosts_list` and `session_open`, so a client restart is not normally required after editing it.
160
+ The daemon validates this file at startup. Restart the daemon after editing it.
260
161
 
261
162
  ### Direct SSH, no jump host
262
163
 
@@ -339,26 +240,14 @@ Use this for non-SSH interactive shells, containers, or custom connection tools.
339
240
 
340
241
  ## MCP client config
341
242
 
342
- Prefer the installer:
343
-
344
- ```bash
345
- hostbridge install
346
- ```
347
-
348
- Single-client install is also available:
349
-
350
- ```bash
351
- hostbridge install --target codex
352
- hostbridge install --target cursor
353
- hostbridge install --target opencode
354
- ```
243
+ Register the stdio entrypoint in each client. HostBridge v1 does not mutate client configuration files.
355
244
 
356
- Codex config written by the installer, for example in `~/.codex/config.toml`:
245
+ Codex example in `~/.codex/config.toml`:
357
246
 
358
247
  ```toml
359
248
  [mcp_servers.hostbridge]
360
249
  command = "npx"
361
- args = ["-y", "hostbridge"]
250
+ args = ["-y", "@neoline/hostbridge"]
362
251
  [mcp_servers.hostbridge.env]
363
252
  HOSTBRIDGE_AGENT_ID = "codex"
364
253
  ```
@@ -418,14 +307,12 @@ See `SECURITY.md` and `docs/THREAT_MODEL.md` for the trust model and audited eve
418
307
  ## Typical agent workflow
419
308
 
420
309
  1. Call `hosts_list` and choose a configured host id.
421
- 2. If the host is missing, ask for only the missing fields and use `hostbridge setup <host_id>` rather than manually editing config.
310
+ 2. Add missing hosts with `hostbridge hosts add '<json>'`, then restart the daemon.
422
311
  3. Call `session_open` once for the chosen host.
423
- 4. Only if `session_open` times out or fails, run `hostbridge check <host_id>` and retry after fixing the login path.
424
- 5. Use `command_run` for quick commands such as `nvidia-smi`, `pwd`, `df -h`, or `tmux ls`.
425
- 6. Use `task_start` for long training jobs, downloads, builds, or scripts.
426
- 7. Poll `task_status` until `state` is `finished`; inspect `exit_code` and `log_tail`.
427
- 8. Call `task_stop` early if a job should be aborted.
428
- 9. Call `session_close` when finished, or `sessions_close_all` to tear down every session.
312
+ 4. Only after a connection failure, run `hostbridge hosts check <host_id>`.
313
+ 5. Use `command_run` for quick work and `task_start` / `task_status` for long jobs.
314
+ 6. Use text tools for UTF-8 and upload/download tools for streamed binary transfers.
315
+ 7. Close the session when finished.
429
316
 
430
317
  ## Security notes
431
318
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neoline/hostbridge",
3
- "version": "1.4.3",
3
+ "version": "2.0.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.4.3"
7
+ version = "2.0.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"
@@ -45,6 +45,7 @@ dependencies = [
45
45
  [project.optional-dependencies]
46
46
  dev = [
47
47
  "pytest>=8.0.0",
48
+ "pytest-asyncio>=0.24.0",
48
49
  "pytest-cov>=4.1.0",
49
50
  "ruff>=0.6.0",
50
51
  "mypy>=1.10.0",
@@ -2,13 +2,13 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- import sys
6
5
  import os
7
6
  import subprocess
7
+ import sys
8
8
  from typing import NoReturn
9
9
 
10
10
  __all__ = ["__version__", "ensure_runtime"]
11
- __version__ = "1.4.1"
11
+ __version__ = "2.0.0"
12
12
 
13
13
  # (import_name, pip_name) pairs for runtime dependency probing.
14
14
  _REQUIRED_DEPENDENCIES = (
@@ -40,9 +40,8 @@ def ensure_runtime() -> None:
40
40
  missing = _missing_dependencies()
41
41
  if not missing:
42
42
  return
43
- if _auto_install_enabled() and _install_missing_dependencies(missing):
44
- if not _missing_dependencies():
45
- return
43
+ if _auto_install_enabled() and _install_missing_dependencies(missing) and not _missing_dependencies():
44
+ return
46
45
 
47
46
  install_command = "pip install " + " ".join(missing)
48
47
  message = (