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,787 @@
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 dect-devices.")
10
+
11
+
12
+ @app.command("create")
13
+ def create(
14
+ location_id: str = typer.Argument(help="locationId"),
15
+ name: str = typer.Option(None, "--name", help="(required) Name of the DECT network. Min and max length supported for t"),
16
+ display_name: str = typer.Option(None, "--display-name", help="Add a default name (11 characters max) to display for all ha"),
17
+ model: str = typer.Option(None, "--model", help="(required) Choices: DMS Cisco DBS110, Cisco DECT 110 Base, DMS Cisco DBS210, Cisco DECT 210 Base"),
18
+ default_access_code_enabled: bool = typer.Option(None, "--default-access-code-enabled/--no-default-access-code-enabled", help="(required) If set to true, need to provide a default access code that w"),
19
+ default_access_code: str = typer.Option(None, "--default-access-code", help="(required) If `defaultAccessCodeEnabled` is set to true, then provide a"),
20
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
21
+ output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
22
+ debug: bool = typer.Option(False, "--debug"),
23
+ ):
24
+ """Create a DECT Network."""
25
+ api = get_api(debug=debug)
26
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/dectNetworks"
27
+ params = {}
28
+ org_id = get_org_id()
29
+ if org_id is not None:
30
+ params["orgId"] = org_id
31
+ if json_body:
32
+ body = json.loads(json_body)
33
+ else:
34
+ body = {}
35
+ if name is not None:
36
+ body["name"] = name
37
+ if display_name is not None:
38
+ body["displayName"] = display_name
39
+ if model is not None:
40
+ body["model"] = model
41
+ if default_access_code_enabled is not None:
42
+ body["defaultAccessCodeEnabled"] = default_access_code_enabled
43
+ if default_access_code is not None:
44
+ body["defaultAccessCode"] = default_access_code
45
+ _missing = [f for f in ['name', 'model', 'defaultAccessCodeEnabled', 'defaultAccessCode'] if f not in body or body[f] is None]
46
+ if _missing:
47
+ typer.echo("Error: Missing required fields: " + ", ".join(_missing), err=True)
48
+ raise typer.Exit(1)
49
+ try:
50
+ result = api.session.rest_post(url, json=body, params=params)
51
+ except WebexError as e:
52
+ handle_rest_error(e)
53
+ if output == "json":
54
+ print_json(result)
55
+ elif isinstance(result, dict) and "dectNetworkId" in result:
56
+ typer.echo(f"Created: {result['dectNetworkId']}")
57
+ elif isinstance(result, dict) and "id" in result:
58
+ typer.echo(f"Created: {result['id']}")
59
+ elif not result or result == {}:
60
+ typer.echo("Created.")
61
+ else:
62
+ print_json(result)
63
+
64
+
65
+
66
+ @app.command("list")
67
+ def cmd_list(
68
+ name: str = typer.Option(None, "--name", help="List of DECT networks with this name."),
69
+ location_id: str = typer.Option(None, "--location-id", help="List of DECT networks at this location."),
70
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
71
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
72
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
73
+ debug: bool = typer.Option(False, "--debug"),
74
+ ):
75
+ """Get the List of DECT Networks for an organization."""
76
+ api = get_api(debug=debug)
77
+ url = f"https://webexapis.com/v1/telephony/config/dectNetworks"
78
+ params = {}
79
+ if name is not None:
80
+ params["name"] = name
81
+ if location_id is not None:
82
+ params["locationId"] = location_id
83
+ if limit > 0:
84
+ params["max"] = limit
85
+ if offset > 0:
86
+ params["start"] = offset
87
+ org_id = get_org_id()
88
+ if org_id is not None:
89
+ params["orgId"] = org_id
90
+ result = None
91
+ try:
92
+ result = api.session.rest_get(url, params=params)
93
+ except WebexError as e:
94
+ handle_rest_error(e)
95
+ result = result or []
96
+ items = result.get("dectNetworks", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
97
+ if output == "json":
98
+ print_json(items)
99
+ else:
100
+ print_table(items, columns=[('ID', 'id'), ('Name', 'name'), ('Model', 'model')], limit=limit)
101
+
102
+
103
+
104
+ @app.command("show")
105
+ def show(
106
+ location_id: str = typer.Argument(help="locationId"),
107
+ dect_network_id: str = typer.Argument(help="dectNetworkId"),
108
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
109
+ debug: bool = typer.Option(False, "--debug"),
110
+ ):
111
+ """Get DECT Network Details."""
112
+ api = get_api(debug=debug)
113
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/dectNetworks/{dect_network_id}"
114
+ params = {}
115
+ org_id = get_org_id()
116
+ if org_id is not None:
117
+ params["orgId"] = org_id
118
+ try:
119
+ result = api.session.rest_get(url, params=params)
120
+ except WebexError as e:
121
+ handle_rest_error(e)
122
+ if output == "json":
123
+ print_json(result)
124
+ else:
125
+ if isinstance(result, dict):
126
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
127
+ elif isinstance(result, list):
128
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
129
+ else:
130
+ print_json(result)
131
+
132
+
133
+
134
+ @app.command("update")
135
+ def update(
136
+ location_id: str = typer.Argument(help="locationId"),
137
+ dect_network_id: str = typer.Argument(help="dectNetworkId"),
138
+ name: str = typer.Option(None, "--name", help="Name of the DECT network. This should be unique across the l"),
139
+ display_name: str = typer.Option(None, "--display-name", help="DECT network name that will be displayed on the handset."),
140
+ default_access_code_enabled: bool = typer.Option(None, "--default-access-code-enabled/--no-default-access-code-enabled", help="Default access code is enabled. If true, the default access"),
141
+ default_access_code: str = typer.Option(None, "--default-access-code", help="Default access code for the DECT network. The default access"),
142
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
143
+ debug: bool = typer.Option(False, "--debug"),
144
+ ):
145
+ """Update DECT Network."""
146
+ api = get_api(debug=debug)
147
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/dectNetworks/{dect_network_id}"
148
+ params = {}
149
+ org_id = get_org_id()
150
+ if org_id is not None:
151
+ params["orgId"] = org_id
152
+ if json_body:
153
+ body = json.loads(json_body)
154
+ else:
155
+ body = {}
156
+ if name is not None:
157
+ body["name"] = name
158
+ if display_name is not None:
159
+ body["displayName"] = display_name
160
+ if default_access_code_enabled is not None:
161
+ body["defaultAccessCodeEnabled"] = default_access_code_enabled
162
+ if default_access_code is not None:
163
+ body["defaultAccessCode"] = default_access_code
164
+ try:
165
+ result = api.session.rest_put(url, json=body, params=params)
166
+ except WebexError as e:
167
+ handle_rest_error(e)
168
+ typer.echo(f"Updated.")
169
+
170
+
171
+
172
+ @app.command("delete")
173
+ def delete(
174
+ location_id: str = typer.Argument(help="locationId"),
175
+ dect_network_id: str = typer.Argument(help="dectNetworkId"),
176
+ force: bool = typer.Option(False, "--force", help="Skip confirmation"),
177
+ debug: bool = typer.Option(False, "--debug"),
178
+ ):
179
+ """Delete DECT Network."""
180
+ if not force:
181
+ typer.confirm(f"Delete {dect_network_id}?", abort=True)
182
+ api = get_api(debug=debug)
183
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/dectNetworks/{dect_network_id}"
184
+ params = {}
185
+ org_id = get_org_id()
186
+ if org_id is not None:
187
+ params["orgId"] = org_id
188
+ try:
189
+ api.session.rest_delete(url, params=params)
190
+ except WebexError as e:
191
+ handle_rest_error(e)
192
+ typer.echo(f"Deleted: {dect_network_id}")
193
+
194
+
195
+
196
+ @app.command("list-base-stations")
197
+ def list_base_stations(
198
+ location_id: str = typer.Argument(help="locationId"),
199
+ dect_network_id: str = typer.Argument(help="dectNetworkId"),
200
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
201
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
202
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
203
+ debug: bool = typer.Option(False, "--debug"),
204
+ ):
205
+ """Get a list of DECT Network Base Stations."""
206
+ api = get_api(debug=debug)
207
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/dectNetworks/{dect_network_id}/baseStations"
208
+ params = {}
209
+ if limit > 0:
210
+ params["max"] = limit
211
+ if offset > 0:
212
+ params["start"] = offset
213
+ org_id = get_org_id()
214
+ if org_id is not None:
215
+ params["orgId"] = org_id
216
+ result = None
217
+ try:
218
+ result = api.session.rest_get(url, params=params)
219
+ except WebexError as e:
220
+ handle_rest_error(e)
221
+ result = result or []
222
+ items = result.get("baseStations", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
223
+ if output == "json":
224
+ print_json(items)
225
+ else:
226
+ print_table(items, columns=[('ID', 'id'), ('Name', 'name'), ('Model', 'model')], limit=limit)
227
+
228
+
229
+
230
+ @app.command("create-base-stations")
231
+ def create_base_stations(
232
+ location_id: str = typer.Argument(help="locationId"),
233
+ dect_network_id: str = typer.Argument(help="dectNetworkId"),
234
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
235
+ output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
236
+ debug: bool = typer.Option(False, "--debug"),
237
+ ):
238
+ """Create Multiple Base Stations\n\nExample --json-body:\n '{"baseStationMacs":["..."]}'."""
239
+ api = get_api(debug=debug)
240
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/dectNetworks/{dect_network_id}/baseStations"
241
+ params = {}
242
+ org_id = get_org_id()
243
+ if org_id is not None:
244
+ params["orgId"] = org_id
245
+ if json_body:
246
+ body = json.loads(json_body)
247
+ else:
248
+ body = {}
249
+ try:
250
+ result = api.session.rest_post(url, json=body, params=params)
251
+ except WebexError as e:
252
+ handle_rest_error(e)
253
+ if output == "json":
254
+ print_json(result)
255
+ elif isinstance(result, dict) and "baseStations" in result:
256
+ typer.echo(f"Created: {result['baseStations']}")
257
+ elif isinstance(result, dict) and "id" in result:
258
+ typer.echo(f"Created: {result['id']}")
259
+ elif not result or result == {}:
260
+ typer.echo("Created.")
261
+ else:
262
+ print_json(result)
263
+
264
+
265
+
266
+ @app.command("delete-base-stations-dect-networks")
267
+ def delete_base_stations_dect_networks(
268
+ location_id: str = typer.Argument(help="locationId"),
269
+ dect_network_id: str = typer.Argument(help="dectNetworkId"),
270
+ force: bool = typer.Option(False, "--force", help="Skip confirmation"),
271
+ debug: bool = typer.Option(False, "--debug"),
272
+ ):
273
+ """Delete bulk DECT Network Base Stations."""
274
+ if not force:
275
+ typer.confirm(f"Delete {dect_network_id}?", abort=True)
276
+ api = get_api(debug=debug)
277
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/dectNetworks/{dect_network_id}/baseStations"
278
+ params = {}
279
+ org_id = get_org_id()
280
+ if org_id is not None:
281
+ params["orgId"] = org_id
282
+ try:
283
+ api.session.rest_delete(url, params=params)
284
+ except WebexError as e:
285
+ handle_rest_error(e)
286
+ typer.echo(f"Deleted: {dect_network_id}")
287
+
288
+
289
+
290
+ @app.command("show-base-stations")
291
+ def show_base_stations(
292
+ location_id: str = typer.Argument(help="locationId"),
293
+ dect_network_id: str = typer.Argument(help="dectNetworkId"),
294
+ base_station_id: str = typer.Argument(help="baseStationId"),
295
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
296
+ debug: bool = typer.Option(False, "--debug"),
297
+ ):
298
+ """Get the details of a specific DECT Network Base Station."""
299
+ api = get_api(debug=debug)
300
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/dectNetworks/{dect_network_id}/baseStations/{base_station_id}"
301
+ params = {}
302
+ org_id = get_org_id()
303
+ if org_id is not None:
304
+ params["orgId"] = org_id
305
+ try:
306
+ result = api.session.rest_get(url, params=params)
307
+ except WebexError as e:
308
+ handle_rest_error(e)
309
+ if output == "json":
310
+ print_json(result)
311
+ else:
312
+ if isinstance(result, dict):
313
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
314
+ elif isinstance(result, list):
315
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
316
+ else:
317
+ print_json(result)
318
+
319
+
320
+
321
+ @app.command("delete-base-stations-dect-networks-1")
322
+ def delete_base_stations_dect_networks_1(
323
+ location_id: str = typer.Argument(help="locationId"),
324
+ dect_network_id: str = typer.Argument(help="dectNetworkId"),
325
+ base_station_id: str = typer.Argument(help="baseStationId"),
326
+ force: bool = typer.Option(False, "--force", help="Skip confirmation"),
327
+ debug: bool = typer.Option(False, "--debug"),
328
+ ):
329
+ """Delete a specific DECT Network Base Station."""
330
+ if not force:
331
+ typer.confirm(f"Delete {base_station_id}?", abort=True)
332
+ api = get_api(debug=debug)
333
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/dectNetworks/{dect_network_id}/baseStations/{base_station_id}"
334
+ params = {}
335
+ org_id = get_org_id()
336
+ if org_id is not None:
337
+ params["orgId"] = org_id
338
+ try:
339
+ api.session.rest_delete(url, params=params)
340
+ except WebexError as e:
341
+ handle_rest_error(e)
342
+ typer.echo(f"Deleted: {base_station_id}")
343
+
344
+
345
+
346
+ @app.command("list-handsets")
347
+ def list_handsets(
348
+ location_id: str = typer.Argument(help="locationId"),
349
+ dect_network_id: str = typer.Argument(help="dectNetworkId"),
350
+ basestation_id: str = typer.Option(None, "--basestation-id", help="Search handset details in the specified DECT base station ID"),
351
+ member_id: str = typer.Option(None, "--member-id", help="ID of the member of the handset. Members can be of type PEOP"),
352
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
353
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
354
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
355
+ debug: bool = typer.Option(False, "--debug"),
356
+ ):
357
+ """Get List of Handsets for a DECT Network ID."""
358
+ api = get_api(debug=debug)
359
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/dectNetworks/{dect_network_id}/handsets"
360
+ params = {}
361
+ if basestation_id is not None:
362
+ params["basestationId"] = basestation_id
363
+ if member_id is not None:
364
+ params["memberId"] = member_id
365
+ if limit > 0:
366
+ params["max"] = limit
367
+ if offset > 0:
368
+ params["start"] = offset
369
+ org_id = get_org_id()
370
+ if org_id is not None:
371
+ params["orgId"] = org_id
372
+ result = None
373
+ try:
374
+ result = api.session.rest_get(url, params=params)
375
+ except WebexError as e:
376
+ handle_rest_error(e)
377
+ result = result or []
378
+ items = result.get("handsets", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
379
+ if output == "json":
380
+ print_json(items)
381
+ else:
382
+ print_table(items, columns=[('ID', 'id'), ('Name', 'name'), ('Model', 'model')], limit=limit)
383
+
384
+
385
+
386
+ @app.command("create-handsets")
387
+ def create_handsets(
388
+ location_id: str = typer.Argument(help="locationId"),
389
+ dect_network_id: str = typer.Argument(help="dectNetworkId"),
390
+ line1_member_id: str = typer.Option(None, "--line1-member-id", help="(required) ID of the member on line1 of the handset. Members can be PEO"),
391
+ line2_member_id: str = typer.Option(None, "--line2-member-id", help="ID of the member on line2 of the handset. Members can be PEO"),
392
+ custom_display_name: str = typer.Option(None, "--custom-display-name", help="(required) Custom display name on the handset. Min and max length suppo"),
393
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
394
+ output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
395
+ debug: bool = typer.Option(False, "--debug"),
396
+ ):
397
+ """Add a Handset to a DECT Network."""
398
+ api = get_api(debug=debug)
399
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/dectNetworks/{dect_network_id}/handsets"
400
+ params = {}
401
+ org_id = get_org_id()
402
+ if org_id is not None:
403
+ params["orgId"] = org_id
404
+ if json_body:
405
+ body = json.loads(json_body)
406
+ else:
407
+ body = {}
408
+ if line1_member_id is not None:
409
+ body["line1MemberId"] = line1_member_id
410
+ if line2_member_id is not None:
411
+ body["line2MemberId"] = line2_member_id
412
+ if custom_display_name is not None:
413
+ body["customDisplayName"] = custom_display_name
414
+ _missing = [f for f in ['line1MemberId', 'customDisplayName'] if f not in body or body[f] is None]
415
+ if _missing:
416
+ typer.echo("Error: Missing required fields: " + ", ".join(_missing), err=True)
417
+ raise typer.Exit(1)
418
+ try:
419
+ result = api.session.rest_post(url, json=body, params=params)
420
+ except WebexError as e:
421
+ handle_rest_error(e)
422
+ if output == "json":
423
+ print_json(result)
424
+ elif isinstance(result, dict) and "id" in result:
425
+ typer.echo(f"Created: {result['id']}")
426
+ elif not result or result == {}:
427
+ typer.echo("Created.")
428
+ else:
429
+ print_json(result)
430
+
431
+
432
+
433
+ @app.command("show-handsets")
434
+ def show_handsets(
435
+ location_id: str = typer.Argument(help="locationId"),
436
+ dect_network_id: str = typer.Argument(help="dectNetworkId"),
437
+ handset_id: str = typer.Argument(help="handsetId"),
438
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
439
+ debug: bool = typer.Option(False, "--debug"),
440
+ ):
441
+ """Get Specific DECT Network Handset Details."""
442
+ api = get_api(debug=debug)
443
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/dectNetworks/{dect_network_id}/handsets/{handset_id}"
444
+ params = {}
445
+ org_id = get_org_id()
446
+ if org_id is not None:
447
+ params["orgId"] = org_id
448
+ try:
449
+ result = api.session.rest_get(url, params=params)
450
+ except WebexError as e:
451
+ handle_rest_error(e)
452
+ if output == "json":
453
+ print_json(result)
454
+ else:
455
+ if isinstance(result, dict):
456
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
457
+ elif isinstance(result, list):
458
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
459
+ else:
460
+ print_json(result)
461
+
462
+
463
+
464
+ @app.command("update-handsets")
465
+ def update_handsets(
466
+ location_id: str = typer.Argument(help="locationId"),
467
+ dect_network_id: str = typer.Argument(help="dectNetworkId"),
468
+ handset_id: str = typer.Argument(help="handsetId"),
469
+ line1_member_id: str = typer.Option(None, "--line1-member-id", help="ID of the member on line1 of the handset. Members can be PEO"),
470
+ line2_member_id: str = typer.Option(None, "--line2-member-id", help="ID of the member on line2 of the handset. Members can be PEO"),
471
+ custom_display_name: str = typer.Option(None, "--custom-display-name", help="Custom display name on the handset."),
472
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
473
+ debug: bool = typer.Option(False, "--debug"),
474
+ ):
475
+ """Update DECT Network Handset."""
476
+ api = get_api(debug=debug)
477
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/dectNetworks/{dect_network_id}/handsets/{handset_id}"
478
+ params = {}
479
+ org_id = get_org_id()
480
+ if org_id is not None:
481
+ params["orgId"] = org_id
482
+ if json_body:
483
+ body = json.loads(json_body)
484
+ else:
485
+ body = {}
486
+ if line1_member_id is not None:
487
+ body["line1MemberId"] = line1_member_id
488
+ if line2_member_id is not None:
489
+ body["line2MemberId"] = line2_member_id
490
+ if custom_display_name is not None:
491
+ body["customDisplayName"] = custom_display_name
492
+ try:
493
+ result = api.session.rest_put(url, json=body, params=params)
494
+ except WebexError as e:
495
+ handle_rest_error(e)
496
+ typer.echo(f"Updated.")
497
+
498
+
499
+
500
+ @app.command("delete-handsets-dect-networks")
501
+ def delete_handsets_dect_networks(
502
+ location_id: str = typer.Argument(help="locationId"),
503
+ dect_network_id: str = typer.Argument(help="dectNetworkId"),
504
+ handset_id: str = typer.Argument(help="handsetId"),
505
+ force: bool = typer.Option(False, "--force", help="Skip confirmation"),
506
+ debug: bool = typer.Option(False, "--debug"),
507
+ ):
508
+ """Delete specific DECT Network Handset Details."""
509
+ if not force:
510
+ typer.confirm(f"Delete {handset_id}?", abort=True)
511
+ api = get_api(debug=debug)
512
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/dectNetworks/{dect_network_id}/handsets/{handset_id}"
513
+ params = {}
514
+ org_id = get_org_id()
515
+ if org_id is not None:
516
+ params["orgId"] = org_id
517
+ try:
518
+ api.session.rest_delete(url, params=params)
519
+ except WebexError as e:
520
+ handle_rest_error(e)
521
+ typer.echo(f"Deleted: {handset_id}")
522
+
523
+
524
+
525
+ @app.command("delete-handsets-dect-networks-1")
526
+ def delete_handsets_dect_networks_1(
527
+ location_id: str = typer.Argument(help="locationId"),
528
+ dect_network_id: str = typer.Argument(help="dectNetworkId"),
529
+ force: bool = typer.Option(False, "--force", help="Skip confirmation"),
530
+ debug: bool = typer.Option(False, "--debug"),
531
+ ):
532
+ """Delete multiple handsets."""
533
+ if not force:
534
+ typer.confirm(f"Delete {dect_network_id}?", abort=True)
535
+ api = get_api(debug=debug)
536
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/dectNetworks/{dect_network_id}/handsets/"
537
+ params = {}
538
+ org_id = get_org_id()
539
+ if org_id is not None:
540
+ params["orgId"] = org_id
541
+ try:
542
+ api.session.rest_delete(url, params=params)
543
+ except WebexError as e:
544
+ handle_rest_error(e)
545
+ typer.echo(f"Deleted: {dect_network_id}")
546
+
547
+
548
+
549
+ @app.command("create-bulk")
550
+ def create_bulk(
551
+ location_id: str = typer.Argument(help="locationId"),
552
+ dect_network_id: str = typer.Argument(help="dectNetworkId"),
553
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
554
+ output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
555
+ debug: bool = typer.Option(False, "--debug"),
556
+ ):
557
+ """Add a List of Handsets to a DECT Network\n\nExample --json-body:\n '{"items":[{"line1MemberId":"...","customDisplayName":"...","line2MemberId":"..."}]}'."""
558
+ api = get_api(debug=debug)
559
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/dectNetworks/{dect_network_id}/handsets/bulk"
560
+ params = {}
561
+ org_id = get_org_id()
562
+ if org_id is not None:
563
+ params["orgId"] = org_id
564
+ if json_body:
565
+ body = json.loads(json_body)
566
+ else:
567
+ body = {}
568
+ try:
569
+ result = api.session.rest_post(url, json=body, params=params)
570
+ except WebexError as e:
571
+ handle_rest_error(e)
572
+ if output == "json":
573
+ print_json(result)
574
+ elif isinstance(result, dict) and "items" in result:
575
+ typer.echo(f"Created: {result['items']}")
576
+ elif isinstance(result, dict) and "id" in result:
577
+ typer.echo(f"Created: {result['id']}")
578
+ elif not result or result == {}:
579
+ typer.echo("Created.")
580
+ else:
581
+ print_json(result)
582
+
583
+
584
+
585
+ @app.command("list-dect-networks-people")
586
+ def list_dect_networks_people(
587
+ person_id: str = typer.Argument(help="personId"),
588
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
589
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
590
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
591
+ debug: bool = typer.Option(False, "--debug"),
592
+ ):
593
+ """GET List of DECT networks associated with a Person."""
594
+ api = get_api(debug=debug)
595
+ url = f"https://webexapis.com/v1/telephony/config/people/{person_id}/dectNetworks"
596
+ params = {}
597
+ if limit > 0:
598
+ params["max"] = limit
599
+ if offset > 0:
600
+ params["start"] = offset
601
+ org_id = get_org_id()
602
+ if org_id is not None:
603
+ params["orgId"] = org_id
604
+ result = None
605
+ try:
606
+ result = api.session.rest_get(url, params=params)
607
+ except WebexError as e:
608
+ handle_rest_error(e)
609
+ result = result or []
610
+ items = result.get("dectNetworks", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
611
+ if output == "json":
612
+ print_json(items)
613
+ else:
614
+ print_table(items, columns=[('ID', 'id'), ('Name', 'name'), ('Model', 'model')], limit=limit)
615
+
616
+
617
+
618
+ @app.command("list-dect-networks-workspaces")
619
+ def list_dect_networks_workspaces(
620
+ workspace_id: str = typer.Argument(help="workspaceId"),
621
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
622
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
623
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
624
+ debug: bool = typer.Option(False, "--debug"),
625
+ ):
626
+ """GET List of DECT networks associated with a workspace."""
627
+ api = get_api(debug=debug)
628
+ url = f"https://webexapis.com/v1/telephony/config/workspaces/{workspace_id}/dectNetworks"
629
+ params = {}
630
+ if limit > 0:
631
+ params["max"] = limit
632
+ if offset > 0:
633
+ params["start"] = offset
634
+ org_id = get_org_id()
635
+ if org_id is not None:
636
+ params["orgId"] = org_id
637
+ result = None
638
+ try:
639
+ result = api.session.rest_get(url, params=params)
640
+ except WebexError as e:
641
+ handle_rest_error(e)
642
+ result = result or []
643
+ items = result.get("dectNetworks", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
644
+ if output == "json":
645
+ print_json(items)
646
+ else:
647
+ print_table(items, columns=[('ID', 'id'), ('Name', 'name'), ('Model', 'model')], limit=limit)
648
+
649
+
650
+
651
+ @app.command("list-available-members")
652
+ def list_available_members(
653
+ member_name: str = typer.Option(None, "--member-name", help="Search (Contains) numbers based on member name."),
654
+ phone_number: str = typer.Option(None, "--phone-number", help="Search (Contains) based on number."),
655
+ extension: str = typer.Option(None, "--extension", help="Search (Contains) based on extension."),
656
+ order: str = typer.Option(None, "--order", help="Sort the list of available members on the device in ascendin"),
657
+ location_id: str = typer.Option(None, "--location-id", help="List members for the location ID."),
658
+ exclude_virtual_line: str = typer.Option(None, "--exclude-virtual-line", help="If true, search results will exclude virtual lines in the me"),
659
+ usage_type: str = typer.Option(None, "--usage-type", help="Choices: DEVICE_OWNER, SHARED_LINE"),
660
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
661
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
662
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
663
+ debug: bool = typer.Option(False, "--debug"),
664
+ ):
665
+ """Search Available Members."""
666
+ api = get_api(debug=debug)
667
+ url = f"https://webexapis.com/v1/telephony/config/devices/availableMembers"
668
+ params = {}
669
+ if member_name is not None:
670
+ params["memberName"] = member_name
671
+ if phone_number is not None:
672
+ params["phoneNumber"] = phone_number
673
+ if extension is not None:
674
+ params["extension"] = extension
675
+ if order is not None:
676
+ params["order"] = order
677
+ if location_id is not None:
678
+ params["locationId"] = location_id
679
+ if exclude_virtual_line is not None:
680
+ params["excludeVirtualLine"] = exclude_virtual_line
681
+ if usage_type is not None:
682
+ params["usageType"] = usage_type
683
+ if limit > 0:
684
+ params["max"] = limit
685
+ if offset > 0:
686
+ params["start"] = offset
687
+ org_id = get_org_id()
688
+ if org_id is not None:
689
+ params["orgId"] = org_id
690
+ result = None
691
+ try:
692
+ result = api.session.rest_get(url, params=params)
693
+ except WebexError as e:
694
+ handle_rest_error(e)
695
+ result = result or []
696
+ items = result.get("members", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
697
+ if output == "json":
698
+ print_json(items)
699
+ else:
700
+ print_table(items, columns=[('ID', 'id'), ('Name', 'name'), ('Model', 'model')], limit=limit)
701
+
702
+
703
+
704
+ @app.command("generate-and-enable")
705
+ def generate_and_enable(
706
+ location_id: str = typer.Argument(help="locationId"),
707
+ dect_network_id: str = typer.Argument(help="dectNetworkId"),
708
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body"),
709
+ debug: bool = typer.Option(False, "--debug"),
710
+ ):
711
+ """Generate and Enable DECT Serviceability Password."""
712
+ api = get_api(debug=debug)
713
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/dectNetworks/{dect_network_id}/serviceabilityPassword/actions/generate/invoke"
714
+ params = {}
715
+ org_id = get_org_id()
716
+ if org_id is not None:
717
+ params["orgId"] = org_id
718
+ if json_body:
719
+ body = json.loads(json_body)
720
+ else:
721
+ body = {}
722
+ try:
723
+ result = api.session.rest_post(url, json=body, params=params)
724
+ except WebexError as e:
725
+ handle_rest_error(e)
726
+ print_json(result)
727
+
728
+
729
+
730
+ @app.command("show-serviceability-password")
731
+ def show_serviceability_password(
732
+ location_id: str = typer.Argument(help="locationId"),
733
+ dect_network_id: str = typer.Argument(help="dectNetworkId"),
734
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
735
+ debug: bool = typer.Option(False, "--debug"),
736
+ ):
737
+ """Get DECT Serviceability Password status."""
738
+ api = get_api(debug=debug)
739
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/dectNetworks/{dect_network_id}/serviceabilityPassword"
740
+ params = {}
741
+ org_id = get_org_id()
742
+ if org_id is not None:
743
+ params["orgId"] = org_id
744
+ try:
745
+ result = api.session.rest_get(url, params=params)
746
+ except WebexError as e:
747
+ handle_rest_error(e)
748
+ if output == "json":
749
+ print_json(result)
750
+ else:
751
+ if isinstance(result, dict):
752
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
753
+ elif isinstance(result, list):
754
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
755
+ else:
756
+ print_json(result)
757
+
758
+
759
+
760
+ @app.command("update-serviceability-password")
761
+ def update_serviceability_password(
762
+ location_id: str = typer.Argument(help="locationId"),
763
+ dect_network_id: str = typer.Argument(help="dectNetworkId"),
764
+ enabled: bool = typer.Option(None, "--enabled/--no-enabled", help="DECT serviceability password status. When `enabled` is set t"),
765
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
766
+ debug: bool = typer.Option(False, "--debug"),
767
+ ):
768
+ """Update DECT Serviceability Password status."""
769
+ api = get_api(debug=debug)
770
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/dectNetworks/{dect_network_id}/serviceabilityPassword"
771
+ params = {}
772
+ org_id = get_org_id()
773
+ if org_id is not None:
774
+ params["orgId"] = org_id
775
+ if json_body:
776
+ body = json.loads(json_body)
777
+ else:
778
+ body = {}
779
+ if enabled is not None:
780
+ body["enabled"] = enabled
781
+ try:
782
+ result = api.session.rest_put(url, json=body, params=params)
783
+ except WebexError as e:
784
+ handle_rest_error(e)
785
+ typer.echo(f"Updated.")
786
+
787
+