pz-cli 5.0.0__py3-none-any.whl

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 (399) hide show
  1. pz_cli-5.0.0.dist-info/METADATA +51 -0
  2. pz_cli-5.0.0.dist-info/RECORD +399 -0
  3. pz_cli-5.0.0.dist-info/WHEEL +4 -0
  4. pz_cli-5.0.0.dist-info/entry_points.txt +5 -0
  5. pz_core/__init__.py +64 -0
  6. pz_core/__main__.py +7 -0
  7. pz_core/app.py +45 -0
  8. pz_core/assembly/__init__.py +17 -0
  9. pz_core/assembly/app_discovery.py +1240 -0
  10. pz_core/assembly/apps/__init__.py +25 -0
  11. pz_core/assembly/apps/compiler.py +824 -0
  12. pz_core/assembly/apps/providers.py +188 -0
  13. pz_core/assembly/cli/__init__.py +9 -0
  14. pz_core/assembly/cli/compiler.py +166 -0
  15. pz_core/assembly/diagnostics/__init__.py +21 -0
  16. pz_core/assembly/diagnostics/checks.py +257 -0
  17. pz_core/assembly/diagnostics/dev_sources.py +569 -0
  18. pz_core/assembly/events/__init__.py +5 -0
  19. pz_core/assembly/events/compiler.py +219 -0
  20. pz_core/assembly/extensions/__init__.py +5 -0
  21. pz_core/assembly/extensions/compiler.py +696 -0
  22. pz_core/assembly/infra/__init__.py +6 -0
  23. pz_core/assembly/infra/compiler.py +272 -0
  24. pz_core/assembly/lifecycle/__init__.py +5 -0
  25. pz_core/assembly/lifecycle/compiler.py +95 -0
  26. pz_core/assembly/middlewares/__init__.py +19 -0
  27. pz_core/assembly/middlewares/compiler.py +508 -0
  28. pz_core/assembly/project/__init__.py +7 -0
  29. pz_core/assembly/project/compiler.py +9 -0
  30. pz_core/assembly/project/context.py +17 -0
  31. pz_core/assembly/project/discovery.py +227 -0
  32. pz_core/assembly/resources/__init__.py +19 -0
  33. pz_core/assembly/resources/compiler.py +310 -0
  34. pz_core/assembly/resources/settings.py +129 -0
  35. pz_core/assembly/routes/__init__.py +7 -0
  36. pz_core/assembly/routes/compiler.py +400 -0
  37. pz_core/assembly/runtime_manifests/__init__.py +13 -0
  38. pz_core/assembly/runtime_manifests/compiler.py +159 -0
  39. pz_core/assembly/settings/__init__.py +17 -0
  40. pz_core/assembly/settings/compiler.py +786 -0
  41. pz_core/assembly/settings/host_project_overrides.py +82 -0
  42. pz_core/assembly/settings/host_runtime_endpoint.py +207 -0
  43. pz_core/authoring/__init__.py +79 -0
  44. pz_core/authoring/app_init_service.py +946 -0
  45. pz_core/authoring/app_versions.py +814 -0
  46. pz_core/authoring/cli.py +192 -0
  47. pz_core/authoring/commands.py +1810 -0
  48. pz_core/authoring/conflicts.py +180 -0
  49. pz_core/authoring/dev_bindings.py +916 -0
  50. pz_core/authoring/dev_git.py +1087 -0
  51. pz_core/authoring/dev_import.py +1061 -0
  52. pz_core/authoring/dev_manifest.py +522 -0
  53. pz_core/authoring/dev_sources.py +1638 -0
  54. pz_core/authoring/env_cli.py +454 -0
  55. pz_core/authoring/files.py +109 -0
  56. pz_core/authoring/ide_projection.py +1129 -0
  57. pz_core/authoring/naming.py +58 -0
  58. pz_core/authoring/official_dev_sources.py +432 -0
  59. pz_core/authoring/official_sources.json +156 -0
  60. pz_core/authoring/orchestration_service.py +558 -0
  61. pz_core/authoring/project_default.jsonc +53 -0
  62. pz_core/authoring/project_defaults.py +88 -0
  63. pz_core/authoring/project_metadata_service.py +199 -0
  64. pz_core/authoring/project_settings_checks.py +51 -0
  65. pz_core/authoring/project_settings_cli.py +299 -0
  66. pz_core/authoring/project_settings_service.py +1700 -0
  67. pz_core/authoring/project_state.py +399 -0
  68. pz_core/authoring/publish_git.py +423 -0
  69. pz_core/authoring/publish_lock.py +188 -0
  70. pz_core/authoring/publish_source.py +195 -0
  71. pz_core/authoring/release.py +1382 -0
  72. pz_core/authoring/repo_keys.py +29 -0
  73. pz_core/authoring/root_cli.py +28 -0
  74. pz_core/authoring/scaffold_service.py +325 -0
  75. pz_core/authoring/sync_service.py +787 -0
  76. pz_core/authoring/templates/app/__init__.py +1 -0
  77. pz_core/authoring/templates/app/api.py +25 -0
  78. pz_core/authoring/templates/app/apps.py +31 -0
  79. pz_core/authoring/templates/app/auth_package_init.py +14 -0
  80. pz_core/authoring/templates/app/auth_readme.py +29 -0
  81. pz_core/authoring/templates/app/configs_package_init.py +14 -0
  82. pz_core/authoring/templates/app/default_env.py +18 -0
  83. pz_core/authoring/templates/app/default_jsonc.py +25 -0
  84. pz_core/authoring/templates/app/locales_readme.py +25 -0
  85. pz_core/authoring/templates/app/migrations_readme.py +19 -0
  86. pz_core/authoring/templates/app/models.py +14 -0
  87. pz_core/authoring/templates/app/package_init.py +8 -0
  88. pz_core/authoring/templates/app/pages_base.py +161 -0
  89. pz_core/authoring/templates/app/pages_readme.py +25 -0
  90. pz_core/authoring/templates/app/public.py +31 -0
  91. pz_core/authoring/templates/app/pyproject.py +77 -0
  92. pz_core/authoring/templates/app/readme.py +74 -0
  93. pz_core/authoring/templates/app/settings.py +46 -0
  94. pz_core/authoring/templates/app/static_app_js.py +24 -0
  95. pz_core/authoring/templates/app/static_readme.py +31 -0
  96. pz_core/authoring/templates/app/template_config_page.py +23 -0
  97. pz_core/authoring/templates/app/template_index_html.py +28 -0
  98. pz_core/authoring/templates/app/templates_readme.py +32 -0
  99. pz_core/authoring/templates/app/urls.py +36 -0
  100. pz_core/authoring/templates/app/views.py +36 -0
  101. pz_core/authoring/templates/project/__init__.py +1 -0
  102. pz_core/authoring/templates/project/agents.py +51 -0
  103. pz_core/authoring/templates/project/apps_container_readme.py +37 -0
  104. pz_core/authoring/templates/project/main_py.py +59 -0
  105. pz_core/authoring/templates/project/project_env.py +19 -0
  106. pz_core/authoring/templates/project/pyproject.py +26 -0
  107. pz_core/authoring/templates/project/pz_env_env.py +65 -0
  108. pz_core/authoring/templates/project/pz_env_jsonc.py +69 -0
  109. pz_core/authoring/templates/project/pz_jsonc.py +12 -0
  110. pz_core/authoring/templates/project/readme.py +94 -0
  111. pz_core/authoring/templates/project/root_gitkeep.py +8 -0
  112. pz_core/authoring/templates/shared/__init__.py +5 -0
  113. pz_core/authoring/templates/shared/text.py +10 -0
  114. pz_core/authoring/venv_service.py +87 -0
  115. pz_core/authoring/workspace.py +167 -0
  116. pz_core/cli.py +291 -0
  117. pz_core/cli_options.py +74 -0
  118. pz_core/core/__init__.py +3 -0
  119. pz_core/core/errors/__init__.py +5 -0
  120. pz_core/core/errors/models.py +117 -0
  121. pz_core/core/events/__init__.py +5 -0
  122. pz_core/core/events/registry.py +54 -0
  123. pz_core/core/extensions/__init__.py +51 -0
  124. pz_core/core/extensions/registry.py +790 -0
  125. pz_core/core/health/__init__.py +6 -0
  126. pz_core/core/health/contracts.py +10 -0
  127. pz_core/core/middlewares/__init__.py +6 -0
  128. pz_core/core/middlewares/models.py +24 -0
  129. pz_core/core/routing/__init__.py +5 -0
  130. pz_core/core/routing/models.py +21 -0
  131. pz_core/core/settings_contracts.py +25 -0
  132. pz_core/docs/README.md +20 -0
  133. pz_core/docs/distribution-README.md +438 -0
  134. pz_core/docs/pz/345/221/275/344/273/244/346/200/273/350/247/210.md +1011 -0
  135. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/00-pz/346/236/266/346/236/204/346/200/273/347/272/262.md +240 -0
  136. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/01-/347/233/256/345/275/225/344/270/216/345/210/206/345/261/202/350/247/204/350/214/203.md +216 -0
  137. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/02-/345/214/205/344/270/216/345/210/206/345/217/221/350/247/204/350/214/203.md +403 -0
  138. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/03-App/346/234/200/345/260/217/345/255/220/345/214/205/350/247/204/350/214/203.md +571 -0
  139. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/04-Settings/344/270/216/351/205/215/347/275/256/350/247/204/350/214/203.md +1177 -0
  140. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/05-/350/265/204/346/272/220/344/270/216/350/277/220/350/241/214/347/233/256/345/275/225/350/247/204/350/214/203.md +575 -0
  141. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/06-/346/225/260/346/215/256/345/272/223/344/270/216/345/217/257/351/200/211/345/237/272/347/241/200/350/256/276/346/226/275/350/247/204/350/214/203.md +499 -0
  142. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/07-App/346/233/277/346/215/242/350/247/204/350/214/203.md +206 -0
  143. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/08-/345/271/263/345/217/260/347/272/247App/344/270/216/350/277/220/350/241/214/346/227/266/345/215/225/344/276/213/350/247/204/350/214/203.md +445 -0
  144. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/09-CLI/344/270/216Worker/345/205/245/345/217/243/350/247/204/350/214/203.md +950 -0
  145. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/10-/346/265/213/350/257/225/344/270/216/345/245/221/347/272/246/350/247/204/350/214/203.md +1001 -0
  146. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/11-/344/273/223/345/272/223/347/234/237/347/233/270/346/272/220/345/234/260/345/233/276.md +102 -0
  147. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/12-/346/236/204/345/273/272/344/270/216/345/217/221/345/270/203/346/243/200/346/237/245/346/270/205/345/215/225.md +171 -0
  148. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/13-/346/255/243/345/274/217Facade/346/200/273/350/241/250.md +402 -0
  149. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/14-/345/210/206/345/261/202/344/270/216/346/211/251/345/261/225/350/247/204/350/214/203.md +550 -0
  150. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/15-/345/255/220/345/214/205/345/274/200/345/217/221/344/275/277/347/224/250/346/211/213/345/206/214.md +1004 -0
  151. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/16-/346/211/251/345/261/225/347/202/271/346/255/243/345/274/217/345/215/217/350/256/256/346/270/205/345/215/225.md +352 -0
  152. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/17-pz.protocols.v1/346/255/243/345/274/217/345/215/217/350/256/256.md +266 -0
  153. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/18-/346/211/251/345/261/225/347/202/271/345/206/262/347/252/201/350/243/201/345/206/263/346/200/273/350/241/250.md +131 -0
  154. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/19-/351/224/231/350/257/257/347/240/201/344/270/216/350/257/212/346/226/255/345/215/217/350/256/256.md +176 -0
  155. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/20-/347/247/201/346/234/211/345/214/205/344/273/223/344/270/216/347/273/237/344/270/200/350/256/244/350/257/201/351/203/250/347/275/262/350/247/204/350/214/203.md +243 -0
  156. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/21-contrib/347/272/246/345/256/232/344/270/216Provider/346/216/245/345/205/245/350/247/204/350/214/203.md +365 -0
  157. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/22-/345/244/232/345/233/275/350/257/255/350/250/200/350/265/204/346/272/220/346/262/273/347/220/206/344/270/216/346/214/211/351/234/200/345/212/240/350/275/275/350/247/204/350/214/203.md +912 -0
  158. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/23-/351/246/226/345/261/217/345/261/225/347/244/272/344/270/216/351/246/226/345/270/247/347/250/263/345/256/232/350/247/204/350/214/203.md +269 -0
  159. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/24-pz/345/220/257/345/212/250/344/270/216/350/277/220/350/241/214/346/227/266/347/274/226/350/257/221/346/265/201/347/250/213.md +245 -0
  160. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/25-/351/235/242/345/220/221/345/257/271/350/261/241/345/274/200/345/217/221/350/247/204/350/214/203.md +300 -0
  161. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/README.md +98 -0
  162. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/archive//344/270/255/351/227/264/346/200/201/00-legacy_core/346/240/207/345/207/206pip/345/214/205/345/214/226/351/207/215/346/236/204/346/200/273/347/272/262.md +1073 -0
  163. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/archive//344/270/255/351/227/264/346/200/201/01-/347/233/256/345/275/225/344/270/216/345/210/206/345/261/202/351/207/215/346/236/204/346/211/247/350/241/214/346/226/271/346/241/210.md +224 -0
  164. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/archive//344/270/255/351/227/264/346/200/201/02-/345/214/205/344/270/216/345/210/206/345/217/221/350/247/204/350/214/203-/345/220/253/345/216/206/345/217/262/345/217/243/345/276/204.md +671 -0
  165. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/archive//344/270/255/351/227/264/346/200/201/13-/344/270/200/346/254/241/346/200/247/345/256/236/346/226/275/346/270/205/345/215/225.md +387 -0
  166. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/archive//344/270/255/351/227/264/346/200/201/14-/345/255/220/345/214/205/351/200/202/351/205/215/346/224/271/351/200/240/346/270/205/345/215/225.md +592 -0
  167. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/archive//344/270/255/351/227/264/346/200/201/15-infra/350/243/201/345/211/252/344/270/216/350/220/275/344/275/215/346/270/205/345/215/225.md +81 -0
  168. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/archive//344/270/255/351/227/264/346/200/201/19-core/345/215/225/344/270/200/346/255/243/345/274/217/346/226/271/346/241/210/346/224/266/346/225/233/346/211/247/350/241/214/346/270/205/345/215/225.md +161 -0
  169. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/archive//344/270/255/351/227/264/346/200/201/25-/345/215/217/350/256/256/345/210/260/346/272/220/347/240/201/345/267/256/350/267/235/346/270/205/345/215/225.md +377 -0
  170. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/archive//344/270/255/351/227/264/346/200/201/26-cutover/345/256/214/345/267/245/346/243/200/346/237/245/346/270/205/345/215/225.md +117 -0
  171. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/archive//344/270/255/351/227/264/346/200/201/29-/345/244/232/345/233/275/350/257/255/350/250/200/350/265/204/346/272/220/346/262/273/347/220/206/344/270/216/346/214/211/351/234/200/345/212/240/350/275/275/350/247/204/350/214/203-/345/220/253/350/277/201/347/247/273/347/255/226/347/225/245.md +956 -0
  172. pz_core/docs/pz/350/247/204/350/214/203/344/270/216/344/275/277/347/224/250/archive//344/270/255/351/227/264/346/200/201/99-/350/265/204/346/272/220/346/224/266/345/217/243/345/220/216/347/273/255TODO.md +84 -0
  173. pz_core/docs//345/217/221/347/211/210/345/211/215/346/270/205/345/215/225-4.0.11.md +84 -0
  174. pz_core/docs//346/226/260/346/211/213/346/214/207/345/215/227.md +422 -0
  175. pz_core/infra/__init__.py +17 -0
  176. pz_core/infra/db/__init__.py +23 -0
  177. pz_core/infra/db/contracts.py +73 -0
  178. pz_core/infra/health/__init__.py +5 -0
  179. pz_core/infra/health/contracts.py +8 -0
  180. pz_core/infra/kv/__init__.py +5 -0
  181. pz_core/infra/kv/contracts.py +17 -0
  182. pz_core/infra/observability/__init__.py +23 -0
  183. pz_core/infra/observability/contracts.py +142 -0
  184. pz_core/infra/resilience/__init__.py +5 -0
  185. pz_core/infra/resilience/contracts.py +17 -0
  186. pz_core/infra/resources/__init__.py +5 -0
  187. pz_core/infra/resources/contracts.py +16 -0
  188. pz_core/infra/settings/__init__.py +17 -0
  189. pz_core/infra/settings/models.py +81 -0
  190. pz_core/infra/storage/__init__.py +5 -0
  191. pz_core/infra/storage/ports.py +27 -0
  192. pz_core/project_root_commands.py +282 -0
  193. pz_core/project_runtime_cli.py +344 -0
  194. pz_core/project_sources.py +253 -0
  195. pz_core/pz_launcher.py +209 -0
  196. pz_core/root_cli.py +70 -0
  197. pz_core/runtime/__init__.py +229 -0
  198. pz_core/runtime/access.py +86 -0
  199. pz_core/runtime/adapters/__init__.py +5 -0
  200. pz_core/runtime/adapters/base.py +18 -0
  201. pz_core/runtime/asyncio_bridge.py +18 -0
  202. pz_core/runtime/bindings/__init__.py +5 -0
  203. pz_core/runtime/bindings/container.py +50 -0
  204. pz_core/runtime/cli/__init__.py +5 -0
  205. pz_core/runtime/cli/_args.py +67 -0
  206. pz_core/runtime/cli/app_commands.py +417 -0
  207. pz_core/runtime/cli/check.py +214 -0
  208. pz_core/runtime/cli/db.py +297 -0
  209. pz_core/runtime/cli/main.py +89 -0
  210. pz_core/runtime/cli/resources.py +280 -0
  211. pz_core/runtime/discovery.py +23 -0
  212. pz_core/runtime/environment_integrity.py +157 -0
  213. pz_core/runtime/errors.py +5 -0
  214. pz_core/runtime/events.py +87 -0
  215. pz_core/runtime/facades/__init__.py +38 -0
  216. pz_core/runtime/facades/_capabilities.py +46 -0
  217. pz_core/runtime/facades/cache.py +103 -0
  218. pz_core/runtime/facades/database.py +71 -0
  219. pz_core/runtime/facades/events.py +380 -0
  220. pz_core/runtime/facades/health.py +101 -0
  221. pz_core/runtime/facades/mail.py +121 -0
  222. pz_core/runtime/facades/observability.py +102 -0
  223. pz_core/runtime/facades/providers.py +75 -0
  224. pz_core/runtime/facades/resources.py +52 -0
  225. pz_core/runtime/facades/storage.py +296 -0
  226. pz_core/runtime/health/__init__.py +5 -0
  227. pz_core/runtime/health/service.py +102 -0
  228. pz_core/runtime/host.py +188 -0
  229. pz_core/runtime/host_runner.py +282 -0
  230. pz_core/runtime/http/__init__.py +20 -0
  231. pz_core/runtime/http/app.py +576 -0
  232. pz_core/runtime/http_timing.py +173 -0
  233. pz_core/runtime/invocation.py +29 -0
  234. pz_core/runtime/json_safe.py +47 -0
  235. pz_core/runtime/lifecycle/__init__.py +25 -0
  236. pz_core/runtime/lifecycle/manager.py +533 -0
  237. pz_core/runtime/plan.py +214 -0
  238. pz_core/runtime/plan_context.py +33 -0
  239. pz_core/runtime/plan_snapshot.py +177 -0
  240. pz_core/runtime/registries/__init__.py +27 -0
  241. pz_core/runtime/registries/capabilities.py +101 -0
  242. pz_core/runtime/registries/providers.py +396 -0
  243. pz_core/runtime/resource_assets.py +344 -0
  244. pz_core/runtime/run_cli.py +211 -0
  245. pz_core/runtime/settings.py +888 -0
  246. pz_core/runtime/worker/__init__.py +21 -0
  247. pz_core/runtime/worker/engine.py +921 -0
  248. pz_core/runtime/worker/runtime.py +25 -0
  249. pz_core/testing/__init__.py +15 -0
  250. pz_core/testing/doc_route_contracts.py +305 -0
  251. pz_core/utils/__init__.py +44 -0
  252. pz_core/utils/env.py +65 -0
  253. pz_core/utils/project_jsonc/__init__.py +29 -0
  254. pz_core/utils/project_jsonc/document.py +420 -0
  255. pz_core/utils/project_settings.py +99 -0
  256. pz_foundation/__init__.py +50 -0
  257. pz_foundation/_contracts.py +81 -0
  258. pz_foundation/_errors.py +34 -0
  259. pz_foundation/api.py +14 -0
  260. pz_foundation/apps.py +127 -0
  261. pz_foundation/auth/__init__.py +1 -0
  262. pz_foundation/cache_control.py +233 -0
  263. pz_foundation/cli/__init__.py +2 -0
  264. pz_foundation/cli/workers.py +565 -0
  265. pz_foundation/configs/__init__.py +0 -0
  266. pz_foundation/configs/default.env +15 -0
  267. pz_foundation/configs/default.jsonc +452 -0
  268. pz_foundation/db/__init__.py +94 -0
  269. pz_foundation/db/aerich_executor.py +664 -0
  270. pz_foundation/db/config.py +358 -0
  271. pz_foundation/db/connection_manager.py +689 -0
  272. pz_foundation/db/connection_probe.py +204 -0
  273. pz_foundation/db/database.py +1291 -0
  274. pz_foundation/db/deps.py +47 -0
  275. pz_foundation/db/lifecycle_status.py +85 -0
  276. pz_foundation/db/migrations.py +477 -0
  277. pz_foundation/db/mysql.py +57 -0
  278. pz_foundation/db/postgresql.py +409 -0
  279. pz_foundation/db/production_pool_fallback.py +143 -0
  280. pz_foundation/db/public.py +45 -0
  281. pz_foundation/db/session.py +369 -0
  282. pz_foundation/db/sqlite.py +15 -0
  283. pz_foundation/db/tortoise_manager.py +724 -0
  284. pz_foundation/db/tortoise_psycopg.py +148 -0
  285. pz_foundation/docs/README.md +18 -0
  286. pz_foundation/docs/distribution-README.md +319 -0
  287. pz_foundation/docs/infra/350/243/201/345/211/252/344/270/216/350/220/275/344/275/215/350/257/264/346/230/216.md +39 -0
  288. pz_foundation/docs//345/205/254/345/274/200/345/205/215/350/264/271/346/216/245/345/217/243/351/230/262/346/273/245/347/224/250/344/270/216/344/272/272/346/234/272/346/240/241/351/252/214.md +680 -0
  289. pz_foundation/docs//350/256/276/350/256/241/346/226/207/346/241/243.md +288 -0
  290. pz_foundation/docs//350/267/257/347/224/261/344/270/216URL/345/244/204/347/220/206/350/247/204/350/214/203.md +119 -0
  291. pz_foundation/health.py +105 -0
  292. pz_foundation/host/__init__.py +11 -0
  293. pz_foundation/host/builder.py +34 -0
  294. pz_foundation/host/cache_runtime.py +44 -0
  295. pz_foundation/host/db/__init__.py +17 -0
  296. pz_foundation/host/db/aerich_config.py +36 -0
  297. pz_foundation/host/db/config.py +106 -0
  298. pz_foundation/host/db/connection_manager.py +48 -0
  299. pz_foundation/host/db/deps.py +5 -0
  300. pz_foundation/host/db/orm_retry_patch.py +566 -0
  301. pz_foundation/host/db/request_adapter.py +325 -0
  302. pz_foundation/host/db/router.py +28 -0
  303. pz_foundation/host/db/session.py +284 -0
  304. pz_foundation/host/db/tortoise_manager.py +10 -0
  305. pz_foundation/host/idempotency.py +132 -0
  306. pz_foundation/host/lifespan.py +247 -0
  307. pz_foundation/host/redis_runtime.py +100 -0
  308. pz_foundation/host/web.py +790 -0
  309. pz_foundation/infra_provider.py +236 -0
  310. pz_foundation/kv/__init__.py +25 -0
  311. pz_foundation/kv/cache.py +1166 -0
  312. pz_foundation/kv/deps.py +84 -0
  313. pz_foundation/kv/public.py +25 -0
  314. pz_foundation/kv/redis_manager.py +1531 -0
  315. pz_foundation/lazy_exports.py +43 -0
  316. pz_foundation/lazy_imports.py +124 -0
  317. pz_foundation/lifecycle.py +392 -0
  318. pz_foundation/managed_workers.py +1117 -0
  319. pz_foundation/middlewares/__init__.py +26 -0
  320. pz_foundation/middlewares/_settings.py +250 -0
  321. pz_foundation/middlewares/analytics.py +124 -0
  322. pz_foundation/middlewares/cache_control.py +156 -0
  323. pz_foundation/middlewares/compression.py +202 -0
  324. pz_foundation/middlewares/cors.py +62 -0
  325. pz_foundation/middlewares/geo_country.py +44 -0
  326. pz_foundation/middlewares/inner_router.py +50 -0
  327. pz_foundation/middlewares/rate_limited/__init__.py +3 -0
  328. pz_foundation/middlewares/rate_limited/key_limiter.py +115 -0
  329. pz_foundation/middlewares/request_logging.py +104 -0
  330. pz_foundation/middlewares/route_owner.py +309 -0
  331. pz_foundation/middlewares/security_csrf.py +44 -0
  332. pz_foundation/middlewares/security_headers/__init__.py +4 -0
  333. pz_foundation/middlewares/security_headers/conflicts.py +44 -0
  334. pz_foundation/middlewares/security_headers/csp_nonce.py +200 -0
  335. pz_foundation/middlewares/security_headers/security_headers.py +44 -0
  336. pz_foundation/migrations/__init__.py +1 -0
  337. pz_foundation/migrations/pz_foundation/__init__.py +1 -0
  338. pz_foundation/models.py +22 -0
  339. pz_foundation/notifications/__init__.py +23 -0
  340. pz_foundation/notifications/email.py +542 -0
  341. pz_foundation/notifications/public.py +31 -0
  342. pz_foundation/observability/__init__.py +71 -0
  343. pz_foundation/observability/http.py +34 -0
  344. pz_foundation/observability/logger/__init__.py +49 -0
  345. pz_foundation/observability/logger/_bot_settings.py +126 -0
  346. pz_foundation/observability/logger/bot_message.py +592 -0
  347. pz_foundation/observability/logger/core.py +385 -0
  348. pz_foundation/observability/logger/delivery_audit.py +84 -0
  349. pz_foundation/observability/logger/feishu_bot.py +199 -0
  350. pz_foundation/observability/logger/telegram_bot.py +194 -0
  351. pz_foundation/observability/metrics.py +365 -0
  352. pz_foundation/observability/runtime.py +68 -0
  353. pz_foundation/public.py +405 -0
  354. pz_foundation/resources/__init__.py +12 -0
  355. pz_foundation/resources/builder.py +900 -0
  356. pz_foundation/resources/images.py +363 -0
  357. pz_foundation/root_cli.py +20 -0
  358. pz_foundation/routing/__init__.py +203 -0
  359. pz_foundation/routing/canonical.py +486 -0
  360. pz_foundation/routing/convertors.py +82 -0
  361. pz_foundation/routing/declarations.py +843 -0
  362. pz_foundation/routing/exports.py +148 -0
  363. pz_foundation/routing/loaders.py +138 -0
  364. pz_foundation/routing/manifest.py +215 -0
  365. pz_foundation/routing/manifest_provider.py +47 -0
  366. pz_foundation/routing/mounts.py +101 -0
  367. pz_foundation/routing/ordering.py +172 -0
  368. pz_foundation/routing/registry.py +133 -0
  369. pz_foundation/routing/utils.py +28 -0
  370. pz_foundation/runtime.py +234 -0
  371. pz_foundation/settings.py +253 -0
  372. pz_foundation/settings_normalization.py +130 -0
  373. pz_foundation/storage/__init__.py +25 -0
  374. pz_foundation/storage/local.py +209 -0
  375. pz_foundation/storage/public.py +106 -0
  376. pz_foundation/storage/s3.py +116 -0
  377. pz_foundation/storage/service.py +234 -0
  378. pz_foundation/urls.py +19 -0
  379. pz_foundation/utils/__init__.py +6 -0
  380. pz_foundation/utils/merge.py +160 -0
  381. pz_foundation/worker_replicas.py +55 -0
  382. pz_settings_center/__init__.py +5 -0
  383. pz_settings_center/api/__init__.py +5 -0
  384. pz_settings_center/api/runtime_overrides.py +360 -0
  385. pz_settings_center/apps.py +28 -0
  386. pz_settings_center/configs/__init__.py +0 -0
  387. pz_settings_center/configs/default.env +2 -0
  388. pz_settings_center/configs/default.jsonc +1 -0
  389. pz_settings_center/docs/01-settings_center/350/203/275/345/212/233/350/276/271/347/225/214.md +124 -0
  390. pz_settings_center/docs/README.md +12 -0
  391. pz_settings_center/docs/distribution-README.md +150 -0
  392. pz_settings_center/docs//350/256/276/350/256/241/346/226/207/346/241/243.md +48 -0
  393. pz_settings_center/integration_payload.py +18 -0
  394. pz_settings_center/project_helpers.py +405 -0
  395. pz_settings_center/public.py +139 -0
  396. pz_settings_center/routing_helpers.py +73 -0
  397. pz_settings_center/runtime_override_provider.py +61 -0
  398. pz_settings_center/typed_settings.py +324 -0
  399. pz_settings_center/urls.py +9 -0
@@ -0,0 +1,29 @@
1
+ from __future__ import annotations
2
+
3
+
4
+ def _identity_basename(identity: str) -> str:
5
+ candidate = str(identity or "").strip().rstrip("/\\")
6
+ if not candidate:
7
+ return "repo"
8
+ normalized = candidate.replace("\\", "/").replace(":", "/")
9
+ parts = [part for part in normalized.split("/") if part]
10
+ name = parts[-1] if parts else "repo"
11
+ if name.lower().endswith(".git"):
12
+ name = name[:-4]
13
+ return name or "repo"
14
+
15
+
16
+ def build_repo_key(identity: str) -> str:
17
+ name = _identity_basename(identity)
18
+ safe_name = "".join(ch if ch.isalnum() or ch in {"_", "-"} else "_" for ch in name)
19
+ return safe_name or "repo"
20
+
21
+
22
+ def normalize_repo_key_candidate(value: object) -> str | None:
23
+ if not isinstance(value, str):
24
+ return None
25
+ candidate = value.strip()
26
+ if not candidate:
27
+ return None
28
+ candidate = "".join(ch if ch.isalnum() or ch in {"_", "-"} else "_" for ch in candidate)
29
+ return candidate or None
@@ -0,0 +1,28 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Sequence
4
+
5
+ from pz_core.root_cli import RootCommandHandler
6
+ from pz_core.runtime.run_cli import main as run_main
7
+
8
+ from .cli import main
9
+ from .project_settings_cli import main as settings_main
10
+
11
+
12
+ def _delegate_with_prefix(prefix: str, argv: Sequence[str]) -> int:
13
+ return main([prefix, *argv])
14
+
15
+
16
+ def get_root_command_handlers() -> tuple[RootCommandHandler, ...]:
17
+ return (
18
+ RootCommandHandler(command_path=("settings",), handler=settings_main),
19
+ RootCommandHandler(command_path=("env",), handler=lambda argv: _delegate_with_prefix("env", argv)),
20
+ RootCommandHandler(command_path=("startapp",), handler=lambda argv: _delegate_with_prefix("startapp", argv)),
21
+ RootCommandHandler(command_path=("app",), handler=lambda argv: _delegate_with_prefix("app", argv)),
22
+ RootCommandHandler(command_path=("dev",), handler=lambda argv: _delegate_with_prefix("dev", argv)),
23
+ RootCommandHandler(command_path=("pub",), handler=lambda argv: _delegate_with_prefix("pub", argv)),
24
+ RootCommandHandler(command_path=("run",), handler=run_main),
25
+ )
26
+
27
+
28
+ __all__ = ["get_root_command_handlers"]
@@ -0,0 +1,325 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ from dataclasses import dataclass
5
+ from pathlib import Path
6
+
7
+ from pz_core.project_runtime_cli import resolve_project_runtime_package_root
8
+ from pz_core.runtime.errors import PzError
9
+ from pz_core.project_sources import resolve_sources_root
10
+
11
+ from .files import (
12
+ ScaffoldFile,
13
+ append_or_create_file,
14
+ render_upserted_managed_block,
15
+ write_files,
16
+ )
17
+ from .naming import build_app_id, derive_default_local_app_slug, normalize_app_slug
18
+ from .project_state import resolve_runtime_source_path
19
+ from .repo_keys import build_repo_key
20
+ from .workspace import resolve_workspace_package
21
+ from .templates.app.apps import app_apps_template
22
+ from .templates.app.auth_package_init import app_auth_package_init_template
23
+ from .templates.app.auth_readme import app_auth_readme_template
24
+ from .templates.app.configs_package_init import app_configs_package_init_template
25
+ from .templates.app.default_env import app_default_env_template
26
+ from .templates.app.default_jsonc import app_default_jsonc_template
27
+ from .templates.app.locales_readme import app_locales_readme_template
28
+ from .templates.app.migrations_readme import app_migrations_readme_template
29
+ from .templates.app.models import app_models_template
30
+ from .templates.app.package_init import app_package_init_template
31
+ from .templates.app.api import app_api_template
32
+ from .templates.app.pages_base import (
33
+ app_pages_base_template,
34
+ app_pages_key_template,
35
+ app_pages_namespace_base_template,
36
+ )
37
+ from .templates.app.pages_readme import app_pages_readme_template
38
+ from .templates.app.public import app_public_template
39
+ from .templates.app.pyproject import app_pyproject_template
40
+ from .templates.app.readme import app_readme_template
41
+ from .templates.app.urls import app_urls_template
42
+ from .templates.app.settings import app_settings_template
43
+ from .templates.app.static_app_js import app_static_app_js_template
44
+ from .templates.app.static_readme import app_static_readme_template
45
+ from .templates.app.template_config_page import app_template_config_page_template
46
+ from .templates.app.template_index_html import app_template_index_html_template
47
+ from .templates.app.templates_readme import app_templates_readme_template
48
+ from .templates.app.views import app_views_template
49
+ from .templates.project.agents import project_agents_template
50
+ from .templates.project.apps_container_readme import apps_container_readme_template
51
+ from .templates.project.main_py import main_py_template
52
+ from .templates.project.project_env import project_env_template
53
+ from .templates.project.pyproject import project_pyproject_template
54
+ from .templates.project.pz_env_env import pz_env_env_template
55
+ from .templates.project.pz_env_jsonc import pz_env_jsonc_template
56
+ from .templates.project.pz_jsonc import pz_jsonc_template
57
+ from .templates.project.readme import project_readme_template
58
+ from .templates.project.root_gitkeep import root_gitkeep_template
59
+
60
+ _AGENTS_START_MARKER = "<!-- pz:managed:start -->"
61
+ _AGENTS_END_MARKER = "<!-- pz:managed:end -->"
62
+ _PZ_RUNTIME_REPO = "git@github.com:JiXiangTools/pyzza.git"
63
+
64
+
65
+ @dataclass(frozen=True, slots=True)
66
+ class ProjectScaffoldResult:
67
+ created: bool
68
+
69
+
70
+ @dataclass(frozen=True, slots=True)
71
+ class LocalAppScaffoldResult:
72
+ app_id: str | None
73
+ created: bool
74
+
75
+
76
+ @dataclass(frozen=True, slots=True)
77
+ class ProjectAgentsRefreshResult:
78
+ path: Path
79
+ created: bool
80
+
81
+
82
+ def _repo_key(identity: str) -> str:
83
+ return build_repo_key(identity)
84
+
85
+
86
+ def _default_project_local_pz_docs_root(project_dir: Path) -> Path:
87
+ return (
88
+ resolve_sources_root(project_dir)
89
+ / _repo_key(_PZ_RUNTIME_REPO)
90
+ / "pz_core"
91
+ / "pz_core"
92
+ / "docs"
93
+ / "pz规范与使用"
94
+ )
95
+
96
+
97
+ def resolve_project_local_pz_docs_root(project_dir: Path) -> Path:
98
+ workspace_package = resolve_workspace_package("pz", anchors=(project_dir,))
99
+ if workspace_package is not None:
100
+ workspace_root = workspace_package.repo
101
+ for docs_candidate in (
102
+ workspace_root / "docs" / "pz规范与使用",
103
+ workspace_root / "pz_core" / "docs" / "pz规范与使用",
104
+ workspace_root / "pz_core" / "pz_core" / "docs" / "pz规范与使用",
105
+ workspace_root.parent / "docs" / "pz规范与使用",
106
+ ):
107
+ if docs_candidate.is_dir():
108
+ return docs_candidate.resolve()
109
+ runtime_root = resolve_runtime_source_path(project_dir, "pz")
110
+ if runtime_root is not None:
111
+ docs_candidate = runtime_root / "docs" / "pz规范与使用"
112
+ if not docs_candidate.is_dir():
113
+ parent_candidate = runtime_root.parent / "docs" / "pz规范与使用"
114
+ if parent_candidate.is_dir():
115
+ docs_candidate = parent_candidate
116
+ if docs_candidate.is_dir():
117
+ return docs_candidate.resolve()
118
+ for candidate in sorted(resolve_sources_root(project_dir).glob("*/pz_core/pz_core/docs/pz规范与使用")):
119
+ if candidate.is_dir():
120
+ return candidate.resolve()
121
+ return _default_project_local_pz_docs_root(project_dir)
122
+
123
+
124
+ def _relative_doc_path(project_dir: Path, target: Path) -> str:
125
+ return Path(os.path.relpath(target, start=project_dir.resolve())).as_posix()
126
+
127
+
128
+ def _resolve_project_local_pz_docs_display_root(project_dir: Path) -> Path:
129
+ for candidate in sorted(resolve_sources_root(project_dir).glob("*/pz_core/pz_core/docs/pz规范与使用")):
130
+ if candidate.is_dir():
131
+ return candidate
132
+ runtime_package_root = resolve_project_runtime_package_root(project_dir)
133
+ if runtime_package_root is not None:
134
+ docs_candidate = runtime_package_root / "docs" / "pz规范与使用"
135
+ if docs_candidate.is_dir():
136
+ return docs_candidate
137
+ parent_candidate = runtime_package_root.parent / "docs" / "pz规范与使用"
138
+ if parent_candidate.is_dir():
139
+ return parent_candidate
140
+ return resolve_project_local_pz_docs_root(project_dir)
141
+
142
+
143
+ def render_project_agents_content(project_dir: Path) -> str:
144
+ docs_guides_dir = _resolve_project_local_pz_docs_display_root(project_dir)
145
+ docs_dir = docs_guides_dir.parent
146
+ return project_agents_template(
147
+ pz_docs_readme=_relative_doc_path(project_dir, docs_dir / "README.md"),
148
+ pz_design_docs_dir=_relative_doc_path(project_dir, docs_guides_dir),
149
+ pz_pz_commands_doc=_relative_doc_path(project_dir, docs_dir / "pz命令总览.md"),
150
+ )
151
+
152
+
153
+ def render_project_agents_block(project_dir: Path) -> str:
154
+ return f"{_AGENTS_START_MARKER}\n{render_project_agents_content(project_dir).strip()}\n{_AGENTS_END_MARKER}\n"
155
+
156
+
157
+ def render_project_agents_file_text(project_dir: Path) -> str:
158
+ path = project_dir / "AGENTS.md"
159
+ existing = path.read_text(encoding="utf-8") if path.exists() else None
160
+ return render_upserted_managed_block(
161
+ existing,
162
+ start_marker=_AGENTS_START_MARKER,
163
+ end_marker=_AGENTS_END_MARKER,
164
+ content=render_project_agents_content(project_dir),
165
+ )
166
+
167
+
168
+ def write_project_agents_file(project_dir: Path) -> None:
169
+ path = project_dir / "AGENTS.md"
170
+ path.parent.mkdir(parents=True, exist_ok=True)
171
+ path.write_text(render_project_agents_file_text(project_dir), encoding="utf-8")
172
+
173
+
174
+ def refresh_project_agents_file(project_dir: Path) -> ProjectAgentsRefreshResult:
175
+ path = project_dir / "AGENTS.md"
176
+ existed_before = path.exists()
177
+ write_project_agents_file(project_dir)
178
+ return ProjectAgentsRefreshResult(path=path, created=not existed_before)
179
+
180
+
181
+ def reset_project_agents_file(project_dir: Path) -> ProjectAgentsRefreshResult:
182
+ path = project_dir / "AGENTS.md"
183
+ existed_before = path.exists()
184
+ path.parent.mkdir(parents=True, exist_ok=True)
185
+ path.write_text(render_project_agents_block(project_dir), encoding="utf-8")
186
+ return ProjectAgentsRefreshResult(path=path, created=not existed_before)
187
+
188
+
189
+ def project_init_files(project_dir: Path) -> list[ScaffoldFile]:
190
+ project_name = derive_default_local_app_slug(project_dir)
191
+ return [
192
+ ScaffoldFile(
193
+ project_dir / "pyproject.toml",
194
+ project_pyproject_template(project_name),
195
+ ),
196
+ ScaffoldFile(project_dir / "main.py", main_py_template()),
197
+ ScaffoldFile(project_dir / "README.md", project_readme_template()),
198
+ ScaffoldFile(project_dir / "pz.jsonc", pz_jsonc_template()),
199
+ ScaffoldFile(project_dir / "pz.develop.jsonc", pz_env_jsonc_template("develop")),
200
+ ScaffoldFile(project_dir / "pz.test.jsonc", pz_env_jsonc_template("test")),
201
+ ScaffoldFile(project_dir / "pz.product.jsonc", pz_env_jsonc_template("product")),
202
+ ScaffoldFile(project_dir / ".env", project_env_template()),
203
+ ScaffoldFile(project_dir / ".env.develop", pz_env_env_template("develop")),
204
+ ScaffoldFile(project_dir / ".env.test", pz_env_env_template("test")),
205
+ ScaffoldFile(project_dir / ".env.product", pz_env_env_template("product")),
206
+ ScaffoldFile(project_dir / "apps" / "__init__.py", app_package_init_template()),
207
+ ScaffoldFile(project_dir / "apps" / ".gitkeep", root_gitkeep_template()),
208
+ ]
209
+
210
+
211
+ def app_files(project_dir: Path, app_id: str, *, target_dir: Path | None = None) -> list[ScaffoldFile]:
212
+ package_root = (target_dir or (project_dir / "apps")) / app_id
213
+ module_root = package_root / app_id
214
+ return [
215
+ ScaffoldFile(package_root / "pyproject.toml", app_pyproject_template(app_id)),
216
+ ScaffoldFile(package_root / "README.md", app_readme_template(app_id)),
217
+ ScaffoldFile(module_root / "__init__.py", app_package_init_template()),
218
+ ScaffoldFile(module_root / "apps.py", app_apps_template(app_id)),
219
+ ScaffoldFile(module_root / "public.py", app_public_template()),
220
+ ScaffoldFile(module_root / "auth" / "__init__.py", app_auth_package_init_template()),
221
+ ScaffoldFile(module_root / "auth" / "README.md", app_auth_readme_template(app_id)),
222
+ ScaffoldFile(module_root / "configs" / "__init__.py", app_configs_package_init_template()),
223
+ ScaffoldFile(module_root / "configs" / "default.jsonc", app_default_jsonc_template()),
224
+ ScaffoldFile(module_root / "configs" / "default.env", app_default_env_template(app_id)),
225
+ ScaffoldFile(module_root / "settings.py", app_settings_template()),
226
+ ScaffoldFile(module_root / "models.py", app_models_template()),
227
+ ScaffoldFile(module_root / "views.py", app_views_template(app_id)),
228
+ ScaffoldFile(module_root / "api.py", app_api_template(app_id)),
229
+ ScaffoldFile(module_root / "urls.py", app_urls_template(app_id)),
230
+ ScaffoldFile(module_root / "pages" / "__init__.py", app_package_init_template()),
231
+ ScaffoldFile(module_root / "pages" / "README.md", app_pages_readme_template(app_id)),
232
+ ScaffoldFile(module_root / "pages" / "base.jsonc", app_pages_base_template(app_id)),
233
+ ScaffoldFile(module_root / "pages" / app_id / "base.jsonc", app_pages_namespace_base_template(app_id)),
234
+ ScaffoldFile(module_root / "pages" / app_id / "key.jsonc", app_pages_key_template(app_id)),
235
+ ScaffoldFile(module_root / "template_config" / "__init__.py", app_package_init_template()),
236
+ ScaffoldFile(module_root / "template_config" / app_id / "__init__.py", app_package_init_template()),
237
+ ScaffoldFile(module_root / "template_config" / app_id / f"{app_id}_index.json", app_template_config_page_template(app_id)),
238
+ ScaffoldFile(module_root / "templates" / "__init__.py", app_package_init_template()),
239
+ ScaffoldFile(module_root / "templates" / app_id / "__init__.py", app_package_init_template()),
240
+ ScaffoldFile(module_root / "templates" / app_id / "index.html", app_template_index_html_template(app_id)),
241
+ ScaffoldFile(module_root / "templates" / "README.md", app_templates_readme_template(app_id)),
242
+ ScaffoldFile(module_root / "static" / "__init__.py", app_package_init_template()),
243
+ ScaffoldFile(module_root / "static" / app_id / "__init__.py", app_package_init_template()),
244
+ ScaffoldFile(module_root / "static" / app_id / "app.js", app_static_app_js_template(app_id)),
245
+ ScaffoldFile(module_root / "static" / "README.md", app_static_readme_template(app_id)),
246
+ ScaffoldFile(module_root / "locales" / "__init__.py", app_package_init_template()),
247
+ ScaffoldFile(module_root / "locales" / "README.md", app_locales_readme_template(app_id)),
248
+ ScaffoldFile(module_root / "migrations" / "__init__.py", app_package_init_template()),
249
+ ScaffoldFile(module_root / "migrations" / "README.md", app_migrations_readme_template(app_id)),
250
+ ScaffoldFile(module_root / "migrations" / app_id / "__init__.py", app_package_init_template()),
251
+ ]
252
+
253
+
254
+ def write_missing_files(entries: list[ScaffoldFile]) -> list[Path]:
255
+ created: list[Path] = []
256
+ seen: set[Path] = set()
257
+ for entry in entries:
258
+ if entry.path in seen:
259
+ raise PzError(
260
+ code="PZ_SCAFFOLD_TARGET_DUPLICATE",
261
+ message=f"duplicate scaffold target declared: {entry.path}",
262
+ stage="cli",
263
+ owner="pz_core.authoring",
264
+ action="write_missing_files",
265
+ )
266
+ seen.add(entry.path)
267
+ if entry.path.exists():
268
+ continue
269
+ entry.path.parent.mkdir(parents=True, exist_ok=True)
270
+ entry.path.write_text(entry.content, encoding="utf-8")
271
+ created.append(entry.path)
272
+ return created
273
+
274
+
275
+ def ensure_apps_container_readme(project_dir: Path) -> None:
276
+ append_or_create_file(project_dir / "apps" / "README.md", apps_container_readme_template())
277
+
278
+
279
+ def write_project_scaffold(project_dir: Path, *, action: str) -> None:
280
+ write_files(project_init_files(project_dir), action=action)
281
+ write_project_agents_file(project_dir)
282
+ ensure_apps_container_readme(project_dir)
283
+
284
+
285
+ def ensure_project_scaffold(project_dir: Path) -> ProjectScaffoldResult:
286
+ created = bool(write_missing_files(project_init_files(project_dir)))
287
+ write_project_agents_file(project_dir)
288
+ ensure_apps_container_readme(project_dir)
289
+ return ProjectScaffoldResult(created=created)
290
+
291
+
292
+ def discover_local_app_ids(project_dir: Path) -> set[str]:
293
+ apps_root = project_dir / "apps"
294
+ if not apps_root.is_dir():
295
+ return set()
296
+ app_ids: set[str] = set()
297
+ for child in apps_root.iterdir():
298
+ if not child.is_dir() or child.name.startswith("."):
299
+ continue
300
+ if (child / "pyproject.toml").is_file():
301
+ app_ids.add(child.name)
302
+ return app_ids
303
+
304
+
305
+ def ensure_default_local_app(
306
+ project_dir: Path,
307
+ *,
308
+ app_name: str | None,
309
+ scaffold_created: bool,
310
+ action: str,
311
+ ) -> LocalAppScaffoldResult:
312
+ slug = normalize_app_slug(app_name or derive_default_local_app_slug(project_dir), action=action)
313
+ default_app_id = build_app_id(slug)
314
+ existing_local_apps = discover_local_app_ids(project_dir)
315
+ should_ensure_default_app = (
316
+ scaffold_created
317
+ or default_app_id in existing_local_apps
318
+ or not existing_local_apps
319
+ or isinstance(app_name, str)
320
+ )
321
+ if not should_ensure_default_app:
322
+ return LocalAppScaffoldResult(app_id=None, created=False)
323
+ created = bool(write_missing_files(app_files(project_dir, default_app_id)))
324
+ ensure_apps_container_readme(project_dir)
325
+ return LocalAppScaffoldResult(app_id=default_app_id, created=created)