smbcloud-cli 0.4.12__tar.gz → 0.5.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (186) hide show
  1. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/Cargo.lock +34 -16
  2. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/Cargo.toml +14 -12
  3. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/PKG-INFO +1 -1
  4. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/cli/Cargo.toml +3 -1
  5. smbcloud_cli-0.5.0/crates/cli/src/cli/mod.rs +210 -0
  6. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/cli/src/cloud-auth/render.rs +6 -2
  7. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/cli/src/cloud-deploy/config.rs +3 -0
  8. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/cli/src/cloud-deploy/process_deploy_nextjs_ssr.rs +125 -19
  9. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/cli/src/cloud-deploy/process_migrate.rs +1 -0
  10. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/cli/src/cloud-mail/cli.rs +34 -0
  11. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/cli/src/cloud-mail/mod.rs +1 -0
  12. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/cli/src/cloud-mail/process.rs +53 -0
  13. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/cli/src/cloud-mail/render.rs +12 -0
  14. smbcloud_cli-0.5.0/crates/cli/src/cloud-mail/send.rs +188 -0
  15. smbcloud_cli-0.5.0/crates/cli/src/controlkit.rs +129 -0
  16. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/cli/src/lib.rs +1 -0
  17. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/cli/src/main.rs +4 -1
  18. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/cli/src/mcp.rs +138 -7
  19. smbcloud_cli-0.5.0/crates/cli/src/mcp_xcrs.rs +156 -0
  20. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-auth/Cargo.toml +1 -1
  21. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-deploy/Cargo.toml +1 -1
  22. smbcloud_cli-0.5.0/crates/smbcloud-email-sdk/Cargo.toml +30 -0
  23. smbcloud_cli-0.5.0/crates/smbcloud-email-sdk/README.md +81 -0
  24. smbcloud_cli-0.5.0/crates/smbcloud-email-sdk/src/client.rs +111 -0
  25. smbcloud_cli-0.5.0/crates/smbcloud-email-sdk/src/client_credentials.rs +23 -0
  26. smbcloud_cli-0.5.0/crates/smbcloud-email-sdk/src/error.rs +15 -0
  27. smbcloud_cli-0.5.0/crates/smbcloud-email-sdk/src/message.rs +150 -0
  28. smbcloud_cli-0.5.0/crates/smbcloud-email-sdk/src/smbcloud_email_sdk.rs +42 -0
  29. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-mail/Cargo.toml +1 -1
  30. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-model/Cargo.toml +1 -1
  31. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-model/src/app_auth.rs +4 -1
  32. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-model/src/deploy_config.rs +5 -0
  33. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-model/src/project.rs +7 -0
  34. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-network/Cargo.toml +1 -1
  35. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-networking/Cargo.toml +1 -1
  36. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-networking-project/Cargo.toml +1 -1
  37. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-utils/Cargo.toml +1 -1
  38. smbcloud_cli-0.5.0/crates/xcrs/Cargo.toml +31 -0
  39. smbcloud_cli-0.5.0/crates/xcrs/README.md +30 -0
  40. smbcloud_cli-0.5.0/crates/xcrs/src/main.rs +111 -0
  41. smbcloud_cli-0.5.0/crates/xcrs/src/mcp.rs +2431 -0
  42. smbcloud_cli-0.5.0/crates/xcrs/src/xcrs.rs +1391 -0
  43. smbcloud_cli-0.4.12/crates/cli/src/cli/mod.rs +0 -104
  44. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/README.md +0 -0
  45. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/cli/README.md +0 -0
  46. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/cli/src/account/cli.rs +0 -0
  47. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/cli/src/account/forgot/mod.rs +0 -0
  48. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/cli/src/account/lib.rs +0 -0
  49. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/cli/src/account/login/mod.rs +0 -0
  50. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/cli/src/account/login/process.rs +0 -0
  51. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/cli/src/account/logout/mod.rs +0 -0
  52. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/cli/src/account/me/mod.rs +0 -0
  53. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/cli/src/account/mod.rs +0 -0
  54. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/cli/src/account/signup/mod.rs +0 -0
  55. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/cli/src/account/signup/process.rs +0 -0
  56. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/cli/src/ci.rs +0 -0
  57. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/cli/src/cloud-auth/auth_app.rs +0 -0
  58. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/cli/src/cloud-auth/cli.rs +0 -0
  59. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/cli/src/cloud-auth/mod.rs +0 -0
  60. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/cli/src/cloud-auth/process.rs +0 -0
  61. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/cli/src/cloud-auth/request.rs +0 -0
  62. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/cli/src/cloud-auth/url_builder.rs +0 -0
  63. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/cli/src/cloud-deploy/git.rs +0 -0
  64. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/cli/src/cloud-deploy/mod.rs +0 -0
  65. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/cli/src/cloud-deploy/process_deploy.rs +0 -0
  66. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/cli/src/cloud-deploy/process_deploy_rails.rs +0 -0
  67. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/cli/src/cloud-deploy/process_deploy_rust.rs +0 -0
  68. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/cli/src/cloud-deploy/process_deploy_swift.rs +0 -0
  69. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/cli/src/cloud-deploy/process_deploy_vite_spa.rs +0 -0
  70. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/cli/src/cloud-deploy/remote_messages.rs +0 -0
  71. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/cli/src/cloud-deploy/setup.rs +0 -0
  72. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/cli/src/cloud-deploy/setup_create_new_project.rs +0 -0
  73. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/cli/src/cloud-deploy/setup_project.rs +0 -0
  74. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/cli/src/cloud-deploy/setup_select_project.rs +0 -0
  75. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/cli/src/cloud-mail/current_project.rs +0 -0
  76. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/cli/src/interface.rs +0 -0
  77. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/cli/src/project/cli.rs +0 -0
  78. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/cli/src/project/crud_create.rs +0 -0
  79. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/cli/src/project/crud_delete.rs +0 -0
  80. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/cli/src/project/crud_read.rs +0 -0
  81. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/cli/src/project/crud_update.rs +0 -0
  82. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/cli/src/project/deploy_target.rs +0 -0
  83. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/cli/src/project/deployment.rs +0 -0
  84. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/cli/src/project/mod.rs +0 -0
  85. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/cli/src/project/process.rs +0 -0
  86. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/cli/src/session_config.rs +0 -0
  87. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/cli/src/tenant/cli.rs +0 -0
  88. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/cli/src/tenant/mod.rs +0 -0
  89. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/cli/src/tenant/process.rs +0 -0
  90. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/cli/src/tenant/render.rs +0 -0
  91. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/cli/src/tenant/request.rs +0 -0
  92. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/cli/src/tenant/tenant_client.rs +0 -0
  93. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/cli/src/tenant/url_builder.rs +0 -0
  94. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/cli/src/token/clear_smb_token.rs +0 -0
  95. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/cli/src/token/get_smb_token.rs +0 -0
  96. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/cli/src/token/is_logged_in.rs +0 -0
  97. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/cli/src/token/mod.rs +0 -0
  98. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/cli/src/token/smb_token_file_path.rs +0 -0
  99. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/cli/src/ui/confirm_dialog.rs +0 -0
  100. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/cli/src/ui/deployment_detail_view.rs +0 -0
  101. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/cli/src/ui/deployment_table.rs +0 -0
  102. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/cli/src/ui/me_view.rs +0 -0
  103. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/cli/src/ui/mod.rs +0 -0
  104. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/cli/src/ui/plain.rs +0 -0
  105. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/cli/src/ui/project_detail_view.rs +0 -0
  106. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/cli/src/ui/project_table.rs +0 -0
  107. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/cli/src/ui/prompt.rs +0 -0
  108. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/cli/src/ui/reporter.rs +0 -0
  109. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/cli/src/ui/theme.rs +0 -0
  110. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-auth/.gitignore +0 -0
  111. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-auth/README.md +0 -0
  112. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-auth/src/check_email.rs +0 -0
  113. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-auth/src/lib.rs +0 -0
  114. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-auth/src/login.rs +0 -0
  115. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-auth/src/logout.rs +0 -0
  116. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-auth/src/me.rs +0 -0
  117. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-auth/src/oauth/get_account_status.rs +0 -0
  118. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-auth/src/oauth/get_consent_url.rs +0 -0
  119. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-auth/src/oauth/get_profile.rs +0 -0
  120. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-auth/src/oauth/get_token.rs +0 -0
  121. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-auth/src/oauth/mod.rs +0 -0
  122. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-auth/src/oauth/provider.rs +0 -0
  123. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-auth/src/remove.rs +0 -0
  124. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-auth/src/resend_email_verification.rs +0 -0
  125. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-auth/src/resend_reset_password_instruction.rs +0 -0
  126. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-auth/src/reset_password.rs +0 -0
  127. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-auth/src/signup.rs +0 -0
  128. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-deploy/README.md +0 -0
  129. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-deploy/src/build.rs +0 -0
  130. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-deploy/src/error.rs +0 -0
  131. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-deploy/src/known_hosts.rs +0 -0
  132. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-deploy/src/lib.rs +0 -0
  133. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-deploy/src/report.rs +0 -0
  134. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-deploy/src/runner.rs +0 -0
  135. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-deploy/src/transport.rs +0 -0
  136. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-mail/README.md +0 -0
  137. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-mail/src/mail_app.rs +0 -0
  138. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-mail/src/mail_inbox.rs +0 -0
  139. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-mail/src/mail_message.rs +0 -0
  140. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-mail/src/request.rs +0 -0
  141. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-mail/src/smbcloud_mail.rs +0 -0
  142. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-mail/src/url_builder.rs +0 -0
  143. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-model/README.md +0 -0
  144. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-model/src/account.rs +0 -0
  145. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-model/src/deploy_config_update.rs +0 -0
  146. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-model/src/error_codes.rs +0 -0
  147. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-model/src/forgot.rs +0 -0
  148. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-model/src/frontend_app.rs +0 -0
  149. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-model/src/lib.rs +0 -0
  150. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-model/src/login.rs +0 -0
  151. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-model/src/mail.rs +0 -0
  152. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-model/src/oauth.rs +0 -0
  153. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-model/src/repository.rs +0 -0
  154. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-model/src/reset_password_response.rs +0 -0
  155. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-model/src/runner.rs +0 -0
  156. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-model/src/signup.rs +0 -0
  157. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-model/src/tenant.rs +0 -0
  158. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-network/README.md +0 -0
  159. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-network/src/environment.rs +0 -0
  160. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-network/src/lib.rs +0 -0
  161. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-network/src/network.rs +0 -0
  162. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-networking/README.md +0 -0
  163. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-networking/src/constants.rs +0 -0
  164. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-networking/src/lib.rs +0 -0
  165. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-networking/src/smb_client.rs +0 -0
  166. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-networking-project/README.md +0 -0
  167. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-networking-project/src/crud_deploy_repo_create.rs +0 -0
  168. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-networking-project/src/crud_frontend_app_create.rs +0 -0
  169. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-networking-project/src/crud_frontend_app_deploy_config.rs +0 -0
  170. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-networking-project/src/crud_frontend_app_read.rs +0 -0
  171. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-networking-project/src/crud_frontend_app_update_deploy_config.rs +0 -0
  172. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-networking-project/src/crud_project_create.rs +0 -0
  173. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-networking-project/src/crud_project_delete.rs +0 -0
  174. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-networking-project/src/crud_project_deployment_create.rs +0 -0
  175. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-networking-project/src/crud_project_deployment_read.rs +0 -0
  176. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-networking-project/src/crud_project_deployment_update.rs +0 -0
  177. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-networking-project/src/crud_project_read.rs +0 -0
  178. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-networking-project/src/crud_project_update.rs +0 -0
  179. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-networking-project/src/lib.rs +0 -0
  180. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-networking-project/src/request.rs +0 -0
  181. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-networking-project/src/url_builder.rs +0 -0
  182. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-utils/README.md +0 -0
  183. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-utils/src/config.rs +0 -0
  184. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-utils/src/lib.rs +0 -0
  185. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/crates/smbcloud-utils/src/write_config.rs +0 -0
  186. {smbcloud_cli-0.4.12 → smbcloud_cli-0.5.0}/pyproject.toml +0 -0
@@ -2528,7 +2528,7 @@ checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90"
2528
2528
 
2529
2529
  [[package]]
2530
2530
  name = "smbcloud-auth"
2531
- version = "0.4.12"
2531
+ version = "0.5.0"
2532
2532
  dependencies = [
2533
2533
  "log",
2534
2534
  "reqwest",
@@ -2543,7 +2543,7 @@ dependencies = [
2543
2543
 
2544
2544
  [[package]]
2545
2545
  name = "smbcloud-auth-sdk"
2546
- version = "0.4.12"
2546
+ version = "0.5.0"
2547
2547
  dependencies = [
2548
2548
  "base64",
2549
2549
  "reqwest",
@@ -2558,7 +2558,7 @@ dependencies = [
2558
2558
 
2559
2559
  [[package]]
2560
2560
  name = "smbcloud-auth-sdk-py"
2561
- version = "0.4.12"
2561
+ version = "0.5.0"
2562
2562
  dependencies = [
2563
2563
  "pyo3",
2564
2564
  "pythonize",
@@ -2572,7 +2572,7 @@ dependencies = [
2572
2572
 
2573
2573
  [[package]]
2574
2574
  name = "smbcloud-auth-sdk-wasm"
2575
- version = "0.4.12"
2575
+ version = "0.5.0"
2576
2576
  dependencies = [
2577
2577
  "serde-wasm-bindgen",
2578
2578
  "smbcloud-auth-sdk",
@@ -2585,7 +2585,7 @@ dependencies = [
2585
2585
 
2586
2586
  [[package]]
2587
2587
  name = "smbcloud-cli"
2588
- version = "0.4.12"
2588
+ version = "0.5.0"
2589
2589
  dependencies = [
2590
2590
  "anyhow",
2591
2591
  "chrono",
@@ -2611,6 +2611,7 @@ dependencies = [
2611
2611
  "serde_repr",
2612
2612
  "smbcloud-auth",
2613
2613
  "smbcloud-deploy",
2614
+ "smbcloud-email-sdk",
2614
2615
  "smbcloud-mail",
2615
2616
  "smbcloud-model",
2616
2617
  "smbcloud-network",
@@ -2627,11 +2628,12 @@ dependencies = [
2627
2628
  "tracing-bunyan-formatter",
2628
2629
  "tracing-subscriber",
2629
2630
  "url-builder",
2631
+ "xcrs",
2630
2632
  ]
2631
2633
 
2632
2634
  [[package]]
2633
2635
  name = "smbcloud-deploy"
2634
- version = "0.4.12"
2636
+ version = "0.5.0"
2635
2637
  dependencies = [
2636
2638
  "anyhow",
2637
2639
  "smbcloud-model",
@@ -2643,7 +2645,7 @@ dependencies = [
2643
2645
 
2644
2646
  [[package]]
2645
2647
  name = "smbcloud-email-sdk"
2646
- version = "0.4.12"
2648
+ version = "0.5.0"
2647
2649
  dependencies = [
2648
2650
  "anyhow",
2649
2651
  "log",
@@ -2658,7 +2660,7 @@ dependencies = [
2658
2660
 
2659
2661
  [[package]]
2660
2662
  name = "smbcloud-gresiq"
2661
- version = "0.4.12"
2663
+ version = "0.5.0"
2662
2664
  dependencies = [
2663
2665
  "native-tls",
2664
2666
  "postgres-native-tls",
@@ -2672,7 +2674,7 @@ dependencies = [
2672
2674
 
2673
2675
  [[package]]
2674
2676
  name = "smbcloud-gresiq-sdk"
2675
- version = "0.4.12"
2677
+ version = "0.5.0"
2676
2678
  dependencies = [
2677
2679
  "anyhow",
2678
2680
  "log",
@@ -2686,7 +2688,7 @@ dependencies = [
2686
2688
 
2687
2689
  [[package]]
2688
2690
  name = "smbcloud-mail"
2689
- version = "0.4.12"
2691
+ version = "0.5.0"
2690
2692
  dependencies = [
2691
2693
  "reqwest",
2692
2694
  "serde",
@@ -2698,7 +2700,7 @@ dependencies = [
2698
2700
 
2699
2701
  [[package]]
2700
2702
  name = "smbcloud-model"
2701
- version = "0.4.12"
2703
+ version = "0.5.0"
2702
2704
  dependencies = [
2703
2705
  "chrono",
2704
2706
  "log",
@@ -2714,7 +2716,7 @@ dependencies = [
2714
2716
 
2715
2717
  [[package]]
2716
2718
  name = "smbcloud-network"
2717
- version = "0.4.12"
2719
+ version = "0.5.0"
2718
2720
  dependencies = [
2719
2721
  "clap 4.6.2",
2720
2722
  "log",
@@ -2727,7 +2729,7 @@ dependencies = [
2727
2729
 
2728
2730
  [[package]]
2729
2731
  name = "smbcloud-networking"
2730
- version = "0.4.12"
2732
+ version = "0.5.0"
2731
2733
  dependencies = [
2732
2734
  "smbcloud-network",
2733
2735
  "url-builder",
@@ -2735,7 +2737,7 @@ dependencies = [
2735
2737
 
2736
2738
  [[package]]
2737
2739
  name = "smbcloud-networking-project"
2738
- version = "0.4.12"
2740
+ version = "0.5.0"
2739
2741
  dependencies = [
2740
2742
  "anyhow",
2741
2743
  "async-trait",
@@ -2753,11 +2755,11 @@ dependencies = [
2753
2755
 
2754
2756
  [[package]]
2755
2757
  name = "smbcloud-s6n"
2756
- version = "0.4.12"
2758
+ version = "0.5.0"
2757
2759
 
2758
2760
  [[package]]
2759
2761
  name = "smbcloud-utils"
2760
- version = "0.4.12"
2762
+ version = "0.5.0"
2761
2763
  dependencies = [
2762
2764
  "anyhow",
2763
2765
  "dirs",
@@ -3993,6 +3995,22 @@ version = "0.6.3"
3993
3995
  source = "registry+https://github.com/rust-lang/crates.io-index"
3994
3996
  checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4"
3995
3997
 
3998
+ [[package]]
3999
+ name = "xcrs"
4000
+ version = "0.5.0"
4001
+ dependencies = [
4002
+ "anyhow",
4003
+ "base64",
4004
+ "clap 4.6.2",
4005
+ "reqwest",
4006
+ "rmcp",
4007
+ "schemars",
4008
+ "serde",
4009
+ "serde_json",
4010
+ "tempfile",
4011
+ "tokio",
4012
+ ]
4013
+
3996
4014
  [[package]]
3997
4015
  name = "yoke"
3998
4016
  version = "0.8.3"
@@ -1,10 +1,11 @@
1
1
  [workspace]
2
2
  resolver = "2"
3
- members = ["crates/cli", "crates/smbcloud-*"]
3
+ members = ["crates/cli", "crates/smbcloud-*", "crates/xcrs"]
4
4
 
5
5
  [workspace.dependencies]
6
6
  anyhow = "1.0.58"
7
7
  async-trait = "0.1.51"
8
+ base64 = "0.22"
8
9
  chrono = "0.4"
9
10
  clap = "4.1.6"
10
11
  clap_mangen = "0.2.10"
@@ -28,17 +29,17 @@ schemars = "1"
28
29
  serde = "1"
29
30
  serde_json = "1.0.82"
30
31
  serde_repr = "0.1"
31
- smbcloud-auth = { version = "0.4.9", path = "crates/smbcloud-auth" }
32
- smbcloud-auth-sdk = { version = "0.4.9", path = "crates/smbcloud-auth-sdk" }
33
- smbcloud-deploy = { version = "0.4.9", path = "crates/smbcloud-deploy" }
34
- smbcloud-email-sdk = { version = "0.4.9", path = "crates/smbcloud-email-sdk" }
35
- smbcloud-gresiq-sdk = { version = "0.4.9", path = "crates/smbcloud-gresiq-sdk" }
36
- smbcloud-mail = { version = "0.4.9", path = "crates/smbcloud-mail" }
37
- smbcloud-model = { version = "0.4.9", path = "crates/smbcloud-model" }
38
- smbcloud-network = { version = "0.4.9", path = "crates/smbcloud-network" }
39
- smbcloud-networking = { version = "0.4.9", path = "crates/smbcloud-networking" }
40
- smbcloud-networking-project = { version = "0.4.9", path = "crates/smbcloud-networking-project" }
41
- smbcloud-utils = { version = "0.4.9", path = "crates/smbcloud-utils" }
32
+ smbcloud-auth = { version = "0.5.0", path = "crates/smbcloud-auth" }
33
+ smbcloud-auth-sdk = { version = "0.5.0", path = "crates/smbcloud-auth-sdk" }
34
+ smbcloud-deploy = { version = "0.5.0", path = "crates/smbcloud-deploy" }
35
+ smbcloud-email-sdk = { version = "0.5.0", path = "crates/smbcloud-email-sdk" }
36
+ smbcloud-gresiq-sdk = { version = "0.5.0", path = "crates/smbcloud-gresiq-sdk" }
37
+ smbcloud-mail = { version = "0.5.0", path = "crates/smbcloud-mail" }
38
+ smbcloud-model = { version = "0.5.0", path = "crates/smbcloud-model" }
39
+ smbcloud-network = { version = "0.5.0", path = "crates/smbcloud-network" }
40
+ smbcloud-networking = { version = "0.5.0", path = "crates/smbcloud-networking" }
41
+ smbcloud-networking-project = { version = "0.5.0", path = "crates/smbcloud-networking-project" }
42
+ smbcloud-utils = { version = "0.5.0", path = "crates/smbcloud-utils" }
42
43
  spinners = "4.1.0"
43
44
  strum = "0.27"
44
45
  strum_macros = "0.27"
@@ -55,3 +56,4 @@ tsync = "2"
55
56
  uniffi = "0.31"
56
57
  url-builder = "0.1.1"
57
58
  wasm-bindgen = "0.2"
59
+ xcrs = { version = "0.5.0", path = "crates/xcrs" }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: smbcloud-cli
3
- Version: 0.4.12
3
+ Version: 0.5.0
4
4
  Classifier: Development Status :: 4 - Beta
5
5
  Classifier: Environment :: Console
6
6
  Classifier: Intended Audience :: Developers
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "smbcloud-cli"
3
- version = "0.4.12"
3
+ version = "0.5.0"
4
4
  edition = "2021"
5
5
  authors = ["Seto Elkahfi <seto@smbcloud.xyz>"]
6
6
  description = "smbCloud command line interface."
@@ -47,6 +47,7 @@ serde_json = { workspace = true }
47
47
  serde_repr = { workspace = true }
48
48
  smbcloud-auth = { workspace = true }
49
49
  smbcloud-deploy = { workspace = true }
50
+ smbcloud-email-sdk = { workspace = true }
50
51
  smbcloud-mail = { workspace = true }
51
52
  smbcloud-model = { workspace = true }
52
53
  smbcloud-network = { workspace = true }
@@ -63,6 +64,7 @@ tracing = { workspace = true, features = ["log"] }
63
64
  tracing-bunyan-formatter = { workspace = true }
64
65
  tracing-subscriber = { workspace = true, features = ["registry", "env-filter"] }
65
66
  url-builder = { workspace = true }
67
+ xcrs = { workspace = true }
66
68
 
67
69
  [target.'cfg(not(windows))'.dependencies]
68
70
  openssl = { workspace = true }
@@ -0,0 +1,210 @@
1
+ use {
2
+ crate::{account, cloud_auth, mail, project, tenant},
3
+ clap::{Parser, Subcommand, ValueEnum},
4
+ smbcloud_network::environment::Environment,
5
+ spinners::Spinner,
6
+ std::path::PathBuf,
7
+ };
8
+
9
+ pub struct CommandResult {
10
+ pub spinner: Spinner,
11
+ pub symbol: String,
12
+ pub msg: String,
13
+ }
14
+
15
+ impl CommandResult {
16
+ pub fn stop_and_persist(mut self) {
17
+ self.spinner.stop_and_persist(&self.symbol, self.msg);
18
+ }
19
+ }
20
+
21
+ #[derive(Debug, Clone, Copy, Default, PartialEq, Eq, ValueEnum)]
22
+ pub enum McpScope {
23
+ #[default]
24
+ Cloud,
25
+ Automation,
26
+ }
27
+
28
+ #[derive(Parser)]
29
+ #[clap(author, version, about)]
30
+ pub struct Cli {
31
+ /// Environment: dev, production
32
+ #[arg(short, long, env = "ENVIRONMENT", default_value = "production")]
33
+ pub environment: Environment,
34
+
35
+ /// Log level: trace, debug, info, warn, error, off
36
+ #[clap(short, long, global = true)]
37
+ pub log_level: Option<String>,
38
+
39
+ /// Non-interactive mode for CI/automation: disable prompts. Confirmations
40
+ /// use their default; prompts that need real input fail fast instead of
41
+ /// blocking. Also enabled by SMB_CI=1 or the conventional CI env var.
42
+ #[arg(long, global = true, env = "SMB_CI")]
43
+ pub ci: bool,
44
+
45
+ /// Full-screen TUI mode: render read commands in an interactive ratatui
46
+ /// view instead of plain text. Mutually exclusive with --mcp.
47
+ #[arg(long, global = true, conflicts_with = "mcp")]
48
+ pub tui: bool,
49
+
50
+ /// Run as an MCP (Model Context Protocol) server over stdio instead of a
51
+ /// one-shot command. Implies non-interactive; the subcommand is ignored.
52
+ #[arg(long, global = true)]
53
+ pub mcp: bool,
54
+
55
+ /// MCP tool profile to expose. Cloud serves smbCloud resources; automation
56
+ /// serves cross-platform mobile and TV device tools.
57
+ #[arg(
58
+ long = "scope",
59
+ global = true,
60
+ value_enum,
61
+ default_value_t,
62
+ requires = "mcp"
63
+ )]
64
+ pub mcp_scope: McpScope,
65
+
66
+ #[command(subcommand)]
67
+ pub command: Option<Commands>,
68
+ }
69
+
70
+ #[derive(Subcommand)]
71
+ pub enum Commands {
72
+ #[clap(about = "Your account info.", display_order = 3)]
73
+ Me {},
74
+ #[clap(
75
+ about = "Deploy project. This is smb main command. Requires an smbCloud account.",
76
+ display_order = 0
77
+ )]
78
+ Deploy {
79
+ /// Name of the sub-project to deploy (for monorepo configs with [[projects]]).
80
+ /// Matches the `name` field in .smb/config.toml. Omit to deploy the root project.
81
+ #[arg(short, long)]
82
+ project: Option<String>,
83
+ },
84
+ #[clap(
85
+ about = "Initialize project. Requires an smbCloud account.",
86
+ display_order = 1
87
+ )]
88
+ Init {},
89
+ #[clap(about = "Login to your account.", display_order = 2)]
90
+ Login {},
91
+ #[clap(about = "Logout from your account.", display_order = 3)]
92
+ Logout {},
93
+ #[clap(about = "Manage your account.")]
94
+ Account {
95
+ #[clap(subcommand)]
96
+ command: account::cli::Commands,
97
+ },
98
+ #[clap(about = "Manage your projects.")]
99
+ Project {
100
+ #[clap(subcommand)]
101
+ command: project::cli::Commands,
102
+ },
103
+ #[clap(about = "Manage your tenants.")]
104
+ Tenant {
105
+ #[clap(subcommand)]
106
+ command: tenant::cli::Commands,
107
+ },
108
+ #[clap(about = "Manage smbCloud Mail.")]
109
+ Mail {
110
+ #[clap(subcommand)]
111
+ command: mail::cli::Commands,
112
+ },
113
+ #[clap(about = "Manage smbCloud Auth apps.")]
114
+ Auth {
115
+ #[clap(subcommand)]
116
+ command: cloud_auth::cli::Commands,
117
+ },
118
+ #[clap(about = "Run and control XCRS ControlKit on Apple devices.")]
119
+ ControlKit {
120
+ #[clap(subcommand)]
121
+ command: ControlKitCommands,
122
+ },
123
+ #[clap(
124
+ about = "Migrate local .smb/config.toml deploy fields to the smbCloud server.",
125
+ display_order = 4
126
+ )]
127
+ Migrate {},
128
+ }
129
+
130
+ #[cfg(test)]
131
+ mod tests {
132
+ use super::*;
133
+
134
+ #[test]
135
+ fn mcp_defaults_to_cloud_scope() {
136
+ let cli = Cli::try_parse_from(["smb", "--mcp"]).expect("MCP arguments should parse");
137
+
138
+ assert_eq!(cli.mcp_scope, McpScope::Cloud);
139
+ }
140
+
141
+ #[test]
142
+ fn mcp_accepts_automation_scope() {
143
+ let cli = Cli::try_parse_from(["smb", "--mcp", "--scope", "automation"])
144
+ .expect("automation MCP arguments should parse");
145
+
146
+ assert_eq!(cli.mcp_scope, McpScope::Automation);
147
+ }
148
+
149
+ #[test]
150
+ fn scope_requires_mcp_mode() {
151
+ // `Cli` has no `Debug` impl (clap's `Parser` doesn't require one), so
152
+ // `expect_err`/`unwrap_err` (which bound the `Ok` type on `Debug`)
153
+ // don't apply here; match the `Result` instead.
154
+ let result = Cli::try_parse_from(["smb", "--scope", "automation"]);
155
+ let error = match result {
156
+ Err(error) => error,
157
+ Ok(_) => panic!("scope without MCP mode should be rejected"),
158
+ };
159
+
160
+ assert_eq!(
161
+ error.kind(),
162
+ clap::error::ErrorKind::MissingRequiredArgument
163
+ );
164
+ }
165
+ }
166
+
167
+ #[derive(Subcommand)]
168
+ pub enum ControlKitCommands {
169
+ #[clap(about = "Build a signed ControlKit XCTest runner for a physical device.")]
170
+ Build {
171
+ #[arg(long)]
172
+ project_path: PathBuf,
173
+ #[arg(long, default_value = "ControlKit")]
174
+ scheme: String,
175
+ #[arg(long, default_value = "Release")]
176
+ configuration: String,
177
+ #[arg(long)]
178
+ device_udid: String,
179
+ #[arg(long)]
180
+ derived_data_path: PathBuf,
181
+ },
182
+ #[clap(about = "Start a built ControlKit XCTest runner on a physical device.")]
183
+ Start {
184
+ #[arg(long)]
185
+ device_udid: String,
186
+ #[arg(long)]
187
+ xctestrun_path: PathBuf,
188
+ #[arg(long, default_value = "::")]
189
+ listen_host: String,
190
+ #[arg(long, default_value_t = 12004)]
191
+ listen_port: u16,
192
+ #[arg(long, default_value_t = 120)]
193
+ timeout_seconds: u64,
194
+ #[arg(long)]
195
+ log_path: Option<PathBuf>,
196
+ },
197
+ #[clap(about = "Call a running ControlKit JSON-RPC method.")]
198
+ Call {
199
+ #[arg(long, required_unless_present = "host", conflicts_with = "host")]
200
+ device_udid: Option<String>,
201
+ #[arg(long, conflicts_with = "device_udid")]
202
+ host: Option<String>,
203
+ #[arg(long, default_value_t = 12004)]
204
+ port: u16,
205
+ #[arg(long)]
206
+ method: String,
207
+ #[arg(long, default_value = "{}")]
208
+ params: String,
209
+ },
210
+ }
@@ -28,7 +28,9 @@ pub(crate) fn print_auth_apps(auth_apps: &[AuthApp]) {
28
28
  "#{} {} project={}",
29
29
  auth_app.id,
30
30
  style(&auth_app.name).bold(),
31
- auth_app.project_id.as_deref().unwrap_or("-")
31
+ auth_app
32
+ .project_id
33
+ .map_or_else(|| "-".to_string(), |id| id.to_string())
32
34
  );
33
35
  }
34
36
  }
@@ -37,7 +39,9 @@ pub(crate) fn print_auth_app_detail(auth_app: &AuthApp) {
37
39
  print_heading("Auth app");
38
40
  print_field("ID", &auth_app.id);
39
41
  print_field("Name", &auth_app.name);
40
- print_optional_field("Project ID", auth_app.project_id.as_deref());
42
+ if let Some(project_id) = auth_app.project_id {
43
+ print_field("Project ID", project_id.to_string());
44
+ }
41
45
  print_optional_field("Support email", auth_app.support_email.as_deref());
42
46
  print_optional_field("Secret", auth_app.secret.as_deref());
43
47
  print_field("Created at", auth_app.created_at.to_rfc3339());
@@ -124,6 +124,9 @@ pub(crate) async fn overlay_server_config(
124
124
  if let Some(remote_pm2_env) = deploy_config.pm2_env {
125
125
  config.project.pm2_env = Some(remote_pm2_env);
126
126
  }
127
+ if let Some(remote_process_manager) = deploy_config.process_manager {
128
+ config.project.process_manager = Some(remote_process_manager);
129
+ }
127
130
  if let Some(remote_port) = deploy_config.port {
128
131
  config.project.port = Some(remote_port);
129
132
  }
@@ -85,6 +85,83 @@ fn prune_dangling_symlinks(root: &std::path::Path) -> std::io::Result<usize> {
85
85
  Ok(removed)
86
86
  }
87
87
 
88
+ /// Build the shell snippet that (re)starts the app after the standalone bundle
89
+ /// is on the server.
90
+ ///
91
+ /// Two supervisors are supported, selected by `process_manager` (matched
92
+ /// case-insensitively):
93
+ /// - `None` / `"pm2"` → **pm2** (default): delete + start fresh, preferring an
94
+ /// operator-managed `ecosystem.config.cjs`/`.js`, else `pm2 start node --
95
+ /// server.js` with `PORT`/`HOSTNAME` inline.
96
+ /// - `"systemd"` → a **git-owned `systemctl --user` service** named
97
+ /// `<pm2_app>.service` (created out-of-band; see the smbcloud-deploy-nextjs
98
+ /// skill). Restarted with no sudo — the deploy SSHes as the git app-owner,
99
+ /// and `systemctl --user` (with `XDG_RUNTIME_DIR` set) drives that user's own
100
+ /// manager. Fails loudly if the unit does not exist.
101
+ ///
102
+ /// Any other value is a hard error rather than a silent fall back to pm2: on an
103
+ /// app already migrated to systemd, a typo like `system` would otherwise start
104
+ /// a second pm2 process that collides with the systemd-owned port — the exact
105
+ /// double-supervisor outage this option exists to prevent.
106
+ ///
107
+ /// The returned snippet references the shell var `$PM2_APP` (set earlier in the
108
+ /// deploy script). `port` is only used by the pm2 path.
109
+ fn restart_stanza(process_manager: Option<&str>, port: u16) -> Result<String> {
110
+ let use_systemd = match process_manager.map(str::trim) {
111
+ None | Some("") => false,
112
+ Some(m) if m.eq_ignore_ascii_case("pm2") => false,
113
+ Some(m) if m.eq_ignore_ascii_case("systemd") => true,
114
+ Some(other) => {
115
+ return Err(anyhow!(fail_message(&format!(
116
+ "Unknown process_manager '{other}' in .smb/config.toml. \
117
+ Expected \"pm2\" (default) or \"systemd\"."
118
+ ))))
119
+ }
120
+ };
121
+
122
+ Ok(if use_systemd {
123
+ r#" echo "Restarting $PM2_APP via systemd --user…"
124
+ export XDG_RUNTIME_DIR="/run/user/$(id -u)"
125
+ UNIT="$PM2_APP.service"
126
+ if ! systemctl --user cat "$UNIT" >/dev/null 2>&1; then
127
+ echo "Error: systemd --user unit $UNIT not found on the server."
128
+ echo "Create ~/.config/systemd/user/$UNIT and enable it (see the"
129
+ echo "smbcloud-deploy-nextjs skill, 'Process manager: systemd --user'),"
130
+ echo "or unset process_manager to stay on pm2. Then re-deploy."
131
+ exit 1
132
+ fi
133
+ systemctl --user restart "$UNIT"
134
+ sleep 3
135
+ if ! systemctl --user is-active "$UNIT" >/dev/null 2>&1; then
136
+ echo "Error: $UNIT failed to become active after restart."
137
+ systemctl --user status "$UNIT" --no-pager 2>&1 | head -20 || true
138
+ exit 1
139
+ fi
140
+ echo "Done."
141
+ "#
142
+ .to_string()
143
+ } else {
144
+ format!(
145
+ r#" echo "Starting $PM2_APP with pm2..."
146
+ if pm2 describe "$PM2_APP" > /dev/null 2>&1; then
147
+ pm2 delete "$PM2_APP"
148
+ fi
149
+
150
+ if [ -f ecosystem.config.cjs ]; then
151
+ pm2 start ecosystem.config.cjs --only "$PM2_APP" --env production
152
+ elif [ -f ecosystem.config.js ]; then
153
+ pm2 start ecosystem.config.js --only "$PM2_APP" --env production
154
+ else
155
+ NODE_ENV=production PORT={port} HOSTNAME=127.0.0.1 pm2 start node --name "$PM2_APP" -- server.js
156
+ fi
157
+
158
+ pm2 save
159
+ echo "Done."
160
+ "#
161
+ )
162
+ })
163
+ }
164
+
88
165
  /// Deploys a Next.js SSR app using standalone output mode.
89
166
  ///
90
167
  /// Requires `output: 'standalone'` in next.config.js. This produces a
@@ -99,7 +176,8 @@ fn prune_dangling_symlinks(root: &std::path::Path) -> std::io::Result<usize> {
99
176
  /// 4. rsync .next/standalone/ → server:path/ (server + bundled deps)
100
177
  /// 5. rsync .next/static/ → server:path/.next/static/ (static chunks)
101
178
  /// 6. rsync public/ → server:path/public/ (public assets)
102
- /// 7. SSH: pm2 delete + start fresh (prefer server `ecosystem.config.cjs` or `.js` if present)
179
+ /// 7. SSH: restart the app pm2 (default) or a git-owned systemd --user
180
+ /// service when `process_manager = "systemd"` (see `restart_stanza`)
103
181
  /// 8. PATCH deployment record as Done
104
182
  pub async fn process_deploy_nextjs_ssr(env: Environment, config: Config) -> Result<CommandResult> {
105
183
  let source = config.project.source.as_deref().unwrap_or(".");
@@ -530,6 +608,9 @@ pub async fn process_deploy_nextjs_ssr(env: Environment, config: Config) -> Resu
530
608
  )
531
609
  };
532
610
 
611
+ // Runtime supervisor: pm2 (default) or a git-owned systemd --user service.
612
+ let restart_stanza = restart_stanza(config.project.process_manager.as_deref(), port)?;
613
+
533
614
  let deploy_script = format!(
534
615
  r#"set -e
535
616
  APP_PATH="{remote_path}"
@@ -662,26 +743,11 @@ EOF
662
743
  ln -sfn ../../server.js .next/standalone/server.js
663
744
  fi
664
745
 
665
- echo "Starting $PM2_APP with pm2..."
666
- if pm2 describe "$PM2_APP" > /dev/null 2>&1; then
667
- pm2 delete "$PM2_APP"
668
- fi
669
-
670
- if [ -f ecosystem.config.cjs ]; then
671
- pm2 start ecosystem.config.cjs --only "$PM2_APP" --env production
672
- elif [ -f ecosystem.config.js ]; then
673
- pm2 start ecosystem.config.js --only "$PM2_APP" --env production
674
- else
675
- NODE_ENV=production PORT={port} HOSTNAME=127.0.0.1 pm2 start node --name "$PM2_APP" -- server.js
676
- fi
677
-
678
- pm2 save
679
- echo "Done."
680
- "#,
746
+ {restart} "#,
681
747
  remote_path = remote_path,
682
748
  pm2_app = pm2_app,
683
749
  runtime_subdir = runtime_subdir_for_shell,
684
- port = port,
750
+ restart = restart_stanza,
685
751
  ecosystem_config = ecosystem_config_content,
686
752
  );
687
753
 
@@ -828,7 +894,11 @@ async fn mark_failed(
828
894
 
829
895
  #[cfg(all(test, unix))]
830
896
  mod tests {
831
- use {super::prune_dangling_symlinks, std::os::unix::fs::symlink, tempfile::tempdir};
897
+ use {
898
+ super::{prune_dangling_symlinks, restart_stanza},
899
+ std::os::unix::fs::symlink,
900
+ tempfile::tempdir,
901
+ };
832
902
 
833
903
  /// Mirrors the real failure: a pnpm compat link points at a version dir that
834
904
  /// was never traced into the bundle, so it dangles and must be pruned, while
@@ -915,4 +985,40 @@ mod tests {
915
985
  "the dangling root symlink is gone"
916
986
  );
917
987
  }
988
+
989
+ #[test]
990
+ fn restart_stanza_defaults_to_pm2() {
991
+ for pm in [None, Some(""), Some("pm2"), Some("PM2"), Some(" pm2 ")] {
992
+ let s = restart_stanza(pm, 3025).expect("pm2 selector is valid");
993
+ assert!(s.contains("pm2 start"), "pm={pm:?} should use pm2");
994
+ assert!(s.contains("PORT=3025"), "pm={pm:?} injects the port");
995
+ assert!(!s.contains("systemctl"), "pm={pm:?} must not touch systemd");
996
+ }
997
+ }
998
+
999
+ /// An unrecognized supervisor must abort the deploy, not quietly run pm2
1000
+ /// alongside a systemd-owned process and fight over the port.
1001
+ #[test]
1002
+ fn restart_stanza_rejects_unknown_process_manager() {
1003
+ for pm in ["system", "systemctl", "supervisord", "PM3"] {
1004
+ let err = restart_stanza(Some(pm), 3025)
1005
+ .expect_err("unknown process_manager must be rejected");
1006
+ let msg = err.to_string();
1007
+ assert!(msg.contains(pm), "error names the offending value: {msg}");
1008
+ assert!(msg.contains("systemd"), "error lists valid options: {msg}");
1009
+ }
1010
+ }
1011
+
1012
+ #[test]
1013
+ fn restart_stanza_systemd_uses_user_unit_no_sudo() {
1014
+ let s = restart_stanza(Some("systemd"), 3025).expect("systemd selector is valid");
1015
+ assert!(s.contains("systemctl --user restart \"$UNIT\""));
1016
+ assert!(s.contains("UNIT=\"$PM2_APP.service\""));
1017
+ assert!(s.contains("XDG_RUNTIME_DIR=\"/run/user/$(id -u)\""));
1018
+ // No sudo (git drives its own user manager) and no pm2 fallback.
1019
+ assert!(!s.contains("sudo"), "must not require sudo");
1020
+ assert!(!s.contains("pm2 "), "systemd path must not call pm2");
1021
+ // Case-insensitive selector.
1022
+ assert_eq!(s, restart_stanza(Some("SystemD"), 3025).unwrap());
1023
+ }
918
1024
  }
@@ -197,6 +197,7 @@ fn strip_project(project: &Project) -> Project {
197
197
  package_manager: None,
198
198
  pm2_app: None,
199
199
  pm2_env: None,
200
+ process_manager: None,
200
201
  port: None,
201
202
  shared_lib: None,
202
203
  compile_cmd: None,