fdai-deployment-cli 0.1.2__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 (203) hide show
  1. fdai_deployment_cli/__about__.py +3 -0
  2. fdai_deployment_cli/__init__.py +5 -0
  3. fdai_deployment_cli/__main__.py +5 -0
  4. fdai_deployment_cli/acr_publish.py +585 -0
  5. fdai_deployment_cli/aks_catalog_review.py +137 -0
  6. fdai_deployment_cli/aks_historical_reconciliation.py +732 -0
  7. fdai_deployment_cli/aks_job_environment.py +59 -0
  8. fdai_deployment_cli/aks_job_execution.py +794 -0
  9. fdai_deployment_cli/aks_kubernetes_defaults.py +66 -0
  10. fdai_deployment_cli/aks_preflight.py +422 -0
  11. fdai_deployment_cli/aks_readiness.py +220 -0
  12. fdai_deployment_cli/aks_service_update.py +180 -0
  13. fdai_deployment_cli/aks_workload_jobs.py +281 -0
  14. fdai_deployment_cli/application_state_adoption.py +347 -0
  15. fdai_deployment_cli/azure_actor.py +61 -0
  16. fdai_deployment_cli/azure_naming.py +252 -0
  17. fdai_deployment_cli/azure_retail_prices.py +173 -0
  18. fdai_deployment_cli/bootstrap_approval.py +99 -0
  19. fdai_deployment_cli/bootstrap_channel.py +321 -0
  20. fdai_deployment_cli/bootstrap_executor.py +311 -0
  21. fdai_deployment_cli/bootstrap_plan.py +237 -0
  22. fdai_deployment_cli/bootstrap_reconcile.py +402 -0
  23. fdai_deployment_cli/bootstrap_support.py +138 -0
  24. fdai_deployment_cli/bootstrap_worker.py +448 -0
  25. fdai_deployment_cli/bundle.py +352 -0
  26. fdai_deployment_cli/capacity.py +143 -0
  27. fdai_deployment_cli/catalog_review_profile.py +306 -0
  28. fdai_deployment_cli/cli.py +798 -0
  29. fdai_deployment_cli/cli_help.py +214 -0
  30. fdai_deployment_cli/cli_parser.py +791 -0
  31. fdai_deployment_cli/cli_plan.py +450 -0
  32. fdai_deployment_cli/compiler.py +99 -0
  33. fdai_deployment_cli/console_artifact.py +218 -0
  34. fdai_deployment_cli/console_config.py +214 -0
  35. fdai_deployment_cli/console_update.py +832 -0
  36. fdai_deployment_cli/contracts.py +307 -0
  37. fdai_deployment_cli/control_package.py +140 -0
  38. fdai_deployment_cli/deadline_transport.py +70 -0
  39. fdai_deployment_cli/deployment_cost.py +131 -0
  40. fdai_deployment_cli/deployment_deadline.py +28 -0
  41. fdai_deployment_cli/deployment_kit.py +790 -0
  42. fdai_deployment_cli/deployment_kit_cache.py +191 -0
  43. fdai_deployment_cli/deployment_progress.py +648 -0
  44. fdai_deployment_cli/development_authority_cli.py +56 -0
  45. fdai_deployment_cli/development_authority_profile.py +319 -0
  46. fdai_deployment_cli/doctor.py +136 -0
  47. fdai_deployment_cli/entitlement_preflight.py +68 -0
  48. fdai_deployment_cli/entitlement_transport.py +49 -0
  49. fdai_deployment_cli/entra_bootstrap.py +579 -0
  50. fdai_deployment_cli/entra_control_profile.py +214 -0
  51. fdai_deployment_cli/entra_graph.py +349 -0
  52. fdai_deployment_cli/entra_profiles.py +215 -0
  53. fdai_deployment_cli/entra_source.py +244 -0
  54. fdai_deployment_cli/execution_bundle.py +478 -0
  55. fdai_deployment_cli/execution_copies.py +63 -0
  56. fdai_deployment_cli/foundation_adoption.py +438 -0
  57. fdai_deployment_cli/foundation_adoption_evidence.py +245 -0
  58. fdai_deployment_cli/foundation_adoption_host.py +157 -0
  59. fdai_deployment_cli/foundation_adoption_transport.py +51 -0
  60. fdai_deployment_cli/foundation_failure.py +72 -0
  61. fdai_deployment_cli/foundation_image.py +168 -0
  62. fdai_deployment_cli/foundation_input.py +235 -0
  63. fdai_deployment_cli/foundation_output.py +154 -0
  64. fdai_deployment_cli/foundation_plan.py +381 -0
  65. fdai_deployment_cli/foundation_process.py +64 -0
  66. fdai_deployment_cli/foundation_progress.py +231 -0
  67. fdai_deployment_cli/github_actions.py +171 -0
  68. fdai_deployment_cli/github_apply_artifact.py +259 -0
  69. fdai_deployment_cli/github_artifact_io.py +48 -0
  70. fdai_deployment_cli/github_dispatch.py +248 -0
  71. fdai_deployment_cli/github_plan_artifact.py +181 -0
  72. fdai_deployment_cli/github_provider_artifact.py +170 -0
  73. fdai_deployment_cli/github_transport.py +46 -0
  74. fdai_deployment_cli/github_workflow_values.py +312 -0
  75. fdai_deployment_cli/hub_image_deploy.py +354 -0
  76. fdai_deployment_cli/hub_image_stage.py +162 -0
  77. fdai_deployment_cli/hub_image_trust.py +281 -0
  78. fdai_deployment_cli/hub_ownership.py +161 -0
  79. fdai_deployment_cli/hub_workload_handoff.py +264 -0
  80. fdai_deployment_cli/hub_workload_handoff_inputs.py +227 -0
  81. fdai_deployment_cli/identity_profile.py +227 -0
  82. fdai_deployment_cli/image_targets.py +197 -0
  83. fdai_deployment_cli/installation_scope.py +280 -0
  84. fdai_deployment_cli/license.py +311 -0
  85. fdai_deployment_cli/license_issue.py +239 -0
  86. fdai_deployment_cli/lifecycle_authorization.py +252 -0
  87. fdai_deployment_cli/lifecycle_commands.py +173 -0
  88. fdai_deployment_cli/lifecycle_configuration.py +722 -0
  89. fdai_deployment_cli/lifecycle_configuration_artifact.py +45 -0
  90. fdai_deployment_cli/lifecycle_configuration_cli.py +324 -0
  91. fdai_deployment_cli/lifecycle_configuration_document.py +74 -0
  92. fdai_deployment_cli/lifecycle_configuration_entities.py +146 -0
  93. fdai_deployment_cli/lifecycle_configuration_package.py +492 -0
  94. fdai_deployment_cli/lifecycle_configuration_publication.py +277 -0
  95. fdai_deployment_cli/lifecycle_configuration_publication_cli.py +191 -0
  96. fdai_deployment_cli/lifecycle_configuration_sealing.py +127 -0
  97. fdai_deployment_cli/lifecycle_enrollment.py +45 -0
  98. fdai_deployment_cli/lifecycle_infrastructure_agent.py +174 -0
  99. fdai_deployment_cli/lifecycle_installation_binding.py +65 -0
  100. fdai_deployment_cli/lifecycle_native.py +247 -0
  101. fdai_deployment_cli/lifecycle_native_reconciliation.py +568 -0
  102. fdai_deployment_cli/lifecycle_native_transition.py +339 -0
  103. fdai_deployment_cli/lifecycle_ownership_exclusion.py +78 -0
  104. fdai_deployment_cli/lifecycle_plan.py +800 -0
  105. fdai_deployment_cli/lifecycle_promotion.py +185 -0
  106. fdai_deployment_cli/lifecycle_release.py +308 -0
  107. fdai_deployment_cli/lifecycle_self_upgrade.py +202 -0
  108. fdai_deployment_cli/lifecycle_upgrade_bundle.py +176 -0
  109. fdai_deployment_cli/notification_recipients_input.py +222 -0
  110. fdai_deployment_cli/observer_artifact.py +106 -0
  111. fdai_deployment_cli/oci_archive.py +353 -0
  112. fdai_deployment_cli/offline_kit.py +797 -0
  113. fdai_deployment_cli/offline_prepare.py +135 -0
  114. fdai_deployment_cli/operational_evidence_verifier_input.py +575 -0
  115. fdai_deployment_cli/plan_input.py +101 -0
  116. fdai_deployment_cli/private_output.py +192 -0
  117. fdai_deployment_cli/profile.py +70 -0
  118. fdai_deployment_cli/progress.py +183 -0
  119. fdai_deployment_cli/readiness.py +286 -0
  120. fdai_deployment_cli/run_command_receiver.py +418 -0
  121. fdai_deployment_cli/runtime_build.py +386 -0
  122. fdai_deployment_cli/runtime_profile.py +287 -0
  123. fdai_deployment_cli/runtime_release.py +745 -0
  124. fdai_deployment_cli/runtime_stage.py +82 -0
  125. fdai_deployment_cli/runtime_support_installation.py +215 -0
  126. fdai_deployment_cli/simulation.py +80 -0
  127. fdai_deployment_cli/source_application_inputs.py +397 -0
  128. fdai_deployment_cli/source_azure.py +586 -0
  129. fdai_deployment_cli/source_change_cli.py +107 -0
  130. fdai_deployment_cli/source_change_parser.py +75 -0
  131. fdai_deployment_cli/source_change_plan.py +181 -0
  132. fdai_deployment_cli/source_changed_update.py +196 -0
  133. fdai_deployment_cli/source_deploy.py +170 -0
  134. fdai_deployment_cli/source_foundation.py +174 -0
  135. fdai_deployment_cli/source_host_tools.py +163 -0
  136. fdai_deployment_cli/source_image_stage.py +400 -0
  137. fdai_deployment_cli/source_input.py +173 -0
  138. fdai_deployment_cli/source_projection.py +164 -0
  139. fdai_deployment_cli/source_receiver.py +76 -0
  140. fdai_deployment_cli/source_recovery.py +264 -0
  141. fdai_deployment_cli/source_runtime.py +81 -0
  142. fdai_deployment_cli/source_runtime_support.py +368 -0
  143. fdai_deployment_cli/source_service_update.py +726 -0
  144. fdai_deployment_cli/source_service_update_cli.py +47 -0
  145. fdai_deployment_cli/source_snapshot.py +178 -0
  146. fdai_deployment_cli/source_teardown.py +574 -0
  147. fdai_deployment_cli/source_terraform.py +85 -0
  148. fdai_deployment_cli/source_transport.py +399 -0
  149. fdai_deployment_cli/standalone_aks_inventory.py +558 -0
  150. fdai_deployment_cli/standalone_aks_job_execution.py +769 -0
  151. fdai_deployment_cli/standalone_aks_nodepool_guard.py +141 -0
  152. fdai_deployment_cli/standalone_application.py +977 -0
  153. fdai_deployment_cli/standalone_application_completion.py +151 -0
  154. fdai_deployment_cli/standalone_application_input_recovery.py +107 -0
  155. fdai_deployment_cli/standalone_application_recovery.py +116 -0
  156. fdai_deployment_cli/standalone_approval_input.py +24 -0
  157. fdai_deployment_cli/standalone_catalog_checkpoint.py +122 -0
  158. fdai_deployment_cli/standalone_catalog_review.py +251 -0
  159. fdai_deployment_cli/standalone_checkpoint_failure.py +127 -0
  160. fdai_deployment_cli/standalone_console_publish.py +174 -0
  161. fdai_deployment_cli/standalone_database_principals.py +41 -0
  162. fdai_deployment_cli/standalone_deploy.py +638 -0
  163. fdai_deployment_cli/standalone_development_authority.py +166 -0
  164. fdai_deployment_cli/standalone_foundation_adoption.py +87 -0
  165. fdai_deployment_cli/standalone_foundation_transition.py +767 -0
  166. fdai_deployment_cli/standalone_host.py +4608 -0
  167. fdai_deployment_cli/standalone_host_rebind.py +226 -0
  168. fdai_deployment_cli/standalone_host_residual.py +104 -0
  169. fdai_deployment_cli/standalone_host_state.py +145 -0
  170. fdai_deployment_cli/standalone_host_values.py +563 -0
  171. fdai_deployment_cli/standalone_kubernetes_tools.py +100 -0
  172. fdai_deployment_cli/standalone_license_installation.py +177 -0
  173. fdai_deployment_cli/standalone_management_egress.py +83 -0
  174. fdai_deployment_cli/standalone_migration_evidence.py +38 -0
  175. fdai_deployment_cli/standalone_operational_evidence.py +356 -0
  176. fdai_deployment_cli/standalone_planned_outputs.py +147 -0
  177. fdai_deployment_cli/standalone_private_access_probe.py +100 -0
  178. fdai_deployment_cli/standalone_product_profile.py +110 -0
  179. fdai_deployment_cli/standalone_remote_prepare.py +442 -0
  180. fdai_deployment_cli/standalone_residual_apply.py +540 -0
  181. fdai_deployment_cli/standalone_residual_successor.py +431 -0
  182. fdai_deployment_cli/standalone_review.py +266 -0
  183. fdai_deployment_cli/standalone_service_update_finalization.py +116 -0
  184. fdai_deployment_cli/standalone_service_update_recovery.py +277 -0
  185. fdai_deployment_cli/standalone_stage_targets.py +325 -0
  186. fdai_deployment_cli/standalone_status.py +67 -0
  187. fdai_deployment_cli/standalone_terraform_environment.py +237 -0
  188. fdai_deployment_cli/standalone_transfer_cleanup.py +237 -0
  189. fdai_deployment_cli/standalone_trial_activation.py +193 -0
  190. fdai_deployment_cli/state.py +434 -0
  191. fdai_deployment_cli/state_handoff.py +253 -0
  192. fdai_deployment_cli/status_projection.py +97 -0
  193. fdai_deployment_cli/support_install.py +257 -0
  194. fdai_deployment_cli/target.py +46 -0
  195. fdai_deployment_cli/trust/capabilities.json +62 -0
  196. fdai_deployment_cli/trust/deployment-bundle-root.pub +3 -0
  197. fdai_deployment_cli/trust/deployment-release-root.pub +3 -0
  198. fdai_deployment_cli/trust/upstream-signing-key.pub +3 -0
  199. fdai_deployment_cli/trust_roots.py +50 -0
  200. fdai_deployment_cli-0.1.2.dist-info/METADATA +112 -0
  201. fdai_deployment_cli-0.1.2.dist-info/RECORD +203 -0
  202. fdai_deployment_cli-0.1.2.dist-info/WHEEL +4 -0
  203. fdai_deployment_cli-0.1.2.dist-info/entry_points.txt +2 -0
@@ -0,0 +1,791 @@
1
+ """Declare the public CLI grammar without loading profiles or selecting a deployment target."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ from collections.abc import Callable, Mapping
7
+ from pathlib import Path
8
+
9
+ from fdai_service_contracts.product_profile import ObservationDataSource, ProductAddOn
10
+
11
+ from fdai_deployment_cli import source_change_parser
12
+ from fdai_deployment_cli.__about__ import __version__
13
+ from fdai_deployment_cli.aks_service_update import SERVICES as AKS_SERVICES
14
+ from fdai_deployment_cli.cli_help import (
15
+ AZURE_DESCRIPTION,
16
+ AZURE_EPILOG,
17
+ ROOT_DESCRIPTION,
18
+ ROOT_EPILOG,
19
+ HelpParser,
20
+ command,
21
+ command_group,
22
+ )
23
+ from fdai_deployment_cli.foundation_plan import register_foundation_plan_command
24
+ from fdai_deployment_cli.lifecycle_configuration_cli import register_configuration_commands
25
+ from fdai_deployment_cli.runtime_profile import DATABASE_SKUS
26
+ from fdai_deployment_cli.state_handoff import register_state_handoff_command
27
+
28
+ CommandHandler = Callable[[argparse.Namespace], int]
29
+
30
+
31
+ def build_parser(handlers: Mapping[str, CommandHandler]) -> argparse.ArgumentParser:
32
+ """Bind command handlers without calling them; empty groups show only their own help.
33
+
34
+ Leaf requirements, types, choices, and mutually exclusive sources remain argparse
35
+ contracts. Resolving the default work directory belongs to execution, not discovery.
36
+ """
37
+
38
+ parser = HelpParser(prog="fdaictl", description=ROOT_DESCRIPTION, epilog=ROOT_EPILOG)
39
+ parser.add_argument(
40
+ "--version", action="version", version=__version__, help="show the CLI version and exit"
41
+ )
42
+ subcommands = command_group(parser)
43
+ register_configuration_commands(subcommands)
44
+
45
+ version = command(subcommands, "version", "Show the installed CLI version")
46
+ version.add_argument("--output", choices=("text", "json"), default="text")
47
+ version.set_defaults(handler=handlers["version"])
48
+
49
+ doctor = command(subcommands, "doctor", "Check local tools and Azure authentication")
50
+ doctor.add_argument("--output", choices=("text", "json"), default="text")
51
+ doctor.set_defaults(handler=handlers["doctor"])
52
+
53
+ offline = command(
54
+ subcommands,
55
+ "offline",
56
+ "Prepare and verify local signed-kit artifacts",
57
+ epilog="Next: fdaictl offline prepare --help\nThese operations do not deploy Azure resources.",
58
+ )
59
+ offline_commands = command_group(offline)
60
+ prepare = command(
61
+ offline_commands, "prepare", "Verify a signed kit and create a private snapshot"
62
+ )
63
+ prepare.add_argument("--offline-kit", type=Path, required=True)
64
+ prepare.add_argument("--release-root", type=Path, required=True)
65
+ prepare.add_argument("--bundle-public-key", type=Path, required=True)
66
+ prepare.add_argument("--profile", type=Path, required=True)
67
+ prepare.add_argument("--source-commit", required=True)
68
+ prepare.add_argument("--work-dir", type=Path, required=True)
69
+ prepare.add_argument("--output", choices=("text", "json"), default="text")
70
+ prepare.set_defaults(handler=handlers["offline_prepare"])
71
+ configure = command(
72
+ offline_commands, "configure-console", "Write local Console configuration; do not publish"
73
+ )
74
+ configure.add_argument(
75
+ "--directory",
76
+ type=Path,
77
+ required=True,
78
+ help="Private prebuilt Console directory whose shipped configuration will be replaced",
79
+ )
80
+ configure.add_argument("--settings", type=Path, required=True)
81
+ configure.add_argument("--output", choices=("text", "json"), default="text")
82
+ configure.set_defaults(handler=handlers["offline_configure_console"])
83
+ install = command(
84
+ offline_commands, "install-support", "Install migration support from verified local wheels"
85
+ )
86
+ install.add_argument("--offline-kit", type=Path, required=True)
87
+ install.add_argument("--release-root", type=Path, required=True)
88
+ install.add_argument("--work-dir", type=Path, required=True)
89
+ install.add_argument("--output", choices=("text", "json"), default="text")
90
+ install.set_defaults(handler=handlers["offline_install_support"])
91
+
92
+ provision = command(
93
+ subcommands,
94
+ "provision",
95
+ "Prepare deployment inputs or deploy FDAI to Azure",
96
+ epilog=(
97
+ "Start here: fdaictl provision azure --help\n"
98
+ "Advanced profile and verification commands never replace exact approval."
99
+ ),
100
+ )
101
+ provision_commands = command_group(provision)
102
+ azure = command(
103
+ provision_commands,
104
+ "azure",
105
+ "Prepare source or deploy a signed kit with exact approvals",
106
+ description=AZURE_DESCRIPTION,
107
+ epilog=AZURE_EPILOG,
108
+ )
109
+ source_options = azure.add_argument_group("Artifact source (choose one)")
110
+ kit_source = source_options.add_mutually_exclusive_group(required=True)
111
+ kit_source.add_argument(
112
+ "--offline-kit",
113
+ type=Path,
114
+ metavar="PATH",
115
+ help="Use a local signed kit; no public artifact fallback",
116
+ )
117
+ kit_source.add_argument(
118
+ "--source",
119
+ type=Path,
120
+ metavar="PATH",
121
+ help="Select a clean source checkout for connected dev AKS deployment",
122
+ )
123
+ kit_source.add_argument(
124
+ "--hub",
125
+ type=Path,
126
+ metavar="PATH",
127
+ help="Select a clean checkout and import signed Release Hub images; never build services",
128
+ )
129
+ source_options.add_argument("--hub-url", help="Public HTTPS Release Hub origin")
130
+ source_options.add_argument(
131
+ "--hub-public-key",
132
+ type=Path,
133
+ help="Independently provisioned Ed25519 public PEM (or FDAI_HUB_PUBLIC_KEY path)",
134
+ )
135
+ source_options.add_argument(
136
+ "--hub-release", help="Exact signed Release in the selected channel"
137
+ )
138
+ source_options.add_argument("--hub-channel", help="Release channel (default: RELEASE)")
139
+ source_action = azure.add_mutually_exclusive_group()
140
+ source_action.add_argument(
141
+ "--prepare-only",
142
+ action="store_true",
143
+ help="Verify source and retain a private snapshot without Azure access (source mode only)",
144
+ )
145
+ source_action.add_argument(
146
+ "--preflight-only",
147
+ action="store_true",
148
+ help="Prepare source and read AKS SKU/quota feasibility without Azure mutation",
149
+ )
150
+ source_action.add_argument(
151
+ "--teardown",
152
+ action="store_true",
153
+ help="Remove only resources proven to belong to the retained source installation",
154
+ )
155
+ azure.add_argument(
156
+ "--teardown-confirmation",
157
+ metavar="TEXT",
158
+ help="Typed confirmation printed by --teardown review; performs the deletion",
159
+ )
160
+ azure.add_argument(
161
+ "--approval-file",
162
+ type=Path,
163
+ metavar="PATH",
164
+ help="Existing exact human checkpoint approval (source mode only); never prompt",
165
+ )
166
+ azure.add_argument(
167
+ "--foundation-recovery-directory",
168
+ type=Path,
169
+ metavar="PATH",
170
+ help="Resume an existing source run after separately verified Foundation recovery",
171
+ )
172
+ settings = azure.add_argument_group("Deployment settings")
173
+ settings.add_argument(
174
+ "--region", default="koreacentral", help="Azure deployment region (default: %(default)s)"
175
+ )
176
+ settings.add_argument(
177
+ "--runtime",
178
+ choices=("container-apps", "aks"),
179
+ default="aks",
180
+ help="Runtime platform for a new installation (default: %(default)s)",
181
+ )
182
+ settings.add_argument(
183
+ "--database",
184
+ choices=("postgres-flex", "postgres-aks"),
185
+ default="postgres-flex",
186
+ metavar="PLACEMENT",
187
+ help="PostgreSQL placement for a new installation (default: %(default)s)",
188
+ )
189
+ settings.add_argument(
190
+ "--system-nodes",
191
+ type=int,
192
+ default=3,
193
+ metavar="COUNT",
194
+ help="AKS system node count; minimum 2 (default: %(default)s)",
195
+ )
196
+ settings.add_argument(
197
+ "--system-node-sku",
198
+ default=None,
199
+ metavar="SKU",
200
+ help="AKS system node VM SKU (AKS default: Standard_D4ds_v4)",
201
+ )
202
+ settings.add_argument(
203
+ "--user-nodes",
204
+ type=int,
205
+ default=3,
206
+ metavar="COUNT",
207
+ help="AKS user node minimum; postgres-aks requires 4 (default: %(default)s)",
208
+ )
209
+ settings.add_argument(
210
+ "--max-user-nodes",
211
+ type=int,
212
+ default=5,
213
+ metavar="COUNT",
214
+ help="AKS user node autoscaler maximum (default: %(default)s)",
215
+ )
216
+ settings.add_argument(
217
+ "--user-node-sku",
218
+ default="Standard_D4ds_v4",
219
+ metavar="SKU",
220
+ help="AKS user node VM SKU (default: %(default)s)",
221
+ )
222
+ settings.add_argument(
223
+ "--database-sku",
224
+ default=None,
225
+ choices=DATABASE_SKUS,
226
+ metavar="SKU",
227
+ help="PostgreSQL Flexible Server SKU for postgres-flex (default: GP_Standard_D4ads_v5)",
228
+ )
229
+ settings.add_argument(
230
+ "--monthly-cost-ceiling",
231
+ type=int,
232
+ default=None,
233
+ metavar="USD",
234
+ help=(
235
+ "Monthly cost review input in USD (default: the retained run's value, "
236
+ "otherwise 1500); not a billing cap"
237
+ ),
238
+ )
239
+ settings.add_argument(
240
+ "--work-dir",
241
+ type=Path,
242
+ default=None,
243
+ metavar="PATH",
244
+ help="Private run directory (default: ~/.local/state/fdai/azure)",
245
+ )
246
+ settings.add_argument(
247
+ "--foundation-workload",
248
+ default="fdai",
249
+ metavar="TOKEN",
250
+ help=(
251
+ "Foundation naming token for a new source installation "
252
+ "(default: %(default)s); repeat unchanged on approval resume"
253
+ ),
254
+ )
255
+ settings.add_argument(
256
+ "--catalog-review-profile",
257
+ type=Path,
258
+ metavar="PATH",
259
+ help=(
260
+ "Private reviewed GitHub App profile; omit for an explicit skipped "
261
+ "catalog-review checkpoint"
262
+ ),
263
+ )
264
+ settings.add_argument(
265
+ "--evidence-verifier-input",
266
+ type=Path,
267
+ metavar="PATH",
268
+ dest="operational_evidence_verifier_input",
269
+ help=(
270
+ "Private reviewed verifier deployment input; omit to keep the independent "
271
+ "operational evidence verifier disabled"
272
+ ),
273
+ )
274
+ settings.add_argument(
275
+ "--notification-recipients",
276
+ type=Path,
277
+ metavar="PATH",
278
+ dest="notification_recipients",
279
+ help=(
280
+ 'Private JSON file {"recipients": ["name@domain"]} for the notifications add-on '
281
+ "on a source AKS installation; required with that add-on, never stored in the profile"
282
+ ),
283
+ )
284
+ settings.add_argument(
285
+ "--development-authority-profile",
286
+ type=Path,
287
+ metavar="PATH",
288
+ help=("Private reviewed full-authority development profile; source AKS deployment only"),
289
+ )
290
+ settings.add_argument(
291
+ "--clear-development-profile",
292
+ action="store_true",
293
+ dest="clear_development_authority_profile",
294
+ help="Explicitly remove a retained source-AKS development profile through a new plan",
295
+ )
296
+ settings.add_argument(
297
+ "--add-on",
298
+ action="append",
299
+ choices=tuple(item.value for item in ProductAddOn),
300
+ default=[],
301
+ dest="product_add_ons",
302
+ metavar="ADD_ON",
303
+ help=(
304
+ "Explicit optional product surface; repeat for multiple add-ons. "
305
+ "The observation-first default selects none"
306
+ ),
307
+ )
308
+ settings.add_argument(
309
+ "--observation-source",
310
+ action="append",
311
+ choices=tuple(item.value for item in ObservationDataSource),
312
+ default=[],
313
+ dest="observation_data_sources",
314
+ metavar="SOURCE",
315
+ help=("Optional observation source; its minimum read-only role is derived by the profile"),
316
+ )
317
+ initial = azure.add_argument_group("Initial source installation scope")
318
+ initial.add_argument(
319
+ "--setup-cost-ceiling",
320
+ type=int,
321
+ metavar="USD",
322
+ help="Setup cost estimate ceiling; requested at initial confirmation if omitted",
323
+ )
324
+ initial.add_argument(
325
+ "--console-access",
326
+ choices=("public-https-entra", "private-https-entra"),
327
+ default=None,
328
+ metavar="ACCESS",
329
+ help=(
330
+ "Console exposure for an explicitly selected read-only-console add-on; "
331
+ "the observation-first default is headless"
332
+ ),
333
+ )
334
+ initial.add_argument(
335
+ "--allow-dedicated-identities",
336
+ action="store_true",
337
+ help="Include dedicated identities and minimum role assignments in the initial scope",
338
+ )
339
+ initial.add_argument(
340
+ "--cleanup-temporary-resources",
341
+ action="store_true",
342
+ help="Include cleanup of this run's temporary resources; retain services and data",
343
+ )
344
+ settings.add_argument(
345
+ "--timeout-seconds",
346
+ type=int,
347
+ default=14_400,
348
+ metavar="SECONDS",
349
+ help=(
350
+ "Foundation supervision budget in seconds (default: 14400, 4 hours); "
351
+ "not a whole-deployment deadline"
352
+ ),
353
+ )
354
+ output = azure.add_argument_group("Output")
355
+ output.add_argument(
356
+ "--output",
357
+ choices=("text", "json"),
358
+ default="text",
359
+ help="Final result format (default: text); json disables progress",
360
+ )
361
+ output.add_argument(
362
+ "--progress",
363
+ choices=("auto", "stream", "plain", "off"),
364
+ default="auto",
365
+ help=(
366
+ "Live view, append-only color stream, plain logs, or off (default: auto); "
367
+ "source deployment maps auto to the installer stream; json disables it"
368
+ ),
369
+ )
370
+ advanced = azure.add_argument_group("Advanced (optional)")
371
+ advanced.add_argument(
372
+ "--control-package",
373
+ type=Path,
374
+ metavar="PATH",
375
+ help="Signed deployment-control wheelhouse for the managed host (--offline-kit only)",
376
+ )
377
+ advanced.add_argument(
378
+ "--trial-token",
379
+ type=Path,
380
+ metavar="PATH",
381
+ help="Private pre-issued capability-token file; never pass the token value",
382
+ )
383
+ adoption = azure.add_argument_group("Recovered public deployment (advanced)")
384
+ adoption.add_argument(
385
+ "--adopt-runner-image-receipt",
386
+ type=Path,
387
+ metavar="PATH",
388
+ help="Verified runner-image receipt to reuse without another image apply",
389
+ )
390
+ adoption.add_argument(
391
+ "--adopt-application-state",
392
+ type=Path,
393
+ metavar="PATH",
394
+ help="Recovered owner-only public-development Terraform state",
395
+ )
396
+ adoption.add_argument(
397
+ "--adopt-application-recovery",
398
+ type=Path,
399
+ metavar="PATH",
400
+ help="Verified contributor recovery receipt for the state",
401
+ )
402
+ adoption.add_argument(
403
+ "--adopt-resolved-models",
404
+ type=Path,
405
+ metavar="PATH",
406
+ help="Resolved model manifest paired with the recovered state",
407
+ )
408
+ foundation_adoption = azure.add_argument_group("Foundation adoption (advanced)")
409
+ foundation_adoption.add_argument(
410
+ "--adopt-foundation-directory",
411
+ type=Path,
412
+ metavar="PATH",
413
+ help="Retained source Foundation directory containing its profile and SSH key",
414
+ )
415
+ foundation_adoption.add_argument(
416
+ "--adopt-foundation-recovery",
417
+ type=Path,
418
+ metavar="PATH",
419
+ help="Verified recovery, enrollment, state, authority, and host-key evidence",
420
+ )
421
+ azure.set_defaults(handler=handlers["provision_azure"])
422
+
423
+ entra = command(
424
+ provision_commands,
425
+ "entra",
426
+ "Observe or apply only the tenant-local FDAI Entra configuration",
427
+ epilog=(
428
+ "Without --apply this command is read-only. Apply requires a current interactive "
429
+ "exact-plan approval and never runs Foundation or application deployment."
430
+ ),
431
+ )
432
+ entra.add_argument(
433
+ "--source",
434
+ type=Path,
435
+ required=True,
436
+ help="Clean source checkout containing the identity-only coordinator",
437
+ )
438
+ entra.add_argument(
439
+ "--work-dir",
440
+ type=Path,
441
+ required=True,
442
+ help="Absolute private directory for approval, claim, and receipt records",
443
+ )
444
+ entra.add_argument(
445
+ "--target-profile",
446
+ type=Path,
447
+ required=True,
448
+ help="Current-UID mode-0600 target and approved-executor profile",
449
+ )
450
+ entra.add_argument(
451
+ "--control-profile",
452
+ type=Path,
453
+ required=True,
454
+ help="Current-UID mode-0600 reviewed identity-control profile",
455
+ )
456
+ entra.add_argument(
457
+ "--apply",
458
+ action="store_true",
459
+ help="Apply the exact Entra app/group plan after current interactive approval",
460
+ )
461
+ entra.add_argument(
462
+ "--timeout-seconds",
463
+ type=int,
464
+ default=600,
465
+ help="Total operation deadline from 30 through 1800 seconds (default: %(default)s)",
466
+ )
467
+ entra.add_argument("--output", choices=("text", "json"), default="text")
468
+ entra.set_defaults(handler=handlers["provision_entra"])
469
+
470
+ source_change_parser.register(
471
+ provision_commands,
472
+ command=command,
473
+ handlers=handlers,
474
+ )
475
+
476
+ source_update = command(
477
+ provision_commands,
478
+ "source-service-update",
479
+ "Build and deploy one service from a clean source checkout",
480
+ )
481
+ source_update.add_argument("--source", type=Path, required=True)
482
+ source_update.add_argument("--service", choices=sorted(AKS_SERVICES), required=True)
483
+ source_update.add_argument("--application-work-dir", type=Path, required=True)
484
+ source_update.add_argument(
485
+ "--development-authority-profile",
486
+ type=Path,
487
+ help="Private reviewed profile for a Core update",
488
+ )
489
+ source_update.add_argument(
490
+ "--clear-development-profile",
491
+ action="store_true",
492
+ dest="clear_development_authority_profile",
493
+ help="Remove the retained profile during a Core update",
494
+ )
495
+ source_update.add_argument(
496
+ "--adopt-historical-binding",
497
+ type=Path,
498
+ help="Private execution binding for a retained AKS application baseline",
499
+ )
500
+ source_update.add_argument(
501
+ "--adopt-historical-state",
502
+ type=Path,
503
+ help="Retained Terraform state for the AKS application baseline",
504
+ )
505
+ source_update.add_argument(
506
+ "--adopt-historical-variables",
507
+ type=Path,
508
+ help="Retained workload variables paired with the Terraform state",
509
+ )
510
+ source_update.add_argument(
511
+ "--adopt-historical-live",
512
+ type=Path,
513
+ help="Retained live Deployment snapshot paired with the Terraform state",
514
+ )
515
+ source_update.add_argument(
516
+ "--adopt-historical-plan",
517
+ type=Path,
518
+ help="Retained exact Terraform plan for the historical deployment change",
519
+ )
520
+ source_update.add_argument("--work-dir", type=Path)
521
+ source_update.add_argument("--timeout-seconds", type=int, default=14400)
522
+ source_update.add_argument("--output", choices=("text", "json"), default="text")
523
+ source_update.set_defaults(handler=handlers["provision_source_service_update"])
524
+ console_update = command(
525
+ provision_commands,
526
+ "console-update",
527
+ "Plan or apply an exact existing-development Console update",
528
+ epilog="Create a private plan first. Apply requires its exact digest in a real terminal.",
529
+ )
530
+ console_update_commands = command_group(console_update)
531
+ console_build = command(
532
+ console_update_commands,
533
+ "build",
534
+ "Build a deterministic artifact from protected Git source without Azure access",
535
+ )
536
+ console_build.add_argument(
537
+ "--source", type=Path, required=True, help="Git checkout containing protected origin/main"
538
+ )
539
+ console_build.add_argument(
540
+ "--revision", default="HEAD", help="Protected origin/main revision or ancestor"
541
+ )
542
+ console_build.add_argument(
543
+ "--output-dir", type=Path, required=True, help="New private artifact directory"
544
+ )
545
+ console_build.add_argument(
546
+ "--timeout-seconds", type=int, default=900, help="Bound for the offline Console build"
547
+ )
548
+ console_build.add_argument(
549
+ "--output", choices=("text", "json"), default="text", help="Result format"
550
+ )
551
+ console_build.set_defaults(handler=handlers["provision_console_update_build"])
552
+ console_plan = command(
553
+ console_update_commands,
554
+ "plan",
555
+ "Seal candidate, rollback, source, and Azure target without publishing",
556
+ )
557
+ console_plan.add_argument(
558
+ "--source", type=Path, required=True, help="Clean checkout at protected origin/main"
559
+ )
560
+ console_plan.add_argument(
561
+ "--candidate-archive", type=Path, required=True, help="Candidate Console archive"
562
+ )
563
+ console_plan.add_argument(
564
+ "--candidate-manifest", type=Path, required=True, help="Candidate source manifest"
565
+ )
566
+ console_plan.add_argument(
567
+ "--rollback-archive", type=Path, required=True, help="Known-good rollback archive"
568
+ )
569
+ console_plan.add_argument(
570
+ "--rollback-manifest", type=Path, required=True, help="Rollback source manifest"
571
+ )
572
+ console_plan.add_argument(
573
+ "--target", type=Path, required=True, help="Mode-0600 existing Console target manifest"
574
+ )
575
+ console_plan.add_argument(
576
+ "--work-dir", type=Path, required=True, help="New private plan directory"
577
+ )
578
+ console_plan.add_argument(
579
+ "--ttl-seconds", type=int, default=1200, help="Plan validity in seconds, at most 1200"
580
+ )
581
+ console_plan.add_argument(
582
+ "--output", choices=("text", "json"), default="text", help="Result format"
583
+ )
584
+ console_plan.set_defaults(handler=handlers["provision_console_update_plan"])
585
+ console_apply = command(
586
+ console_update_commands,
587
+ "apply",
588
+ "Publish one explicitly invoked exact dev plan and verify readback",
589
+ epilog=(
590
+ "Invocation is explicit coding-session authorization; plan digests remain internally bound."
591
+ ),
592
+ )
593
+ console_apply.add_argument(
594
+ "--source", type=Path, required=True, help="Clean checkout at the planned source revision"
595
+ )
596
+ console_apply.add_argument(
597
+ "--work-dir", type=Path, required=True, help="Private directory containing the exact plan"
598
+ )
599
+ console_apply.add_argument(
600
+ "--timeout-seconds", type=int, default=900, help="Bound for publication and readback"
601
+ )
602
+ console_apply.add_argument(
603
+ "--output", choices=("text", "json"), default="text", help="Result format"
604
+ )
605
+ console_apply.set_defaults(handler=handlers["provision_console_update_apply"])
606
+ register_state_handoff_command(provision_commands)
607
+ register_foundation_plan_command(provision_commands)
608
+ initialize = command(provision_commands, "init", "Create a private manual deployment profile")
609
+ initialize.add_argument(
610
+ "--profile",
611
+ type=Path,
612
+ required=True,
613
+ help="Private profile file to create; replacement requires --force",
614
+ )
615
+ initialize.add_argument("--environment", choices=("dev", "staging", "prod"), required=True)
616
+ initialize.add_argument("--region", required=True)
617
+ initialize.add_argument("--target-binding", required=True)
618
+ initialize.add_argument("--connectivity", choices=("online", "offline"), required=True)
619
+ initialize.add_argument("--host", choices=("existing-host", "managed-vm"), required=True)
620
+ initialize.add_argument("--transport", choices=("manual",), required=True)
621
+ initialize.add_argument(
622
+ "--access-method",
623
+ choices=(
624
+ "internal_ssh",
625
+ "temporary_public_ssh",
626
+ "bastion",
627
+ "run_command",
628
+ ),
629
+ required=True,
630
+ )
631
+ initialize.add_argument("--approval-quorum", type=int, default=1)
632
+ initialize.add_argument("--monthly-cost-ceiling", type=int, default=0)
633
+ initialize.add_argument("--force", action="store_true")
634
+ initialize.add_argument("--output", choices=("text", "json"), default="text")
635
+ initialize.set_defaults(handler=handlers["provision_init"])
636
+
637
+ inspect = command(
638
+ provision_commands, "inspect", "Inspect a profile and local deployment prerequisites"
639
+ )
640
+ inspect.add_argument("--profile", type=Path, required=True)
641
+ inspect.add_argument("--output", choices=("text", "json"), default="text")
642
+ inspect.set_defaults(handler=handlers["provision_inspect"])
643
+
644
+ plan = command(
645
+ provision_commands, "plan", "Plan a verified Terraform root without applying changes"
646
+ )
647
+ plan_source = plan.add_mutually_exclusive_group(required=True)
648
+ plan_source.add_argument(
649
+ "--offline-kit",
650
+ type=Path,
651
+ help="Complete signed deployment kit; release and bundle keys required",
652
+ )
653
+ plan_source.add_argument(
654
+ "--source-snapshot", type=Path, help="Private prepared source snapshot (Foundation only)"
655
+ )
656
+ plan.add_argument(
657
+ "--source-snapshot-digest",
658
+ metavar="SHA256",
659
+ help="Independently retained source snapshot SHA-256",
660
+ )
661
+ plan.add_argument(
662
+ "--terraform",
663
+ type=Path,
664
+ help="Terraform executable matching the committed source toolchain",
665
+ )
666
+ plan.add_argument("--release-root", type=Path)
667
+ plan.add_argument("--bundle-public-key", type=Path)
668
+ plan.add_argument("--work-dir", type=Path, required=True)
669
+ plan.add_argument("--variables-file", type=Path, required=True)
670
+ plan.add_argument("--profile", type=Path, required=True)
671
+ plan.add_argument("--stage", choices=("platform", "foundation"), default="platform")
672
+ plan.add_argument(
673
+ "--save-plan",
674
+ action="store_true",
675
+ help="retain a private foundation plan and review digest; does not authorize apply",
676
+ )
677
+ plan.add_argument("--output", choices=("text", "json"), default="text")
678
+ plan.set_defaults(handler=handlers["provision_plan"])
679
+
680
+ bootstrap_reconcile = command(
681
+ provision_commands,
682
+ "bootstrap-reconcile",
683
+ "Read Azure Foundation state into an expiring plan",
684
+ )
685
+ bootstrap_reconcile.add_argument("--profile", type=Path, required=True)
686
+ bootstrap_reconcile.add_argument("--source-commit", required=True)
687
+ bootstrap_reconcile.add_argument("--ops-resource-group", required=True)
688
+ bootstrap_reconcile.add_argument("--app-resource-group", required=True)
689
+ bootstrap_reconcile.add_argument("--state-storage-account", required=True)
690
+ bootstrap_reconcile.add_argument("--output-plan", type=Path, required=True)
691
+ bootstrap_reconcile.add_argument("--ttl-seconds", type=int, default=3600)
692
+ bootstrap_reconcile.add_argument("--output", choices=("text", "json"), default="text")
693
+ bootstrap_reconcile.set_defaults(handler=handlers["provision_bootstrap_reconcile"])
694
+
695
+ bundle = command(
696
+ subcommands,
697
+ "bundle",
698
+ "Verify deployment-bundle signatures and integrity",
699
+ epilog="Next: fdaictl bundle verify --help",
700
+ )
701
+ bundle_commands = command_group(bundle)
702
+ bundle_verify = command(
703
+ bundle_commands, "verify", "Verify signature, compatibility, files, and digests"
704
+ )
705
+ bundle_verify.add_argument("--bundle", type=Path, required=True)
706
+ bundle_verify.add_argument("--public-key", type=Path, required=True)
707
+ bundle_verify.add_argument("--output", choices=("text", "json"), default="text")
708
+ bundle_verify.set_defaults(handler=handlers["bundle_verify"])
709
+
710
+ license_command = command(
711
+ subcommands,
712
+ "license",
713
+ "Inspect or seal existing licensing bytes without granting authority",
714
+ epilog="Next: fdaictl license inspect --help",
715
+ )
716
+ license_commands = command_group(license_command)
717
+ license_inspect = command(
718
+ license_commands, "inspect", "Verify a local token and report its declared capabilities"
719
+ )
720
+ license_inspect.add_argument("--token", type=Path, required=True)
721
+ license_inspect.add_argument("--public-key", type=Path, required=True)
722
+ license_inspect.add_argument("--image-digest", default=None)
723
+ license_inspect.add_argument("--tenant-binding", default=None)
724
+ license_inspect.add_argument("--installation-binding", default=None, metavar="DIGEST")
725
+ license_inspect.add_argument("--output", choices=("text", "json"), default="text")
726
+ license_inspect.set_defaults(handler=handlers["license_inspect"])
727
+ license_seal = command(
728
+ license_commands,
729
+ "seal-entitlement",
730
+ "Seal an existing token to an independently supplied installation recipient",
731
+ )
732
+ license_seal.add_argument(
733
+ "--token",
734
+ type=Path,
735
+ required=True,
736
+ help="Private file containing existing original token bytes",
737
+ )
738
+ license_seal.add_argument(
739
+ "--recipient-public-key",
740
+ type=Path,
741
+ required=True,
742
+ help="Private file containing the independently provisioned X25519 recipient public key",
743
+ )
744
+ license_seal.add_argument(
745
+ "--installation-binding",
746
+ required=True,
747
+ metavar="DIGEST",
748
+ help="Exact installation SHA-256 binding",
749
+ )
750
+ license_seal.add_argument(
751
+ "--deployment-binding",
752
+ required=True,
753
+ metavar="DIGEST",
754
+ help="Exact deployment SHA-256 binding",
755
+ )
756
+ license_seal.add_argument(
757
+ "--destination",
758
+ type=Path,
759
+ required=True,
760
+ help="New owner-only encrypted carrier output file",
761
+ )
762
+ license_seal.set_defaults(handler=handlers["license_seal_entitlement"])
763
+
764
+ onboard = command(
765
+ subcommands,
766
+ "onboard",
767
+ "Run a local simulation or read its journal",
768
+ epilog=(
769
+ "Simulation only: fdaictl onboard guided --help\n"
770
+ "For live deployment use fdaictl provision azure --help."
771
+ ),
772
+ )
773
+ onboard_commands = command_group(onboard)
774
+ guided = command(
775
+ onboard_commands,
776
+ "guided",
777
+ "Rehearse the stage graph; requires --simulate and never deploys",
778
+ )
779
+ guided.add_argument("--profile", type=Path, required=True)
780
+ guided.add_argument("--source-commit", required=True)
781
+ guided.add_argument("--run-id", required=True)
782
+ guided.add_argument("--journal", type=Path, required=True)
783
+ guided.add_argument("--simulate", action="store_true")
784
+ guided.add_argument("--interrupt-after", default=None)
785
+ guided.add_argument("--output", choices=("text", "json"), default="text")
786
+ guided.set_defaults(handler=handlers["onboard_guided"])
787
+ status = command(onboard_commands, "status", "Read progress from a local simulation journal")
788
+ status.add_argument("--journal", type=Path, required=True)
789
+ status.add_argument("--output", choices=("text", "json"), default="text")
790
+ status.set_defaults(handler=handlers["onboard_status"])
791
+ return parser