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,1297 @@
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-settings.")
10
+
11
+
12
+ @app.command("list")
13
+ def cmd_list(
14
+ dial_plan_id: str = typer.Argument(help="dialPlanId"),
15
+ dial_pattern: str = typer.Option(None, "--dial-pattern", help="An enterprise dial pattern is represented by a sequence of d"),
16
+ order: str = typer.Option(None, "--order", help="Order the dial patterns according to the designated fields."),
17
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
18
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
19
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
20
+ debug: bool = typer.Option(False, "--debug"),
21
+ ):
22
+ """Read the List of Dial Patterns."""
23
+ api = get_api(debug=debug)
24
+ url = f"https://webexapis.com/v1/telephony/config/premisePstn/dialPlans/{dial_plan_id}/dialPatterns"
25
+ params = {}
26
+ if dial_pattern is not None:
27
+ params["dialPattern"] = dial_pattern
28
+ if order is not None:
29
+ params["order"] = order
30
+ if limit > 0:
31
+ params["max"] = limit
32
+ if offset > 0:
33
+ params["start"] = offset
34
+ org_id = get_org_id()
35
+ if org_id is not None:
36
+ params["orgId"] = org_id
37
+ result = None
38
+ try:
39
+ result = api.session.rest_get(url, params=params)
40
+ except WebexError as e:
41
+ handle_rest_error(e)
42
+ result = result or []
43
+ items = result.get("dialPatterns", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
44
+ if output == "json":
45
+ print_json(items)
46
+ else:
47
+ print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
48
+
49
+
50
+
51
+ @app.command("list-1")
52
+ def list_1(
53
+ name: str = typer.Option(None, "--name", help="List locations whose name contains this string."),
54
+ order: str = typer.Option(None, "--order", help="Sort the list of locations based on `name`, either asc or de"),
55
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
56
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
57
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
58
+ debug: bool = typer.Option(False, "--debug"),
59
+ ):
60
+ """List Locations Webex Calling Details."""
61
+ api = get_api(debug=debug)
62
+ url = f"https://webexapis.com/v1/telephony/config/locations"
63
+ params = {}
64
+ if name is not None:
65
+ params["name"] = name
66
+ if order is not None:
67
+ params["order"] = order
68
+ if limit > 0:
69
+ params["max"] = limit
70
+ if offset > 0:
71
+ params["start"] = offset
72
+ org_id = get_org_id()
73
+ if org_id is not None:
74
+ params["orgId"] = org_id
75
+ try:
76
+ if limit > 0:
77
+ result = api.session.rest_get(url, params=params)
78
+ result = result or {}
79
+ items = result.get("locations", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
80
+ else:
81
+ items = list(api.session.follow_pagination(url=url, params=params, item_key="locations"))
82
+ except WebexError as e:
83
+ handle_rest_error(e)
84
+ if output == "json":
85
+ print_json(items)
86
+ else:
87
+ print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
88
+
89
+
90
+
91
+ @app.command("create")
92
+ def create(
93
+ id_param: str = typer.Option(None, "--id", help="(required) A unique identifier for the location."),
94
+ name: str = typer.Option(None, "--name", help="(required) The name of the location."),
95
+ time_zone: str = typer.Option(None, "--time-zone", help="(required) Time zone associated with this location. Refer to this link"),
96
+ preferred_language: str = typer.Option(None, "--preferred-language", help="(required) Default email language."),
97
+ announcement_language: str = typer.Option(None, "--announcement-language", help="(required) Location's phone announcement language."),
98
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
99
+ output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
100
+ debug: bool = typer.Option(False, "--debug"),
101
+ ):
102
+ """Enable a Location for Webex Calling\n\nExample --json-body:\n '{"id":"...","name":"...","timeZone":"...","preferredLanguage":"...","announcementLanguage":"...","address":{"address1":"...","city":"...","state":"...","postalCode":"...","country":"...","address2":"..."}}'."""
103
+ api = get_api(debug=debug)
104
+ url = f"https://webexapis.com/v1/telephony/config/locations"
105
+ params = {}
106
+ org_id = get_org_id()
107
+ if org_id is not None:
108
+ params["orgId"] = org_id
109
+ if json_body:
110
+ body = json.loads(json_body)
111
+ else:
112
+ body = {}
113
+ if id_param is not None:
114
+ body["id"] = id_param
115
+ if name is not None:
116
+ body["name"] = name
117
+ if time_zone is not None:
118
+ body["timeZone"] = time_zone
119
+ if preferred_language is not None:
120
+ body["preferredLanguage"] = preferred_language
121
+ if announcement_language is not None:
122
+ body["announcementLanguage"] = announcement_language
123
+ _missing = [f for f in ['id', 'name', 'timeZone', 'preferredLanguage', 'announcementLanguage'] if f not in body or body[f] is None]
124
+ if _missing:
125
+ typer.echo("Error: Missing required fields: " + ", ".join(_missing), err=True)
126
+ raise typer.Exit(1)
127
+ try:
128
+ result = api.session.rest_post(url, json=body, params=params)
129
+ except WebexError as e:
130
+ handle_rest_error(e)
131
+ if output == "json":
132
+ print_json(result)
133
+ elif isinstance(result, dict) and "id" in result:
134
+ typer.echo(f"Created: {result['id']}")
135
+ elif not result or result == {}:
136
+ typer.echo("Created.")
137
+ else:
138
+ print_json(result)
139
+
140
+
141
+
142
+ @app.command("show")
143
+ def show(
144
+ location_id: str = typer.Argument(help="locationId"),
145
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
146
+ debug: bool = typer.Option(False, "--debug"),
147
+ ):
148
+ """Get Location Webex Calling Details."""
149
+ api = get_api(debug=debug)
150
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}"
151
+ params = {}
152
+ org_id = get_org_id()
153
+ if org_id is not None:
154
+ params["orgId"] = org_id
155
+ try:
156
+ result = api.session.rest_get(url, params=params)
157
+ except WebexError as e:
158
+ handle_rest_error(e)
159
+ if output == "json":
160
+ print_json(result)
161
+ else:
162
+ if isinstance(result, dict):
163
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
164
+ elif isinstance(result, list):
165
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
166
+ else:
167
+ print_json(result)
168
+
169
+
170
+
171
+ @app.command("update")
172
+ def update(
173
+ location_id: str = typer.Argument(help="locationId"),
174
+ announcement_language: str = typer.Option(None, "--announcement-language", help="Location's phone announcement language."),
175
+ external_caller_id_name: str = typer.Option(None, "--external-caller-id-name", help="External caller ID name value. Unicode characters."),
176
+ p_access_network_info: str = typer.Option(None, "--p-access-network-info", help="Emergency Location Identifier for a location. The `pAccessNe"),
177
+ outside_dial_digit: str = typer.Option(None, "--outside-dial-digit", help="Must dial to reach an outside line. Default is none."),
178
+ enforce_outside_dial_digit: bool = typer.Option(None, "--enforce-outside-dial-digit/--no-enforce-outside-dial-digit", help="True when enforcing outside dial digit at location level to"),
179
+ routing_prefix: str = typer.Option(None, "--routing-prefix", help="Must dial a prefix when calling between locations having sam"),
180
+ charge_number: str = typer.Option(None, "--charge-number", help="Set the chargeable number for the line placing the call. Wh"),
181
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
182
+ debug: bool = typer.Option(False, "--debug"),
183
+ ):
184
+ """Update Location Webex Calling Details\n\nExample --json-body:\n '{"announcementLanguage":"...","callingLineId":{"name":"...","phoneNumber":"..."},"connection":{"type":"ROUTE_GROUP","id":"..."},"externalCallerIdName":"...","pAccessNetworkInfo":"...","outsideDialDigit":"...","enforceOutsideDialDigit":true,"routingPrefix":"..."}'."""
185
+ api = get_api(debug=debug)
186
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}"
187
+ params = {}
188
+ org_id = get_org_id()
189
+ if org_id is not None:
190
+ params["orgId"] = org_id
191
+ if json_body:
192
+ body = json.loads(json_body)
193
+ else:
194
+ body = {}
195
+ if announcement_language is not None:
196
+ body["announcementLanguage"] = announcement_language
197
+ if external_caller_id_name is not None:
198
+ body["externalCallerIdName"] = external_caller_id_name
199
+ if p_access_network_info is not None:
200
+ body["pAccessNetworkInfo"] = p_access_network_info
201
+ if outside_dial_digit is not None:
202
+ body["outsideDialDigit"] = outside_dial_digit
203
+ if enforce_outside_dial_digit is not None:
204
+ body["enforceOutsideDialDigit"] = enforce_outside_dial_digit
205
+ if routing_prefix is not None:
206
+ body["routingPrefix"] = routing_prefix
207
+ if charge_number is not None:
208
+ body["chargeNumber"] = charge_number
209
+ try:
210
+ result = api.session.rest_put(url, json=body, params=params)
211
+ except WebexError as e:
212
+ handle_rest_error(e)
213
+ typer.echo(f"Updated.")
214
+
215
+
216
+
217
+ @app.command("list-update-routing-prefix")
218
+ def list_update_routing_prefix(
219
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
220
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
221
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
222
+ debug: bool = typer.Option(False, "--debug"),
223
+ ):
224
+ """Get a List of Update Routing Prefix jobs."""
225
+ api = get_api(debug=debug)
226
+ url = f"https://webexapis.com/v1/telephony/config/jobs/updateRoutingPrefix"
227
+ params = {}
228
+ if limit > 0:
229
+ params["max"] = limit
230
+ if offset > 0:
231
+ params["start"] = offset
232
+ org_id = get_org_id()
233
+ if org_id is not None:
234
+ params["orgId"] = org_id
235
+ result = None
236
+ try:
237
+ result = api.session.rest_get(url, params=params)
238
+ except WebexError as e:
239
+ handle_rest_error(e)
240
+ result = result or []
241
+ items = result.get("items", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
242
+ if output == "json":
243
+ print_json(items)
244
+ else:
245
+ print_table(items, columns=[('ID', 'id'), ('Status', 'latestExecutionStatus')], limit=limit)
246
+
247
+
248
+
249
+ @app.command("show-update-routing-prefix")
250
+ def show_update_routing_prefix(
251
+ job_id: str = typer.Argument(help="jobId"),
252
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
253
+ debug: bool = typer.Option(False, "--debug"),
254
+ ):
255
+ """Get the job status of Update Routing Prefix job."""
256
+ api = get_api(debug=debug)
257
+ url = f"https://webexapis.com/v1/telephony/config/jobs/updateRoutingPrefix/{job_id}"
258
+ params = {}
259
+ org_id = get_org_id()
260
+ if org_id is not None:
261
+ params["orgId"] = org_id
262
+ try:
263
+ result = api.session.rest_get(url, params=params)
264
+ except WebexError as e:
265
+ handle_rest_error(e)
266
+ if output == "json":
267
+ print_json(result)
268
+ else:
269
+ if isinstance(result, dict):
270
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
271
+ elif isinstance(result, list):
272
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
273
+ else:
274
+ print_json(result)
275
+
276
+
277
+
278
+ @app.command("show-errors")
279
+ def show_errors(
280
+ job_id: str = typer.Argument(help="jobId"),
281
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
282
+ debug: bool = typer.Option(False, "--debug"),
283
+ ):
284
+ """Get job errors for update routing prefix job."""
285
+ api = get_api(debug=debug)
286
+ url = f"https://webexapis.com/v1/telephony/config/jobs/updateRoutingPrefix/{job_id}/errors"
287
+ params = {}
288
+ org_id = get_org_id()
289
+ if org_id is not None:
290
+ params["orgId"] = org_id
291
+ try:
292
+ result = api.session.rest_get(url, params=params)
293
+ except WebexError as e:
294
+ handle_rest_error(e)
295
+ if output == "json":
296
+ print_json(result)
297
+ else:
298
+ if isinstance(result, dict):
299
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
300
+ elif isinstance(result, list):
301
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
302
+ else:
303
+ print_json(result)
304
+
305
+
306
+
307
+ @app.command("change-announcement-language")
308
+ def change_announcement_language(
309
+ location_id: str = typer.Argument(help="locationId"),
310
+ agent_enabled: str = typer.Option(None, "--agent-enabled", help="Set to `true` to change announcement language for existing p"),
311
+ service_enabled: str = typer.Option(None, "--service-enabled", help="Set to `true` to change announcement language for existing f"),
312
+ announcement_language_code: str = typer.Option(None, "--announcement-language-code", help="Language code."),
313
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body"),
314
+ debug: bool = typer.Option(False, "--debug"),
315
+ ):
316
+ """Change Announcement Language."""
317
+ api = get_api(debug=debug)
318
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/actions/modifyAnnouncementLanguage/invoke"
319
+ params = {}
320
+ org_id = get_org_id()
321
+ if org_id is not None:
322
+ params["orgId"] = org_id
323
+ if json_body:
324
+ body = json.loads(json_body)
325
+ else:
326
+ body = {}
327
+ if agent_enabled is not None:
328
+ body["agentEnabled"] = agent_enabled
329
+ if service_enabled is not None:
330
+ body["serviceEnabled"] = service_enabled
331
+ if announcement_language_code is not None:
332
+ body["announcementLanguageCode"] = announcement_language_code
333
+ try:
334
+ result = api.session.rest_post(url, json=body, params=params)
335
+ except WebexError as e:
336
+ handle_rest_error(e)
337
+ print_json(result)
338
+
339
+
340
+
341
+ @app.command("show-emergency-callback-number")
342
+ def show_emergency_callback_number(
343
+ location_id: str = typer.Argument(help="locationId"),
344
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
345
+ debug: bool = typer.Option(False, "--debug"),
346
+ ):
347
+ """Get a Location Emergency callback number."""
348
+ api = get_api(debug=debug)
349
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/features/emergencyCallbackNumber"
350
+ params = {}
351
+ org_id = get_org_id()
352
+ if org_id is not None:
353
+ params["orgId"] = org_id
354
+ try:
355
+ result = api.session.rest_get(url, params=params)
356
+ except WebexError as e:
357
+ handle_rest_error(e)
358
+ if output == "json":
359
+ print_json(result)
360
+ else:
361
+ if isinstance(result, dict):
362
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
363
+ elif isinstance(result, list):
364
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
365
+ else:
366
+ print_json(result)
367
+
368
+
369
+
370
+ @app.command("update-emergency-callback-number")
371
+ def update_emergency_callback_number(
372
+ location_id: str = typer.Argument(help="locationId"),
373
+ selected: str = typer.Option(None, "--selected", help="Choices: LOCATION_NUMBER, LOCATION_MEMBER_NUMBER"),
374
+ location_member_id: str = typer.Option(None, "--location-member-id", help="Member ID of user/place/virtual line/hunt group within the l"),
375
+ elin_expiry_time_minutes: str = typer.Option(None, "--elin-expiry-time-minutes", help="ELIN (Emergency Location Identification Number) provides loc"),
376
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
377
+ debug: bool = typer.Option(False, "--debug"),
378
+ ):
379
+ """Update a Location Emergency callback number\n\nExample --json-body:\n '{"selected":"LOCATION_NUMBER","locationMemberId":"...","elinExpiryTimeMinutes":0}'."""
380
+ api = get_api(debug=debug)
381
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/features/emergencyCallbackNumber"
382
+ params = {}
383
+ org_id = get_org_id()
384
+ if org_id is not None:
385
+ params["orgId"] = org_id
386
+ if json_body:
387
+ body = json.loads(json_body)
388
+ else:
389
+ body = {}
390
+ if selected is not None:
391
+ body["selected"] = selected
392
+ if location_member_id is not None:
393
+ body["locationMemberId"] = location_member_id
394
+ if elin_expiry_time_minutes is not None:
395
+ body["elinExpiryTimeMinutes"] = elin_expiry_time_minutes
396
+ try:
397
+ result = api.session.rest_put(url, json=body, params=params)
398
+ except WebexError as e:
399
+ handle_rest_error(e)
400
+ typer.echo(f"Updated.")
401
+
402
+
403
+
404
+ @app.command("validate-the-list")
405
+ def validate_the_list(
406
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body"),
407
+ debug: bool = typer.Option(False, "--debug"),
408
+ ):
409
+ """Validate the List of Extensions\n\nExample --json-body:\n '{"extensions":["..."]}'."""
410
+ api = get_api(debug=debug)
411
+ url = f"https://webexapis.com/v1/telephony/config/actions/validateExtensions/invoke"
412
+ params = {}
413
+ org_id = get_org_id()
414
+ if org_id is not None:
415
+ params["orgId"] = org_id
416
+ if json_body:
417
+ body = json.loads(json_body)
418
+ else:
419
+ body = {}
420
+ try:
421
+ result = api.session.rest_post(url, json=body, params=params)
422
+ except WebexError as e:
423
+ handle_rest_error(e)
424
+ print_json(result)
425
+
426
+
427
+
428
+ @app.command("validate-extensions")
429
+ def validate_extensions(
430
+ location_id: str = typer.Argument(help="locationId"),
431
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body"),
432
+ debug: bool = typer.Option(False, "--debug"),
433
+ ):
434
+ """Validate Extensions\n\nExample --json-body:\n '{"extensions":["..."]}'."""
435
+ api = get_api(debug=debug)
436
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/actions/validateExtensions/invoke"
437
+ params = {}
438
+ org_id = get_org_id()
439
+ if org_id is not None:
440
+ params["orgId"] = org_id
441
+ if json_body:
442
+ body = json.loads(json_body)
443
+ else:
444
+ body = {}
445
+ try:
446
+ result = api.session.rest_post(url, json=body, params=params)
447
+ except WebexError as e:
448
+ handle_rest_error(e)
449
+ print_json(result)
450
+
451
+
452
+
453
+ @app.command("show-music-on-hold")
454
+ def show_music_on_hold(
455
+ location_id: str = typer.Argument(help="locationId"),
456
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
457
+ debug: bool = typer.Option(False, "--debug"),
458
+ ):
459
+ """Get Music On Hold."""
460
+ api = get_api(debug=debug)
461
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/musicOnHold"
462
+ params = {}
463
+ org_id = get_org_id()
464
+ if org_id is not None:
465
+ params["orgId"] = org_id
466
+ try:
467
+ result = api.session.rest_get(url, params=params)
468
+ except WebexError as e:
469
+ handle_rest_error(e)
470
+ if output == "json":
471
+ print_json(result)
472
+ else:
473
+ if isinstance(result, dict):
474
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
475
+ elif isinstance(result, list):
476
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
477
+ else:
478
+ print_json(result)
479
+
480
+
481
+
482
+ @app.command("update-music-on-hold")
483
+ def update_music_on_hold(
484
+ location_id: str = typer.Argument(help="locationId"),
485
+ moh_enabled: bool = typer.Option(None, "--moh-enabled/--no-moh-enabled", help="Music on hold is enabled or disabled for the workspace."),
486
+ greeting: str = typer.Option(None, "--greeting", help="Choices: DEFAULT, CUSTOM"),
487
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
488
+ debug: bool = typer.Option(False, "--debug"),
489
+ ):
490
+ """Update Music On Hold\n\nExample --json-body:\n '{"mohEnabled":true,"greeting":"DEFAULT","audioAnnouncementFile":{"id":"...","fileName":"...","mediaFileType":"WAV","level":"ORGANIZATION"}}'."""
491
+ api = get_api(debug=debug)
492
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/musicOnHold"
493
+ params = {}
494
+ org_id = get_org_id()
495
+ if org_id is not None:
496
+ params["orgId"] = org_id
497
+ if json_body:
498
+ body = json.loads(json_body)
499
+ else:
500
+ body = {}
501
+ if moh_enabled is not None:
502
+ body["mohEnabled"] = moh_enabled
503
+ if greeting is not None:
504
+ body["greeting"] = greeting
505
+ try:
506
+ result = api.session.rest_put(url, json=body, params=params)
507
+ except WebexError as e:
508
+ handle_rest_error(e)
509
+ typer.echo(f"Updated.")
510
+
511
+
512
+
513
+ @app.command("show-private-network-connect")
514
+ def show_private_network_connect(
515
+ location_id: str = typer.Argument(help="locationId"),
516
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
517
+ debug: bool = typer.Option(False, "--debug"),
518
+ ):
519
+ """Get Private Network Connect."""
520
+ api = get_api(debug=debug)
521
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/privateNetworkConnect"
522
+ params = {}
523
+ org_id = get_org_id()
524
+ if org_id is not None:
525
+ params["orgId"] = org_id
526
+ try:
527
+ result = api.session.rest_get(url, params=params)
528
+ except WebexError as e:
529
+ handle_rest_error(e)
530
+ if output == "json":
531
+ print_json(result)
532
+ else:
533
+ if isinstance(result, dict):
534
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
535
+ elif isinstance(result, list):
536
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
537
+ else:
538
+ print_json(result)
539
+
540
+
541
+
542
+ @app.command("update-private-network-connect")
543
+ def update_private_network_connect(
544
+ location_id: str = typer.Argument(help="locationId"),
545
+ network_connection_type: str = typer.Option(None, "--network-connection-type", help="Choices: PUBLIC_INTERNET, PRIVATE_NETWORK"),
546
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
547
+ debug: bool = typer.Option(False, "--debug"),
548
+ ):
549
+ """Update Private Network Connect."""
550
+ api = get_api(debug=debug)
551
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/privateNetworkConnect"
552
+ params = {}
553
+ org_id = get_org_id()
554
+ if org_id is not None:
555
+ params["orgId"] = org_id
556
+ if json_body:
557
+ body = json.loads(json_body)
558
+ else:
559
+ body = {}
560
+ if network_connection_type is not None:
561
+ body["networkConnectionType"] = network_connection_type
562
+ try:
563
+ result = api.session.rest_put(url, json=body, params=params)
564
+ except WebexError as e:
565
+ handle_rest_error(e)
566
+ typer.echo(f"Updated.")
567
+
568
+
569
+
570
+ @app.command("list-route-choices")
571
+ def list_route_choices(
572
+ route_group_name: str = typer.Option(None, "--route-group-name", help="Return the list of route identities matching the Route group"),
573
+ trunk_name: str = typer.Option(None, "--trunk-name", help="Return the list of route identities matching the Trunk name."),
574
+ order: str = typer.Option(None, "--order", help="Order the route identities according to the designated field"),
575
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
576
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
577
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
578
+ debug: bool = typer.Option(False, "--debug"),
579
+ ):
580
+ """Read the List of Routing Choices."""
581
+ api = get_api(debug=debug)
582
+ url = f"https://webexapis.com/v1/telephony/config/routeChoices"
583
+ params = {}
584
+ if route_group_name is not None:
585
+ params["routeGroupName"] = route_group_name
586
+ if trunk_name is not None:
587
+ params["trunkName"] = trunk_name
588
+ if order is not None:
589
+ params["order"] = order
590
+ if limit > 0:
591
+ params["max"] = limit
592
+ if offset > 0:
593
+ params["start"] = offset
594
+ org_id = get_org_id()
595
+ if org_id is not None:
596
+ params["orgId"] = org_id
597
+ result = None
598
+ try:
599
+ result = api.session.rest_get(url, params=params)
600
+ except WebexError as e:
601
+ handle_rest_error(e)
602
+ result = result or []
603
+ items = result.get("routeIdentities", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
604
+ if output == "json":
605
+ print_json(items)
606
+ else:
607
+ print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
608
+
609
+
610
+
611
+ @app.command("list-available-numbers-external-caller-id")
612
+ def list_available_numbers_external_caller_id(
613
+ location_id: str = typer.Argument(help="locationId"),
614
+ phone_number: str = typer.Option(None, "--phone-number", help="Filter phone numbers based on the provided list in the `phon"),
615
+ owner_name: str = typer.Option(None, "--owner-name", help="Return the list of phone numbers that are owned by the given"),
616
+ person_id: str = typer.Option(None, "--person-id", help="Retrieve available external caller ID numbers for this perso"),
617
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
618
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
619
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
620
+ debug: bool = typer.Option(False, "--debug"),
621
+ ):
622
+ """Get the List of Phone Numbers Available for External Caller ID."""
623
+ api = get_api(debug=debug)
624
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/externalCallerId/availableNumbers"
625
+ params = {}
626
+ if phone_number is not None:
627
+ params["phoneNumber"] = phone_number
628
+ if owner_name is not None:
629
+ params["ownerName"] = owner_name
630
+ if person_id is not None:
631
+ params["personId"] = person_id
632
+ if limit > 0:
633
+ params["max"] = limit
634
+ if offset > 0:
635
+ params["start"] = offset
636
+ org_id = get_org_id()
637
+ if org_id is not None:
638
+ params["orgId"] = org_id
639
+ result = None
640
+ try:
641
+ result = api.session.rest_get(url, params=params)
642
+ except WebexError as e:
643
+ handle_rest_error(e)
644
+ result = result or []
645
+ items = result.get("phoneNumbers", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
646
+ if output == "json":
647
+ print_json(items)
648
+ else:
649
+ print_table(items, columns=[('Phone Number', 'phoneNumber'), ('State', 'state')], limit=limit)
650
+
651
+
652
+
653
+ @app.command("list-available-numbers-locations")
654
+ def list_available_numbers_locations(
655
+ location_id: str = typer.Argument(help="locationId"),
656
+ phone_number: str = typer.Option(None, "--phone-number", help="Filter phone numbers based on the comma-separated list provi"),
657
+ owner_name: str = typer.Option(None, "--owner-name", help="Return the list of phone numbers that are owned by the given"),
658
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
659
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
660
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
661
+ debug: bool = typer.Option(False, "--debug"),
662
+ ):
663
+ """Get Available Phone Numbers for a Location with Given Criteria."""
664
+ api = get_api(debug=debug)
665
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/availableNumbers"
666
+ params = {}
667
+ if phone_number is not None:
668
+ params["phoneNumber"] = phone_number
669
+ if owner_name is not None:
670
+ params["ownerName"] = owner_name
671
+ if limit > 0:
672
+ params["max"] = limit
673
+ if offset > 0:
674
+ params["start"] = offset
675
+ org_id = get_org_id()
676
+ if org_id is not None:
677
+ params["orgId"] = org_id
678
+ result = None
679
+ try:
680
+ result = api.session.rest_get(url, params=params)
681
+ except WebexError as e:
682
+ handle_rest_error(e)
683
+ result = result or []
684
+ items = result.get("phoneNumbers", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
685
+ if output == "json":
686
+ print_json(items)
687
+ else:
688
+ print_table(items, columns=[('Phone Number', 'phoneNumber'), ('State', 'state')], limit=limit)
689
+
690
+
691
+
692
+ @app.command("list-available-numbers-webex-go")
693
+ def list_available_numbers_webex_go(
694
+ location_id: str = typer.Argument(help="locationId"),
695
+ phone_number: str = typer.Option(None, "--phone-number", help="Filter phone numbers based on the comma-separated list provi"),
696
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
697
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
698
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
699
+ debug: bool = typer.Option(False, "--debug"),
700
+ ):
701
+ """Get Webex Go Available Phone Numbers."""
702
+ api = get_api(debug=debug)
703
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/webexGo/availableNumbers"
704
+ params = {}
705
+ if phone_number is not None:
706
+ params["phoneNumber"] = phone_number
707
+ if limit > 0:
708
+ params["max"] = limit
709
+ if offset > 0:
710
+ params["start"] = offset
711
+ org_id = get_org_id()
712
+ if org_id is not None:
713
+ params["orgId"] = org_id
714
+ result = None
715
+ try:
716
+ result = api.session.rest_get(url, params=params)
717
+ except WebexError as e:
718
+ handle_rest_error(e)
719
+ result = result or []
720
+ items = result.get("phoneNumbers", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
721
+ if output == "json":
722
+ print_json(items)
723
+ else:
724
+ print_table(items, columns=[('Phone Number', 'phoneNumber'), ('State', 'state')], limit=limit)
725
+
726
+
727
+
728
+ @app.command("list-available-numbers-emergency-callback-number")
729
+ def list_available_numbers_emergency_callback_number(
730
+ location_id: str = typer.Argument(help="locationId"),
731
+ phone_number: str = typer.Option(None, "--phone-number", help="Filter phone numbers based on the comma-separated list provi"),
732
+ owner_name: str = typer.Option(None, "--owner-name", help="Return the list of phone numbers that are owned by the given"),
733
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
734
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
735
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
736
+ debug: bool = typer.Option(False, "--debug"),
737
+ ):
738
+ """Get Location ECBN Available Phone Numbers."""
739
+ api = get_api(debug=debug)
740
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/emergencyCallbackNumber/availableNumbers"
741
+ params = {}
742
+ if phone_number is not None:
743
+ params["phoneNumber"] = phone_number
744
+ if owner_name is not None:
745
+ params["ownerName"] = owner_name
746
+ if limit > 0:
747
+ params["max"] = limit
748
+ if offset > 0:
749
+ params["start"] = offset
750
+ org_id = get_org_id()
751
+ if org_id is not None:
752
+ params["orgId"] = org_id
753
+ result = None
754
+ try:
755
+ result = api.session.rest_get(url, params=params)
756
+ except WebexError as e:
757
+ handle_rest_error(e)
758
+ result = result or []
759
+ items = result.get("phoneNumbers", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
760
+ if output == "json":
761
+ print_json(items)
762
+ else:
763
+ print_table(items, columns=[('Phone Number', 'phoneNumber'), ('State', 'state')], limit=limit)
764
+
765
+
766
+
767
+ @app.command("list-available-numbers-call-intercept")
768
+ def list_available_numbers_call_intercept(
769
+ location_id: str = typer.Argument(help="locationId"),
770
+ phone_number: str = typer.Option(None, "--phone-number", help="Filter phone numbers based on the comma-separated list provi"),
771
+ owner_name: str = typer.Option(None, "--owner-name", help="Return the list of phone numbers that are owned by the given"),
772
+ extension: str = typer.Option(None, "--extension", help="Returns the list of phone numbers with the given `extension`"),
773
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
774
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
775
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
776
+ debug: bool = typer.Option(False, "--debug"),
777
+ ):
778
+ """Get Location Call Intercept Available Phone Numbers."""
779
+ api = get_api(debug=debug)
780
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/callIntercept/availableNumbers"
781
+ params = {}
782
+ if phone_number is not None:
783
+ params["phoneNumber"] = phone_number
784
+ if owner_name is not None:
785
+ params["ownerName"] = owner_name
786
+ if extension is not None:
787
+ params["extension"] = extension
788
+ if limit > 0:
789
+ params["max"] = limit
790
+ if offset > 0:
791
+ params["start"] = offset
792
+ org_id = get_org_id()
793
+ if org_id is not None:
794
+ params["orgId"] = org_id
795
+ result = None
796
+ try:
797
+ result = api.session.rest_get(url, params=params)
798
+ except WebexError as e:
799
+ handle_rest_error(e)
800
+ result = result or []
801
+ items = result.get("phoneNumbers", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
802
+ if output == "json":
803
+ print_json(items)
804
+ else:
805
+ print_table(items, columns=[('Phone Number', 'phoneNumber'), ('State', 'state')], limit=limit)
806
+
807
+
808
+
809
+ @app.command("list-directories")
810
+ def list_directories(
811
+ location_id: str = typer.Argument(help="locationId"),
812
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
813
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
814
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
815
+ debug: bool = typer.Option(False, "--debug"),
816
+ ):
817
+ """Read list of Receptionist Contact Directories."""
818
+ api = get_api(debug=debug)
819
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/receptionistContacts/directories"
820
+ params = {}
821
+ if limit > 0:
822
+ params["max"] = limit
823
+ if offset > 0:
824
+ params["start"] = offset
825
+ org_id = get_org_id()
826
+ if org_id is not None:
827
+ params["orgId"] = org_id
828
+ result = None
829
+ try:
830
+ result = api.session.rest_get(url, params=params)
831
+ except WebexError as e:
832
+ handle_rest_error(e)
833
+ result = result or []
834
+ items = result.get("directories", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
835
+ if output == "json":
836
+ print_json(items)
837
+ else:
838
+ print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
839
+
840
+
841
+
842
+ @app.command("create-directories")
843
+ def create_directories(
844
+ location_id: str = typer.Argument(help="locationId"),
845
+ name: str = typer.Option(None, "--name", help="(required) Receptionist Contact Directory name. The directory name shou"),
846
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
847
+ output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
848
+ debug: bool = typer.Option(False, "--debug"),
849
+ ):
850
+ """Create a Receptionist Contact Directory\n\nExample --json-body:\n '{"name":"...","contacts":[{"personId":"...","featureId":"...","type":"..."}]}'."""
851
+ api = get_api(debug=debug)
852
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/receptionistContacts/directories"
853
+ params = {}
854
+ org_id = get_org_id()
855
+ if org_id is not None:
856
+ params["orgId"] = org_id
857
+ if json_body:
858
+ body = json.loads(json_body)
859
+ else:
860
+ body = {}
861
+ if name is not None:
862
+ body["name"] = name
863
+ _missing = [f for f in ['name'] if f not in body or body[f] is None]
864
+ if _missing:
865
+ typer.echo("Error: Missing required fields: " + ", ".join(_missing), err=True)
866
+ raise typer.Exit(1)
867
+ try:
868
+ result = api.session.rest_post(url, json=body, params=params)
869
+ except WebexError as e:
870
+ handle_rest_error(e)
871
+ if output == "json":
872
+ print_json(result)
873
+ elif isinstance(result, dict) and "id" in result:
874
+ typer.echo(f"Created: {result['id']}")
875
+ elif not result or result == {}:
876
+ typer.echo("Created.")
877
+ else:
878
+ print_json(result)
879
+
880
+
881
+
882
+ @app.command("show-directories")
883
+ def show_directories(
884
+ location_id: str = typer.Argument(help="locationId"),
885
+ directory_id: str = typer.Argument(help="directoryId"),
886
+ search_criteria_mode_or: str = typer.Option(None, "--search-criteria-mode-or", help="When `true`, results matching any one of the search criteria"),
887
+ first_name: str = typer.Option(None, "--first-name", help="Search for directories that contain people with the indicate"),
888
+ last_name: str = typer.Option(None, "--last-name", help="Search for directories that contain people with the indicate"),
889
+ phone_number: str = typer.Option(None, "--phone-number", help="Search for directories that contain people with the indicate"),
890
+ extension: str = typer.Option(None, "--extension", help="Search for directories that contain people with the indicate"),
891
+ person_id: str = typer.Option(None, "--person-id", help="Search for directories that contain people with the indicate"),
892
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
893
+ debug: bool = typer.Option(False, "--debug"),
894
+ ):
895
+ """Get details for a Receptionist Contact Directory."""
896
+ api = get_api(debug=debug)
897
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/receptionistContacts/directories/{directory_id}"
898
+ params = {}
899
+ if search_criteria_mode_or is not None:
900
+ params["searchCriteriaModeOr"] = search_criteria_mode_or
901
+ if first_name is not None:
902
+ params["firstName"] = first_name
903
+ if last_name is not None:
904
+ params["lastName"] = last_name
905
+ if phone_number is not None:
906
+ params["phoneNumber"] = phone_number
907
+ if extension is not None:
908
+ params["extension"] = extension
909
+ if person_id is not None:
910
+ params["personId"] = person_id
911
+ org_id = get_org_id()
912
+ if org_id is not None:
913
+ params["orgId"] = org_id
914
+ try:
915
+ result = api.session.rest_get(url, params=params)
916
+ except WebexError as e:
917
+ handle_rest_error(e)
918
+ if output == "json":
919
+ print_json(result)
920
+ else:
921
+ if isinstance(result, dict):
922
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
923
+ elif isinstance(result, list):
924
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
925
+ else:
926
+ print_json(result)
927
+
928
+
929
+
930
+ @app.command("update-directories")
931
+ def update_directories(
932
+ location_id: str = typer.Argument(help="locationId"),
933
+ directory_id: str = typer.Argument(help="directoryId"),
934
+ name: str = typer.Option(None, "--name", help="Receptionist Contact Directory name. The directory name shou"),
935
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
936
+ debug: bool = typer.Option(False, "--debug"),
937
+ ):
938
+ """Modify a Receptionist Contact Directory\n\nExample --json-body:\n '{"name":"...","contacts":["..."]}'."""
939
+ api = get_api(debug=debug)
940
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/receptionistContacts/directories/{directory_id}"
941
+ params = {}
942
+ org_id = get_org_id()
943
+ if org_id is not None:
944
+ params["orgId"] = org_id
945
+ if json_body:
946
+ body = json.loads(json_body)
947
+ else:
948
+ body = {}
949
+ if name is not None:
950
+ body["name"] = name
951
+ try:
952
+ result = api.session.rest_put(url, json=body, params=params)
953
+ except WebexError as e:
954
+ handle_rest_error(e)
955
+ typer.echo(f"Updated.")
956
+
957
+
958
+
959
+ @app.command("delete")
960
+ def delete(
961
+ location_id: str = typer.Argument(help="locationId"),
962
+ directory_id: str = typer.Argument(help="directoryId"),
963
+ force: bool = typer.Option(False, "--force", help="Skip confirmation"),
964
+ debug: bool = typer.Option(False, "--debug"),
965
+ ):
966
+ """Delete a Receptionist Contact Directory."""
967
+ if not force:
968
+ typer.confirm(f"Delete {directory_id}?", abort=True)
969
+ api = get_api(debug=debug)
970
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/receptionistContacts/directories/{directory_id}"
971
+ params = {}
972
+ org_id = get_org_id()
973
+ if org_id is not None:
974
+ params["orgId"] = org_id
975
+ try:
976
+ api.session.rest_delete(url, params=params)
977
+ except WebexError as e:
978
+ handle_rest_error(e)
979
+ typer.echo(f"Deleted: {directory_id}")
980
+
981
+
982
+
983
+ @app.command("list-available-numbers-charge-number")
984
+ def list_available_numbers_charge_number(
985
+ location_id: str = typer.Argument(help="locationId"),
986
+ phone_number: str = typer.Option(None, "--phone-number", help="Filter phone numbers based on the comma-separated list provi"),
987
+ owner_name: str = typer.Option(None, "--owner-name", help="Return the list of phone numbers that are owned by the given"),
988
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
989
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
990
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
991
+ debug: bool = typer.Option(False, "--debug"),
992
+ ):
993
+ """Get Available Charge Numbers for a Location with Given Criteria."""
994
+ api = get_api(debug=debug)
995
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/chargeNumber/availableNumbers"
996
+ params = {}
997
+ if phone_number is not None:
998
+ params["phoneNumber"] = phone_number
999
+ if owner_name is not None:
1000
+ params["ownerName"] = owner_name
1001
+ if limit > 0:
1002
+ params["max"] = limit
1003
+ if offset > 0:
1004
+ params["start"] = offset
1005
+ org_id = get_org_id()
1006
+ if org_id is not None:
1007
+ params["orgId"] = org_id
1008
+ result = None
1009
+ try:
1010
+ result = api.session.rest_get(url, params=params)
1011
+ except WebexError as e:
1012
+ handle_rest_error(e)
1013
+ result = result or []
1014
+ items = result.get("phoneNumbers", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
1015
+ if output == "json":
1016
+ print_json(items)
1017
+ else:
1018
+ print_table(items, columns=[('Phone Number', 'phoneNumber'), ('State', 'state')], limit=limit)
1019
+
1020
+
1021
+
1022
+ @app.command("list-delete-calling-location")
1023
+ def list_delete_calling_location(
1024
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
1025
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
1026
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
1027
+ debug: bool = typer.Option(False, "--debug"),
1028
+ ):
1029
+ """Get a List of Disable Calling Location Jobs."""
1030
+ api = get_api(debug=debug)
1031
+ url = f"https://webexapis.com/v1/telephony/config/jobs/locations/deleteCallingLocation"
1032
+ params = {}
1033
+ if limit > 0:
1034
+ params["max"] = limit
1035
+ if offset > 0:
1036
+ params["start"] = offset
1037
+ org_id = get_org_id()
1038
+ if org_id is not None:
1039
+ params["orgId"] = org_id
1040
+ result = None
1041
+ try:
1042
+ result = api.session.rest_get(url, params=params)
1043
+ except WebexError as e:
1044
+ handle_rest_error(e)
1045
+ result = result or []
1046
+ items = result.get("items", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
1047
+ if output == "json":
1048
+ print_json(items)
1049
+ else:
1050
+ print_table(items, columns=[('ID', 'id'), ('Status', 'latestExecutionStatus')], limit=limit)
1051
+
1052
+
1053
+
1054
+ @app.command("create-delete-calling-location")
1055
+ def create_delete_calling_location(
1056
+ location_id: str = typer.Option(None, "--location-id", help="(required) Unique identifier for the calling location to disable."),
1057
+ location_name: str = typer.Option(None, "--location-name", help="Name of the calling location to disable."),
1058
+ force_delete: bool = typer.Option(None, "--force-delete/--no-force-delete", help="Force delete is only applicable when calling features like c"),
1059
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
1060
+ output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
1061
+ debug: bool = typer.Option(False, "--debug"),
1062
+ ):
1063
+ """Disable a Location for Webex Calling."""
1064
+ api = get_api(debug=debug)
1065
+ url = f"https://webexapis.com/v1/telephony/config/jobs/locations/deleteCallingLocation"
1066
+ params = {}
1067
+ org_id = get_org_id()
1068
+ if org_id is not None:
1069
+ params["orgId"] = org_id
1070
+ if json_body:
1071
+ body = json.loads(json_body)
1072
+ else:
1073
+ body = {}
1074
+ if location_id is not None:
1075
+ body["locationId"] = location_id
1076
+ if location_name is not None:
1077
+ body["locationName"] = location_name
1078
+ if force_delete is not None:
1079
+ body["forceDelete"] = force_delete
1080
+ _missing = [f for f in ['locationId'] if f not in body or body[f] is None]
1081
+ if _missing:
1082
+ typer.echo("Error: Missing required fields: " + ", ".join(_missing), err=True)
1083
+ raise typer.Exit(1)
1084
+ try:
1085
+ result = api.session.rest_post(url, json=body, params=params)
1086
+ except WebexError as e:
1087
+ handle_rest_error(e)
1088
+ if output == "json":
1089
+ print_json(result)
1090
+ elif isinstance(result, dict) and "id" in result:
1091
+ typer.echo(f"Created: {result['id']}")
1092
+ elif not result or result == {}:
1093
+ typer.echo("Created.")
1094
+ else:
1095
+ print_json(result)
1096
+
1097
+
1098
+
1099
+ @app.command("safe-delete-check")
1100
+ def safe_delete_check(
1101
+ location_id: str = typer.Argument(help="locationId"),
1102
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body"),
1103
+ debug: bool = typer.Option(False, "--debug"),
1104
+ ):
1105
+ """Safe Delete Check Before Disabling a Location for Webex Calling."""
1106
+ api = get_api(debug=debug)
1107
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/actions/precheckForDeletion/invoke"
1108
+ params = {}
1109
+ org_id = get_org_id()
1110
+ if org_id is not None:
1111
+ params["orgId"] = org_id
1112
+ if json_body:
1113
+ body = json.loads(json_body)
1114
+ else:
1115
+ body = {}
1116
+ try:
1117
+ result = api.session.rest_post(url, json=body, params=params)
1118
+ except WebexError as e:
1119
+ handle_rest_error(e)
1120
+ print_json(result)
1121
+
1122
+
1123
+
1124
+ @app.command("pause-a-disable")
1125
+ def pause_a_disable(
1126
+ job_id: str = typer.Argument(help="jobId"),
1127
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body"),
1128
+ debug: bool = typer.Option(False, "--debug"),
1129
+ ):
1130
+ """Pause a Disable Calling Location Job."""
1131
+ api = get_api(debug=debug)
1132
+ url = f"https://webexapis.com/v1/telephony/config/jobs/locations/deleteCallingLocation/{job_id}/actions/pause/invoke"
1133
+ params = {}
1134
+ org_id = get_org_id()
1135
+ if org_id is not None:
1136
+ params["orgId"] = org_id
1137
+ if json_body:
1138
+ body = json.loads(json_body)
1139
+ else:
1140
+ body = {}
1141
+ try:
1142
+ result = api.session.rest_post(url, json=body, params=params)
1143
+ except WebexError as e:
1144
+ handle_rest_error(e)
1145
+ print_json(result)
1146
+
1147
+
1148
+
1149
+ @app.command("resume-a-paused")
1150
+ def resume_a_paused(
1151
+ job_id: str = typer.Argument(help="jobId"),
1152
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body"),
1153
+ debug: bool = typer.Option(False, "--debug"),
1154
+ ):
1155
+ """Resume a Paused Disable Calling Location Job."""
1156
+ api = get_api(debug=debug)
1157
+ url = f"https://webexapis.com/v1/telephony/config/jobs/locations/deleteCallingLocation/{job_id}/actions/resume/invoke"
1158
+ params = {}
1159
+ org_id = get_org_id()
1160
+ if org_id is not None:
1161
+ params["orgId"] = org_id
1162
+ if json_body:
1163
+ body = json.loads(json_body)
1164
+ else:
1165
+ body = {}
1166
+ try:
1167
+ result = api.session.rest_post(url, json=body, params=params)
1168
+ except WebexError as e:
1169
+ handle_rest_error(e)
1170
+ print_json(result)
1171
+
1172
+
1173
+
1174
+ @app.command("list-errors")
1175
+ def list_errors(
1176
+ job_id: str = typer.Argument(help="jobId"),
1177
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
1178
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
1179
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
1180
+ debug: bool = typer.Option(False, "--debug"),
1181
+ ):
1182
+ """Retrieve Errors for a Disable Calling Location Job."""
1183
+ api = get_api(debug=debug)
1184
+ url = f"https://webexapis.com/v1/telephony/config/jobs/locations/deleteCallingLocation/{job_id}/errors"
1185
+ params = {}
1186
+ if limit > 0:
1187
+ params["max"] = limit
1188
+ if offset > 0:
1189
+ params["start"] = offset
1190
+ org_id = get_org_id()
1191
+ if org_id is not None:
1192
+ params["orgId"] = org_id
1193
+ result = None
1194
+ try:
1195
+ result = api.session.rest_get(url, params=params)
1196
+ except WebexError as e:
1197
+ handle_rest_error(e)
1198
+ result = result or []
1199
+ items = result.get("items", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
1200
+ if output == "json":
1201
+ print_json(items)
1202
+ else:
1203
+ print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
1204
+
1205
+
1206
+
1207
+ @app.command("show-delete-calling-location")
1208
+ def show_delete_calling_location(
1209
+ job_id: str = typer.Argument(help="jobId"),
1210
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
1211
+ debug: bool = typer.Option(False, "--debug"),
1212
+ ):
1213
+ """Get Disable Calling Location Job Status."""
1214
+ api = get_api(debug=debug)
1215
+ url = f"https://webexapis.com/v1/telephony/config/jobs/locations/deleteCallingLocation/{job_id}"
1216
+ params = {}
1217
+ org_id = get_org_id()
1218
+ if org_id is not None:
1219
+ params["orgId"] = org_id
1220
+ try:
1221
+ result = api.session.rest_get(url, params=params)
1222
+ except WebexError as e:
1223
+ handle_rest_error(e)
1224
+ if output == "json":
1225
+ print_json(result)
1226
+ else:
1227
+ if isinstance(result, dict):
1228
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
1229
+ elif isinstance(result, list):
1230
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
1231
+ else:
1232
+ print_json(result)
1233
+
1234
+
1235
+
1236
+ @app.command("show-call-captions")
1237
+ def show_call_captions(
1238
+ location_id: str = typer.Argument(help="locationId"),
1239
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
1240
+ debug: bool = typer.Option(False, "--debug"),
1241
+ ):
1242
+ """Get the location call captions settings."""
1243
+ api = get_api(debug=debug)
1244
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/callCaptions"
1245
+ params = {}
1246
+ org_id = get_org_id()
1247
+ if org_id is not None:
1248
+ params["orgId"] = org_id
1249
+ try:
1250
+ result = api.session.rest_get(url, params=params)
1251
+ except WebexError as e:
1252
+ handle_rest_error(e)
1253
+ if output == "json":
1254
+ print_json(result)
1255
+ else:
1256
+ if isinstance(result, dict):
1257
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
1258
+ elif isinstance(result, list):
1259
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
1260
+ else:
1261
+ print_json(result)
1262
+
1263
+
1264
+
1265
+ @app.command("update-call-captions")
1266
+ def update_call_captions(
1267
+ location_id: str = typer.Argument(help="locationId"),
1268
+ location_closed_captions_enabled: bool = typer.Option(None, "--location-closed-captions-enabled/--no-location-closed-captions-enabled", help="Enable or disable location-level closed captions."),
1269
+ location_transcripts_enabled: bool = typer.Option(None, "--location-transcripts-enabled/--no-location-transcripts-enabled", help="Enable or disable location-level transcripts."),
1270
+ use_org_settings_enabled: bool = typer.Option(None, "--use-org-settings-enabled/--no-use-org-settings-enabled", help="If `useOrgSettingsEnabled` is `true`, organization-level set"),
1271
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
1272
+ debug: bool = typer.Option(False, "--debug"),
1273
+ ):
1274
+ """Update the location call captions settings."""
1275
+ api = get_api(debug=debug)
1276
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/callCaptions"
1277
+ params = {}
1278
+ org_id = get_org_id()
1279
+ if org_id is not None:
1280
+ params["orgId"] = org_id
1281
+ if json_body:
1282
+ body = json.loads(json_body)
1283
+ else:
1284
+ body = {}
1285
+ if location_closed_captions_enabled is not None:
1286
+ body["locationClosedCaptionsEnabled"] = location_closed_captions_enabled
1287
+ if location_transcripts_enabled is not None:
1288
+ body["locationTranscriptsEnabled"] = location_transcripts_enabled
1289
+ if use_org_settings_enabled is not None:
1290
+ body["useOrgSettingsEnabled"] = use_org_settings_enabled
1291
+ try:
1292
+ result = api.session.rest_put(url, json=body, params=params)
1293
+ except WebexError as e:
1294
+ handle_rest_error(e)
1295
+ typer.echo(f"Updated.")
1296
+
1297
+