wxcli 1.2.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 (413) hide show
  1. wxcli/__init__.py +4 -0
  2. wxcli/_playbook/.claude/agents/migration-advisor.md +239 -0
  3. wxcli/_playbook/.claude/agents/wxc-calling-builder.md +946 -0
  4. wxcli/_playbook/.claude/rules/cleanup.md +29 -0
  5. wxcli/_playbook/.claude/rules/cucm-migration.md +58 -0
  6. wxcli/_playbook/.claude/rules/org-health.md +14 -0
  7. wxcli/_playbook/.claude/settings.json +8 -0
  8. wxcli/_playbook/.claude/skills/audit-compliance/SKILL.md +515 -0
  9. wxcli/_playbook/.claude/skills/call-control/SKILL.md +747 -0
  10. wxcli/_playbook/.claude/skills/configure-features/SKILL.md +623 -0
  11. wxcli/_playbook/.claude/skills/configure-routing/SKILL.md +627 -0
  12. wxcli/_playbook/.claude/skills/contact-center/SKILL.md +1181 -0
  13. wxcli/_playbook/.claude/skills/cucm-migrate/SKILL.md +820 -0
  14. wxcli/_playbook/.claude/skills/customer-assist/SKILL.md +443 -0
  15. wxcli/_playbook/.claude/skills/device-platform/SKILL.md +407 -0
  16. wxcli/_playbook/.claude/skills/manage-call-settings/SKILL.md +757 -0
  17. wxcli/_playbook/.claude/skills/manage-devices/SKILL.md +790 -0
  18. wxcli/_playbook/.claude/skills/manage-identity/SKILL.md +638 -0
  19. wxcli/_playbook/.claude/skills/manage-licensing/SKILL.md +528 -0
  20. wxcli/_playbook/.claude/skills/manage-meetings/SKILL.md +708 -0
  21. wxcli/_playbook/.claude/skills/messaging-bots/SKILL.md +365 -0
  22. wxcli/_playbook/.claude/skills/messaging-spaces/SKILL.md +520 -0
  23. wxcli/_playbook/.claude/skills/org-health/SKILL.md +159 -0
  24. wxcli/_playbook/.claude/skills/provision-calling/SKILL.md +421 -0
  25. wxcli/_playbook/.claude/skills/query-live/SKILL.md +183 -0
  26. wxcli/_playbook/.claude/skills/query-live/domains/call-flow-trace.md +223 -0
  27. wxcli/_playbook/.claude/skills/query-live/domains/call-history.md +44 -0
  28. wxcli/_playbook/.claude/skills/query-live/domains/features.md +169 -0
  29. wxcli/_playbook/.claude/skills/query-live/domains/numbers.md +93 -0
  30. wxcli/_playbook/.claude/skills/query-live/domains/people-and-settings.md +146 -0
  31. wxcli/_playbook/.claude/skills/query-live/domains/routing.md +124 -0
  32. wxcli/_playbook/.claude/skills/reporting/SKILL.md +501 -0
  33. wxcli/_playbook/.claude/skills/reporting/references/cdr-recipes.md +1628 -0
  34. wxcli/_playbook/.claude/skills/reporting-cc/SKILL.md +251 -0
  35. wxcli/_playbook/.claude/skills/reporting-meetings/SKILL.md +290 -0
  36. wxcli/_playbook/.claude/skills/teardown/SKILL.md +214 -0
  37. wxcli/_playbook/.claude/skills/video-mesh/SKILL.md +379 -0
  38. wxcli/_playbook/.claude/skills/wxc-calling-debug/SKILL.md +475 -0
  39. wxcli/_playbook/CLAUDE.md +373 -0
  40. wxcli/_playbook/docs/reference/CLAUDE.md +25 -0
  41. wxcli/_playbook/docs/reference/admin-apps-data.md +643 -0
  42. wxcli/_playbook/docs/reference/admin-audit-security.md +322 -0
  43. wxcli/_playbook/docs/reference/admin-hybrid.md +383 -0
  44. wxcli/_playbook/docs/reference/admin-identity-scim.md +1007 -0
  45. wxcli/_playbook/docs/reference/admin-licensing.md +414 -0
  46. wxcli/_playbook/docs/reference/admin-org-management.md +586 -0
  47. wxcli/_playbook/docs/reference/admin-partner.md +357 -0
  48. wxcli/_playbook/docs/reference/archive/wxc-sdk-patterns.md +1229 -0
  49. wxcli/_playbook/docs/reference/archive/wxcadm-advanced.md +835 -0
  50. wxcli/_playbook/docs/reference/archive/wxcadm-core.md +743 -0
  51. wxcli/_playbook/docs/reference/archive/wxcadm-devices-workspaces.md +1305 -0
  52. wxcli/_playbook/docs/reference/archive/wxcadm-features.md +849 -0
  53. wxcli/_playbook/docs/reference/archive/wxcadm-locations.md +938 -0
  54. wxcli/_playbook/docs/reference/archive/wxcadm-person.md +1255 -0
  55. wxcli/_playbook/docs/reference/archive/wxcadm-routing.md +1262 -0
  56. wxcli/_playbook/docs/reference/authentication.md +965 -0
  57. wxcli/_playbook/docs/reference/call-control.md +1205 -0
  58. wxcli/_playbook/docs/reference/call-features-additional.md +2541 -0
  59. wxcli/_playbook/docs/reference/call-features-major.md +1320 -0
  60. wxcli/_playbook/docs/reference/call-routing.md +2304 -0
  61. wxcli/_playbook/docs/reference/contact-center-analytics.md +1049 -0
  62. wxcli/_playbook/docs/reference/contact-center-core.md +1614 -0
  63. wxcli/_playbook/docs/reference/contact-center-journey.md +457 -0
  64. wxcli/_playbook/docs/reference/contact-center-routing.md +1138 -0
  65. wxcli/_playbook/docs/reference/devices-core.md +1869 -0
  66. wxcli/_playbook/docs/reference/devices-dect.md +1150 -0
  67. wxcli/_playbook/docs/reference/devices-platform.md +467 -0
  68. wxcli/_playbook/docs/reference/devices-workspaces.md +1426 -0
  69. wxcli/_playbook/docs/reference/emergency-services.md +1122 -0
  70. wxcli/_playbook/docs/reference/location-calling-core.md +2037 -0
  71. wxcli/_playbook/docs/reference/location-calling-media.md +1334 -0
  72. wxcli/_playbook/docs/reference/location-recording-advanced.md +1482 -0
  73. wxcli/_playbook/docs/reference/meetings-content.md +473 -0
  74. wxcli/_playbook/docs/reference/meetings-core.md +903 -0
  75. wxcli/_playbook/docs/reference/meetings-infrastructure.md +899 -0
  76. wxcli/_playbook/docs/reference/meetings-settings.md +850 -0
  77. wxcli/_playbook/docs/reference/messaging-bots.md +1091 -0
  78. wxcli/_playbook/docs/reference/messaging-spaces.md +695 -0
  79. wxcli/_playbook/docs/reference/person-call-settings-behavior.md +1639 -0
  80. wxcli/_playbook/docs/reference/person-call-settings-handling.md +1394 -0
  81. wxcli/_playbook/docs/reference/person-call-settings-media.md +1898 -0
  82. wxcli/_playbook/docs/reference/person-call-settings-permissions.md +1329 -0
  83. wxcli/_playbook/docs/reference/provisioning.md +1366 -0
  84. wxcli/_playbook/docs/reference/reporting-analytics.md +1318 -0
  85. wxcli/_playbook/docs/reference/self-service-call-settings.md +1020 -0
  86. wxcli/_playbook/docs/reference/virtual-lines.md +1132 -0
  87. wxcli/_playbook/docs/reference/webhooks-events.md +1444 -0
  88. wxcli/_playbook/docs/reference/wxcadm-xsi-realtime.md +953 -0
  89. wxcli/_version.py +24 -0
  90. wxcli/auth.py +137 -0
  91. wxcli/commands/__init__.py +0 -0
  92. wxcli/commands/_registry.py +181 -0
  93. wxcli/commands/activation_email.py +103 -0
  94. wxcli/commands/admin_recordings.py +585 -0
  95. wxcli/commands/analytics.py +105 -0
  96. wxcli/commands/announcement_playlists.py +219 -0
  97. wxcli/commands/announcements.py +519 -0
  98. wxcli/commands/archive_users.py +36 -0
  99. wxcli/commands/attachment_actions.py +60 -0
  100. wxcli/commands/audit_events.py +83 -0
  101. wxcli/commands/authorizations.py +113 -0
  102. wxcli/commands/auto_attendant.py +642 -0
  103. wxcli/commands/broadworks_billing_reports.py +126 -0
  104. wxcli/commands/broadworks_enterprises.py +160 -0
  105. wxcli/commands/broadworks_subscribers.py +319 -0
  106. wxcli/commands/broadworks_workspaces.py +108 -0
  107. wxcli/commands/call_controls.py +1304 -0
  108. wxcli/commands/call_park.py +494 -0
  109. wxcli/commands/call_pickup.py +229 -0
  110. wxcli/commands/call_queue.py +1878 -0
  111. wxcli/commands/call_recording.py +707 -0
  112. wxcli/commands/call_routing.py +1610 -0
  113. wxcli/commands/call_settings_for_me_phase_5.py +212 -0
  114. wxcli/commands/caller_reputation.py +169 -0
  115. wxcli/commands/calling_service.py +305 -0
  116. wxcli/commands/cc_address_book.py +770 -0
  117. wxcli/commands/cc_agent_greetings.py +242 -0
  118. wxcli/commands/cc_agent_summaries.py +39 -0
  119. wxcli/commands/cc_agent_wellbeing.py +216 -0
  120. wxcli/commands/cc_agents.py +425 -0
  121. wxcli/commands/cc_ai_assistant.py +55 -0
  122. wxcli/commands/cc_ai_feature.py +294 -0
  123. wxcli/commands/cc_audio_files.py +149 -0
  124. wxcli/commands/cc_auto_csat.py +311 -0
  125. wxcli/commands/cc_aux_code.py +458 -0
  126. wxcli/commands/cc_business_hour.py +353 -0
  127. wxcli/commands/cc_call_monitoring.py +228 -0
  128. wxcli/commands/cc_callbacks.py +194 -0
  129. wxcli/commands/cc_campaign.py +212 -0
  130. wxcli/commands/cc_captures.py +39 -0
  131. wxcli/commands/cc_contact_list.py +169 -0
  132. wxcli/commands/cc_contact_number.py +295 -0
  133. wxcli/commands/cc_data_sources.py +235 -0
  134. wxcli/commands/cc_desktop_layout.py +380 -0
  135. wxcli/commands/cc_desktop_profile.py +322 -0
  136. wxcli/commands/cc_dial_number.py +511 -0
  137. wxcli/commands/cc_dial_plan.py +347 -0
  138. wxcli/commands/cc_dnc.py +101 -0
  139. wxcli/commands/cc_entry_point.py +545 -0
  140. wxcli/commands/cc_ewt.py +51 -0
  141. wxcli/commands/cc_flow.py +477 -0
  142. wxcli/commands/cc_global_vars.py +430 -0
  143. wxcli/commands/cc_holiday_list.py +335 -0
  144. wxcli/commands/cc_journey.py +1261 -0
  145. wxcli/commands/cc_legacy_flows.py +49 -0
  146. wxcli/commands/cc_multimedia_profile.py +436 -0
  147. wxcli/commands/cc_notification.py +51 -0
  148. wxcli/commands/cc_outdial_ani.py +604 -0
  149. wxcli/commands/cc_overrides.py +341 -0
  150. wxcli/commands/cc_queue.py +1083 -0
  151. wxcli/commands/cc_queue_stats.py +55 -0
  152. wxcli/commands/cc_realtime.py +51 -0
  153. wxcli/commands/cc_resource_collection.py +312 -0
  154. wxcli/commands/cc_search.py +46 -0
  155. wxcli/commands/cc_site.py +400 -0
  156. wxcli/commands/cc_skill.py +350 -0
  157. wxcli/commands/cc_skill_profile.py +289 -0
  158. wxcli/commands/cc_subscriptions.py +426 -0
  159. wxcli/commands/cc_summaries.py +130 -0
  160. wxcli/commands/cc_tasks.py +844 -0
  161. wxcli/commands/cc_team.py +325 -0
  162. wxcli/commands/cc_user_profiles.py +349 -0
  163. wxcli/commands/cc_users.py +701 -0
  164. wxcli/commands/cc_work_types.py +363 -0
  165. wxcli/commands/cdr.py +89 -0
  166. wxcli/commands/classifications.py +38 -0
  167. wxcli/commands/cleanup.py +1427 -0
  168. wxcli/commands/client_settings.py +72 -0
  169. wxcli/commands/conference.py +328 -0
  170. wxcli/commands/configure.py +72 -0
  171. wxcli/commands/converged_recordings.py +347 -0
  172. wxcli/commands/converged_recordings_export.py +310 -0
  173. wxcli/commands/cq_playlists.py +42 -0
  174. wxcli/commands/cucm.py +2794 -0
  175. wxcli/commands/cucm_config.py +129 -0
  176. wxcli/commands/customer_assist.py +373 -0
  177. wxcli/commands/data_sources.py +227 -0
  178. wxcli/commands/dect_devices.py +787 -0
  179. wxcli/commands/device_configurations.py +77 -0
  180. wxcli/commands/device_dynamic_settings.py +320 -0
  181. wxcli/commands/device_settings.py +1489 -0
  182. wxcli/commands/devices.py +280 -0
  183. wxcli/commands/domains.py +142 -0
  184. wxcli/commands/ecm.py +178 -0
  185. wxcli/commands/emergency_services.py +827 -0
  186. wxcli/commands/events.py +83 -0
  187. wxcli/commands/external_voicemail.py +52 -0
  188. wxcli/commands/groups.py +206 -0
  189. wxcli/commands/guest_management.py +76 -0
  190. wxcli/commands/hds.py +340 -0
  191. wxcli/commands/hot_desk.py +67 -0
  192. wxcli/commands/hot_desking_members.py +118 -0
  193. wxcli/commands/hot_desking_portal.py +124 -0
  194. wxcli/commands/hunt_group.py +577 -0
  195. wxcli/commands/hybrid_clusters.py +71 -0
  196. wxcli/commands/hybrid_connectors.py +67 -0
  197. wxcli/commands/identity_org.py +159 -0
  198. wxcli/commands/init_playbook.py +155 -0
  199. wxcli/commands/licenses.py +106 -0
  200. wxcli/commands/live_monitoring.py +40 -0
  201. wxcli/commands/location_call_handling.py +577 -0
  202. wxcli/commands/location_schedules.py +341 -0
  203. wxcli/commands/location_settings.py +1297 -0
  204. wxcli/commands/location_voicemail.py +494 -0
  205. wxcli/commands/locations.py +336 -0
  206. wxcli/commands/meeting_captions.py +110 -0
  207. wxcli/commands/meeting_chats.py +63 -0
  208. wxcli/commands/meeting_invitees.py +234 -0
  209. wxcli/commands/meeting_messages.py +28 -0
  210. wxcli/commands/meeting_participants.py +278 -0
  211. wxcli/commands/meeting_polls.py +111 -0
  212. wxcli/commands/meeting_preferences.py +515 -0
  213. wxcli/commands/meeting_qa.py +78 -0
  214. wxcli/commands/meeting_qualities.py +43 -0
  215. wxcli/commands/meeting_reports.py +101 -0
  216. wxcli/commands/meeting_session_types.py +105 -0
  217. wxcli/commands/meeting_site.py +57 -0
  218. wxcli/commands/meeting_slido.py +44 -0
  219. wxcli/commands/meeting_summaries.py +92 -0
  220. wxcli/commands/meeting_tracking_codes.py +233 -0
  221. wxcli/commands/meeting_transcripts.py +230 -0
  222. wxcli/commands/meetings.py +1997 -0
  223. wxcli/commands/memberships.py +164 -0
  224. wxcli/commands/messages.py +213 -0
  225. wxcli/commands/mode_management.py +244 -0
  226. wxcli/commands/my_call_settings.py +3577 -0
  227. wxcli/commands/numbers.py +405 -0
  228. wxcli/commands/operating_modes.py +410 -0
  229. wxcli/commands/org_contacts.py +284 -0
  230. wxcli/commands/org_health_cli.py +55 -0
  231. wxcli/commands/org_settings.py +71 -0
  232. wxcli/commands/organizations.py +83 -0
  233. wxcli/commands/paging_group.py +257 -0
  234. wxcli/commands/partner_admins.py +141 -0
  235. wxcli/commands/partner_reports.py +188 -0
  236. wxcli/commands/partner_tags.py +216 -0
  237. wxcli/commands/people.py +279 -0
  238. wxcli/commands/person_call_settings.py +71 -0
  239. wxcli/commands/pstn.py +280 -0
  240. wxcli/commands/recording_report.py +154 -0
  241. wxcli/commands/report_templates.py +38 -0
  242. wxcli/commands/reports.py +144 -0
  243. wxcli/commands/resource_group_memberships.py +160 -0
  244. wxcli/commands/resource_groups.py +67 -0
  245. wxcli/commands/roles.py +63 -0
  246. wxcli/commands/room_tabs.py +160 -0
  247. wxcli/commands/rooms.py +225 -0
  248. wxcli/commands/scim_bulk.py +46 -0
  249. wxcli/commands/scim_groups.py +246 -0
  250. wxcli/commands/scim_schemas.py +82 -0
  251. wxcli/commands/scim_users.py +283 -0
  252. wxcli/commands/security_audit.py +54 -0
  253. wxcli/commands/service_apps.py +51 -0
  254. wxcli/commands/single_number_reach.py +209 -0
  255. wxcli/commands/team_memberships.py +155 -0
  256. wxcli/commands/teams.py +153 -0
  257. wxcli/commands/ucm_profile.py +42 -0
  258. wxcli/commands/update.py +219 -0
  259. wxcli/commands/user_settings.py +5116 -0
  260. wxcli/commands/video_mesh.py +1090 -0
  261. wxcli/commands/virtual_extensions.py +502 -0
  262. wxcli/commands/virtual_line_settings.py +2072 -0
  263. wxcli/commands/webhooks.py +176 -0
  264. wxcli/commands/wholesale_billing_reports.py +147 -0
  265. wxcli/commands/wholesale_provisioning.py +523 -0
  266. wxcli/commands/workspace_locations.py +339 -0
  267. wxcli/commands/workspace_metrics.py +103 -0
  268. wxcli/commands/workspace_personalization.py +70 -0
  269. wxcli/commands/workspace_settings.py +3347 -0
  270. wxcli/commands/workspaces.py +279 -0
  271. wxcli/commands/xapi.py +114 -0
  272. wxcli/config.py +139 -0
  273. wxcli/errors.py +77 -0
  274. wxcli/main.py +210 -0
  275. wxcli/migration/CLAUDE.md +106 -0
  276. wxcli/migration/__init__.py +1 -0
  277. wxcli/migration/advisory/CLAUDE.md +196 -0
  278. wxcli/migration/advisory/__init__.py +46 -0
  279. wxcli/migration/advisory/advisor.py +107 -0
  280. wxcli/migration/advisory/advisory_patterns.py +2552 -0
  281. wxcli/migration/advisory/recommendation_rules.py +835 -0
  282. wxcli/migration/cucm/CLAUDE.md +74 -0
  283. wxcli/migration/cucm/__init__.py +5 -0
  284. wxcli/migration/cucm/connection.py +265 -0
  285. wxcli/migration/cucm/discovery.py +225 -0
  286. wxcli/migration/cucm/extractors/__init__.py +1 -0
  287. wxcli/migration/cucm/extractors/announcements.py +92 -0
  288. wxcli/migration/cucm/extractors/base.py +90 -0
  289. wxcli/migration/cucm/extractors/device_profiles.py +98 -0
  290. wxcli/migration/cucm/extractors/devices.py +193 -0
  291. wxcli/migration/cucm/extractors/e911.py +103 -0
  292. wxcli/migration/cucm/extractors/features.py +309 -0
  293. wxcli/migration/cucm/extractors/helpers.py +73 -0
  294. wxcli/migration/cucm/extractors/informational.py +295 -0
  295. wxcli/migration/cucm/extractors/locations.py +221 -0
  296. wxcli/migration/cucm/extractors/moh.py +100 -0
  297. wxcli/migration/cucm/extractors/remote_destinations.py +96 -0
  298. wxcli/migration/cucm/extractors/routing.py +493 -0
  299. wxcli/migration/cucm/extractors/shared_lines.py +146 -0
  300. wxcli/migration/cucm/extractors/templates.py +229 -0
  301. wxcli/migration/cucm/extractors/tier4.py +173 -0
  302. wxcli/migration/cucm/extractors/users.py +240 -0
  303. wxcli/migration/cucm/extractors/voicemail.py +243 -0
  304. wxcli/migration/cucm/extractors/workspaces.py +88 -0
  305. wxcli/migration/cucm/unity_connection.py +304 -0
  306. wxcli/migration/execute/CLAUDE.md +372 -0
  307. wxcli/migration/execute/__init__.py +303 -0
  308. wxcli/migration/execute/batch.py +277 -0
  309. wxcli/migration/execute/dependency.py +483 -0
  310. wxcli/migration/execute/engine.py +964 -0
  311. wxcli/migration/execute/handlers.py +2232 -0
  312. wxcli/migration/execute/planner.py +1939 -0
  313. wxcli/migration/execute/runtime.py +571 -0
  314. wxcli/migration/export/CLAUDE.md +49 -0
  315. wxcli/migration/export/__init__.py +8 -0
  316. wxcli/migration/export/csv_export.py +126 -0
  317. wxcli/migration/export/deployment_plan.py +479 -0
  318. wxcli/migration/export/json_export.py +60 -0
  319. wxcli/migration/models.py +941 -0
  320. wxcli/migration/phone_models.py +50 -0
  321. wxcli/migration/preflight/CLAUDE.md +52 -0
  322. wxcli/migration/preflight/__init__.py +89 -0
  323. wxcli/migration/preflight/checks.py +911 -0
  324. wxcli/migration/preflight/runner.py +293 -0
  325. wxcli/migration/rate_limiter.py +144 -0
  326. wxcli/migration/report/CLAUDE.md +245 -0
  327. wxcli/migration/report/__init__.py +7 -0
  328. wxcli/migration/report/appendix.py +2437 -0
  329. wxcli/migration/report/assembler.py +183 -0
  330. wxcli/migration/report/charts.py +263 -0
  331. wxcli/migration/report/executive.py +776 -0
  332. wxcli/migration/report/explainer.py +704 -0
  333. wxcli/migration/report/helpers.py +74 -0
  334. wxcli/migration/report/ingest.py +152 -0
  335. wxcli/migration/report/notice_templates.py +192 -0
  336. wxcli/migration/report/score.py +375 -0
  337. wxcli/migration/report/styles.py +1052 -0
  338. wxcli/migration/report/user_diff.py +1052 -0
  339. wxcli/migration/report/user_notice.py +425 -0
  340. wxcli/migration/state.py +139 -0
  341. wxcli/migration/store.py +860 -0
  342. wxcli/migration/transform/CLAUDE.md +107 -0
  343. wxcli/migration/transform/__init__.py +12 -0
  344. wxcli/migration/transform/analysis_pipeline.py +453 -0
  345. wxcli/migration/transform/analyzers/__init__.py +142 -0
  346. wxcli/migration/transform/analyzers/css_permission.py +189 -0
  347. wxcli/migration/transform/analyzers/css_routing.py +335 -0
  348. wxcli/migration/transform/analyzers/device_compatibility.py +122 -0
  349. wxcli/migration/transform/analyzers/dn_ambiguity.py +131 -0
  350. wxcli/migration/transform/analyzers/duplicate_user.py +221 -0
  351. wxcli/migration/transform/analyzers/extension_conflict.py +173 -0
  352. wxcli/migration/transform/analyzers/feature_approximation.py +227 -0
  353. wxcli/migration/transform/analyzers/layout_overflow.py +174 -0
  354. wxcli/migration/transform/analyzers/location_ambiguity.py +184 -0
  355. wxcli/migration/transform/analyzers/missing_data.py +300 -0
  356. wxcli/migration/transform/analyzers/selective_call_handling.py +394 -0
  357. wxcli/migration/transform/analyzers/shared_line.py +170 -0
  358. wxcli/migration/transform/analyzers/voicemail_compatibility.py +267 -0
  359. wxcli/migration/transform/analyzers/workspace_license.py +212 -0
  360. wxcli/migration/transform/cross_reference.py +1136 -0
  361. wxcli/migration/transform/cucm_pattern.py +246 -0
  362. wxcli/migration/transform/decisions.py +301 -0
  363. wxcli/migration/transform/e164.py +145 -0
  364. wxcli/migration/transform/engine.py +256 -0
  365. wxcli/migration/transform/mappers/CLAUDE.md +123 -0
  366. wxcli/migration/transform/mappers/__init__.py +76 -0
  367. wxcli/migration/transform/mappers/announcement_mapper.py +122 -0
  368. wxcli/migration/transform/mappers/base.py +190 -0
  369. wxcli/migration/transform/mappers/button_template_mapper.py +186 -0
  370. wxcli/migration/transform/mappers/call_forwarding_mapper.py +228 -0
  371. wxcli/migration/transform/mappers/call_settings_mapper.py +144 -0
  372. wxcli/migration/transform/mappers/css_mapper.py +868 -0
  373. wxcli/migration/transform/mappers/dect_mapper.py +367 -0
  374. wxcli/migration/transform/mappers/device_layout_mapper.py +289 -0
  375. wxcli/migration/transform/mappers/device_mapper.py +227 -0
  376. wxcli/migration/transform/mappers/device_profile_mapper.py +190 -0
  377. wxcli/migration/transform/mappers/device_settings_mapper.py +464 -0
  378. wxcli/migration/transform/mappers/e911_mapper.py +129 -0
  379. wxcli/migration/transform/mappers/ecbn_mapper.py +263 -0
  380. wxcli/migration/transform/mappers/executive_assistant_mapper.py +156 -0
  381. wxcli/migration/transform/mappers/feature_mapper.py +1179 -0
  382. wxcli/migration/transform/mappers/line_mapper.py +310 -0
  383. wxcli/migration/transform/mappers/location_mapper.py +235 -0
  384. wxcli/migration/transform/mappers/moh_mapper.py +114 -0
  385. wxcli/migration/transform/mappers/monitoring_mapper.py +221 -0
  386. wxcli/migration/transform/mappers/receptionist_mapper.py +245 -0
  387. wxcli/migration/transform/mappers/routing_mapper.py +821 -0
  388. wxcli/migration/transform/mappers/snr_mapper.py +201 -0
  389. wxcli/migration/transform/mappers/softkey_mapper.py +205 -0
  390. wxcli/migration/transform/mappers/user_mapper.py +264 -0
  391. wxcli/migration/transform/mappers/voicemail_group_mapper.py +286 -0
  392. wxcli/migration/transform/mappers/voicemail_mapper.py +514 -0
  393. wxcli/migration/transform/mappers/workspace_mapper.py +464 -0
  394. wxcli/migration/transform/normalizers.py +2008 -0
  395. wxcli/migration/transform/pattern_converter.py +106 -0
  396. wxcli/migration/transform/pipeline.py +271 -0
  397. wxcli/migration/transform/rules.py +255 -0
  398. wxcli/org_health/CLAUDE.md +63 -0
  399. wxcli/org_health/__init__.py +7 -0
  400. wxcli/org_health/analyze.py +93 -0
  401. wxcli/org_health/checks.py +458 -0
  402. wxcli/org_health/collector.py +62 -0
  403. wxcli/org_health/models.py +78 -0
  404. wxcli/org_health/report.py +300 -0
  405. wxcli/output.py +101 -0
  406. wxcli-1.2.0.dist-info/METADATA +505 -0
  407. wxcli-1.2.0.dist-info/RECORD +413 -0
  408. wxcli-1.2.0.dist-info/WHEEL +5 -0
  409. wxcli-1.2.0.dist-info/entry_points.txt +2 -0
  410. wxcli-1.2.0.dist-info/licenses/LICENSE +191 -0
  411. wxcli-1.2.0.dist-info/scm_file_list.json +751 -0
  412. wxcli-1.2.0.dist-info/scm_version.json +8 -0
  413. wxcli-1.2.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,523 @@
1
+ import json
2
+ import typer
3
+ from wxcli.auth import get_api
4
+ from wxcli.errors import WebexError, handle_rest_error
5
+ from wxcli.output import print_table, print_json
6
+ from wxcli.config import get_org_id
7
+
8
+
9
+ app = typer.Typer(help="Manage Webex Calling wholesale-provisioning.")
10
+
11
+
12
+ @app.command("list")
13
+ def cmd_list(
14
+ external_id: str = typer.Option(None, "--external-id", help="Customer external ID."),
15
+ status: str = typer.Option(None, "--status", help="Customer API status."),
16
+ on_behalf_of_sub_partner_org_id: str = typer.Option(None, "--on-behalf-of-sub-partner-org-id", help="The encoded organization ID for the sub partner."),
17
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
18
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
19
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
20
+ debug: bool = typer.Option(False, "--debug"),
21
+ ):
22
+ """List Wholesale Customers."""
23
+ api = get_api(debug=debug)
24
+ url = f"https://webexapis.com/v1/wholesale/customers"
25
+ params = {}
26
+ if external_id is not None:
27
+ params["externalId"] = external_id
28
+ if status is not None:
29
+ params["status"] = status
30
+ if on_behalf_of_sub_partner_org_id is not None:
31
+ params["onBehalfOfSubPartnerOrgId"] = on_behalf_of_sub_partner_org_id
32
+ if limit > 0:
33
+ params["max"] = limit
34
+ if offset > 0:
35
+ params["offset"] = offset
36
+ org_id = get_org_id()
37
+ if org_id is not None:
38
+ params["orgId"] = org_id
39
+ result = None
40
+ try:
41
+ result = api.session.rest_get(url, params=params)
42
+ except WebexError as e:
43
+ handle_rest_error(e)
44
+ result = result or []
45
+ items = result.get("items", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
46
+ if output == "json":
47
+ print_json(items)
48
+ else:
49
+ print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
50
+
51
+
52
+
53
+ @app.command("create")
54
+ def create(
55
+ on_behalf_of_sub_partner_org_id: str = typer.Option(None, "--on-behalf-of-sub-partner-org-id", help="The encoded organization ID for the sub partner."),
56
+ provisioning_id: str = typer.Option(None, "--provisioning-id", help="(required) This Provisioning ID defines how this customer is to be prov"),
57
+ org_id: str = typer.Option(None, "--org-id", help="The organization ID of the enterprise in Webex. Mandatory fo"),
58
+ external_id: str = typer.Option(None, "--external-id", help="(required) External ID of the Wholesale customer."),
59
+ sub_partner_admin_email: str = typer.Option(None, "--sub-partner-admin-email", help="The email of the sub partner organization admin."),
60
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
61
+ output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
62
+ debug: bool = typer.Option(False, "--debug"),
63
+ ):
64
+ """Provision a Wholesale Customer\n\nExample --json-body:\n '{"provisioningId":"...","packages":["common_area_calling"],"externalId":"...","address":{"addressLine1":"...","city":"...","country":"...","addressLine2":"...","stateOrProvince":"...","zipOrPostalCode":"..."},"orgId":"...","customerInfo":{"name":"...","primaryEmail":"...","language":"..."},"provisioningParameters":{"calling":{"location":"..."},"meetings":{"timezone":"..."},"packages":{"limits":"..."}},"subPartnerAdminEmail":"..."}'."""
65
+ api = get_api(debug=debug)
66
+ url = f"https://webexapis.com/v1/wholesale/customers"
67
+ params = {}
68
+ if on_behalf_of_sub_partner_org_id is not None:
69
+ params["onBehalfOfSubPartnerOrgId"] = on_behalf_of_sub_partner_org_id
70
+ if json_body:
71
+ body = json.loads(json_body)
72
+ else:
73
+ body = {}
74
+ if provisioning_id is not None:
75
+ body["provisioningId"] = provisioning_id
76
+ if org_id is not None:
77
+ body["orgId"] = org_id
78
+ if external_id is not None:
79
+ body["externalId"] = external_id
80
+ if sub_partner_admin_email is not None:
81
+ body["subPartnerAdminEmail"] = sub_partner_admin_email
82
+ _missing = [f for f in ['provisioningId', 'externalId'] if f not in body or body[f] is None]
83
+ if _missing:
84
+ typer.echo("Error: Missing required fields: " + ", ".join(_missing), err=True)
85
+ raise typer.Exit(1)
86
+ try:
87
+ result = api.session.rest_post(url, json=body, params=params)
88
+ except WebexError as e:
89
+ handle_rest_error(e)
90
+ if output == "json":
91
+ print_json(result)
92
+ elif isinstance(result, dict) and "id" in result:
93
+ typer.echo(f"Created: {result['id']}")
94
+ elif not result or result == {}:
95
+ typer.echo("Created.")
96
+ else:
97
+ print_json(result)
98
+
99
+
100
+
101
+ @app.command("show")
102
+ def show(
103
+ customer_id: str = typer.Argument(help="customerId"),
104
+ on_behalf_of_sub_partner_org_id: str = typer.Option(None, "--on-behalf-of-sub-partner-org-id", help="The encoded organization ID for the sub partner."),
105
+ include_package_license_info: str = typer.Option(None, "--include-package-license-info", help="If specified as true, a list of licenseIds will be returned"),
106
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
107
+ debug: bool = typer.Option(False, "--debug"),
108
+ ):
109
+ """Get a Wholesale Customer."""
110
+ api = get_api(debug=debug)
111
+ url = f"https://webexapis.com/v1/wholesale/customers/{customer_id}"
112
+ params = {}
113
+ if on_behalf_of_sub_partner_org_id is not None:
114
+ params["onBehalfOfSubPartnerOrgId"] = on_behalf_of_sub_partner_org_id
115
+ if include_package_license_info is not None:
116
+ params["includePackageLicenseInfo"] = include_package_license_info
117
+ try:
118
+ result = api.session.rest_get(url, params=params)
119
+ except WebexError as e:
120
+ handle_rest_error(e)
121
+ if output == "json":
122
+ print_json(result)
123
+ else:
124
+ if isinstance(result, dict):
125
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
126
+ elif isinstance(result, list):
127
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
128
+ else:
129
+ print_json(result)
130
+
131
+
132
+
133
+ @app.command("update")
134
+ def update(
135
+ customer_id: str = typer.Argument(help="customerId"),
136
+ on_behalf_of_sub_partner_org_id: str = typer.Option(None, "--on-behalf-of-sub-partner-org-id", help="The encoded organization ID for the sub partner."),
137
+ external_id: str = typer.Option(None, "--external-id", help="External ID of the Wholesale customer."),
138
+ sub_partner_admin_email: str = typer.Option(None, "--sub-partner-admin-email", help="The email of the sub partner organization admin."),
139
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
140
+ debug: bool = typer.Option(False, "--debug"),
141
+ ):
142
+ """Update a Wholesale Customer\n\nExample --json-body:\n '{"packages":["common_area_calling"],"externalId":"...","address":{"addressLine1":"...","city":"...","country":"...","addressLine2":"...","stateOrProvince":"...","zipOrPostalCode":"..."},"provisioningParameters":{"calling":{"location":"..."},"meetings":{"timezone":"..."},"packages":{"limits":"..."}},"subPartnerAdminEmail":"..."}'."""
143
+ api = get_api(debug=debug)
144
+ url = f"https://webexapis.com/v1/wholesale/customers/{customer_id}"
145
+ params = {}
146
+ if on_behalf_of_sub_partner_org_id is not None:
147
+ params["onBehalfOfSubPartnerOrgId"] = on_behalf_of_sub_partner_org_id
148
+ if json_body:
149
+ body = json.loads(json_body)
150
+ else:
151
+ body = {}
152
+ if external_id is not None:
153
+ body["externalId"] = external_id
154
+ if sub_partner_admin_email is not None:
155
+ body["subPartnerAdminEmail"] = sub_partner_admin_email
156
+ try:
157
+ result = api.session.rest_put(url, json=body, params=params)
158
+ except WebexError as e:
159
+ handle_rest_error(e)
160
+ typer.echo(f"Updated.")
161
+
162
+
163
+
164
+ @app.command("delete")
165
+ def delete(
166
+ customer_id: str = typer.Argument(help="customerId"),
167
+ on_behalf_of_sub_partner_org_id: str = typer.Option(None, "--on-behalf-of-sub-partner-org-id", help="The encoded organization ID for the sub partner."),
168
+ force: bool = typer.Option(False, "--force", help="Skip confirmation"),
169
+ debug: bool = typer.Option(False, "--debug"),
170
+ ):
171
+ """Remove a Wholesale Customer."""
172
+ if not force:
173
+ typer.confirm(f"Delete {customer_id}?", abort=True)
174
+ api = get_api(debug=debug)
175
+ url = f"https://webexapis.com/v1/wholesale/customers/{customer_id}"
176
+ params = {}
177
+ if on_behalf_of_sub_partner_org_id is not None:
178
+ params["onBehalfOfSubPartnerOrgId"] = on_behalf_of_sub_partner_org_id
179
+ try:
180
+ api.session.rest_delete(url, params=params)
181
+ except WebexError as e:
182
+ handle_rest_error(e)
183
+ typer.echo(f"Deleted: {customer_id}")
184
+
185
+
186
+
187
+ @app.command("create-validate-customers")
188
+ def create_validate_customers(
189
+ on_behalf_of_sub_partner_org_id: str = typer.Option(None, "--on-behalf-of-sub-partner-org-id", help="The encoded organization ID for the sub partner."),
190
+ provisioning_id: str = typer.Option(None, "--provisioning-id", help="Defines how this wholesale customer is to be provisioned for"),
191
+ org_id: str = typer.Option(None, "--org-id", help="The organization ID of the enterprise in Cisco Webex."),
192
+ external_id: str = typer.Option(None, "--external-id", help="External ID of the Wholesale customer."),
193
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
194
+ output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
195
+ debug: bool = typer.Option(False, "--debug"),
196
+ ):
197
+ """Precheck a Wholesale Customer Provisioning\n\nExample --json-body:\n '{"address":{"addressLine1":"...","city":"...","country":"...","addressLine2":"...","stateOrProvince":"...","zipOrPostalCode":"..."},"provisioningId":"...","packages":["common_area_calling"],"orgId":"...","externalId":"...","customerInfo":{"primaryEmail":"...","name":"..."},"provisioningParameters":{"calling":{"location":"..."},"meetings":{"timezone":"..."},"packages":{"limits":"..."}}}'."""
198
+ api = get_api(debug=debug)
199
+ url = f"https://webexapis.com/v1/wholesale/customers/validate"
200
+ params = {}
201
+ if on_behalf_of_sub_partner_org_id is not None:
202
+ params["onBehalfOfSubPartnerOrgId"] = on_behalf_of_sub_partner_org_id
203
+ if json_body:
204
+ body = json.loads(json_body)
205
+ else:
206
+ body = {}
207
+ if provisioning_id is not None:
208
+ body["provisioningId"] = provisioning_id
209
+ if org_id is not None:
210
+ body["orgId"] = org_id
211
+ if external_id is not None:
212
+ body["externalId"] = external_id
213
+ try:
214
+ result = api.session.rest_post(url, json=body, params=params)
215
+ except WebexError as e:
216
+ handle_rest_error(e)
217
+ if output == "json":
218
+ print_json(result)
219
+ elif isinstance(result, dict) and "id" in result:
220
+ typer.echo(f"Created: {result['id']}")
221
+ elif not result or result == {}:
222
+ typer.echo("Created.")
223
+ else:
224
+ print_json(result)
225
+
226
+
227
+
228
+ @app.command("list-sub-partners")
229
+ def list_sub_partners(
230
+ provisioning_state: str = typer.Option(None, "--provisioning-state", help="Status to filter sub-partners based on provisioning state."),
231
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
232
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
233
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
234
+ debug: bool = typer.Option(False, "--debug"),
235
+ ):
236
+ """List Wholesale Sub-partners."""
237
+ api = get_api(debug=debug)
238
+ url = f"https://webexapis.com/v1/wholesale/subPartners"
239
+ params = {}
240
+ if provisioning_state is not None:
241
+ params["provisioningState"] = provisioning_state
242
+ if limit > 0:
243
+ params["max"] = limit
244
+ if offset > 0:
245
+ params["offset"] = offset
246
+ result = None
247
+ try:
248
+ result = api.session.rest_get(url, params=params)
249
+ except WebexError as e:
250
+ handle_rest_error(e)
251
+ result = result or []
252
+ items = result.get("items", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
253
+ if output == "json":
254
+ print_json(items)
255
+ else:
256
+ print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
257
+
258
+
259
+
260
+ @app.command("list-subscribers")
261
+ def list_subscribers(
262
+ customer_id: str = typer.Option(None, "--customer-id", help="Wholesale customer ID."),
263
+ person_id: str = typer.Option(None, "--person-id", help="The person ID of the subscriber used in the [/v1/people API]"),
264
+ external_customer_id: str = typer.Option(None, "--external-customer-id", help="Customer external ID."),
265
+ email: str = typer.Option(None, "--email", help="The email address of the subscriber."),
266
+ status: str = typer.Option(None, "--status", help="The provisioning status of the subscriber."),
267
+ after: str = typer.Option(None, "--after", help="Only include subscribers created after this date and time. E"),
268
+ last_status_change: str = typer.Option(None, "--last-status-change", help="Only include subscribers with a provisioning status change a"),
269
+ sort_by: str = typer.Option(None, "--sort-by", help="Supported `sortBy` attributes are `created` and `lastStatusC"),
270
+ sort_order: str = typer.Option(None, "--sort-order", help="Sort by `ASC` (ascending) or `DESC` (descending)."),
271
+ on_behalf_of_sub_partner_org_id: str = typer.Option(None, "--on-behalf-of-sub-partner-org-id", help="The encoded organization ID for the sub partner."),
272
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
273
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
274
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
275
+ debug: bool = typer.Option(False, "--debug"),
276
+ ):
277
+ """List Wholesale Subscribers."""
278
+ api = get_api(debug=debug)
279
+ url = f"https://webexapis.com/v1/wholesale/subscribers"
280
+ params = {}
281
+ if customer_id is not None:
282
+ params["customerId"] = customer_id
283
+ if person_id is not None:
284
+ params["personId"] = person_id
285
+ if external_customer_id is not None:
286
+ params["externalCustomerId"] = external_customer_id
287
+ if email is not None:
288
+ params["email"] = email
289
+ if status is not None:
290
+ params["status"] = status
291
+ if after is not None:
292
+ params["after"] = after
293
+ if last_status_change is not None:
294
+ params["lastStatusChange"] = last_status_change
295
+ if sort_by is not None:
296
+ params["sortBy"] = sort_by
297
+ if sort_order is not None:
298
+ params["sortOrder"] = sort_order
299
+ if on_behalf_of_sub_partner_org_id is not None:
300
+ params["onBehalfOfSubPartnerOrgId"] = on_behalf_of_sub_partner_org_id
301
+ if limit > 0:
302
+ params["max"] = limit
303
+ if offset > 0:
304
+ params["offset"] = offset
305
+ result = None
306
+ try:
307
+ result = api.session.rest_get(url, params=params)
308
+ except WebexError as e:
309
+ handle_rest_error(e)
310
+ result = result or []
311
+ items = result.get("items", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
312
+ if output == "json":
313
+ print_json(items)
314
+ else:
315
+ print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
316
+
317
+
318
+
319
+ @app.command("create-subscribers")
320
+ def create_subscribers(
321
+ on_behalf_of_sub_partner_org_id: str = typer.Option(None, "--on-behalf-of-sub-partner-org-id", help="The encoded organization ID for the sub partner."),
322
+ customer_id: str = typer.Option(None, "--customer-id", help="(required) ID of the Provisioned Customer for Webex Wholesale."),
323
+ email: str = typer.Option(None, "--email", help="(required) The email address of the subscriber (mandatory for the trust"),
324
+ package: str = typer.Option(None, "--package", help="Choices: webex_calling, webex_meetings, webex_suite, webex_voice, cx_essentials, webex_calling_standard"),
325
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
326
+ output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
327
+ debug: bool = typer.Option(False, "--debug"),
328
+ ):
329
+ """Provision a Wholesale Subscriber\n\nExample --json-body:\n '{"customerId":"...","email":"...","provisioningParameters":{"firstName":"...","lastName":"...","primaryPhoneNumber":"...","extension":"...","locationId":"..."},"package":"webex_calling","packages":["webex_calling"]}'."""
330
+ api = get_api(debug=debug)
331
+ url = f"https://webexapis.com/v1/wholesale/subscribers"
332
+ params = {}
333
+ if on_behalf_of_sub_partner_org_id is not None:
334
+ params["onBehalfOfSubPartnerOrgId"] = on_behalf_of_sub_partner_org_id
335
+ if json_body:
336
+ body = json.loads(json_body)
337
+ else:
338
+ body = {}
339
+ if customer_id is not None:
340
+ body["customerId"] = customer_id
341
+ if email is not None:
342
+ body["email"] = email
343
+ if package is not None:
344
+ body["package"] = package
345
+ _missing = [f for f in ['customerId', 'email'] if f not in body or body[f] is None]
346
+ if _missing:
347
+ typer.echo("Error: Missing required fields: " + ", ".join(_missing), err=True)
348
+ raise typer.Exit(1)
349
+ try:
350
+ result = api.session.rest_post(url, json=body, params=params)
351
+ except WebexError as e:
352
+ handle_rest_error(e)
353
+ if output == "json":
354
+ print_json(result)
355
+ elif isinstance(result, dict) and "id" in result:
356
+ typer.echo(f"Created: {result['id']}")
357
+ elif not result or result == {}:
358
+ typer.echo("Created.")
359
+ else:
360
+ print_json(result)
361
+
362
+
363
+
364
+ @app.command("show-subscribers")
365
+ def show_subscribers(
366
+ subscriber_id: str = typer.Argument(help="subscriberId"),
367
+ on_behalf_of_sub_partner_org_id: str = typer.Option(None, "--on-behalf-of-sub-partner-org-id", help="The encoded organization ID for the sub partner."),
368
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
369
+ debug: bool = typer.Option(False, "--debug"),
370
+ ):
371
+ """Get a Wholesale Subscriber."""
372
+ api = get_api(debug=debug)
373
+ url = f"https://webexapis.com/v1/wholesale/subscribers/{subscriber_id}"
374
+ params = {}
375
+ if on_behalf_of_sub_partner_org_id is not None:
376
+ params["onBehalfOfSubPartnerOrgId"] = on_behalf_of_sub_partner_org_id
377
+ try:
378
+ result = api.session.rest_get(url, params=params)
379
+ except WebexError as e:
380
+ handle_rest_error(e)
381
+ if output == "json":
382
+ print_json(result)
383
+ else:
384
+ if isinstance(result, dict):
385
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
386
+ elif isinstance(result, list):
387
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
388
+ else:
389
+ print_json(result)
390
+
391
+
392
+
393
+ @app.command("update-subscribers")
394
+ def update_subscribers(
395
+ subscriber_id: str = typer.Argument(help="subscriberId"),
396
+ on_behalf_of_sub_partner_org_id: str = typer.Option(None, "--on-behalf-of-sub-partner-org-id", help="The encoded organization ID for the sub partner."),
397
+ package: str = typer.Option(None, "--package", help="Choices: webex_calling, webex_meetings, webex_suite, webex_voice, cx_essentials, webex_calling_standard, attendant_console, cx_premium_agent, cx_standard_agent"),
398
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
399
+ debug: bool = typer.Option(False, "--debug"),
400
+ ):
401
+ """Update a Wholesale Subscriber\n\nExample --json-body:\n '{"package":"webex_calling","packages":["webex_calling"],"provisioningParameters":{"primaryPhoneNumber":"...","extension":"...","locationId":"..."}}'."""
402
+ api = get_api(debug=debug)
403
+ url = f"https://webexapis.com/v1/wholesale/subscribers/{subscriber_id}"
404
+ params = {}
405
+ if on_behalf_of_sub_partner_org_id is not None:
406
+ params["onBehalfOfSubPartnerOrgId"] = on_behalf_of_sub_partner_org_id
407
+ if json_body:
408
+ body = json.loads(json_body)
409
+ else:
410
+ body = {}
411
+ if package is not None:
412
+ body["package"] = package
413
+ try:
414
+ result = api.session.rest_put(url, json=body, params=params)
415
+ except WebexError as e:
416
+ handle_rest_error(e)
417
+ typer.echo(f"Updated.")
418
+
419
+
420
+
421
+ @app.command("delete-subscribers")
422
+ def delete_subscribers(
423
+ subscriber_id: str = typer.Argument(help="subscriberId"),
424
+ on_behalf_of_sub_partner_org_id: str = typer.Option(None, "--on-behalf-of-sub-partner-org-id", help="The encoded organization ID for the sub partner."),
425
+ force: bool = typer.Option(False, "--force", help="Skip confirmation"),
426
+ debug: bool = typer.Option(False, "--debug"),
427
+ ):
428
+ """Remove a Wholesale Subscriber."""
429
+ if not force:
430
+ typer.confirm(f"Delete {subscriber_id}?", abort=True)
431
+ api = get_api(debug=debug)
432
+ url = f"https://webexapis.com/v1/wholesale/subscribers/{subscriber_id}"
433
+ params = {}
434
+ if on_behalf_of_sub_partner_org_id is not None:
435
+ params["onBehalfOfSubPartnerOrgId"] = on_behalf_of_sub_partner_org_id
436
+ try:
437
+ api.session.rest_delete(url, params=params)
438
+ except WebexError as e:
439
+ handle_rest_error(e)
440
+ typer.echo(f"Deleted: {subscriber_id}")
441
+
442
+
443
+
444
+ @app.command("create-validate-subscribers")
445
+ def create_validate_subscribers(
446
+ on_behalf_of_sub_partner_org_id: str = typer.Option(None, "--on-behalf-of-sub-partner-org-id", help="The encoded organization ID for the sub partner."),
447
+ provisioning_id: str = typer.Option(None, "--provisioning-id", help="Defines how this wholesale subscriber is to be provisioned f"),
448
+ customer_id: str = typer.Option(None, "--customer-id", help="ID of the Provisioned Customer for Webex Wholesale."),
449
+ email: str = typer.Option(None, "--email", help="(required) The email address of the subscriber."),
450
+ package: str = typer.Option(None, "--package", help="Choices: webex_calling, webex_meetings, webex_suite, webex_voice, cx_essentials, webex_calling_standard"),
451
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
452
+ output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
453
+ debug: bool = typer.Option(False, "--debug"),
454
+ ):
455
+ """Precheck a Wholesale Subscriber Provisioning\n\nExample --json-body:\n '{"email":"...","provisioningId":"...","customerId":"...","package":"webex_calling","packages":["webex_calling"],"provisioningParameters":{"firstName":"...","lastName":"...","primaryPhoneNumber":"...","extension":"...","locationId":"..."},"customerInfo":{"primaryEmail":"..."}}'."""
456
+ api = get_api(debug=debug)
457
+ url = f"https://webexapis.com/v1/wholesale/subscribers/validate"
458
+ params = {}
459
+ if on_behalf_of_sub_partner_org_id is not None:
460
+ params["onBehalfOfSubPartnerOrgId"] = on_behalf_of_sub_partner_org_id
461
+ if json_body:
462
+ body = json.loads(json_body)
463
+ else:
464
+ body = {}
465
+ if provisioning_id is not None:
466
+ body["provisioningId"] = provisioning_id
467
+ if customer_id is not None:
468
+ body["customerId"] = customer_id
469
+ if email is not None:
470
+ body["email"] = email
471
+ if package is not None:
472
+ body["package"] = package
473
+ _missing = [f for f in ['email'] if f not in body or body[f] is None]
474
+ if _missing:
475
+ typer.echo("Error: Missing required fields: " + ", ".join(_missing), err=True)
476
+ raise typer.Exit(1)
477
+ try:
478
+ result = api.session.rest_post(url, json=body, params=params)
479
+ except WebexError as e:
480
+ handle_rest_error(e)
481
+ if output == "json":
482
+ print_json(result)
483
+ elif isinstance(result, dict) and "id" in result:
484
+ typer.echo(f"Created: {result['id']}")
485
+ elif not result or result == {}:
486
+ typer.echo("Created.")
487
+ else:
488
+ print_json(result)
489
+
490
+
491
+
492
+ @app.command("create-consent-move")
493
+ def create_consent_move(
494
+ subscriber_id: str = typer.Argument(help="subscriberId"),
495
+ on_behalf_of_sub_partner_org_id: str = typer.Option(None, "--on-behalf-of-sub-partner-org-id", help="The encoded organization ID for the sub partner."),
496
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
497
+ output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
498
+ debug: bool = typer.Option(False, "--debug"),
499
+ ):
500
+ """Send Consent User Move Email to Pending Wholesale Subscribers."""
501
+ api = get_api(debug=debug)
502
+ url = f"https://webexapis.com/v1/subscribers/{subscriber_id}/emails/consentMove"
503
+ params = {}
504
+ if on_behalf_of_sub_partner_org_id is not None:
505
+ params["onBehalfOfSubPartnerOrgId"] = on_behalf_of_sub_partner_org_id
506
+ if json_body:
507
+ body = json.loads(json_body)
508
+ else:
509
+ body = {}
510
+ try:
511
+ result = api.session.rest_post(url, json=body, params=params)
512
+ except WebexError as e:
513
+ handle_rest_error(e)
514
+ if output == "json":
515
+ print_json(result)
516
+ elif isinstance(result, dict) and "id" in result:
517
+ typer.echo(f"Created: {result['id']}")
518
+ elif not result or result == {}:
519
+ typer.echo("Created.")
520
+ else:
521
+ print_json(result)
522
+
523
+