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,1810 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ from dataclasses import replace
5
+ import shutil
6
+ import subprocess
7
+ import sys
8
+ from pathlib import Path
9
+
10
+ from pz_core.runtime.cli._args import (
11
+ PzArgumentParser,
12
+ json_output_requested,
13
+ normalize_cli_argv,
14
+ )
15
+ from pz_core.runtime.errors import PzError, as_pz_error
16
+ from pz_core.cli_options import add_cli_option, resolve_program_name
17
+
18
+ from .dev_sources import (
19
+ AddDevSourceRequest,
20
+ BranchDevSourceRequest,
21
+ DevTransactionResult,
22
+ add_dev_source,
23
+ branch_dev_source,
24
+ )
25
+ from .dev_manifest import load_dev_manifest
26
+ from .dev_import import (
27
+ cache_git_source,
28
+ find_existing_local_import,
29
+ import_git_app,
30
+ import_local_dev_source,
31
+ load_cached_git_source,
32
+ )
33
+ from .ide_projection import IdeSyncResult, sync_ide_projections
34
+ from .official_dev_sources import resolve_source_locator
35
+ from .app_versions import (
36
+ declare_app_version,
37
+ install_declared_app,
38
+ query_app_versions,
39
+ reconcile_project_environment,
40
+ resolve_app_version,
41
+ undeclare_app_version,
42
+ )
43
+ from .files import append_or_create_file, write_files
44
+ from .naming import build_app_id, normalize_app_slug, resolve_project_dir
45
+ from .orchestration_service import (
46
+ execute_app_init,
47
+ execute_dev_init,
48
+ execute_publish_batch,
49
+ )
50
+ from .project_settings_service import update_settings_file
51
+ from .project_metadata_service import update_project_uv_sources
52
+ from .conflicts import (
53
+ build_app_binding_conflict_error,
54
+ ensure_no_app_binding_conflicts,
55
+ )
56
+ from .project_state import (
57
+ add_declared_app,
58
+ ensure_project_state,
59
+ load_installed_apps,
60
+ normalize_formal_app_id,
61
+ remove_declared_app,
62
+ )
63
+ from .release import (
64
+ install_env,
65
+ load_install_target,
66
+ publish_app,
67
+ publish_commit,
68
+ publish_tag,
69
+ publish_upload,
70
+ unpublish_app,
71
+ )
72
+ from .scaffold_service import (
73
+ app_files,
74
+ resolve_project_local_pz_docs_root,
75
+ )
76
+ from .sync_service import sync_project_targets
77
+ from .templates.project.apps_container_readme import apps_container_readme_template
78
+
79
+
80
+ def json_error(error: PzError) -> str:
81
+ payload = error.to_dict()
82
+ payload["status"] = "error"
83
+ return json.dumps(payload, ensure_ascii=True, sort_keys=True)
84
+
85
+
86
+ def _success_payload(**payload: object) -> str:
87
+ return json.dumps({"status": "ok", **payload}, ensure_ascii=True, sort_keys=True)
88
+
89
+
90
+ def _resolve_project_local_pz_docs_root(project_dir: Path) -> Path:
91
+ return resolve_project_local_pz_docs_root(project_dir)
92
+
93
+
94
+ def _handle_command_error(*, exc: Exception, json_mode: bool, action: str) -> int:
95
+ error = as_pz_error(exc, owner="pz_core.authoring", action=action, stage="cli")
96
+ if json_mode:
97
+ print(json_error(error))
98
+ else:
99
+ print(str(error), file=sys.stderr)
100
+ return 1
101
+
102
+
103
+ def _incomplete_orchestration_error(
104
+ exc: Exception,
105
+ *,
106
+ code: str,
107
+ action: str,
108
+ completed_stages: list[str],
109
+ next_stage: str,
110
+ selected_version: str | None = None,
111
+ ) -> PzError:
112
+ original = as_pz_error(
113
+ exc, owner="pz_core.authoring", action=action, stage="cli"
114
+ )
115
+ context: dict[str, object] = {
116
+ "completed_stages": list(completed_stages),
117
+ "next_stage": next_stage,
118
+ "original_code": original.code,
119
+ }
120
+ if selected_version is not None:
121
+ context["selected_version"] = selected_version
122
+ return PzError(
123
+ code=code,
124
+ message="orchestration stopped after one or more atomic stages completed.",
125
+ stage="cli",
126
+ owner="pz_core.authoring",
127
+ action=action,
128
+ detail=str(original),
129
+ context=context,
130
+ )
131
+
132
+
133
+ def _print_success(*, json_mode: bool, text: str, **payload: object) -> None:
134
+ if json_mode:
135
+ print(_success_payload(message=text, **payload))
136
+ else:
137
+ print(text)
138
+
139
+
140
+ def _prog(*parts: str) -> str:
141
+ return " ".join((resolve_program_name(), *parts))
142
+
143
+
144
+ def _has_dev_source(project_dir: Path, app_id: str) -> bool:
145
+ manifest = load_dev_manifest(project_dir)
146
+ return manifest is not None and app_id in manifest.bindings
147
+
148
+
149
+ def _print_dev_transaction(
150
+ result: DevTransactionResult,
151
+ *,
152
+ json_mode: bool,
153
+ ide_result: IdeSyncResult | None = None,
154
+ ) -> None:
155
+ repos = ", ".join(result.repo_keys) or "direct-link"
156
+ bindings = ", ".join(result.bindings) or "none"
157
+ ide_statuses = (
158
+ {}
159
+ if ide_result is None
160
+ else {item.provider: item.status for item in ide_result.providers}
161
+ )
162
+ ide_note = (
163
+ ""
164
+ if not ide_statuses
165
+ else " IDE projection: "
166
+ + ", ".join(f"{provider}={status}" for provider, status in ide_statuses.items())
167
+ + "."
168
+ )
169
+ text = (
170
+ f"Completed pz dev {result.operation}: repos={repos}; "
171
+ f"business_branch={result.business_branch}; "
172
+ f"base_branch={result.base_branch}; bindings={bindings}.{ide_note}"
173
+ )
174
+ _print_success(
175
+ json_mode=json_mode,
176
+ text=text,
177
+ operation=result.operation,
178
+ project_dir=str(result.project_dir),
179
+ business_branch=result.business_branch,
180
+ base_branch=result.base_branch,
181
+ repo_keys=list(result.repo_keys),
182
+ checkouts=[str(path) for path in result.checkouts],
183
+ bindings=list(result.bindings),
184
+ links=[str(path) for path in result.links],
185
+ updated=result.updated,
186
+ upstream=result.upstream,
187
+ checkout_deleted=result.checkout_deleted,
188
+ ide_providers=ide_statuses,
189
+ )
190
+
191
+
192
+ def dev_ide_sync_main(argv: list[object] | None = None) -> int:
193
+ json_mode = json_output_requested(argv)
194
+ try:
195
+ normalized = normalize_cli_argv(
196
+ argv,
197
+ code="PZ_CLI_ARGV_INVALID",
198
+ stage="cli",
199
+ owner="pz_core.authoring",
200
+ action="dev_ide_sync_main",
201
+ )
202
+ parser = PzArgumentParser(
203
+ prog=_prog("dev", "ide", "sync"),
204
+ description="Synchronize PZ-owned IDE projections for visible minimal apps.",
205
+ error_code="PZ_SCAFFOLD_ARGUMENTS_INVALID",
206
+ error_stage="cli",
207
+ error_owner="pz_core.authoring",
208
+ error_action="dev_ide_sync_main",
209
+ )
210
+ add_cli_option(parser, "--json", action="store_true")
211
+ add_cli_option(parser, "--project-dir", dest="project_dir")
212
+ ns = parser.parse_args(normalized)
213
+ project_dir = resolve_project_dir(ns.project_dir)
214
+ result = sync_ide_projections(project_dir)
215
+ provider_statuses = {
216
+ item.provider: item.status for item in result.providers
217
+ }
218
+ _print_success(
219
+ json_mode=json_mode,
220
+ text=(
221
+ f"Synchronized IDE projection for {len(result.app_ids)} app(s): "
222
+ + ", ".join(
223
+ f"{provider}={status}"
224
+ for provider, status in provider_statuses.items()
225
+ )
226
+ + "."
227
+ ),
228
+ project_dir=str(result.project_dir),
229
+ app_ids=list(result.app_ids),
230
+ providers=provider_statuses,
231
+ )
232
+ return 0
233
+ except Exception as exc:
234
+ return _handle_command_error(
235
+ exc=exc,
236
+ json_mode=json_mode,
237
+ action="dev_ide_sync_main",
238
+ )
239
+
240
+
241
+ def dev_source_main(argv: list[object] | None = None) -> int:
242
+ args = list(argv or [])
243
+ json_mode = json_output_requested(args)
244
+ action = "dev_source_main"
245
+ try:
246
+ if not args:
247
+ raise PzError(
248
+ code="PZ_SCAFFOLD_ARGUMENTS_INVALID",
249
+ message="pz dev source requires cache/import/link/branch/update/unlink.",
250
+ stage="cli",
251
+ owner="pz_core.authoring",
252
+ action=action,
253
+ )
254
+ command = str(args.pop(0)).strip().lower()
255
+ normalized = normalize_cli_argv(
256
+ args,
257
+ code="PZ_CLI_ARGV_INVALID",
258
+ stage="cli",
259
+ owner="pz_core.authoring",
260
+ action=action,
261
+ )
262
+ parser = PzArgumentParser(
263
+ prog=_prog("dev", "source", command),
264
+ description=f"Execute the atomic dev source {command} stage.",
265
+ error_code="PZ_SCAFFOLD_ARGUMENTS_INVALID",
266
+ error_stage="cli",
267
+ error_owner="pz_core.authoring",
268
+ error_action=action,
269
+ )
270
+ add_cli_option(parser, "--json", action="store_true")
271
+ parser.add_argument("source")
272
+ add_cli_option(parser, "--project-dir", dest="project_dir")
273
+ if command in {"cache", "import"}:
274
+ parser.add_argument("--branch", default="develop")
275
+ if command == "cache":
276
+ parser.add_argument("--revision")
277
+ if command == "import":
278
+ parser.add_argument("--repo-key")
279
+ parser.add_argument("--revision")
280
+ if command in {"link", "branch"}:
281
+ add_cli_option(
282
+ parser,
283
+ "--base-branch",
284
+ choices=("develop", "main"),
285
+ default="develop",
286
+ dest="base_branch",
287
+ )
288
+ if command == "unlink":
289
+ add_cli_option(parser, "--delete-checkout", action="store_true", dest="delete_checkout")
290
+ ns = parser.parse_args(normalized)
291
+ project_dir = resolve_project_dir(ns.project_dir)
292
+ if command == "cache":
293
+ resolved = resolve_source_locator(ns.source, project_dir=project_dir, action=action)
294
+ if resolved.source_kind != "git" or resolved.repo_key is None:
295
+ raise PzError(
296
+ code="PZ_DEV_SOURCE_NOT_GIT",
297
+ message="dev source cache 只接受 Git 来源。",
298
+ stage="dev",
299
+ owner="pz_core.authoring",
300
+ action=action,
301
+ )
302
+ result = cache_git_source(
303
+ repo_key=resolved.repo_key,
304
+ source=resolved.clone_source,
305
+ branch=ns.branch,
306
+ revision=ns.revision,
307
+ action=action,
308
+ )
309
+ _print_success(
310
+ json_mode=json_mode,
311
+ text=f"Cached {result.repo_key} at {result.commit}.",
312
+ repo_key=result.repo_key,
313
+ checkout=str(result.checkout),
314
+ commit=result.commit,
315
+ branch=result.branch,
316
+ resolution_ref=result.resolution_ref,
317
+ )
318
+ return 0
319
+ if command == "import":
320
+ if (ns.repo_key is None) != (ns.revision is None):
321
+ raise PzError(
322
+ code="PZ_SCAFFOLD_ARGUMENTS_INVALID",
323
+ message="Git 导入必须同时提供 --repo-key 和 --revision。",
324
+ stage="cli",
325
+ owner="pz_core.authoring",
326
+ action=action,
327
+ )
328
+ if ns.repo_key is None:
329
+ result = import_local_dev_source(
330
+ ns.source, project_dir=project_dir, action=action
331
+ )
332
+ else:
333
+ resolved = resolve_source_locator(
334
+ ns.source, project_dir=project_dir, action=action
335
+ )
336
+ if resolved.source_kind != "git" or resolved.repo_key != ns.repo_key:
337
+ raise PzError(
338
+ code="PZ_DEV_CACHE_RESOLUTION_MISSING",
339
+ message="source locator 与 cache repo key 不匹配。",
340
+ stage="dev",
341
+ owner="pz_core.authoring",
342
+ action=action,
343
+ )
344
+ cache = load_cached_git_source(
345
+ repo_key=ns.repo_key,
346
+ source=resolved.clone_source,
347
+ revision=ns.revision,
348
+ branch=ns.branch,
349
+ action=action,
350
+ )
351
+ result = import_git_app(
352
+ ns.source, cache=cache, project_dir=project_dir, action=action
353
+ )
354
+ _print_success(
355
+ json_mode=json_mode,
356
+ text=f"Imported {result.app_id} into {result.destination}.",
357
+ app_id=result.app_id,
358
+ destination=str(result.destination),
359
+ revision=result.commit,
360
+ created=result.created,
361
+ )
362
+ return 0
363
+ if command == "link":
364
+ result = add_dev_source(
365
+ AddDevSourceRequest(project_dir, (ns.source,), base_branch=ns.base_branch)
366
+ )
367
+ result = replace(result, operation="link")
368
+ elif command == "branch":
369
+ result = branch_dev_source(
370
+ BranchDevSourceRequest(project_dir, ns.source, base_branch=ns.base_branch)
371
+ )
372
+ elif command == "update":
373
+ from .dev_sources import UpdateDevSourceRequest, update_dev_source
374
+
375
+ result = update_dev_source(UpdateDevSourceRequest(project_dir, ns.source))
376
+ elif command == "unlink":
377
+ from .dev_sources import RemoveDevSourceRequest, remove_dev_source
378
+
379
+ result = remove_dev_source(
380
+ RemoveDevSourceRequest(project_dir, ns.source, delete_checkout=bool(ns.delete_checkout))
381
+ )
382
+ result = replace(result, operation="unlink")
383
+ else:
384
+ raise PzError(
385
+ code="PZ_SCAFFOLD_ARGUMENTS_INVALID",
386
+ message=f"unknown dev source command: {command}",
387
+ stage="cli",
388
+ owner="pz_core.authoring",
389
+ action=action,
390
+ )
391
+ _print_dev_transaction(result, json_mode=json_mode)
392
+ return 0
393
+ except Exception as exc:
394
+ return _handle_command_error(exc=exc, json_mode=json_mode, action=action)
395
+
396
+
397
+ def _publish_result_message(result) -> str:
398
+ verb = "Built and dry-ran publish for" if result.dry_run else "Built and published"
399
+ return (
400
+ f"{verb} {result.app_id} {result.version} "
401
+ f"using target {result.target.name} from {result.dist_dir}."
402
+ )
403
+
404
+
405
+ def _publish_atom_parser(command: str, argv: list[object] | None) -> tuple[object, Path, bool]:
406
+ action = f"publish_{command}_main"
407
+ normalized = normalize_cli_argv(
408
+ argv,
409
+ code="PZ_CLI_ARGV_INVALID",
410
+ stage="cli",
411
+ owner="pz_core.authoring",
412
+ action=action,
413
+ )
414
+ parser = PzArgumentParser(
415
+ prog=_prog("pub", command),
416
+ description=f"Execute the atomic pz pub {command} stage.",
417
+ error_code="PZ_SCAFFOLD_ARGUMENTS_INVALID",
418
+ error_stage="cli",
419
+ error_owner="pz_core.authoring",
420
+ error_action=action,
421
+ )
422
+ add_cli_option(parser, "--json", action="store_true")
423
+ parser.add_argument("app_id")
424
+ if command == "upload":
425
+ add_cli_option(parser, "--target")
426
+ add_cli_option(parser, "--dry-run", action="store_true")
427
+ parser.add_argument("--version")
428
+ add_cli_option(parser, "--project-dir", dest="project_dir")
429
+ ns = parser.parse_args(normalized)
430
+ return ns, resolve_project_dir(ns.project_dir), bool(getattr(ns, "json", False))
431
+
432
+
433
+ def publish_commit_main(argv: list[object] | None = None) -> int:
434
+ json_mode = json_output_requested(argv)
435
+ try:
436
+ ns, project_dir, json_mode = _publish_atom_parser("commit", argv)
437
+ result = publish_commit(ns.app_id, project_dir=project_dir, action="publish_commit_main")
438
+ _print_success(
439
+ json_mode=json_mode,
440
+ text=f"Committed {result.source.app_id} {result.version} at {result.commit}.",
441
+ app_id=result.source.app_id,
442
+ version=result.version,
443
+ commit=result.commit,
444
+ reused=result.reused,
445
+ package_root=str(result.source.package_root),
446
+ git_root=str(result.source.git_root),
447
+ )
448
+ return 0
449
+ except Exception as exc:
450
+ return _handle_command_error(exc=exc, json_mode=json_mode, action="publish_commit_main")
451
+
452
+
453
+ def publish_tag_main(argv: list[object] | None = None) -> int:
454
+ json_mode = json_output_requested(argv)
455
+ try:
456
+ ns, project_dir, json_mode = _publish_atom_parser("tag", argv)
457
+ result = publish_tag(ns.app_id, project_dir=project_dir, action="publish_tag_main")
458
+ _print_success(
459
+ json_mode=json_mode,
460
+ text=f"Tagged {result.commit_result.source.app_id} as {result.tag}.",
461
+ app_id=result.commit_result.source.app_id,
462
+ version=result.commit_result.version,
463
+ commit=result.commit_result.commit,
464
+ tag=result.tag,
465
+ reused=result.reused,
466
+ )
467
+ return 0
468
+ except Exception as exc:
469
+ return _handle_command_error(exc=exc, json_mode=json_mode, action="publish_tag_main")
470
+
471
+
472
+ def publish_upload_main(argv: list[object] | None = None) -> int:
473
+ json_mode = json_output_requested(argv)
474
+ try:
475
+ ns, project_dir, json_mode = _publish_atom_parser("upload", argv)
476
+ result = publish_upload(
477
+ ns.app_id,
478
+ project_dir=project_dir,
479
+ target_name=ns.target,
480
+ dry_run=bool(ns.dry_run),
481
+ version=ns.version,
482
+ action="publish_upload_main",
483
+ )
484
+ _print_success(
485
+ json_mode=json_mode,
486
+ text=_publish_result_message(result),
487
+ app_id=result.app_id,
488
+ version=result.version,
489
+ commit=result.commit,
490
+ tag=result.tag,
491
+ target=result.target.name,
492
+ dry_run=result.dry_run,
493
+ )
494
+ return 0
495
+ except Exception as exc:
496
+ return _handle_command_error(exc=exc, json_mode=json_mode, action="publish_upload_main")
497
+
498
+
499
+ def _unpublish_result_message(result) -> str:
500
+ versions = ", ".join(result.removed_versions)
501
+ verb = "Planned unpublish for" if result.dry_run else "Unpublished"
502
+ force_suffix = " with force" if result.force else ""
503
+ return (
504
+ f"{verb} {result.app_id} ({result.distribution_name}) versions [{versions}] "
505
+ f"from target {result.target.name}{force_suffix}."
506
+ )
507
+
508
+
509
+ def _require_uv(*, action: str) -> str:
510
+ uv_binary = shutil.which("uv")
511
+ if uv_binary:
512
+ return uv_binary
513
+ raise PzError(
514
+ code="PZ_SCAFFOLD_UV_MISSING",
515
+ message="required dependency backend was not found on PATH.",
516
+ stage="cli",
517
+ owner="pz_core.authoring",
518
+ action=action,
519
+ )
520
+
521
+
522
+ def _install_distribution(
523
+ app_id: str, *, project_dir: Path, upgrade: bool, action: str
524
+ ) -> str:
525
+ uv_binary = _require_uv(action=action)
526
+ distribution = app_id.replace("_", "-")
527
+ argv = [uv_binary, "pip", "install", "--python", sys.executable]
528
+ if upgrade:
529
+ argv.append("--upgrade")
530
+ argv.append(distribution)
531
+ env = install_env(
532
+ target=load_install_target(app_id, project_dir=project_dir, action=action),
533
+ project_dir=project_dir,
534
+ action=action,
535
+ )
536
+ completed = subprocess.run(
537
+ argv, check=False, capture_output=True, text=True, env=env
538
+ )
539
+ if completed.returncode != 0:
540
+ detail = (
541
+ completed.stderr or completed.stdout or "dependency install failed"
542
+ ).strip()
543
+ raise PzError(
544
+ code="PZ_SCAFFOLD_INSTALL_FAILED",
545
+ message=f"failed to install {app_id}.",
546
+ stage="cli",
547
+ owner="pz_core.authoring",
548
+ action=action,
549
+ detail=detail,
550
+ )
551
+ return distribution
552
+
553
+
554
+ def startapp_main(argv: list[object] | None = None) -> int:
555
+ json_mode = json_output_requested(argv)
556
+ try:
557
+ normalized = normalize_cli_argv(
558
+ argv,
559
+ code="PZ_CLI_ARGV_INVALID",
560
+ stage="cli",
561
+ owner="pz_core.authoring",
562
+ action="startapp_main",
563
+ )
564
+ parser = PzArgumentParser(
565
+ prog=_prog("startapp"),
566
+ description="Create a formal minimal package under apps/<app_id>/.",
567
+ error_code="PZ_SCAFFOLD_ARGUMENTS_INVALID",
568
+ error_stage="cli",
569
+ error_owner="pz_core.authoring",
570
+ error_action="startapp_main",
571
+ )
572
+ add_cli_option(parser, "--json", action="store_true")
573
+ parser.add_argument("name")
574
+ add_cli_option(parser, "--project-dir", dest="project_dir")
575
+ add_cli_option(
576
+ parser,
577
+ "--target-dir",
578
+ dest="target_dir",
579
+ help="Directory that will contain <app_id>/; defaults to <project-dir>/apps.",
580
+ )
581
+ ns = parser.parse_args(normalized)
582
+ slug = normalize_app_slug(ns.name, action="startapp_main")
583
+ app_id = build_app_id(slug)
584
+ project_dir = resolve_project_dir(ns.project_dir)
585
+ default_target_dir = (project_dir / "apps").resolve()
586
+ if ns.target_dir:
587
+ raw_target_dir = Path(ns.target_dir).expanduser()
588
+ target_dir = (
589
+ raw_target_dir
590
+ if raw_target_dir.is_absolute()
591
+ else project_dir / raw_target_dir
592
+ )
593
+ target_dir = target_dir.resolve()
594
+ else:
595
+ target_dir = default_target_dir
596
+ if target_dir.exists() and not target_dir.is_dir():
597
+ raise PzError(
598
+ code="PZ_SCAFFOLD_APPS_DIR_MISSING",
599
+ message=f"app container path is not a directory: {target_dir}",
600
+ stage="cli",
601
+ owner="pz_core.authoring",
602
+ action="startapp_main",
603
+ )
604
+ target_dir.mkdir(parents=True, exist_ok=True)
605
+ write_files(
606
+ app_files(project_dir, app_id, target_dir=target_dir),
607
+ action="startapp_main",
608
+ )
609
+ settings_path: Path | None = None
610
+ if target_dir == default_target_dir:
611
+ append_or_create_file(
612
+ target_dir / "README.md", apps_container_readme_template()
613
+ )
614
+ add_declared_app(project_dir, app_id, action="startapp_main")
615
+ settings_path = update_settings_file(project_dir).path
616
+ if json_mode:
617
+ print(
618
+ _success_payload(
619
+ message=f"Created app scaffold for {app_id}.",
620
+ app_id=app_id,
621
+ target_dir=str(target_dir),
622
+ settings_path=None if settings_path is None else str(settings_path),
623
+ settings_updated=settings_path is not None,
624
+ )
625
+ )
626
+ return 0
627
+ except Exception as exc:
628
+ return _handle_command_error(
629
+ exc=exc, json_mode=json_mode, action="startapp_main"
630
+ )
631
+
632
+
633
+ def app_add_main(argv: list[object] | None = None) -> int:
634
+ json_mode = json_output_requested(argv)
635
+ completed_stages: list[str] = []
636
+ version: str | None = None
637
+ try:
638
+ normalized = normalize_cli_argv(
639
+ argv,
640
+ code="PZ_CLI_ARGV_INVALID",
641
+ stage="cli",
642
+ owner="pz_core.authoring",
643
+ action="app_add_main",
644
+ )
645
+ parser = PzArgumentParser(
646
+ prog=_prog("app", "add"),
647
+ description="Declare a formal minimal app and install its released distribution into the current runtime.",
648
+ error_code="PZ_SCAFFOLD_ARGUMENTS_INVALID",
649
+ error_stage="cli",
650
+ error_owner="pz_core.authoring",
651
+ error_action="app_add_main",
652
+ )
653
+ add_cli_option(parser, "--json", action="store_true")
654
+ parser.add_argument("app_id")
655
+ parser.add_argument("--version")
656
+ add_cli_option(parser, "--project-dir", dest="project_dir")
657
+ ns = parser.parse_args(normalized)
658
+ project_dir = resolve_project_dir(ns.project_dir)
659
+ ensure_project_state(project_dir, action="app_add_main")
660
+ app_id = normalize_formal_app_id(ns.app_id, action="app_add_main")
661
+ state = query_app_versions(app_id, project_dir=project_dir, action="app_add_main")
662
+ recovering = (
663
+ state.declared is not None
664
+ and (state.locked != state.declared or state.installed != state.declared)
665
+ and (ns.version is None or ns.version == state.declared)
666
+ )
667
+ version = state.declared if recovering else resolve_app_version(
668
+ state, ns.version, action="app_add_main", project_dir=project_dir
669
+ )
670
+ assert version is not None
671
+ completed_stages.append("versions")
672
+ installed_apps = load_installed_apps(project_dir, action="app_add_main")
673
+ if state.declared == version and app_id in installed_apps:
674
+ declared_apps = tuple(installed_apps)
675
+ else:
676
+ declaration = declare_app_version(
677
+ app_id, version, project_dir=project_dir, action="app_add_main"
678
+ )
679
+ declared_apps = declaration.installed_apps
680
+ completed_stages.append("declare")
681
+ if state.locked == version and state.installed == version:
682
+ distribution = state.distribution
683
+ lock_path = project_dir / "uv.lock"
684
+ else:
685
+ installation = install_declared_app(
686
+ app_id, version, project_dir=project_dir, action="app_add_main"
687
+ )
688
+ distribution = installation.distribution
689
+ lock_path = installation.lock_path
690
+ completed_stages.append("install")
691
+ settings_path = update_settings_file(project_dir).path
692
+ completed_stages.append("settings")
693
+ _print_success(
694
+ json_mode=json_mode,
695
+ text=f"Declared and installed {distribution}=={version} into the project environment.",
696
+ app_id=app_id,
697
+ distribution=distribution,
698
+ version=version,
699
+ installed_apps=list(declared_apps),
700
+ lock_path=str(lock_path),
701
+ settings_path=str(settings_path),
702
+ completed_stages=completed_stages,
703
+ )
704
+ return 0
705
+ except Exception as exc:
706
+ if completed_stages:
707
+ stages = ("versions", "declare", "install", "settings")
708
+ next_stage = stages[min(len(completed_stages), len(stages) - 1)]
709
+ exc = _incomplete_orchestration_error(
710
+ exc,
711
+ code="PZ_APP_ORCHESTRATION_INCOMPLETE",
712
+ action="app_add_main",
713
+ completed_stages=completed_stages,
714
+ next_stage=next_stage,
715
+ selected_version=version,
716
+ )
717
+ return _handle_command_error(
718
+ exc=exc, json_mode=json_mode, action="app_add_main"
719
+ )
720
+
721
+
722
+ def app_rm_main(argv: list[object] | None = None) -> int:
723
+ json_mode = json_output_requested(argv)
724
+ completed_stages: list[str] = []
725
+ try:
726
+ normalized = normalize_cli_argv(
727
+ argv,
728
+ code="PZ_CLI_ARGV_INVALID",
729
+ stage="cli",
730
+ owner="pz_core.authoring",
731
+ action="app_rm_main",
732
+ )
733
+ parser = PzArgumentParser(
734
+ prog=_prog("app", "rm"),
735
+ description="Remove a formal minimal app from the current project declaration.",
736
+ error_code="PZ_SCAFFOLD_ARGUMENTS_INVALID",
737
+ error_stage="cli",
738
+ error_owner="pz_core.authoring",
739
+ error_action="app_rm_main",
740
+ )
741
+ add_cli_option(parser, "--json", action="store_true")
742
+ parser.add_argument("app_id")
743
+ add_cli_option(parser, "--project-dir", dest="project_dir")
744
+ ns = parser.parse_args(normalized)
745
+ project_dir = resolve_project_dir(ns.project_dir)
746
+ ensure_project_state(project_dir, action="app_rm_main")
747
+ app_id = normalize_formal_app_id(ns.app_id, action="app_rm_main")
748
+ installed_apps = load_installed_apps(project_dir, action="app_rm_main")
749
+ if app_id not in installed_apps:
750
+ raise PzError(
751
+ code="PZ_SCAFFOLD_APP_NOT_DECLARED",
752
+ message=f"{app_id} is not declared in project installed_apps.",
753
+ stage="cli",
754
+ owner="pz_core.authoring",
755
+ action="app_rm_main",
756
+ )
757
+ declaration = undeclare_app_version(
758
+ app_id, project_dir=project_dir, action="app_rm_main"
759
+ )
760
+ completed_stages.append("undeclare")
761
+ reconcile_project_environment(
762
+ app_id, project_dir=project_dir, action="app_rm_main"
763
+ )
764
+ completed_stages.append("env_sync")
765
+ settings_path = update_settings_file(project_dir).path
766
+ completed_stages.append("settings")
767
+ note = ""
768
+ if _has_dev_source(project_dir, app_id):
769
+ note = " Local dev source still exists; run `pz dev unlink <app_id>` to fully stop auto-discovery."
770
+ _print_success(
771
+ json_mode=json_mode,
772
+ text=f"Removed {app_id} from project declaration.{note}",
773
+ app_id=app_id,
774
+ installed_apps=list(declaration.installed_apps),
775
+ has_dev_source=_has_dev_source(project_dir, app_id),
776
+ settings_path=str(settings_path),
777
+ completed_stages=completed_stages,
778
+ )
779
+ return 0
780
+ except Exception as exc:
781
+ if completed_stages:
782
+ stages = ("undeclare", "env_sync", "settings")
783
+ next_stage = stages[min(len(completed_stages), len(stages) - 1)]
784
+ exc = _incomplete_orchestration_error(
785
+ exc,
786
+ code="PZ_APP_ORCHESTRATION_INCOMPLETE",
787
+ action="app_rm_main",
788
+ completed_stages=completed_stages,
789
+ next_stage=next_stage,
790
+ )
791
+ return _handle_command_error(exc=exc, json_mode=json_mode, action="app_rm_main")
792
+
793
+
794
+ def app_update_main(argv: list[object] | None = None) -> int:
795
+ json_mode = json_output_requested(argv)
796
+ completed_stages: list[str] = []
797
+ version: str | None = None
798
+ try:
799
+ normalized = normalize_cli_argv(
800
+ argv,
801
+ code="PZ_CLI_ARGV_INVALID",
802
+ stage="cli",
803
+ owner="pz_core.authoring",
804
+ action="app_update_main",
805
+ )
806
+ parser = PzArgumentParser(
807
+ prog=_prog("app", "update"),
808
+ description="Refresh the installed package for an already-declared formal app.",
809
+ error_code="PZ_SCAFFOLD_ARGUMENTS_INVALID",
810
+ error_stage="cli",
811
+ error_owner="pz_core.authoring",
812
+ error_action="app_update_main",
813
+ )
814
+ add_cli_option(parser, "--json", action="store_true")
815
+ parser.add_argument("app_id")
816
+ parser.add_argument("--version")
817
+ add_cli_option(parser, "--project-dir", dest="project_dir")
818
+ ns = parser.parse_args(normalized)
819
+ project_dir = resolve_project_dir(ns.project_dir)
820
+ ensure_project_state(project_dir, action="app_update_main")
821
+ app_id = normalize_formal_app_id(ns.app_id, action="app_update_main")
822
+ installed_apps = load_installed_apps(project_dir, action="app_update_main")
823
+ if app_id not in installed_apps:
824
+ raise PzError(
825
+ code="PZ_SCAFFOLD_APP_NOT_DECLARED",
826
+ message=f"{app_id} is not declared in project installed_apps.",
827
+ stage="cli",
828
+ owner="pz_core.authoring",
829
+ action="app_update_main",
830
+ )
831
+ state = query_app_versions(app_id, project_dir=project_dir, action="app_update_main")
832
+ recovering = (
833
+ state.declared is not None
834
+ and (state.locked != state.declared or state.installed != state.declared)
835
+ and (ns.version is None or ns.version == state.declared)
836
+ )
837
+ version = state.declared if recovering else resolve_app_version(
838
+ state, ns.version, action="app_update_main", project_dir=project_dir
839
+ )
840
+ assert version is not None
841
+ completed_stages.append("versions")
842
+ if state.declared == version:
843
+ declared_apps = tuple(installed_apps)
844
+ else:
845
+ declaration = declare_app_version(
846
+ app_id, version, project_dir=project_dir, action="app_update_main"
847
+ )
848
+ declared_apps = declaration.installed_apps
849
+ completed_stages.append("declare")
850
+ if state.locked == version and state.installed == version:
851
+ distribution = state.distribution
852
+ else:
853
+ installation = install_declared_app(
854
+ app_id, version, project_dir=project_dir, action="app_update_main"
855
+ )
856
+ distribution = installation.distribution
857
+ completed_stages.append("install")
858
+ settings_path = update_settings_file(project_dir).path
859
+ completed_stages.append("settings")
860
+ note = ""
861
+ if _has_dev_source(project_dir, app_id):
862
+ note = " Local dev source still has higher precedence than the installed package."
863
+ _print_success(
864
+ json_mode=json_mode,
865
+ text=f"Updated {distribution} to {version}.{note}",
866
+ app_id=app_id,
867
+ distribution=distribution,
868
+ version=version,
869
+ installed_apps=list(declared_apps),
870
+ has_dev_source=_has_dev_source(project_dir, app_id),
871
+ settings_path=str(settings_path),
872
+ completed_stages=completed_stages,
873
+ )
874
+ return 0
875
+ except Exception as exc:
876
+ if completed_stages:
877
+ stages = ("versions", "declare", "install", "settings")
878
+ next_stage = stages[min(len(completed_stages), len(stages) - 1)]
879
+ exc = _incomplete_orchestration_error(
880
+ exc,
881
+ code="PZ_APP_ORCHESTRATION_INCOMPLETE",
882
+ action="app_update_main",
883
+ completed_stages=completed_stages,
884
+ next_stage=next_stage,
885
+ selected_version=version,
886
+ )
887
+ return _handle_command_error(
888
+ exc=exc, json_mode=json_mode, action="app_update_main"
889
+ )
890
+
891
+
892
+ def app_versions_main(argv: list[object] | None = None) -> int:
893
+ json_mode = json_output_requested(argv)
894
+ try:
895
+ normalized = normalize_cli_argv(
896
+ argv,
897
+ code="PZ_CLI_ARGV_INVALID",
898
+ stage="cli",
899
+ owner="pz_core.authoring",
900
+ action="app_versions_main",
901
+ )
902
+ parser = PzArgumentParser(
903
+ prog=_prog("app", "versions"),
904
+ description="List published and current project versions for one app.",
905
+ error_code="PZ_SCAFFOLD_ARGUMENTS_INVALID",
906
+ error_stage="cli",
907
+ error_owner="pz_core.authoring",
908
+ error_action="app_versions_main",
909
+ )
910
+ add_cli_option(parser, "--json", action="store_true")
911
+ parser.add_argument("app_id")
912
+ add_cli_option(parser, "--project-dir", dest="project_dir")
913
+ ns = parser.parse_args(normalized)
914
+ project_dir = resolve_project_dir(ns.project_dir)
915
+ app_id = normalize_formal_app_id(ns.app_id, action="app_versions_main")
916
+ state = query_app_versions(app_id, project_dir=project_dir, action="app_versions_main")
917
+ available = [
918
+ {
919
+ "version": item.version,
920
+ "prerelease": item.prerelease,
921
+ "yanked": item.yanked,
922
+ "compatible": item.compatible,
923
+ }
924
+ for item in state.available
925
+ ]
926
+ if json_mode:
927
+ print(
928
+ _success_payload(
929
+ app_id=state.app_id,
930
+ distribution=state.distribution,
931
+ available=available,
932
+ latest=state.latest,
933
+ declared=state.declared,
934
+ locked=state.locked,
935
+ installed=state.installed,
936
+ )
937
+ )
938
+ else:
939
+ print(f"Versions for {state.app_id} ({state.distribution}):")
940
+ for item in state.available:
941
+ markers = []
942
+ if item.version == state.latest:
943
+ markers.append("latest")
944
+ if item.version == state.declared:
945
+ markers.append("declared")
946
+ if item.version == state.locked:
947
+ markers.append("locked")
948
+ if item.version == state.installed:
949
+ markers.append("installed")
950
+ if item.prerelease:
951
+ markers.append("prerelease")
952
+ if item.yanked:
953
+ markers.append("yanked")
954
+ suffix = f" [{', '.join(markers)}]" if markers else ""
955
+ print(f" {item.version}{suffix}")
956
+ return 0
957
+ except Exception as exc:
958
+ return _handle_command_error(exc=exc, json_mode=json_mode, action="app_versions_main")
959
+
960
+
961
+ def app_declare_main(argv: list[object] | None = None) -> int:
962
+ return _app_declaration_main(argv, remove=False)
963
+
964
+
965
+ def app_undeclare_main(argv: list[object] | None = None) -> int:
966
+ return _app_declaration_main(argv, remove=True)
967
+
968
+
969
+ def _app_declaration_main(argv: list[object] | None, *, remove: bool) -> int:
970
+ action = "app_undeclare_main" if remove else "app_declare_main"
971
+ command = "undeclare" if remove else "declare"
972
+ json_mode = json_output_requested(argv)
973
+ try:
974
+ normalized = normalize_cli_argv(
975
+ argv,
976
+ code="PZ_CLI_ARGV_INVALID",
977
+ stage="cli",
978
+ owner="pz_core.authoring",
979
+ action=action,
980
+ )
981
+ parser = PzArgumentParser(
982
+ prog=_prog("app", command),
983
+ description=f"Atomically {command} an exact app dependency.",
984
+ error_code="PZ_SCAFFOLD_ARGUMENTS_INVALID",
985
+ error_stage="cli",
986
+ error_owner="pz_core.authoring",
987
+ error_action=action,
988
+ )
989
+ add_cli_option(parser, "--json", action="store_true")
990
+ parser.add_argument("app_id")
991
+ if not remove:
992
+ parser.add_argument("--version", required=True)
993
+ add_cli_option(parser, "--project-dir", dest="project_dir")
994
+ ns = parser.parse_args(normalized)
995
+ project_dir = resolve_project_dir(ns.project_dir)
996
+ app_id = normalize_formal_app_id(ns.app_id, action=action)
997
+ result = (
998
+ undeclare_app_version(app_id, project_dir=project_dir, action=action)
999
+ if remove
1000
+ else declare_app_version(app_id, ns.version, project_dir=project_dir, action=action)
1001
+ )
1002
+ _print_success(
1003
+ json_mode=json_mode,
1004
+ text=f"{command.title()}d {result.distribution}{'' if result.version is None else '==' + result.version}.",
1005
+ app_id=app_id,
1006
+ distribution=result.distribution,
1007
+ version=result.version,
1008
+ installed_apps=list(result.installed_apps),
1009
+ changed=result.changed,
1010
+ )
1011
+ return 0
1012
+ except Exception as exc:
1013
+ return _handle_command_error(exc=exc, json_mode=json_mode, action=action)
1014
+
1015
+
1016
+ def app_install_main(argv: list[object] | None = None) -> int:
1017
+ json_mode = json_output_requested(argv)
1018
+ try:
1019
+ normalized = normalize_cli_argv(
1020
+ argv,
1021
+ code="PZ_CLI_ARGV_INVALID",
1022
+ stage="cli",
1023
+ owner="pz_core.authoring",
1024
+ action="app_install_main",
1025
+ )
1026
+ parser = PzArgumentParser(
1027
+ prog=_prog("app", "install"),
1028
+ description="Install one already-declared exact app version.",
1029
+ error_code="PZ_SCAFFOLD_ARGUMENTS_INVALID",
1030
+ error_stage="cli",
1031
+ error_owner="pz_core.authoring",
1032
+ error_action="app_install_main",
1033
+ )
1034
+ add_cli_option(parser, "--json", action="store_true")
1035
+ parser.add_argument("app_id")
1036
+ parser.add_argument("--version", required=True)
1037
+ add_cli_option(parser, "--project-dir", dest="project_dir")
1038
+ ns = parser.parse_args(normalized)
1039
+ project_dir = resolve_project_dir(ns.project_dir)
1040
+ app_id = normalize_formal_app_id(ns.app_id, action="app_install_main")
1041
+ result = install_declared_app(
1042
+ app_id, ns.version, project_dir=project_dir, action="app_install_main"
1043
+ )
1044
+ _print_success(
1045
+ json_mode=json_mode,
1046
+ text=f"Installed {result.distribution}=={result.version}.",
1047
+ app_id=app_id,
1048
+ distribution=result.distribution,
1049
+ version=result.version,
1050
+ python=str(result.python),
1051
+ lock_path=str(result.lock_path),
1052
+ )
1053
+ return 0
1054
+ except Exception as exc:
1055
+ return _handle_command_error(exc=exc, json_mode=json_mode, action="app_install_main")
1056
+
1057
+
1058
+ def app_init_main(argv: list[object] | None = None) -> int:
1059
+ json_mode = json_output_requested(argv)
1060
+ try:
1061
+ normalized = normalize_cli_argv(
1062
+ argv,
1063
+ code="PZ_CLI_ARGV_INVALID",
1064
+ stage="cli",
1065
+ owner="pz_core.authoring",
1066
+ action="app_init_main",
1067
+ )
1068
+ parser = PzArgumentParser(
1069
+ prog=_prog("app", "init"),
1070
+ description=(
1071
+ "Initialize project default packages from existing apps/.sources metadata."
1072
+ ),
1073
+ error_code="PZ_SCAFFOLD_ARGUMENTS_INVALID",
1074
+ error_stage="cli",
1075
+ error_owner="pz_core.authoring",
1076
+ error_action="app_init_main",
1077
+ )
1078
+ add_cli_option(parser, "--json", action="store_true")
1079
+ add_cli_option(
1080
+ parser,
1081
+ "--no-sync",
1082
+ action="store_true",
1083
+ dest="no_sync",
1084
+ help="Skip project virtualenv preparation and distribution installation.",
1085
+ )
1086
+ add_cli_option(parser, "--project-dir", dest="project_dir")
1087
+ ns = parser.parse_args(normalized)
1088
+ project_dir = resolve_project_dir(ns.project_dir)
1089
+ result = execute_app_init(
1090
+ project_dir,
1091
+ no_sync=bool(ns.no_sync),
1092
+ action="app_init_main",
1093
+ )
1094
+ discovered_apps = [item.app_id for item in result.source_apps.discovered_apps]
1095
+ if result.project_venv is None:
1096
+ project_venv = None
1097
+ project_venv_created = False
1098
+ text = (
1099
+ f"Declared {len(discovered_apps)} default app(s) from apps/.sources. "
1100
+ "Distribution installation and project virtualenv preparation were skipped."
1101
+ )
1102
+ else:
1103
+ project_venv = str(result.project_venv.path)
1104
+ project_venv_created = result.project_venv.created
1105
+ text = (
1106
+ f"Declared {len(discovered_apps)} default app(s) from apps/.sources "
1107
+ f"and installed {len(result.source_install.distributions)} distribution(s) "
1108
+ f"into project virtualenv {result.project_venv.path}."
1109
+ )
1110
+ _print_success(
1111
+ json_mode=json_mode,
1112
+ text=text,
1113
+ discovered_apps=discovered_apps,
1114
+ declared_apps=list(result.source_apps.declared_apps),
1115
+ installed_apps=list(result.source_apps.installed_apps),
1116
+ project_venv=project_venv,
1117
+ project_venv_created=project_venv_created,
1118
+ install_distributions=list(result.source_install.distributions),
1119
+ install_target=None
1120
+ if result.source_install.install_target is None
1121
+ else result.source_install.install_target.name,
1122
+ install_index_url=None
1123
+ if result.source_install.install_target is None
1124
+ else result.source_install.install_target.index_url,
1125
+ install_targets=[
1126
+ target.name for target in result.source_install.install_targets
1127
+ ],
1128
+ install_index_urls=[
1129
+ target.index_url for target in result.source_install.install_targets
1130
+ ],
1131
+ install_python=None
1132
+ if result.source_install.python is None
1133
+ else str(result.source_install.python),
1134
+ synced_targets=[str(item.path) for item in result.synced_targets],
1135
+ settings_path=None
1136
+ if result.settings_path is None
1137
+ else str(result.settings_path),
1138
+ settings_updated=result.settings_path is not None,
1139
+ )
1140
+ return 0
1141
+ except Exception as exc:
1142
+ return _handle_command_error(
1143
+ exc=exc, json_mode=json_mode, action="app_init_main"
1144
+ )
1145
+
1146
+
1147
+ def dev_link_main(argv: list[object] | None = None) -> int:
1148
+ json_mode = json_output_requested(argv)
1149
+ completed_stages: list[str] = []
1150
+ try:
1151
+ normalized = normalize_cli_argv(
1152
+ argv,
1153
+ code="PZ_CLI_ARGV_INVALID",
1154
+ stage="cli",
1155
+ owner="pz_core.authoring",
1156
+ action="dev_link_main",
1157
+ )
1158
+ parser = PzArgumentParser(
1159
+ prog=_prog("dev", "link"),
1160
+ description="Bind one Git repository, directory, or app leaf for development.",
1161
+ error_code="PZ_SCAFFOLD_ARGUMENTS_INVALID",
1162
+ error_stage="cli",
1163
+ error_owner="pz_core.authoring",
1164
+ error_action="dev_link_main",
1165
+ )
1166
+ add_cli_option(parser, "--json", action="store_true")
1167
+ parser.add_argument("source")
1168
+ add_cli_option(
1169
+ parser,
1170
+ "--base-branch",
1171
+ choices=("develop", "main"),
1172
+ default="develop",
1173
+ dest="base_branch",
1174
+ )
1175
+ add_cli_option(parser, "--project-dir", dest="project_dir")
1176
+ ns = parser.parse_args(normalized)
1177
+ project_dir = resolve_project_dir(ns.project_dir)
1178
+ result = add_dev_source(
1179
+ AddDevSourceRequest(
1180
+ project_dir=project_dir,
1181
+ locators=(ns.source,),
1182
+ base_branch=ns.base_branch,
1183
+ )
1184
+ )
1185
+ result = replace(result, operation="link")
1186
+ completed_stages.append("source_link")
1187
+ update_project_uv_sources(project_dir, action="dev_link_main")
1188
+ completed_stages.append("project_sources")
1189
+ update_settings_file(project_dir)
1190
+ completed_stages.append("settings")
1191
+ sync_project_targets(project_dir, action="dev_link_main")
1192
+ completed_stages.append("env_sync")
1193
+ ide_result = sync_ide_projections(project_dir)
1194
+ completed_stages.append("ide_sync")
1195
+ _print_dev_transaction(result, json_mode=json_mode, ide_result=ide_result)
1196
+ return 0
1197
+ except Exception as exc:
1198
+ if completed_stages:
1199
+ stages = ("source_link", "project_sources", "settings", "env_sync", "ide_sync")
1200
+ exc = _incomplete_orchestration_error(
1201
+ exc,
1202
+ code="PZ_DEV_ORCHESTRATION_INCOMPLETE",
1203
+ action="dev_link_main",
1204
+ completed_stages=completed_stages,
1205
+ next_stage=stages[min(len(completed_stages), len(stages) - 1)],
1206
+ )
1207
+ return _handle_command_error(
1208
+ exc=exc, json_mode=json_mode, action="dev_link_main"
1209
+ )
1210
+
1211
+
1212
+ def dev_add_main(argv: list[object] | None = None) -> int:
1213
+ json_mode = json_output_requested(argv)
1214
+ completed_stages: list[str] = []
1215
+ try:
1216
+ normalized = normalize_cli_argv(
1217
+ argv,
1218
+ code="PZ_CLI_ARGV_INVALID",
1219
+ stage="cli",
1220
+ owner="pz_core.authoring",
1221
+ action="dev_add_main",
1222
+ )
1223
+ parser = PzArgumentParser(
1224
+ prog=_prog("dev", "add"),
1225
+ description="Import one formal minimal app into business-owned apps source.",
1226
+ error_code="PZ_SCAFFOLD_ARGUMENTS_INVALID",
1227
+ error_stage="cli",
1228
+ error_owner="pz_core.authoring",
1229
+ error_action="dev_add_main",
1230
+ )
1231
+ add_cli_option(parser, "--json", action="store_true")
1232
+ parser.add_argument("source")
1233
+ parser.add_argument("--branch", default="develop")
1234
+ add_cli_option(parser, "--project-dir", dest="project_dir")
1235
+ ns = parser.parse_args(normalized)
1236
+ project_dir = resolve_project_dir(ns.project_dir)
1237
+ resolved = resolve_source_locator(
1238
+ ns.source, project_dir=project_dir, action="dev_add_main"
1239
+ )
1240
+ if resolved.source_kind == "git":
1241
+ assert resolved.repo_key is not None
1242
+ cached = cache_git_source(
1243
+ repo_key=resolved.repo_key,
1244
+ source=resolved.clone_source,
1245
+ branch=ns.branch,
1246
+ action="dev_add_main",
1247
+ )
1248
+ completed_stages.append("source_cache")
1249
+ pinned = load_cached_git_source(
1250
+ repo_key=cached.repo_key,
1251
+ source=cached.source,
1252
+ revision=cached.commit,
1253
+ branch=cached.branch,
1254
+ action="dev_add_main",
1255
+ )
1256
+ result = import_git_app(
1257
+ ns.source,
1258
+ cache=pinned,
1259
+ project_dir=project_dir,
1260
+ action="dev_add_main",
1261
+ )
1262
+ else:
1263
+ result = find_existing_local_import(
1264
+ ns.source, project_dir=project_dir, action="dev_add_main"
1265
+ ) or import_local_dev_source(
1266
+ ns.source, project_dir=project_dir, action="dev_add_main"
1267
+ )
1268
+ completed_stages.append("source_import")
1269
+ settings = update_settings_file(project_dir)
1270
+ completed_stages.append("settings")
1271
+ synced = sync_project_targets(project_dir, action="dev_add_main")
1272
+ completed_stages.append("env_sync")
1273
+ ide_result = sync_ide_projections(project_dir)
1274
+ completed_stages.append("ide_sync")
1275
+ ide_statuses = {
1276
+ item.provider: item.status for item in ide_result.providers
1277
+ }
1278
+ _print_success(
1279
+ json_mode=json_mode,
1280
+ text=f"Imported {result.app_id} into {result.destination} for business ownership.",
1281
+ app_id=result.app_id,
1282
+ destination=str(result.destination),
1283
+ revision=result.commit,
1284
+ created=result.created,
1285
+ settings_path=str(settings.path),
1286
+ synced_targets=[str(item.path) for item in synced],
1287
+ ide_providers=ide_statuses,
1288
+ completed_stages=completed_stages,
1289
+ )
1290
+ return 0
1291
+ except Exception as exc:
1292
+ if completed_stages:
1293
+ stages = ("source_cache", "source_import", "settings", "env_sync", "ide_sync")
1294
+ if completed_stages[0] != "source_cache":
1295
+ stages = ("source_import", "settings", "env_sync", "ide_sync")
1296
+ next_stage = stages[min(len(completed_stages), len(stages) - 1)]
1297
+ exc = _incomplete_orchestration_error(
1298
+ exc,
1299
+ code="PZ_DEV_ORCHESTRATION_INCOMPLETE",
1300
+ action="dev_add_main",
1301
+ completed_stages=completed_stages,
1302
+ next_stage=next_stage,
1303
+ )
1304
+ return _handle_command_error(exc=exc, json_mode=json_mode, action="dev_add_main")
1305
+
1306
+
1307
+ def dev_init_main(argv: list[object] | None = None) -> int:
1308
+ json_mode = json_output_requested(argv)
1309
+ try:
1310
+ normalized = normalize_cli_argv(
1311
+ argv,
1312
+ code="PZ_CLI_ARGV_INVALID",
1313
+ stage="cli",
1314
+ owner="pz_core.authoring",
1315
+ action="dev_init_main",
1316
+ )
1317
+ parser = PzArgumentParser(
1318
+ prog=_prog("dev", "init"),
1319
+ description="Initialize a project by importing official apps or explicitly linking sources.",
1320
+ error_code="PZ_SCAFFOLD_ARGUMENTS_INVALID",
1321
+ error_stage="cli",
1322
+ error_owner="pz_core.authoring",
1323
+ error_action="dev_init_main",
1324
+ )
1325
+ add_cli_option(parser, "--json", action="store_true")
1326
+ add_cli_option(parser, "--app-name")
1327
+ add_cli_option(
1328
+ parser,
1329
+ "--base-branch",
1330
+ choices=("develop", "main"),
1331
+ default="develop",
1332
+ dest="base_branch",
1333
+ )
1334
+ add_cli_option(parser, "--project-dir", dest="project_dir")
1335
+ parser.add_argument("--link", action="store_true")
1336
+ parser.add_argument("--source-dir", dest="source_dir")
1337
+ add_cli_option(
1338
+ parser,
1339
+ "--clone-sources",
1340
+ action="store_true",
1341
+ dest="clone_sources",
1342
+ )
1343
+ add_cli_option(
1344
+ parser,
1345
+ "--no-sync",
1346
+ action="store_true",
1347
+ dest="no_sync",
1348
+ help="Skip the final project environment installation.",
1349
+ )
1350
+ ns = parser.parse_args(normalized)
1351
+ project_dir = resolve_project_dir(ns.project_dir)
1352
+ if bool(ns.link) and bool(ns.clone_sources) == bool(ns.source_dir):
1353
+ raise PzError(
1354
+ code="PZ_DEV_INIT_SOURCE_SELECTOR_INVALID",
1355
+ message="pz dev init --link 必须且只能指定 --clone-sources 或 --source-dir。",
1356
+ stage="cli",
1357
+ owner="pz_core.authoring",
1358
+ action="dev_init_main",
1359
+ )
1360
+ if not ns.link and (ns.clone_sources or ns.source_dir):
1361
+ raise PzError(
1362
+ code="PZ_DEV_INIT_SOURCE_SELECTOR_INVALID",
1363
+ message="--clone-sources 和 --source-dir 只能与 --link 一起使用。",
1364
+ stage="cli",
1365
+ owner="pz_core.authoring",
1366
+ action="dev_init_main",
1367
+ )
1368
+ progress_reporter = None if json_mode else print
1369
+ preparation, result = execute_dev_init(
1370
+ project_dir,
1371
+ app_name=ns.app_name,
1372
+ base_branch=ns.base_branch,
1373
+ clone_sources=bool(ns.clone_sources),
1374
+ link=bool(ns.link),
1375
+ source_dir=ns.source_dir,
1376
+ no_sync=bool(ns.no_sync),
1377
+ action="dev_init_main",
1378
+ progress_reporter=progress_reporter,
1379
+ )
1380
+ bootstrap_note = ""
1381
+ if (
1382
+ preparation.scaffold_created
1383
+ and preparation.default_app_created
1384
+ and preparation.default_app_id is not None
1385
+ ):
1386
+ bootstrap_note = f" Created a new project scaffold and default local app `{preparation.default_app_id}` first."
1387
+ elif preparation.scaffold_created:
1388
+ bootstrap_note = " Created a new project scaffold first."
1389
+ elif preparation.default_app_created and preparation.default_app_id is not None:
1390
+ bootstrap_note = (
1391
+ f" Added the default local app `{preparation.default_app_id}`."
1392
+ )
1393
+ project_venv = (
1394
+ None
1395
+ if preparation.project_venv is None
1396
+ else str(preparation.project_venv.path)
1397
+ )
1398
+ project_venv_created = (
1399
+ False
1400
+ if preparation.project_venv is None
1401
+ else preparation.project_venv.created
1402
+ )
1403
+ install_note = (
1404
+ " Initial environment install was skipped. Run `pz env install` before `pz run`."
1405
+ if ns.no_sync
1406
+ else " Initial environment install finished. `pz run` can start directly."
1407
+ )
1408
+ _print_success(
1409
+ json_mode=json_mode,
1410
+ text=(
1411
+ (
1412
+ "Initialized linked development sources on business branch "
1413
+ if ns.link
1414
+ else "Imported official pz_apps source into business ownership for "
1415
+ )
1416
+ + f"{result.business_branch}."
1417
+ f"{bootstrap_note}"
1418
+ f"{install_note}"
1419
+ ),
1420
+ operation=result.operation,
1421
+ business_branch=result.business_branch,
1422
+ base_branch=result.base_branch,
1423
+ repo_keys=list(result.repo_keys),
1424
+ checkouts=[str(path) for path in result.checkouts],
1425
+ bindings=list(result.bindings),
1426
+ links=[str(path) for path in result.links],
1427
+ project_scaffold_created=preparation.scaffold_created,
1428
+ default_app_created=preparation.default_app_created,
1429
+ default_app_id=preparation.default_app_id,
1430
+ project_venv=project_venv,
1431
+ project_venv_created=project_venv_created,
1432
+ )
1433
+ return 0
1434
+ except Exception as exc:
1435
+ return _handle_command_error(
1436
+ exc=exc, json_mode=json_mode, action="dev_init_main"
1437
+ )
1438
+
1439
+
1440
+ def dev_update_main(argv: list[object] | None = None) -> int:
1441
+ json_mode = json_output_requested(argv)
1442
+ completed_stages: list[str] = []
1443
+ try:
1444
+ normalized = normalize_cli_argv(
1445
+ argv,
1446
+ code="PZ_CLI_ARGV_INVALID",
1447
+ stage="cli",
1448
+ owner="pz_core.authoring",
1449
+ action="dev_update_main",
1450
+ )
1451
+ parser = PzArgumentParser(
1452
+ prog=_prog("dev", "update"),
1453
+ description="Fast-forward a managed checkout on its business branch.",
1454
+ error_code="PZ_SCAFFOLD_ARGUMENTS_INVALID",
1455
+ error_stage="cli",
1456
+ error_owner="pz_core.authoring",
1457
+ error_action="dev_update_main",
1458
+ )
1459
+ add_cli_option(parser, "--json", action="store_true")
1460
+ parser.add_argument("repo_or_app")
1461
+ add_cli_option(parser, "--project-dir", dest="project_dir")
1462
+ ns = parser.parse_args(normalized)
1463
+ project_dir = resolve_project_dir(ns.project_dir)
1464
+ from .dev_sources import UpdateDevSourceRequest, update_dev_source
1465
+
1466
+ result = update_dev_source(UpdateDevSourceRequest(project_dir, ns.repo_or_app))
1467
+ completed_stages.append("source_update")
1468
+ sync_project_targets(project_dir, action="dev_update_main")
1469
+ completed_stages.append("env_sync")
1470
+ ide_result = sync_ide_projections(project_dir)
1471
+ completed_stages.append("ide_sync")
1472
+ _print_dev_transaction(result, json_mode=json_mode, ide_result=ide_result)
1473
+ return 0
1474
+ except Exception as exc:
1475
+ if completed_stages:
1476
+ stages = ("source_update", "env_sync", "ide_sync")
1477
+ exc = _incomplete_orchestration_error(
1478
+ exc,
1479
+ code="PZ_DEV_ORCHESTRATION_INCOMPLETE",
1480
+ action="dev_update_main",
1481
+ completed_stages=completed_stages,
1482
+ next_stage=stages[min(len(completed_stages), len(stages) - 1)],
1483
+ )
1484
+ return _handle_command_error(
1485
+ exc=exc, json_mode=json_mode, action="dev_update_main"
1486
+ )
1487
+
1488
+
1489
+ def dev_branch_main(argv: list[object] | None = None) -> int:
1490
+ json_mode = json_output_requested(argv)
1491
+ completed_stages: list[str] = []
1492
+ try:
1493
+ normalized = normalize_cli_argv(
1494
+ argv,
1495
+ code="PZ_CLI_ARGV_INVALID",
1496
+ stage="cli",
1497
+ owner="pz_core.authoring",
1498
+ action="dev_branch_main",
1499
+ )
1500
+ parser = PzArgumentParser(
1501
+ prog=_prog("dev", "branch"),
1502
+ description="Prepare an existing managed checkout on the business branch.",
1503
+ error_code="PZ_SCAFFOLD_ARGUMENTS_INVALID",
1504
+ error_stage="cli",
1505
+ error_owner="pz_core.authoring",
1506
+ error_action="dev_branch_main",
1507
+ )
1508
+ add_cli_option(parser, "--json", action="store_true")
1509
+ parser.add_argument("repo_or_app")
1510
+ add_cli_option(
1511
+ parser,
1512
+ "--base-branch",
1513
+ choices=("develop", "main"),
1514
+ default="develop",
1515
+ dest="base_branch",
1516
+ )
1517
+ add_cli_option(parser, "--project-dir", dest="project_dir")
1518
+ ns = parser.parse_args(normalized)
1519
+ project_dir = resolve_project_dir(ns.project_dir)
1520
+ result = branch_dev_source(
1521
+ BranchDevSourceRequest(
1522
+ project_dir=project_dir,
1523
+ locator=ns.repo_or_app,
1524
+ base_branch=ns.base_branch,
1525
+ )
1526
+ )
1527
+ completed_stages.append("source_branch")
1528
+ sync_project_targets(project_dir, action="dev_branch_main")
1529
+ completed_stages.append("env_sync")
1530
+ ide_result = sync_ide_projections(project_dir)
1531
+ completed_stages.append("ide_sync")
1532
+ _print_dev_transaction(result, json_mode=json_mode, ide_result=ide_result)
1533
+ return 0
1534
+ except Exception as exc:
1535
+ if completed_stages:
1536
+ stages = ("source_branch", "env_sync", "ide_sync")
1537
+ exc = _incomplete_orchestration_error(
1538
+ exc,
1539
+ code="PZ_DEV_ORCHESTRATION_INCOMPLETE",
1540
+ action="dev_branch_main",
1541
+ completed_stages=completed_stages,
1542
+ next_stage=stages[min(len(completed_stages), len(stages) - 1)],
1543
+ )
1544
+ return _handle_command_error(
1545
+ exc=exc,
1546
+ json_mode=json_mode,
1547
+ action="dev_branch_main",
1548
+ )
1549
+
1550
+
1551
+ def dev_unlink_main(argv: list[object] | None = None) -> int:
1552
+ json_mode = json_output_requested(argv)
1553
+ completed_stages: list[str] = []
1554
+ try:
1555
+ normalized = normalize_cli_argv(
1556
+ argv,
1557
+ code="PZ_CLI_ARGV_INVALID",
1558
+ stage="cli",
1559
+ owner="pz_core.authoring",
1560
+ action="dev_unlink_main",
1561
+ )
1562
+ parser = PzArgumentParser(
1563
+ prog=_prog("dev", "unlink"),
1564
+ description="Remove one managed binding and optionally its safe checkout.",
1565
+ error_code="PZ_SCAFFOLD_ARGUMENTS_INVALID",
1566
+ error_stage="cli",
1567
+ error_owner="pz_core.authoring",
1568
+ error_action="dev_unlink_main",
1569
+ )
1570
+ add_cli_option(parser, "--json", action="store_true")
1571
+ parser.add_argument("binding")
1572
+ add_cli_option(
1573
+ parser,
1574
+ "--delete-checkout",
1575
+ action="store_true",
1576
+ dest="delete_checkout",
1577
+ )
1578
+ add_cli_option(parser, "--project-dir", dest="project_dir")
1579
+ ns = parser.parse_args(normalized)
1580
+ project_dir = resolve_project_dir(ns.project_dir)
1581
+ from .dev_sources import RemoveDevSourceRequest, remove_dev_source
1582
+
1583
+ result = remove_dev_source(
1584
+ RemoveDevSourceRequest(
1585
+ project_dir,
1586
+ ns.binding,
1587
+ delete_checkout=bool(ns.delete_checkout),
1588
+ )
1589
+ )
1590
+ result = replace(result, operation="unlink")
1591
+ completed_stages.append("source_unlink")
1592
+ update_project_uv_sources(project_dir, action="dev_unlink_main")
1593
+ completed_stages.append("project_sources")
1594
+ update_settings_file(project_dir)
1595
+ completed_stages.append("settings")
1596
+ sync_project_targets(project_dir, action="dev_unlink_main")
1597
+ completed_stages.append("env_sync")
1598
+ ide_result = sync_ide_projections(project_dir)
1599
+ completed_stages.append("ide_sync")
1600
+ _print_dev_transaction(result, json_mode=json_mode, ide_result=ide_result)
1601
+ return 0
1602
+ except Exception as exc:
1603
+ if completed_stages:
1604
+ stages = ("source_unlink", "project_sources", "settings", "env_sync", "ide_sync")
1605
+ exc = _incomplete_orchestration_error(
1606
+ exc,
1607
+ code="PZ_DEV_ORCHESTRATION_INCOMPLETE",
1608
+ action="dev_unlink_main",
1609
+ completed_stages=completed_stages,
1610
+ next_stage=stages[min(len(completed_stages), len(stages) - 1)],
1611
+ )
1612
+ return _handle_command_error(exc=exc, json_mode=json_mode, action="dev_unlink_main")
1613
+
1614
+
1615
+ # Internal compatibility for callers migrating from the pre-cutover function name.
1616
+ dev_rm_main = dev_unlink_main
1617
+
1618
+
1619
+ def publish_main(argv: list[object] | None = None) -> int:
1620
+ json_mode = json_output_requested(argv)
1621
+ try:
1622
+ normalized = normalize_cli_argv(
1623
+ argv,
1624
+ code="PZ_CLI_ARGV_INVALID",
1625
+ stage="cli",
1626
+ owner="pz_core.authoring",
1627
+ action="publish_main",
1628
+ )
1629
+ parser = PzArgumentParser(
1630
+ prog=_prog("pub"),
1631
+ description="Build and publish a versioned release artifact for a formal minimal app.",
1632
+ error_code="PZ_SCAFFOLD_ARGUMENTS_INVALID",
1633
+ error_stage="cli",
1634
+ error_owner="pz_core.authoring",
1635
+ error_action="publish_main",
1636
+ )
1637
+ add_cli_option(parser, "--json", action="store_true")
1638
+ parser.add_argument("app_id")
1639
+ add_cli_option(parser, "--target")
1640
+ add_cli_option(parser, "--dry-run", action="store_true")
1641
+ add_cli_option(parser, "--project-dir", dest="project_dir")
1642
+ ns = parser.parse_args(normalized)
1643
+ project_dir = resolve_project_dir(ns.project_dir)
1644
+ result = publish_app(
1645
+ ns.app_id,
1646
+ project_dir=project_dir,
1647
+ target_name=ns.target,
1648
+ dry_run=bool(ns.dry_run),
1649
+ action="publish_main",
1650
+ )
1651
+ _print_success(
1652
+ json_mode=json_mode,
1653
+ text=_publish_result_message(result),
1654
+ app_id=result.app_id,
1655
+ version=result.version,
1656
+ package_root=str(result.package_root),
1657
+ dist_dir=str(result.dist_dir),
1658
+ target=result.target.name,
1659
+ dry_run=result.dry_run,
1660
+ commit=result.commit,
1661
+ tag=result.tag,
1662
+ )
1663
+ return 0
1664
+ except Exception as exc:
1665
+ return _handle_command_error(
1666
+ exc=exc, json_mode=json_mode, action="publish_main"
1667
+ )
1668
+
1669
+
1670
+ def publish_batch_main(argv: list[object] | None = None) -> int:
1671
+ json_mode = json_output_requested(argv)
1672
+ try:
1673
+ normalized = normalize_cli_argv(
1674
+ argv,
1675
+ code="PZ_CLI_ARGV_INVALID",
1676
+ stage="cli",
1677
+ owner="pz_core.authoring",
1678
+ action="publish_batch_main",
1679
+ )
1680
+ parser = PzArgumentParser(
1681
+ prog=_prog("pub", "batch"),
1682
+ description="Publish multiple versioned release artifacts in the explicit order provided.",
1683
+ error_code="PZ_SCAFFOLD_ARGUMENTS_INVALID",
1684
+ error_stage="cli",
1685
+ error_owner="pz_core.authoring",
1686
+ error_action="publish_batch_main",
1687
+ )
1688
+ add_cli_option(parser, "--json", action="store_true")
1689
+ parser.add_argument("app_ids", nargs="+")
1690
+ add_cli_option(parser, "--target")
1691
+ add_cli_option(parser, "--dry-run", action="store_true")
1692
+ add_cli_option(parser, "--project-dir", dest="project_dir")
1693
+ ns = parser.parse_args(normalized)
1694
+ project_dir = resolve_project_dir(ns.project_dir)
1695
+
1696
+ execution = execute_publish_batch(
1697
+ ns.app_ids,
1698
+ project_dir=project_dir,
1699
+ target_name=ns.target,
1700
+ dry_run=bool(ns.dry_run),
1701
+ action="publish_batch_main",
1702
+ )
1703
+ results = list(execution.results)
1704
+ for result in results:
1705
+ if not json_mode:
1706
+ print(_publish_result_message(result))
1707
+ if json_mode:
1708
+ print(
1709
+ _success_payload(
1710
+ message=execution.summary_message,
1711
+ dry_run=bool(ns.dry_run),
1712
+ target=results[0].target.name
1713
+ if results
1714
+ else (ns.target or "default"),
1715
+ results=[
1716
+ {
1717
+ "app_id": result.app_id,
1718
+ "version": result.version,
1719
+ "package_root": str(result.package_root),
1720
+ "dist_dir": str(result.dist_dir),
1721
+ }
1722
+ for result in results
1723
+ ],
1724
+ )
1725
+ )
1726
+ else:
1727
+ print(execution.summary_message)
1728
+ return 0
1729
+ except Exception as exc:
1730
+ return _handle_command_error(
1731
+ exc=exc, json_mode=json_mode, action="publish_batch_main"
1732
+ )
1733
+
1734
+
1735
+ def publish_rm_main(argv: list[object] | None = None) -> int:
1736
+ json_mode = json_output_requested(argv)
1737
+ try:
1738
+ normalized = normalize_cli_argv(
1739
+ argv,
1740
+ code="PZ_CLI_ARGV_INVALID",
1741
+ stage="cli",
1742
+ owner="pz_core.authoring",
1743
+ action="publish_rm_main",
1744
+ )
1745
+ parser = PzArgumentParser(
1746
+ prog=_prog("pub", "rm"),
1747
+ description="Remove all published release artifacts for one versioned package from a publish target.",
1748
+ error_code="PZ_SCAFFOLD_ARGUMENTS_INVALID",
1749
+ error_stage="cli",
1750
+ error_owner="pz_core.authoring",
1751
+ error_action="publish_rm_main",
1752
+ )
1753
+ add_cli_option(parser, "--json", action="store_true")
1754
+ parser.add_argument("app_id")
1755
+ add_cli_option(parser, "--target")
1756
+ add_cli_option(parser, "--dry-run", action="store_true")
1757
+ add_cli_option(parser, "--force", action="store_true")
1758
+ add_cli_option(parser, "--project-dir", dest="project_dir")
1759
+ ns = parser.parse_args(normalized)
1760
+ project_dir = resolve_project_dir(ns.project_dir)
1761
+ result = unpublish_app(
1762
+ ns.app_id,
1763
+ project_dir=project_dir,
1764
+ target_name=ns.target,
1765
+ dry_run=bool(ns.dry_run),
1766
+ force=bool(ns.force),
1767
+ action="publish_rm_main",
1768
+ )
1769
+ _print_success(
1770
+ json_mode=json_mode,
1771
+ text=_unpublish_result_message(result),
1772
+ app_id=result.app_id,
1773
+ distribution_name=result.distribution_name,
1774
+ version=result.version,
1775
+ package_root=str(result.package_root),
1776
+ target=result.target.name,
1777
+ removed_versions=list(result.removed_versions),
1778
+ dry_run=result.dry_run,
1779
+ force=result.force,
1780
+ )
1781
+ return 0
1782
+ except Exception as exc:
1783
+ return _handle_command_error(
1784
+ exc=exc, json_mode=json_mode, action="publish_rm_main"
1785
+ )
1786
+
1787
+
1788
+ __all__ = [
1789
+ "app_add_main",
1790
+ "app_init_main",
1791
+ "app_rm_main",
1792
+ "app_update_main",
1793
+ "dev_add_main",
1794
+ "dev_branch_main",
1795
+ "dev_ide_sync_main",
1796
+ "dev_init_main",
1797
+ "dev_link_main",
1798
+ "dev_rm_main",
1799
+ "dev_source_main",
1800
+ "dev_unlink_main",
1801
+ "dev_update_main",
1802
+ "json_error",
1803
+ "publish_main",
1804
+ "publish_batch_main",
1805
+ "publish_commit_main",
1806
+ "publish_rm_main",
1807
+ "publish_tag_main",
1808
+ "publish_upload_main",
1809
+ "startapp_main",
1810
+ ]