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,257 @@
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 paging-group.")
10
+
11
+
12
+ @app.command("list")
13
+ def cmd_list(
14
+ location_id: str = typer.Option(None, "--location-id", help="Return only paging groups with matching location ID. Default"),
15
+ name: str = typer.Option(None, "--name", help="Return only paging groups with the matching name."),
16
+ phone_number: str = typer.Option(None, "--phone-number", help="Return only paging groups with matching primary phone number"),
17
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
18
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
19
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
20
+ debug: bool = typer.Option(False, "--debug"),
21
+ ):
22
+ """Read the List of Paging Groups."""
23
+ api = get_api(debug=debug)
24
+ url = f"https://webexapis.com/v1/telephony/config/paging"
25
+ params = {}
26
+ if location_id is not None:
27
+ params["locationId"] = location_id
28
+ if name is not None:
29
+ params["name"] = name
30
+ if phone_number is not None:
31
+ params["phoneNumber"] = phone_number
32
+ if limit > 0:
33
+ params["max"] = limit
34
+ if offset > 0:
35
+ params["start"] = offset
36
+ org_id = get_org_id()
37
+ if org_id is not None:
38
+ params["orgId"] = org_id
39
+ try:
40
+ if limit > 0:
41
+ result = api.session.rest_get(url, params=params)
42
+ result = result or {}
43
+ items = result.get("locationPaging", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
44
+ else:
45
+ items = list(api.session.follow_pagination(url=url, params=params, item_key="locationPaging"))
46
+ except WebexError as e:
47
+ handle_rest_error(e)
48
+ if output == "json":
49
+ print_json(items)
50
+ else:
51
+ print_table(items, columns=[('ID', 'id'), ('Name', 'name'), ('Extension', 'extension'), ('Location', 'locationName'), ('Enabled', 'enabled')], limit=limit)
52
+
53
+
54
+
55
+ @app.command("create")
56
+ def create(
57
+ location_id: str = typer.Argument(help="locationId"),
58
+ name: str = typer.Option(None, "--name", help="(required) Unique name for the paging group. Minimum length is 1. Maxim"),
59
+ phone_number: str = typer.Option(None, "--phone-number", help="Paging group phone number. Minimum length is 1. Maximum leng"),
60
+ extension: str = typer.Option(None, "--extension", help="Paging group extension. Minimum length is 2. Maximum length"),
61
+ language_code: str = typer.Option(None, "--language-code", help="Language code."),
62
+ first_name: str = typer.Option(None, "--first-name", help="First name that displays when a group page is performed. Min"),
63
+ last_name: str = typer.Option(None, "--last-name", help="Last name that displays when a group page is performed. Mini"),
64
+ originator_caller_id_enabled: bool = typer.Option(None, "--originator-caller-id-enabled/--no-originator-caller-id-enabled", help="Determines what is shown on target users caller ID when a gr"),
65
+ dial_by_name: str = typer.Option(None, "--dial-by-name", help="The name to be used for dial by name functions. Characters"),
66
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
67
+ output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
68
+ debug: bool = typer.Option(False, "--debug"),
69
+ ):
70
+ """Create a new Paging Group\n\nExample --json-body:\n '{"name":"...","phoneNumber":"...","extension":"...","languageCode":"...","firstName":"...","lastName":"...","originatorCallerIdEnabled":true,"originators":["..."]}'."""
71
+ api = get_api(debug=debug)
72
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/paging"
73
+ params = {}
74
+ org_id = get_org_id()
75
+ if org_id is not None:
76
+ params["orgId"] = org_id
77
+ if json_body:
78
+ body = json.loads(json_body)
79
+ else:
80
+ body = {}
81
+ if name is not None:
82
+ body["name"] = name
83
+ if phone_number is not None:
84
+ body["phoneNumber"] = phone_number
85
+ if extension is not None:
86
+ body["extension"] = extension
87
+ if language_code is not None:
88
+ body["languageCode"] = language_code
89
+ if first_name is not None:
90
+ body["firstName"] = first_name
91
+ if last_name is not None:
92
+ body["lastName"] = last_name
93
+ if originator_caller_id_enabled is not None:
94
+ body["originatorCallerIdEnabled"] = originator_caller_id_enabled
95
+ if dial_by_name is not None:
96
+ body["dialByName"] = dial_by_name
97
+ _missing = [f for f in ['name'] if f not in body or body[f] is None]
98
+ if _missing:
99
+ typer.echo("Error: Missing required fields: " + ", ".join(_missing), err=True)
100
+ raise typer.Exit(1)
101
+ try:
102
+ result = api.session.rest_post(url, json=body, params=params)
103
+ except WebexError as e:
104
+ handle_rest_error(e)
105
+ if output == "json":
106
+ print_json(result)
107
+ elif isinstance(result, dict) and "id" in result:
108
+ typer.echo(f"Created: {result['id']}")
109
+ elif not result or result == {}:
110
+ typer.echo("Created.")
111
+ else:
112
+ print_json(result)
113
+
114
+
115
+
116
+ @app.command("show")
117
+ def show(
118
+ location_id: str = typer.Argument(help="locationId"),
119
+ paging_id: str = typer.Argument(help="pagingId"),
120
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
121
+ debug: bool = typer.Option(False, "--debug"),
122
+ ):
123
+ """Get Details for a Paging Group."""
124
+ api = get_api(debug=debug)
125
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/paging/{paging_id}"
126
+ params = {}
127
+ org_id = get_org_id()
128
+ if org_id is not None:
129
+ params["orgId"] = org_id
130
+ try:
131
+ result = api.session.rest_get(url, params=params)
132
+ except WebexError as e:
133
+ handle_rest_error(e)
134
+ if output == "json":
135
+ print_json(result)
136
+ else:
137
+ if isinstance(result, dict):
138
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
139
+ elif isinstance(result, list):
140
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
141
+ else:
142
+ print_json(result)
143
+
144
+
145
+
146
+ @app.command("update")
147
+ def update(
148
+ location_id: str = typer.Argument(help="locationId"),
149
+ paging_id: str = typer.Argument(help="pagingId"),
150
+ enabled: bool = typer.Option(None, "--enabled/--no-enabled", help="Whether or not the paging group is enabled."),
151
+ name: str = typer.Option(None, "--name", help="Unique name for the paging group. Minimum length is 1. Maxim"),
152
+ phone_number: str = typer.Option(None, "--phone-number", help="Paging group phone number. Minimum length is 1. Maximum leng"),
153
+ extension: str = typer.Option(None, "--extension", help="Paging group extension. Minimum length is 2. Maximum length"),
154
+ language_code: str = typer.Option(None, "--language-code", help="Language code."),
155
+ first_name: str = typer.Option(None, "--first-name", help="First name to be shown when calls are forwarded out of this"),
156
+ last_name: str = typer.Option(None, "--last-name", help="Last name to be shown when calls are forwarded out of this p"),
157
+ originator_caller_id_enabled: bool = typer.Option(None, "--originator-caller-id-enabled/--no-originator-caller-id-enabled", help="Determines what is shown on target users caller ID when a gr"),
158
+ dial_by_name: str = typer.Option(None, "--dial-by-name", help="Sets or clears the name to be used for dial by name function"),
159
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
160
+ debug: bool = typer.Option(False, "--debug"),
161
+ ):
162
+ """Update a Paging Group\n\nExample --json-body:\n '{"enabled":true,"name":"...","phoneNumber":"...","extension":"...","languageCode":"...","firstName":"...","lastName":"...","originatorCallerIdEnabled":true}'."""
163
+ api = get_api(debug=debug)
164
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/paging/{paging_id}"
165
+ params = {}
166
+ org_id = get_org_id()
167
+ if org_id is not None:
168
+ params["orgId"] = org_id
169
+ if json_body:
170
+ body = json.loads(json_body)
171
+ else:
172
+ body = {}
173
+ if enabled is not None:
174
+ body["enabled"] = enabled
175
+ if name is not None:
176
+ body["name"] = name
177
+ if phone_number is not None:
178
+ body["phoneNumber"] = phone_number
179
+ if extension is not None:
180
+ body["extension"] = extension
181
+ if language_code is not None:
182
+ body["languageCode"] = language_code
183
+ if first_name is not None:
184
+ body["firstName"] = first_name
185
+ if last_name is not None:
186
+ body["lastName"] = last_name
187
+ if originator_caller_id_enabled is not None:
188
+ body["originatorCallerIdEnabled"] = originator_caller_id_enabled
189
+ if dial_by_name is not None:
190
+ body["dialByName"] = dial_by_name
191
+ try:
192
+ result = api.session.rest_put(url, json=body, params=params)
193
+ except WebexError as e:
194
+ handle_rest_error(e)
195
+ typer.echo(f"Updated.")
196
+
197
+
198
+
199
+ @app.command("delete")
200
+ def delete(
201
+ location_id: str = typer.Argument(help="locationId"),
202
+ paging_id: str = typer.Argument(help="pagingId"),
203
+ force: bool = typer.Option(False, "--force", help="Skip confirmation"),
204
+ debug: bool = typer.Option(False, "--debug"),
205
+ ):
206
+ """Delete a Paging Group."""
207
+ if not force:
208
+ typer.confirm(f"Delete {paging_id}?", abort=True)
209
+ api = get_api(debug=debug)
210
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/paging/{paging_id}"
211
+ params = {}
212
+ org_id = get_org_id()
213
+ if org_id is not None:
214
+ params["orgId"] = org_id
215
+ try:
216
+ api.session.rest_delete(url, params=params)
217
+ except WebexError as e:
218
+ handle_rest_error(e)
219
+ typer.echo(f"Deleted: {paging_id}")
220
+
221
+
222
+
223
+ @app.command("list-available-numbers")
224
+ def list_available_numbers(
225
+ location_id: str = typer.Argument(help="locationId"),
226
+ phone_number: str = typer.Option(None, "--phone-number", help="Filter phone numbers based on the comma-separated list provi"),
227
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
228
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
229
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
230
+ debug: bool = typer.Option(False, "--debug"),
231
+ ):
232
+ """Get Paging Group Primary Available Phone Numbers."""
233
+ api = get_api(debug=debug)
234
+ url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/paging/availableNumbers"
235
+ params = {}
236
+ if phone_number is not None:
237
+ params["phoneNumber"] = phone_number
238
+ if limit > 0:
239
+ params["max"] = limit
240
+ if offset > 0:
241
+ params["start"] = offset
242
+ org_id = get_org_id()
243
+ if org_id is not None:
244
+ params["orgId"] = org_id
245
+ result = None
246
+ try:
247
+ result = api.session.rest_get(url, params=params)
248
+ except WebexError as e:
249
+ handle_rest_error(e)
250
+ result = result or []
251
+ items = result.get("phoneNumbers", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
252
+ if output == "json":
253
+ print_json(items)
254
+ else:
255
+ print_table(items, columns=[('ID', 'id'), ('Name', 'name'), ('Extension', 'extension'), ('Location', 'locationName'), ('Enabled', 'enabled')], limit=limit)
256
+
257
+
@@ -0,0 +1,141 @@
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 resolve_org_id
7
+
8
+
9
+ app = typer.Typer(help="Manage Webex Calling partner-admins.")
10
+
11
+
12
+ @app.command("list")
13
+ def cmd_list(
14
+ managed_by: str = typer.Option(..., "--managed-by", help="List customer orgs associated with this person ID."),
15
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
16
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
17
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
18
+ debug: bool = typer.Option(False, "--debug"),
19
+ ):
20
+ """Get all customers managed by a partner admin."""
21
+ api = get_api(debug=debug)
22
+ url = f"https://webexapis.com/v1/partner/organizations"
23
+ params = {}
24
+ if managed_by is not None:
25
+ params["managedBy"] = managed_by
26
+ if limit > 0:
27
+ params["max"] = limit
28
+ if offset > 0:
29
+ params["start"] = offset
30
+ result = None
31
+ try:
32
+ result = api.session.rest_get(url, params=params)
33
+ except WebexError as e:
34
+ handle_rest_error(e)
35
+ result = result or []
36
+ items = result.get("items", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
37
+ if output == "json":
38
+ print_json(items)
39
+ else:
40
+ print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
41
+
42
+
43
+
44
+ @app.command("list-partner-admins")
45
+ def list_partner_admins(
46
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
47
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
48
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
49
+ debug: bool = typer.Option(False, "--debug"),
50
+ ):
51
+ """Get all partner admins assigned to a customer."""
52
+ api = get_api(debug=debug)
53
+ org_id = resolve_org_id(api.session)
54
+ url = f"https://webexapis.com/v1/partner/organizations/{org_id}/partnerAdmins"
55
+ params = {}
56
+ if limit > 0:
57
+ params["max"] = limit
58
+ if offset > 0:
59
+ params["start"] = offset
60
+ result = None
61
+ try:
62
+ result = api.session.rest_get(url, params=params)
63
+ except WebexError as e:
64
+ handle_rest_error(e)
65
+ result = result or []
66
+ items = result.get("items", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
67
+ if output == "json":
68
+ print_json(items)
69
+ else:
70
+ print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
71
+
72
+
73
+
74
+ @app.command("create")
75
+ def create(
76
+ person_id: str = typer.Argument(help="personId"),
77
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
78
+ output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
79
+ debug: bool = typer.Option(False, "--debug"),
80
+ ):
81
+ """Assign partner admin to a customer."""
82
+ api = get_api(debug=debug)
83
+ org_id = resolve_org_id(api.session)
84
+ url = f"https://webexapis.com/v1/partner/organizations/{org_id}/partnerAdmin/{person_id}/assign"
85
+ if json_body:
86
+ body = json.loads(json_body)
87
+ else:
88
+ body = {}
89
+ try:
90
+ result = api.session.rest_post(url, json=body)
91
+ except WebexError as e:
92
+ handle_rest_error(e)
93
+ if output == "json":
94
+ print_json(result)
95
+ elif isinstance(result, dict) and "id" in result:
96
+ typer.echo(f"Created: {result['id']}")
97
+ elif not result or result == {}:
98
+ typer.echo("Created.")
99
+ else:
100
+ print_json(result)
101
+
102
+
103
+
104
+ @app.command("delete")
105
+ def delete(
106
+ person_id: str = typer.Argument(help="personId"),
107
+ force: bool = typer.Option(False, "--force", help="Skip confirmation"),
108
+ debug: bool = typer.Option(False, "--debug"),
109
+ ):
110
+ """Unassign partner admin from a customer."""
111
+ if not force:
112
+ typer.confirm(f"Delete {person_id}?", abort=True)
113
+ api = get_api(debug=debug)
114
+ org_id = resolve_org_id(api.session)
115
+ url = f"https://webexapis.com/v1/partner/organizations/{org_id}/partnerAdmin/{person_id}/unassign"
116
+ try:
117
+ api.session.rest_delete(url)
118
+ except WebexError as e:
119
+ handle_rest_error(e)
120
+ typer.echo(f"Deleted: {person_id}")
121
+
122
+
123
+
124
+ @app.command("delete-partner-admin")
125
+ def delete_partner_admin(
126
+ person_id: str = typer.Argument(help="personId"),
127
+ force: bool = typer.Option(False, "--force", help="Skip confirmation"),
128
+ debug: bool = typer.Option(False, "--debug"),
129
+ ):
130
+ """Revoke all partner admin roles for a given person ID."""
131
+ if not force:
132
+ typer.confirm(f"Delete {person_id}?", abort=True)
133
+ api = get_api(debug=debug)
134
+ url = f"https://webexapis.com/v1/partner/organizations/partnerAdmin/{person_id}"
135
+ try:
136
+ api.session.rest_delete(url)
137
+ except WebexError as e:
138
+ handle_rest_error(e)
139
+ typer.echo(f"Deleted: {person_id}")
140
+
141
+
@@ -0,0 +1,188 @@
1
+ import json
2
+ import typer
3
+ from wxcli.auth import get_api
4
+ from wxcli.errors import WebexError, handle_rest_error
5
+ from wxcli.output import print_table, print_json
6
+
7
+
8
+ app = typer.Typer(help="Manage Webex Calling partner-reports.")
9
+
10
+
11
+ @app.command("list")
12
+ def cmd_list(
13
+ service: str = typer.Option(None, "--service", help="List reports which use this service."),
14
+ template_id: str = typer.Option(None, "--template-id", help="List reports with this report template ID."),
15
+ from_param: str = typer.Option(None, "--from", help="List reports that were created on or after this date."),
16
+ to: str = typer.Option(None, "--to", help="List reports that were created before this date."),
17
+ region_id: str = typer.Option(None, "--region-id", help="Data in the report will be from organizations in this region"),
18
+ on_behalf_of_sub_partner_org_id: str = typer.Option(None, "--on-behalf-of-sub-partner-org-id", help="The encoded organization ID for the sub partner."),
19
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
20
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
21
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
22
+ debug: bool = typer.Option(False, "--debug"),
23
+ ):
24
+ """List Reports."""
25
+ api = get_api(debug=debug)
26
+ url = f"https://webexapis.com/v1/partner/reports"
27
+ params = {}
28
+ if service is not None:
29
+ params["service"] = service
30
+ if template_id is not None:
31
+ params["templateId"] = template_id
32
+ if from_param is not None:
33
+ params["from"] = from_param
34
+ if to is not None:
35
+ params["to"] = to
36
+ if region_id is not None:
37
+ params["regionId"] = region_id
38
+ if on_behalf_of_sub_partner_org_id is not None:
39
+ params["onBehalfOfSubPartnerOrgId"] = on_behalf_of_sub_partner_org_id
40
+ if limit > 0:
41
+ params["max"] = limit
42
+ if offset > 0:
43
+ params["start"] = offset
44
+ result = None
45
+ try:
46
+ result = api.session.rest_get(url, params=params)
47
+ except WebexError as e:
48
+ handle_rest_error(e)
49
+ result = result or []
50
+ items = result.get("Report Attributes", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
51
+ if output == "json":
52
+ print_json(items)
53
+ else:
54
+ print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
55
+
56
+
57
+
58
+ @app.command("create")
59
+ def create(
60
+ on_behalf_of_sub_partner_org_id: str = typer.Option(None, "--on-behalf-of-sub-partner-org-id", help="The encoded organization ID for the sub partner."),
61
+ template_id: str = typer.Option(None, "--template-id", help="(required) Unique ID representing valid report templates."),
62
+ start_date: str = typer.Option(None, "--start-date", help="(required) Data in the report will be from this date onwards."),
63
+ end_date: str = typer.Option(None, "--end-date", help="(required) Data in the report will be until this date."),
64
+ region_id: str = typer.Option(None, "--region-id", help="Data in the report will be from organizations in this region"),
65
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
66
+ output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
67
+ debug: bool = typer.Option(False, "--debug"),
68
+ ):
69
+ """Create a Report."""
70
+ api = get_api(debug=debug)
71
+ url = f"https://webexapis.com/v1/partner/reports"
72
+ params = {}
73
+ if on_behalf_of_sub_partner_org_id is not None:
74
+ params["onBehalfOfSubPartnerOrgId"] = on_behalf_of_sub_partner_org_id
75
+ if json_body:
76
+ body = json.loads(json_body)
77
+ else:
78
+ body = {}
79
+ if template_id is not None:
80
+ body["templateId"] = template_id
81
+ if start_date is not None:
82
+ body["startDate"] = start_date
83
+ if end_date is not None:
84
+ body["endDate"] = end_date
85
+ if region_id is not None:
86
+ body["regionId"] = region_id
87
+ _missing = [f for f in ['templateId', 'startDate', 'endDate'] if f not in body or body[f] is None]
88
+ if _missing:
89
+ typer.echo("Error: Missing required fields: " + ", ".join(_missing), err=True)
90
+ raise typer.Exit(1)
91
+ try:
92
+ result = api.session.rest_post(url, json=body, params=params)
93
+ except WebexError as e:
94
+ handle_rest_error(e)
95
+ if output == "json":
96
+ print_json(result)
97
+ elif isinstance(result, dict) and "id" in result:
98
+ typer.echo(f"Created: {result['id']}")
99
+ elif not result or result == {}:
100
+ typer.echo("Created.")
101
+ else:
102
+ print_json(result)
103
+
104
+
105
+
106
+ @app.command("show")
107
+ def show(
108
+ report_id: str = typer.Argument(help="reportId"),
109
+ on_behalf_of_sub_partner_org_id: str = typer.Option(None, "--on-behalf-of-sub-partner-org-id", help="The encoded organization ID for the sub partner."),
110
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
111
+ debug: bool = typer.Option(False, "--debug"),
112
+ ):
113
+ """Get Report Details."""
114
+ api = get_api(debug=debug)
115
+ url = f"https://webexapis.com/v1/partner/reports/{report_id}"
116
+ params = {}
117
+ if on_behalf_of_sub_partner_org_id is not None:
118
+ params["onBehalfOfSubPartnerOrgId"] = on_behalf_of_sub_partner_org_id
119
+ try:
120
+ result = api.session.rest_get(url, params=params)
121
+ except WebexError as e:
122
+ handle_rest_error(e)
123
+ if output == "json":
124
+ print_json(result)
125
+ else:
126
+ if isinstance(result, dict):
127
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
128
+ elif isinstance(result, list):
129
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
130
+ else:
131
+ print_json(result)
132
+
133
+
134
+
135
+ @app.command("delete")
136
+ def delete(
137
+ report_id: str = typer.Argument(help="reportId"),
138
+ on_behalf_of_sub_partner_org_id: str = typer.Option(None, "--on-behalf-of-sub-partner-org-id", help="The encoded organization ID for the sub partner."),
139
+ force: bool = typer.Option(False, "--force", help="Skip confirmation"),
140
+ debug: bool = typer.Option(False, "--debug"),
141
+ ):
142
+ """Delete a Report."""
143
+ if not force:
144
+ typer.confirm(f"Delete {report_id}?", abort=True)
145
+ api = get_api(debug=debug)
146
+ url = f"https://webexapis.com/v1/partner/reports/{report_id}"
147
+ params = {}
148
+ if on_behalf_of_sub_partner_org_id is not None:
149
+ params["onBehalfOfSubPartnerOrgId"] = on_behalf_of_sub_partner_org_id
150
+ try:
151
+ api.session.rest_delete(url, params=params)
152
+ except WebexError as e:
153
+ handle_rest_error(e)
154
+ typer.echo(f"Deleted: {report_id}")
155
+
156
+
157
+
158
+ @app.command("list-templates")
159
+ def list_templates(
160
+ on_behalf_of_sub_partner_org_id: str = typer.Option(None, "--on-behalf-of-sub-partner-org-id", help="The encoded organization ID for the sub partner."),
161
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
162
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
163
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
164
+ debug: bool = typer.Option(False, "--debug"),
165
+ ):
166
+ """List Report Templates."""
167
+ api = get_api(debug=debug)
168
+ url = f"https://webexapis.com/v1/partner/reports/templates"
169
+ params = {}
170
+ if on_behalf_of_sub_partner_org_id is not None:
171
+ params["onBehalfOfSubPartnerOrgId"] = on_behalf_of_sub_partner_org_id
172
+ if limit > 0:
173
+ params["max"] = limit
174
+ if offset > 0:
175
+ params["start"] = offset
176
+ result = None
177
+ try:
178
+ result = api.session.rest_get(url, params=params)
179
+ except WebexError as e:
180
+ handle_rest_error(e)
181
+ result = result or []
182
+ items = result.get("Template Collection", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
183
+ if output == "json":
184
+ print_json(items)
185
+ else:
186
+ print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
187
+
188
+