msdev 0.14.1__tar.gz → 0.15.1__tar.gz

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.
Files changed (117) hide show
  1. {msdev-0.14.1 → msdev-0.15.1}/PKG-INFO +47 -51
  2. {msdev-0.14.1 → msdev-0.15.1}/README.md +46 -50
  3. {msdev-0.14.1 → msdev-0.15.1}/pyproject.toml +1 -4
  4. {msdev-0.14.1 → msdev-0.15.1}/src/msdev/__init__.py +1 -1
  5. {msdev-0.14.1 → msdev-0.15.1}/src/msdev/cli.py +57 -35
  6. {msdev-0.14.1 → msdev-0.15.1}/src/msdev/core/compat.py +129 -19
  7. {msdev-0.14.1 → msdev-0.15.1}/src/msdev/core/config.py +0 -17
  8. msdev-0.15.1/src/msdev/core/direct_exec.py +178 -0
  9. msdev-0.15.1/src/msdev/core/env_exec.py +173 -0
  10. {msdev-0.14.1 → msdev-0.15.1}/src/msdev/core/guides.py +12 -0
  11. {msdev-0.14.1 → msdev-0.15.1}/src/msdev/core/inventory.py +1 -1
  12. {msdev-0.14.1 → msdev-0.15.1}/src/msdev/core/limits.py +19 -1
  13. {msdev-0.14.1 → msdev-0.15.1}/src/msdev/core/resources.py +50 -6
  14. {msdev-0.14.1 → msdev-0.15.1}/src/msdev/core/services/context.py +1 -6
  15. {msdev-0.14.1 → msdev-0.15.1}/src/msdev/core/services/environment.py +5 -6
  16. {msdev-0.14.1 → msdev-0.15.1}/src/msdev/core/services/node.py +106 -66
  17. {msdev-0.14.1 → msdev-0.15.1}/src/msdev/core/ssh_config.py +42 -0
  18. msdev-0.15.1/src/msdev/core/transport.py +1021 -0
  19. {msdev-0.14.1 → msdev-0.15.1}/src/msdev/skills/msdev-cli/SKILL.md +35 -20
  20. {msdev-0.14.1 → msdev-0.15.1}/src/msdev.egg-info/PKG-INFO +47 -51
  21. {msdev-0.14.1 → msdev-0.15.1}/src/msdev.egg-info/SOURCES.txt +6 -37
  22. {msdev-0.14.1 → msdev-0.15.1}/src/msdev.egg-info/entry_points.txt +0 -1
  23. {msdev-0.14.1 → msdev-0.15.1}/tests/test_cli.py +51 -18
  24. {msdev-0.14.1 → msdev-0.15.1}/tests/test_cli_only_architecture.py +1 -1
  25. msdev-0.15.1/tests/test_compat.py +99 -0
  26. {msdev-0.14.1 → msdev-0.15.1}/tests/test_config.py +0 -18
  27. {msdev-0.14.1 → msdev-0.15.1}/tests/test_config_bundle.py +3 -3
  28. msdev-0.15.1/tests/test_connect_env_scenario.py +75 -0
  29. msdev-0.15.1/tests/test_env_exec_scenario.py +55 -0
  30. {msdev-0.14.1 → msdev-0.15.1}/tests/test_env_transfer.py +5 -6
  31. {msdev-0.14.1 → msdev-0.15.1}/tests/test_exec_command_scenario.py +3 -55
  32. msdev-0.15.1/tests/test_exec_default_timeout_scenario.py +75 -0
  33. msdev-0.15.1/tests/test_live_dev122_dev186_scenario.py +196 -0
  34. msdev-0.15.1/tests/test_local_docker_ssh_smoke_scenario.py +1078 -0
  35. msdev-0.15.1/tests/test_node_bootstrap_version_scenario.py +77 -0
  36. {msdev-0.14.1 → msdev-0.15.1}/tests/test_node_connect_provision_scenario.py +1 -1
  37. {msdev-0.14.1 → msdev-0.15.1}/tests/test_node_connect_save_password_scenario.py +1 -1
  38. {msdev-0.14.1 → msdev-0.15.1}/tests/test_node_env_config.py +65 -0
  39. {msdev-0.14.1 → msdev-0.15.1}/tests/test_ssh_auth.py +15 -76
  40. {msdev-0.14.1 → msdev-0.15.1}/tests/test_target_exec_services.py +100 -36
  41. msdev-0.15.1/tests/test_transport.py +379 -0
  42. msdev-0.15.1/tests/test_windows_git_bash_ssh_scenario.py +45 -0
  43. {msdev-0.14.1 → msdev-0.15.1}/tests/test_windows_local_scenario.py +2 -19
  44. msdev-0.14.1/src/msdev/core/container_helper.py +0 -251
  45. msdev-0.14.1/src/msdev/core/daemon_deploy.py +0 -49
  46. msdev-0.14.1/src/msdev/core/daemon_version.py +0 -26
  47. msdev-0.14.1/src/msdev/core/helper_deploy.py +0 -143
  48. msdev-0.14.1/src/msdev/core/host_files.py +0 -92
  49. msdev-0.14.1/src/msdev/core/host_mutations.py +0 -332
  50. msdev-0.14.1/src/msdev/core/local_rpc.py +0 -85
  51. msdev-0.14.1/src/msdev/core/multiplex.py +0 -143
  52. msdev-0.14.1/src/msdev/core/request_client.py +0 -174
  53. msdev-0.14.1/src/msdev/core/ssh_tunnel.py +0 -561
  54. msdev-0.14.1/src/msdev/core/stream_limits.py +0 -17
  55. msdev-0.14.1/src/msdev/core/stream_server.py +0 -606
  56. msdev-0.14.1/src/msdev/core/stream_transport.py +0 -376
  57. msdev-0.14.1/src/msdev/core/streamproto.py +0 -129
  58. msdev-0.14.1/src/msdev/core/transport.py +0 -1694
  59. msdev-0.14.1/src/msdev/daemon.py +0 -1499
  60. msdev-0.14.1/src/msdev/helpers/msdev-file-helper-linux-amd64 +0 -0
  61. msdev-0.14.1/src/msdev/helpers/msdev-file-helper-linux-amd64.sha256 +0 -1
  62. msdev-0.14.1/src/msdev/helpers/msdev-file-helper-linux-arm64 +0 -0
  63. msdev-0.14.1/src/msdev/helpers/msdev-file-helper-linux-arm64.sha256 +0 -1
  64. msdev-0.14.1/src/msdev/helpers/msdevd-linux-amd64 +0 -0
  65. msdev-0.14.1/src/msdev/helpers/msdevd-linux-amd64.sha256 +0 -1
  66. msdev-0.14.1/src/msdev/helpers/msdevd-linux-arm64 +0 -0
  67. msdev-0.14.1/src/msdev/helpers/msdevd-linux-arm64.sha256 +0 -1
  68. msdev-0.14.1/tests/test_compat.py +0 -64
  69. msdev-0.14.1/tests/test_connect_env_scenario.py +0 -146
  70. msdev-0.14.1/tests/test_container_helper.py +0 -234
  71. msdev-0.14.1/tests/test_daemon.py +0 -1114
  72. msdev-0.14.1/tests/test_daemon_container_stream.py +0 -84
  73. msdev-0.14.1/tests/test_daemon_deploy.py +0 -148
  74. msdev-0.14.1/tests/test_docker_stream_dispatch.py +0 -237
  75. msdev-0.14.1/tests/test_exec_default_timeout_scenario.py +0 -128
  76. msdev-0.14.1/tests/test_go_msdevd_scenario.py +0 -209
  77. msdev-0.14.1/tests/test_helper_deploy.py +0 -185
  78. msdev-0.14.1/tests/test_multiplex.py +0 -199
  79. msdev-0.14.1/tests/test_node_bootstrap_version_scenario.py +0 -174
  80. msdev-0.14.1/tests/test_request_client.py +0 -241
  81. msdev-0.14.1/tests/test_ssh_tunnel.py +0 -57
  82. msdev-0.14.1/tests/test_stale_daemon_autobootstrap_scenario.py +0 -100
  83. msdev-0.14.1/tests/test_stream_server.py +0 -510
  84. msdev-0.14.1/tests/test_stream_transport.py +0 -314
  85. msdev-0.14.1/tests/test_streamproto.py +0 -65
  86. msdev-0.14.1/tests/test_transport.py +0 -1233
  87. {msdev-0.14.1 → msdev-0.15.1}/LICENSE +0 -0
  88. {msdev-0.14.1 → msdev-0.15.1}/setup.cfg +0 -0
  89. {msdev-0.14.1 → msdev-0.15.1}/src/msdev/core/__init__.py +0 -0
  90. {msdev-0.14.1 → msdev-0.15.1}/src/msdev/core/config_bundle.py +0 -0
  91. {msdev-0.14.1 → msdev-0.15.1}/src/msdev/core/env_transfer.py +0 -0
  92. {msdev-0.14.1 → msdev-0.15.1}/src/msdev/core/exec_argv.py +0 -0
  93. {msdev-0.14.1 → msdev-0.15.1}/src/msdev/core/init_setup.py +0 -0
  94. {msdev-0.14.1 → msdev-0.15.1}/src/msdev/core/services/__init__.py +0 -0
  95. {msdev-0.14.1 → msdev-0.15.1}/src/msdev/core/services/capabilities.py +0 -0
  96. {msdev-0.14.1 → msdev-0.15.1}/src/msdev/core/services/execution.py +0 -0
  97. {msdev-0.14.1 → msdev-0.15.1}/src/msdev/core/services/model.py +0 -0
  98. {msdev-0.14.1 → msdev-0.15.1}/src/msdev/core/services/npu.py +0 -0
  99. {msdev-0.14.1 → msdev-0.15.1}/src/msdev/core/services/results.py +0 -0
  100. {msdev-0.14.1 → msdev-0.15.1}/src/msdev/core/sftp_transfer.py +0 -0
  101. {msdev-0.14.1 → msdev-0.15.1}/src/msdev/core/skill_install.py +0 -0
  102. {msdev-0.14.1 → msdev-0.15.1}/src/msdev/core/ssh_auth.py +0 -0
  103. {msdev-0.14.1 → msdev-0.15.1}/src/msdev.egg-info/dependency_links.txt +0 -0
  104. {msdev-0.14.1 → msdev-0.15.1}/src/msdev.egg-info/top_level.txt +0 -0
  105. {msdev-0.14.1 → msdev-0.15.1}/tests/test_env_put_get_scenario.py +0 -0
  106. {msdev-0.14.1 → msdev-0.15.1}/tests/test_env_read_write_scenario.py +0 -0
  107. {msdev-0.14.1 → msdev-0.15.1}/tests/test_exec_argv.py +0 -0
  108. {msdev-0.14.1 → msdev-0.15.1}/tests/test_init_skill.py +0 -0
  109. {msdev-0.14.1 → msdev-0.15.1}/tests/test_inventory.py +0 -0
  110. {msdev-0.14.1 → msdev-0.15.1}/tests/test_model_cli.py +0 -0
  111. {msdev-0.14.1 → msdev-0.15.1}/tests/test_model_services.py +0 -0
  112. {msdev-0.14.1 → msdev-0.15.1}/tests/test_msdev_cli_skill.py +0 -0
  113. {msdev-0.14.1 → msdev-0.15.1}/tests/test_node_env_services.py +0 -0
  114. {msdev-0.14.1 → msdev-0.15.1}/tests/test_node_sync_ssh_scenario.py +0 -0
  115. {msdev-0.14.1 → msdev-0.15.1}/tests/test_sftp_transfer_scenario.py +0 -0
  116. {msdev-0.14.1 → msdev-0.15.1}/tests/test_ssh_config.py +0 -0
  117. {msdev-0.14.1 → msdev-0.15.1}/tests/test_target_guides.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: msdev
3
- Version: 0.14.1
3
+ Version: 0.15.1
4
4
  Summary: Portable nodes and execution environments for msModelSlim development
5
5
  Author: msModelSlim Agent Contributors
6
6
  License-Expression: MulanPSL-2.0
@@ -25,12 +25,13 @@ Dynamic: license-file
25
25
 
26
26
  # msdev
27
27
 
28
- `msdev` exposes three capabilities: **`node`** (connect + host agent),
28
+ `msdev` exposes three capabilities: **`node`** (SSH + connection reuse),
29
29
  **`env`** (shared execution context, shell, files), and **`exec`** (commands).
30
30
 
31
- A Node owns SSH reachability and `msdevd` deployment. An Env owns the full
32
- execution context: host or Docker runtime, conda/venv/uv layers, optional
33
- `setup` script (root switch, proxy, etc.), command `wrap`, and static env vars.
31
+ A Node owns SSH reachability and optional ControlMaster multiplexing. An Env
32
+ owns the full execution context: host or Docker runtime, conda/venv/uv layers,
33
+ optional `setup` script (root switch, proxy, etc.), command `wrap`, and static
34
+ env vars. Commands run over direct SSH (no remote `msdevd`).
34
35
 
35
36
  The CLI is intentionally stateless: execution always names the env as the first
36
37
  `exec` argument.
@@ -45,7 +46,7 @@ python3 -m pip install -e .
45
46
  msdev --help
46
47
  ```
47
48
 
48
- The package installs `msdev` and `msdevd`.
49
+ The package installs the `msdev` CLI.
49
50
 
50
51
  Install the agent skill for Cursor and Claude Code, and sync SSH config:
51
52
 
@@ -63,43 +64,39 @@ overwrite; use `-f` to force.
63
64
 
64
65
  ## Windows client
65
66
 
66
- The typical setup is a **Windows laptop talking to Linux Nodes** via OpenSSH
67
- (`C:\Windows\System32\OpenSSH\ssh.exe`). Native Windows OpenSSH does not
68
- implement ControlMaster multiplexing, so msdev:
69
-
70
- - omits `ControlMaster`/`ControlPath` on Windows (override with
71
- `MSDEV_SSH_CONTROL_MASTER=1` only if your SSH client actually supports mux);
72
- - starts a persistent `ssh -L` LocalForward tunnel automatically on the first
73
- RPC (same role as ControlMaster). Explicit `msdev node connect NODE` is
74
- optional; disable persist with `MSDEV_SSH_LOCAL_FORWARD_PERSIST=0`;
75
- - reuses that tunnel for captured RPC and default streaming `msdev exec`;
76
- `env put`/`get` stay on sftp/ssh streams (file bytes do not go through JSON RPC);
77
- - hides SSH console windows (`CREATE_NO_WINDOW`) and kills process trees with
78
- `taskkill /T` on timeout or cancel;
67
+ The typical setup is a **Windows laptop talking to Linux Nodes**. Native
68
+ `C:\Windows\System32\OpenSSH\ssh.exe` does **not** support ControlMaster.
69
+ msdev therefore prefers **Git for Windows** OpenSSH when installed
70
+ (`C:\Program Files\Git\usr\bin\ssh.exe`), which does:
71
+
72
+ - auto-selects Git Bash `ssh`/`sftp`/`ssh-keygen` over PATH / System32 OpenSSH;
73
+ override with `MSDEV_SSH` / `MSDEV_SFTP` / `MSDEV_SSH_KEYGEN`;
74
+ - enables ControlMaster when the resolved client is Git/MSYS OpenSSH (disable
75
+ with `MSDEV_SSH_CONTROL_MASTER=0`, force with `=1`);
76
+ - falls back to System32 OpenSSH without mux if Git is not installed;
77
+ - hides SSH console windows (`CREATE_NO_WINDOW`) and kills local process trees
78
+ with `taskkill /T` on timeout or cancel;
79
79
  - SSH is non-interactive: `BatchMode` unless a password is supplied, host keys
80
80
  use `accept-new`, and there is no password/passphrase/yes-no prompt. When a
81
81
  password is known, injects OpenSSH `SSH_ASKPASS` so `node connect` (and later
82
82
  ssh/sftp) can authenticate without a TTY; the local public key is then
83
83
  installed on the Node so later logins are passwordless. The password is
84
- never written to the node registry;
85
- - serves a TCP loopback endpoint for local `msdevd` when this Python build has
86
- no `AF_UNIX` sockets.
84
+ never written to the node registry.
87
85
 
88
86
  Install with `python -m pip install -e .`. `install-user.py` writes
89
- `msdev.cmd` / `msdevd.cmd` under `%USERPROFILE%\.local\bin`.
87
+ `msdev.cmd` under `%USERPROFILE%\.local\bin`.
90
88
 
91
89
  Remote paths inside a Linux Env remain POSIX (`/srv/...`). Local `env put`/`get`
92
90
  on Windows accept Windows absolute paths (`C:\...`).
93
91
 
94
92
  Local `exec` on Windows wraps Conda with `conda run` and venvs with
95
93
  `Scripts\activate.bat` (remote Linux Nodes still use POSIX `sh`/`bash` wrappers).
96
- If `ssh` is not on PATH, msdev looks in `%SystemRoot%\System32\OpenSSH\`.
97
94
 
98
95
  ## Nodes and execution environments
99
96
 
100
97
  Use **`node connect`** as the single entry point: it writes `~/.ssh/config`
101
98
  when needed, registers the node, saves passwords, installs the local public
102
- key, and deploys `msdevd`:
99
+ key, and opens an SSH ControlMaster when multiplexing is supported:
103
100
 
104
101
  ```bash
105
102
  msdev node connect dev-122 --hostname 80.48.33.133 --user root --password PASSWORD
@@ -111,25 +108,27 @@ msdev node list
111
108
  ```
112
109
 
113
110
  Connect writes or updates a dedicated `Host <name>` block in OpenSSH config.
114
- The msdev registry stores only the Host alias plus msdevd settings
115
- (`remote_bin`, `auto_bootstrap`). User, port, keys, and proxy remain in SSH
116
- config. `node sync-ssh` registers Host aliases that were added manually.
111
+ The msdev registry stores the Host alias. User, port, keys, and proxy remain in
112
+ SSH config. `node sync-ssh` registers Host aliases that were added manually.
117
113
 
118
- Connect atomically creates a same-named host Env on first use and uploads a
119
- **static Go msdevd** binary matching the node architecture
120
- (`linux-amd64` / `linux-arm64`) when the agent is missing or when the remote
121
- `ping` version does not match the local CLI. Use **`msdev node bootstrap NODE`**
122
- to force a redeploy after upgrading msdev. No Python is required on the node.
114
+ Connect atomically creates a same-named host Env on first use. There is no
115
+ remote agent binary to deploy. Use **`msdev node bootstrap NODE`** only to
116
+ re-check SSH readiness after connect problems.
123
117
 
124
- Build the static daemons before release or local testing:
118
+ Inspect connectivity for one node or for every registered node (probes run in
119
+ parallel; partial failures exit non-zero):
125
120
 
126
121
  ```bash
127
- bash msdevd/build-static.sh
128
- bash file-helper/build-static.sh
122
+ msdev node status
123
+ msdev node status dev-122
129
124
  ```
130
125
 
131
- Packaged artifacts live under `src/msdev/helpers/` (gitignored until built).
132
- The Go source is in `msdev/`.
126
+ Rename a node together with its same-named host Env, guide files, and SSH
127
+ `Host` alias:
128
+
129
+ ```bash
130
+ msdev node rename dev-122 lab-122
131
+ ```
133
132
 
134
133
  Persistent OpenSSH masters are optional. All SSH from msdev is
135
134
  non-interactive: host keys use `accept-new`, and password/passphrase prompts
@@ -179,11 +178,11 @@ msdev env add dev-122-root \
179
178
  msdev env open dev-122-root
180
179
  ```
181
180
 
182
- Open a human-operated interactive shell in an Env:
181
+ Open an interactive shell in an env. Docker envs enter the container:
183
182
 
184
183
  ```bash
185
184
  msdev env shell dev-122
186
- msdev env shell dev-122 --cwd /srv/project
185
+ msdev env shell dev-122-docker --cwd /srv/project
187
186
  ```
188
187
 
189
188
  Agent automation should use `msdev exec`, not `env shell`.
@@ -259,11 +258,9 @@ CRLF on a Windows console so remote line output does not staircase; piped
259
258
  CLI output keeps LF.
260
259
 
261
260
  `--timeout-seconds` defaults to `-1` (no deadline). Any positive finite duration
262
- is also accepted. A positive timeout kills the complete command
263
- process group and returns exit code 124. Cancelling the local CLI closes the RPC
264
- connection, causing `msdevd` to kill and reap the remote process group. Finite
265
- SSH/Unix RPC deadlines include a short completion grace for output drain and
266
- serialization.
261
+ is also accepted. A positive timeout kills the local SSH/command process tree
262
+ and returns a timed-out result. Cancelling the local CLI closes the SSH
263
+ session (best-effort remote cancel).
267
264
 
268
265
  `--result-json` selects captured, non-streaming execution and prints one
269
266
  structured JSON result containing stdout and stderr.
@@ -273,12 +270,11 @@ structured JSON result containing stdout and stderr.
273
270
  ```text
274
271
  msdev CLI
275
272
  -> typed core service
276
- -> UnixRpcTransport or SshRpcTransport
277
- -> per-user msdevd (env session + job runner + files)
273
+ -> UnixRpcTransport (local subprocess) or SshRpcTransport
274
+ -> SSH ControlMaster (when supported) + direct remote command / SFTP
278
275
  ```
279
276
 
280
- Remote RPC responses and command output are bounded. Node/Env registries use
281
- locked atomic updates.
277
+ Remote command output is bounded. Node/Env registries use locked atomic updates.
282
278
 
283
279
  ## Testing
284
280
 
@@ -287,5 +283,5 @@ python3 -m unittest discover -s tests -v
287
283
  ```
288
284
 
289
285
  Feature work must include at least one actual scenario test on the real
290
- control path (`msdev` CLI → service → transport → `msdevd` → effect). Unit
291
- tests alone are not sufficient.
286
+ control path (`msdev` CLI → service → SSH/SFTP → effect). Unit tests alone
287
+ are not sufficient.
@@ -1,11 +1,12 @@
1
1
  # msdev
2
2
 
3
- `msdev` exposes three capabilities: **`node`** (connect + host agent),
3
+ `msdev` exposes three capabilities: **`node`** (SSH + connection reuse),
4
4
  **`env`** (shared execution context, shell, files), and **`exec`** (commands).
5
5
 
6
- A Node owns SSH reachability and `msdevd` deployment. An Env owns the full
7
- execution context: host or Docker runtime, conda/venv/uv layers, optional
8
- `setup` script (root switch, proxy, etc.), command `wrap`, and static env vars.
6
+ A Node owns SSH reachability and optional ControlMaster multiplexing. An Env
7
+ owns the full execution context: host or Docker runtime, conda/venv/uv layers,
8
+ optional `setup` script (root switch, proxy, etc.), command `wrap`, and static
9
+ env vars. Commands run over direct SSH (no remote `msdevd`).
9
10
 
10
11
  The CLI is intentionally stateless: execution always names the env as the first
11
12
  `exec` argument.
@@ -20,7 +21,7 @@ python3 -m pip install -e .
20
21
  msdev --help
21
22
  ```
22
23
 
23
- The package installs `msdev` and `msdevd`.
24
+ The package installs the `msdev` CLI.
24
25
 
25
26
  Install the agent skill for Cursor and Claude Code, and sync SSH config:
26
27
 
@@ -38,43 +39,39 @@ overwrite; use `-f` to force.
38
39
 
39
40
  ## Windows client
40
41
 
41
- The typical setup is a **Windows laptop talking to Linux Nodes** via OpenSSH
42
- (`C:\Windows\System32\OpenSSH\ssh.exe`). Native Windows OpenSSH does not
43
- implement ControlMaster multiplexing, so msdev:
44
-
45
- - omits `ControlMaster`/`ControlPath` on Windows (override with
46
- `MSDEV_SSH_CONTROL_MASTER=1` only if your SSH client actually supports mux);
47
- - starts a persistent `ssh -L` LocalForward tunnel automatically on the first
48
- RPC (same role as ControlMaster). Explicit `msdev node connect NODE` is
49
- optional; disable persist with `MSDEV_SSH_LOCAL_FORWARD_PERSIST=0`;
50
- - reuses that tunnel for captured RPC and default streaming `msdev exec`;
51
- `env put`/`get` stay on sftp/ssh streams (file bytes do not go through JSON RPC);
52
- - hides SSH console windows (`CREATE_NO_WINDOW`) and kills process trees with
53
- `taskkill /T` on timeout or cancel;
42
+ The typical setup is a **Windows laptop talking to Linux Nodes**. Native
43
+ `C:\Windows\System32\OpenSSH\ssh.exe` does **not** support ControlMaster.
44
+ msdev therefore prefers **Git for Windows** OpenSSH when installed
45
+ (`C:\Program Files\Git\usr\bin\ssh.exe`), which does:
46
+
47
+ - auto-selects Git Bash `ssh`/`sftp`/`ssh-keygen` over PATH / System32 OpenSSH;
48
+ override with `MSDEV_SSH` / `MSDEV_SFTP` / `MSDEV_SSH_KEYGEN`;
49
+ - enables ControlMaster when the resolved client is Git/MSYS OpenSSH (disable
50
+ with `MSDEV_SSH_CONTROL_MASTER=0`, force with `=1`);
51
+ - falls back to System32 OpenSSH without mux if Git is not installed;
52
+ - hides SSH console windows (`CREATE_NO_WINDOW`) and kills local process trees
53
+ with `taskkill /T` on timeout or cancel;
54
54
  - SSH is non-interactive: `BatchMode` unless a password is supplied, host keys
55
55
  use `accept-new`, and there is no password/passphrase/yes-no prompt. When a
56
56
  password is known, injects OpenSSH `SSH_ASKPASS` so `node connect` (and later
57
57
  ssh/sftp) can authenticate without a TTY; the local public key is then
58
58
  installed on the Node so later logins are passwordless. The password is
59
- never written to the node registry;
60
- - serves a TCP loopback endpoint for local `msdevd` when this Python build has
61
- no `AF_UNIX` sockets.
59
+ never written to the node registry.
62
60
 
63
61
  Install with `python -m pip install -e .`. `install-user.py` writes
64
- `msdev.cmd` / `msdevd.cmd` under `%USERPROFILE%\.local\bin`.
62
+ `msdev.cmd` under `%USERPROFILE%\.local\bin`.
65
63
 
66
64
  Remote paths inside a Linux Env remain POSIX (`/srv/...`). Local `env put`/`get`
67
65
  on Windows accept Windows absolute paths (`C:\...`).
68
66
 
69
67
  Local `exec` on Windows wraps Conda with `conda run` and venvs with
70
68
  `Scripts\activate.bat` (remote Linux Nodes still use POSIX `sh`/`bash` wrappers).
71
- If `ssh` is not on PATH, msdev looks in `%SystemRoot%\System32\OpenSSH\`.
72
69
 
73
70
  ## Nodes and execution environments
74
71
 
75
72
  Use **`node connect`** as the single entry point: it writes `~/.ssh/config`
76
73
  when needed, registers the node, saves passwords, installs the local public
77
- key, and deploys `msdevd`:
74
+ key, and opens an SSH ControlMaster when multiplexing is supported:
78
75
 
79
76
  ```bash
80
77
  msdev node connect dev-122 --hostname 80.48.33.133 --user root --password PASSWORD
@@ -86,25 +83,27 @@ msdev node list
86
83
  ```
87
84
 
88
85
  Connect writes or updates a dedicated `Host <name>` block in OpenSSH config.
89
- The msdev registry stores only the Host alias plus msdevd settings
90
- (`remote_bin`, `auto_bootstrap`). User, port, keys, and proxy remain in SSH
91
- config. `node sync-ssh` registers Host aliases that were added manually.
86
+ The msdev registry stores the Host alias. User, port, keys, and proxy remain in
87
+ SSH config. `node sync-ssh` registers Host aliases that were added manually.
92
88
 
93
- Connect atomically creates a same-named host Env on first use and uploads a
94
- **static Go msdevd** binary matching the node architecture
95
- (`linux-amd64` / `linux-arm64`) when the agent is missing or when the remote
96
- `ping` version does not match the local CLI. Use **`msdev node bootstrap NODE`**
97
- to force a redeploy after upgrading msdev. No Python is required on the node.
89
+ Connect atomically creates a same-named host Env on first use. There is no
90
+ remote agent binary to deploy. Use **`msdev node bootstrap NODE`** only to
91
+ re-check SSH readiness after connect problems.
98
92
 
99
- Build the static daemons before release or local testing:
93
+ Inspect connectivity for one node or for every registered node (probes run in
94
+ parallel; partial failures exit non-zero):
100
95
 
101
96
  ```bash
102
- bash msdevd/build-static.sh
103
- bash file-helper/build-static.sh
97
+ msdev node status
98
+ msdev node status dev-122
104
99
  ```
105
100
 
106
- Packaged artifacts live under `src/msdev/helpers/` (gitignored until built).
107
- The Go source is in `msdev/`.
101
+ Rename a node together with its same-named host Env, guide files, and SSH
102
+ `Host` alias:
103
+
104
+ ```bash
105
+ msdev node rename dev-122 lab-122
106
+ ```
108
107
 
109
108
  Persistent OpenSSH masters are optional. All SSH from msdev is
110
109
  non-interactive: host keys use `accept-new`, and password/passphrase prompts
@@ -154,11 +153,11 @@ msdev env add dev-122-root \
154
153
  msdev env open dev-122-root
155
154
  ```
156
155
 
157
- Open a human-operated interactive shell in an Env:
156
+ Open an interactive shell in an env. Docker envs enter the container:
158
157
 
159
158
  ```bash
160
159
  msdev env shell dev-122
161
- msdev env shell dev-122 --cwd /srv/project
160
+ msdev env shell dev-122-docker --cwd /srv/project
162
161
  ```
163
162
 
164
163
  Agent automation should use `msdev exec`, not `env shell`.
@@ -234,11 +233,9 @@ CRLF on a Windows console so remote line output does not staircase; piped
234
233
  CLI output keeps LF.
235
234
 
236
235
  `--timeout-seconds` defaults to `-1` (no deadline). Any positive finite duration
237
- is also accepted. A positive timeout kills the complete command
238
- process group and returns exit code 124. Cancelling the local CLI closes the RPC
239
- connection, causing `msdevd` to kill and reap the remote process group. Finite
240
- SSH/Unix RPC deadlines include a short completion grace for output drain and
241
- serialization.
236
+ is also accepted. A positive timeout kills the local SSH/command process tree
237
+ and returns a timed-out result. Cancelling the local CLI closes the SSH
238
+ session (best-effort remote cancel).
242
239
 
243
240
  `--result-json` selects captured, non-streaming execution and prints one
244
241
  structured JSON result containing stdout and stderr.
@@ -248,12 +245,11 @@ structured JSON result containing stdout and stderr.
248
245
  ```text
249
246
  msdev CLI
250
247
  -> typed core service
251
- -> UnixRpcTransport or SshRpcTransport
252
- -> per-user msdevd (env session + job runner + files)
248
+ -> UnixRpcTransport (local subprocess) or SshRpcTransport
249
+ -> SSH ControlMaster (when supported) + direct remote command / SFTP
253
250
  ```
254
251
 
255
- Remote RPC responses and command output are bounded. Node/Env registries use
256
- locked atomic updates.
252
+ Remote command output is bounded. Node/Env registries use locked atomic updates.
257
253
 
258
254
  ## Testing
259
255
 
@@ -262,5 +258,5 @@ python3 -m unittest discover -s tests -v
262
258
  ```
263
259
 
264
260
  Feature work must include at least one actual scenario test on the real
265
- control path (`msdev` CLI → service → transport → `msdevd` → effect). Unit
266
- tests alone are not sufficient.
261
+ control path (`msdev` CLI → service → SSH/SFTP → effect). Unit tests alone
262
+ are not sufficient.
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "msdev"
7
- version = "0.14.1"
7
+ version = "0.15.1"
8
8
  description = "Portable nodes and execution environments for msModelSlim development"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -35,7 +35,6 @@ Repository = "https://gitcode.com/rookie_hongchuan/msdev"
35
35
 
36
36
  [project.scripts]
37
37
  msdev = "msdev.cli:main"
38
- msdevd = "msdev.daemon:main"
39
38
 
40
39
  [tool.setuptools]
41
40
  package-dir = { "" = "src" }
@@ -45,8 +44,6 @@ where = ["src"]
45
44
 
46
45
  [tool.setuptools.package-data]
47
46
  msdev = [
48
- "helpers/msdev-file-helper-*",
49
- "helpers/msdevd-linux-*",
50
47
  "skills/msdev-cli/SKILL.md",
51
48
  ]
52
49
 
@@ -1,3 +1,3 @@
1
1
  """Portable node and execution-environment tooling for msModelSlim."""
2
2
 
3
- __version__ = "0.14.1"
3
+ __version__ = "0.15.1"
@@ -154,8 +154,6 @@ def _provision_request_for_connect(
154
154
  if ssh_host_defined(args.name):
155
155
  return NodeAddRequest(
156
156
  name=args.name,
157
- remote_bin=args.remote_bin,
158
- auto_bootstrap=not args.no_auto_bootstrap,
159
157
  force=args.force,
160
158
  )
161
159
  raise ValueError(
@@ -169,8 +167,6 @@ def _provision_request_for_connect(
169
167
  port=args.port,
170
168
  identity_file=args.identity_file,
171
169
  ssh_host=args.ssh_host,
172
- remote_bin=args.remote_bin,
173
- auto_bootstrap=not args.no_auto_bootstrap,
174
170
  force=bool(
175
171
  args.force or has_params
176
172
  ),
@@ -204,9 +200,7 @@ def command_node(
204
200
  print("local\tregistered\t-")
205
201
  for node in service.list().nodes:
206
202
  print(
207
- f"{node.name}\tregistered\tssh={node.ssh_host}\t"
208
- f"auto-bootstrap={'on' if node.auto_bootstrap else 'off'}\t"
209
- f"bin={node.remote_bin}"
203
+ f"{node.name}\tregistered\tssh={node.ssh_host}"
210
204
  )
211
205
  if args.ssh:
212
206
  for host in service.list_ssh_hosts():
@@ -236,12 +230,39 @@ def command_node(
236
230
  if removed:
237
231
  print("removed environments: " + ", ".join(removed))
238
232
  return 0
239
- if action == "status":
240
- print(f"node: {args.name}")
241
- _print_json(service.status(args.name))
233
+ if action == "rename":
234
+ node = service.rename(args.name, args.new_name)
235
+ print(f"renamed node {args.name} -> {node.name} (ssh={node.ssh_host})")
242
236
  return 0
237
+ if action == "status":
238
+ if args.name:
239
+ print(f"node: {args.name}")
240
+ _print_json(service.status(args.name))
241
+ return 0
242
+ result = service.status_all()
243
+ for item in result.items:
244
+ if item.error is not None:
245
+ print(
246
+ f"{item.node} ({item.ssh_host}): unreachable: {item.error}",
247
+ file=sys.stderr,
248
+ )
249
+ else:
250
+ info = item.info if isinstance(item.info, dict) else {}
251
+ parts: list[str] = []
252
+ hostname = str(info.get("hostname") or "")
253
+ ip = str(info.get("ip") or "")
254
+ target = str(info.get("target") or "")
255
+ if hostname:
256
+ parts.append(f"hostname={hostname}")
257
+ if ip:
258
+ parts.append(f"ip={ip}")
259
+ if target and target not in {hostname, ip, item.ssh_host}:
260
+ parts.append(f"target={target}")
261
+ detail = (" " + " ".join(parts)) if parts else ""
262
+ print(f"{item.node} ({item.ssh_host}): reachable{detail}")
263
+ return result.exit_code
243
264
  if action == "bootstrap":
244
- result = service.bootstrap(args.name, package_path=args.package)
265
+ result = service.bootstrap(args.name)
245
266
  _print_json(result)
246
267
  return 0
247
268
  if action in {"connect", "disconnect"}:
@@ -281,8 +302,8 @@ def command_node(
281
302
  key = getattr(item, "key", None)
282
303
  if key:
283
304
  suffix = f"(persist={item.persist}, key={key})"
284
- if item.agent_ready:
285
- suffix = suffix[:-1] + ", agent=ready)"
305
+ if item.ssh_ready:
306
+ suffix = suffix[:-1] + ", ssh=ready)"
286
307
  print(
287
308
  f"{item.node}: {item.state} to {item.ssh_host} "
288
309
  f"{suffix}"
@@ -615,27 +636,31 @@ def build_parser() -> argparse.ArgumentParser:
615
636
  action="store_true",
616
637
  help="also remove every environment on this node",
617
638
  )
618
- node_status = node_sub.add_parser("status")
619
- node_status.add_argument("name")
639
+ node_rename = node_sub.add_parser(
640
+ "rename",
641
+ help="rename a node, its host environment, and a matching SSH Host alias",
642
+ )
643
+ node_rename.add_argument("name")
644
+ node_rename.add_argument("new_name")
645
+ node_status = node_sub.add_parser(
646
+ "status",
647
+ help="check node connectivity (every registered SSH node by default)",
648
+ )
649
+ node_status.add_argument(
650
+ "name",
651
+ nargs="?",
652
+ help="check one node; omit to check every registered SSH node",
653
+ )
620
654
  node_bootstrap = node_sub.add_parser(
621
655
  "bootstrap",
622
- help=(
623
- "force-redeploy msdevd on the node "
624
- "(use after local upgrade when remote version mismatches)"
625
- ),
656
+ help="ensure SSH reachability / ControlMaster for the node",
626
657
  )
627
658
  node_bootstrap.add_argument("name")
628
- node_bootstrap.add_argument(
629
- "--package",
630
- type=Path,
631
- default=None,
632
- help="optional source checkout for source-layout bootstrap",
633
- )
634
659
  node_connect = node_sub.add_parser(
635
660
  "connect",
636
661
  help=(
637
- "register a node if needed, write ~/.ssh/config, connect, "
638
- "and deploy/start msdevd when needed"
662
+ "register a node if needed, write ~/.ssh/config, "
663
+ "and open an SSH ControlMaster when supported"
639
664
  ),
640
665
  )
641
666
  node_connect.add_argument(
@@ -662,17 +687,11 @@ def build_parser() -> argparse.ArgumentParser:
662
687
  "--ssh-host",
663
688
  help="legacy shorthand for user@hostname when writing ~/.ssh/config",
664
689
  )
665
- node_connect.add_argument("--remote-bin", default="~/.local/bin/msdevd")
666
690
  node_connect.add_argument(
667
691
  "--force",
668
692
  action="store_true",
669
693
  help="replace an existing node and its same-named host environment",
670
694
  )
671
- node_connect.add_argument(
672
- "--no-auto-bootstrap",
673
- action="store_true",
674
- help="do not install/start msdevd automatically on first use",
675
- )
676
695
  node_connect.add_argument(
677
696
  "--password",
678
697
  default=None,
@@ -752,7 +771,7 @@ def build_parser() -> argparse.ArgumentParser:
752
771
  environment_inspect.add_argument("name")
753
772
  environment_open = environment_sub.add_parser(
754
773
  "open",
755
- help="establish the shared environment session (runs setup once)",
774
+ help="check that the environment is ready (setup runs on each exec)",
756
775
  )
757
776
  environment_open.add_argument("name")
758
777
  environment_guide = environment_sub.add_parser("guide")
@@ -760,7 +779,10 @@ def build_parser() -> argparse.ArgumentParser:
760
779
  environment_guide_action = environment_guide.add_mutually_exclusive_group()
761
780
  environment_guide_action.add_argument("--write", metavar="FILE")
762
781
  environment_guide_action.add_argument("--delete", action="store_true")
763
- environment_shell = environment_sub.add_parser("shell")
782
+ environment_shell = environment_sub.add_parser(
783
+ "shell",
784
+ help="open an interactive shell in the environment (including Docker)",
785
+ )
764
786
  environment_shell.add_argument("name")
765
787
  environment_shell.add_argument("--cwd")
766
788
  environment_put = environment_sub.add_parser(