msdev 0.12.0__tar.gz → 0.13.0__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 (106) hide show
  1. {msdev-0.12.0 → msdev-0.13.0}/PKG-INFO +77 -33
  2. {msdev-0.12.0 → msdev-0.13.0}/README.md +76 -32
  3. {msdev-0.12.0 → msdev-0.13.0}/pyproject.toml +6 -2
  4. {msdev-0.12.0 → msdev-0.13.0}/src/msdev/__init__.py +1 -1
  5. {msdev-0.12.0 → msdev-0.13.0}/src/msdev/cli.py +378 -67
  6. {msdev-0.12.0 → msdev-0.13.0}/src/msdev/core/compat.py +6 -6
  7. msdev-0.13.0/src/msdev/core/config_bundle.py +338 -0
  8. {msdev-0.12.0 → msdev-0.13.0}/src/msdev/core/env_transfer.py +30 -29
  9. msdev-0.13.0/src/msdev/core/exec_argv.py +196 -0
  10. msdev-0.13.0/src/msdev/core/init_setup.py +42 -0
  11. {msdev-0.12.0 → msdev-0.13.0}/src/msdev/core/services/node.py +117 -3
  12. msdev-0.13.0/src/msdev/core/sftp_transfer.py +120 -0
  13. msdev-0.13.0/src/msdev/core/skill_install.py +258 -0
  14. {msdev-0.12.0 → msdev-0.13.0}/src/msdev/core/ssh_auth.py +72 -82
  15. msdev-0.13.0/src/msdev/core/ssh_config.py +380 -0
  16. {msdev-0.12.0 → msdev-0.13.0}/src/msdev/core/ssh_tunnel.py +5 -2
  17. {msdev-0.12.0 → msdev-0.13.0}/src/msdev/core/stream_transport.py +9 -3
  18. {msdev-0.12.0 → msdev-0.13.0}/src/msdev/core/transport.py +84 -48
  19. {msdev-0.12.0 → msdev-0.13.0}/src/msdev/helpers/msdevd-linux-amd64 +0 -0
  20. msdev-0.13.0/src/msdev/helpers/msdevd-linux-amd64.sha256 +1 -0
  21. {msdev-0.12.0 → msdev-0.13.0}/src/msdev/helpers/msdevd-linux-arm64 +0 -0
  22. msdev-0.13.0/src/msdev/helpers/msdevd-linux-arm64.sha256 +1 -0
  23. msdev-0.13.0/src/msdev/skills/msdev-cli/SKILL.md +182 -0
  24. {msdev-0.12.0 → msdev-0.13.0}/src/msdev.egg-info/PKG-INFO +77 -33
  25. {msdev-0.12.0 → msdev-0.13.0}/src/msdev.egg-info/SOURCES.txt +14 -0
  26. {msdev-0.12.0 → msdev-0.13.0}/tests/test_cli.py +41 -33
  27. {msdev-0.12.0 → msdev-0.13.0}/tests/test_cli_only_architecture.py +11 -4
  28. msdev-0.13.0/tests/test_config_bundle.py +224 -0
  29. {msdev-0.12.0 → msdev-0.13.0}/tests/test_daemon_deploy.py +16 -11
  30. {msdev-0.12.0 → msdev-0.13.0}/tests/test_env_transfer.py +16 -11
  31. msdev-0.13.0/tests/test_exec_argv.py +133 -0
  32. msdev-0.13.0/tests/test_exec_command_scenario.py +152 -0
  33. {msdev-0.12.0 → msdev-0.13.0}/tests/test_exec_default_timeout_scenario.py +2 -8
  34. {msdev-0.12.0 → msdev-0.13.0}/tests/test_go_msdevd_scenario.py +1 -1
  35. msdev-0.13.0/tests/test_init_skill.py +241 -0
  36. {msdev-0.12.0 → msdev-0.13.0}/tests/test_msdev_cli_skill.py +20 -3
  37. msdev-0.13.0/tests/test_node_connect_provision_scenario.py +104 -0
  38. msdev-0.13.0/tests/test_node_connect_save_password_scenario.py +87 -0
  39. {msdev-0.12.0 → msdev-0.13.0}/tests/test_node_env_services.py +17 -8
  40. msdev-0.13.0/tests/test_node_sync_ssh_scenario.py +76 -0
  41. msdev-0.13.0/tests/test_sftp_transfer_scenario.py +109 -0
  42. {msdev-0.12.0 → msdev-0.13.0}/tests/test_ssh_auth.py +75 -58
  43. msdev-0.13.0/tests/test_ssh_config.py +171 -0
  44. {msdev-0.12.0 → msdev-0.13.0}/tests/test_transport.py +54 -31
  45. {msdev-0.12.0 → msdev-0.13.0}/tests/test_windows_local_scenario.py +12 -17
  46. msdev-0.12.0/src/msdev/core/exec_argv.py +0 -82
  47. msdev-0.12.0/src/msdev/helpers/msdevd-linux-amd64.sha256 +0 -1
  48. msdev-0.12.0/src/msdev/helpers/msdevd-linux-arm64.sha256 +0 -1
  49. msdev-0.12.0/tests/test_exec_argv.py +0 -55
  50. {msdev-0.12.0 → msdev-0.13.0}/LICENSE +0 -0
  51. {msdev-0.12.0 → msdev-0.13.0}/setup.cfg +0 -0
  52. {msdev-0.12.0 → msdev-0.13.0}/src/msdev/core/__init__.py +0 -0
  53. {msdev-0.12.0 → msdev-0.13.0}/src/msdev/core/config.py +0 -0
  54. {msdev-0.12.0 → msdev-0.13.0}/src/msdev/core/container_helper.py +0 -0
  55. {msdev-0.12.0 → msdev-0.13.0}/src/msdev/core/daemon_deploy.py +0 -0
  56. {msdev-0.12.0 → msdev-0.13.0}/src/msdev/core/guides.py +0 -0
  57. {msdev-0.12.0 → msdev-0.13.0}/src/msdev/core/helper_deploy.py +0 -0
  58. {msdev-0.12.0 → msdev-0.13.0}/src/msdev/core/host_files.py +0 -0
  59. {msdev-0.12.0 → msdev-0.13.0}/src/msdev/core/host_mutations.py +0 -0
  60. {msdev-0.12.0 → msdev-0.13.0}/src/msdev/core/inventory.py +0 -0
  61. {msdev-0.12.0 → msdev-0.13.0}/src/msdev/core/limits.py +0 -0
  62. {msdev-0.12.0 → msdev-0.13.0}/src/msdev/core/local_rpc.py +0 -0
  63. {msdev-0.12.0 → msdev-0.13.0}/src/msdev/core/multiplex.py +0 -0
  64. {msdev-0.12.0 → msdev-0.13.0}/src/msdev/core/request_client.py +0 -0
  65. {msdev-0.12.0 → msdev-0.13.0}/src/msdev/core/resources.py +0 -0
  66. {msdev-0.12.0 → msdev-0.13.0}/src/msdev/core/services/__init__.py +0 -0
  67. {msdev-0.12.0 → msdev-0.13.0}/src/msdev/core/services/capabilities.py +0 -0
  68. {msdev-0.12.0 → msdev-0.13.0}/src/msdev/core/services/context.py +0 -0
  69. {msdev-0.12.0 → msdev-0.13.0}/src/msdev/core/services/environment.py +0 -0
  70. {msdev-0.12.0 → msdev-0.13.0}/src/msdev/core/services/execution.py +0 -0
  71. {msdev-0.12.0 → msdev-0.13.0}/src/msdev/core/services/model.py +0 -0
  72. {msdev-0.12.0 → msdev-0.13.0}/src/msdev/core/services/npu.py +0 -0
  73. {msdev-0.12.0 → msdev-0.13.0}/src/msdev/core/services/results.py +0 -0
  74. {msdev-0.12.0 → msdev-0.13.0}/src/msdev/core/stream_limits.py +0 -0
  75. {msdev-0.12.0 → msdev-0.13.0}/src/msdev/core/stream_server.py +0 -0
  76. {msdev-0.12.0 → msdev-0.13.0}/src/msdev/core/streamproto.py +0 -0
  77. {msdev-0.12.0 → msdev-0.13.0}/src/msdev/daemon.py +0 -0
  78. {msdev-0.12.0 → msdev-0.13.0}/src/msdev/helpers/msdev-file-helper-linux-amd64 +0 -0
  79. {msdev-0.12.0 → msdev-0.13.0}/src/msdev/helpers/msdev-file-helper-linux-amd64.sha256 +0 -0
  80. {msdev-0.12.0 → msdev-0.13.0}/src/msdev/helpers/msdev-file-helper-linux-arm64 +0 -0
  81. {msdev-0.12.0 → msdev-0.13.0}/src/msdev/helpers/msdev-file-helper-linux-arm64.sha256 +0 -0
  82. {msdev-0.12.0 → msdev-0.13.0}/src/msdev.egg-info/dependency_links.txt +0 -0
  83. {msdev-0.12.0 → msdev-0.13.0}/src/msdev.egg-info/entry_points.txt +0 -0
  84. {msdev-0.12.0 → msdev-0.13.0}/src/msdev.egg-info/top_level.txt +0 -0
  85. {msdev-0.12.0 → msdev-0.13.0}/tests/test_compat.py +0 -0
  86. {msdev-0.12.0 → msdev-0.13.0}/tests/test_config.py +0 -0
  87. {msdev-0.12.0 → msdev-0.13.0}/tests/test_connect_env_scenario.py +0 -0
  88. {msdev-0.12.0 → msdev-0.13.0}/tests/test_container_helper.py +0 -0
  89. {msdev-0.12.0 → msdev-0.13.0}/tests/test_daemon.py +0 -0
  90. {msdev-0.12.0 → msdev-0.13.0}/tests/test_daemon_container_stream.py +0 -0
  91. {msdev-0.12.0 → msdev-0.13.0}/tests/test_docker_stream_dispatch.py +0 -0
  92. {msdev-0.12.0 → msdev-0.13.0}/tests/test_env_put_get_scenario.py +0 -0
  93. {msdev-0.12.0 → msdev-0.13.0}/tests/test_env_read_write_scenario.py +0 -0
  94. {msdev-0.12.0 → msdev-0.13.0}/tests/test_helper_deploy.py +0 -0
  95. {msdev-0.12.0 → msdev-0.13.0}/tests/test_inventory.py +0 -0
  96. {msdev-0.12.0 → msdev-0.13.0}/tests/test_model_cli.py +0 -0
  97. {msdev-0.12.0 → msdev-0.13.0}/tests/test_model_services.py +0 -0
  98. {msdev-0.12.0 → msdev-0.13.0}/tests/test_multiplex.py +0 -0
  99. {msdev-0.12.0 → msdev-0.13.0}/tests/test_node_env_config.py +0 -0
  100. {msdev-0.12.0 → msdev-0.13.0}/tests/test_request_client.py +0 -0
  101. {msdev-0.12.0 → msdev-0.13.0}/tests/test_ssh_tunnel.py +0 -0
  102. {msdev-0.12.0 → msdev-0.13.0}/tests/test_stream_server.py +0 -0
  103. {msdev-0.12.0 → msdev-0.13.0}/tests/test_stream_transport.py +0 -0
  104. {msdev-0.12.0 → msdev-0.13.0}/tests/test_streamproto.py +0 -0
  105. {msdev-0.12.0 → msdev-0.13.0}/tests/test_target_exec_services.py +0 -0
  106. {msdev-0.12.0 → msdev-0.13.0}/tests/test_target_guides.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: msdev
3
- Version: 0.12.0
3
+ Version: 0.13.0
4
4
  Summary: Portable nodes and execution environments for msModelSlim development
5
5
  Author: msModelSlim Agent Contributors
6
6
  License-Expression: MulanPSL-2.0
@@ -32,7 +32,8 @@ A Node owns SSH reachability and `msdevd` deployment. An Env owns the full
32
32
  execution context: host or Docker runtime, conda/venv/uv layers, optional
33
33
  `setup` script (root switch, proxy, etc.), command `wrap`, and static env vars.
34
34
 
35
- The CLI is intentionally stateless: execution always names `--env` explicitly.
35
+ The CLI is intentionally stateless: execution always names the env as the first
36
+ `exec` argument.
36
37
  MCP is not part of the architecture.
37
38
 
38
39
  ## Install
@@ -46,6 +47,20 @@ msdev --help
46
47
 
47
48
  The package installs `msdev` and `msdevd`.
48
49
 
50
+ Install the agent skill for Cursor and Claude Code, and sync SSH config:
51
+
52
+ ```bash
53
+ msdev init
54
+ msdev init --scope project
55
+ msdev init -f
56
+ ```
57
+
58
+ `init` registers Host aliases from `~/.ssh/config`, prints added/removed
59
+ changes, and writes `skills/msdev-cli/SKILL.md` into `~/.cursor/skills/` and
60
+ `~/.claude/skills/` (or into `.cursor/` / `.claude/` under the current
61
+ directory with `--scope project`). Outdated skill files prompt before
62
+ overwrite; use `-f` to force.
63
+
49
64
  ## Windows client
50
65
 
51
66
  The typical setup is a **Windows laptop talking to Linux Nodes** via OpenSSH
@@ -58,13 +73,13 @@ implement ControlMaster multiplexing, so msdev:
58
73
  RPC (same role as ControlMaster). Explicit `msdev node connect NODE` is
59
74
  optional; disable persist with `MSDEV_SSH_LOCAL_FORWARD_PERSIST=0`;
60
75
  - reuses that tunnel for captured RPC and default streaming `msdev exec`;
61
- `env put`/`get` stay on scp/ssh streams (file bytes do not go through JSON RPC);
76
+ `env put`/`get` stay on sftp/ssh streams (file bytes do not go through JSON RPC);
62
77
  - hides SSH console windows (`CREATE_NO_WINDOW`) and kills process trees with
63
78
  `taskkill /T` on timeout or cancel;
64
79
  - SSH is non-interactive: `BatchMode` unless a password is supplied, host keys
65
80
  use `accept-new`, and there is no password/passphrase/yes-no prompt. When a
66
81
  password is known, injects OpenSSH `SSH_ASKPASS` so `node connect` (and later
67
- ssh/scp) can authenticate without a TTY; the local public key is then
82
+ ssh/sftp) can authenticate without a TTY; the local public key is then
68
83
  installed on the Node so later logins are passwordless. The password is
69
84
  never written to the node registry;
70
85
  - serves a TCP loopback endpoint for local `msdevd` when this Python build has
@@ -82,22 +97,25 @@ If `ssh` is not on PATH, msdev looks in `%SystemRoot%\System32\OpenSSH\`.
82
97
 
83
98
  ## Nodes and execution environments
84
99
 
85
- Register a host from `~/.ssh/config`:
100
+ Use **`node connect`** as the single entry point: it writes `~/.ssh/config`
101
+ when needed, registers the node, saves passwords, installs the local public
102
+ key, and deploys `msdevd`:
86
103
 
87
104
  ```bash
88
- msdev node add dev-122 --ssh-host dev-122
105
+ msdev node connect dev-122 --hostname 80.48.33.133 --user root --password PASSWORD
106
+ msdev node connect dev-122
107
+ msdev node sync-ssh
108
+ msdev node list --ssh
89
109
  msdev node list
90
- msdev node status dev-122
91
110
  ```
92
111
 
93
- Adding a Node atomically creates a same-named host Env. Connect deploys and
94
- starts the remote `msdevd` when needed:
112
+ Connect writes or updates a dedicated `Host <name>` block in OpenSSH config.
113
+ The msdev registry stores only the Host alias plus msdevd settings
114
+ (`remote_bin`, `auto_bootstrap`). User, port, keys, and proxy remain in SSH
115
+ config. `node sync-ssh` registers Host aliases that were added manually.
95
116
 
96
- ```bash
97
- msdev node connect dev-122
98
- ```
99
-
100
- Connect uploads a **static Go msdevd** binary matching the node architecture
117
+ Connect atomically creates a same-named host Env on first use and uploads a
118
+ **static Go msdevd** binary matching the node architecture
101
119
  (`linux-amd64` / `linux-arm64`) when the agent is missing or stale. No Python
102
120
  is required on the node.
103
121
 
@@ -113,19 +131,14 @@ The Go source is in `msdev/`.
113
131
 
114
132
  Persistent OpenSSH masters are optional. All SSH from msdev is
115
133
  non-interactive: host keys use `accept-new`, and password/passphrase prompts
116
- are disabled. Supply a password via environment or file (never a prompt);
117
- connect then installs the local public key (`~/.ssh/id_ed25519`, generated
118
- if missing) so later connections are passwordless:
134
+ are disabled. Supply `--password` on connect; it is saved to ``~/.ssh/config``
135
+ as ``# msdev Password ...`` and used to install the local public key
136
+ (``~/.ssh/id_ed25519``, generated if missing) so later connections can be
137
+ passwordless:
119
138
 
120
139
  ```bash
121
- # Environment (also used by exec / env put after connect if persist is off)
122
- export MSDEV_SSH_PASSWORD='...'
123
- export MSDEV_SSH_PASSWORD_DEV_122='...' # per-node override
124
-
140
+ msdev node connect dev-122 --hostname 80.48.33.133 --user root --password PASSWORD
125
141
  msdev node connect dev-122
126
- msdev node connect dev-122 --password-file FILE
127
- msdev node connect dev-122 --password-env VAR
128
- msdev node connect dev-122 --password PASSWORD # non-interactive; visible in ps
129
142
  msdev node connect dev-122 --identity FILE # choose which key to install
130
143
  msdev node connect dev-122 --no-install-key # password only, do not copy key
131
144
  msdev node disconnect dev-122
@@ -144,6 +157,17 @@ Guides are stored under `~/.config/msdev/guides/nodes/` and
144
157
  `~/.config/msdev/guides/environments/`. **Node guides** document how to
145
158
  connect; **Env guides** document how to initialize shared context (`setup`).
146
159
 
160
+ Share a full local configuration (registry + guides) with:
161
+
162
+ ```bash
163
+ msdev config export ./msdev-config.tar.gz
164
+ msdev config import ./msdev-config.tar.gz
165
+ msdev config import ./msdev-config.tar.gz --force # overwrite name conflicts
166
+ ```
167
+
168
+ Bundles contain `resources.json`, both guide trees, and a `manifest.json`.
169
+ SSH keys and passwords are never exported.
170
+
147
171
  ```bash
148
172
  msdev env add dev-122-root \
149
173
  --node dev-122 \
@@ -173,7 +197,7 @@ msdev env get dev-122 /srv/project/config.yaml > ./config.yaml
173
197
  ```
174
198
 
175
199
  Editing uses content-oriented read/write. Paths inside the Env must be
176
- absolute. Host Envs use direct filesystem copy locally and ``scp``/``ssh``
200
+ absolute. Host Envs use direct filesystem copy locally and ``sftp``/``ssh``
177
201
  streams remotely. Docker Envs stage through the node with ``docker cp``.
178
202
  File bytes never pass through JSON RPC.
179
203
 
@@ -193,18 +217,38 @@ replacement that refuses to overwrite a changed remote file.
193
217
 
194
218
  Commands execute synchronously and stream stdout and stderr to the terminal as
195
219
  they are produced. Cursor or Claude Code should use their native background
196
- terminal support for long jobs. Long jobs may also write a persistent progress
197
- log when later inspection is required.
220
+ terminal support for long jobs.
221
+
222
+ Pick **one** form:
223
+
224
+ | Scenario | Form |
225
+ |----------|------|
226
+ | Simple one-liner | `-c '…'` |
227
+ | Quotes, pipes, multi-line | `--file -` + heredoc (see below) |
228
+ | Script on disk | `--file ./script.sh` |
229
+
230
+ **Heredoc** (`<<'EOF'`) is a local-shell mechanism: it writes a multi-line
231
+ string to msdev's **stdin** when you pass `--file -`. Quote the delimiter
232
+ (`<<'EOF'`) so `$` and backticks are not expanded locally. msdev then runs the
233
+ body through a remote shell (`sh -c`), or via shebang if the body starts with
234
+ `#!`.
198
235
 
199
236
  ```bash
200
- msdev exec \
201
- --env dev-122 \
202
- --cwd /srv/project \
203
- --timeout-seconds 1800 \
204
- --env-var MODE=test \
205
- -- python3 run.py
237
+ msdev exec dev-122 --file - <<'EOF'
238
+ python3 -c 'print("no local quoting")'
239
+ grep 'pattern' /var/log/app.log | tail -20
240
+ EOF
241
+
242
+ msdev exec dev-122 -c 'python3 train.py --config "path with spaces" | tee log.txt'
243
+
244
+ msdev exec dev-122 --file ./scripts/run.sh
206
245
  ```
207
246
 
247
+ Positional commands after `--` are not supported. The env name is the first
248
+ argument after `exec`; use `-c` for inline strings or `--file` for script
249
+ content (path or `-` for stdin/heredoc). `-c -` is accepted but identical to
250
+ `--file -`; prefer **`--file -`** only.
251
+
208
252
  Quoted pipelines and `|` tokens are executed through a remote POSIX shell
209
253
  (`sh -c`), so they run on the Node instead of being treated as a single
210
254
  executable name. On a Windows `local` Env they use `cmd.exe /s /c`. Prefer
@@ -7,7 +7,8 @@ A Node owns SSH reachability and `msdevd` deployment. An Env owns the full
7
7
  execution context: host or Docker runtime, conda/venv/uv layers, optional
8
8
  `setup` script (root switch, proxy, etc.), command `wrap`, and static env vars.
9
9
 
10
- The CLI is intentionally stateless: execution always names `--env` explicitly.
10
+ The CLI is intentionally stateless: execution always names the env as the first
11
+ `exec` argument.
11
12
  MCP is not part of the architecture.
12
13
 
13
14
  ## Install
@@ -21,6 +22,20 @@ msdev --help
21
22
 
22
23
  The package installs `msdev` and `msdevd`.
23
24
 
25
+ Install the agent skill for Cursor and Claude Code, and sync SSH config:
26
+
27
+ ```bash
28
+ msdev init
29
+ msdev init --scope project
30
+ msdev init -f
31
+ ```
32
+
33
+ `init` registers Host aliases from `~/.ssh/config`, prints added/removed
34
+ changes, and writes `skills/msdev-cli/SKILL.md` into `~/.cursor/skills/` and
35
+ `~/.claude/skills/` (or into `.cursor/` / `.claude/` under the current
36
+ directory with `--scope project`). Outdated skill files prompt before
37
+ overwrite; use `-f` to force.
38
+
24
39
  ## Windows client
25
40
 
26
41
  The typical setup is a **Windows laptop talking to Linux Nodes** via OpenSSH
@@ -33,13 +48,13 @@ implement ControlMaster multiplexing, so msdev:
33
48
  RPC (same role as ControlMaster). Explicit `msdev node connect NODE` is
34
49
  optional; disable persist with `MSDEV_SSH_LOCAL_FORWARD_PERSIST=0`;
35
50
  - reuses that tunnel for captured RPC and default streaming `msdev exec`;
36
- `env put`/`get` stay on scp/ssh streams (file bytes do not go through JSON RPC);
51
+ `env put`/`get` stay on sftp/ssh streams (file bytes do not go through JSON RPC);
37
52
  - hides SSH console windows (`CREATE_NO_WINDOW`) and kills process trees with
38
53
  `taskkill /T` on timeout or cancel;
39
54
  - SSH is non-interactive: `BatchMode` unless a password is supplied, host keys
40
55
  use `accept-new`, and there is no password/passphrase/yes-no prompt. When a
41
56
  password is known, injects OpenSSH `SSH_ASKPASS` so `node connect` (and later
42
- ssh/scp) can authenticate without a TTY; the local public key is then
57
+ ssh/sftp) can authenticate without a TTY; the local public key is then
43
58
  installed on the Node so later logins are passwordless. The password is
44
59
  never written to the node registry;
45
60
  - serves a TCP loopback endpoint for local `msdevd` when this Python build has
@@ -57,22 +72,25 @@ If `ssh` is not on PATH, msdev looks in `%SystemRoot%\System32\OpenSSH\`.
57
72
 
58
73
  ## Nodes and execution environments
59
74
 
60
- Register a host from `~/.ssh/config`:
75
+ Use **`node connect`** as the single entry point: it writes `~/.ssh/config`
76
+ when needed, registers the node, saves passwords, installs the local public
77
+ key, and deploys `msdevd`:
61
78
 
62
79
  ```bash
63
- msdev node add dev-122 --ssh-host dev-122
80
+ msdev node connect dev-122 --hostname 80.48.33.133 --user root --password PASSWORD
81
+ msdev node connect dev-122
82
+ msdev node sync-ssh
83
+ msdev node list --ssh
64
84
  msdev node list
65
- msdev node status dev-122
66
85
  ```
67
86
 
68
- Adding a Node atomically creates a same-named host Env. Connect deploys and
69
- starts the remote `msdevd` when needed:
87
+ Connect writes or updates a dedicated `Host <name>` block in OpenSSH config.
88
+ The msdev registry stores only the Host alias plus msdevd settings
89
+ (`remote_bin`, `auto_bootstrap`). User, port, keys, and proxy remain in SSH
90
+ config. `node sync-ssh` registers Host aliases that were added manually.
70
91
 
71
- ```bash
72
- msdev node connect dev-122
73
- ```
74
-
75
- Connect uploads a **static Go msdevd** binary matching the node architecture
92
+ Connect atomically creates a same-named host Env on first use and uploads a
93
+ **static Go msdevd** binary matching the node architecture
76
94
  (`linux-amd64` / `linux-arm64`) when the agent is missing or stale. No Python
77
95
  is required on the node.
78
96
 
@@ -88,19 +106,14 @@ The Go source is in `msdev/`.
88
106
 
89
107
  Persistent OpenSSH masters are optional. All SSH from msdev is
90
108
  non-interactive: host keys use `accept-new`, and password/passphrase prompts
91
- are disabled. Supply a password via environment or file (never a prompt);
92
- connect then installs the local public key (`~/.ssh/id_ed25519`, generated
93
- if missing) so later connections are passwordless:
109
+ are disabled. Supply `--password` on connect; it is saved to ``~/.ssh/config``
110
+ as ``# msdev Password ...`` and used to install the local public key
111
+ (``~/.ssh/id_ed25519``, generated if missing) so later connections can be
112
+ passwordless:
94
113
 
95
114
  ```bash
96
- # Environment (also used by exec / env put after connect if persist is off)
97
- export MSDEV_SSH_PASSWORD='...'
98
- export MSDEV_SSH_PASSWORD_DEV_122='...' # per-node override
99
-
115
+ msdev node connect dev-122 --hostname 80.48.33.133 --user root --password PASSWORD
100
116
  msdev node connect dev-122
101
- msdev node connect dev-122 --password-file FILE
102
- msdev node connect dev-122 --password-env VAR
103
- msdev node connect dev-122 --password PASSWORD # non-interactive; visible in ps
104
117
  msdev node connect dev-122 --identity FILE # choose which key to install
105
118
  msdev node connect dev-122 --no-install-key # password only, do not copy key
106
119
  msdev node disconnect dev-122
@@ -119,6 +132,17 @@ Guides are stored under `~/.config/msdev/guides/nodes/` and
119
132
  `~/.config/msdev/guides/environments/`. **Node guides** document how to
120
133
  connect; **Env guides** document how to initialize shared context (`setup`).
121
134
 
135
+ Share a full local configuration (registry + guides) with:
136
+
137
+ ```bash
138
+ msdev config export ./msdev-config.tar.gz
139
+ msdev config import ./msdev-config.tar.gz
140
+ msdev config import ./msdev-config.tar.gz --force # overwrite name conflicts
141
+ ```
142
+
143
+ Bundles contain `resources.json`, both guide trees, and a `manifest.json`.
144
+ SSH keys and passwords are never exported.
145
+
122
146
  ```bash
123
147
  msdev env add dev-122-root \
124
148
  --node dev-122 \
@@ -148,7 +172,7 @@ msdev env get dev-122 /srv/project/config.yaml > ./config.yaml
148
172
  ```
149
173
 
150
174
  Editing uses content-oriented read/write. Paths inside the Env must be
151
- absolute. Host Envs use direct filesystem copy locally and ``scp``/``ssh``
175
+ absolute. Host Envs use direct filesystem copy locally and ``sftp``/``ssh``
152
176
  streams remotely. Docker Envs stage through the node with ``docker cp``.
153
177
  File bytes never pass through JSON RPC.
154
178
 
@@ -168,18 +192,38 @@ replacement that refuses to overwrite a changed remote file.
168
192
 
169
193
  Commands execute synchronously and stream stdout and stderr to the terminal as
170
194
  they are produced. Cursor or Claude Code should use their native background
171
- terminal support for long jobs. Long jobs may also write a persistent progress
172
- log when later inspection is required.
195
+ terminal support for long jobs.
196
+
197
+ Pick **one** form:
198
+
199
+ | Scenario | Form |
200
+ |----------|------|
201
+ | Simple one-liner | `-c '…'` |
202
+ | Quotes, pipes, multi-line | `--file -` + heredoc (see below) |
203
+ | Script on disk | `--file ./script.sh` |
204
+
205
+ **Heredoc** (`<<'EOF'`) is a local-shell mechanism: it writes a multi-line
206
+ string to msdev's **stdin** when you pass `--file -`. Quote the delimiter
207
+ (`<<'EOF'`) so `$` and backticks are not expanded locally. msdev then runs the
208
+ body through a remote shell (`sh -c`), or via shebang if the body starts with
209
+ `#!`.
173
210
 
174
211
  ```bash
175
- msdev exec \
176
- --env dev-122 \
177
- --cwd /srv/project \
178
- --timeout-seconds 1800 \
179
- --env-var MODE=test \
180
- -- python3 run.py
212
+ msdev exec dev-122 --file - <<'EOF'
213
+ python3 -c 'print("no local quoting")'
214
+ grep 'pattern' /var/log/app.log | tail -20
215
+ EOF
216
+
217
+ msdev exec dev-122 -c 'python3 train.py --config "path with spaces" | tee log.txt'
218
+
219
+ msdev exec dev-122 --file ./scripts/run.sh
181
220
  ```
182
221
 
222
+ Positional commands after `--` are not supported. The env name is the first
223
+ argument after `exec`; use `-c` for inline strings or `--file` for script
224
+ content (path or `-` for stdin/heredoc). `-c -` is accepted but identical to
225
+ `--file -`; prefer **`--file -`** only.
226
+
183
227
  Quoted pipelines and `|` tokens are executed through a remote POSIX shell
184
228
  (`sh -c`), so they run on the Node instead of being treated as a single
185
229
  executable name. On a Windows `local` Env they use `cmd.exe /s /c`. Prefer
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "msdev"
7
- version = "0.12.0"
7
+ version = "0.13.0"
8
8
  description = "Portable nodes and execution environments for msModelSlim development"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -44,7 +44,11 @@ package-dir = { "" = "src" }
44
44
  where = ["src"]
45
45
 
46
46
  [tool.setuptools.package-data]
47
- msdev = ["helpers/msdev-file-helper-*", "helpers/msdevd-linux-*"]
47
+ msdev = [
48
+ "helpers/msdev-file-helper-*",
49
+ "helpers/msdevd-linux-*",
50
+ "skills/msdev-cli/SKILL.md",
51
+ ]
48
52
 
49
53
  [tool.pytest.ini_options]
50
54
  testpaths = ["tests"]
@@ -1,3 +1,3 @@
1
1
  """Portable node and execution-environment tooling for msModelSlim."""
2
2
 
3
- __version__ = "0.12.0"
3
+ __version__ = "0.13.0"