nuspace 0.2.0__tar.gz → 0.2.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (155) hide show
  1. {nuspace-0.2.0 → nuspace-0.2.1}/CHANGELOG.md +13 -2
  2. {nuspace-0.2.0 → nuspace-0.2.1}/PKG-INFO +6 -6
  3. {nuspace-0.2.0 → nuspace-0.2.1}/README.md +1 -1
  4. {nuspace-0.2.0 → nuspace-0.2.1}/examples/apps.py +4 -4
  5. {nuspace-0.2.0 → nuspace-0.2.1}/examples/pages.py +12 -12
  6. {nuspace-0.2.0 → nuspace-0.2.1}/examples/web.py +2 -2
  7. {nuspace-0.2.0 → nuspace-0.2.1}/pyproject.toml +7 -5
  8. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/agent/model.py +5 -5
  9. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/agent/ops.py +1 -1
  10. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/agent/prompt.py +7 -6
  11. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/agent/runner.py +7 -7
  12. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/agent/shapes.py +12 -12
  13. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/apps/runner.py +10 -8
  14. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/apps/shapes.py +8 -8
  15. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/chat/shapes.py +2 -2
  16. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/cli/main.py +1 -1
  17. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/core/host.py +11 -11
  18. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/core/session.py +8 -8
  19. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/core/shapes.py +9 -8
  20. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/core/tpl.py +9 -9
  21. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/core/ui.py +5 -5
  22. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/pages/runner.py +74 -47
  23. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/pages/shapes.py +14 -14
  24. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/apps/driver.py +2 -2
  25. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/apps/ref.py +2 -2
  26. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/chat/driver.py +3 -3
  27. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/chat/ref.py +2 -2
  28. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/lens/driver.py +2 -2
  29. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/lens/ref.py +2 -2
  30. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/lens/reflect.py +2 -2
  31. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/nav.py +2 -2
  32. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/pages/driver.py +2 -2
  33. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/pages/ref.py +2 -2
  34. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/pages/session.py +9 -9
  35. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/serve/__init__.py +2 -2
  36. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/serve/app.py +1 -1
  37. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/serve/fabric.py +1 -1
  38. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/serve/session.py +4 -4
  39. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/serve/shell.py +3 -3
  40. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/space.py +1 -2
  41. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/ui/package-lock.json +2 -2
  42. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/ui/package.json +1 -1
  43. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/ui/pyproject.toml +1 -1
  44. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/ui/src/app/router.ts +1 -1
  45. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/ui/src/app/theme.ts +1 -1
  46. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/ui/src/components/index.ts +3 -3
  47. nuspace-0.2.1/src/nuspace/web/ui/src/components/page-header.tsx +180 -0
  48. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/ui/src/design/apps.ts +2 -2
  49. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/ui/src/design/document.ts +201 -43
  50. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/ui/src/design/index.ts +1 -3
  51. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/ui/src/design/tokens.css +40 -5
  52. nuspace-0.2.1/src/nuspace/web/ui/src/refs/pages/Canvas.tsx +1349 -0
  53. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/ui/src/refs/pages/Code.tsx +38 -2
  54. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/ui/src/refs/pages/Program.tsx +15 -90
  55. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/ui/src/refs/pages/ProseRef.tsx +63 -11
  56. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/ui/src/refs/pages/Slash.tsx +47 -44
  57. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/ui/src/refs/pages/blocks.ts +8 -6
  58. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/ui/src/refs/pages/pages.tsx +16 -8
  59. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/ui/src/refs/pages/state.ts +31 -7
  60. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/wire.py +2 -2
  61. {nuspace-0.2.0 → nuspace-0.2.1}/tests/nuspace/apps/conftest.py +7 -7
  62. {nuspace-0.2.0 → nuspace-0.2.1}/tests/nuspace/apps/test_changed_key.py +3 -3
  63. {nuspace-0.2.0 → nuspace-0.2.1}/tests/nuspace/apps/test_ops.py +6 -6
  64. {nuspace-0.2.0 → nuspace-0.2.1}/tests/nuspace/pages/conftest.py +13 -13
  65. {nuspace-0.2.0 → nuspace-0.2.1}/tests/nuspace/web/test_apps_driver.py +3 -3
  66. {nuspace-0.2.0 → nuspace-0.2.1}/tests/nuspace/web/test_lens_driver.py +3 -3
  67. {nuspace-0.2.0 → nuspace-0.2.1}/tests/nuspace/web/test_lens_reflect.py +11 -11
  68. {nuspace-0.2.0 → nuspace-0.2.1}/tests/nuspace/web/test_pages_driver.py +3 -3
  69. {nuspace-0.2.0 → nuspace-0.2.1}/tests/nuspace/web/test_serve.py +5 -5
  70. {nuspace-0.2.0 → nuspace-0.2.1}/tests/nuspace/web/test_supervised.py +9 -9
  71. nuspace-0.2.0/src/nuspace/web/ui/src/components/page-header.tsx +0 -275
  72. nuspace-0.2.0/src/nuspace/web/ui/src/design/header.ts +0 -150
  73. nuspace-0.2.0/src/nuspace/web/ui/src/refs/pages/Canvas.tsx +0 -839
  74. {nuspace-0.2.0 → nuspace-0.2.1}/.github/workflows/publish-nuspace-ui.yml +0 -0
  75. {nuspace-0.2.0 → nuspace-0.2.1}/.github/workflows/publish-nuspace.yml +0 -0
  76. {nuspace-0.2.0 → nuspace-0.2.1}/.gitignore +0 -0
  77. {nuspace-0.2.0 → nuspace-0.2.1}/.pre-commit-config.yaml +0 -0
  78. {nuspace-0.2.0 → nuspace-0.2.1}/LICENSE.md +0 -0
  79. {nuspace-0.2.0 → nuspace-0.2.1}/Makefile +0 -0
  80. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/__init__.py +0 -0
  81. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/_root.py +0 -0
  82. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/agent/__init__.py +0 -0
  83. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/apps/__init__.py +0 -0
  84. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/apps/ops.py +0 -0
  85. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/chat/__init__.py +0 -0
  86. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/chat/ops.py +0 -0
  87. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/cli/__init__.py +0 -0
  88. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/cli/__main__.py +0 -0
  89. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/cli/_meta.py +0 -0
  90. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/core/__init__.py +0 -0
  91. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/core/ids.py +0 -0
  92. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/pages/__init__.py +0 -0
  93. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/pages/ops.py +0 -0
  94. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/__init__.py +0 -0
  95. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/apps/__init__.py +0 -0
  96. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/arms.py +0 -0
  97. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/chat/__init__.py +0 -0
  98. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/lens/__init__.py +0 -0
  99. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/pages/__init__.py +0 -0
  100. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/ui/.gitignore +0 -0
  101. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/ui/biome.json +0 -0
  102. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/ui/index.html +0 -0
  103. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/ui/src/App.tsx +0 -0
  104. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/ui/src/app/connect.ts +0 -0
  105. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/ui/src/app/local.ts +0 -0
  106. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/ui/src/app/surfaces.ts +0 -0
  107. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/ui/src/app/wire.ts +0 -0
  108. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/ui/src/components/rail/header.tsx +0 -0
  109. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/ui/src/components/rail/index.ts +0 -0
  110. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/ui/src/components/rail/roving-focus.ts +0 -0
  111. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/ui/src/components/rail/row-input.tsx +0 -0
  112. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/ui/src/components/rail/row.tsx +0 -0
  113. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/ui/src/components/section-status-dot.tsx +0 -0
  114. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/ui/src/design/chat.ts +0 -0
  115. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/ui/src/design/lens.css +0 -0
  116. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/ui/src/design/lens.ts +0 -0
  117. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/ui/src/design/rail.ts +0 -0
  118. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/ui/src/design/section-status.ts +0 -0
  119. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/ui/src/design/shell.ts +0 -0
  120. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/ui/src/index.css +0 -0
  121. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/ui/src/main.tsx +0 -0
  122. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/ui/src/refs/apps/Rail.tsx +0 -0
  123. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/ui/src/refs/apps/Source.tsx +0 -0
  124. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/ui/src/refs/apps/apps.tsx +0 -0
  125. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/ui/src/refs/apps/ops.ts +0 -0
  126. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/ui/src/refs/apps/state.ts +0 -0
  127. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/ui/src/refs/apps/types.ts +0 -0
  128. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/ui/src/refs/chat/chat.tsx +0 -0
  129. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/ui/src/refs/chat/ops.ts +0 -0
  130. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/ui/src/refs/chat/types.ts +0 -0
  131. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/ui/src/refs/lens/lens.tsx +0 -0
  132. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/ui/src/refs/lens/types.ts +0 -0
  133. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/ui/src/refs/nav/nav.ts +0 -0
  134. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/ui/src/refs/pages/ProseRef.test.tsx +0 -0
  135. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/ui/src/refs/pages/Rail.tsx +0 -0
  136. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/ui/src/refs/pages/monaco-impl.ts +0 -0
  137. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/ui/src/refs/pages/monaco.ts +0 -0
  138. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/ui/src/refs/pages/ops.ts +0 -0
  139. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/ui/src/refs/pages/prose.ts +0 -0
  140. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/ui/src/refs/pages/types.ts +0 -0
  141. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/ui/src/refs/register.ts +0 -0
  142. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/ui/src/vite-env.d.ts +0 -0
  143. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/ui/tsconfig.app.json +0 -0
  144. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/ui/tsconfig.json +0 -0
  145. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/ui/tsconfig.node.json +0 -0
  146. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/ui/vite.config.ts +0 -0
  147. {nuspace-0.2.0 → nuspace-0.2.1}/src/nuspace/web/ui/vitest.config.ts +0 -0
  148. {nuspace-0.2.0 → nuspace-0.2.1}/tests/nuspace/apps/__init__.py +0 -0
  149. {nuspace-0.2.0 → nuspace-0.2.1}/tests/nuspace/apps/test_lifecycle.py +0 -0
  150. {nuspace-0.2.0 → nuspace-0.2.1}/tests/nuspace/pages/__init__.py +0 -0
  151. {nuspace-0.2.0 → nuspace-0.2.1}/tests/nuspace/pages/test_invariant.py +0 -0
  152. {nuspace-0.2.0 → nuspace-0.2.1}/tests/nuspace/pages/test_lifecycle.py +0 -0
  153. {nuspace-0.2.0 → nuspace-0.2.1}/tests/nuspace/pages/test_ops.py +0 -0
  154. {nuspace-0.2.0 → nuspace-0.2.1}/tests/nuspace/test_imports.py +0 -0
  155. {nuspace-0.2.0 → nuspace-0.2.1}/tests/nuspace/web/__init__.py +0 -0
@@ -2,13 +2,24 @@
2
2
 
3
3
  ## Packages shipped from this repo
4
4
 
5
- - **nuspace** (the runtime, the `nuspace` command) — 0.2.0
6
- - **nuspace-ui** (the compiled web bundle) — 0.2.0
5
+ - **nuspace** (the runtime, the `nuspace` command) — 0.2.1
6
+ - **nuspace-ui** (the compiled web bundle) — 0.2.1
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.2.1 — 2026-09-17
13
+
14
+ - Open a page on quiet air and its own name instead of decoration
15
+ - Type where you want a block instead of choosing what to make first
16
+ - Adding or deleting a block now leaves every other block on the page running
17
+ - Hovering a page block now reveals only that block's controls, and they hide again
18
+ - Slow the block controls' tooltips so passing over them says nothing
19
+ - Wipe a block's ui before it reruns, so its refs stop duplicating
20
+ - Give every block the same hover chrome: insert, drag, copy, code and status
21
+ - Follow nu 0.5.0's import split so apps and snippets keep resolving the fabrics
22
+
12
23
  ## 0.2.0 — 2026-09-15
13
24
 
14
25
  - Root a snippet's refs under its own block, so an author never says where
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: nuspace
3
- Version: 0.2.0
3
+ Version: 0.2.1
4
4
  Summary: Nu runtime built on Nu. A self-hosted programmable space that runs Nu apps in a tree of containers and applications.
5
5
  Project-URL: Repository, https://github.com/nustackdev/nuspace
6
6
  Author-email: Gor Arakelyan <gorarkln@gmail.com>
@@ -16,10 +16,10 @@ Classifier: Programming Language :: Python :: 3.12
16
16
  Classifier: Programming Language :: Python :: 3.13
17
17
  Requires-Python: >=3.10
18
18
  Requires-Dist: click>=8.0
19
- Requires-Dist: nuagent>=0.1.1
20
- Requires-Dist: nucore>=0.4.0
21
- Requires-Dist: nuspace-ui>=0.2.0
22
- Requires-Dist: nustd[all]>=0.4.0
19
+ Requires-Dist: nuagent>=0.1.3
20
+ Requires-Dist: nucore>=0.5.1
21
+ Requires-Dist: nuspace-ui>=0.2.1
22
+ Requires-Dist: nustd[all]>=0.5.1
23
23
  Requires-Dist: rich-click>=1.6
24
24
  Requires-Dist: rich>=10.7
25
25
  Requires-Dist: typing-extensions>=4.7.0
@@ -30,7 +30,7 @@ Description-Content-Type: text/markdown
30
30
  Nu runtime built on Nu. Self-hosted.
31
31
 
32
32
  A tree of containers and applications.
33
- The runtime evaluates Nu apps, provides the fabrics they use (nu.kv, nu.invisibles, nu.ray, nulog, nu.ui), and renders a human interface for the ones that opt in.
33
+ The runtime evaluates Nu apps, provides the fabrics they use (nustd.kv, nustd.proxy, nustd.mp_pool, nulog, nustd.ui), and renders a human interface for the ones that opt in.
34
34
 
35
35
  ## License
36
36
 
@@ -3,7 +3,7 @@
3
3
  Nu runtime built on Nu. Self-hosted.
4
4
 
5
5
  A tree of containers and applications.
6
- The runtime evaluates Nu apps, provides the fabrics they use (nu.kv, nu.invisibles, nu.ray, nulog, nu.ui), and renders a human interface for the ones that opt in.
6
+ The runtime evaluates Nu apps, provides the fabrics they use (nustd.kv, nustd.proxy, nustd.mp_pool, nulog, nustd.ui), and renders a human interface for the ones that opt in.
7
7
 
8
8
  ## License
9
9
 
@@ -34,7 +34,7 @@ ROOT = Path("/tmp/nuspace-apps-demo") # noqa: S108
34
34
  # gets the same scope a section does, and `section` is its own id: an app is a
35
35
  # section with no page. Editing `step` is what the demo edits.
36
36
  COUNTER = '''import nu
37
- import nu.kv
37
+ import nustd.kv
38
38
  from nuspace.core.shapes import Space
39
39
 
40
40
 
@@ -43,7 +43,7 @@ def out(section):
43
43
  data = Space.state[section].data
44
44
  now = nu.ToInt(data.get_item("ticks", nu.Str("0")))
45
45
  tick = data.set_item("ticks", nu.ToStr(now + nu.Int({step})))
46
- return nu.kv.auto_flow_atomic(
46
+ return nustd.kv.auto_flow_atomic(
47
47
  data.set_item("ticks", nu.Str("0")) >> nu.ForeverDo(nu.DelayedDo(0.1, tick)),
48
48
  scope=Space,
49
49
  )
@@ -53,7 +53,7 @@ def out(section):
53
53
  # processes, both reaching the one store through their own proxy, and the app
54
54
  # being watched is named by id rather than by a path formatted into the source.
55
55
  MIRROR = '''import nu
56
- import nu.kv
56
+ import nustd.kv
57
57
  from nuspace.core.shapes import Space
58
58
 
59
59
  WATCHED = "a_counter"
@@ -64,7 +64,7 @@ def out(section):
64
64
  mine = Space.state[section].data
65
65
  theirs = Space.state[WATCHED].data
66
66
  copy = mine.set_item("seen", nu.ToStr(theirs.get_item("ticks", nu.Str("0"))))
67
- return nu.kv.auto_flow_atomic(nu.ForeverDo(nu.DelayedDo(0.1, copy)), scope=Space)
67
+ return nustd.kv.auto_flow_atomic(nu.ForeverDo(nu.DelayedDo(0.1, copy)), scope=Space)
68
68
  '''
69
69
 
70
70
 
@@ -35,14 +35,14 @@ import sys
35
35
  from pathlib import Path
36
36
 
37
37
  import nu
38
- import nu.kv
39
- import nu.mp_pool
40
- import nu.proxy
38
+ import nustd.kv
39
+ import nustd.mp_pool
40
+ import nustd.proxy
41
41
  from nu.core.io import STDOUT
42
- from nu.kv.fabrics import Navigator
43
42
  from nuspace.apps import free_port, worker_init
44
43
  from nuspace.core.shapes import Space
45
44
  from nuspace.pages import ROOT_PAGE_ID, Runner, ops, page_tree
45
+ from nustd.kv.fabrics import Navigator
46
46
 
47
47
 
48
48
  ROOT = Path("/tmp/nuspace-pages-demo") # noqa: S108
@@ -54,7 +54,7 @@ PAGE = "guides"
54
54
  # row is reached by navigating -- `section` is the id this block runs under --
55
55
  # so nothing here builds a key. Editing `step` is what the demo edits.
56
56
  COUNTER = '''import nu
57
- import nu.kv
57
+ import nustd.kv
58
58
  from nuspace.core.shapes import Space
59
59
 
60
60
 
@@ -63,7 +63,7 @@ def out(section):
63
63
  data = Space.state[section].data
64
64
  now = nu.ToInt(data.get_item("ticks", nu.Str("0")))
65
65
  tick = data.set_item("ticks", nu.ToStr(now + nu.Int({step})))
66
- return nu.kv.auto_flow_atomic(
66
+ return nustd.kv.auto_flow_atomic(
67
67
  data.set_item("ticks", nu.Str("0")) >> nu.ForeverDo(nu.DelayedDo(0.1, tick)),
68
68
  scope=Space,
69
69
  )
@@ -76,7 +76,7 @@ def out(section):
76
76
  # section's row is `Space.state["s_counter"]`, an id used as a key, not a path
77
77
  # a python format hole built. Nothing is formatted into this source at all.
78
78
  MIRROR = '''import nu
79
- import nu.kv
79
+ import nustd.kv
80
80
  from nuspace.core.shapes import Space
81
81
 
82
82
  WATCHED = "s_counter"
@@ -87,7 +87,7 @@ def out(section):
87
87
  mine = Space.state[section].data
88
88
  theirs = Space.state[WATCHED].data
89
89
  copy = mine.set_item("seen", nu.ToStr(theirs.get_item("ticks", nu.Str("0"))))
90
- return nu.kv.auto_flow_atomic(nu.ForeverDo(nu.DelayedDo(0.1, copy)), scope=Space)
90
+ return nustd.kv.auto_flow_atomic(nu.ForeverDo(nu.DelayedDo(0.1, copy)), scope=Space)
91
91
  '''
92
92
 
93
93
 
@@ -155,10 +155,10 @@ def host(path, body):
155
155
  """
156
156
  address = f"127.0.0.1:{free_port()}"
157
157
  return nu.With(
158
- nu.kv.rocksdb_navigator(path),
158
+ nustd.kv.rocksdb_navigator(path),
159
159
  nu.Provide(dict, {}),
160
160
  nu.Provide(
161
- nu.proxy.InvisiblesServer,
161
+ nustd.proxy.InvisiblesServer,
162
162
  {
163
163
  "target": Navigator,
164
164
  "address": address,
@@ -167,7 +167,7 @@ def host(path, body):
167
167
  },
168
168
  ),
169
169
  nu.Provide(
170
- nu.mp_pool.WorkerPool,
170
+ nustd.mp_pool.WorkerPool,
171
171
  {"name": "nuspace-pages", "init": worker_init(address)},
172
172
  ),
173
173
  body=body,
@@ -179,7 +179,7 @@ def demo():
179
179
  ROOT.mkdir(parents=True, exist_ok=True)
180
180
  tree = host(
181
181
  str(ROOT / "db"),
182
- nu.kv.auto_flow_atomic(SEED, scope=Space)
182
+ nustd.kv.auto_flow_atomic(SEED, scope=Space)
183
183
  >> page_tree(PAGE, alongside=SCRIPT, duration=15.0),
184
184
  )
185
185
  asyncio.run(nu.arun(tree, nu.Context(), max_parallel=64))
@@ -44,7 +44,7 @@ import sys
44
44
  from pathlib import Path
45
45
 
46
46
  import nu
47
- import nu.kv
47
+ import nustd.kv
48
48
  from nuspace.core.shapes import Space
49
49
  from nuspace.web import space_tree
50
50
 
@@ -58,7 +58,7 @@ def demo(fresh=True):
58
58
  shutil.rmtree(ROOT, ignore_errors=True)
59
59
  ROOT.mkdir(parents=True, exist_ok=True)
60
60
  tree = space_tree(
61
- nu.kv.rocksdb_navigator(str(ROOT / "db"), tags=(Space,)),
61
+ nustd.kv.rocksdb_navigator(str(ROOT / "db"), tags=(Space,)),
62
62
  store_tag=Space,
63
63
  port=PORT,
64
64
  )
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "nuspace"
7
- version = "0.2.0"
7
+ version = "0.2.1"
8
8
  description = "Nu runtime built on Nu. A self-hosted programmable space that runs Nu apps in a tree of containers and applications."
9
9
  readme = "README.md"
10
10
  license = "AGPL-3.0-or-later"
@@ -16,13 +16,13 @@ dependencies = [
16
16
  # ui for the browser, proxy (invisibles) for the worker's session, mp_pool
17
17
  # for the workers themselves. Picking extras just means discovering the
18
18
  # missing one at import time.
19
- "nucore>=0.4.0",
20
- "nustd[all]>=0.4.0",
19
+ "nucore>=0.5.1",
20
+ "nustd[all]>=0.5.1",
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.2.0",
25
- "nuagent>=0.1.1",
24
+ "nuspace-ui>=0.2.1",
25
+ "nuagent>=0.1.3",
26
26
  "typing-extensions>=4.7.0",
27
27
  "click>=8.0",
28
28
  "rich>=10.7",
@@ -174,6 +174,8 @@ convention = "google"
174
174
  known-first-party = [
175
175
  "nuspace",
176
176
  "nu",
177
+ "nustd",
178
+ "nucli",
177
179
  ]
178
180
  force-single-line = false
179
181
  lines-after-imports = 2
@@ -1,8 +1,8 @@
1
1
  """The model endpoint: a Claude Code prompt, fed the run's whole transcript.
2
2
 
3
3
  nuagent's ``turn`` calls ``chat(messages=...)`` and expects a term yielding
4
- ``{"text": ...}``. ``nu.llm`` speaks that natively but wants a provider and a
5
- key; ``nu.cc`` wants neither, and a space is a thing you run on your own
4
+ ``{"text": ...}``. ``nustd.llm`` speaks that natively but wants a provider and a
5
+ key; ``nustd.cc`` wants neither, and a space is a thing you run on your own
6
6
  machine, so that is what is wired here. The cost is that each call is a fresh
7
7
  Claude Code session, so the conversation has to ride along in the prompt --
8
8
  which is what :class:`Rendered` is for.
@@ -29,7 +29,7 @@ from __future__ import annotations
29
29
  from typing import TYPE_CHECKING
30
30
 
31
31
  import nu
32
- import nu.cc
32
+ import nustd.cc
33
33
  from nu.lang import ScalarQuery
34
34
  from nu.lang.sentinels import EMPTY, INVALID
35
35
 
@@ -47,7 +47,7 @@ __all__ = ["Bot", "Rendered", "chat_with"]
47
47
  class Bot(nu.Service):
48
48
  """The Claude Code endpoint one space's agent runs against."""
49
49
 
50
- ask = nu.cc.PromptRef.method()
50
+ ask = nustd.cc.PromptRef.method()
51
51
 
52
52
 
53
53
  class Rendered(ScalarQuery):
@@ -86,7 +86,7 @@ class Rendered(ScalarQuery):
86
86
  def _render(messages: object) -> object:
87
87
  """Role-tagged blocks, or INVALID for anything that is not a transcript.
88
88
 
89
- Duck-typed, and it has to be. A ``nu.kv`` ``ListRef`` of dicts does not
89
+ Duck-typed, and it has to be. A ``nustd.kv`` ``ListRef`` of dicts does not
90
90
  read back as ``list[dict]`` -- it reads back as a lazy sequence view whose
91
91
  elements are mapping views, live cursors into the store. An
92
92
  ``isinstance(messages, list)`` gate therefore rejected every real
@@ -215,7 +215,7 @@ def messages_of(*, root: type[Shape] | None = None) -> nu.Nu:
215
215
  def running(*, root: type[Shape] | None = None) -> nu.Nu:
216
216
  """Whether a run is in flight, as a term.
217
217
 
218
- Read off the store rather than off a ``nu.mem`` record, unlike the apps
218
+ Read off the store rather than off a ``nustd.mem`` record, unlike the apps
219
219
  runner's ``attached``. An agent run is a fact about the space, not about
220
220
  the process: the browser has to see it, and it is the same answer for
221
221
  every connection.
@@ -18,7 +18,7 @@ prose around a fence and the person sees silence.
18
18
 
19
19
  One section is nuspace's own, and it exists to *correct* the stock surface
20
20
  preamble. That preamble tells the model to redeclare every Shape it touches,
21
- because ``nu.mem`` addresses by slot name and a matching declaration therefore
21
+ because ``nustd.mem`` addresses by slot name and a matching declaration therefore
22
22
  reaches the host's world. A space's store does not work that way: it is bound
23
23
  ``tags=(Space,)``, keyed on the class object, so a redeclared ``Space`` is a
24
24
  different address and every write through it lands nowhere, silently. The
@@ -79,12 +79,13 @@ else -- by writing it:
79
79
 
80
80
  ```python
81
81
  import nu
82
+ import nustd.mem
82
83
 
83
84
  from nuspace.core.shapes import Space
84
85
 
85
86
 
86
87
  class Run(nu.Shape):
87
- done = nu.mem.BoolRef.slot()
88
+ done = nustd.mem.BoolRef.slot()
88
89
 
89
90
 
90
91
  def out():
@@ -151,7 +152,7 @@ lands in a store nobody reads. Import, never redeclare. The same goes for
151
152
  name them at all.
152
153
 
153
154
  **Do not bracket your program.** The host already holds the atomic bracket over
154
- the store. No `nu.kv.auto_flow_atomic`, no `nu.With`, no `nu.Provide`.
155
+ the store. No `nustd.kv.auto_flow_atomic`, no `nu.With`, no `nu.Provide`.
155
156
 
156
157
  **Prefer the ops modules to hand-written ref chains.** Each function returns a
157
158
  Nu term and fixes every invariant the store has -- a page's `parent` and its
@@ -184,16 +185,16 @@ runs under; ask for the ones you need and leave out the ones you do not:
184
185
 
185
186
  ```python
186
187
  import nu
187
- import nu.ui
188
+ import nustd.ui
188
189
  from nuspace.core.shapes import Space
189
190
 
190
191
 
191
192
  def out(section):
192
- heading = nu.ui.TextRef("title")
193
+ heading = nustd.ui.TextRef("title")
193
194
  return heading.set(nu.ToStr(Space.state[section].data.get_item("name", nu.Str("?"))))
194
195
  ```
195
196
 
196
- **A block does not say where it renders.** Name a `nu.ui` Ref plainly, as
197
+ **A block does not say where it renders.** Name a `nustd.ui` Ref plainly, as
197
198
  above, and nuspace roots it under this block on the page. `Space.state[id]` is
198
199
  any block's own scratch row, which is also how one block reads another's: pass
199
200
  the other block's id, never a key built out of a prefix.
@@ -33,7 +33,7 @@ the whole security model:
33
33
  ``space_tree``'s head. The model reaches it by importing the real ``Space``.
34
34
  - an untagged ``dict`` is provided **here**, and it is where ``nuagent.Run``
35
35
  lives. The model writes ``Run.done`` into it to end the run, and any
36
- ``nu.mem`` scratch it invents lands there too, out of the way.
36
+ ``nustd.mem`` scratch it invents lands there too, out of the way.
37
37
 
38
38
  ``Agent`` carries the session slots, so the conversation is in kv and the
39
39
  sidebar subscribes to it directly. It is handed to nuagent as ``session``
@@ -48,8 +48,8 @@ from typing import TYPE_CHECKING
48
48
  import nuagent
49
49
 
50
50
  import nu
51
- import nu.cc
52
- import nu.kv
51
+ import nustd.cc
52
+ import nustd.kv
53
53
  from nuspace._root import resolve_root
54
54
  from nuspace.apps import ops as apps_ops
55
55
  from nuspace.chat import ops as chat_ops
@@ -115,7 +115,7 @@ def agent_runner(
115
115
  space at ollama or an api key instead -- or at a stub, which is
116
116
  what makes the wiring testable without spending a model call.
117
117
  bind: the bracket that provides whatever ``chat`` addresses. Must be
118
- passed with ``chat``: the default pair is ``nu.cc.bind(Bot, ...)``
118
+ passed with ``chat``: the default pair is ``nustd.cc.bind(Bot, ...)``
119
119
  and a Service endpoint with nothing provided for it is a
120
120
  ``LookupError`` on the first turn, not at boot.
121
121
 
@@ -136,7 +136,7 @@ def agent_runner(
136
136
  if chat is None:
137
137
  chat = chat_with(system_prompt(root))
138
138
  if bind is None:
139
- bind = nu.cc.bind(Bot, model=model, allowed_tools=[], permission_mode="default")
139
+ bind = nustd.cc.bind(Bot, model=model, allowed_tools=[], permission_mode="default")
140
140
 
141
141
  loop = nuagent.agent(
142
142
  # `Space.agent` is the session: the six slots nuagent reads are
@@ -190,11 +190,11 @@ def agent_runner(
190
190
  arm = arms.event(_RUN, agent.nonce.on_change(), once)
191
191
 
192
192
  return nu.With(
193
- # Untagged, so `nuagent.Run` -- and any nu.mem the model invents --
193
+ # Untagged, so `nuagent.Run` -- and any nustd.mem the model invents --
194
194
  # resolves here and nowhere near the conversation.
195
195
  nu.Provide(dict, {}),
196
196
  bind,
197
- body=nu.kv.auto_flow_atomic(boot >> arm, scope=root),
197
+ body=nustd.kv.auto_flow_atomic(boot >> arm, scope=root),
198
198
  )
199
199
 
200
200
 
@@ -25,7 +25,7 @@ One run at a time, because nuagent is one run at a time. When that stops being
25
25
  true this grows a ``ShapesDictRef`` keyed by run id and nothing else moves.
26
26
 
27
27
  ``KVRun`` is not used and neither is nuagent's ``Run``-on-the-session idea:
28
- the model ends a run by writing ``Run.done``, which is ``nu.mem`` and
28
+ the model ends a run by writing ``Run.done``, which is ``nustd.mem`` and
29
29
  addresses by slot name into the untagged store :mod:`nuspace.agent.runner`
30
30
  provides around the loop. Keeping it off ``Agent`` is what keeps the model
31
31
  from reaching the conversation.
@@ -34,7 +34,7 @@ from reaching the conversation.
34
34
  from __future__ import annotations
35
35
 
36
36
  import nu
37
- import nu.kv
37
+ import nustd.kv
38
38
 
39
39
 
40
40
  __all__ = [
@@ -70,15 +70,15 @@ class Agent(nu.Shape):
70
70
  """One agent run: nuagent's session slots, plus how a human starts one."""
71
71
 
72
72
  # -- the submission, nuspace's own ---------------------------------------
73
- task = nu.kv.StrRef.slot()
74
- nonce = nu.kv.IntRef.slot()
75
- status = nu.kv.StrRef.slot()
76
- error = nu.kv.StrRef.slot()
73
+ task = nustd.kv.StrRef.slot()
74
+ nonce = nustd.kv.IntRef.slot()
75
+ status = nustd.kv.StrRef.slot()
76
+ error = nustd.kv.StrRef.slot()
77
77
 
78
78
  # -- nuagent's session contract, slot for slot ---------------------------
79
- messages = nu.kv.ListRef.slot(dict)
80
- reply = nu.kv.StrRef.slot()
81
- draft = nu.kv.ProgramRef.slot()
82
- outcome = nu.kv.StrRef.slot()
83
- observation = nu.kv.StrRef.slot()
84
- turns = nu.kv.IntRef.slot()
79
+ messages = nustd.kv.ListRef.slot(dict)
80
+ reply = nustd.kv.StrRef.slot()
81
+ draft = nustd.kv.ProgramRef.slot()
82
+ outcome = nustd.kv.StrRef.slot()
83
+ observation = nustd.kv.StrRef.slot()
84
+ turns = nustd.kv.IntRef.slot()
@@ -11,9 +11,9 @@ from __future__ import annotations
11
11
  from typing import TYPE_CHECKING
12
12
 
13
13
  import nu
14
- import nu.kv
15
- import nu.mp_pool
16
14
  import nu.prog
15
+ import nustd.kv
16
+ import nustd.mp_pool
17
17
  from nuspace._root import resolve_root
18
18
  from nuspace.core.host import DEFAULT_CHANNEL_PREFIX, free_port, host, worker_init
19
19
 
@@ -77,12 +77,12 @@ def app_body(*, root: type[Shape] | None = None) -> nu.Nu:
77
77
  # Construction failures are written to the app's own row, not raised: a
78
78
  # dispatched body has no waiter, so an uncaught error vanishes silently.
79
79
  load = root.apps[_APP].snippet.load(scope=scope)
80
- report = nu.kv.auto_flow_atomic(
80
+ report = nustd.kv.auto_flow_atomic(
81
81
  root.state[_APP].error.set(nu.ToStr(nu.AttrRef("error"))),
82
82
  scope=root,
83
83
  )
84
84
  return nu.TryCatch(
85
- nu.prog.Eval(nu.kv.auto_flow_atomic(load, scope=root)),
85
+ nu.prog.Eval(nustd.kv.auto_flow_atomic(load, scope=root)),
86
86
  catch=report,
87
87
  errors=nu.prog.ConstructionError,
88
88
  )
@@ -97,7 +97,7 @@ def reconcile(*, root: type[Shape] | None = None) -> nu.Nu:
97
97
  without the first app blocking the rest.
98
98
  """
99
99
  root = resolve_root(root)
100
- pool = nu.mp_pool.PoolRef()
100
+ pool = nustd.mp_pool.PoolRef()
101
101
  stop = nu.IfDo(
102
102
  Runner.workers.contains(_APP),
103
103
  pool.kill(Runner.workers[_APP]) >> Runner.workers.del_item(_APP),
@@ -182,7 +182,7 @@ def supervisor(
182
182
  flow = live if alongside is None else (live | alongside)
183
183
  if duration is not None:
184
184
  flow = nu.Race(flow, nu.DelayedDo(nu.Float(duration), nu.Noop()))
185
- return nu.kv.auto_flow_atomic(seed >> flow, scope=root)
185
+ return nustd.kv.auto_flow_atomic(seed >> flow, scope=root)
186
186
 
187
187
 
188
188
  def apps_store(
@@ -193,8 +193,10 @@ def apps_store(
193
193
  ) -> nu.Nu:
194
194
  """The navigator bracket for a space on disk, with or without redis."""
195
195
  if redis_url is None:
196
- return nu.kv.rocksdb_navigator(path)
197
- return nu.kv.rocksdb_navigator_redis(path, redis_url=redis_url, channel_prefix=channel_prefix)
196
+ return nustd.kv.rocksdb_navigator(path)
197
+ return nustd.kv.rocksdb_navigator_redis(
198
+ path, redis_url=redis_url, channel_prefix=channel_prefix
199
+ )
198
200
 
199
201
 
200
202
  def apps_tree(
@@ -8,8 +8,8 @@ This module imports nothing from :mod:`nuspace.core`, which is what keeps
8
8
  from __future__ import annotations
9
9
 
10
10
  import nu
11
- import nu.kv
12
- import nu.mem
11
+ import nustd.kv
12
+ import nustd.mem
13
13
 
14
14
 
15
15
  __all__ = ["DEFAULT_POLICY", "App", "Runner"]
@@ -30,22 +30,22 @@ class App(nu.Shape):
30
30
 
31
31
  # An app is headless -- it produces, a page displays -- so nothing roots
32
32
  # the refs it names and it has nowhere to render.
33
- name = nu.kv.StrRef.slot()
34
- snippet = nu.kv.ProgramRef.slot()
33
+ name = nustd.kv.StrRef.slot()
34
+ snippet = nustd.kv.ProgramRef.slot()
35
35
  # Metadata. The policy engine slots in behind this, not beside it.
36
- policy = nu.kv.StrRef.slot()
36
+ policy = nustd.kv.StrRef.slot()
37
37
 
38
38
 
39
39
  class Runner(nu.Shape):
40
40
  """What the host process knows about who is running what.
41
41
 
42
- ``nu.mem`` deliberately: this is host-local bookkeeping, and a kv write
42
+ ``nustd.mem`` deliberately: this is host-local bookkeeping, and a kv write
43
43
  here would let the driver wake itself. Pool ids are never reused, so "the
44
44
  worker id changed" is a sound test for "that app restarted".
45
45
  """
46
46
 
47
- workers = nu.mem.DictRef.slot(int)
47
+ workers = nustd.mem.DictRef.slot(int)
48
48
  # Written by the runner's boot pass and by nothing else. Mem, so it reads
49
49
  # True only where the runner itself is: a web driver in the same process
50
50
  # sees it, one in another process never can, and neither has to be told.
51
- attached = nu.mem.BoolRef.slot()
51
+ attached = nustd.mem.BoolRef.slot()
@@ -31,7 +31,7 @@ one call the model can write without looking anything up::
31
31
  from __future__ import annotations
32
32
 
33
33
  import nu
34
- import nu.kv
34
+ import nustd.kv
35
35
 
36
36
 
37
37
  __all__ = ["ROLES", "ROLE_AGENT", "ROLE_SYSTEM", "ROLE_USER", "Chat"]
@@ -53,4 +53,4 @@ ROLES = (ROLE_USER, ROLE_AGENT, ROLE_SYSTEM)
53
53
  class Chat(nu.Shape):
54
54
  """One conversation. A list of ``{role, text}``, oldest first."""
55
55
 
56
- messages = nu.kv.ListRef.slot(dict)
56
+ messages = nustd.kv.ListRef.slot(dict)
@@ -2,7 +2,7 @@
2
2
 
3
3
  No commands yet. The CLI is a DX layer that will grow once the core
4
4
  primitives settle. External automation should attach to a running space
5
- via ``nu.proxy`` instead of via bespoke CLI commands.
5
+ via ``nustd.proxy`` instead of via bespoke CLI commands.
6
6
  """
7
7
 
8
8
  from __future__ import annotations
@@ -17,11 +17,11 @@ import socket
17
17
  from typing import TYPE_CHECKING
18
18
 
19
19
  import nu
20
- import nu.kv
21
- import nu.mp_pool
22
- import nu.proxy
20
+ import nustd.kv
21
+ import nustd.mp_pool
22
+ import nustd.proxy
23
23
  from nu.core.reactive import ObserverProtocol
24
- from nu.kv.fabrics import InMemoryObserver, InMemoryTransport, Navigator
24
+ from nustd.kv.fabrics import InMemoryObserver, InMemoryTransport, Navigator
25
25
 
26
26
 
27
27
  if TYPE_CHECKING:
@@ -138,12 +138,12 @@ def worker_init(
138
138
  """
139
139
  observer: tuple[nu.Nu, ...] = ()
140
140
  if observer_address is not None:
141
- observer = (nu.kv.proxy_observer(observer_address),)
141
+ observer = (nustd.kv.proxy_observer(observer_address),)
142
142
  elif redis_url is not None:
143
- observer = (nu.kv.redis_observer(redis_url=redis_url, channel_prefix=channel_prefix),)
143
+ observer = (nustd.kv.redis_observer(redis_url=redis_url, channel_prefix=channel_prefix),)
144
144
  return nu.With(
145
145
  *observer,
146
- nu.proxy.InvisiblesProxy(Navigator, address=address),
146
+ nustd.proxy.InvisiblesProxy(Navigator, address=address),
147
147
  nu.Provide(dict, {}),
148
148
  )
149
149
 
@@ -156,7 +156,7 @@ def served_navigator(address: str, *, store_tag: object = None) -> nu.Provide:
156
156
  tag here is a ``LookupError`` before the server ever boots.
157
157
  """
158
158
  return nu.Provide(
159
- nu.proxy.InvisiblesServer,
159
+ nustd.proxy.InvisiblesServer,
160
160
  {
161
161
  "target": Navigator,
162
162
  "target_tag": store_tag,
@@ -172,10 +172,10 @@ def served_observer(address: str) -> nu.With:
172
172
 
173
173
  The store's observer sees every write this process makes, and a worker on
174
174
  the other end of a proxied Navigator sees none of them. This is the ears:
175
- bind it here, bind :func:`nu.kv.proxy_observer` in the worker, and a page
175
+ bind it here, bind :func:`nustd.kv.proxy_observer` in the worker, and a page
176
176
  can be told something changed instead of being killed to find out.
177
177
  """
178
- return nu.kv.served_observer(address)
178
+ return nustd.kv.served_observer(address)
179
179
 
180
180
 
181
181
  def worker_pool(
@@ -192,7 +192,7 @@ def worker_pool(
192
192
  provides one has decided how long those processes live.
193
193
  """
194
194
  return nu.Provide(
195
- nu.mp_pool.WorkerPool,
195
+ nustd.mp_pool.WorkerPool,
196
196
  {
197
197
  "name": name,
198
198
  "init": worker_init(