nuspace 0.6.0__tar.gz → 0.6.2__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.
- {nuspace-0.6.0 → nuspace-0.6.2}/CHANGELOG.md +20 -2
- {nuspace-0.6.0 → nuspace-0.6.2}/PKG-INFO +16 -13
- {nuspace-0.6.0 → nuspace-0.6.2}/README.md +9 -7
- {nuspace-0.6.0 → nuspace-0.6.2}/examples/space.py +2 -1
- {nuspace-0.6.0 → nuspace-0.6.2}/pyproject.toml +9 -6
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/_cli/_installed.py +33 -11
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/_cli/_meta.py +48 -25
- nuspace-0.6.2/src/nuspace/_cli/commands/init.py +60 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/_cli/commands/run.py +26 -11
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/_cli/commands/telemetry.py +9 -19
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/_telemetry/__init__.py +11 -8
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/_telemetry/config.py +16 -6
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/_telemetry/send.py +34 -10
- nuspace-0.6.2/src/nuspace/host/presets.py +173 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/host/space.py +8 -5
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/ops/__init__.py +2 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/ops/cell.py +40 -15
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/ops/extend.py +28 -4
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/ops/plane.py +32 -6
- nuspace-0.6.2/src/nuspace/ops/usage.py +115 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/shapes/__init__.py +1 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/shapes/space.py +7 -2
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/system/kernel/__init__.py +8 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/system/kernel/kernel.py +4 -6
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/system/kernel/space.py +89 -22
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/system/ui/home.py +2 -3
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/system/ui/search.py +3 -3
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/system/ui/seed.py +6 -9
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/system/ui/settings.py +1 -2
- {nuspace-0.6.0 → nuspace-0.6.2}/tests/nuspace/test_cli.py +262 -33
- {nuspace-0.6.0 → nuspace-0.6.2}/tests/nuspace/test_host.py +55 -31
- {nuspace-0.6.0 → nuspace-0.6.2}/tests/nuspace/test_search.py +1 -1
- {nuspace-0.6.0 → nuspace-0.6.2}/tests/nuspace/test_settings.py +2 -6
- {nuspace-0.6.0 → nuspace-0.6.2}/tests/nuspace/test_telemetry.py +63 -16
- nuspace-0.6.0/src/nuspace/_cli/commands/init.py +0 -43
- nuspace-0.6.0/src/nuspace/host/presets.py +0 -116
- nuspace-0.6.0/src/nuspace/system/usage.py +0 -146
- {nuspace-0.6.0 → nuspace-0.6.2}/.gitignore +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/.pre-commit-config.yaml +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/LICENSE.md +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/Makefile +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/pkgs/ts/nuspace-ui/python/nuspace_ui/build +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/__init__.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/_branding/__init__.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/_branding/header.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/_branding/logo.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/_branding/palette.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/_cli/__init__.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/_cli/__main__.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/_cli/commands/__init__.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/_cli/commands/verse.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/_cli/main.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/content/__init__.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/content/kind.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/content/search.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/ext/__init__.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/ext/registry.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/ext/specs.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/host/__init__.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/ops/kernel.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/ops/pin.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/ops/read.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/ops/state.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/ops/tree.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/ops/utils.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/shapes/cell.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/shapes/connection.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/shapes/contents.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/shapes/kernel.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/shapes/plane.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/shapes/reroot.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/shapes/state.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/shapes/states.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/system/__init__.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/system/backends/__init__.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/system/backends/async_.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/system/backends/base.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/system/backends/mp.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/system/backends/pool.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/system/devices/__init__.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/system/devices/web/__init__.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/system/devices/web/device.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/system/devices/web/env.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/system/devices/web/session.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/system/devices/web/shell.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/system/devices/web/sidebar/__init__.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/system/devices/web/sidebar/feed.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/system/devices/web/sidebar/interactions.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/system/devices/web/sidebar/ref.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/system/devices/web/utils.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/system/devices/web/viewer/__init__.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/system/devices/web/viewer/feed.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/system/devices/web/viewer/interactions.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/system/devices/web/viewer/ref.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/system/info.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/system/kernel/body.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/system/kernel/dispatch.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/system/kernel/envs.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/system/kernel/out.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/system/kernel/reconcile.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/system/kernel/runs.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/system/kernel/utils.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/system/services/__init__.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/system/services/bootstrap.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/system/services/init.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/system/services/nav.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/system/services/reactions.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/system/services/reload.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/system/services/supervisor.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/system/ui/__init__.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/system/utils.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/src/nuspace/ui.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/tests/_ext/nuspace_testing/__init__.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/tests/_ext/pyproject.toml +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/tests/_lib/_support/__init__.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/tests/_lib/_support/actions.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/tests/_lib/_support/contents.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/tests/_lib/_support/kernel.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/tests/_lib/_support/kernel_envs.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/tests/_lib/_support/made.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/tests/_lib/_support/probe.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/tests/_lib/_support/specs.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/tests/conftest.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/tests/nuspace/test_content.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/tests/nuspace/test_home.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/tests/nuspace/test_kernel.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/tests/nuspace/test_ops_kernel.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/tests/nuspace/test_ops_state.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/tests/nuspace/test_ops_structure.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/tests/nuspace/test_packaging.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/tests/nuspace/test_reactions.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/tests/nuspace/test_services.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/tests/nuspace/test_session_pipe.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/tests/nuspace/test_shapes.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/tests/nuspace/test_ui.py +0 -0
- {nuspace-0.6.0 → nuspace-0.6.2}/tests/nuspace/test_web.py +0 -0
|
@@ -2,13 +2,31 @@
|
|
|
2
2
|
|
|
3
3
|
## Packages shipped from this repo
|
|
4
4
|
|
|
5
|
-
- **nuspace** (the runtime, the `nuspace` command) — 0.6.
|
|
6
|
-
- **nuspace-ui** (the compiled web bundle) — 0.5.
|
|
5
|
+
- **nuspace** (the runtime, the `nuspace` command) — 0.6.2
|
|
6
|
+
- **nuspace-ui** (the compiled web bundle) — 0.5.8
|
|
7
7
|
|
|
8
8
|
Below is the changelog for **nuspace** - the full commit stream. Newest first.
|
|
9
9
|
The `nuspace-ui` wheel is the same source tree's vite output and is not tracked
|
|
10
10
|
separately here.
|
|
11
11
|
|
|
12
|
+
## 0.6.2 — 2026-10-30
|
|
13
|
+
|
|
14
|
+
- Release nuspace 0.6.2 on nu 0.6.8 and nuverse 0.3.5
|
|
15
|
+
- Trim the README description to what nuspace is, dropping the example list
|
|
16
|
+
- Keep space data in .nuspace and find spaces like git, with a home default
|
|
17
|
+
- Export usage tracking from ops so planes can count first-run steps
|
|
18
|
+
- Package planes get their own id and reopen the same plane across runs
|
|
19
|
+
- Bare plane names default to nuverse, so first runs need no package name
|
|
20
|
+
- Restructure telemetry tracking
|
|
21
|
+
- Edit README and summary to match the new identity lines
|
|
22
|
+
|
|
23
|
+
## 0.6.1 — 2026-10-27
|
|
24
|
+
|
|
25
|
+
- Release nuspace 0.6.1, built on nuspace-ui 0.5.8 and ui-kit 0.3.6
|
|
26
|
+
- Update sidebar and seed tests for the new rail layout
|
|
27
|
+
- Restructure the sidebar into nav rows, pinned, planes and extensions, one keyboard stop
|
|
28
|
+
- Pick plane icons from the whole lucide set, loaded on demand
|
|
29
|
+
|
|
12
30
|
## 0.6.0 — 2026-10-26
|
|
13
31
|
|
|
14
32
|
- Release nuspace 0.6.0, built on nuspace-ui 0.5.7 and nu 0.6.7
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: nuspace
|
|
3
|
-
Version: 0.6.
|
|
4
|
-
Summary: Computational space for
|
|
3
|
+
Version: 0.6.2
|
|
4
|
+
Summary: Computational space for your data and tools.
|
|
5
5
|
Project-URL: Repository, https://github.com/nustackdev/nuspace
|
|
6
6
|
Author-email: Gor Arakelyan <gorarkln@gmail.com>
|
|
7
7
|
License-Expression: AGPL-3.0-or-later
|
|
@@ -15,11 +15,12 @@ Classifier: Programming Language :: Python :: 3.11
|
|
|
15
15
|
Classifier: Programming Language :: Python :: 3.12
|
|
16
16
|
Classifier: Programming Language :: Python :: 3.13
|
|
17
17
|
Requires-Python: >=3.10
|
|
18
|
+
Requires-Dist: certifi>=2023.7.22
|
|
18
19
|
Requires-Dist: click>=8.0
|
|
19
|
-
Requires-Dist: nucore>=0.6.
|
|
20
|
-
Requires-Dist: nuspace-ui>=0.5.
|
|
21
|
-
Requires-Dist: nustd[kv,mp-pool,proxy,ui,valkey]>=0.6.
|
|
22
|
-
Requires-Dist: nuverse>=0.3.
|
|
20
|
+
Requires-Dist: nucore>=0.6.8
|
|
21
|
+
Requires-Dist: nuspace-ui>=0.5.8
|
|
22
|
+
Requires-Dist: nustd[kv,mp-pool,proxy,ui,valkey]>=0.6.8
|
|
23
|
+
Requires-Dist: nuverse>=0.3.5
|
|
23
24
|
Requires-Dist: rich-click>=1.6
|
|
24
25
|
Requires-Dist: rich>=10.7
|
|
25
26
|
Requires-Dist: typing-extensions>=4.7.0
|
|
@@ -31,7 +32,8 @@ Description-Content-Type: text/markdown
|
|
|
31
32
|
<div align="center">
|
|
32
33
|
<img width="112" alt="nuspace" src="https://nustack.dev/brand/logo-square-512@2x.png" />
|
|
33
34
|
<h1>nuspace</h1>
|
|
34
|
-
<p>Computational space for
|
|
35
|
+
<p>Computational space for your data and tools. Human-first. Agent-native.</p>
|
|
36
|
+
<p><sub>Open-source · Self-hosted · Pure Python</sub></p>
|
|
35
37
|
|
|
36
38
|
[](https://pypi.org/project/nuspace/)
|
|
37
39
|
[](https://pypi.org/project/nuspace/)
|
|
@@ -44,7 +46,7 @@ Description-Content-Type: text/markdown
|
|
|
44
46
|
</div>
|
|
45
47
|
|
|
46
48
|
|
|
47
|
-
|
|
49
|
+
In nuspace, your data and tools live in cells: tiny reactive programs that manage data, run logic and render UI. Cells are written in nu, a Python programming model with no glue code between layers, so a cell is a few lines, not a thousand. Humans get the space as UI, agents get it as code.
|
|
48
50
|
|
|
49
51
|
**nuspace in action:**
|
|
50
52
|
|
|
@@ -186,10 +188,11 @@ Install from PyPI and run a space yourself. Python 3.10 or newer.
|
|
|
186
188
|
2. **Run**
|
|
187
189
|
|
|
188
190
|
```bash
|
|
189
|
-
nuspace run
|
|
191
|
+
nuspace run
|
|
190
192
|
```
|
|
191
193
|
|
|
192
|
-
A browser opens on your space.
|
|
194
|
+
A browser opens on your space: the nearest one up from here, else your home space.
|
|
195
|
+
`nuspace init` makes the current directory a space of its own, data in `.nuspace/`.
|
|
193
196
|
|
|
194
197
|
3. **Try a demo**
|
|
195
198
|
|
|
@@ -198,7 +201,7 @@ Install from PyPI and run a space yourself. Python 3.10 or newer.
|
|
|
198
201
|
nuspace run --with nudemos.movies
|
|
199
202
|
```
|
|
200
203
|
|
|
201
|
-
A
|
|
204
|
+
A full app in your space. Add `--tmp` for a throwaway space instead.
|
|
202
205
|
|
|
203
206
|
</details>
|
|
204
207
|
|
|
@@ -300,8 +303,8 @@ Any agent that reads skills learns nu and nuspace from them, installs nuspace an
|
|
|
300
303
|
| 01 | <code>the theory</code> <br/> [**interaction-model**](https://github.com/nustackdev/interaction-model) | A programming model where every resource is a Ref and Interactions are first-class, so you compose logic across systems. |
|
|
301
304
|
| 02 | <code>the primitive</code> <br/> [**nu**](https://github.com/nustackdev/nu) | The interaction model, implemented in Python. |
|
|
302
305
|
| 03 | <code>the std library</code> <br/> [**nustd**](https://github.com/nustackdev/nu/tree/main/pkgs/nustd) | Batteries for nu: storage, UI, workers and more, ready to use. |
|
|
303
|
-
| 04 | <code>the space</code> <br/> [**nuspace**](https://github.com/nustackdev/nuspace) | A space that runs nu programs.
|
|
304
|
-
| 05 | <code>the ecosystem</code> <br/> [**nuverse**](https://github.com/nustackdev/nuverse) | Ready-made
|
|
306
|
+
| 04 | <code>the space</code> <br/> [**nuspace**](https://github.com/nustackdev/nuspace) | A space that runs nu programs. |
|
|
307
|
+
| 05 | <code>the ecosystem</code> <br/> [**nuverse**](https://github.com/nustackdev/nuverse) | Ready-made packages, one install away. |
|
|
305
308
|
| + | **your space**, on top of it all | Everything below exists so your space can be anything. |
|
|
306
309
|
|
|
307
310
|
## Community
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
<div align="center">
|
|
2
2
|
<img width="112" alt="nuspace" src="https://nustack.dev/brand/logo-square-512@2x.png" />
|
|
3
3
|
<h1>nuspace</h1>
|
|
4
|
-
<p>Computational space for
|
|
4
|
+
<p>Computational space for your data and tools. Human-first. Agent-native.</p>
|
|
5
|
+
<p><sub>Open-source · Self-hosted · Pure Python</sub></p>
|
|
5
6
|
|
|
6
7
|
[](https://pypi.org/project/nuspace/)
|
|
7
8
|
[](https://pypi.org/project/nuspace/)
|
|
@@ -14,7 +15,7 @@
|
|
|
14
15
|
</div>
|
|
15
16
|
|
|
16
17
|
|
|
17
|
-
|
|
18
|
+
In nuspace, your data and tools live in cells: tiny reactive programs that manage data, run logic and render UI. Cells are written in nu, a Python programming model with no glue code between layers, so a cell is a few lines, not a thousand. Humans get the space as UI, agents get it as code.
|
|
18
19
|
|
|
19
20
|
**nuspace in action:**
|
|
20
21
|
|
|
@@ -156,10 +157,11 @@ Install from PyPI and run a space yourself. Python 3.10 or newer.
|
|
|
156
157
|
2. **Run**
|
|
157
158
|
|
|
158
159
|
```bash
|
|
159
|
-
nuspace run
|
|
160
|
+
nuspace run
|
|
160
161
|
```
|
|
161
162
|
|
|
162
|
-
A browser opens on your space.
|
|
163
|
+
A browser opens on your space: the nearest one up from here, else your home space.
|
|
164
|
+
`nuspace init` makes the current directory a space of its own, data in `.nuspace/`.
|
|
163
165
|
|
|
164
166
|
3. **Try a demo**
|
|
165
167
|
|
|
@@ -168,7 +170,7 @@ Install from PyPI and run a space yourself. Python 3.10 or newer.
|
|
|
168
170
|
nuspace run --with nudemos.movies
|
|
169
171
|
```
|
|
170
172
|
|
|
171
|
-
A
|
|
173
|
+
A full app in your space. Add `--tmp` for a throwaway space instead.
|
|
172
174
|
|
|
173
175
|
</details>
|
|
174
176
|
|
|
@@ -270,8 +272,8 @@ Any agent that reads skills learns nu and nuspace from them, installs nuspace an
|
|
|
270
272
|
| 01 | <code>the theory</code> <br/> [**interaction-model**](https://github.com/nustackdev/interaction-model) | A programming model where every resource is a Ref and Interactions are first-class, so you compose logic across systems. |
|
|
271
273
|
| 02 | <code>the primitive</code> <br/> [**nu**](https://github.com/nustackdev/nu) | The interaction model, implemented in Python. |
|
|
272
274
|
| 03 | <code>the std library</code> <br/> [**nustd**](https://github.com/nustackdev/nu/tree/main/pkgs/nustd) | Batteries for nu: storage, UI, workers and more, ready to use. |
|
|
273
|
-
| 04 | <code>the space</code> <br/> [**nuspace**](https://github.com/nustackdev/nuspace) | A space that runs nu programs.
|
|
274
|
-
| 05 | <code>the ecosystem</code> <br/> [**nuverse**](https://github.com/nustackdev/nuverse) | Ready-made
|
|
275
|
+
| 04 | <code>the space</code> <br/> [**nuspace**](https://github.com/nustackdev/nuspace) | A space that runs nu programs. |
|
|
276
|
+
| 05 | <code>the ecosystem</code> <br/> [**nuverse**](https://github.com/nustackdev/nuverse) | Ready-made packages, one install away. |
|
|
275
277
|
| + | **your space**, on top of it all | Everything below exists so your space can be anything. |
|
|
276
278
|
|
|
277
279
|
## Community
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
Every Plane, Cell and content kind comes from an installed extension, found
|
|
4
4
|
through its entry point.
|
|
5
|
-
The space is
|
|
5
|
+
The space's root is ``examples/example.nuspace``, its data in ``.nuspace/``
|
|
6
|
+
inside it, kept between runs.
|
|
6
7
|
|
|
7
8
|
Run it::
|
|
8
9
|
|
|
@@ -4,8 +4,8 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "nuspace"
|
|
7
|
-
version = "0.6.
|
|
8
|
-
description = "Computational space for
|
|
7
|
+
version = "0.6.2"
|
|
8
|
+
description = "Computational space for your data and tools."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "AGPL-3.0-or-later"
|
|
11
11
|
authors = [{ name = "Gor Arakelyan", email = "gorarkln@gmail.com" }]
|
|
@@ -14,21 +14,24 @@ dependencies = [
|
|
|
14
14
|
# The fabrics a space runs on, nothing more: kv for the store, valkey for
|
|
15
15
|
# its change notifications, ui for the browser, mp_pool for the workers,
|
|
16
16
|
# proxy (invisibles) for the worker's session.
|
|
17
|
-
"nucore>=0.6.
|
|
18
|
-
"nustd[kv,ui,valkey,mp_pool,proxy]>=0.6.
|
|
17
|
+
"nucore>=0.6.8",
|
|
18
|
+
"nustd[kv,ui,valkey,mp_pool,proxy]>=0.6.8",
|
|
19
19
|
# The store's change notifications run over virtuals' redis backend.
|
|
20
20
|
"virtuals-py[redis]>=0.1.10",
|
|
21
21
|
# The compiled web bundle. A separate distribution only because PyPI caps
|
|
22
22
|
# project size and a SPA rebuilt on every UI tweak would eat the quota --
|
|
23
23
|
# not optional. `pip install nuspace` gives you the browser.
|
|
24
|
-
"nuspace-ui>=0.5.
|
|
24
|
+
"nuspace-ui>=0.5.8",
|
|
25
25
|
# The everyday Planes and Cells. A plugin like any other, found through
|
|
26
26
|
# its entry point, but every space ships with it.
|
|
27
|
-
"nuverse>=0.3.
|
|
27
|
+
"nuverse>=0.3.5",
|
|
28
28
|
"typing-extensions>=4.7.0",
|
|
29
29
|
"click>=8.0",
|
|
30
30
|
"rich>=10.7",
|
|
31
31
|
"rich-click>=1.6",
|
|
32
|
+
# CA bundle for telemetry's HTTPS POST: python.org macOS builds ship none,
|
|
33
|
+
# so without it every send fails verification, silently.
|
|
34
|
+
"certifi>=2023.7.22",
|
|
32
35
|
]
|
|
33
36
|
keywords = [
|
|
34
37
|
"nu",
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
``nuspace verse`` lists it and ``nuspace run --with`` picks a Plane out of
|
|
4
4
|
it. A package is its entry point's name, and its Planes are
|
|
5
5
|
``<package>.<name>``, as a space registers them
|
|
6
|
-
(:func:`~nuspace.ext.qualified`).
|
|
6
|
+
(:func:`~nuspace.ext.qualified`). A bare name is a Plane of
|
|
7
|
+
:data:`DEFAULT_PACKAGE`, the one nuspace ships with.
|
|
7
8
|
"""
|
|
8
9
|
|
|
9
10
|
from __future__ import annotations
|
|
@@ -24,7 +25,11 @@ if TYPE_CHECKING:
|
|
|
24
25
|
from nuspace.ext import Extension, PlaneSpec
|
|
25
26
|
|
|
26
27
|
|
|
27
|
-
__all__ = ["Installed", "installed", "resolve"]
|
|
28
|
+
__all__ = ["DEFAULT_PACKAGE", "Installed", "installed", "resolve"]
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
DEFAULT_PACKAGE = "nuverse"
|
|
32
|
+
"""The package a bare plane name is looked up in: ``jobs`` is ``nuverse.jobs``."""
|
|
28
33
|
|
|
29
34
|
|
|
30
35
|
@dataclass(frozen=True)
|
|
@@ -61,21 +66,38 @@ def installed(group: str = GROUP) -> list[Installed]:
|
|
|
61
66
|
def resolve(spec: str, packages: list[Installed] | None = None) -> PlaneSpec:
|
|
62
67
|
"""``pkg.plane`` as the Plane it names, qualified as a space registers it.
|
|
63
68
|
|
|
69
|
+
A bare name, with no dot, is a Plane of :data:`DEFAULT_PACKAGE`.
|
|
70
|
+
|
|
64
71
|
Raises:
|
|
65
|
-
click.ClickException: ``spec`` is not ``pkg.plane
|
|
66
|
-
package adds it.
|
|
72
|
+
click.ClickException: ``spec`` is not ``pkg.plane`` or a bare name, or
|
|
73
|
+
no installed package adds it.
|
|
67
74
|
"""
|
|
68
75
|
packages = installed() if packages is None else packages
|
|
69
|
-
|
|
76
|
+
bare = "." not in spec
|
|
77
|
+
full = f"{DEFAULT_PACKAGE}.{spec}" if bare else spec
|
|
78
|
+
pkg, _, name = full.partition(".")
|
|
70
79
|
hits = [
|
|
71
80
|
plane
|
|
72
81
|
for p in packages
|
|
73
82
|
if p.extension is not None and p.name == pkg
|
|
74
83
|
for plane in qualified(p.extension, p.name).planes
|
|
75
|
-
if plane.name ==
|
|
84
|
+
if plane.name == full
|
|
76
85
|
]
|
|
77
|
-
if
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
)
|
|
81
|
-
|
|
86
|
+
if pkg and name and hits:
|
|
87
|
+
return hits[0]
|
|
88
|
+
if bare:
|
|
89
|
+
raise click.ClickException(_not_in_default(spec, packages))
|
|
90
|
+
raise click.ClickException(
|
|
91
|
+
f"No plane {spec!r} installed. Name it pkg.plane, see: nuspace verse"
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def _not_in_default(spec: str, packages: list[Installed]) -> str:
|
|
96
|
+
"""Why bare ``spec`` names no Plane: what :data:`DEFAULT_PACKAGE` has instead, if anything."""
|
|
97
|
+
ext = next((p.extension for p in packages if p.name == DEFAULT_PACKAGE and p.extension), None)
|
|
98
|
+
head = f"No plane {spec!r} in {DEFAULT_PACKAGE}, where a bare name is looked up."
|
|
99
|
+
tail = "Name another package's as pkg.plane, see: nuspace verse"
|
|
100
|
+
if ext is None:
|
|
101
|
+
return f"{head} {DEFAULT_PACKAGE} is not installed or failed to load. {tail}"
|
|
102
|
+
names = ", ".join(plane.name for plane in ext.planes) or "none"
|
|
103
|
+
return f"{head} Its planes: {names}. {tail}"
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
"""What the commands share: the console, the versions, and how
|
|
1
|
+
"""What the commands share: the console, the versions, which space, and how it is held open.
|
|
2
|
+
|
|
3
|
+
Which space, the way ``git`` finds a repository: PATH when given, else the
|
|
4
|
+
nearest space at or above the current directory, else the home space (root
|
|
5
|
+
``~``, data in ``~/.nuspace``). ``--tmp`` is a throwaway one instead.
|
|
6
|
+
"""
|
|
2
7
|
|
|
3
8
|
from __future__ import annotations
|
|
4
9
|
|
|
@@ -14,24 +19,22 @@ from rich.text import Text
|
|
|
14
19
|
import nu
|
|
15
20
|
from nuspace import _telemetry
|
|
16
21
|
from nuspace._branding import BLUE, header
|
|
17
|
-
from nuspace.system.kernel import NotASpace
|
|
18
|
-
from nuspace.system.usage import Usage
|
|
22
|
+
from nuspace.system.kernel import DATA_DIR, NotASpace, find_space
|
|
19
23
|
|
|
20
24
|
|
|
21
25
|
__all__ = [
|
|
22
|
-
"STORE_ENV",
|
|
23
26
|
"announce",
|
|
24
27
|
"console",
|
|
25
28
|
"hold",
|
|
26
29
|
"opened",
|
|
27
30
|
"path_argument",
|
|
31
|
+
"space_root",
|
|
32
|
+
"tilde",
|
|
33
|
+
"tmp_option",
|
|
28
34
|
"version_of",
|
|
29
35
|
]
|
|
30
36
|
|
|
31
37
|
|
|
32
|
-
#: The environment variable naming the space directory when no path is given.
|
|
33
|
-
STORE_ENV = "NUSPACE_STORE"
|
|
34
|
-
|
|
35
38
|
console = Console()
|
|
36
39
|
|
|
37
40
|
_F = TypeVar("_F", bound=Callable[..., Any])
|
|
@@ -46,17 +49,37 @@ def version_of(package: str) -> str:
|
|
|
46
49
|
|
|
47
50
|
|
|
48
51
|
def path_argument(fn: _F) -> _F:
|
|
49
|
-
"""The space
|
|
50
|
-
return click.argument("path", required=False
|
|
52
|
+
"""The space's root a command takes, positional and optional: see :func:`space_root`."""
|
|
53
|
+
return click.argument("path", required=False)(fn)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def tmp_option(fn: _F) -> _F:
|
|
57
|
+
"""``--tmp``: a throwaway space instead of one found, for the commands that open one."""
|
|
58
|
+
return click.option(
|
|
59
|
+
"--tmp", is_flag=True, help="A throwaway space in a temp dir, gone at close."
|
|
60
|
+
)(fn)
|
|
61
|
+
|
|
51
62
|
|
|
63
|
+
def space_root(path: str | None, *, tmp: bool = False) -> str | None:
|
|
64
|
+
"""The root of the space a command means, absolute. None with ``tmp``, a throwaway.
|
|
52
65
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
66
|
+
PATH when given, else the nearest space at or above the current
|
|
67
|
+
directory, else home.
|
|
68
|
+
"""
|
|
69
|
+
if tmp:
|
|
70
|
+
if path is not None:
|
|
71
|
+
raise click.UsageError("Give PATH or --tmp, not both.")
|
|
72
|
+
return None
|
|
73
|
+
if path is not None:
|
|
74
|
+
return str(Path(path).expanduser().resolve())
|
|
75
|
+
return str(find_space(Path.cwd()) or Path.home())
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def tilde(path: str | Path) -> str:
|
|
79
|
+
"""``path`` as a person reads it: absolute, home as ``~``."""
|
|
57
80
|
root = Path(path).expanduser().resolve()
|
|
58
81
|
try:
|
|
59
|
-
return str(Path("~") / root.relative_to(Path.home()))
|
|
82
|
+
return str(Path("~") / root.relative_to(Path.home().resolve()))
|
|
60
83
|
except ValueError:
|
|
61
84
|
return str(root)
|
|
62
85
|
|
|
@@ -68,7 +91,13 @@ def announce(path: str | None, *, headless: bool = False, plane: str | None = No
|
|
|
68
91
|
the header leaves both to it.
|
|
69
92
|
"""
|
|
70
93
|
console.print(
|
|
71
|
-
header(
|
|
94
|
+
header(
|
|
95
|
+
"sm",
|
|
96
|
+
version=version_of("nuspace"),
|
|
97
|
+
where="throwaway space, gone at close"
|
|
98
|
+
if path is None
|
|
99
|
+
else f"Path: {tilde(Path(path) / DATA_DIR)}",
|
|
100
|
+
),
|
|
72
101
|
highlight=False,
|
|
73
102
|
soft_wrap=True,
|
|
74
103
|
)
|
|
@@ -91,21 +120,15 @@ def opened(preset: Callable[..., nu.Nu], *args: object, **kwargs: object) -> nu.
|
|
|
91
120
|
def hold(term: nu.Nu) -> None:
|
|
92
121
|
"""Run a space until interrupted, then let the brackets reap the fleet.
|
|
93
122
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
so the host tracks what is made and removed in it, and what is still
|
|
98
|
-
queued at the end gets a short grace to go.
|
|
123
|
+
What is still queued for telemetry (see :mod:`nuspace._telemetry`) gets
|
|
124
|
+
a short grace to go at the end. The space tells it what happens itself,
|
|
125
|
+
from its open on, the same as when opened from code.
|
|
99
126
|
|
|
100
127
|
Compiled and evaluated without law validation: the space program is ours
|
|
101
128
|
and tested, and validating its whole tree costs seconds per start.
|
|
102
129
|
"""
|
|
103
|
-
_telemetry.first_run()
|
|
104
|
-
_telemetry.track("space_opened")
|
|
105
130
|
try:
|
|
106
|
-
nu.eval_in_loop(
|
|
107
|
-
nu.compile(nu.Provide(Usage, {"sink": _telemetry.track}, term)), nu.Context()
|
|
108
|
-
)
|
|
131
|
+
nu.eval_in_loop(nu.compile(term), nu.Context())
|
|
109
132
|
except KeyboardInterrupt:
|
|
110
133
|
console.print("[dim]Stopped[/dim]")
|
|
111
134
|
finally:
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"""``nuspace init``: make a directory a space, without opening it to run.
|
|
2
|
+
|
|
3
|
+
The store only, through :func:`~nuspace.host.init_space`: ``.nuspace/``
|
|
4
|
+
with its two SQLite files and the system planes, seeded as the first open
|
|
5
|
+
would. Without PATH it is the current directory, said so when that sits
|
|
6
|
+
inside another space. No venv, no packaging: the directory is not set up
|
|
7
|
+
for code of its own yet.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
|
|
14
|
+
import rich_click as click
|
|
15
|
+
from rich.text import Text
|
|
16
|
+
|
|
17
|
+
import nu
|
|
18
|
+
from nuspace._branding import BLUE, PURPLE
|
|
19
|
+
from nuspace.host import init_space
|
|
20
|
+
from nuspace.system.kernel import find_space, is_space
|
|
21
|
+
|
|
22
|
+
from .._meta import console, opened, path_argument, tilde
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
__all__ = ["init"]
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@click.command(
|
|
29
|
+
short_help="Make a directory a space, without running it.",
|
|
30
|
+
help=(
|
|
31
|
+
"Make PATH a space, the current directory without one: its data in .nuspace/,"
|
|
32
|
+
" the system planes seeded, nothing run. No venv or packaging is set up yet."
|
|
33
|
+
),
|
|
34
|
+
)
|
|
35
|
+
@path_argument
|
|
36
|
+
def init(path: str | None) -> None:
|
|
37
|
+
"""Make the space, or say it already is one."""
|
|
38
|
+
root = Path(path or ".").expanduser().resolve()
|
|
39
|
+
if path is None:
|
|
40
|
+
_warn_if_nested(root)
|
|
41
|
+
there = is_space(root)
|
|
42
|
+
term = opened(init_space, str(root))
|
|
43
|
+
nu.eval_in_loop(nu.compile(term), nu.Context())
|
|
44
|
+
what = "Already a space" if there else "Initialized a space"
|
|
45
|
+
console.print(Text.assemble((what, f"bold {PURPLE}"), (" ", ""), (tilde(root), BLUE)))
|
|
46
|
+
again = "nuspace run" if path is None else f"nuspace run {path}"
|
|
47
|
+
console.print(Text.assemble(("open it with: ", "dim"), (again, f"bold {BLUE}")))
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _warn_if_nested(root: Path) -> None:
|
|
51
|
+
"""Say so when ``root`` sits inside another space. The home space, everyone's fallback, does not count."""
|
|
52
|
+
outer = find_space(root.parent) if root != root.parent else None
|
|
53
|
+
if outer is not None and outer != Path.home().resolve():
|
|
54
|
+
console.print(
|
|
55
|
+
Text.assemble(
|
|
56
|
+
("inside the space at ", "yellow"),
|
|
57
|
+
(tilde(outer), BLUE),
|
|
58
|
+
(", making a new one here", "yellow"),
|
|
59
|
+
)
|
|
60
|
+
)
|
|
@@ -2,23 +2,28 @@
|
|
|
2
2
|
|
|
3
3
|
One flag, one :mod:`nuspace.host` preset: :func:`~nuspace.host.serve` by
|
|
4
4
|
default, :func:`~nuspace.host.headless` with ``--headless``. Runs until
|
|
5
|
-
interrupted, then the brackets reap the workers on the way out.
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
interrupted, then the brackets reap the workers on the way out. Which space
|
|
6
|
+
is :func:`~nuspace._cli._meta.space_root`: PATH, made a space if it is not
|
|
7
|
+
one, else the nearest space up from here, else home. ``--tmp`` is a
|
|
8
|
+
throwaway one, gone at close. The header names the space's root either way.
|
|
8
9
|
|
|
9
10
|
``--with`` names a Plane an installed package adds, ``pkg.plane`` as
|
|
10
|
-
``nuspace verse`` lists them
|
|
11
|
-
:func:`~nuspace.host.with_plane`, and the tab
|
|
11
|
+
``nuspace verse`` lists them, or a bare name for a nuverse Plane: it is made
|
|
12
|
+
in the space once, through :func:`~nuspace.host.with_plane`, and the tab
|
|
13
|
+
opens on it. The space remembers the plane it made, so the next run with
|
|
14
|
+
the same name reopens it. The header names the Plane: its id is only known
|
|
15
|
+
once the space is open.
|
|
12
16
|
"""
|
|
13
17
|
|
|
14
18
|
from __future__ import annotations
|
|
15
19
|
|
|
16
20
|
import rich_click as click
|
|
17
21
|
|
|
22
|
+
from nuspace.ext.registry import registered_name
|
|
18
23
|
from nuspace.host import presets
|
|
19
24
|
|
|
20
25
|
from .._installed import resolve
|
|
21
|
-
from .._meta import
|
|
26
|
+
from .._meta import announce, hold, opened, path_argument, space_root, tmp_option
|
|
22
27
|
|
|
23
28
|
|
|
24
29
|
__all__ = ["run"]
|
|
@@ -27,17 +32,20 @@ __all__ = ["run"]
|
|
|
27
32
|
@click.command(
|
|
28
33
|
short_help="Open a space, in a browser or headless.",
|
|
29
34
|
help=(
|
|
30
|
-
"Open the space at PATH and serve it in a browser, or headless:
|
|
31
|
-
|
|
35
|
+
"Open the space at PATH, made if missing, and serve it in a browser, or headless:"
|
|
36
|
+
" kernel and services, nothing drawn. Without PATH, the nearest space up from"
|
|
37
|
+
" here, else the home space at ~."
|
|
32
38
|
),
|
|
33
39
|
)
|
|
34
40
|
@path_argument
|
|
41
|
+
@tmp_option
|
|
35
42
|
@click.option(
|
|
36
43
|
"--with",
|
|
37
44
|
"plane",
|
|
38
45
|
metavar="PLANE",
|
|
39
46
|
help=(
|
|
40
|
-
"Make this plane in the space once and open on it: pkg.plane, as nuspace verse
|
|
47
|
+
"Make this plane in the space once and open on it: pkg.plane, as nuspace verse"
|
|
48
|
+
" lists them, or a bare name for a nuverse plane (jobs is nuverse.jobs)."
|
|
41
49
|
),
|
|
42
50
|
)
|
|
43
51
|
@click.option(
|
|
@@ -47,13 +55,20 @@ __all__ = ["run"]
|
|
|
47
55
|
@click.option("-p", "--port", default=8080, show_default=True, help="Port to bind.")
|
|
48
56
|
@click.option("--no-browser", is_flag=True, help="Do not open a browser tab on the way up.")
|
|
49
57
|
def run(
|
|
50
|
-
path: str | None,
|
|
58
|
+
path: str | None,
|
|
59
|
+
tmp: bool,
|
|
60
|
+
plane: str | None,
|
|
61
|
+
headless: bool,
|
|
62
|
+
host: str,
|
|
63
|
+
port: int,
|
|
64
|
+
no_browser: bool,
|
|
51
65
|
) -> None:
|
|
52
66
|
"""Run until Ctrl+C."""
|
|
67
|
+
path = space_root(path, tmp=tmp)
|
|
53
68
|
web = {"host": host, "port": port, "open_browser": not no_browser}
|
|
54
69
|
if plane is not None:
|
|
55
70
|
spec = resolve(plane)
|
|
56
|
-
made =
|
|
71
|
+
made = registered_name(spec)
|
|
57
72
|
if headless:
|
|
58
73
|
term = opened(presets.with_plane, spec, path, web=False)
|
|
59
74
|
else:
|
|
@@ -6,15 +6,13 @@ you open. ``NUSPACE_TELEMETRY=0`` turns it off whatever the file says.
|
|
|
6
6
|
|
|
7
7
|
from __future__ import annotations
|
|
8
8
|
|
|
9
|
-
from pathlib import Path
|
|
10
|
-
|
|
11
9
|
import rich_click as click
|
|
12
10
|
from rich.text import Text
|
|
13
11
|
|
|
14
12
|
from nuspace import _telemetry
|
|
15
13
|
from nuspace._branding import BLUE, PURPLE
|
|
16
14
|
|
|
17
|
-
from .._meta import console
|
|
15
|
+
from .._meta import console, tilde
|
|
18
16
|
|
|
19
17
|
|
|
20
18
|
__all__ = ["telemetry"]
|
|
@@ -52,19 +50,11 @@ def _switch(on: bool) -> str:
|
|
|
52
50
|
"""How to flip it from here."""
|
|
53
51
|
if on:
|
|
54
52
|
return "nuspace telemetry off"
|
|
55
|
-
if _telemetry.env_override():
|
|
56
|
-
return f"unset ${
|
|
53
|
+
if (name := _telemetry.env_override()) is not None:
|
|
54
|
+
return f"unset ${name}"
|
|
57
55
|
return "nuspace telemetry on"
|
|
58
56
|
|
|
59
57
|
|
|
60
|
-
def _where(path: Path) -> str:
|
|
61
|
-
"""``path`` as a person reads it, home as ``~``."""
|
|
62
|
-
try:
|
|
63
|
-
return str(Path("~") / path.relative_to(Path.home()))
|
|
64
|
-
except ValueError:
|
|
65
|
-
return str(path)
|
|
66
|
-
|
|
67
|
-
|
|
68
58
|
def _print(*lines: Text | str) -> None:
|
|
69
59
|
for line in lines:
|
|
70
60
|
console.print(line, highlight=False, soft_wrap=True)
|
|
@@ -77,7 +67,7 @@ def _print(*lines: Text | str) -> None:
|
|
|
77
67
|
"Anonymous usage telemetry, set once for you across every space. It sends a few usage"
|
|
78
68
|
" events, never anything from inside your spaces. Status with no command. nuspace is"
|
|
79
69
|
" still young, so we'd appreciate you keeping it on."
|
|
80
|
-
f" ${_telemetry.ENV}=0 turns it off whatever is set here."
|
|
70
|
+
f" ${_telemetry.ENV}=0, $DO_NOT_TRACK or $CI turns it off whatever is set here."
|
|
81
71
|
),
|
|
82
72
|
)
|
|
83
73
|
@click.pass_context
|
|
@@ -92,8 +82,8 @@ def status() -> None:
|
|
|
92
82
|
"""Print the state and why, what is sent and what never is, the id, the path, how to switch."""
|
|
93
83
|
on = _telemetry.telemetry_enabled()
|
|
94
84
|
_print(_state_line(on))
|
|
95
|
-
if _telemetry.env_override():
|
|
96
|
-
_print(f"[dim]off by ${
|
|
85
|
+
if (name := _telemetry.env_override()) is not None:
|
|
86
|
+
_print(f"[dim]off by ${name}, whatever the config says[/dim]")
|
|
97
87
|
if _telemetry.is_dev_install():
|
|
98
88
|
_print("[dim]dev install: nothing is sent either way[/dim]")
|
|
99
89
|
_print(
|
|
@@ -103,7 +93,7 @@ def status() -> None:
|
|
|
103
93
|
"",
|
|
104
94
|
_heading("Your settings"),
|
|
105
95
|
_row("id", _telemetry.distinct_id(), BLUE),
|
|
106
|
-
_row("config",
|
|
96
|
+
_row("config", tilde(_telemetry.config_path()), BLUE),
|
|
107
97
|
_row("turn " + ("off" if on else "on"), _switch(on), BLUE),
|
|
108
98
|
"",
|
|
109
99
|
f"[dim]{_WHY}[/dim]",
|
|
@@ -115,8 +105,8 @@ def on() -> None:
|
|
|
115
105
|
"""Set the flag on."""
|
|
116
106
|
_telemetry.set_telemetry(True)
|
|
117
107
|
_print(_state_line(True))
|
|
118
|
-
if _telemetry.env_override():
|
|
119
|
-
_print(f"[dim]still off while ${
|
|
108
|
+
if (name := _telemetry.env_override()) is not None:
|
|
109
|
+
_print(f"[dim]still off while ${name} says so[/dim]")
|
|
120
110
|
else:
|
|
121
111
|
_print("[dim]Thank you, it helps a lot while nuspace is young.[/dim]")
|
|
122
112
|
|