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,129 @@
1
+ """Configuration management for CUCM migration projects.
2
+
3
+ Manages config.json in the project directory. Keys control pipeline behavior
4
+ (country code, language, auto-rules, etc.).
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import json
10
+ from pathlib import Path
11
+ from typing import Any
12
+
13
+
14
+ # Default auto-resolution rules for clear-cut, low-risk decisions.
15
+ #
16
+ # These reduce manual review burden without risking incorrect choices.
17
+ # Override per-project by writing "auto_rules" to config.json.
18
+ #
19
+ # IMPORTANT: Defaults are seeded into a project's `config.json` ONCE by
20
+ # `wxcli cucm init`. They are NOT applied at runtime — `load_config()`
21
+ # reads the saved file literally. Operators who edit `config.json` to
22
+ # remove a default can restore it via `wxcli cucm config reset auto_rules`
23
+ # (which clobbers any custom rules in `auto_rules` — preserve them by
24
+ # hand-editing instead).
25
+ #
26
+ # To add a new default rule WITHOUT regressing existing in-flight projects:
27
+ # 1. Append the rule here.
28
+ # 2. Document the upgrade procedure in the relevant runbook.
29
+ # 3. Operators on existing projects must either (a) hand-edit their
30
+ # config.json to append the new rule, or (b) run
31
+ # `wxcli cucm config reset auto_rules` (destructive to custom rules).
32
+ DEFAULT_AUTO_RULES: list[dict[str, Any]] = [
33
+ # Incompatible devices have no migration path — always skip
34
+ {"type": "DEVICE_INCOMPATIBLE", "choice": "skip",
35
+ "reason": "No MPP migration path exists for this device"},
36
+ # NOTE: DEVICE_FIRMWARE_CONVERTIBLE was removed 2026-04-15. Convertibility
37
+ # is now a phone model classification, not an operator choice — the
38
+ # planner emits a create_activation_code op unconditionally for every
39
+ # convertible device.
40
+ # Hotdesk DN conflicts — primary DN always wins
41
+ {"type": "HOTDESK_DN_CONFLICT", "choice": "keep_primary",
42
+ "reason": "Primary DN takes precedence over hotdesk conflict"},
43
+ # CUCM-only forwarding variants — accept the loss (rarely configured)
44
+ {"type": "FORWARDING_LOSSY", "choice": "accept",
45
+ "reason": "CUCM-specific forwarding variant has no Webex equivalent"},
46
+ # SNR timer controls — accept Webex simplification
47
+ {"type": "SNR_LOSSY", "choice": "accept",
48
+ "reason": "SNR timer controls are simplified in Webex"},
49
+ # Unmappable CUCM button types — no Webex equivalent exists
50
+ {"type": "BUTTON_UNMAPPABLE", "choice": "accept_loss",
51
+ "reason": "CUCM button type has no Webex equivalent"},
52
+ # Calling permissions with 0 affected users — orphaned profile
53
+ # Analyzer writes "assigned_users_count" in context (css_permission.py line 128)
54
+ {"type": "CALLING_PERMISSION_MISMATCH",
55
+ "match": {"assigned_users_count": 0}, "choice": "skip",
56
+ "reason": "Orphaned permission profile — 0 users affected"},
57
+ # Missing data on devices that are already incompatible — skip
58
+ # (fixing missing data on a device we're not migrating is pointless)
59
+ # `is_on_incompatible_device` is written by
60
+ # enrich_cross_decision_context() during analysis_pipeline step 3.5.
61
+ {"type": "MISSING_DATA",
62
+ "match": {"is_on_incompatible_device": True}, "choice": "skip",
63
+ "reason": "Missing data on incompatible device — skipping device anyway"},
64
+ ]
65
+
66
+ DEFAULT_CONFIG: dict[str, Any] = {
67
+ "country_code": "+1",
68
+ "default_language": "en_us",
69
+ "default_country": "US",
70
+ "outside_dial_digit": "9",
71
+ "create_method": "people_api",
72
+ "include_phoneless_users": False,
73
+ "auto_rules": DEFAULT_AUTO_RULES,
74
+ "recording_vendor": "Webex",
75
+ "site_prefix_rules": [],
76
+ "category_rules": None,
77
+ # Execution tuning (from 2026-04-10-bulk-operations spec §5b).
78
+ # Device count at/above which the planner replaces per-device ops with
79
+ # Webex bulk job submissions. Set to 0 to force bulk always; set to a
80
+ # very large number (e.g., 999999) to disable bulk.
81
+ "bulk_device_threshold": 100,
82
+ # FedRAMP / Webex for Government: rebuildPhones is not supported.
83
+ # Set to True to omit bulk_rebuild_phones ops from the plan.
84
+ "skip_rebuild_phones": False,
85
+ "e911": {
86
+ "auto_configure_ecbn": True,
87
+ "notification_email": None,
88
+ "redsky_enabled": False,
89
+ "primary_did_strategy": "first_line",
90
+ },
91
+ }
92
+
93
+
94
+ def load_config(project_dir: Path) -> dict[str, Any]:
95
+ """Load config.json from the project directory, with defaults."""
96
+ config_path = project_dir / "config.json"
97
+ config = dict(DEFAULT_CONFIG)
98
+ if config_path.exists():
99
+ with open(config_path) as f:
100
+ saved = json.load(f)
101
+ config.update(saved)
102
+ return config
103
+
104
+
105
+ def save_config(project_dir: Path, config: dict[str, Any]) -> None:
106
+ """Write config.json to the project directory."""
107
+ config_path = project_dir / "config.json"
108
+ with open(config_path, "w") as f:
109
+ json.dump(config, f, indent=2)
110
+
111
+
112
+ def coerce_value(value: str) -> Any:
113
+ """Coerce a string CLI value to the appropriate Python type."""
114
+ if value.lower() in ("true", "yes"):
115
+ return True
116
+ if value.lower() in ("false", "no"):
117
+ return False
118
+ try:
119
+ n = int(value)
120
+ # Only coerce if round-trip matches (preserves "+44" as string)
121
+ if str(n) == value:
122
+ return n
123
+ except ValueError:
124
+ pass
125
+ try:
126
+ return json.loads(value)
127
+ except (json.JSONDecodeError, ValueError):
128
+ pass
129
+ return value
@@ -0,0 +1,373 @@
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 customer-assist.")
10
+
11
+
12
+ @app.command("list")
13
+ def cmd_list(
14
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
15
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
16
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
17
+ debug: bool = typer.Option(False, "--debug"),
18
+ ):
19
+ """List Wrap Up Reasons."""
20
+ api = get_api(debug=debug)
21
+ url = f"https://webexapis.com/v1/telephony/config/cxEssentials/wrapup/reasons"
22
+ params = {}
23
+ if limit > 0:
24
+ params["max"] = limit
25
+ if offset > 0:
26
+ params["start"] = offset
27
+ result = None
28
+ try:
29
+ result = api.session.rest_get(url, params=params)
30
+ except WebexError as e:
31
+ handle_rest_error(e)
32
+ result = result or []
33
+ items = result.get("wrapupReasons", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
34
+ if output == "json":
35
+ print_json(items)
36
+ else:
37
+ print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
38
+
39
+
40
+
41
+ @app.command("create")
42
+ def create(
43
+ name: str = typer.Option(None, "--name", help="(required) Name of the wrap-up reason."),
44
+ description: str = typer.Option(None, "--description", help="Description of the wrap-up reason."),
45
+ assign_all_queues_enabled: bool = typer.Option(None, "--assign-all-queues-enabled/--no-assign-all-queues-enabled", help="Denotes whether all queues are assigned to the wrap-up reaso"),
46
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
47
+ output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
48
+ debug: bool = typer.Option(False, "--debug"),
49
+ ):
50
+ """Create Wrap Up Reason\n\nExample --json-body:\n '{"name":"...","description":"...","queues":["..."],"assignAllQueuesEnabled":true}'."""
51
+ api = get_api(debug=debug)
52
+ url = f"https://webexapis.com/v1/telephony/config/cxEssentials/wrapup/reasons"
53
+ if json_body:
54
+ body = json.loads(json_body)
55
+ else:
56
+ body = {}
57
+ if name is not None:
58
+ body["name"] = name
59
+ if description is not None:
60
+ body["description"] = description
61
+ if assign_all_queues_enabled is not None:
62
+ body["assignAllQueuesEnabled"] = assign_all_queues_enabled
63
+ _missing = [f for f in ['name'] if f not in body or body[f] is None]
64
+ if _missing:
65
+ typer.echo("Error: Missing required fields: " + ", ".join(_missing), err=True)
66
+ raise typer.Exit(1)
67
+ try:
68
+ result = api.session.rest_post(url, json=body)
69
+ except WebexError as e:
70
+ handle_rest_error(e)
71
+ if output == "json":
72
+ print_json(result)
73
+ elif isinstance(result, dict) and "id" in result:
74
+ typer.echo(f"Created: {result['id']}")
75
+ elif not result or result == {}:
76
+ typer.echo("Created.")
77
+ else:
78
+ print_json(result)
79
+
80
+
81
+
82
+ @app.command("show")
83
+ def show(
84
+ wrapup_reason_id: str = typer.Argument(help="wrapupReasonId"),
85
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
86
+ debug: bool = typer.Option(False, "--debug"),
87
+ ):
88
+ """Read Wrap Up Reason."""
89
+ api = get_api(debug=debug)
90
+ url = f"https://webexapis.com/v1/telephony/config/cxEssentials/wrapup/reasons/{wrapup_reason_id}"
91
+ try:
92
+ result = api.session.rest_get(url)
93
+ except WebexError as e:
94
+ handle_rest_error(e)
95
+ if output == "json":
96
+ print_json(result)
97
+ else:
98
+ if isinstance(result, dict):
99
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
100
+ elif isinstance(result, list):
101
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
102
+ else:
103
+ print_json(result)
104
+
105
+
106
+
107
+ @app.command("update")
108
+ def update(
109
+ wrapup_reason_id: str = typer.Argument(help="wrapupReasonId"),
110
+ name: str = typer.Option(None, "--name", help="Name of the wrap-up reason."),
111
+ description: str = typer.Option(None, "--description", help="Description of the wrap-up reason."),
112
+ assign_all_queues_enabled: bool = typer.Option(None, "--assign-all-queues-enabled/--no-assign-all-queues-enabled", help="Denotes whether all queues are assigned to the wrap-up reaso"),
113
+ unassign_all_queues_enabled: bool = typer.Option(None, "--unassign-all-queues-enabled/--no-unassign-all-queues-enabled", help="Denotes whether all queues are unassigned from the wrap-up r"),
114
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
115
+ debug: bool = typer.Option(False, "--debug"),
116
+ ):
117
+ """Update Wrap Up Reason\n\nExample --json-body:\n '{"name":"...","description":"...","queuesToAssign":["..."],"queuesToUnassign":["..."],"assignAllQueuesEnabled":true,"unassignAllQueuesEnabled":true}'."""
118
+ api = get_api(debug=debug)
119
+ url = f"https://webexapis.com/v1/telephony/config/cxEssentials/wrapup/reasons/{wrapup_reason_id}"
120
+ if json_body:
121
+ body = json.loads(json_body)
122
+ else:
123
+ body = {}
124
+ if name is not None:
125
+ body["name"] = name
126
+ if description is not None:
127
+ body["description"] = description
128
+ if assign_all_queues_enabled is not None:
129
+ body["assignAllQueuesEnabled"] = assign_all_queues_enabled
130
+ if unassign_all_queues_enabled is not None:
131
+ body["unassignAllQueuesEnabled"] = unassign_all_queues_enabled
132
+ try:
133
+ result = api.session.rest_put(url, json=body)
134
+ except WebexError as e:
135
+ handle_rest_error(e)
136
+ typer.echo(f"Updated.")
137
+
138
+
139
+
140
+ @app.command("delete")
141
+ def delete(
142
+ wrapup_reason_id: str = typer.Argument(help="wrapupReasonId"),
143
+ force: bool = typer.Option(False, "--force", help="Skip confirmation"),
144
+ debug: bool = typer.Option(False, "--debug"),
145
+ ):
146
+ """Delete Wrap Up Reason."""
147
+ if not force:
148
+ typer.confirm(f"Delete {wrapup_reason_id}?", abort=True)
149
+ api = get_api(debug=debug)
150
+ url = f"https://webexapis.com/v1/telephony/config/cxEssentials/wrapup/reasons/{wrapup_reason_id}"
151
+ try:
152
+ api.session.rest_delete(url)
153
+ except WebexError as e:
154
+ handle_rest_error(e)
155
+ typer.echo(f"Deleted: {wrapup_reason_id}")
156
+
157
+
158
+
159
+ @app.command("validate-wrap-up")
160
+ def validate_wrap_up(
161
+ name: str = typer.Option(None, "--name", help="Name of the wrap-up reason."),
162
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body"),
163
+ debug: bool = typer.Option(False, "--debug"),
164
+ ):
165
+ """Validate Wrap Up Reason."""
166
+ api = get_api(debug=debug)
167
+ url = f"https://webexapis.com/v1/telephony/config/cxEssentials/wrapup/reasons/actions/validateName/invoke"
168
+ if json_body:
169
+ body = json.loads(json_body)
170
+ else:
171
+ body = {}
172
+ if name is not None:
173
+ body["name"] = name
174
+ try:
175
+ result = api.session.rest_post(url, json=body)
176
+ except WebexError as e:
177
+ handle_rest_error(e)
178
+ print_json(result)
179
+
180
+
181
+
182
+ @app.command("list-available-queues")
183
+ def list_available_queues(
184
+ wrapup_reason_id: str = typer.Argument(help="wrapupReasonId"),
185
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
186
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
187
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
188
+ debug: bool = typer.Option(False, "--debug"),
189
+ ):
190
+ """Read Available Queues."""
191
+ api = get_api(debug=debug)
192
+ url = f"https://webexapis.com/v1/telephony/config/cxEssentials/wrapup/reasons/{wrapup_reason_id}/availableQueues"
193
+ params = {}
194
+ if limit > 0:
195
+ params["max"] = limit
196
+ if offset > 0:
197
+ params["start"] = offset
198
+ result = None
199
+ try:
200
+ result = api.session.rest_get(url, params=params)
201
+ except WebexError as e:
202
+ handle_rest_error(e)
203
+ result = result or []
204
+ items = result.get("queues", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
205
+ if output == "json":
206
+ print_json(items)
207
+ else:
208
+ print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
209
+
210
+
211
+
212
+ @app.command("list-settings")
213
+ def list_settings(
214
+ location_id: str = typer.Argument(help="locationId"),
215
+ queue_id: str = typer.Argument(help="queueId"),
216
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
217
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
218
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
219
+ debug: bool = typer.Option(False, "--debug"),
220
+ ):
221
+ """Read Wrap Up Reason Settings."""
222
+ api = get_api(debug=debug)
223
+ url = f"https://webexapis.com/v1/telephony/config/cxEssentials/locations/{location_id}/queues/{queue_id}/wrapup/settings"
224
+ params = {}
225
+ if limit > 0:
226
+ params["max"] = limit
227
+ if offset > 0:
228
+ params["start"] = offset
229
+ result = None
230
+ try:
231
+ result = api.session.rest_get(url, params=params)
232
+ except WebexError as e:
233
+ handle_rest_error(e)
234
+ result = result or []
235
+ items = result.get("wrapupReasons", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
236
+ if output == "json":
237
+ print_json(items)
238
+ else:
239
+ print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
240
+
241
+
242
+
243
+ @app.command("update-settings")
244
+ def update_settings(
245
+ location_id: str = typer.Argument(help="locationId"),
246
+ queue_id: str = typer.Argument(help="queueId"),
247
+ default_wrapup_reason_id: str = typer.Option(None, "--default-wrapup-reason-id", help="Unique wrap-up identifier."),
248
+ wrapup_timer_enabled: bool = typer.Option(None, "--wrapup-timer-enabled/--no-wrapup-timer-enabled", help="Denotes whether the wrap-up timer is enabled."),
249
+ wrapup_timer: str = typer.Option(None, "--wrapup-timer", help="Wrap up timer value in seconds."),
250
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
251
+ debug: bool = typer.Option(False, "--debug"),
252
+ ):
253
+ """Update Wrap Up Reason Settings\n\nExample --json-body:\n '{"wrapupReasons":["..."],"defaultWrapupReasonId":"...","wrapupTimerEnabled":true,"wrapupTimer":0}'."""
254
+ api = get_api(debug=debug)
255
+ url = f"https://webexapis.com/v1/telephony/config/cxEssentials/locations/{location_id}/queues/{queue_id}/wrapup/settings"
256
+ if json_body:
257
+ body = json.loads(json_body)
258
+ else:
259
+ body = {}
260
+ if default_wrapup_reason_id is not None:
261
+ body["defaultWrapupReasonId"] = default_wrapup_reason_id
262
+ if wrapup_timer_enabled is not None:
263
+ body["wrapupTimerEnabled"] = wrapup_timer_enabled
264
+ if wrapup_timer is not None:
265
+ body["wrapupTimer"] = wrapup_timer
266
+ try:
267
+ result = api.session.rest_put(url, json=body)
268
+ except WebexError as e:
269
+ handle_rest_error(e)
270
+ typer.echo(f"Updated.")
271
+
272
+
273
+
274
+ @app.command("show-screen-pop")
275
+ def show_screen_pop(
276
+ location_id: str = typer.Argument(help="locationId"),
277
+ queue_id: str = typer.Argument(help="queueId"),
278
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
279
+ debug: bool = typer.Option(False, "--debug"),
280
+ ):
281
+ """Read Screen Pop Configuration."""
282
+ api = get_api(debug=debug)
283
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/queues/{queue_id}/cxEssentials/screenPop"
284
+ params = {}
285
+ org_id = get_org_id()
286
+ if org_id is not None:
287
+ params["orgId"] = org_id
288
+ try:
289
+ result = api.session.rest_get(url, params=params)
290
+ except WebexError as e:
291
+ handle_rest_error(e)
292
+ if output == "json":
293
+ print_json(result)
294
+ else:
295
+ if isinstance(result, dict):
296
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
297
+ elif isinstance(result, list):
298
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
299
+ else:
300
+ print_json(result)
301
+
302
+
303
+
304
+ @app.command("update-screen-pop")
305
+ def update_screen_pop(
306
+ location_id: str = typer.Argument(help="locationId"),
307
+ queue_id: str = typer.Argument(help="queueId"),
308
+ enabled: bool = typer.Option(None, "--enabled/--no-enabled", help="Enable/disable screen pop."),
309
+ screen_pop_url: str = typer.Option(None, "--screen-pop-url", help="The screen pop URL that integrates Webex calls with other bu"),
310
+ desktop_label: str = typer.Option(None, "--desktop-label", help="A label for the screen pop configuration."),
311
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
312
+ debug: bool = typer.Option(False, "--debug"),
313
+ ):
314
+ """Update Screen Pop Configuration\n\nExample --json-body:\n '{"enabled":true,"screenPopUrl":"...","desktopLabel":"...","queryParams":{"example_param_1":"...","example_param_2":"...","example_param_3":"..."}}'."""
315
+ api = get_api(debug=debug)
316
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/queues/{queue_id}/cxEssentials/screenPop"
317
+ params = {}
318
+ org_id = get_org_id()
319
+ if org_id is not None:
320
+ params["orgId"] = org_id
321
+ if json_body:
322
+ body = json.loads(json_body)
323
+ else:
324
+ body = {}
325
+ if enabled is not None:
326
+ body["enabled"] = enabled
327
+ if screen_pop_url is not None:
328
+ body["screenPopUrl"] = screen_pop_url
329
+ if desktop_label is not None:
330
+ body["desktopLabel"] = desktop_label
331
+ try:
332
+ result = api.session.rest_put(url, json=body, params=params)
333
+ except WebexError as e:
334
+ handle_rest_error(e)
335
+ typer.echo(f"Updated.")
336
+
337
+
338
+
339
+ @app.command("list-available-agents")
340
+ def list_available_agents(
341
+ location_id: str = typer.Argument(help="locationId"),
342
+ has_cx_essentials: str = typer.Option(None, "--has-cx-essentials", help="Returns only the list of available agents with Customer Assi"),
343
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
344
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
345
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
346
+ debug: bool = typer.Option(False, "--debug"),
347
+ ):
348
+ """List Available Agents."""
349
+ api = get_api(debug=debug)
350
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/cxEssentials/agents/availableAgents"
351
+ params = {}
352
+ if has_cx_essentials is not None:
353
+ params["hasCxEssentials"] = has_cx_essentials
354
+ if limit > 0:
355
+ params["max"] = limit
356
+ if offset > 0:
357
+ params["start"] = offset
358
+ org_id = get_org_id()
359
+ if org_id is not None:
360
+ params["orgId"] = org_id
361
+ result = None
362
+ try:
363
+ result = api.session.rest_get(url, params=params)
364
+ except WebexError as e:
365
+ handle_rest_error(e)
366
+ result = result or []
367
+ items = result.get("agents", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
368
+ if output == "json":
369
+ print_json(items)
370
+ else:
371
+ print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
372
+
373
+