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,54 @@
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 security-audit.")
10
+
11
+
12
+ @app.command("list")
13
+ def cmd_list(
14
+ start_time: str = typer.Option(..., "--start-time", help="List events which occurred after a specific date and time."),
15
+ end_time: str = typer.Option(..., "--end-time", help="List events which occurred before a specific date and time."),
16
+ actor_id: str = typer.Option(None, "--actor-id", help="List events performed by this person, by ID."),
17
+ event_categories: str = typer.Option(None, "--event-categories", help="List events, by event categories."),
18
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
19
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
20
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
21
+ debug: bool = typer.Option(False, "--debug"),
22
+ ):
23
+ """List Security Audit Events."""
24
+ api = get_api(debug=debug)
25
+ url = f"https://webexapis.com/v1/admin/securityAudit/events"
26
+ params = {}
27
+ if start_time is not None:
28
+ params["startTime"] = start_time
29
+ if end_time is not None:
30
+ params["endTime"] = end_time
31
+ if actor_id is not None:
32
+ params["actorId"] = actor_id
33
+ if event_categories is not None:
34
+ params["eventCategories"] = event_categories
35
+ if limit > 0:
36
+ params["max"] = limit
37
+ if offset > 0:
38
+ params["start"] = offset
39
+ org_id = get_org_id()
40
+ if org_id is not None:
41
+ params["orgId"] = org_id
42
+ result = None
43
+ try:
44
+ result = api.session.rest_get(url, params=params)
45
+ except WebexError as e:
46
+ handle_rest_error(e)
47
+ result = result or []
48
+ items = result.get("items", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
49
+ if output == "json":
50
+ print_json(items)
51
+ else:
52
+ print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
53
+
54
+
@@ -0,0 +1,51 @@
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 service-apps.")
9
+
10
+
11
+ @app.command("create")
12
+ def create(
13
+ application_id: str = typer.Argument(help="applicationId"),
14
+ client_id: str = typer.Option(None, "--client-id", help="(required) The OAuth client ID of the Service App"),
15
+ client_secret: str = typer.Option(None, "--client-secret", help="(required) The OAuth client secret of the Service App"),
16
+ target_org_id: str = typer.Option(None, "--target-org-id", help="(required) The organization ID for which the token will be created"),
17
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
18
+ output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
19
+ debug: bool = typer.Option(False, "--debug"),
20
+ ):
21
+ """Create Service App Access Token."""
22
+ api = get_api(debug=debug)
23
+ url = f"https://webexapis.com/v1/applications/{application_id}/token"
24
+ if json_body:
25
+ body = json.loads(json_body)
26
+ else:
27
+ body = {}
28
+ if client_id is not None:
29
+ body["clientId"] = client_id
30
+ if client_secret is not None:
31
+ body["clientSecret"] = client_secret
32
+ if target_org_id is not None:
33
+ body["targetOrgId"] = target_org_id
34
+ _missing = [f for f in ['clientId', 'clientSecret', 'targetOrgId'] if f not in body or body[f] is None]
35
+ if _missing:
36
+ typer.echo("Error: Missing required fields: " + ", ".join(_missing), err=True)
37
+ raise typer.Exit(1)
38
+ try:
39
+ result = api.session.rest_post(url, json=body)
40
+ except WebexError as e:
41
+ handle_rest_error(e)
42
+ if output == "json":
43
+ print_json(result)
44
+ elif isinstance(result, dict) and "id" in result:
45
+ typer.echo(f"Created: {result['id']}")
46
+ elif not result or result == {}:
47
+ typer.echo("Created.")
48
+ else:
49
+ print_json(result)
50
+
51
+
@@ -0,0 +1,209 @@
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 single-number-reach.")
10
+
11
+
12
+ @app.command("list")
13
+ def cmd_list(
14
+ location_id: str = typer.Argument(help="locationId"),
15
+ phone_number: str = typer.Option(None, "--phone-number", help="Filter phone numbers based on the comma-separated list provi"),
16
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
17
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
18
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
19
+ debug: bool = typer.Option(False, "--debug"),
20
+ ):
21
+ """Get Single Number Reach Primary Available Phone Numbers."""
22
+ api = get_api(debug=debug)
23
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/singleNumberReach/availableNumbers"
24
+ params = {}
25
+ if phone_number is not None:
26
+ params["phoneNumber"] = phone_number
27
+ if limit > 0:
28
+ params["max"] = limit
29
+ if offset > 0:
30
+ params["start"] = offset
31
+ org_id = get_org_id()
32
+ if org_id is not None:
33
+ params["orgId"] = org_id
34
+ result = None
35
+ try:
36
+ result = api.session.rest_get(url, params=params)
37
+ except WebexError as e:
38
+ handle_rest_error(e)
39
+ result = result or []
40
+ items = result.get("phoneNumbers", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
41
+ if output == "json":
42
+ print_json(items)
43
+ else:
44
+ print_table(items, columns=[('Phone Number', 'phoneNumber'), ('State', 'state')], limit=limit)
45
+
46
+
47
+
48
+ @app.command("create")
49
+ def create(
50
+ person_id: str = typer.Argument(help="personId"),
51
+ phone_number: str = typer.Option(None, "--phone-number", help="(required) Personal phone number used as single Number Reach."),
52
+ enabled: bool = typer.Option(None, "--enabled/--no-enabled", help="(required) A flag to enable or disable single Number Reach."),
53
+ name: str = typer.Option(None, "--name", help="(required) Name of the single number reach phone number entry."),
54
+ do_not_forward_calls_enabled: bool = typer.Option(None, "--do-not-forward-calls-enabled/--no-do-not-forward-calls-enabled", help="If enabled, the call forwarding settings of provided phone N"),
55
+ answer_confirmation_enabled: bool = typer.Option(None, "--answer-confirmation-enabled/--no-answer-confirmation-enabled", help="If enabled, the call recepient will be prompted to press a k"),
56
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
57
+ output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
58
+ debug: bool = typer.Option(False, "--debug"),
59
+ ):
60
+ """Create Single Number Reach For a Person."""
61
+ api = get_api(debug=debug)
62
+ url = f"https://webexapis.com/v1/telephony/config/people/{person_id}/singleNumberReach/numbers"
63
+ if json_body:
64
+ body = json.loads(json_body)
65
+ else:
66
+ body = {}
67
+ if phone_number is not None:
68
+ body["phoneNumber"] = phone_number
69
+ if enabled is not None:
70
+ body["enabled"] = enabled
71
+ if name is not None:
72
+ body["name"] = name
73
+ if do_not_forward_calls_enabled is not None:
74
+ body["doNotForwardCallsEnabled"] = do_not_forward_calls_enabled
75
+ if answer_confirmation_enabled is not None:
76
+ body["answerConfirmationEnabled"] = answer_confirmation_enabled
77
+ _missing = [f for f in ['phoneNumber', 'enabled', 'name'] if f not in body or body[f] is None]
78
+ if _missing:
79
+ typer.echo("Error: Missing required fields: " + ", ".join(_missing), err=True)
80
+ raise typer.Exit(1)
81
+ try:
82
+ result = api.session.rest_post(url, json=body)
83
+ except WebexError as e:
84
+ handle_rest_error(e)
85
+ if output == "json":
86
+ print_json(result)
87
+ elif isinstance(result, dict) and "id" in result:
88
+ typer.echo(f"Created: {result['id']}")
89
+ elif not result or result == {}:
90
+ typer.echo("Created.")
91
+ else:
92
+ print_json(result)
93
+
94
+
95
+
96
+ @app.command("list-single-number-reach")
97
+ def list_single_number_reach(
98
+ person_id: str = typer.Argument(help="personId"),
99
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
100
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
101
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
102
+ debug: bool = typer.Option(False, "--debug"),
103
+ ):
104
+ """Get Single Number Reach Settings For A Person."""
105
+ api = get_api(debug=debug)
106
+ url = f"https://webexapis.com/v1/telephony/config/people/{person_id}/singleNumberReach"
107
+ params = {}
108
+ if limit > 0:
109
+ params["max"] = limit
110
+ if offset > 0:
111
+ params["start"] = offset
112
+ result = None
113
+ try:
114
+ result = api.session.rest_get(url, params=params)
115
+ except WebexError as e:
116
+ handle_rest_error(e)
117
+ result = result or []
118
+ items = result.get("numbers", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
119
+ if output == "json":
120
+ print_json(items)
121
+ else:
122
+ print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
123
+
124
+
125
+
126
+ @app.command("update")
127
+ def update(
128
+ person_id: str = typer.Argument(help="personId"),
129
+ alert_all_numbers_for_click_to_dial_calls_enabled: bool = typer.Option(None, "--alert-all-numbers-for-click-to-dial-calls-enabled/--no-alert-all-numbers-for-click-to-dial-calls-enabled", help="Flag to enable alerting single number reach numbers for clic"),
130
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
131
+ debug: bool = typer.Option(False, "--debug"),
132
+ ):
133
+ """Update Single Number Reach Settings For A Person."""
134
+ api = get_api(debug=debug)
135
+ url = f"https://webexapis.com/v1/telephony/config/people/{person_id}/singleNumberReach"
136
+ if json_body:
137
+ body = json.loads(json_body)
138
+ else:
139
+ body = {}
140
+ if alert_all_numbers_for_click_to_dial_calls_enabled is not None:
141
+ body["alertAllNumbersForClickToDialCallsEnabled"] = alert_all_numbers_for_click_to_dial_calls_enabled
142
+ try:
143
+ result = api.session.rest_put(url, json=body)
144
+ except WebexError as e:
145
+ handle_rest_error(e)
146
+ typer.echo(f"Updated.")
147
+
148
+
149
+
150
+ @app.command("update-numbers")
151
+ def update_numbers(
152
+ person_id: str = typer.Argument(help="personId"),
153
+ id: str = typer.Argument(help="id"),
154
+ phone_number: str = typer.Option(None, "--phone-number", help="Personal phone number used as single Number Reach."),
155
+ enabled: bool = typer.Option(None, "--enabled/--no-enabled", help="A flag to enable or disable single Number Reach phone number"),
156
+ name: str = typer.Option(None, "--name", help="Name of the single number reach phone number entry."),
157
+ do_not_forward_calls_enabled: bool = typer.Option(None, "--do-not-forward-calls-enabled/--no-do-not-forward-calls-enabled", help="If enabled, the call forwarding settings of provided phone N"),
158
+ answer_confirmation_enabled: bool = typer.Option(None, "--answer-confirmation-enabled/--no-answer-confirmation-enabled", help="If enabled, the call recepient will be prompted to press a k"),
159
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
160
+ debug: bool = typer.Option(False, "--debug"),
161
+ ):
162
+ """Update Single Number Reach Settings For A Number."""
163
+ api = get_api(debug=debug)
164
+ url = f"https://webexapis.com/v1/telephony/config/people/{person_id}/singleNumberReach/numbers/{id}"
165
+ if json_body:
166
+ body = json.loads(json_body)
167
+ else:
168
+ body = {}
169
+ if phone_number is not None:
170
+ body["phoneNumber"] = phone_number
171
+ if enabled is not None:
172
+ body["enabled"] = enabled
173
+ if name is not None:
174
+ body["name"] = name
175
+ if do_not_forward_calls_enabled is not None:
176
+ body["doNotForwardCallsEnabled"] = do_not_forward_calls_enabled
177
+ if answer_confirmation_enabled is not None:
178
+ body["answerConfirmationEnabled"] = answer_confirmation_enabled
179
+ try:
180
+ result = api.session.rest_put(url, json=body)
181
+ except WebexError as e:
182
+ handle_rest_error(e)
183
+ typer.echo(f"Updated.")
184
+
185
+
186
+
187
+ @app.command("delete")
188
+ def delete(
189
+ person_id: str = typer.Argument(help="personId"),
190
+ id: str = typer.Argument(help="id"),
191
+ force: bool = typer.Option(False, "--force", help="Skip confirmation"),
192
+ debug: bool = typer.Option(False, "--debug"),
193
+ ):
194
+ """Delete A Single Number Reach Number."""
195
+ if not force:
196
+ typer.confirm(f"Delete {id}?", abort=True)
197
+ api = get_api(debug=debug)
198
+ url = f"https://webexapis.com/v1/telephony/config/people/{person_id}/singleNumberReach/numbers/{id}"
199
+ params = {}
200
+ org_id = get_org_id()
201
+ if org_id is not None:
202
+ params["orgId"] = org_id
203
+ try:
204
+ api.session.rest_delete(url, params=params)
205
+ except WebexError as e:
206
+ handle_rest_error(e)
207
+ typer.echo(f"Deleted: {id}")
208
+
209
+
@@ -0,0 +1,155 @@
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 team-memberships.")
9
+
10
+
11
+ @app.command("list")
12
+ def cmd_list(
13
+ team_id: str = typer.Option(..., "--team-id", help="List memberships for a team, by ID."),
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 Team Memberships."""
20
+ api = get_api(debug=debug)
21
+ url = f"https://webexapis.com/v1/team/memberships"
22
+ params = {}
23
+ if team_id is not None:
24
+ params["teamId"] = team_id
25
+ if limit > 0:
26
+ params["max"] = limit
27
+ if offset > 0:
28
+ params["start"] = offset
29
+ try:
30
+ if limit > 0:
31
+ result = api.session.rest_get(url, params=params)
32
+ result = result or {}
33
+ items = result.get("items", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
34
+ else:
35
+ items = list(api.session.follow_pagination(url=url, params=params, item_key="items"))
36
+ except WebexError as e:
37
+ handle_rest_error(e)
38
+ if output == "json":
39
+ print_json(items)
40
+ else:
41
+ print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
42
+
43
+
44
+
45
+ @app.command("create")
46
+ def create(
47
+ team_id: str = typer.Option(None, "--team-id", help="(required) The team ID."),
48
+ person_id: str = typer.Option(None, "--person-id", help="The person ID."),
49
+ person_email: str = typer.Option(None, "--person-email", help="The email address of the person."),
50
+ is_moderator: bool = typer.Option(None, "--is-moderator/--no-is-moderator", help="Whether or not the participant is a team moderator."),
51
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
52
+ output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
53
+ debug: bool = typer.Option(False, "--debug"),
54
+ ):
55
+ """Create a Team Membership."""
56
+ api = get_api(debug=debug)
57
+ url = f"https://webexapis.com/v1/team/memberships"
58
+ if json_body:
59
+ body = json.loads(json_body)
60
+ else:
61
+ body = {}
62
+ if team_id is not None:
63
+ body["teamId"] = team_id
64
+ if person_id is not None:
65
+ body["personId"] = person_id
66
+ if person_email is not None:
67
+ body["personEmail"] = person_email
68
+ if is_moderator is not None:
69
+ body["isModerator"] = is_moderator
70
+ _missing = [f for f in ['teamId'] if f not in body or body[f] is None]
71
+ if _missing:
72
+ typer.echo("Error: Missing required fields: " + ", ".join(_missing), err=True)
73
+ raise typer.Exit(1)
74
+ try:
75
+ result = api.session.rest_post(url, json=body)
76
+ except WebexError as e:
77
+ handle_rest_error(e)
78
+ if output == "json":
79
+ print_json(result)
80
+ elif isinstance(result, dict) and "id" in result:
81
+ typer.echo(f"Created: {result['id']}")
82
+ elif not result or result == {}:
83
+ typer.echo("Created.")
84
+ else:
85
+ print_json(result)
86
+
87
+
88
+
89
+ @app.command("show")
90
+ def show(
91
+ membership_id: str = typer.Argument(help="membershipId"),
92
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
93
+ debug: bool = typer.Option(False, "--debug"),
94
+ ):
95
+ """Get Team Membership Details."""
96
+ api = get_api(debug=debug)
97
+ url = f"https://webexapis.com/v1/team/memberships/{membership_id}"
98
+ try:
99
+ result = api.session.rest_get(url)
100
+ except WebexError as e:
101
+ handle_rest_error(e)
102
+ if output == "json":
103
+ print_json(result)
104
+ else:
105
+ if isinstance(result, dict):
106
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
107
+ elif isinstance(result, list):
108
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
109
+ else:
110
+ print_json(result)
111
+
112
+
113
+
114
+ @app.command("update")
115
+ def update(
116
+ membership_id: str = typer.Argument(help="membershipId"),
117
+ is_moderator: bool = typer.Option(None, "--is-moderator/--no-is-moderator", help="Whether or not the participant is a team moderator."),
118
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
119
+ debug: bool = typer.Option(False, "--debug"),
120
+ ):
121
+ """Update a Team Membership."""
122
+ api = get_api(debug=debug)
123
+ url = f"https://webexapis.com/v1/team/memberships/{membership_id}"
124
+ if json_body:
125
+ body = json.loads(json_body)
126
+ else:
127
+ body = {}
128
+ if is_moderator is not None:
129
+ body["isModerator"] = is_moderator
130
+ try:
131
+ result = api.session.rest_put(url, json=body)
132
+ except WebexError as e:
133
+ handle_rest_error(e)
134
+ typer.echo(f"Updated.")
135
+
136
+
137
+
138
+ @app.command("delete")
139
+ def delete(
140
+ membership_id: str = typer.Argument(help="membershipId"),
141
+ force: bool = typer.Option(False, "--force", help="Skip confirmation"),
142
+ debug: bool = typer.Option(False, "--debug"),
143
+ ):
144
+ """Delete a Team Membership."""
145
+ if not force:
146
+ typer.confirm(f"Delete {membership_id}?", abort=True)
147
+ api = get_api(debug=debug)
148
+ url = f"https://webexapis.com/v1/team/memberships/{membership_id}"
149
+ try:
150
+ api.session.rest_delete(url)
151
+ except WebexError as e:
152
+ handle_rest_error(e)
153
+ typer.echo(f"Deleted: {membership_id}")
154
+
155
+
@@ -0,0 +1,153 @@
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 teams.")
9
+
10
+
11
+ @app.command("list")
12
+ def cmd_list(
13
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
14
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
15
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
16
+ debug: bool = typer.Option(False, "--debug"),
17
+ ):
18
+ """List Teams."""
19
+ api = get_api(debug=debug)
20
+ url = f"https://webexapis.com/v1/teams"
21
+ params = {}
22
+ if limit > 0:
23
+ params["max"] = limit
24
+ if offset > 0:
25
+ params["start"] = offset
26
+ try:
27
+ if limit > 0:
28
+ result = api.session.rest_get(url, params=params)
29
+ result = result or {}
30
+ items = result.get("items", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
31
+ else:
32
+ items = list(api.session.follow_pagination(url=url, params=params, item_key="items"))
33
+ except WebexError as e:
34
+ handle_rest_error(e)
35
+ if output == "json":
36
+ print_json(items)
37
+ else:
38
+ print_table(items, columns=[('ID', 'id'), ('Name', 'name')], limit=limit)
39
+
40
+
41
+
42
+ @app.command("create")
43
+ def create(
44
+ name: str = typer.Option(None, "--name", help="(required) A user-friendly name for the team."),
45
+ description: str = typer.Option(None, "--description", help="The teams description."),
46
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
47
+ output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
48
+ debug: bool = typer.Option(False, "--debug"),
49
+ ):
50
+ """Create a Team."""
51
+ api = get_api(debug=debug)
52
+ url = f"https://webexapis.com/v1/teams"
53
+ if json_body:
54
+ body = json.loads(json_body)
55
+ else:
56
+ body = {}
57
+ if name is not None:
58
+ body["name"] = name
59
+ if description is not None:
60
+ body["description"] = description
61
+ _missing = [f for f in ['name'] if f not in body or body[f] is None]
62
+ if _missing:
63
+ typer.echo("Error: Missing required fields: " + ", ".join(_missing), err=True)
64
+ raise typer.Exit(1)
65
+ try:
66
+ result = api.session.rest_post(url, json=body)
67
+ except WebexError as e:
68
+ handle_rest_error(e)
69
+ if output == "json":
70
+ print_json(result)
71
+ elif isinstance(result, dict) and "id" in result:
72
+ typer.echo(f"Created: {result['id']}")
73
+ elif not result or result == {}:
74
+ typer.echo("Created.")
75
+ else:
76
+ print_json(result)
77
+
78
+
79
+
80
+ @app.command("show")
81
+ def show(
82
+ team_id: str = typer.Argument(help="teamId"),
83
+ description: str = typer.Option(None, "--description", help="The teams description."),
84
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
85
+ debug: bool = typer.Option(False, "--debug"),
86
+ ):
87
+ """Get Team Details."""
88
+ api = get_api(debug=debug)
89
+ url = f"https://webexapis.com/v1/teams/{team_id}"
90
+ params = {}
91
+ if description is not None:
92
+ params["description"] = description
93
+ try:
94
+ result = api.session.rest_get(url, params=params)
95
+ except WebexError as e:
96
+ handle_rest_error(e)
97
+ if output == "json":
98
+ print_json(result)
99
+ else:
100
+ if isinstance(result, dict):
101
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
102
+ elif isinstance(result, list):
103
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
104
+ else:
105
+ print_json(result)
106
+
107
+
108
+
109
+ @app.command("update")
110
+ def update(
111
+ team_id: str = typer.Argument(help="teamId"),
112
+ name: str = typer.Option(None, "--name", help="A user-friendly name for the team."),
113
+ description: str = typer.Option(None, "--description", help="The teams description."),
114
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
115
+ debug: bool = typer.Option(False, "--debug"),
116
+ ):
117
+ """Update a Team."""
118
+ api = get_api(debug=debug)
119
+ url = f"https://webexapis.com/v1/teams/{team_id}"
120
+ if json_body:
121
+ body = json.loads(json_body)
122
+ else:
123
+ body = {}
124
+ if name is not None:
125
+ body["name"] = name
126
+ if description is not None:
127
+ body["description"] = description
128
+ try:
129
+ result = api.session.rest_put(url, json=body)
130
+ except WebexError as e:
131
+ handle_rest_error(e)
132
+ typer.echo(f"Updated.")
133
+
134
+
135
+
136
+ @app.command("delete")
137
+ def delete(
138
+ team_id: str = typer.Argument(help="teamId"),
139
+ force: bool = typer.Option(False, "--force", help="Skip confirmation"),
140
+ debug: bool = typer.Option(False, "--debug"),
141
+ ):
142
+ """Delete a Team."""
143
+ if not force:
144
+ typer.confirm(f"Delete {team_id}?", abort=True)
145
+ api = get_api(debug=debug)
146
+ url = f"https://webexapis.com/v1/teams/{team_id}"
147
+ try:
148
+ api.session.rest_delete(url)
149
+ except WebexError as e:
150
+ handle_rest_error(e)
151
+ typer.echo(f"Deleted: {team_id}")
152
+
153
+
@@ -0,0 +1,42 @@
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 ucm-profile.")
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
+ """Read the List of UC Manager Profiles."""
20
+ api = get_api(debug=debug)
21
+ url = f"https://webexapis.com/v1/telephony/config/callingProfiles"
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("callingProfiles", 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
+