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,67 @@
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 hybrid-connectors.")
10
+
11
+
12
+ @app.command("list")
13
+ def cmd_list(
14
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
15
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
16
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
17
+ debug: bool = typer.Option(False, "--debug"),
18
+ ):
19
+ """List Hybrid Connectors."""
20
+ api = get_api(debug=debug)
21
+ url = f"https://webexapis.com/v1/hybrid/connectors"
22
+ params = {}
23
+ if limit > 0:
24
+ params["max"] = limit
25
+ if offset > 0:
26
+ params["start"] = offset
27
+ org_id = get_org_id()
28
+ if org_id is not None:
29
+ params["orgId"] = org_id
30
+ result = None
31
+ try:
32
+ result = api.session.rest_get(url, params=params)
33
+ except WebexError as e:
34
+ handle_rest_error(e)
35
+ result = result or []
36
+ items = result.get("items", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
37
+ if output == "json":
38
+ print_json(items)
39
+ else:
40
+ print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
41
+
42
+
43
+
44
+ @app.command("show")
45
+ def show(
46
+ connector_id: str = typer.Argument(help="connectorId"),
47
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
48
+ debug: bool = typer.Option(False, "--debug"),
49
+ ):
50
+ """Get Hybrid Connector Details."""
51
+ api = get_api(debug=debug)
52
+ url = f"https://webexapis.com/v1/hybrid/connectors/{connector_id}"
53
+ try:
54
+ result = api.session.rest_get(url)
55
+ except WebexError as e:
56
+ handle_rest_error(e)
57
+ if output == "json":
58
+ print_json(result)
59
+ else:
60
+ if isinstance(result, dict):
61
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
62
+ elif isinstance(result, list):
63
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
64
+ else:
65
+ print_json(result)
66
+
67
+
@@ -0,0 +1,159 @@
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 resolve_org_id
7
+
8
+
9
+ app = typer.Typer(help="Manage Webex Calling identity-org.")
10
+
11
+
12
+ @app.command("show")
13
+ def show(
14
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
15
+ debug: bool = typer.Option(False, "--debug"),
16
+ ):
17
+ """Get an organization."""
18
+ api = get_api(debug=debug)
19
+ org_id = resolve_org_id(api.session)
20
+ url = f"https://webexapis.com/identity/organizations/{org_id}"
21
+ try:
22
+ result = api.session.rest_get(url)
23
+ except WebexError as e:
24
+ handle_rest_error(e)
25
+ if output == "json":
26
+ print_json(result)
27
+ else:
28
+ if isinstance(result, dict):
29
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
30
+ elif isinstance(result, list):
31
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
32
+ else:
33
+ print_json(result)
34
+
35
+
36
+
37
+ @app.command("update")
38
+ def update(
39
+ display_name: str = typer.Option(None, "--display-name", help="New full name of the organization."),
40
+ preferred_language: str = typer.Option(None, "--preferred-language", help="It is the default preferredLanguage for user creation in thi"),
41
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
42
+ debug: bool = typer.Option(False, "--debug"),
43
+ ):
44
+ """Update an organization\n\nExample --json-body:\n '{"schemas":["..."],"displayName":"...","preferredLanguage":"..."}'."""
45
+ api = get_api(debug=debug)
46
+ org_id = resolve_org_id(api.session)
47
+ url = f"https://webexapis.com/identity/organizations/{org_id}"
48
+ if json_body:
49
+ body = json.loads(json_body)
50
+ else:
51
+ body = {}
52
+ if display_name is not None:
53
+ body["displayName"] = display_name
54
+ if preferred_language is not None:
55
+ body["preferredLanguage"] = preferred_language
56
+ try:
57
+ result = api.session.rest_patch(url, json=body)
58
+ except WebexError as e:
59
+ handle_rest_error(e)
60
+ typer.echo(f"Updated.")
61
+
62
+
63
+
64
+ @app.command("update-authentication-config")
65
+ def update_authentication_config(
66
+ remember_my_login_id: bool = typer.Option(None, "--remember-my-login-id/--no-remember-my-login-id", help="Login Id set to true if it should be remembered."),
67
+ remember_my_login_id_duration: str = typer.Option(None, "--remember-my-login-id-duration", help="Specifies the number of days the user's login ID is remember"),
68
+ mfa_enabled: bool = typer.Option(None, "--mfa-enabled/--no-mfa-enabled", help="Enable/ Disable multi-factor authentication on an organizati"),
69
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
70
+ debug: bool = typer.Option(False, "--debug"),
71
+ ):
72
+ """Update Organization Authentication Configuration Settings\n\nExample --json-body:\n '{"schemas":["..."],"RememberMyLoginId":true,"RememberMyLoginIdDuration":0,"mfaEnabled":true}'."""
73
+ api = get_api(debug=debug)
74
+ org_id = resolve_org_id(api.session)
75
+ url = f"https://webexapis.com/identity/organizations/{org_id}/authenticationConfig"
76
+ if json_body:
77
+ body = json.loads(json_body)
78
+ else:
79
+ body = {}
80
+ if remember_my_login_id is not None:
81
+ body["RememberMyLoginId"] = remember_my_login_id
82
+ if remember_my_login_id_duration is not None:
83
+ body["RememberMyLoginIdDuration"] = remember_my_login_id_duration
84
+ if mfa_enabled is not None:
85
+ body["mfaEnabled"] = mfa_enabled
86
+ try:
87
+ result = api.session.rest_patch(url, json=body)
88
+ except WebexError as e:
89
+ handle_rest_error(e)
90
+ typer.echo(f"Updated.")
91
+
92
+
93
+
94
+ @app.command("update-password-policy")
95
+ def update_password_policy(
96
+ minimum_numeric: str = typer.Option(None, "--minimum-numeric", help="Minimum number of numeric characters in password"),
97
+ minimum_cap_alpha: str = typer.Option(None, "--minimum-cap-alpha", help="Minimum number of uppercase alphabetic character letters in"),
98
+ minimum_low_alpha: str = typer.Option(None, "--minimum-low-alpha", help="Minimum number of lowercase alphabetic character letters in"),
99
+ minimum_special: str = typer.Option(None, "--minimum-special", help="Minimum number of special characters included \"~!@#$%^&*()-_"),
100
+ minimum_length: str = typer.Option(None, "--minimum-length", help="Minimum length of password. Must be between 8 and 256, inclu"),
101
+ history_count: str = typer.Option(None, "--history-count", help="The number of former passwords in history, the new password"),
102
+ max_password_age: str = typer.Option(None, "--max-password-age", help="The password expired time, unit: day, that means user need t"),
103
+ not_acceptable_strings: str = typer.Option(None, "--not-acceptable-strings", help="The password can not be any one in this string list."),
104
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
105
+ debug: bool = typer.Option(False, "--debug"),
106
+ ):
107
+ """Update Organization Password Policy\n\nExample --json-body:\n '{"schemas":["..."],"minimumNumeric":"...","minimumCapAlpha":"...","minimumLowAlpha":"...","minimumSpecial":"...","minimumLength":"...","historyCount":"...","maxPasswordAge":"..."}'."""
108
+ api = get_api(debug=debug)
109
+ org_id = resolve_org_id(api.session)
110
+ url = f"https://webexapis.com/identity/organizations/{org_id}/passwordPolicy"
111
+ if json_body:
112
+ body = json.loads(json_body)
113
+ else:
114
+ body = {}
115
+ if minimum_numeric is not None:
116
+ body["minimumNumeric"] = minimum_numeric
117
+ if minimum_cap_alpha is not None:
118
+ body["minimumCapAlpha"] = minimum_cap_alpha
119
+ if minimum_low_alpha is not None:
120
+ body["minimumLowAlpha"] = minimum_low_alpha
121
+ if minimum_special is not None:
122
+ body["minimumSpecial"] = minimum_special
123
+ if minimum_length is not None:
124
+ body["minimumLength"] = minimum_length
125
+ if history_count is not None:
126
+ body["historyCount"] = history_count
127
+ if max_password_age is not None:
128
+ body["maxPasswordAge"] = max_password_age
129
+ if not_acceptable_strings is not None:
130
+ body["notAcceptableStrings"] = not_acceptable_strings
131
+ try:
132
+ result = api.session.rest_patch(url, json=body)
133
+ except WebexError as e:
134
+ handle_rest_error(e)
135
+ typer.echo(f"Updated.")
136
+
137
+
138
+
139
+ @app.command("generate-otp")
140
+ def generate_otp(
141
+ user_id: str = typer.Argument(help="userId"),
142
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body"),
143
+ debug: bool = typer.Option(False, "--debug"),
144
+ ):
145
+ """Generate OTP."""
146
+ api = get_api(debug=debug)
147
+ org_id = resolve_org_id(api.session)
148
+ url = f"https://webexapis.com/identity/organizations/{org_id}/users/{user_id}/actions/generateOtp"
149
+ if json_body:
150
+ body = json.loads(json_body)
151
+ else:
152
+ body = {}
153
+ try:
154
+ result = api.session.rest_post(url, json=body)
155
+ except WebexError as e:
156
+ handle_rest_error(e)
157
+ print_json(result)
158
+
159
+
@@ -0,0 +1,155 @@
1
+ """wxcli init — materialize the bundled Claude Code playbook into a local folder.
2
+
3
+ The folder layout mirrors the repo (CLAUDE.md, .claude/, docs/reference/), so
4
+ Claude Code loads everything natively and no path is rewritten (spec §4.3).
5
+ A manifest records every written file; refresh and --uninstall touch ONLY
6
+ manifest-owned paths — user files are never clobbered.
7
+ """
8
+ from __future__ import annotations
9
+
10
+ import hashlib
11
+ import json
12
+ from pathlib import Path
13
+
14
+ import typer
15
+
16
+ import wxcli
17
+ from wxcli import __version__
18
+
19
+ app = typer.Typer(help="Materialize the bundled Claude Code playbook into a local project folder.")
20
+
21
+ MANIFEST_REL = Path(".claude") / ".wxops-manifest.json"
22
+
23
+
24
+ def bundle_root() -> Path:
25
+ """The playbook shipped inside the installed package (test-injectable)."""
26
+ return Path(wxcli.__file__).resolve().parent / "_playbook"
27
+
28
+
29
+ def read_bundle(bundle: Path) -> dict[str, bytes]:
30
+ """posix relpath -> content for every file in the installed bundle."""
31
+ return {
32
+ p.relative_to(bundle).as_posix(): p.read_bytes()
33
+ for p in sorted(bundle.rglob("*")) if p.is_file()
34
+ }
35
+
36
+
37
+ def sha256_hex(data: bytes) -> str:
38
+ return hashlib.sha256(data).hexdigest()
39
+
40
+
41
+ def load_manifest(folder: Path) -> dict | None:
42
+ path = folder / MANIFEST_REL
43
+ return json.loads(path.read_text()) if path.exists() else None
44
+
45
+
46
+ def save_manifest(folder: Path, hashes: dict[str, str]) -> None:
47
+ path = folder / MANIFEST_REL
48
+ path.parent.mkdir(parents=True, exist_ok=True)
49
+ path.write_text(json.dumps(
50
+ {"wxops_playbook": True, "version": __version__, "files": hashes}, indent=2
51
+ ))
52
+
53
+
54
+ def _prune_empty_dirs(start: Path, stop: Path) -> None:
55
+ d = start
56
+ while d != stop and d.is_dir():
57
+ try:
58
+ d.rmdir() # fails (OSError) if non-empty → stop pruning
59
+ except OSError:
60
+ return
61
+ d = d.parent
62
+
63
+
64
+ def apply_bundle(
65
+ folder: Path, bundle_files: dict[str, bytes], owned: dict[str, str]
66
+ ) -> tuple[list[str], list[str], dict[str, str]]:
67
+ """Write/refresh the bundle. Returns (written, deleted, new_hashes).
68
+
69
+ Deletes manifest-owned paths absent from the new bundle (retired skills
70
+ don't linger); never touches paths it doesn't own (spec §4.3).
71
+ """
72
+ written: list[str] = []
73
+ hashes: dict[str, str] = {}
74
+ for rel, content in bundle_files.items():
75
+ dest = folder / rel
76
+ if not dest.exists() or dest.read_bytes() != content:
77
+ dest.parent.mkdir(parents=True, exist_ok=True)
78
+ dest.write_bytes(content)
79
+ written.append(rel)
80
+ hashes[rel] = sha256_hex(content)
81
+ deleted: list[str] = []
82
+ for rel in sorted(set(owned) - set(bundle_files)):
83
+ target = folder / rel
84
+ if target.exists():
85
+ target.unlink()
86
+ deleted.append(rel)
87
+ _prune_empty_dirs(target.parent, folder)
88
+ return written, deleted, hashes
89
+
90
+
91
+ def do_uninstall(folder: Path) -> None:
92
+ manifest = load_manifest(folder)
93
+ if manifest is None:
94
+ typer.echo(f"No wxops manifest in {folder} — nothing to uninstall.", err=True)
95
+ raise typer.Exit(1)
96
+ for rel in sorted(manifest.get("files", {})):
97
+ target = folder / rel
98
+ if target.exists():
99
+ target.unlink()
100
+ _prune_empty_dirs(target.parent, folder)
101
+ (folder / MANIFEST_REL).unlink(missing_ok=True)
102
+ _prune_empty_dirs((folder / MANIFEST_REL).parent, folder)
103
+ typer.echo(f"Removed the wxops playbook from {folder}. Files you added were left in place.")
104
+
105
+
106
+ # A single @app.command() with no callback: Typer collapses the app into this
107
+ # command, so `wxcli init FOLDER --flags` parses positionals and options together.
108
+ # (A group callback would treat post-positional options as subcommand names.)
109
+ @app.command()
110
+ def init(
111
+ folder: Path = typer.Argument(Path("wxcli-playbook"), help="Target playbook folder."),
112
+ force: bool = typer.Option(False, "--force", help="Overwrite collisions / refresh without prompting."),
113
+ yes: bool = typer.Option(False, "--yes", help="Skip the confirmation prompt."),
114
+ uninstall: bool = typer.Option(False, "--uninstall", help="Remove every manifest-owned file, then the manifest."),
115
+ ):
116
+ """Materialize the bundled playbook (CLAUDE.md, .claude/, docs/reference/) into FOLDER."""
117
+ folder = folder.resolve()
118
+ if uninstall:
119
+ do_uninstall(folder)
120
+ return
121
+
122
+ bundle = bundle_root()
123
+ if not bundle.is_dir():
124
+ typer.echo("Bundled playbook not found in this wxcli install.", err=True)
125
+ raise typer.Exit(1)
126
+ bundle_files = read_bundle(bundle)
127
+ manifest = load_manifest(folder)
128
+
129
+ if manifest is None:
130
+ collisions = sorted(rel for rel in bundle_files if (folder / rel).exists())
131
+ if collisions and not force:
132
+ typer.echo(f"{folder} already contains files the playbook would overwrite:", err=True)
133
+ for rel in collisions:
134
+ typer.echo(f" {rel}", err=True)
135
+ typer.echo("Re-run with --force to overwrite them, or choose an empty folder.", err=True)
136
+ raise typer.Exit(1)
137
+ owned: dict[str, str] = {}
138
+ else:
139
+ owned = manifest.get("files", {})
140
+ if not (force or yes) and not typer.confirm(
141
+ f"Refresh the wxops playbook in {folder} (owned files only)?"
142
+ ):
143
+ raise typer.Exit(0)
144
+
145
+ folder.mkdir(parents=True, exist_ok=True)
146
+ written, deleted, hashes = apply_bundle(folder, bundle_files, owned)
147
+ save_manifest(folder, hashes)
148
+ typer.echo(
149
+ f"Playbook v{__version__}: {len(written)} file(s) written, "
150
+ f"{len(deleted)} removed, in {folder}"
151
+ )
152
+ typer.echo(
153
+ "Open this folder in Claude Code (start a fresh session) — "
154
+ "skills and agents load at session start."
155
+ )
@@ -0,0 +1,106 @@
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 licenses.")
10
+
11
+
12
+ @app.command("list")
13
+ def cmd_list(
14
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
15
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
16
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
17
+ debug: bool = typer.Option(False, "--debug"),
18
+ ):
19
+ """List Licenses."""
20
+ api = get_api(debug=debug)
21
+ url = f"https://webexapis.com/v1/licenses"
22
+ params = {}
23
+ if limit > 0:
24
+ params["max"] = limit
25
+ if offset > 0:
26
+ params["start"] = offset
27
+ org_id = get_org_id()
28
+ if org_id is not None:
29
+ params["orgId"] = org_id
30
+ result = None
31
+ try:
32
+ result = api.session.rest_get(url, params=params)
33
+ except WebexError as e:
34
+ handle_rest_error(e)
35
+ result = result or []
36
+ items = result.get("items", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
37
+ if output == "json":
38
+ print_json(items)
39
+ else:
40
+ print_table(items, columns=[('ID', 'id'), ('Name', 'name'), ('Total Units', 'totalUnits'), ('Consumed', 'consumedUnits')], limit=limit)
41
+
42
+
43
+
44
+ @app.command("show")
45
+ def show(
46
+ license_id: str = typer.Argument(help="licenseId"),
47
+ include_assigned_to: str = typer.Option(None, "--include-assigned-to", help="Choices: user"),
48
+ next: str = typer.Option(None, "--next", help="List the next set of users. Applicable only if `includeAssig"),
49
+ limit: str = typer.Option(None, "--limit", help="A limit on the number of users to be returned in the respons"),
50
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
51
+ debug: bool = typer.Option(False, "--debug"),
52
+ ):
53
+ """Get License Details."""
54
+ api = get_api(debug=debug)
55
+ url = f"https://webexapis.com/v1/licenses/{license_id}"
56
+ params = {}
57
+ if include_assigned_to is not None:
58
+ params["includeAssignedTo"] = include_assigned_to
59
+ if next is not None:
60
+ params["next"] = next
61
+ if limit is not None:
62
+ params["limit"] = limit
63
+ try:
64
+ result = api.session.rest_get(url, params=params)
65
+ except WebexError as e:
66
+ handle_rest_error(e)
67
+ if output == "json":
68
+ print_json(result)
69
+ else:
70
+ if isinstance(result, dict):
71
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
72
+ elif isinstance(result, list):
73
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
74
+ else:
75
+ print_json(result)
76
+
77
+
78
+
79
+ @app.command("update")
80
+ def update(
81
+ email: str = typer.Option(None, "--email", help="Email address of the user."),
82
+ person_id: str = typer.Option(None, "--person-id", help="A unique identifier for the user."),
83
+ org_id: str = typer.Option(None, "--org-id", help="The ID of the organization to which the licenses and siteUrl"),
84
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
85
+ debug: bool = typer.Option(False, "--debug"),
86
+ ):
87
+ """Assign Licenses to Users\n\nExample --json-body:\n '{"email":"...","personId":"...","orgId":"...","licenses":[{"id":"...","operation":"...","properties":"..."}],"siteUrls":[{"siteUrl":"...","accountType":"...","operation":"..."}]}'."""
88
+ api = get_api(debug=debug)
89
+ url = f"https://webexapis.com/v1/licenses/users"
90
+ if json_body:
91
+ body = json.loads(json_body)
92
+ else:
93
+ body = {}
94
+ if email is not None:
95
+ body["email"] = email
96
+ if person_id is not None:
97
+ body["personId"] = person_id
98
+ if org_id is not None:
99
+ body["orgId"] = org_id
100
+ try:
101
+ result = api.session.rest_patch(url, json=body)
102
+ except WebexError as e:
103
+ handle_rest_error(e)
104
+ typer.echo(f"Updated.")
105
+
106
+
@@ -0,0 +1,40 @@
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
+
7
+
8
+ app = typer.Typer(help="Manage Webex Calling live-monitoring.")
9
+
10
+
11
+ @app.command("create")
12
+ def create(
13
+ site_url: str = typer.Option(None, "--site-url", help="A site URL."),
14
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
15
+ output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
16
+ debug: bool = typer.Option(False, "--debug"),
17
+ ):
18
+ """Get Live Meeting metrics categorized by Country\n\nExample --json-body:\n '{"siteIds":[0],"siteUrl":"..."}'."""
19
+ api = get_api(debug=debug)
20
+ url = f"https://webexapis.com/v1/livemonitoring/liveMeetingsByCountry"
21
+ if json_body:
22
+ body = json.loads(json_body)
23
+ else:
24
+ body = {}
25
+ if site_url is not None:
26
+ body["siteUrl"] = site_url
27
+ try:
28
+ result = api.session.rest_post(url, json=body)
29
+ except WebexError as e:
30
+ handle_rest_error(e)
31
+ if output == "json":
32
+ print_json(result)
33
+ elif isinstance(result, dict) and "id" in result:
34
+ typer.echo(f"Created: {result['id']}")
35
+ elif not result or result == {}:
36
+ typer.echo("Created.")
37
+ else:
38
+ print_json(result)
39
+
40
+