nerdit 0.7.2__tar.gz → 0.8.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (370) hide show
  1. {nerdit-0.7.2 → nerdit-0.8.0}/PKG-INFO +4 -4
  2. {nerdit-0.7.2 → nerdit-0.8.0}/README.md +3 -3
  3. {nerdit-0.7.2 → nerdit-0.8.0}/pyproject.toml +1 -1
  4. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/__init__.py +1 -1
  5. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/cli/client.py +10 -1
  6. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/cli/commands/dev.py +7 -3
  7. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/cli/commands/routes.py +9 -2
  8. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/cli/commands/share.py +39 -2
  9. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/cli/display.py +22 -10
  10. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/core/cutover.py +27 -14
  11. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/core/proxy/manager.py +23 -1
  12. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/core/services.py +6 -1
  13. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/routes/_resources.py +4 -2
  14. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/routes/domains.py +13 -2
  15. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/routes/proxy.py +3 -1
  16. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/routes/share.py +106 -18
  17. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/routes/system.py +4 -0
  18. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/schemas/exposure.py +27 -0
  19. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/schemas/proxy.py +9 -0
  20. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/views/hosted.py +16 -0
  21. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/views/service.py +7 -1
  22. nerdit-0.7.2/src/nerdit/daemon/web/dist/assets/index-BiZkV8Cy.js → nerdit-0.8.0/src/nerdit/daemon/web/dist/assets/index-BbpL4BJf.js +23 -23
  23. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/dist/index.html +1 -1
  24. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/src/api/types.ts +8 -0
  25. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/src/components/ServicePublicUrl.tsx +10 -0
  26. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/src/components/ServiceShareCard.test.tsx +17 -0
  27. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/src/components/ServiceShareCard.tsx +7 -0
  28. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/src/components/settings/RoutesCard.tsx +19 -3
  29. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/src/lib/format.test.ts +11 -0
  30. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/src/lib/format.ts +8 -1
  31. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/db/database.py +9 -3
  32. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/db/queries/domains.py +15 -4
  33. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/db/queries/services.py +4 -1
  34. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/db/queries/shares.py +54 -7
  35. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/db/rows.py +4 -0
  36. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/mcp/tools/exposure.py +22 -1
  37. {nerdit-0.7.2 → nerdit-0.8.0}/.gitignore +0 -0
  38. {nerdit-0.7.2 → nerdit-0.8.0}/LICENSE +0 -0
  39. {nerdit-0.7.2 → nerdit-0.8.0}/NOTICE +0 -0
  40. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/__main__.py +0 -0
  41. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/cli/__init__.py +0 -0
  42. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/cli/app.py +0 -0
  43. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/cli/checks.py +0 -0
  44. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/cli/commands/__init__.py +0 -0
  45. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/cli/commands/apply.py +0 -0
  46. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/cli/commands/backup.py +0 -0
  47. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/cli/commands/capabilities.py +0 -0
  48. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/cli/commands/check_deps.py +0 -0
  49. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/cli/commands/config.py +0 -0
  50. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/cli/commands/connect.py +0 -0
  51. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/cli/commands/daemon.py +0 -0
  52. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/cli/commands/db.py +0 -0
  53. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/cli/commands/deploy.py +0 -0
  54. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/cli/commands/diagnose.py +0 -0
  55. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/cli/commands/doctor.py +0 -0
  56. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/cli/commands/domains.py +0 -0
  57. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/cli/commands/events.py +0 -0
  58. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/cli/commands/exit.py +0 -0
  59. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/cli/commands/init.py +0 -0
  60. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/cli/commands/license.py +0 -0
  61. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/cli/commands/link.py +0 -0
  62. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/cli/commands/logs.py +0 -0
  63. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/cli/commands/mcp.py +0 -0
  64. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/cli/commands/models.py +0 -0
  65. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/cli/commands/projects.py +0 -0
  66. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/cli/commands/proxy.py +0 -0
  67. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/cli/commands/secrets.py +0 -0
  68. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/cli/commands/serve.py +0 -0
  69. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/cli/commands/services.py +0 -0
  70. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/cli/commands/store.py +0 -0
  71. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/cli/commands/system.py +0 -0
  72. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/cli/commands/token.py +0 -0
  73. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/cli/commands/trust.py +0 -0
  74. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/cli/commands/uninstall.py +0 -0
  75. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/cli/commands/update.py +0 -0
  76. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/cli/commands/vars.py +0 -0
  77. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/cli/upload.py +0 -0
  78. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/config/__init__.py +0 -0
  79. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/config/app_config.py +0 -0
  80. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/config/app_templates/__init__.py +0 -0
  81. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/config/app_templates/builtin.json +0 -0
  82. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/config/app_templates/loader.py +0 -0
  83. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/config/build.py +0 -0
  84. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/config/defaults.py +0 -0
  85. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/config/project.py +0 -0
  86. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/config/redaction.py +0 -0
  87. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/config/settings.py +0 -0
  88. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/config/store.py +0 -0
  89. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/core/__init__.py +0 -0
  90. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/core/app_build.py +0 -0
  91. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/core/backup.py +0 -0
  92. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/core/bindings/__init__.py +0 -0
  93. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/core/bindings/registry.py +0 -0
  94. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/core/bindings/secretref.py +0 -0
  95. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/core/builder.py +0 -0
  96. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/core/data/__init__.py +0 -0
  97. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/core/data/backend.py +0 -0
  98. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/core/data/binding.py +0 -0
  99. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/core/data/controller.py +0 -0
  100. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/core/deploy_state.py +0 -0
  101. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/core/discovery.py +0 -0
  102. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/core/dns.py +0 -0
  103. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/core/eventlog.py +0 -0
  104. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/core/events.py +0 -0
  105. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/core/gitsource.py +0 -0
  106. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/core/gitwatch.py +0 -0
  107. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/core/health.py +0 -0
  108. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/core/jobconfig.py +0 -0
  109. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/core/launch.py +0 -0
  110. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/core/license.py +0 -0
  111. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/core/link/__init__.py +0 -0
  112. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/core/link/apps.py +0 -0
  113. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/core/link/capability.py +0 -0
  114. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/core/link/client.py +0 -0
  115. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/core/link/frames.py +0 -0
  116. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/core/link/hosted.py +0 -0
  117. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/core/link/identity.py +0 -0
  118. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/core/link/manager.py +0 -0
  119. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/core/link/mux.py +0 -0
  120. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/core/models/__init__.py +0 -0
  121. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/core/models/backend.py +0 -0
  122. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/core/models/binding.py +0 -0
  123. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/core/models/controller.py +0 -0
  124. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/core/monitor.py +0 -0
  125. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/core/node_packages.py +0 -0
  126. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/core/node_runtime.py +0 -0
  127. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/core/notify.py +0 -0
  128. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/core/project_identity.py +0 -0
  129. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/core/proxy/__init__.py +0 -0
  130. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/core/proxy/admin.py +0 -0
  131. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/core/proxy/certs.py +0 -0
  132. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/core/proxy/domains.py +0 -0
  133. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/core/proxy/edgeauth.py +0 -0
  134. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/core/proxy/routing.py +0 -0
  135. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/core/proxy/supervisor.py +0 -0
  136. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/core/proxy/tls.py +0 -0
  137. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/core/remediation_settle.py +0 -0
  138. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/core/resources/__init__.py +0 -0
  139. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/core/resources/controller.py +0 -0
  140. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/core/retention.py +0 -0
  141. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/core/runtime/__init__.py +0 -0
  142. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/core/runtime/container.py +0 -0
  143. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/core/runtime/docker.py +0 -0
  144. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/core/runtime/protocol.py +0 -0
  145. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/core/runtime/stub.py +0 -0
  146. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/core/sandbox.py +0 -0
  147. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/core/secrets.py +0 -0
  148. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/core/sweeper.py +0 -0
  149. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/core/variables.py +0 -0
  150. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/core/volumes.py +0 -0
  151. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/core/workload.py +0 -0
  152. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/core/workspaces.py +0 -0
  153. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/__init__.py +0 -0
  154. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/appfactory.py +0 -0
  155. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/audit.py +0 -0
  156. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/auth.py +0 -0
  157. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/bodylimit.py +0 -0
  158. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/bootstrap.py +0 -0
  159. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/deploy_pipeline.py +0 -0
  160. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/errors.py +0 -0
  161. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/idempotency.py +0 -0
  162. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/imagegc.py +0 -0
  163. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/lifecycle.py +0 -0
  164. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/limits.py +0 -0
  165. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/middleware.py +0 -0
  166. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/project_delegation.py +0 -0
  167. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/remediation.py +0 -0
  168. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/routes/__init__.py +0 -0
  169. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/routes/app_config.py +0 -0
  170. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/routes/app_templates.py +0 -0
  171. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/routes/audit.py +0 -0
  172. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/routes/auth.py +0 -0
  173. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/routes/cluster.py +0 -0
  174. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/routes/config.py +0 -0
  175. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/routes/databases.py +0 -0
  176. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/routes/deploy.py +0 -0
  177. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/routes/events.py +0 -0
  178. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/routes/gpus.py +0 -0
  179. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/routes/health.py +0 -0
  180. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/routes/images.py +0 -0
  181. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/routes/license.py +0 -0
  182. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/routes/link.py +0 -0
  183. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/routes/models.py +0 -0
  184. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/routes/projects.py +0 -0
  185. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/routes/secrets.py +0 -0
  186. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/routes/service_diagnose.py +0 -0
  187. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/routes/service_logs_stream.py +0 -0
  188. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/routes/service_run.py +0 -0
  189. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/routes/service_stats.py +0 -0
  190. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/routes/service_wait.py +0 -0
  191. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/routes/services.py +0 -0
  192. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/routes/tokens.py +0 -0
  193. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/routes/workspaces.py +0 -0
  194. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/schemas/__init__.py +0 -0
  195. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/schemas/_base.py +0 -0
  196. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/schemas/audit.py +0 -0
  197. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/schemas/cluster.py +0 -0
  198. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/schemas/config.py +0 -0
  199. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/schemas/databases.py +0 -0
  200. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/schemas/deploy.py +0 -0
  201. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/schemas/events.py +0 -0
  202. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/schemas/gpus.py +0 -0
  203. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/schemas/health.py +0 -0
  204. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/schemas/license.py +0 -0
  205. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/schemas/link.py +0 -0
  206. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/schemas/models.py +0 -0
  207. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/schemas/service_diagnose.py +0 -0
  208. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/schemas/service_purge.py +0 -0
  209. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/schemas/service_run.py +0 -0
  210. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/schemas/service_stats.py +0 -0
  211. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/schemas/service_wait.py +0 -0
  212. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/schemas/services.py +0 -0
  213. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/schemas/system.py +0 -0
  214. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/schemas/tokens.py +0 -0
  215. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/schemas/workspaces.py +0 -0
  216. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/secret_scope.py +0 -0
  217. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/server.py +0 -0
  218. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/service_purge.py +0 -0
  219. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/sse.py +0 -0
  220. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/sweeps.py +0 -0
  221. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/uploads.py +0 -0
  222. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/views/__init__.py +0 -0
  223. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/dist/assets/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2 +0 -0
  224. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/dist/assets/geist-cyrillic-wght-normal-BEAKL7Jp.woff2 +0 -0
  225. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/dist/assets/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
  226. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/dist/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
  227. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/dist/assets/geist-mono-cyrillic-ext-wght-normal-X_5orZeX.woff2 +0 -0
  228. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/dist/assets/geist-mono-cyrillic-wght-normal-DiZS0aHC.woff2 +0 -0
  229. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/dist/assets/geist-mono-latin-ext-wght-normal-Bwz-egvJ.woff2 +0 -0
  230. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/dist/assets/geist-mono-latin-wght-normal-XN7g48iV.woff2 +0 -0
  231. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/dist/assets/geist-mono-symbols2-wght-normal-CO5SzqOn.woff2 +0 -0
  232. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/dist/assets/geist-mono-vietnamese-wght-normal-DadHysG0.woff2 +0 -0
  233. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/dist/assets/geist-vietnamese-wght-normal-6IgcOCM7.woff2 +0 -0
  234. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/dist/assets/index-CLfzcEzV.css +0 -0
  235. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/eslint.config.js +0 -0
  236. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/index.html +0 -0
  237. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/package-lock.json +0 -0
  238. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/package.json +0 -0
  239. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/playwright.config.ts +0 -0
  240. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/postcss.config.js +0 -0
  241. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/src/api/auditStream.ts +0 -0
  242. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/src/api/client.test.ts +0 -0
  243. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/src/api/client.ts +0 -0
  244. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/src/api/clusterEvents.ts +0 -0
  245. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/src/api/queries.test.ts +0 -0
  246. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/src/api/queries.ts +0 -0
  247. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/src/components/CommandPalette.tsx +0 -0
  248. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/src/components/ConfigurePanel.tsx +0 -0
  249. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/src/components/DaemonStatusBanner.tsx +0 -0
  250. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/src/components/DeployDialog.tsx +0 -0
  251. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/src/components/DeployProgress.tsx +0 -0
  252. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/src/components/DiagnosePanel.tsx +0 -0
  253. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/src/components/Dropzone.tsx +0 -0
  254. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/src/components/ErrorBoundary.tsx +0 -0
  255. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/src/components/SecretsPanel.tsx +0 -0
  256. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/src/components/ServiceLogsPanel.tsx +0 -0
  257. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/src/components/TemplateDeployDialog.tsx +0 -0
  258. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/src/components/ToastViewport.tsx +0 -0
  259. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/src/components/UnlinkedBanner.test.tsx +0 -0
  260. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/src/components/UnlinkedBanner.tsx +0 -0
  261. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/src/components/bindings/AiEndpointFields.tsx +0 -0
  262. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/src/components/bindings/BindingSwitch.tsx +0 -0
  263. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/src/components/bindings/GenericBindingsPanel.tsx +0 -0
  264. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/src/components/bindings/KeyFlow.tsx +0 -0
  265. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/src/components/bindings/sections.tsx +0 -0
  266. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/src/components/bindings/useBindingDraft.ts +0 -0
  267. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/src/components/settings/AppearanceCard.tsx +0 -0
  268. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/src/components/settings/DiskCard.tsx +0 -0
  269. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/src/components/settings/DoctorCard.tsx +0 -0
  270. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/src/components/settings/GpuCard.tsx +0 -0
  271. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/src/components/settings/HostnameCard.tsx +0 -0
  272. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/src/components/settings/ProxyStatusCard.tsx +0 -0
  273. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/src/components/ui.test.tsx +0 -0
  274. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/src/components/ui.tsx +0 -0
  275. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/src/lib/aiProviders.ts +0 -0
  276. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/src/lib/analytics.ts +0 -0
  277. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/src/lib/apiErrors.test.ts +0 -0
  278. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/src/lib/apiErrors.ts +0 -0
  279. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/src/lib/auth.ts +0 -0
  280. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/src/lib/bindings.test.ts +0 -0
  281. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/src/lib/bindings.ts +0 -0
  282. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/src/lib/deployPhase.ts +0 -0
  283. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/src/lib/projects.test.ts +0 -0
  284. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/src/lib/projects.ts +0 -0
  285. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/src/lib/sse.test.ts +0 -0
  286. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/src/lib/sse.ts +0 -0
  287. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/src/lib/status.test.ts +0 -0
  288. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/src/lib/status.ts +0 -0
  289. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/src/lib/theme.test.ts +0 -0
  290. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/src/lib/theme.ts +0 -0
  291. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/src/main.tsx +0 -0
  292. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/src/pages/Audit.tsx +0 -0
  293. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/src/pages/Databases.tsx +0 -0
  294. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/src/pages/Login.test.tsx +0 -0
  295. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/src/pages/Login.tsx +0 -0
  296. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/src/pages/Models.tsx +0 -0
  297. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/src/pages/ProjectDetail.tsx +0 -0
  298. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/src/pages/ProjectPage.test.tsx +0 -0
  299. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/src/pages/ProjectPage.tsx +0 -0
  300. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/src/pages/ProjectPolling.test.tsx +0 -0
  301. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/src/pages/Projects.tsx +0 -0
  302. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/src/pages/ServiceRedirect.tsx +0 -0
  303. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/src/pages/Settings.tsx +0 -0
  304. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/src/pages/Tokens.tsx +0 -0
  305. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/src/shell/AppShell.tsx +0 -0
  306. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/src/shell/navItems.ts +0 -0
  307. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/src/state/toastStore.ts +0 -0
  308. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/src/styles/globals.css +0 -0
  309. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/tailwind.config.js +0 -0
  310. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/tests/e2e/01-login-and-overview.spec.ts +0 -0
  311. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/tests/e2e/05-services.spec.ts +0 -0
  312. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/tests/e2e/06-models.spec.ts +0 -0
  313. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/tests/e2e/07-audit.spec.ts +0 -0
  314. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/tests/e2e/08-secrets.spec.ts +0 -0
  315. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/tests/e2e/10-settings.spec.ts +0 -0
  316. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/tests/e2e/11-configure.spec.ts +0 -0
  317. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/tests/e2e/12-deploy.spec.ts +0 -0
  318. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/tests/e2e/13-bindings.spec.ts +0 -0
  319. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/tests/e2e/14-responsive-shell.spec.ts +0 -0
  320. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/tests/e2e/17-projects.spec.ts +0 -0
  321. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/tests/e2e/18-navigation.spec.ts +0 -0
  322. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/tests/e2e/19-project-page.spec.ts +0 -0
  323. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/tests/e2e/20-command-palette.spec.ts +0 -0
  324. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/tests/e2e/21-tokens.spec.ts +0 -0
  325. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/tests/e2e/22-system-cards.spec.ts +0 -0
  326. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/tests/e2e/fixtures.ts +0 -0
  327. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/tsconfig.json +0 -0
  328. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/tsconfig.tsbuildinfo +0 -0
  329. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/daemon/web/vite.config.ts +0 -0
  330. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/db/__init__.py +0 -0
  331. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/db/enums.py +0 -0
  332. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/db/models.py +0 -0
  333. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/db/queries/__init__.py +0 -0
  334. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/db/queries/_base.py +0 -0
  335. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/db/queries/audit.py +0 -0
  336. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/db/queries/endpoints.py +0 -0
  337. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/db/queries/events.py +0 -0
  338. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/db/queries/gpus.py +0 -0
  339. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/db/queries/idempotency.py +0 -0
  340. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/db/queries/logs.py +0 -0
  341. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/db/queries/projects.py +0 -0
  342. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/db/queries/secret_claims.py +0 -0
  343. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/db/queries/service_config.py +0 -0
  344. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/db/queries/tokens.py +0 -0
  345. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/db/queries/workloads.py +0 -0
  346. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/mcp/__init__.py +0 -0
  347. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/mcp/errors.py +0 -0
  348. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/mcp/server.py +0 -0
  349. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/mcp/tools/__init__.py +0 -0
  350. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/mcp/tools/_shared.py +0 -0
  351. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/mcp/tools/cluster.py +0 -0
  352. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/mcp/tools/config.py +0 -0
  353. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/mcp/tools/databases.py +0 -0
  354. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/mcp/tools/deploy.py +0 -0
  355. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/mcp/tools/models.py +0 -0
  356. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/mcp/tools/projects.py +0 -0
  357. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/mcp/tools/secrets.py +0 -0
  358. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/mcp/tools/services.py +0 -0
  359. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/mcp/tools/system.py +0 -0
  360. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/mcp/tools/workspaces.py +0 -0
  361. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/mcp/transport.py +0 -0
  362. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/utils/__init__.py +0 -0
  363. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/utils/certs.py +0 -0
  364. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/utils/disk.py +0 -0
  365. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/utils/frozen.py +0 -0
  366. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/utils/fs.py +0 -0
  367. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/utils/ids.py +0 -0
  368. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/utils/install_layout.py +0 -0
  369. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/utils/logging.py +0 -0
  370. {nerdit-0.7.2 → nerdit-0.8.0}/src/nerdit/utils/names.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: nerdit
3
- Version: 0.7.2
3
+ Version: 0.8.0
4
4
  Summary: The local AI PaaS — deploy and operate apps on your own hardware, with first-class local AI
5
5
  Project-URL: Homepage, https://github.com/nerdit-ai/nerdit
6
6
  Project-URL: Repository, https://github.com/nerdit-ai/nerdit
@@ -272,10 +272,10 @@ Questions or feedback: [feedback@nerdit.ai](mailto:feedback@nerdit.ai).
272
272
 
273
273
  ## License
274
274
 
275
- Apache-2.0, see [LICENSE](https://github.com/nerdit-ai/nerdit/blob/v0.7.2/LICENSE). "Nerdit" and the logo are trademarks and
276
- are not covered by the code license, see [NOTICE](https://github.com/nerdit-ai/nerdit/blob/v0.7.2/NOTICE).
275
+ Apache-2.0, see [LICENSE](https://github.com/nerdit-ai/nerdit/blob/v0.8.0/LICENSE). "Nerdit" and the logo are trademarks and
276
+ are not covered by the code license, see [NOTICE](https://github.com/nerdit-ai/nerdit/blob/v0.8.0/NOTICE).
277
277
 
278
278
  ## Contributing
279
279
 
280
- Issues and pull requests are welcome. [CONTRIBUTING.md](https://github.com/nerdit-ai/nerdit/blob/v0.7.2/CONTRIBUTING.md)
280
+ Issues and pull requests are welcome. [CONTRIBUTING.md](https://github.com/nerdit-ai/nerdit/blob/v0.8.0/CONTRIBUTING.md)
281
281
  explains how contributions land in releases.
@@ -213,10 +213,10 @@ Questions or feedback: [feedback@nerdit.ai](mailto:feedback@nerdit.ai).
213
213
 
214
214
  ## License
215
215
 
216
- Apache-2.0, see [LICENSE](https://github.com/nerdit-ai/nerdit/blob/v0.7.2/LICENSE). "Nerdit" and the logo are trademarks and
217
- are not covered by the code license, see [NOTICE](https://github.com/nerdit-ai/nerdit/blob/v0.7.2/NOTICE).
216
+ Apache-2.0, see [LICENSE](https://github.com/nerdit-ai/nerdit/blob/v0.8.0/LICENSE). "Nerdit" and the logo are trademarks and
217
+ are not covered by the code license, see [NOTICE](https://github.com/nerdit-ai/nerdit/blob/v0.8.0/NOTICE).
218
218
 
219
219
  ## Contributing
220
220
 
221
- Issues and pull requests are welcome. [CONTRIBUTING.md](https://github.com/nerdit-ai/nerdit/blob/v0.7.2/CONTRIBUTING.md)
221
+ Issues and pull requests are welcome. [CONTRIBUTING.md](https://github.com/nerdit-ai/nerdit/blob/v0.8.0/CONTRIBUTING.md)
222
222
  explains how contributions land in releases.
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "nerdit"
7
- version = "0.7.2"
7
+ version = "0.8.0"
8
8
  description = "The local AI PaaS — deploy and operate apps on your own hardware, with first-class local AI"
9
9
  readme = "README.md"
10
10
  license = "Apache-2.0"
@@ -1,3 +1,3 @@
1
1
  """Nerdit — Le moteur de l'IA locale."""
2
2
 
3
- __version__ = "0.7.2"
3
+ __version__ = "0.8.0"
@@ -879,17 +879,26 @@ class NerditClient:
879
879
  *,
880
880
  access: str = "private",
881
881
  consent: bool = False,
882
+ preserve_existing: bool = False,
883
+ hosted_only: bool | None = None,
882
884
  idempotency_key: str | None = None,
883
885
  ) -> dict:
884
886
  """Share a service; requires an idempotency key.
885
887
 
886
888
  The daemon validates entitlement, consent, link state, service kind and
887
889
  label length. Send values unchanged so client validation cannot drift.
890
+ ``preserve_existing`` and ``hosted_only`` travel only when set, so an
891
+ older daemon (``extra="forbid"``) still accepts the plain body.
888
892
  """
893
+ body: dict[str, object] = {"access": access, "consent": consent}
894
+ if preserve_existing:
895
+ body["preserve_existing"] = True
896
+ if hosted_only is not None:
897
+ body["hosted_only"] = hosted_only
889
898
  return await self._request_json(
890
899
  "PUT",
891
900
  f"{self._base_url}/api/services/{self._segment(name)}/share",
892
- json={"access": access, "consent": consent},
901
+ json=body,
893
902
  idempotency_key=idempotency_key,
894
903
  timeout=15.0,
895
904
  )
@@ -19,6 +19,7 @@ import typer
19
19
  from rich.markup import escape
20
20
 
21
21
  from nerdit.cli.display import (
22
+ _hosted_only,
22
23
  _plain,
23
24
  console,
24
25
  display_deploy_result,
@@ -136,9 +137,12 @@ def _cutover_warning(name: str, service: dict | None, cutover: bool | None) -> l
136
137
  reason = "only apps (kind = service) are cutover-eligible"
137
138
  elif (service.get("gpu_count") or 0) > 0:
138
139
  reason = "GPU services keep the same-port swap (two containers cannot hold one GPU)"
139
- elif service.get("status") in ("running", "degraded") and not (
140
- service.get("endpoint") or {}
141
- ).get("public_url"):
140
+ elif (
141
+ service.get("status") in ("running", "degraded")
142
+ and not (endpoint := service.get("endpoint") or {}).get("public_url")
143
+ # A locked app's null public_url is by design, not a missing route.
144
+ and not _hosted_only(endpoint)
145
+ ):
142
146
  reason = "no proxy route — zero-downtime redeploys need [proxy].enabled"
143
147
  else:
144
148
  return []
@@ -25,8 +25,15 @@ def _route_cell(route: object) -> str:
25
25
  return _plain(route)
26
26
 
27
27
 
28
- def _live_cell(live: object) -> str:
28
+ def _live_cell(live: object, reason: object = None) -> str:
29
29
  """Render the live-Caddy annotation; `None` ⇒ `-` (unknown, not "no")."""
30
+ if reason == "hosted_only":
31
+ # `None` is still unknown here: the lock is an intent, and the proxy may
32
+ # keep serving a route the deregistration never reached.
33
+ if live is None:
34
+ return "hosted only [yellow](proxy table unreadable)[/yellow]"
35
+ still_live = isinstance(live, dict) and live.get("registered")
36
+ return "hosted only" + (" [yellow](route still live)[/yellow]" if still_live else "")
30
37
  if live is None:
31
38
  return "-"
32
39
  if not isinstance(live, dict):
@@ -75,7 +82,7 @@ async def _routes_async(*, limit: int, cursor: str | None) -> int:
75
82
  _route_cell(item.get("route")),
76
83
  _plain(item.get("public_url")),
77
84
  _plain(item.get("host_port")),
78
- _live_cell(item.get("live")),
85
+ _live_cell(item.get("live"), item.get("reason")),
79
86
  )
80
87
  console.print(table)
81
88
 
@@ -30,6 +30,16 @@ _STATE_NOTES = {
30
30
  ),
31
31
  }
32
32
 
33
+ #: (Lot 5) ``local_routes`` token → the line printed for a hosted-only share.
34
+ _LOCAL_ROUTES_NOTES = {
35
+ "absent": "local routes: absent (hosted only)",
36
+ "present": "local routes: present: purge pending, retry in a few seconds",
37
+ "unknown": (
38
+ "local routes: unknown: proxy unreadable, or the daemon is reachable "
39
+ "without a token (see nerdit doctor)"
40
+ ),
41
+ }
42
+
33
43
 
34
44
  def _render_share(name: str, result: Any, *, verb: str) -> None:
35
45
  """Print one share as `<verb> <name> (<access>): <url>` + any state note."""
@@ -52,6 +62,11 @@ def _render_share(name: str, result: Any, *, verb: str) -> None:
52
62
  hint = origin.get("hint")
53
63
  if isinstance(hint, str) and hint:
54
64
  console.print(f"[yellow]origin — {_plain(hint)}[/yellow]")
65
+ if result.get("hosted_only") is True:
66
+ routes = result.get("local_routes")
67
+ note = _LOCAL_ROUTES_NOTES.get(routes) if isinstance(routes, str) else None
68
+ line = note or f"local routes: {_plain(routes)}"
69
+ console.print(line if routes == "absent" else f"[yellow]{line}[/yellow]")
55
70
 
56
71
 
57
72
  def share(
@@ -77,12 +92,31 @@ def share(
77
92
  "--show",
78
93
  help="Print the current share instead of changing it.",
79
94
  ),
95
+ hosted_only: bool | None = typer.Option(
96
+ None,
97
+ "--hosted-only/--local-route",
98
+ help=(
99
+ "--hosted-only locks the app to its hosted URL: this machine's proxy "
100
+ "keeps no local route for it. --local-route unlocks and restores the "
101
+ "local routes (needs the link; offline, nerdit unshare <app> also "
102
+ "restores them). Without --public, an existing public share stays public."
103
+ ),
104
+ ),
80
105
  ) -> None:
81
106
  """Share a deployed app at a hosted URL through this node's cloud link."""
82
- asyncio.run(_share_async(name, public=public, consent=consent, show=show))
107
+ if show and hosted_only is not None:
108
+ raise typer.BadParameter(
109
+ "--show prints the share; it cannot be combined with --hosted-only/--local-route",
110
+ param_hint="--show",
111
+ )
112
+ asyncio.run(
113
+ _share_async(name, public=public, consent=consent, show=show, hosted_only=hosted_only)
114
+ )
83
115
 
84
116
 
85
- async def _share_async(name: str, *, public: bool, consent: bool, show: bool) -> None:
117
+ async def _share_async(
118
+ name: str, *, public: bool, consent: bool, show: bool, hosted_only: bool | None = None
119
+ ) -> None:
86
120
  from nerdit.cli.client import get_configured_client
87
121
 
88
122
  client = get_configured_client()
@@ -97,6 +131,9 @@ async def _share_async(name: str, *, public: bool, consent: bool, show: bool) ->
97
131
  name,
98
132
  access="public" if public else "private",
99
133
  consent=consent,
134
+ # (Lot 5) A lock flag alone must not downgrade a public share.
135
+ preserve_existing=hosted_only is not None and not public,
136
+ hosted_only=hosted_only,
100
137
  idempotency_key=uuid4().hex,
101
138
  )
102
139
  except Exception as exc: # noqa: BLE001 — rendered for the user
@@ -223,6 +223,14 @@ def display_gpu_table(gpus: list[dict]) -> None:
223
223
  console.print(table)
224
224
 
225
225
 
226
+ def _hosted_only(endpoint: dict) -> bool:
227
+ """(Lot 5) A locked app: no client shows its loopback ``endpoint.url``."""
228
+ return any(
229
+ isinstance(entry, dict) and entry.get("hosted_only") is True
230
+ for entry in endpoint.get("public_urls") or []
231
+ )
232
+
233
+
226
234
  def display_service_table(services: list[dict]) -> None:
227
235
  """Display a Rich table of services (name, status, restarts, GPUs, URL)."""
228
236
  table = Table(title="Services")
@@ -244,11 +252,14 @@ def display_service_table(services: list[dict]) -> None:
244
252
 
245
253
  endpoint = svc.get("endpoint") or {}
246
254
  host_port = endpoint.get("host_port")
247
- url = (
248
- endpoint.get("public_url")
249
- or endpoint.get("url")
250
- or (f"127.0.0.1:{host_port}" if host_port else "-")
251
- )
255
+ if _hosted_only(endpoint):
256
+ url = endpoint.get("public_url") or "-"
257
+ else:
258
+ url = (
259
+ endpoint.get("public_url")
260
+ or endpoint.get("url")
261
+ or (f"127.0.0.1:{host_port}" if host_port else "-")
262
+ )
252
263
 
253
264
  table.add_row(
254
265
  _plain(svc.get("name", "")),
@@ -433,12 +444,13 @@ def display_service_submitted(service: dict) -> None:
433
444
  console.print(f" Status: {_plain(service.get('status'))}")
434
445
  endpoint = service.get("endpoint") or {}
435
446
  public_url = endpoint.get("public_url")
447
+ local = None if _hosted_only(endpoint) else endpoint.get("url")
436
448
  if public_url:
437
449
  console.print(f" URL: {_plain(public_url)}")
438
- if endpoint.get("url"):
439
- console.print(f" Local: {_plain(endpoint['url'])}")
440
- elif endpoint.get("url"):
441
- console.print(f" URL: {_plain(endpoint['url'])}")
450
+ if local:
451
+ console.print(f" Local: {_plain(local)}")
452
+ elif local:
453
+ console.print(f" URL: {_plain(local)}")
442
454
 
443
455
 
444
456
  def display_deploy_result(
@@ -452,7 +464,7 @@ def display_deploy_result(
452
464
  console.print(f"[green]{heading}:[/green] {name}")
453
465
  console.print(f" Status: {_plain(service.get('status'))}")
454
466
  endpoint = service.get("endpoint") or {}
455
- url = endpoint.get("public_url") or endpoint.get("url")
467
+ url = endpoint.get("public_url") or (None if _hosted_only(endpoint) else endpoint.get("url"))
456
468
  if url:
457
469
  console.print(f" URL: {_plain(url)}")
458
470
  for hint in service.get("hints") or []:
@@ -612,8 +612,18 @@ class CutoverManager:
612
612
  # and this call needs nothing but the ids. The domain rows are read
613
613
  # here, at commit time, so a domain bound during the verify window is
614
614
  # included and one removed during it is not.
615
+ # Lot 5: a hosted-only app has no local route, so there is no dial to
616
+ # repoint or verify; it is promoted on its probe alone (f1 still runs:
617
+ # the hosted resolver dials `active_host_port`). A lock landing after
618
+ # this read makes `register` refuse, the dial never converges, and the
619
+ # cutover settles `repoint_failure` with blue serving and no route.
620
+ locked = await queries.is_hosted_only(name)
615
621
  domain_rows = await queries.get_service_domains(name)
616
- caddy_ids = (_route_id(name), *(_domain_route_id(name, r.domain) for r in domain_rows))
622
+ caddy_ids: tuple[str, ...] = (
623
+ ()
624
+ if locked
625
+ else (_route_id(name), *(_domain_route_id(name, r.domain) for r in domain_rows))
626
+ )
617
627
  # The repoint REBUILDS the whole route object,
618
628
  # so the row's `edge_auth` must ride along or every cutover commit of a
619
629
  # protected service would strip its auth handler — and `_await_dial`
@@ -623,19 +633,20 @@ class CutoverManager:
623
633
  # and the cutover settles `repoint_failure` with blue restored — the
624
634
  # route is then withheld until the secret resolves. Posture beats
625
635
  # availability.
626
- await proxy.register(
627
- name,
628
- green_port,
629
- edge_auth=parse_job_config(job).get("edge_auth"),
630
- project_id=job.project_id,
631
- with_domains=True,
632
- # The SAME snapshot `caddy_ids` was derived from, never a second
633
- # read: a `DELETE` landing between two reads would leave a removed
634
- # id in the awaited set that nothing ever writes, and
635
- # `_await_dials` would burn its whole budget and unwind a healthy
636
- # green. The removed route is torn down by the next reconcile tick.
637
- domains=[r.domain for r in domain_rows],
638
- )
636
+ if not locked:
637
+ await proxy.register(
638
+ name,
639
+ green_port,
640
+ edge_auth=parse_job_config(job).get("edge_auth"),
641
+ project_id=job.project_id,
642
+ with_domains=True,
643
+ # The SAME snapshot `caddy_ids` was derived from, never a second
644
+ # read: a `DELETE` landing between two reads would leave a removed
645
+ # id in the awaited set that nothing ever writes, and
646
+ # `_await_dials` would burn its whole budget and unwind a healthy
647
+ # green. The removed route is torn down by the next reconcile tick.
648
+ domains=[r.domain for r in domain_rows],
649
+ )
639
650
  if not await self._await_dials(caddy_ids, green_port):
640
651
  await self._settle_repoint_failure(
641
652
  job, version, green_id=green_id, name=name, caddy_ids=caddy_ids
@@ -739,6 +750,8 @@ class CutoverManager:
739
750
  One deadline covers the whole set (`cutover_repoint_timeout_s`), not
740
751
  one per id.
741
752
  """
753
+ if not caddy_ids:
754
+ return True # lot 5: a hosted-only app has no route to verify
742
755
  proxy = self._c._proxy
743
756
  assert proxy is not None
744
757
  expected = f"127.0.0.1:{port}"
@@ -22,7 +22,7 @@ import time
22
22
  from collections.abc import Callable, Sequence
23
23
  from enum import Enum
24
24
  from pathlib import Path
25
- from typing import TYPE_CHECKING, Any
25
+ from typing import TYPE_CHECKING, Any, Literal
26
26
  from urllib.parse import urlsplit
27
27
 
28
28
  from nerdit.config.defaults import DEFAULT_HOST, DEFAULT_PORT
@@ -1089,6 +1089,11 @@ class ProxyManager(CaddySupervisorMixin, CaddyTlsMixin):
1089
1089
  return
1090
1090
  new_dial = f"127.0.0.1:{host_port}"
1091
1091
  try:
1092
+ # Lot 5: a hosted-only app has no local route. Inside the try, so a
1093
+ # failed lock read writes nothing (fail closed).
1094
+ if await self._queries.is_hosted_only(service_name):
1095
+ await self._deregister_unlocked(service_name)
1096
+ return
1092
1097
  try:
1093
1098
  spec = await self._build_route_off_loop(
1094
1099
  service_name, host_port, load_edge_auth(edge_auth), project_id
@@ -1240,6 +1245,23 @@ class ProxyManager(CaddySupervisorMixin, CaddyTlsMixin):
1240
1245
  except Exception:
1241
1246
  logger.warning("[proxy] deregister failed for %s", service_name, exc_info=True)
1242
1247
 
1248
+ async def local_route_state(self, service_name: str) -> Literal["absent", "present", "unknown"]:
1249
+ """Observe whether Caddy's live table holds any route of `service_name`.
1250
+
1251
+ Asks the admin API even when the proxy is disabled: a leftover Caddy may
1252
+ still serve. Unreadable is `unknown`, except a disabled proxy with no live
1253
+ pid, which has nothing that could serve (`absent`).
1254
+ """
1255
+ if self.enabled and not self._available:
1256
+ return "unknown"
1257
+ try:
1258
+ live = await self._admin.live_routes()
1259
+ except Exception:
1260
+ live = None
1261
+ if live is None:
1262
+ return "unknown" if self.enabled or self._pid_alive() else "absent"
1263
+ return "present" if any(_split_route_id(r)[0] == service_name for r in live) else "absent"
1264
+
1243
1265
  # -- reconcile (source of truth) -----------------------------------------
1244
1266
 
1245
1267
  async def reconcile(self) -> None:
@@ -2170,7 +2170,12 @@ class ServiceController:
2170
2170
 
2171
2171
  policy = job.restart_policy or "always"
2172
2172
  clean_exit = exit_code == 0
2173
- if policy == "no" or (policy == "on-failure" and clean_exit):
2173
+ # A clean exit is a finished one-shot only for kind=service. A managed
2174
+ # database or model is a long-running server: exit 0 means something
2175
+ # stopped it (a host shutdown SIGTERMs Postgres, which exits 0), so it
2176
+ # restarts whatever its persisted 'on-failure' policy says.
2177
+ one_shot_done = clean_exit and job.kind is JobKind.service
2178
+ if policy == "no" or (policy == "on-failure" and one_shot_done):
2174
2179
  final = JobStatus.completed if clean_exit else JobStatus.failed
2175
2180
  error_class = (
2176
2181
  None if clean_exit else _classify_service_exit(exit_code, oom_killed, gpu_oom)
@@ -74,8 +74,10 @@ def new_workload_row(
74
74
  ) -> Job:
75
75
  """Build the shared `building`/`running` Job skeleton for a create route.
76
76
 
77
- `restart_policy` is the fixed `"on-failure"` literal both routes pass
78
- today (models.py:158, databases.py:176) — not exposed as a parameter.
77
+ `restart_policy` is the fixed `"on-failure"` literal, not exposed as a
78
+ parameter. For these kinds `ServiceController._handle_crash` restarts on a
79
+ clean exit too (a host shutdown stops Postgres with exit 0), so a managed
80
+ server never settles to `completed`.
79
81
  `submitted_by_token`/`idempotency_key` are read from `request` (the
80
82
  same principal `authorize_create` already validated, and the same
81
83
  `Idempotency-Key` header both routes read identically).
@@ -43,6 +43,7 @@ from nerdit.daemon.views.hosted import (
43
43
  domain_cert_state,
44
44
  domain_state,
45
45
  load_hosted_context,
46
+ local_public_url,
46
47
  )
47
48
  from nerdit.daemon.views.service import _not_found, _resolve_service
48
49
  from nerdit.db.models import JobKind, TokenRole
@@ -186,7 +187,7 @@ async def _context(request: Request, service_name: str) -> tuple[HostedContext,
186
187
  endpoint = await queries.get_service_endpoint(service_name)
187
188
  if endpoint is None:
188
189
  return hosted, None
189
- return hosted, public_url_for(
190
+ url = public_url_for(
190
191
  endpoint.service_name,
191
192
  endpoint.route,
192
193
  mode=proxy.mode,
@@ -196,6 +197,7 @@ async def _context(request: Request, service_name: str) -> tuple[HostedContext,
196
197
  https_port=proxy.https_port,
197
198
  public_port=proxy.public_port,
198
199
  )
200
+ return hosted, local_public_url(hosted, endpoint.service_name, url)
199
201
 
200
202
 
201
203
  @router.get(
@@ -312,7 +314,6 @@ async def add_domain(
312
314
  f"'{job.kind.value}' workloads cannot have a direct domain.",
313
315
  hint="Only deployed apps (kind 'service') can be served under a domain.",
314
316
  )
315
-
316
317
  try:
317
318
  validated = validate_domain(domain, reserved=_reserved(request))
318
319
  # Accepted: from here the canonical (folded) name is the resource, and
@@ -378,6 +379,16 @@ async def add_domain(
378
379
  # re-checks under the DB write lock). Nothing was written, so the honest
379
380
  # answer is the 404 the resolve would have given a moment later.
380
381
  raise _not_found(name)
382
+ if outcome == "hosted_only":
383
+ # Lot 5: a domain is a local route, and a locked app has none. Refused
384
+ # under the insert's own write lock, after the shape checks above, so
385
+ # a malformed name still gets its 422 and a lock cannot race in.
386
+ raise NerditError(
387
+ 409,
388
+ "share.hosted_only_conflict",
389
+ f"Service '{service_name}' is hosted only: it has no local route.",
390
+ hint=f"Unlock it first: `nerdit share {service_name} --local-route`.",
391
+ )
381
392
  if outcome == "taken":
382
393
  # The message names the DOMAIN only. An owner-scoped principal must not
383
394
  # be able to enumerate another owner's app names by probing names it
@@ -8,7 +8,7 @@ from fastapi import APIRouter, Request, Response
8
8
 
9
9
  from nerdit.core.proxy import _route_id, public_url_for
10
10
  from nerdit.daemon.errors import NerditError
11
- from nerdit.daemon.views.hosted import load_hosted_context, public_urls_for
11
+ from nerdit.daemon.views.hosted import load_hosted_context, local_public_url, public_urls_for
12
12
  from nerdit.db.models import ProxyStatusResponse, RouteItem, RouteListPage
13
13
  from nerdit.utils.certs import ca_fingerprint
14
14
 
@@ -139,6 +139,7 @@ async def list_routes(
139
139
  https_port=proxy_settings.https_port,
140
140
  public_port=proxy_settings.public_port,
141
141
  )
142
+ public_url = local_public_url(hosted, endpoint.service_name, public_url)
142
143
  live: dict[str, object] | None = None
143
144
  if live_table == "readable" and manager is not None:
144
145
  # (P25, rev. 2 amendment 2) The id seam, NOT `build_route`: since
@@ -169,6 +170,7 @@ async def list_routes(
169
170
  public_url=public_url,
170
171
  public_urls=public_urls_for(hosted, endpoint.service_name, public_url),
171
172
  live=live,
173
+ reason="hosted_only" if hosted.locked(endpoint.service_name) else None,
172
174
  )
173
175
  )
174
176
  return RouteListPage(items=items, next_cursor=next_cursor, live_table=live_table)