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,494 @@
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 location-voicemail.")
10
+
11
+
12
+ @app.command("show")
13
+ def show(
14
+ location_id: str = typer.Argument(help="locationId"),
15
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
16
+ debug: bool = typer.Option(False, "--debug"),
17
+ ):
18
+ """Get Location Voicemail."""
19
+ api = get_api(debug=debug)
20
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/voicemail"
21
+ params = {}
22
+ org_id = get_org_id()
23
+ if org_id is not None:
24
+ params["orgId"] = org_id
25
+ try:
26
+ result = api.session.rest_get(url, params=params)
27
+ except WebexError as e:
28
+ handle_rest_error(e)
29
+ if output == "json":
30
+ print_json(result)
31
+ else:
32
+ if isinstance(result, dict):
33
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
34
+ elif isinstance(result, list):
35
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
36
+ else:
37
+ print_json(result)
38
+
39
+
40
+
41
+ @app.command("update")
42
+ def update(
43
+ location_id: str = typer.Argument(help="locationId"),
44
+ voicemail_transcription_enabled: bool = typer.Option(None, "--voicemail-transcription-enabled/--no-voicemail-transcription-enabled", help="Set to `true` to enable voicemail transcription."),
45
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
46
+ debug: bool = typer.Option(False, "--debug"),
47
+ ):
48
+ """Update Location Voicemail."""
49
+ api = get_api(debug=debug)
50
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/voicemail"
51
+ params = {}
52
+ org_id = get_org_id()
53
+ if org_id is not None:
54
+ params["orgId"] = org_id
55
+ if json_body:
56
+ body = json.loads(json_body)
57
+ else:
58
+ body = {}
59
+ if voicemail_transcription_enabled is not None:
60
+ body["voicemailTranscriptionEnabled"] = voicemail_transcription_enabled
61
+ try:
62
+ result = api.session.rest_put(url, json=body, params=params)
63
+ except WebexError as e:
64
+ handle_rest_error(e)
65
+ typer.echo(f"Updated.")
66
+
67
+
68
+
69
+ @app.command("show-voice-portal")
70
+ def show_voice_portal(
71
+ location_id: str = typer.Argument(help="locationId"),
72
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
73
+ debug: bool = typer.Option(False, "--debug"),
74
+ ):
75
+ """Get VoicePortal."""
76
+ api = get_api(debug=debug)
77
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/voicePortal"
78
+ params = {}
79
+ org_id = get_org_id()
80
+ if org_id is not None:
81
+ params["orgId"] = org_id
82
+ try:
83
+ result = api.session.rest_get(url, params=params)
84
+ except WebexError as e:
85
+ handle_rest_error(e)
86
+ if output == "json":
87
+ print_json(result)
88
+ else:
89
+ if isinstance(result, dict):
90
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
91
+ elif isinstance(result, list):
92
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
93
+ else:
94
+ print_json(result)
95
+
96
+
97
+
98
+ @app.command("update-voice-portal")
99
+ def update_voice_portal(
100
+ location_id: str = typer.Argument(help="locationId"),
101
+ name: str = typer.Option(None, "--name", help="Voice Portal Name."),
102
+ language_code: str = typer.Option(None, "--language-code", help="Language code for voicemail group audio announcement."),
103
+ extension: str = typer.Option(None, "--extension", help="Extension of incoming call."),
104
+ phone_number: str = typer.Option(None, "--phone-number", help="Phone Number of incoming call."),
105
+ first_name: str = typer.Option(None, "--first-name", help="Caller ID First Name. This field has been deprecated. Please"),
106
+ last_name: str = typer.Option(None, "--last-name", help="Caller ID Last Name. This field has been deprecated. Please"),
107
+ dial_by_name: str = typer.Option(None, "--dial-by-name", help="Sets or clears the name to be used for dial by name function"),
108
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
109
+ debug: bool = typer.Option(False, "--debug"),
110
+ ):
111
+ """Update VoicePortal\n\nExample --json-body:\n '{"name":"...","languageCode":"...","extension":"...","phoneNumber":"...","firstName":"...","lastName":"...","passcode":{"newPasscode":"...","confirmPasscode":"..."},"directLineCallerIdName":{"selection":"CUSTOM_NAME","customName":"..."}}'."""
112
+ api = get_api(debug=debug)
113
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/voicePortal"
114
+ params = {}
115
+ org_id = get_org_id()
116
+ if org_id is not None:
117
+ params["orgId"] = org_id
118
+ if json_body:
119
+ body = json.loads(json_body)
120
+ else:
121
+ body = {}
122
+ if name is not None:
123
+ body["name"] = name
124
+ if language_code is not None:
125
+ body["languageCode"] = language_code
126
+ if extension is not None:
127
+ body["extension"] = extension
128
+ if phone_number is not None:
129
+ body["phoneNumber"] = phone_number
130
+ if first_name is not None:
131
+ body["firstName"] = first_name
132
+ if last_name is not None:
133
+ body["lastName"] = last_name
134
+ if dial_by_name is not None:
135
+ body["dialByName"] = dial_by_name
136
+ try:
137
+ result = api.session.rest_put(url, json=body, params=params)
138
+ except WebexError as e:
139
+ handle_rest_error(e)
140
+ typer.echo(f"Updated.")
141
+
142
+
143
+
144
+ @app.command("show-passcode-rules")
145
+ def show_passcode_rules(
146
+ location_id: str = typer.Argument(help="locationId"),
147
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
148
+ debug: bool = typer.Option(False, "--debug"),
149
+ ):
150
+ """Get VoicePortal Passcode Rule."""
151
+ api = get_api(debug=debug)
152
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/voicePortal/passcodeRules"
153
+ params = {}
154
+ org_id = get_org_id()
155
+ if org_id is not None:
156
+ params["orgId"] = org_id
157
+ try:
158
+ result = api.session.rest_get(url, params=params)
159
+ except WebexError as e:
160
+ handle_rest_error(e)
161
+ if output == "json":
162
+ print_json(result)
163
+ else:
164
+ if isinstance(result, dict):
165
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
166
+ elif isinstance(result, list):
167
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
168
+ else:
169
+ print_json(result)
170
+
171
+
172
+
173
+ @app.command("list")
174
+ def cmd_list(
175
+ location_id: str = typer.Option(None, "--location-id", help="Location to which the voicemail group belongs."),
176
+ name: str = typer.Option(None, "--name", help="Search (Contains) based on voicemail group name"),
177
+ phone_number: str = typer.Option(None, "--phone-number", help="Search (Contains) based on number or extension"),
178
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
179
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
180
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
181
+ debug: bool = typer.Option(False, "--debug"),
182
+ ):
183
+ """List VoicemailGroup."""
184
+ api = get_api(debug=debug)
185
+ url = f"https://webexapis.com/v1/telephony/config/voicemailGroups"
186
+ params = {}
187
+ if location_id is not None:
188
+ params["locationId"] = location_id
189
+ if name is not None:
190
+ params["name"] = name
191
+ if phone_number is not None:
192
+ params["phoneNumber"] = phone_number
193
+ if limit > 0:
194
+ params["max"] = limit
195
+ if offset > 0:
196
+ params["start"] = offset
197
+ org_id = get_org_id()
198
+ if org_id is not None:
199
+ params["orgId"] = org_id
200
+ result = None
201
+ try:
202
+ result = api.session.rest_get(url, params=params)
203
+ except WebexError as e:
204
+ handle_rest_error(e)
205
+ result = result or []
206
+ items = result.get("voicemailGroups", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
207
+ if output == "json":
208
+ print_json(items)
209
+ else:
210
+ print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
211
+
212
+
213
+
214
+ @app.command("show-voicemail-groups")
215
+ def show_voicemail_groups(
216
+ location_id: str = typer.Argument(help="locationId"),
217
+ voicemail_group_id: str = typer.Argument(help="voicemailGroupId"),
218
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
219
+ debug: bool = typer.Option(False, "--debug"),
220
+ ):
221
+ """Get Location Voicemail Group."""
222
+ api = get_api(debug=debug)
223
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/voicemailGroups/{voicemail_group_id}"
224
+ params = {}
225
+ org_id = get_org_id()
226
+ if org_id is not None:
227
+ params["orgId"] = org_id
228
+ try:
229
+ result = api.session.rest_get(url, params=params)
230
+ except WebexError as e:
231
+ handle_rest_error(e)
232
+ if output == "json":
233
+ print_json(result)
234
+ else:
235
+ if isinstance(result, dict):
236
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
237
+ elif isinstance(result, list):
238
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
239
+ else:
240
+ print_json(result)
241
+
242
+
243
+
244
+ @app.command("update-voicemail-groups")
245
+ def update_voicemail_groups(
246
+ location_id: str = typer.Argument(help="locationId"),
247
+ voicemail_group_id: str = typer.Argument(help="voicemailGroupId"),
248
+ name: str = typer.Option(None, "--name", help="Set the name of the voicemail group."),
249
+ phone_number: str = typer.Option(None, "--phone-number", help="Set voicemail group phone number."),
250
+ extension: str = typer.Option(None, "--extension", help="Set unique voicemail group extension number."),
251
+ first_name: str = typer.Option(None, "--first-name", help="Set the voicemail group caller ID first name. This field has"),
252
+ last_name: str = typer.Option(None, "--last-name", help="Set the voicemail group called ID last name. This field has"),
253
+ enabled: bool = typer.Option(None, "--enabled/--no-enabled", help="Set to `true` to enable the voicemail group."),
254
+ passcode: str = typer.Option(None, "--passcode", help="Set passcode to access voicemail group when calling."),
255
+ language_code: str = typer.Option(None, "--language-code", help="Language code for the voicemail group audio announcement."),
256
+ greeting: str = typer.Option(None, "--greeting", help="Choices: DEFAULT, CUSTOM"),
257
+ greeting_description: str = typer.Option(None, "--greeting-description", help="CUSTOM greeting for previously uploaded."),
258
+ dial_by_name: str = typer.Option(None, "--dial-by-name", help="Sets or clears the name to be used for dial by name function"),
259
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
260
+ debug: bool = typer.Option(False, "--debug"),
261
+ ):
262
+ """Modify Location Voicemail Group\n\nExample --json-body:\n '{"name":"...","phoneNumber":"...","extension":0,"firstName":"...","lastName":"...","enabled":true,"passcode":0,"languageCode":"..."}'."""
263
+ api = get_api(debug=debug)
264
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/voicemailGroups/{voicemail_group_id}"
265
+ params = {}
266
+ org_id = get_org_id()
267
+ if org_id is not None:
268
+ params["orgId"] = org_id
269
+ if json_body:
270
+ body = json.loads(json_body)
271
+ else:
272
+ body = {}
273
+ if name is not None:
274
+ body["name"] = name
275
+ if phone_number is not None:
276
+ body["phoneNumber"] = phone_number
277
+ if extension is not None:
278
+ body["extension"] = extension
279
+ if first_name is not None:
280
+ body["firstName"] = first_name
281
+ if last_name is not None:
282
+ body["lastName"] = last_name
283
+ if enabled is not None:
284
+ body["enabled"] = enabled
285
+ if passcode is not None:
286
+ body["passcode"] = passcode
287
+ if language_code is not None:
288
+ body["languageCode"] = language_code
289
+ if greeting is not None:
290
+ body["greeting"] = greeting
291
+ if greeting_description is not None:
292
+ body["greetingDescription"] = greeting_description
293
+ if dial_by_name is not None:
294
+ body["dialByName"] = dial_by_name
295
+ try:
296
+ result = api.session.rest_put(url, json=body, params=params)
297
+ except WebexError as e:
298
+ handle_rest_error(e)
299
+ typer.echo(f"Updated.")
300
+
301
+
302
+
303
+ @app.command("delete")
304
+ def delete(
305
+ location_id: str = typer.Argument(help="locationId"),
306
+ voicemail_group_id: str = typer.Argument(help="voicemailGroupId"),
307
+ force: bool = typer.Option(False, "--force", help="Skip confirmation"),
308
+ debug: bool = typer.Option(False, "--debug"),
309
+ ):
310
+ """Delete a Voicemail Group for a Location."""
311
+ if not force:
312
+ typer.confirm(f"Delete {voicemail_group_id}?", abort=True)
313
+ api = get_api(debug=debug)
314
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/voicemailGroups/{voicemail_group_id}"
315
+ params = {}
316
+ org_id = get_org_id()
317
+ if org_id is not None:
318
+ params["orgId"] = org_id
319
+ try:
320
+ api.session.rest_delete(url, params=params)
321
+ except WebexError as e:
322
+ handle_rest_error(e)
323
+ typer.echo(f"Deleted: {voicemail_group_id}")
324
+
325
+
326
+
327
+ @app.command("create")
328
+ def create(
329
+ location_id: str = typer.Argument(help="locationId"),
330
+ name: str = typer.Option(None, "--name", help="(required) Set name to create new voicemail group for a particular loca"),
331
+ phone_number: str = typer.Option(None, "--phone-number", help="Set voicemail group phone number for this particular locatio"),
332
+ extension: str = typer.Option(None, "--extension", help="(required) Set unique voicemail group extension number for this particu"),
333
+ first_name: str = typer.Option(None, "--first-name", help="Set voicemail group caller ID first name. This field has bee"),
334
+ last_name: str = typer.Option(None, "--last-name", help="Set voicemail group called ID last name. This field has been"),
335
+ passcode: str = typer.Option(None, "--passcode", help="(required) Set passcode to access voicemail group when calling."),
336
+ language_code: str = typer.Option(None, "--language-code", help="(required) Language code for voicemail group audio announcement."),
337
+ dial_by_name: str = typer.Option(None, "--dial-by-name", help="The name to be used for dial by name functions. Characters"),
338
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
339
+ output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
340
+ debug: bool = typer.Option(False, "--debug"),
341
+ ):
342
+ """Create a new Voicemail Group for a Location\n\nExample --json-body:\n '{"name":"...","extension":0,"passcode":0,"languageCode":"...","messageStorage":{"storageType":"INTERNAL","externalEmail":"..."},"notifications":{"enabled":true,"destination":"..."},"faxMessage":{"enabled":true,"phoneNumber":"...","extension":0},"transferToNumber":{"enabled":true,"destination":"..."}}'."""
343
+ api = get_api(debug=debug)
344
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/voicemailGroups"
345
+ params = {}
346
+ org_id = get_org_id()
347
+ if org_id is not None:
348
+ params["orgId"] = org_id
349
+ if json_body:
350
+ body = json.loads(json_body)
351
+ else:
352
+ body = {}
353
+ if name is not None:
354
+ body["name"] = name
355
+ if phone_number is not None:
356
+ body["phoneNumber"] = phone_number
357
+ if extension is not None:
358
+ body["extension"] = extension
359
+ if first_name is not None:
360
+ body["firstName"] = first_name
361
+ if last_name is not None:
362
+ body["lastName"] = last_name
363
+ if passcode is not None:
364
+ body["passcode"] = passcode
365
+ if language_code is not None:
366
+ body["languageCode"] = language_code
367
+ if dial_by_name is not None:
368
+ body["dialByName"] = dial_by_name
369
+ _missing = [f for f in ['name', 'extension', 'passcode', 'languageCode'] if f not in body or body[f] is None]
370
+ if _missing:
371
+ typer.echo("Error: Missing required fields: " + ", ".join(_missing), err=True)
372
+ raise typer.Exit(1)
373
+ try:
374
+ result = api.session.rest_post(url, json=body, params=params)
375
+ except WebexError as e:
376
+ handle_rest_error(e)
377
+ if output == "json":
378
+ print_json(result)
379
+ elif isinstance(result, dict) and "id" in result:
380
+ typer.echo(f"Created: {result['id']}")
381
+ elif not result or result == {}:
382
+ typer.echo("Created.")
383
+ else:
384
+ print_json(result)
385
+
386
+
387
+
388
+ @app.command("list-available-numbers-fax-message")
389
+ def list_available_numbers_fax_message(
390
+ location_id: str = typer.Argument(help="locationId"),
391
+ phone_number: str = typer.Option(None, "--phone-number", help="Filter phone numbers based on the comma-separated list provi"),
392
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
393
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
394
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
395
+ debug: bool = typer.Option(False, "--debug"),
396
+ ):
397
+ """Get Voicemail Group Fax Message Available Phone Numbers."""
398
+ api = get_api(debug=debug)
399
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/voicemailGroups/faxMessage/availableNumbers"
400
+ params = {}
401
+ if phone_number is not None:
402
+ params["phoneNumber"] = phone_number
403
+ if limit > 0:
404
+ params["max"] = limit
405
+ if offset > 0:
406
+ params["start"] = offset
407
+ org_id = get_org_id()
408
+ if org_id is not None:
409
+ params["orgId"] = org_id
410
+ result = None
411
+ try:
412
+ result = api.session.rest_get(url, params=params)
413
+ except WebexError as e:
414
+ handle_rest_error(e)
415
+ result = result or []
416
+ items = result.get("phoneNumbers", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
417
+ if output == "json":
418
+ print_json(items)
419
+ else:
420
+ print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
421
+
422
+
423
+
424
+ @app.command("list-available-numbers-voicemail-groups")
425
+ def list_available_numbers_voicemail_groups(
426
+ location_id: str = typer.Argument(help="locationId"),
427
+ phone_number: str = typer.Option(None, "--phone-number", help="Filter phone numbers based on the comma-separated list provi"),
428
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
429
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
430
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
431
+ debug: bool = typer.Option(False, "--debug"),
432
+ ):
433
+ """Get Voicemail Group Available Phone Numbers."""
434
+ api = get_api(debug=debug)
435
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/voicemailGroups/availableNumbers"
436
+ params = {}
437
+ if phone_number is not None:
438
+ params["phoneNumber"] = phone_number
439
+ if limit > 0:
440
+ params["max"] = limit
441
+ if offset > 0:
442
+ params["start"] = offset
443
+ org_id = get_org_id()
444
+ if org_id is not None:
445
+ params["orgId"] = org_id
446
+ result = None
447
+ try:
448
+ result = api.session.rest_get(url, params=params)
449
+ except WebexError as e:
450
+ handle_rest_error(e)
451
+ result = result or []
452
+ items = result.get("phoneNumbers", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
453
+ if output == "json":
454
+ print_json(items)
455
+ else:
456
+ print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
457
+
458
+
459
+
460
+ @app.command("list-available-numbers-voice-portal")
461
+ def list_available_numbers_voice_portal(
462
+ location_id: str = typer.Argument(help="locationId"),
463
+ phone_number: str = typer.Option(None, "--phone-number", help="Filter phone numbers based on the comma-separated list provi"),
464
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
465
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
466
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
467
+ debug: bool = typer.Option(False, "--debug"),
468
+ ):
469
+ """Get VoicePortal Available Phone Numbers."""
470
+ api = get_api(debug=debug)
471
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/voicePortal/availableNumbers"
472
+ params = {}
473
+ if phone_number is not None:
474
+ params["phoneNumber"] = phone_number
475
+ if limit > 0:
476
+ params["max"] = limit
477
+ if offset > 0:
478
+ params["start"] = offset
479
+ org_id = get_org_id()
480
+ if org_id is not None:
481
+ params["orgId"] = org_id
482
+ result = None
483
+ try:
484
+ result = api.session.rest_get(url, params=params)
485
+ except WebexError as e:
486
+ handle_rest_error(e)
487
+ result = result or []
488
+ items = result.get("phoneNumbers", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
489
+ if output == "json":
490
+ print_json(items)
491
+ else:
492
+ print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
493
+
494
+