msdev 0.12.0__tar.gz → 0.14.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 (107) hide show
  1. {msdev-0.12.0 → msdev-0.14.0}/PKG-INFO +81 -35
  2. {msdev-0.12.0 → msdev-0.14.0}/README.md +80 -34
  3. {msdev-0.12.0 → msdev-0.14.0}/pyproject.toml +6 -2
  4. {msdev-0.12.0 → msdev-0.14.0}/src/msdev/__init__.py +1 -1
  5. {msdev-0.12.0 → msdev-0.14.0}/src/msdev/cli.py +396 -67
  6. {msdev-0.12.0 → msdev-0.14.0}/src/msdev/core/compat.py +6 -6
  7. msdev-0.14.0/src/msdev/core/config_bundle.py +338 -0
  8. {msdev-0.12.0 → msdev-0.14.0}/src/msdev/core/env_transfer.py +30 -29
  9. msdev-0.14.0/src/msdev/core/exec_argv.py +196 -0
  10. msdev-0.14.0/src/msdev/core/init_setup.py +42 -0
  11. {msdev-0.12.0 → msdev-0.14.0}/src/msdev/core/services/node.py +168 -17
  12. msdev-0.14.0/src/msdev/core/sftp_transfer.py +156 -0
  13. msdev-0.14.0/src/msdev/core/skill_install.py +258 -0
  14. {msdev-0.12.0 → msdev-0.14.0}/src/msdev/core/ssh_auth.py +72 -82
  15. msdev-0.14.0/src/msdev/core/ssh_config.py +380 -0
  16. {msdev-0.12.0 → msdev-0.14.0}/src/msdev/core/ssh_tunnel.py +5 -2
  17. {msdev-0.12.0 → msdev-0.14.0}/src/msdev/core/stream_transport.py +9 -3
  18. {msdev-0.12.0 → msdev-0.14.0}/src/msdev/core/transport.py +84 -48
  19. {msdev-0.12.0 → msdev-0.14.0}/src/msdev/helpers/msdevd-linux-amd64 +0 -0
  20. msdev-0.14.0/src/msdev/helpers/msdevd-linux-amd64.sha256 +1 -0
  21. {msdev-0.12.0 → msdev-0.14.0}/src/msdev/helpers/msdevd-linux-arm64 +0 -0
  22. msdev-0.14.0/src/msdev/helpers/msdevd-linux-arm64.sha256 +1 -0
  23. msdev-0.14.0/src/msdev/skills/msdev-cli/SKILL.md +186 -0
  24. {msdev-0.12.0 → msdev-0.14.0}/src/msdev.egg-info/PKG-INFO +81 -35
  25. {msdev-0.12.0 → msdev-0.14.0}/src/msdev.egg-info/SOURCES.txt +15 -0
  26. {msdev-0.12.0 → msdev-0.14.0}/tests/test_cli.py +62 -33
  27. {msdev-0.12.0 → msdev-0.14.0}/tests/test_cli_only_architecture.py +17 -5
  28. msdev-0.14.0/tests/test_config_bundle.py +224 -0
  29. {msdev-0.12.0 → msdev-0.14.0}/tests/test_daemon.py +1 -1
  30. {msdev-0.12.0 → msdev-0.14.0}/tests/test_daemon_deploy.py +16 -11
  31. {msdev-0.12.0 → msdev-0.14.0}/tests/test_env_transfer.py +16 -11
  32. msdev-0.14.0/tests/test_exec_argv.py +133 -0
  33. msdev-0.14.0/tests/test_exec_command_scenario.py +152 -0
  34. {msdev-0.12.0 → msdev-0.14.0}/tests/test_exec_default_timeout_scenario.py +2 -8
  35. {msdev-0.12.0 → msdev-0.14.0}/tests/test_go_msdevd_scenario.py +1 -1
  36. msdev-0.14.0/tests/test_init_skill.py +241 -0
  37. {msdev-0.12.0 → msdev-0.14.0}/tests/test_msdev_cli_skill.py +22 -4
  38. msdev-0.14.0/tests/test_node_bootstrap_version_scenario.py +173 -0
  39. msdev-0.14.0/tests/test_node_connect_provision_scenario.py +104 -0
  40. msdev-0.14.0/tests/test_node_connect_save_password_scenario.py +87 -0
  41. {msdev-0.12.0 → msdev-0.14.0}/tests/test_node_env_services.py +17 -8
  42. msdev-0.14.0/tests/test_node_sync_ssh_scenario.py +76 -0
  43. msdev-0.14.0/tests/test_sftp_transfer_scenario.py +239 -0
  44. {msdev-0.12.0 → msdev-0.14.0}/tests/test_ssh_auth.py +75 -58
  45. msdev-0.14.0/tests/test_ssh_config.py +171 -0
  46. {msdev-0.12.0 → msdev-0.14.0}/tests/test_target_exec_services.py +68 -9
  47. {msdev-0.12.0 → msdev-0.14.0}/tests/test_transport.py +54 -31
  48. {msdev-0.12.0 → msdev-0.14.0}/tests/test_windows_local_scenario.py +12 -17
  49. msdev-0.12.0/src/msdev/core/exec_argv.py +0 -82
  50. msdev-0.12.0/src/msdev/helpers/msdevd-linux-amd64.sha256 +0 -1
  51. msdev-0.12.0/src/msdev/helpers/msdevd-linux-arm64.sha256 +0 -1
  52. msdev-0.12.0/tests/test_exec_argv.py +0 -55
  53. {msdev-0.12.0 → msdev-0.14.0}/LICENSE +0 -0
  54. {msdev-0.12.0 → msdev-0.14.0}/setup.cfg +0 -0
  55. {msdev-0.12.0 → msdev-0.14.0}/src/msdev/core/__init__.py +0 -0
  56. {msdev-0.12.0 → msdev-0.14.0}/src/msdev/core/config.py +0 -0
  57. {msdev-0.12.0 → msdev-0.14.0}/src/msdev/core/container_helper.py +0 -0
  58. {msdev-0.12.0 → msdev-0.14.0}/src/msdev/core/daemon_deploy.py +0 -0
  59. {msdev-0.12.0 → msdev-0.14.0}/src/msdev/core/guides.py +0 -0
  60. {msdev-0.12.0 → msdev-0.14.0}/src/msdev/core/helper_deploy.py +0 -0
  61. {msdev-0.12.0 → msdev-0.14.0}/src/msdev/core/host_files.py +0 -0
  62. {msdev-0.12.0 → msdev-0.14.0}/src/msdev/core/host_mutations.py +0 -0
  63. {msdev-0.12.0 → msdev-0.14.0}/src/msdev/core/inventory.py +0 -0
  64. {msdev-0.12.0 → msdev-0.14.0}/src/msdev/core/limits.py +0 -0
  65. {msdev-0.12.0 → msdev-0.14.0}/src/msdev/core/local_rpc.py +0 -0
  66. {msdev-0.12.0 → msdev-0.14.0}/src/msdev/core/multiplex.py +0 -0
  67. {msdev-0.12.0 → msdev-0.14.0}/src/msdev/core/request_client.py +0 -0
  68. {msdev-0.12.0 → msdev-0.14.0}/src/msdev/core/resources.py +0 -0
  69. {msdev-0.12.0 → msdev-0.14.0}/src/msdev/core/services/__init__.py +0 -0
  70. {msdev-0.12.0 → msdev-0.14.0}/src/msdev/core/services/capabilities.py +0 -0
  71. {msdev-0.12.0 → msdev-0.14.0}/src/msdev/core/services/context.py +0 -0
  72. {msdev-0.12.0 → msdev-0.14.0}/src/msdev/core/services/environment.py +0 -0
  73. {msdev-0.12.0 → msdev-0.14.0}/src/msdev/core/services/execution.py +0 -0
  74. {msdev-0.12.0 → msdev-0.14.0}/src/msdev/core/services/model.py +0 -0
  75. {msdev-0.12.0 → msdev-0.14.0}/src/msdev/core/services/npu.py +0 -0
  76. {msdev-0.12.0 → msdev-0.14.0}/src/msdev/core/services/results.py +0 -0
  77. {msdev-0.12.0 → msdev-0.14.0}/src/msdev/core/stream_limits.py +0 -0
  78. {msdev-0.12.0 → msdev-0.14.0}/src/msdev/core/stream_server.py +0 -0
  79. {msdev-0.12.0 → msdev-0.14.0}/src/msdev/core/streamproto.py +0 -0
  80. {msdev-0.12.0 → msdev-0.14.0}/src/msdev/daemon.py +0 -0
  81. {msdev-0.12.0 → msdev-0.14.0}/src/msdev/helpers/msdev-file-helper-linux-amd64 +0 -0
  82. {msdev-0.12.0 → msdev-0.14.0}/src/msdev/helpers/msdev-file-helper-linux-amd64.sha256 +0 -0
  83. {msdev-0.12.0 → msdev-0.14.0}/src/msdev/helpers/msdev-file-helper-linux-arm64 +0 -0
  84. {msdev-0.12.0 → msdev-0.14.0}/src/msdev/helpers/msdev-file-helper-linux-arm64.sha256 +0 -0
  85. {msdev-0.12.0 → msdev-0.14.0}/src/msdev.egg-info/dependency_links.txt +0 -0
  86. {msdev-0.12.0 → msdev-0.14.0}/src/msdev.egg-info/entry_points.txt +0 -0
  87. {msdev-0.12.0 → msdev-0.14.0}/src/msdev.egg-info/top_level.txt +0 -0
  88. {msdev-0.12.0 → msdev-0.14.0}/tests/test_compat.py +0 -0
  89. {msdev-0.12.0 → msdev-0.14.0}/tests/test_config.py +0 -0
  90. {msdev-0.12.0 → msdev-0.14.0}/tests/test_connect_env_scenario.py +0 -0
  91. {msdev-0.12.0 → msdev-0.14.0}/tests/test_container_helper.py +0 -0
  92. {msdev-0.12.0 → msdev-0.14.0}/tests/test_daemon_container_stream.py +0 -0
  93. {msdev-0.12.0 → msdev-0.14.0}/tests/test_docker_stream_dispatch.py +0 -0
  94. {msdev-0.12.0 → msdev-0.14.0}/tests/test_env_put_get_scenario.py +0 -0
  95. {msdev-0.12.0 → msdev-0.14.0}/tests/test_env_read_write_scenario.py +0 -0
  96. {msdev-0.12.0 → msdev-0.14.0}/tests/test_helper_deploy.py +0 -0
  97. {msdev-0.12.0 → msdev-0.14.0}/tests/test_inventory.py +0 -0
  98. {msdev-0.12.0 → msdev-0.14.0}/tests/test_model_cli.py +0 -0
  99. {msdev-0.12.0 → msdev-0.14.0}/tests/test_model_services.py +0 -0
  100. {msdev-0.12.0 → msdev-0.14.0}/tests/test_multiplex.py +0 -0
  101. {msdev-0.12.0 → msdev-0.14.0}/tests/test_node_env_config.py +0 -0
  102. {msdev-0.12.0 → msdev-0.14.0}/tests/test_request_client.py +0 -0
  103. {msdev-0.12.0 → msdev-0.14.0}/tests/test_ssh_tunnel.py +0 -0
  104. {msdev-0.12.0 → msdev-0.14.0}/tests/test_stream_server.py +0 -0
  105. {msdev-0.12.0 → msdev-0.14.0}/tests/test_stream_transport.py +0 -0
  106. {msdev-0.12.0 → msdev-0.14.0}/tests/test_streamproto.py +0 -0
  107. {msdev-0.12.0 → msdev-0.14.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.14.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,24 +97,29 @@ 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 bootstrap dev-122
108
+ msdev node sync-ssh
109
+ msdev node list --ssh
89
110
  msdev node list
90
- msdev node status dev-122
91
111
  ```
92
112
 
93
- Adding a Node atomically creates a same-named host Env. Connect deploys and
94
- starts the remote `msdevd` when needed:
113
+ 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.
95
117
 
96
- ```bash
97
- msdev node connect dev-122
98
- ```
99
-
100
- Connect uploads a **static Go msdevd** binary matching the node architecture
101
- (`linux-amd64` / `linux-arm64`) when the agent is missing or stale. No Python
102
- is required on the node.
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.
103
123
 
104
124
  Build the static daemons before release or local testing:
105
125
 
@@ -113,19 +133,14 @@ The Go source is in `msdev/`.
113
133
 
114
134
  Persistent OpenSSH masters are optional. All SSH from msdev is
115
135
  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:
136
+ are disabled. Supply `--password` on connect; it is saved to ``~/.ssh/config``
137
+ as ``# msdev Password ...`` and used to install the local public key
138
+ (``~/.ssh/id_ed25519``, generated if missing) so later connections can be
139
+ passwordless:
119
140
 
120
141
  ```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
-
142
+ msdev node connect dev-122 --hostname 80.48.33.133 --user root --password PASSWORD
125
143
  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
144
  msdev node connect dev-122 --identity FILE # choose which key to install
130
145
  msdev node connect dev-122 --no-install-key # password only, do not copy key
131
146
  msdev node disconnect dev-122
@@ -144,6 +159,17 @@ Guides are stored under `~/.config/msdev/guides/nodes/` and
144
159
  `~/.config/msdev/guides/environments/`. **Node guides** document how to
145
160
  connect; **Env guides** document how to initialize shared context (`setup`).
146
161
 
162
+ Share a full local configuration (registry + guides) with:
163
+
164
+ ```bash
165
+ msdev config export ./msdev-config.tar.gz
166
+ msdev config import ./msdev-config.tar.gz
167
+ msdev config import ./msdev-config.tar.gz --force # overwrite name conflicts
168
+ ```
169
+
170
+ Bundles contain `resources.json`, both guide trees, and a `manifest.json`.
171
+ SSH keys and passwords are never exported.
172
+
147
173
  ```bash
148
174
  msdev env add dev-122-root \
149
175
  --node dev-122 \
@@ -173,7 +199,7 @@ msdev env get dev-122 /srv/project/config.yaml > ./config.yaml
173
199
  ```
174
200
 
175
201
  Editing uses content-oriented read/write. Paths inside the Env must be
176
- absolute. Host Envs use direct filesystem copy locally and ``scp``/``ssh``
202
+ absolute. Host Envs use direct filesystem copy locally and ``sftp``/``ssh``
177
203
  streams remotely. Docker Envs stage through the node with ``docker cp``.
178
204
  File bytes never pass through JSON RPC.
179
205
 
@@ -193,18 +219,38 @@ replacement that refuses to overwrite a changed remote file.
193
219
 
194
220
  Commands execute synchronously and stream stdout and stderr to the terminal as
195
221
  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.
222
+ terminal support for long jobs.
223
+
224
+ Pick **one** form:
225
+
226
+ | Scenario | Form |
227
+ |----------|------|
228
+ | Simple one-liner | `-c '…'` |
229
+ | Quotes, pipes, multi-line | `--file -` + heredoc (see below) |
230
+ | Script on disk | `--file ./script.sh` |
231
+
232
+ **Heredoc** (`<<'EOF'`) is a local-shell mechanism: it writes a multi-line
233
+ string to msdev's **stdin** when you pass `--file -`. Quote the delimiter
234
+ (`<<'EOF'`) so `$` and backticks are not expanded locally. msdev then runs the
235
+ body through a remote shell (`sh -c`), or via shebang if the body starts with
236
+ `#!`.
198
237
 
199
238
  ```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
239
+ msdev exec dev-122 --file - <<'EOF'
240
+ python3 -c 'print("no local quoting")'
241
+ grep 'pattern' /var/log/app.log | tail -20
242
+ EOF
243
+
244
+ msdev exec dev-122 -c 'python3 train.py --config "path with spaces" | tee log.txt'
245
+
246
+ msdev exec dev-122 --file ./scripts/run.sh
206
247
  ```
207
248
 
249
+ Positional commands after `--` are not supported. The env name is the first
250
+ argument after `exec`; use `-c` for inline strings or `--file` for script
251
+ content (path or `-` for stdin/heredoc). `-c -` is accepted but identical to
252
+ `--file -`; prefer **`--file -`** only.
253
+
208
254
  Quoted pipelines and `|` tokens are executed through a remote POSIX shell
209
255
  (`sh -c`), so they run on the Node instead of being treated as a single
210
256
  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,24 +72,29 @@ 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 bootstrap dev-122
83
+ msdev node sync-ssh
84
+ msdev node list --ssh
64
85
  msdev node list
65
- msdev node status dev-122
66
86
  ```
67
87
 
68
- Adding a Node atomically creates a same-named host Env. Connect deploys and
69
- starts the remote `msdevd` when needed:
88
+ 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.
70
92
 
71
- ```bash
72
- msdev node connect dev-122
73
- ```
74
-
75
- Connect uploads a **static Go msdevd** binary matching the node architecture
76
- (`linux-amd64` / `linux-arm64`) when the agent is missing or stale. No Python
77
- is required on the node.
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.
78
98
 
79
99
  Build the static daemons before release or local testing:
80
100
 
@@ -88,19 +108,14 @@ The Go source is in `msdev/`.
88
108
 
89
109
  Persistent OpenSSH masters are optional. All SSH from msdev is
90
110
  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:
111
+ are disabled. Supply `--password` on connect; it is saved to ``~/.ssh/config``
112
+ as ``# msdev Password ...`` and used to install the local public key
113
+ (``~/.ssh/id_ed25519``, generated if missing) so later connections can be
114
+ passwordless:
94
115
 
95
116
  ```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
-
117
+ msdev node connect dev-122 --hostname 80.48.33.133 --user root --password PASSWORD
100
118
  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
119
  msdev node connect dev-122 --identity FILE # choose which key to install
105
120
  msdev node connect dev-122 --no-install-key # password only, do not copy key
106
121
  msdev node disconnect dev-122
@@ -119,6 +134,17 @@ Guides are stored under `~/.config/msdev/guides/nodes/` and
119
134
  `~/.config/msdev/guides/environments/`. **Node guides** document how to
120
135
  connect; **Env guides** document how to initialize shared context (`setup`).
121
136
 
137
+ Share a full local configuration (registry + guides) with:
138
+
139
+ ```bash
140
+ msdev config export ./msdev-config.tar.gz
141
+ msdev config import ./msdev-config.tar.gz
142
+ msdev config import ./msdev-config.tar.gz --force # overwrite name conflicts
143
+ ```
144
+
145
+ Bundles contain `resources.json`, both guide trees, and a `manifest.json`.
146
+ SSH keys and passwords are never exported.
147
+
122
148
  ```bash
123
149
  msdev env add dev-122-root \
124
150
  --node dev-122 \
@@ -148,7 +174,7 @@ msdev env get dev-122 /srv/project/config.yaml > ./config.yaml
148
174
  ```
149
175
 
150
176
  Editing uses content-oriented read/write. Paths inside the Env must be
151
- absolute. Host Envs use direct filesystem copy locally and ``scp``/``ssh``
177
+ absolute. Host Envs use direct filesystem copy locally and ``sftp``/``ssh``
152
178
  streams remotely. Docker Envs stage through the node with ``docker cp``.
153
179
  File bytes never pass through JSON RPC.
154
180
 
@@ -168,18 +194,38 @@ replacement that refuses to overwrite a changed remote file.
168
194
 
169
195
  Commands execute synchronously and stream stdout and stderr to the terminal as
170
196
  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.
197
+ terminal support for long jobs.
198
+
199
+ Pick **one** form:
200
+
201
+ | Scenario | Form |
202
+ |----------|------|
203
+ | Simple one-liner | `-c '…'` |
204
+ | Quotes, pipes, multi-line | `--file -` + heredoc (see below) |
205
+ | Script on disk | `--file ./script.sh` |
206
+
207
+ **Heredoc** (`<<'EOF'`) is a local-shell mechanism: it writes a multi-line
208
+ string to msdev's **stdin** when you pass `--file -`. Quote the delimiter
209
+ (`<<'EOF'`) so `$` and backticks are not expanded locally. msdev then runs the
210
+ body through a remote shell (`sh -c`), or via shebang if the body starts with
211
+ `#!`.
173
212
 
174
213
  ```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
214
+ msdev exec dev-122 --file - <<'EOF'
215
+ python3 -c 'print("no local quoting")'
216
+ grep 'pattern' /var/log/app.log | tail -20
217
+ EOF
218
+
219
+ msdev exec dev-122 -c 'python3 train.py --config "path with spaces" | tee log.txt'
220
+
221
+ msdev exec dev-122 --file ./scripts/run.sh
181
222
  ```
182
223
 
224
+ Positional commands after `--` are not supported. The env name is the first
225
+ argument after `exec`; use `-c` for inline strings or `--file` for script
226
+ content (path or `-` for stdin/heredoc). `-c -` is accepted but identical to
227
+ `--file -`; prefer **`--file -`** only.
228
+
183
229
  Quoted pipelines and `|` tokens are executed through a remote POSIX shell
184
230
  (`sh -c`), so they run on the Node instead of being treated as a single
185
231
  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.14.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.14.0"