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.
- {msdev-0.14.1 → msdev-0.15.1}/PKG-INFO +47 -51
- {msdev-0.14.1 → msdev-0.15.1}/README.md +46 -50
- {msdev-0.14.1 → msdev-0.15.1}/pyproject.toml +1 -4
- {msdev-0.14.1 → msdev-0.15.1}/src/msdev/__init__.py +1 -1
- {msdev-0.14.1 → msdev-0.15.1}/src/msdev/cli.py +57 -35
- {msdev-0.14.1 → msdev-0.15.1}/src/msdev/core/compat.py +129 -19
- {msdev-0.14.1 → msdev-0.15.1}/src/msdev/core/config.py +0 -17
- msdev-0.15.1/src/msdev/core/direct_exec.py +178 -0
- msdev-0.15.1/src/msdev/core/env_exec.py +173 -0
- {msdev-0.14.1 → msdev-0.15.1}/src/msdev/core/guides.py +12 -0
- {msdev-0.14.1 → msdev-0.15.1}/src/msdev/core/inventory.py +1 -1
- {msdev-0.14.1 → msdev-0.15.1}/src/msdev/core/limits.py +19 -1
- {msdev-0.14.1 → msdev-0.15.1}/src/msdev/core/resources.py +50 -6
- {msdev-0.14.1 → msdev-0.15.1}/src/msdev/core/services/context.py +1 -6
- {msdev-0.14.1 → msdev-0.15.1}/src/msdev/core/services/environment.py +5 -6
- {msdev-0.14.1 → msdev-0.15.1}/src/msdev/core/services/node.py +106 -66
- {msdev-0.14.1 → msdev-0.15.1}/src/msdev/core/ssh_config.py +42 -0
- msdev-0.15.1/src/msdev/core/transport.py +1021 -0
- {msdev-0.14.1 → msdev-0.15.1}/src/msdev/skills/msdev-cli/SKILL.md +35 -20
- {msdev-0.14.1 → msdev-0.15.1}/src/msdev.egg-info/PKG-INFO +47 -51
- {msdev-0.14.1 → msdev-0.15.1}/src/msdev.egg-info/SOURCES.txt +6 -37
- {msdev-0.14.1 → msdev-0.15.1}/src/msdev.egg-info/entry_points.txt +0 -1
- {msdev-0.14.1 → msdev-0.15.1}/tests/test_cli.py +51 -18
- {msdev-0.14.1 → msdev-0.15.1}/tests/test_cli_only_architecture.py +1 -1
- msdev-0.15.1/tests/test_compat.py +99 -0
- {msdev-0.14.1 → msdev-0.15.1}/tests/test_config.py +0 -18
- {msdev-0.14.1 → msdev-0.15.1}/tests/test_config_bundle.py +3 -3
- msdev-0.15.1/tests/test_connect_env_scenario.py +75 -0
- msdev-0.15.1/tests/test_env_exec_scenario.py +55 -0
- {msdev-0.14.1 → msdev-0.15.1}/tests/test_env_transfer.py +5 -6
- {msdev-0.14.1 → msdev-0.15.1}/tests/test_exec_command_scenario.py +3 -55
- msdev-0.15.1/tests/test_exec_default_timeout_scenario.py +75 -0
- msdev-0.15.1/tests/test_live_dev122_dev186_scenario.py +196 -0
- msdev-0.15.1/tests/test_local_docker_ssh_smoke_scenario.py +1078 -0
- msdev-0.15.1/tests/test_node_bootstrap_version_scenario.py +77 -0
- {msdev-0.14.1 → msdev-0.15.1}/tests/test_node_connect_provision_scenario.py +1 -1
- {msdev-0.14.1 → msdev-0.15.1}/tests/test_node_connect_save_password_scenario.py +1 -1
- {msdev-0.14.1 → msdev-0.15.1}/tests/test_node_env_config.py +65 -0
- {msdev-0.14.1 → msdev-0.15.1}/tests/test_ssh_auth.py +15 -76
- {msdev-0.14.1 → msdev-0.15.1}/tests/test_target_exec_services.py +100 -36
- msdev-0.15.1/tests/test_transport.py +379 -0
- msdev-0.15.1/tests/test_windows_git_bash_ssh_scenario.py +45 -0
- {msdev-0.14.1 → msdev-0.15.1}/tests/test_windows_local_scenario.py +2 -19
- msdev-0.14.1/src/msdev/core/container_helper.py +0 -251
- msdev-0.14.1/src/msdev/core/daemon_deploy.py +0 -49
- msdev-0.14.1/src/msdev/core/daemon_version.py +0 -26
- msdev-0.14.1/src/msdev/core/helper_deploy.py +0 -143
- msdev-0.14.1/src/msdev/core/host_files.py +0 -92
- msdev-0.14.1/src/msdev/core/host_mutations.py +0 -332
- msdev-0.14.1/src/msdev/core/local_rpc.py +0 -85
- msdev-0.14.1/src/msdev/core/multiplex.py +0 -143
- msdev-0.14.1/src/msdev/core/request_client.py +0 -174
- msdev-0.14.1/src/msdev/core/ssh_tunnel.py +0 -561
- msdev-0.14.1/src/msdev/core/stream_limits.py +0 -17
- msdev-0.14.1/src/msdev/core/stream_server.py +0 -606
- msdev-0.14.1/src/msdev/core/stream_transport.py +0 -376
- msdev-0.14.1/src/msdev/core/streamproto.py +0 -129
- msdev-0.14.1/src/msdev/core/transport.py +0 -1694
- msdev-0.14.1/src/msdev/daemon.py +0 -1499
- msdev-0.14.1/src/msdev/helpers/msdev-file-helper-linux-amd64 +0 -0
- msdev-0.14.1/src/msdev/helpers/msdev-file-helper-linux-amd64.sha256 +0 -1
- msdev-0.14.1/src/msdev/helpers/msdev-file-helper-linux-arm64 +0 -0
- msdev-0.14.1/src/msdev/helpers/msdev-file-helper-linux-arm64.sha256 +0 -1
- msdev-0.14.1/src/msdev/helpers/msdevd-linux-amd64 +0 -0
- msdev-0.14.1/src/msdev/helpers/msdevd-linux-amd64.sha256 +0 -1
- msdev-0.14.1/src/msdev/helpers/msdevd-linux-arm64 +0 -0
- msdev-0.14.1/src/msdev/helpers/msdevd-linux-arm64.sha256 +0 -1
- msdev-0.14.1/tests/test_compat.py +0 -64
- msdev-0.14.1/tests/test_connect_env_scenario.py +0 -146
- msdev-0.14.1/tests/test_container_helper.py +0 -234
- msdev-0.14.1/tests/test_daemon.py +0 -1114
- msdev-0.14.1/tests/test_daemon_container_stream.py +0 -84
- msdev-0.14.1/tests/test_daemon_deploy.py +0 -148
- msdev-0.14.1/tests/test_docker_stream_dispatch.py +0 -237
- msdev-0.14.1/tests/test_exec_default_timeout_scenario.py +0 -128
- msdev-0.14.1/tests/test_go_msdevd_scenario.py +0 -209
- msdev-0.14.1/tests/test_helper_deploy.py +0 -185
- msdev-0.14.1/tests/test_multiplex.py +0 -199
- msdev-0.14.1/tests/test_node_bootstrap_version_scenario.py +0 -174
- msdev-0.14.1/tests/test_request_client.py +0 -241
- msdev-0.14.1/tests/test_ssh_tunnel.py +0 -57
- msdev-0.14.1/tests/test_stale_daemon_autobootstrap_scenario.py +0 -100
- msdev-0.14.1/tests/test_stream_server.py +0 -510
- msdev-0.14.1/tests/test_stream_transport.py +0 -314
- msdev-0.14.1/tests/test_streamproto.py +0 -65
- msdev-0.14.1/tests/test_transport.py +0 -1233
- {msdev-0.14.1 → msdev-0.15.1}/LICENSE +0 -0
- {msdev-0.14.1 → msdev-0.15.1}/setup.cfg +0 -0
- {msdev-0.14.1 → msdev-0.15.1}/src/msdev/core/__init__.py +0 -0
- {msdev-0.14.1 → msdev-0.15.1}/src/msdev/core/config_bundle.py +0 -0
- {msdev-0.14.1 → msdev-0.15.1}/src/msdev/core/env_transfer.py +0 -0
- {msdev-0.14.1 → msdev-0.15.1}/src/msdev/core/exec_argv.py +0 -0
- {msdev-0.14.1 → msdev-0.15.1}/src/msdev/core/init_setup.py +0 -0
- {msdev-0.14.1 → msdev-0.15.1}/src/msdev/core/services/__init__.py +0 -0
- {msdev-0.14.1 → msdev-0.15.1}/src/msdev/core/services/capabilities.py +0 -0
- {msdev-0.14.1 → msdev-0.15.1}/src/msdev/core/services/execution.py +0 -0
- {msdev-0.14.1 → msdev-0.15.1}/src/msdev/core/services/model.py +0 -0
- {msdev-0.14.1 → msdev-0.15.1}/src/msdev/core/services/npu.py +0 -0
- {msdev-0.14.1 → msdev-0.15.1}/src/msdev/core/services/results.py +0 -0
- {msdev-0.14.1 → msdev-0.15.1}/src/msdev/core/sftp_transfer.py +0 -0
- {msdev-0.14.1 → msdev-0.15.1}/src/msdev/core/skill_install.py +0 -0
- {msdev-0.14.1 → msdev-0.15.1}/src/msdev/core/ssh_auth.py +0 -0
- {msdev-0.14.1 → msdev-0.15.1}/src/msdev.egg-info/dependency_links.txt +0 -0
- {msdev-0.14.1 → msdev-0.15.1}/src/msdev.egg-info/top_level.txt +0 -0
- {msdev-0.14.1 → msdev-0.15.1}/tests/test_env_put_get_scenario.py +0 -0
- {msdev-0.14.1 → msdev-0.15.1}/tests/test_env_read_write_scenario.py +0 -0
- {msdev-0.14.1 → msdev-0.15.1}/tests/test_exec_argv.py +0 -0
- {msdev-0.14.1 → msdev-0.15.1}/tests/test_init_skill.py +0 -0
- {msdev-0.14.1 → msdev-0.15.1}/tests/test_inventory.py +0 -0
- {msdev-0.14.1 → msdev-0.15.1}/tests/test_model_cli.py +0 -0
- {msdev-0.14.1 → msdev-0.15.1}/tests/test_model_services.py +0 -0
- {msdev-0.14.1 → msdev-0.15.1}/tests/test_msdev_cli_skill.py +0 -0
- {msdev-0.14.1 → msdev-0.15.1}/tests/test_node_env_services.py +0 -0
- {msdev-0.14.1 → msdev-0.15.1}/tests/test_node_sync_ssh_scenario.py +0 -0
- {msdev-0.14.1 → msdev-0.15.1}/tests/test_sftp_transfer_scenario.py +0 -0
- {msdev-0.14.1 → msdev-0.15.1}/tests/test_ssh_config.py +0 -0
- {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.
|
|
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`** (
|
|
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
|
|
32
|
-
execution context: host or Docker runtime, conda/venv/uv layers,
|
|
33
|
-
`setup` script (root switch, proxy, etc.), command `wrap`, and static
|
|
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`
|
|
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
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
-
|
|
76
|
-
|
|
77
|
-
|
|
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`
|
|
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
|
|
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
|
|
115
|
-
|
|
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
|
|
119
|
-
|
|
120
|
-
|
|
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
|
-
|
|
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
|
-
|
|
128
|
-
|
|
122
|
+
msdev node status
|
|
123
|
+
msdev node status dev-122
|
|
129
124
|
```
|
|
130
125
|
|
|
131
|
-
|
|
132
|
-
|
|
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
|
|
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
|
|
263
|
-
|
|
264
|
-
|
|
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
|
-
->
|
|
273
|
+
-> UnixRpcTransport (local subprocess) or SshRpcTransport
|
|
274
|
+
-> SSH ControlMaster (when supported) + direct remote command / SFTP
|
|
278
275
|
```
|
|
279
276
|
|
|
280
|
-
Remote
|
|
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 →
|
|
291
|
-
|
|
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`** (
|
|
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
|
|
7
|
-
execution context: host or Docker runtime, conda/venv/uv layers,
|
|
8
|
-
`setup` script (root switch, proxy, etc.), command `wrap`, and static
|
|
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`
|
|
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
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
-
|
|
51
|
-
|
|
52
|
-
|
|
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`
|
|
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
|
|
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
|
|
90
|
-
|
|
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
|
|
94
|
-
|
|
95
|
-
|
|
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
|
-
|
|
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
|
-
|
|
103
|
-
|
|
97
|
+
msdev node status
|
|
98
|
+
msdev node status dev-122
|
|
104
99
|
```
|
|
105
100
|
|
|
106
|
-
|
|
107
|
-
|
|
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
|
|
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
|
|
238
|
-
|
|
239
|
-
|
|
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
|
-
->
|
|
248
|
+
-> UnixRpcTransport (local subprocess) or SshRpcTransport
|
|
249
|
+
-> SSH ControlMaster (when supported) + direct remote command / SFTP
|
|
253
250
|
```
|
|
254
251
|
|
|
255
|
-
Remote
|
|
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 →
|
|
266
|
-
|
|
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.
|
|
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
|
|
|
@@ -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}
|
|
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 == "
|
|
240
|
-
|
|
241
|
-
|
|
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
|
|
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.
|
|
285
|
-
suffix = suffix[:-1] + ",
|
|
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
|
-
|
|
619
|
-
|
|
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,
|
|
638
|
-
"and
|
|
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="
|
|
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(
|
|
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(
|