jpsync 1.2.0__tar.gz → 1.3.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.
- {jpsync-1.2.0 → jpsync-1.3.0}/.gitignore +1 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/CHANGELOG.md +12 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/PKG-INFO +27 -5
- {jpsync-1.2.0 → jpsync-1.3.0}/README.md +26 -4
- jpsync-1.3.0/docs/jp-live.md +253 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/pyproject.toml +3 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/src/jp/__init__.py +1 -1
- jpsync-1.3.0/src/jp/_agent/__init__.py +0 -0
- jpsync-1.3.0/src/jp/_agent/agentd.py +347 -0
- jpsync-1.3.0/src/jp/_sim.py +72 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/src/jp/_ws.py +51 -13
- jpsync-1.3.0/src/jp/agent_loader.py +40 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/src/jp/api.py +100 -4
- {jpsync-1.2.0 → jpsync-1.3.0}/src/jp/changelog.py +50 -3
- {jpsync-1.2.0 → jpsync-1.3.0}/src/jp/commands/__init__.py +2 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/src/jp/commands/_context.py +57 -4
- {jpsync-1.2.0 → jpsync-1.3.0}/src/jp/commands/changelog.py +9 -4
- {jpsync-1.2.0 → jpsync-1.3.0}/src/jp/commands/config_cmd.py +33 -4
- jpsync-1.3.0/src/jp/commands/live.py +669 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/src/jp/commands/run.py +11 -8
- {jpsync-1.2.0 → jpsync-1.3.0}/src/jp/commands/terminal.py +59 -21
- jpsync-1.3.0/src/jp/fsrpc.py +46 -0
- jpsync-1.3.0/src/jp/kernel_conn.py +188 -0
- jpsync-1.3.0/src/jp/kernel_proto.py +146 -0
- jpsync-1.3.0/src/jp/live_session.py +104 -0
- jpsync-1.3.0/src/jp/mount/__init__.py +0 -0
- jpsync-1.3.0/src/jp/mount/live_state.py +163 -0
- jpsync-1.3.0/src/jp/mount/os_mount.py +344 -0
- jpsync-1.3.0/src/jp/mount/vscode_launch.py +87 -0
- jpsync-1.3.0/src/jp/mount/webdav_server.py +729 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/src/jp/pty.py +135 -0
- jpsync-1.3.0/src/jp/remote_fs.py +135 -0
- jpsync-1.3.0/src/jp/stats.py +70 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/src/jp/tui.py +74 -0
- jpsync-1.3.0/src/jp/user_settings.py +129 -0
- jpsync-1.3.0/src/jp/vfs_cache.py +300 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/tests/conftest.py +19 -0
- jpsync-1.3.0/tests/test_agent_loader.py +27 -0
- jpsync-1.3.0/tests/test_agentd.py +243 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/tests/test_api.py +106 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/tests/test_changelog.py +32 -0
- jpsync-1.3.0/tests/test_config_from_url.py +123 -0
- jpsync-1.3.0/tests/test_config_global_keys.py +77 -0
- jpsync-1.3.0/tests/test_fake_jupyter.py +26 -0
- jpsync-1.3.0/tests/test_fsrpc.py +27 -0
- jpsync-1.3.0/tests/test_kernel_conn.py +222 -0
- jpsync-1.3.0/tests/test_kernel_proto.py +62 -0
- jpsync-1.3.0/tests/test_live_cmd.py +526 -0
- jpsync-1.3.0/tests/test_live_dryrun.py +106 -0
- jpsync-1.3.0/tests/test_live_session.py +84 -0
- jpsync-1.3.0/tests/test_live_state.py +117 -0
- jpsync-1.3.0/tests/test_os_mount.py +218 -0
- jpsync-1.3.0/tests/test_real_jupyter.py +265 -0
- jpsync-1.3.0/tests/test_release_notes_ai.py +72 -0
- jpsync-1.3.0/tests/test_remote_fs.py +85 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/tests/test_run.py +8 -6
- jpsync-1.3.0/tests/test_stats.py +34 -0
- jpsync-1.3.0/tests/test_terminal_url.py +211 -0
- jpsync-1.3.0/tests/test_terminal_windows.py +54 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/tests/test_tui.py +49 -0
- jpsync-1.3.0/tests/test_user_settings.py +85 -0
- jpsync-1.3.0/tests/test_vfs_cache.py +347 -0
- jpsync-1.3.0/tests/test_vscode_launch.py +111 -0
- jpsync-1.3.0/tests/test_webdav_server.py +534 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/tests/test_ws.py +41 -0
- jpsync-1.2.0/docs/superpowers/plans/2026-06-05-jp-run-remote.md +0 -1009
- jpsync-1.2.0/docs/superpowers/plans/2026-06-05-update-notifier.md +0 -1798
- jpsync-1.2.0/docs/superpowers/specs/2026-06-05-credential-site-link-design.md +0 -168
- jpsync-1.2.0/docs/superpowers/specs/2026-06-05-jp-run-remote-design.md +0 -228
- jpsync-1.2.0/docs/superpowers/specs/2026-06-05-update-notifier-design.md +0 -298
- jpsync-1.2.0/tests/test_config_global_keys.py +0 -37
- jpsync-1.2.0/tests/test_release_notes_ai.py +0 -38
- {jpsync-1.2.0 → jpsync-1.3.0}/LICENSE +0 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/NOTICE +0 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/docs/architecture.md +0 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/docs/commands.md +0 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/docs/security.md +0 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/docs/vscode-remote-cwd.md +0 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/src/jp/__main__.py +0 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/src/jp/cli.py +0 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/src/jp/clipboard.py +0 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/src/jp/commands/_mirror.py +0 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/src/jp/commands/_report.py +0 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/src/jp/commands/clone.py +0 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/src/jp/commands/credentials_cmd.py +0 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/src/jp/commands/diff.py +0 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/src/jp/commands/doctor.py +0 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/src/jp/commands/ignore_cmd.py +0 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/src/jp/commands/init.py +0 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/src/jp/commands/kernel.py +0 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/src/jp/commands/login.py +0 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/src/jp/commands/ls.py +0 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/src/jp/commands/open_cmd.py +0 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/src/jp/commands/pull.py +0 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/src/jp/commands/push.py +0 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/src/jp/commands/rm.py +0 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/src/jp/commands/status.py +0 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/src/jp/commands/update.py +0 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/src/jp/commands/update_check.py +0 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/src/jp/commands/version.py +0 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/src/jp/config.py +0 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/src/jp/credentials.py +0 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/src/jp/errors.py +0 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/src/jp/global_prefs.py +0 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/src/jp/ignore.py +0 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/src/jp/index.py +0 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/src/jp/paths.py +0 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/src/jp/prefs.py +0 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/src/jp/settings_schema.py +0 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/src/jp/sync.py +0 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/src/jp/ui.py +0 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/src/jp/update_notify.py +0 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/src/jp/urls.py +0 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/tests/test_changelog_cmd.py +0 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/tests/test_cli.py +0 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/tests/test_cli_notify_hook.py +0 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/tests/test_clone_init.py +0 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/tests/test_config.py +0 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/tests/test_credentials.py +0 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/tests/test_credentials_cmd.py +0 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/tests/test_doctor.py +0 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/tests/test_gitignore_guard.py +0 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/tests/test_global_prefs.py +0 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/tests/test_index_ignore.py +0 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/tests/test_kernel.py +0 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/tests/test_login.py +0 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/tests/test_mirror.py +0 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/tests/test_open_cmd.py +0 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/tests/test_paths.py +0 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/tests/test_pty.py +0 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/tests/test_redact.py +0 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/tests/test_rm.py +0 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/tests/test_sync_pull.py +0 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/tests/test_sync_push.py +0 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/tests/test_terminal.py +0 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/tests/test_tui_credential_manager.py +0 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/tests/test_tui_select_credential.py +0 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/tests/test_update.py +0 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/tests/test_update_check_cmd.py +0 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/tests/test_update_notify.py +0 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/tests/test_urls.py +0 -0
- {jpsync-1.2.0 → jpsync-1.3.0}/tests/test_version_changelog.py +0 -0
|
@@ -5,6 +5,18 @@ All notable changes to this project are documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.3.0](https://github.com/pehqge/jpsync/compare/v1.2.0...v1.3.0) (2026-06-05)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* **live:** jp live — mount your remote Jupyter folder locally, edit + run over the kernel ([#26](https://github.com/pehqge/jpsync/issues/26)) ([5c14b23](https://github.com/pehqge/jpsync/commit/5c14b23810bf646a933c72dc24a757424ed961dc))
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
### Bug Fixes
|
|
17
|
+
|
|
18
|
+
* AI release-notes gh bug + config works outside a workspace ([#27](https://github.com/pehqge/jpsync/issues/27)) ([9c9c963](https://github.com/pehqge/jpsync/commit/9c9c9637aeea8d2ed08a105b6d7301960af1d09c))
|
|
19
|
+
|
|
8
20
|
## [1.2.0](https://github.com/pehqge/jpsync/compare/v1.1.1...v1.2.0) (2026-06-05)
|
|
9
21
|
|
|
10
22
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: jpsync
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.3.0
|
|
4
4
|
Summary: A git-like CLI to sync local folders with a remote JupyterHub via the Contents API.
|
|
5
5
|
Project-URL: Homepage, https://github.com/pehqge/jpsync
|
|
6
6
|
Project-URL: Repository, https://github.com/pehqge/jpsync
|
|
@@ -74,6 +74,7 @@ runs — macOS, Windows, Linux.
|
|
|
74
74
|
## Why jp?
|
|
75
75
|
|
|
76
76
|
- **Git-like workflow** — `jp clone`, `jp status`, `jp push`, `jp pull`. Same muscle memory.
|
|
77
|
+
- **Or skip the copy** — `jp live <url>` mounts the remote folder as a local folder you edit in place, and `jp terminal` drops you into a shell on the server. No SSH, no FUSE, no server-side install.
|
|
77
78
|
- **Safe by default** — on a *shared* research machine, jp never deletes remote files unless you explicitly turn that on, and even then it asks you file-by-file. Conflicts are never silently overwritten.
|
|
78
79
|
- **Zero dependencies** — one install, no dependency hell; ships as a wheel, a single `.pyz`, or a standalone binary.
|
|
79
80
|
- **Cross-platform** — macOS, Windows, Linux; Python 3.9 → 3.13.
|
|
@@ -228,6 +229,26 @@ It only opens an ephemeral terminal session (it never touches your files), and
|
|
|
228
229
|
the session is cleaned up when you exit. See the
|
|
229
230
|
[command reference](docs/commands.md#remote-shell) for the details.
|
|
230
231
|
|
|
232
|
+
### Bonus: edit the remote folder live (no clone)
|
|
233
|
+
|
|
234
|
+
Don't want a local copy at all — just open the remote folder and edit it in
|
|
235
|
+
place? `jp live <url>` mounts it as a normal folder on your machine over the same
|
|
236
|
+
kernel transport. Reads and (by default) writes travel straight to the server.
|
|
237
|
+
|
|
238
|
+
```bash
|
|
239
|
+
jp live https://jupyter.example.com/user/<you>/lab/tree/your-folder
|
|
240
|
+
# → confirms writable vs read-only, then mounts it under ./your-folder/
|
|
241
|
+
# edit with any tool; Ctrl-C (or `jp live unmount`) to stop.
|
|
242
|
+
|
|
243
|
+
jp live <url> --code # open it in VS Code with a remote shell wired up
|
|
244
|
+
jp live <url> --read-only # browse without any risk of writing
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
It mounts under one auto-managed handle (a folder on macOS/Linux, a drive letter
|
|
248
|
+
on Windows) using the OS's built-in WebDAV client — no FUSE, no third-party
|
|
249
|
+
deps. The mount is loopback-only and gated behind a per-session secret. Full
|
|
250
|
+
guide and the cross-OS notes: [docs/jp-live.md](docs/jp-live.md).
|
|
251
|
+
|
|
231
252
|
### Bonus: run a local script on the server — without pushing it
|
|
232
253
|
|
|
233
254
|
Editing a script locally and want to run it remotely without `jp push` on every
|
|
@@ -252,8 +273,8 @@ and your files are never overwritten. For Python, `sys.argv[0]`/`__file__`/
|
|
|
252
273
|
tracebacks show the real name, not the temp file. You see only the program's
|
|
253
274
|
output, streamed live — no remote shell prompt, and `input()` works just like
|
|
254
275
|
locally. Any **data** files the script opens must already exist on the remote
|
|
255
|
-
(`jp push` those first).
|
|
256
|
-
|
|
276
|
+
(`jp push` those first). Works on macOS, Linux and Windows (the interactive raw
|
|
277
|
+
proxy uses the same `jp.pty` backend as `jp terminal`).
|
|
257
278
|
|
|
258
279
|
---
|
|
259
280
|
|
|
@@ -270,13 +291,14 @@ with the `jp terminal` Windows fix.
|
|
|
270
291
|
| `jp pull` | Download remote changes. Additive by default. |
|
|
271
292
|
| `jp diff [path]` | Show file-level differences. |
|
|
272
293
|
| `jp ls [remote-path]` | List a remote directory (no local writes). |
|
|
294
|
+
| `jp live <url>` | Mount a remote folder as a local folder over the kernel websocket — edit it in your own tools; changes travel to the server. Writable by default (confirmed); `--read-only` opts out. `--code` opens it in VS Code with a remote shell. `jp live unmount` (from inside) stops it; `jp live --defaults` sets the defaults. ([guide](docs/jp-live.md)) |
|
|
273
295
|
| `jp open` | Open this workspace's folder (or the subfolder you're in) in the Jupyter web UI. Confirms first, shows the URL, and can copy instead of opening. Refuses folders jp never syncs (`.jp/`, hidden dot-names, `.jpignore` matches) since they aren't on the remote. Press `r` in the prompt to remember your choice (skip it next time); `jp open --ask` forgets it. Token-free URL; no network. |
|
|
274
296
|
| `jp config` | Interactive settings editor (see below). Also `config get/set/list`. |
|
|
275
297
|
| `jp ignore [pattern]` | Manage `.jpignore` patterns. |
|
|
276
298
|
| `jp rm <path>` | Delete on the remote — gated, dry-run + typed confirmation. The only deleter. |
|
|
277
299
|
| `jp kernel` | Set up a VS Code remote kernel to run notebooks in the right directory ([guide](docs/vscode-remote-cwd.md)). |
|
|
278
|
-
| `jp terminal` | Open the remote machine's shell in your terminal, in the workspace folder. Creates/deletes only an ephemeral terminal session; touches no files. |
|
|
279
|
-
| `jp run <file> [args…]` | Run a local script on the remote in the current mapped folder, without pushing it. Interpreter from shebang/extension or `--as`; `--dry-run` to preview. Propagates the exit code.
|
|
300
|
+
| `jp terminal [url]` | Open the remote machine's shell in your terminal, in the workspace folder. With a `<url>` it works standalone (no workspace needed). Creates/deletes only an ephemeral terminal session; touches no files. |
|
|
301
|
+
| `jp run <file> [args…]` | Run a local script on the remote in the current mapped folder, without pushing it. Interpreter from shebang/extension or `--as`; `--dry-run` to preview. Propagates the exit code. macOS/Linux/Windows. |
|
|
280
302
|
| `jp doctor` | Diagnose token, connectivity, server status. |
|
|
281
303
|
| `jp update` | Update jp to the latest version. |
|
|
282
304
|
| `jp changelog` | Show release notes (newer releases, a specific version, or `--all`). |
|
|
@@ -37,6 +37,7 @@ runs — macOS, Windows, Linux.
|
|
|
37
37
|
## Why jp?
|
|
38
38
|
|
|
39
39
|
- **Git-like workflow** — `jp clone`, `jp status`, `jp push`, `jp pull`. Same muscle memory.
|
|
40
|
+
- **Or skip the copy** — `jp live <url>` mounts the remote folder as a local folder you edit in place, and `jp terminal` drops you into a shell on the server. No SSH, no FUSE, no server-side install.
|
|
40
41
|
- **Safe by default** — on a *shared* research machine, jp never deletes remote files unless you explicitly turn that on, and even then it asks you file-by-file. Conflicts are never silently overwritten.
|
|
41
42
|
- **Zero dependencies** — one install, no dependency hell; ships as a wheel, a single `.pyz`, or a standalone binary.
|
|
42
43
|
- **Cross-platform** — macOS, Windows, Linux; Python 3.9 → 3.13.
|
|
@@ -191,6 +192,26 @@ It only opens an ephemeral terminal session (it never touches your files), and
|
|
|
191
192
|
the session is cleaned up when you exit. See the
|
|
192
193
|
[command reference](docs/commands.md#remote-shell) for the details.
|
|
193
194
|
|
|
195
|
+
### Bonus: edit the remote folder live (no clone)
|
|
196
|
+
|
|
197
|
+
Don't want a local copy at all — just open the remote folder and edit it in
|
|
198
|
+
place? `jp live <url>` mounts it as a normal folder on your machine over the same
|
|
199
|
+
kernel transport. Reads and (by default) writes travel straight to the server.
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
jp live https://jupyter.example.com/user/<you>/lab/tree/your-folder
|
|
203
|
+
# → confirms writable vs read-only, then mounts it under ./your-folder/
|
|
204
|
+
# edit with any tool; Ctrl-C (or `jp live unmount`) to stop.
|
|
205
|
+
|
|
206
|
+
jp live <url> --code # open it in VS Code with a remote shell wired up
|
|
207
|
+
jp live <url> --read-only # browse without any risk of writing
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
It mounts under one auto-managed handle (a folder on macOS/Linux, a drive letter
|
|
211
|
+
on Windows) using the OS's built-in WebDAV client — no FUSE, no third-party
|
|
212
|
+
deps. The mount is loopback-only and gated behind a per-session secret. Full
|
|
213
|
+
guide and the cross-OS notes: [docs/jp-live.md](docs/jp-live.md).
|
|
214
|
+
|
|
194
215
|
### Bonus: run a local script on the server — without pushing it
|
|
195
216
|
|
|
196
217
|
Editing a script locally and want to run it remotely without `jp push` on every
|
|
@@ -215,8 +236,8 @@ and your files are never overwritten. For Python, `sys.argv[0]`/`__file__`/
|
|
|
215
236
|
tracebacks show the real name, not the temp file. You see only the program's
|
|
216
237
|
output, streamed live — no remote shell prompt, and `input()` works just like
|
|
217
238
|
locally. Any **data** files the script opens must already exist on the remote
|
|
218
|
-
(`jp push` those first).
|
|
219
|
-
|
|
239
|
+
(`jp push` those first). Works on macOS, Linux and Windows (the interactive raw
|
|
240
|
+
proxy uses the same `jp.pty` backend as `jp terminal`).
|
|
220
241
|
|
|
221
242
|
---
|
|
222
243
|
|
|
@@ -233,13 +254,14 @@ with the `jp terminal` Windows fix.
|
|
|
233
254
|
| `jp pull` | Download remote changes. Additive by default. |
|
|
234
255
|
| `jp diff [path]` | Show file-level differences. |
|
|
235
256
|
| `jp ls [remote-path]` | List a remote directory (no local writes). |
|
|
257
|
+
| `jp live <url>` | Mount a remote folder as a local folder over the kernel websocket — edit it in your own tools; changes travel to the server. Writable by default (confirmed); `--read-only` opts out. `--code` opens it in VS Code with a remote shell. `jp live unmount` (from inside) stops it; `jp live --defaults` sets the defaults. ([guide](docs/jp-live.md)) |
|
|
236
258
|
| `jp open` | Open this workspace's folder (or the subfolder you're in) in the Jupyter web UI. Confirms first, shows the URL, and can copy instead of opening. Refuses folders jp never syncs (`.jp/`, hidden dot-names, `.jpignore` matches) since they aren't on the remote. Press `r` in the prompt to remember your choice (skip it next time); `jp open --ask` forgets it. Token-free URL; no network. |
|
|
237
259
|
| `jp config` | Interactive settings editor (see below). Also `config get/set/list`. |
|
|
238
260
|
| `jp ignore [pattern]` | Manage `.jpignore` patterns. |
|
|
239
261
|
| `jp rm <path>` | Delete on the remote — gated, dry-run + typed confirmation. The only deleter. |
|
|
240
262
|
| `jp kernel` | Set up a VS Code remote kernel to run notebooks in the right directory ([guide](docs/vscode-remote-cwd.md)). |
|
|
241
|
-
| `jp terminal` | Open the remote machine's shell in your terminal, in the workspace folder. Creates/deletes only an ephemeral terminal session; touches no files. |
|
|
242
|
-
| `jp run <file> [args…]` | Run a local script on the remote in the current mapped folder, without pushing it. Interpreter from shebang/extension or `--as`; `--dry-run` to preview. Propagates the exit code.
|
|
263
|
+
| `jp terminal [url]` | Open the remote machine's shell in your terminal, in the workspace folder. With a `<url>` it works standalone (no workspace needed). Creates/deletes only an ephemeral terminal session; touches no files. |
|
|
264
|
+
| `jp run <file> [args…]` | Run a local script on the remote in the current mapped folder, without pushing it. Interpreter from shebang/extension or `--as`; `--dry-run` to preview. Propagates the exit code. macOS/Linux/Windows. |
|
|
243
265
|
| `jp doctor` | Diagnose token, connectivity, server status. |
|
|
244
266
|
| `jp update` | Update jp to the latest version. |
|
|
245
267
|
| `jp changelog` | Show release notes (newer releases, a specific version, or `--all`). |
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
# `jp live` — your remote folder, mounted locally
|
|
2
|
+
|
|
3
|
+
`jp live <URL>` makes a folder on your Jupyter/JupyterHub server appear as a
|
|
4
|
+
normal folder on your machine. You open files in your own editor; reads and
|
|
5
|
+
(by default) writes travel to the server.
|
|
6
|
+
|
|
7
|
+
It does this **over the kernel websocket you already have access to** — the same
|
|
8
|
+
transport a notebook uses. There is **no SSH, no server-side install, and no new
|
|
9
|
+
service**. The only credential involved is your existing API token.
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
# mount the folder the URL points at, under ./jp-live-test/ (writable by default)
|
|
13
|
+
jp live https://host/user/you/lab/tree/privado/jp-live-test
|
|
14
|
+
|
|
15
|
+
# read-only, no prompt
|
|
16
|
+
jp live <URL> --read-only
|
|
17
|
+
|
|
18
|
+
# open it in VS Code with a remote shell already wired up
|
|
19
|
+
jp live <URL> --code
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
`jp live` is **workspace-free**: it does NOT need `jp init`/`jp clone` and does
|
|
23
|
+
NOT create a `.jp/` folder. The URL is the same kind you paste into `jp clone`.
|
|
24
|
+
|
|
25
|
+
## How it works
|
|
26
|
+
|
|
27
|
+
`jp live <URL>`:
|
|
28
|
+
|
|
29
|
+
1. parses the URL into a server + a remote sub-path (the *prefix*),
|
|
30
|
+
2. starts a kernel on your server,
|
|
31
|
+
3. injects a small **file agent** into that kernel (a single `execute_request`;
|
|
32
|
+
see `--print-agent` to read the exact code),
|
|
33
|
+
4. opens a Jupyter `comm` channel to talk to the agent,
|
|
34
|
+
5. serves that remote folder over a **loopback-only** WebDAV server (`127.0.0.1`),
|
|
35
|
+
and
|
|
36
|
+
6. mounts it under **one auto-managed local handle named after the prefix leaf**
|
|
37
|
+
— a folder on macOS/Linux, a drive letter on Windows.
|
|
38
|
+
|
|
39
|
+
When you exit (`Ctrl-C`), the mount is removed, anything `jp` created is cleaned
|
|
40
|
+
up, and the kernel is **deleted**, so it does not sit on a GPU/CPU slot.
|
|
41
|
+
|
|
42
|
+
## Command interface
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
jp live <URL> [--read-only] [--credential NAME] [--code] [--yes] [--mount POINT]
|
|
46
|
+
jp live --dry-run --root <folder> [--read-only] [--mount POINT] [--stats] # offline self-test
|
|
47
|
+
jp live --print-agent [--read-only] # transparency
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
- `<URL>` — the Jupyter URL of the folder, copied from your browser address bar
|
|
51
|
+
(e.g. `https://host/user/you/lab/tree/privado/jp-live-test`).
|
|
52
|
+
- **Writable is the default.** `--read-only` opts out (and skips the prompt).
|
|
53
|
+
- `--credential NAME` — pick a saved credential (otherwise: the single one is
|
|
54
|
+
used silently, or you are prompted; run `jp login` first if none).
|
|
55
|
+
- `--code` — open the mounted folder in VS Code with a remote shell running.
|
|
56
|
+
- `--yes` — skip the writable confirmation (assumes writable). Required for
|
|
57
|
+
unattended / non-tty runs.
|
|
58
|
+
- `--mount POINT` — override the auto target (advanced): a folder on
|
|
59
|
+
macOS/Linux, or a drive letter (`Z:`) on Windows. You own the target; it is
|
|
60
|
+
**not** removed on cleanup.
|
|
61
|
+
- `--dry-run --root <folder>` — offline self-test of the whole transport against
|
|
62
|
+
a **local** folder. No network. Writable is the default here too; `--read-only`
|
|
63
|
+
opts out.
|
|
64
|
+
- `--print-agent` — print the **exact** agent code that would be injected, then
|
|
65
|
+
exit (no network). With a `<URL>` it uses that URL's prefix; otherwise it falls
|
|
66
|
+
back to a workspace config.
|
|
67
|
+
|
|
68
|
+
## The writable confirmation
|
|
69
|
+
|
|
70
|
+
Before mounting, `jp live` lists the top of the folder so you can confirm it is
|
|
71
|
+
the folder you expect, then folds the write/read choice into a single prompt:
|
|
72
|
+
|
|
73
|
+
```
|
|
74
|
+
top of 'privado/jp-live-test':
|
|
75
|
+
• dummy.txt
|
|
76
|
+
Mount at ./jp-live-test -- [W]ritable (edits/deletes reach the server) or [r]ead-only? ([W]/r, c=cancel):
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
- **Enter** or `w` → writable. `r` → read-only. `c` (or anything else) → cancel.
|
|
80
|
+
- `--read-only` forces read-only and skips the prompt.
|
|
81
|
+
- `--yes` assumes writable and skips the prompt.
|
|
82
|
+
- A non-interactive shell (no tty) **without** `--yes` is refused.
|
|
83
|
+
|
|
84
|
+
The prompt (and the refresh warning, below) name the **per-OS handle** — the
|
|
85
|
+
folder on macOS/Linux, the drive letter on Windows — not a hardcoded path.
|
|
86
|
+
|
|
87
|
+
## Refresh semantics (important)
|
|
88
|
+
|
|
89
|
+
After mounting, `jp live` prints:
|
|
90
|
+
|
|
91
|
+
> Edits you make here save to the server immediately. Changes made ON the server
|
|
92
|
+
> appear here when your editor/file-browser re-reads a file (open or reload it)
|
|
93
|
+
> — there is no live push. For a live view of server-side changes (logs, job
|
|
94
|
+
> output), use a remote shell + `tail -f` (e.g. `jp terminal <URL>`).
|
|
95
|
+
|
|
96
|
+
## Cross-OS support matrix
|
|
97
|
+
|
|
98
|
+
| Capability | macOS | Linux | Windows |
|
|
99
|
+
| --- | --- | --- | --- |
|
|
100
|
+
| Mount + edit files | folder `./leaf/` | folder `./leaf/` (symlink→gvfs) | drive `Z:` |
|
|
101
|
+
| Write/read prompt, credential picker, workspace guard | ✅ | ✅ | ✅ |
|
|
102
|
+
| `jp terminal <URL>` standalone | ✅ PTY | ✅ PTY | ✅ PTY (VT console; web fallback pre-Win10 1511) |
|
|
103
|
+
| `--code` opens VS Code | `open -a` | `code` / `vscode://` URI | `code` / `vscode://` URI |
|
|
104
|
+
| `--code` integrated remote shell | ✅ | ✅ | ✅ (web fallback pre-Win10 1511) |
|
|
105
|
+
|
|
106
|
+
The auto handle is named after the **prefix leaf**: `privado/jp-live-test`
|
|
107
|
+
mounts under `./jp-live-test/` (macOS/Linux) or the first free drive letter
|
|
108
|
+
(Windows).
|
|
109
|
+
|
|
110
|
+
## `--code`: VS Code with a remote shell
|
|
111
|
+
|
|
112
|
+
`jp live <URL> --code`:
|
|
113
|
+
|
|
114
|
+
1. mounts the folder as above,
|
|
115
|
+
2. writes a **local** `<leaf>.code-workspace` next to the launch dir — never
|
|
116
|
+
inside the mount, because the server rejects dotfiles,
|
|
117
|
+
3. opens it in VS Code (macOS: `open -a "Visual Studio Code"`; else the `code`
|
|
118
|
+
CLI if on PATH; else the `vscode://file/...` URI; else it prints the path),
|
|
119
|
+
4. VS Code shows a one-time **"Allow Automatic Tasks"** prompt and then runs
|
|
120
|
+
`jp terminal "<URL>"` in an integrated terminal — a real raw remote shell on
|
|
121
|
+
macOS, Linux **and** Windows (the Windows path drives the console's
|
|
122
|
+
virtual-terminal modes directly; on a Windows too old for VT, pre-Win10 1511,
|
|
123
|
+
it falls back to the Jupyter web terminal).
|
|
124
|
+
|
|
125
|
+
On exit, the `.code-workspace` file `jp` created is removed along with the mount.
|
|
126
|
+
|
|
127
|
+
## Safety model
|
|
128
|
+
|
|
129
|
+
`jp live` is deliberately paranoid because the server is often shared:
|
|
130
|
+
|
|
131
|
+
- **Refuses to run inside a `.jp` workspace.** If you are in a `jp` workspace
|
|
132
|
+
tree (the root or any subfolder), `jp live` refuses, so a live mount can never
|
|
133
|
+
be nested inside something a `push`/`pull` would walk. `cd` to a plain
|
|
134
|
+
directory first.
|
|
135
|
+
- **Never deletes your data.** The auto handle is created only if absent, reused
|
|
136
|
+
only if empty, and **refused** if it is a non-empty existing folder. Cleanup
|
|
137
|
+
uses `rmdir` (empty-only) / removes only the symlink and the `.code-workspace`
|
|
138
|
+
file `jp` itself created. Never `rm -rf`. With `--mount POINT` the target is
|
|
139
|
+
yours and is never removed.
|
|
140
|
+
- **Writable is gated.** Writable is the default, but a single confirmation
|
|
141
|
+
(showing the folder's top entries) precedes the mount; `--read-only` forces
|
|
142
|
+
read-only, `--yes` assumes writable, and a non-tty without `--yes` is refused.
|
|
143
|
+
- **Never recursive remote delete.** A directory delete only succeeds if the
|
|
144
|
+
directory is already empty; a non-empty directory is refused. There is no code
|
|
145
|
+
path that recursively deletes the remote.
|
|
146
|
+
- **Double path-jail.** The remote prefix is validated locally
|
|
147
|
+
(`paths.validate_prefix`) *and* enforced again inside the agent on the server.
|
|
148
|
+
Shared/too-broad names (`shared`, `public`, `common`, `compartilhado`,
|
|
149
|
+
`lapix`, the server root, `..`, …) are refused before any connection is made.
|
|
150
|
+
- **No automatic undo when writable.** In writable mode the agent overwrites
|
|
151
|
+
remote files in place. There is **no automatic server-side undo** — keep your
|
|
152
|
+
own backup before writing. (Removals are still never recursive.)
|
|
153
|
+
- **`--code` writes nothing to the server.** The `.code-workspace` is local.
|
|
154
|
+
- **Kernel released on exit.** The serving loop deletes the kernel in a
|
|
155
|
+
`finally`, even on `Ctrl-C` or error.
|
|
156
|
+
- **Token never in a URL.** Your token travels only in the `Authorization`
|
|
157
|
+
header on the websocket handshake.
|
|
158
|
+
- **Capability-secret on the mount.** Loopback is not a per-user boundary: any
|
|
159
|
+
local process that finds the ephemeral port could otherwise read (or, when
|
|
160
|
+
writable, write) the mounted files. So the server serves only under a random
|
|
161
|
+
128-bit secret path segment — the URL is `http://127.0.0.1:<port>/<secret>/`
|
|
162
|
+
— and refuses any request without it (HTTP 404). This closes the trivial
|
|
163
|
+
port-scan vector.
|
|
164
|
+
|
|
165
|
+
**Residual, be honest about it:** while the mount is active, the full URL
|
|
166
|
+
(secret included) appears in the OS mount table — `mount` on macOS/Linux,
|
|
167
|
+
`net use` on Windows — which any local user can read. So the secret defends
|
|
168
|
+
against blind local port-scanners, **not** against a local user who actively
|
|
169
|
+
inspects the mount table on the *same* machine. Native OS mounting records
|
|
170
|
+
the URL by design; fully closing this would require a non-native transport
|
|
171
|
+
(e.g. a Unix-domain socket with per-user permissions), which would drop the
|
|
172
|
+
"mount with the OS's built-in client, no FUSE" property. On a hostile
|
|
173
|
+
multi-user host: prefer read-only, and unmount (`Ctrl-C`) when idle rather
|
|
174
|
+
than leaving a writable mount up.
|
|
175
|
+
|
|
176
|
+
## Overriding the mount target
|
|
177
|
+
|
|
178
|
+
Auto-mount is the default. Pass `--mount <point>` to choose your own target — a
|
|
179
|
+
folder (macOS/Linux) or a drive letter like `Z:` (Windows). `jp live` mounts
|
|
180
|
+
there and, on exit, only **unmounts** it: the target is yours and is never
|
|
181
|
+
removed. If the mount fails, `jp live` prints the exact command to run by hand.
|
|
182
|
+
The WebDAV URL is `http://127.0.0.1:<port>/<secret>/` (printed each run).
|
|
183
|
+
|
|
184
|
+
Manual mount commands (the `<port>` stands in for the printed value):
|
|
185
|
+
|
|
186
|
+
- **macOS** (`mount_webdav`):
|
|
187
|
+
|
|
188
|
+
```sh
|
|
189
|
+
mkdir -p ./jp-live-test
|
|
190
|
+
mount_webdav -S http://127.0.0.1:<port>/<secret>/ ./jp-live-test
|
|
191
|
+
umount ./jp-live-test
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
- **Windows** (WebClient redirector):
|
|
195
|
+
|
|
196
|
+
```bat
|
|
197
|
+
net use Z: http://127.0.0.1:<port>/<secret>/
|
|
198
|
+
net use Z: /delete /y
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
- **Linux** (GVfs, userspace, no root):
|
|
202
|
+
|
|
203
|
+
```sh
|
|
204
|
+
gio mount dav://127.0.0.1:<port>/<secret>/
|
|
205
|
+
gio mount -u dav://127.0.0.1:<port>/<secret>/
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
`davfs2` (`mount -t davfs http://127.0.0.1:<port>/<secret>/ /mnt/jp`) also
|
|
209
|
+
works if you prefer a system mount.
|
|
210
|
+
|
|
211
|
+
Leave `jp live` running while you use the folder; it keeps the kernel alive
|
|
212
|
+
(pinging every ~30s to defeat idle-culling) until you press `Ctrl-C`.
|
|
213
|
+
|
|
214
|
+
## Auditing the injected code: `--print-agent`
|
|
215
|
+
|
|
216
|
+
You do not have to trust a description of what runs on the server — you can read
|
|
217
|
+
it. `jp live --print-agent` prints the **exact** bootstrap code that the mount
|
|
218
|
+
would inject and exits without touching the network:
|
|
219
|
+
|
|
220
|
+
```sh
|
|
221
|
+
jp live <URL> --print-agent # the (writable-by-default) agent
|
|
222
|
+
jp live <URL> --print-agent --read-only # the read-only variant
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
The output is the verbatim agent source plus the small registration shim, so you
|
|
226
|
+
can review precisely what the kernel will execute before you trust it.
|
|
227
|
+
|
|
228
|
+
## Manual per-OS test checklist
|
|
229
|
+
|
|
230
|
+
Run against a real, **personal** test folder on a server you control. Repeat per
|
|
231
|
+
OS (macOS, Linux, Windows).
|
|
232
|
+
|
|
233
|
+
1. **Mount.** `jp live <URL>` → confirm the top-of-folder listing matches, press
|
|
234
|
+
Enter (writable). Verify the handle appears: `./<leaf>/` (macOS/Linux) or the
|
|
235
|
+
drive letter (Windows).
|
|
236
|
+
2. **ls / cat.** List the mounted folder and open a known file in your editor;
|
|
237
|
+
the contents match the server.
|
|
238
|
+
3. **Add propagates.** Create a new file in the mount; confirm it appears on the
|
|
239
|
+
server (Jupyter file browser).
|
|
240
|
+
4. **Edit propagates.** Edit and save a file; confirm the server copy updates.
|
|
241
|
+
5. **Delete propagates.** Delete a file; confirm it is gone on the server. (A
|
|
242
|
+
non-empty directory delete is refused — expected.)
|
|
243
|
+
6. **Read-only.** Re-run with `--read-only`; confirm writes/deletes are rejected.
|
|
244
|
+
7. **`--code`.** `jp live <URL> --code` opens VS Code on the folder; accept
|
|
245
|
+
"Allow Automatic Tasks"; a `jp terminal` shell appears in the integrated
|
|
246
|
+
terminal (web fallback on Windows). Confirm the `.code-workspace` file is in
|
|
247
|
+
the launch dir, **not** inside the mount.
|
|
248
|
+
8. **Ctrl-C cleanup.** Press `Ctrl-C`; confirm the mount is gone, the auto
|
|
249
|
+
folder/symlink and the `.code-workspace` file are removed (an empty auto
|
|
250
|
+
folder is `rmdir`-ed; a `--mount` target is left in place), and the kernel is
|
|
251
|
+
deleted on the server.
|
|
252
|
+
9. **Workspace guard.** From inside a `jp` workspace tree, `jp live <URL>`
|
|
253
|
+
refuses with the "must run OUTSIDE a workspace" message.
|
|
@@ -10,7 +10,7 @@ import contextlib
|
|
|
10
10
|
|
|
11
11
|
__all__ = ["__version__"]
|
|
12
12
|
|
|
13
|
-
__version__ = "1.
|
|
13
|
+
__version__ = "1.3.0" # x-release-please-version
|
|
14
14
|
|
|
15
15
|
try: # Prefer the installed distribution's version when present.
|
|
16
16
|
from importlib.metadata import PackageNotFoundError
|
|
File without changes
|