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,477 @@
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, get_cc_base_url, get_cc_org_id
7
+
8
+
9
+ app = typer.Typer(help="Manage Webex Contact Center cc-flow.")
10
+
11
+
12
+ @app.command("list")
13
+ def cmd_list(
14
+ project_id: str = typer.Argument(help="projectId"),
15
+ flow_type: str = typer.Option(None, "--flow-type", help="Either of 'FLOW' or 'SUBFLOW'."),
16
+ ids: str = typer.Option(None, "--ids", help="Filters results based on a comma-separated list of flow IDs."),
17
+ page: str = typer.Option(None, "--page", help="Defines the number of the displayed page. The page number st"),
18
+ partial_name_search: str = typer.Option(None, "--partial-name-search", help="Performs a partial string match on the name of the flow. If"),
19
+ search_by: str = typer.Option(None, "--search-by", help="Filter expression in the format `<field>|<value>`. Supports"),
20
+ size: str = typer.Option(None, "--size", help="Defines the number of items to be displayed on a page. If th"),
21
+ include_pagination: str = typer.Option(None, "--include-pagination", help="If set to to true then a different paginated response object"),
22
+ is_validation: str = typer.Option(None, "--is-validation", help="If true, validates the existence of flows by ID regardless o"),
23
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
24
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
25
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
26
+ debug: bool = typer.Option(False, "--debug"),
27
+ ):
28
+ """List Flows or Subflows."""
29
+ api = get_api(debug=debug)
30
+ cc_base_url = get_cc_base_url()
31
+ org_id = get_cc_org_id(api.session)
32
+ url = f"{cc_base_url}/{org_id}/project/{project_id}/flows"
33
+ params = {}
34
+ if flow_type is not None:
35
+ params["flowType"] = flow_type
36
+ if ids is not None:
37
+ params["ids"] = ids
38
+ if page is not None:
39
+ params["page"] = page
40
+ if partial_name_search is not None:
41
+ params["partialNameSearch"] = partial_name_search
42
+ if search_by is not None:
43
+ params["searchBy"] = search_by
44
+ if size is not None:
45
+ params["size"] = size
46
+ if include_pagination is not None:
47
+ params["includePagination"] = include_pagination
48
+ if is_validation is not None:
49
+ params["isValidation"] = is_validation
50
+ if limit > 0:
51
+ params["max"] = limit
52
+ if offset > 0:
53
+ params["start"] = offset
54
+ result = None
55
+ try:
56
+ result = api.session.rest_get(url, params=params)
57
+ except WebexError as e:
58
+ handle_rest_error(e)
59
+ result = result or []
60
+ items = result.get("items", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
61
+ if output == "json":
62
+ print_json(items)
63
+ else:
64
+ print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
65
+
66
+
67
+
68
+ @app.command("publish")
69
+ def publish(
70
+ flow_id: str = typer.Argument(help="flowId"),
71
+ project_id: str = typer.Argument(help="projectId"),
72
+ skip_validation: str = typer.Option(None, "--skip-validation", help="If true, the flow's pre-publish validation is skipped. Use w"),
73
+ flow_type: str = typer.Option(None, "--flow-type", help="Either of 'FLOW' or 'SUBFLOW'."),
74
+ comment: str = typer.Option(None, "--comment", help="A comment to provide context on publishing the flow."),
75
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
76
+ output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
77
+ debug: bool = typer.Option(False, "--debug"),
78
+ ):
79
+ """Publish a Flow or Subflow\n\nExample --json-body:\n '{"comment":"...","tagIds":["..."]}'."""
80
+ api = get_api(debug=debug)
81
+ cc_base_url = get_cc_base_url()
82
+ org_id = get_cc_org_id(api.session)
83
+ url = f"{cc_base_url}/{org_id}/project/{project_id}/flows/{flow_id}:publish"
84
+ params = {}
85
+ if skip_validation is not None:
86
+ params["skipValidation"] = skip_validation
87
+ if flow_type is not None:
88
+ params["flowType"] = flow_type
89
+ if json_body:
90
+ body = json.loads(json_body)
91
+ else:
92
+ body = {}
93
+ if comment is not None:
94
+ body["comment"] = comment
95
+ try:
96
+ result = api.session.rest_post(url, json=body, params=params)
97
+ except WebexError as e:
98
+ handle_rest_error(e)
99
+ if output == "json":
100
+ print_json(result)
101
+ elif isinstance(result, dict) and "id" in result:
102
+ typer.echo(f"Created: {result['id']}")
103
+ elif not result or result == {}:
104
+ typer.echo("Created.")
105
+ else:
106
+ print_json(result)
107
+
108
+
109
+
110
+ @app.command("create-lock")
111
+ def create_lock(
112
+ flow_id: str = typer.Argument(help="flowId"),
113
+ project_id: str = typer.Argument(help="projectId"),
114
+ flow_type: str = typer.Option(None, "--flow-type", help="Either of 'FLOW' or 'SUBFLOW'."),
115
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
116
+ output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
117
+ debug: bool = typer.Option(False, "--debug"),
118
+ ):
119
+ """Lock a Flow or Subflow."""
120
+ api = get_api(debug=debug)
121
+ cc_base_url = get_cc_base_url()
122
+ org_id = get_cc_org_id(api.session)
123
+ url = f"{cc_base_url}/{org_id}/project/{project_id}/flows/{flow_id}:lock"
124
+ params = {}
125
+ if flow_type is not None:
126
+ params["flowType"] = flow_type
127
+ if json_body:
128
+ body = json.loads(json_body)
129
+ else:
130
+ body = {}
131
+ try:
132
+ result = api.session.rest_post(url, json=body, params=params)
133
+ except WebexError as e:
134
+ handle_rest_error(e)
135
+ if output == "json":
136
+ print_json(result)
137
+ elif isinstance(result, dict) and "id" in result:
138
+ typer.echo(f"Created: {result['id']}")
139
+ elif not result or result == {}:
140
+ typer.echo("Created.")
141
+ else:
142
+ print_json(result)
143
+
144
+
145
+
146
+ @app.command("create-unlock")
147
+ def create_unlock(
148
+ flow_id: str = typer.Argument(help="flowId"),
149
+ project_id: str = typer.Argument(help="projectId"),
150
+ flow_type: str = typer.Option(None, "--flow-type", help="Either of 'FLOW' or 'SUBFLOW'."),
151
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
152
+ output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
153
+ debug: bool = typer.Option(False, "--debug"),
154
+ ):
155
+ """Unlock a Flow or Subflow."""
156
+ api = get_api(debug=debug)
157
+ cc_base_url = get_cc_base_url()
158
+ org_id = get_cc_org_id(api.session)
159
+ url = f"{cc_base_url}/{org_id}/project/{project_id}/flows/{flow_id}:unlock"
160
+ params = {}
161
+ if flow_type is not None:
162
+ params["flowType"] = flow_type
163
+ if json_body:
164
+ body = json.loads(json_body)
165
+ else:
166
+ body = {}
167
+ try:
168
+ result = api.session.rest_post(url, json=body, params=params)
169
+ except WebexError as e:
170
+ handle_rest_error(e)
171
+ if output == "json":
172
+ print_json(result)
173
+ elif isinstance(result, dict) and "id" in result:
174
+ typer.echo(f"Created: {result['id']}")
175
+ elif not result or result == {}:
176
+ typer.echo("Created.")
177
+ else:
178
+ print_json(result)
179
+
180
+
181
+
182
+ @app.command("create-validate")
183
+ def create_validate(
184
+ project_id: str = typer.Argument(help="projectId"),
185
+ flow_name: str = typer.Option(None, "--flow-name", help="Name of the flow."),
186
+ flow_type: str = typer.Option(None, "--flow-type", help="Choices: FLOW, SUBFLOW"),
187
+ contact_type: str = typer.Option(None, "--contact-type", help="Channel type the flow is intended for (for example, `telepho"),
188
+ description: str = typer.Option(None, "--description", help="Human-readable description of the flow."),
189
+ version: str = typer.Option(None, "--version", help="Monotonically increasing version number of the document."),
190
+ status: str = typer.Option(None, "--status", help="Choices: Draft, Published"),
191
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
192
+ output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
193
+ debug: bool = typer.Option(False, "--debug"),
194
+ ):
195
+ """Validate a Flow\n\nExample --json-body:\n '{"flowName":"...","flowType":"FLOW","contactType":"...","description":"...","version":0,"status":"Draft","nodes":[{"name":"...","activityName":"...","inputs":"...","outputs":"...","position":"..."}],"edges":[{"key":"...","from_node":"...","from_port":"...","to_node":"...","condition":"..."}]}'."""
196
+ api = get_api(debug=debug)
197
+ cc_base_url = get_cc_base_url()
198
+ org_id = get_cc_org_id(api.session)
199
+ url = f"{cc_base_url}/{org_id}/project/{project_id}/v2/flows:validate"
200
+ if json_body:
201
+ body = json.loads(json_body)
202
+ else:
203
+ body = {}
204
+ if flow_name is not None:
205
+ body["flowName"] = flow_name
206
+ if flow_type is not None:
207
+ body["flowType"] = flow_type
208
+ if contact_type is not None:
209
+ body["contactType"] = contact_type
210
+ if description is not None:
211
+ body["description"] = description
212
+ if version is not None:
213
+ body["version"] = version
214
+ if status is not None:
215
+ body["status"] = status
216
+ try:
217
+ result = api.session.rest_post(url, json=body)
218
+ except WebexError as e:
219
+ handle_rest_error(e)
220
+ if output == "json":
221
+ print_json(result)
222
+ elif isinstance(result, dict) and "id" in result:
223
+ typer.echo(f"Created: {result['id']}")
224
+ elif not result or result == {}:
225
+ typer.echo("Created.")
226
+ else:
227
+ print_json(result)
228
+
229
+
230
+
231
+ @app.command("create-import")
232
+ def create_import(
233
+ project_id: str = typer.Argument(help="projectId"),
234
+ overwrite: str = typer.Option(None, "--overwrite", help="If true, replaces an existing flow with the same name. Defau"),
235
+ flow_type: str = typer.Option(None, "--flow-type", help="Either of 'FLOW' or 'SUBFLOW'."),
236
+ flow_name: str = typer.Option(None, "--flow-name", help="Name of the flow."),
237
+ contact_type: str = typer.Option(None, "--contact-type", help="Channel type the flow is intended for (for example, `telepho"),
238
+ description: str = typer.Option(None, "--description", help="Human-readable description of the flow."),
239
+ version: str = typer.Option(None, "--version", help="Monotonically increasing version number of the document."),
240
+ status: str = typer.Option(None, "--status", help="Choices: Draft, Published"),
241
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
242
+ output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
243
+ debug: bool = typer.Option(False, "--debug"),
244
+ ):
245
+ """Import a Flow\n\nExample --json-body:\n '{"flowName":"...","flowType":"FLOW","contactType":"...","description":"...","version":0,"status":"Draft","nodes":[{"name":"...","activityName":"...","inputs":"...","outputs":"...","position":"..."}],"edges":[{"key":"...","from_node":"...","from_port":"...","to_node":"...","condition":"..."}]}'."""
246
+ api = get_api(debug=debug)
247
+ cc_base_url = get_cc_base_url()
248
+ org_id = get_cc_org_id(api.session)
249
+ url = f"{cc_base_url}/{org_id}/project/{project_id}/v2/flows:import"
250
+ params = {}
251
+ if overwrite is not None:
252
+ params["overwrite"] = overwrite
253
+ if flow_type is not None:
254
+ params["flowType"] = flow_type
255
+ if json_body:
256
+ body = json.loads(json_body)
257
+ else:
258
+ body = {}
259
+ if flow_name is not None:
260
+ body["flowName"] = flow_name
261
+ if contact_type is not None:
262
+ body["contactType"] = contact_type
263
+ if description is not None:
264
+ body["description"] = description
265
+ if version is not None:
266
+ body["version"] = version
267
+ if status is not None:
268
+ body["status"] = status
269
+ try:
270
+ result = api.session.rest_post(url, json=body, params=params)
271
+ except WebexError as e:
272
+ handle_rest_error(e)
273
+ if output == "json":
274
+ print_json(result)
275
+ elif isinstance(result, dict) and "id" in result:
276
+ typer.echo(f"Created: {result['id']}")
277
+ elif not result or result == {}:
278
+ typer.echo("Created.")
279
+ else:
280
+ print_json(result)
281
+
282
+
283
+
284
+ @app.command("show")
285
+ def show(
286
+ project_id: str = typer.Argument(help="projectId"),
287
+ flow_id: str = typer.Argument(help="flowId"),
288
+ flow_type: str = typer.Option(None, "--flow-type", help="Either of 'FLOW' or 'SUBFLOW'."),
289
+ output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
290
+ debug: bool = typer.Option(False, "--debug"),
291
+ ):
292
+ """Get a Flow."""
293
+ api = get_api(debug=debug)
294
+ cc_base_url = get_cc_base_url()
295
+ org_id = get_cc_org_id(api.session)
296
+ url = f"{cc_base_url}/{org_id}/project/{project_id}/v2/flows/{flow_id}"
297
+ params = {}
298
+ if flow_type is not None:
299
+ params["flowType"] = flow_type
300
+ try:
301
+ result = api.session.rest_get(url, params=params)
302
+ except WebexError as e:
303
+ handle_rest_error(e)
304
+ if output == "json":
305
+ print_json(result)
306
+ else:
307
+ if isinstance(result, dict):
308
+ print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
309
+ elif isinstance(result, list):
310
+ print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
311
+ else:
312
+ print_json(result)
313
+
314
+
315
+
316
+ @app.command("create-flows")
317
+ def create_flows(
318
+ project_id: str = typer.Argument(help="projectId"),
319
+ flow_id: str = typer.Argument(help="flowId"),
320
+ expected_version: str = typer.Option(None, "--expected-version", help="Expected current draft version for optimistic locking. The r"),
321
+ flow_type: str = typer.Option(None, "--flow-type", help="Either of 'FLOW' or 'SUBFLOW'."),
322
+ flow_name: str = typer.Option(None, "--flow-name", help="Name of the flow."),
323
+ contact_type: str = typer.Option(None, "--contact-type", help="Channel type the flow is intended for (for example, `telepho"),
324
+ description: str = typer.Option(None, "--description", help="Human-readable description of the flow."),
325
+ version: str = typer.Option(None, "--version", help="Monotonically increasing version number of the document."),
326
+ status: str = typer.Option(None, "--status", help="Choices: Draft, Published"),
327
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
328
+ output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
329
+ debug: bool = typer.Option(False, "--debug"),
330
+ ):
331
+ """Save a Flow Draft\n\nExample --json-body:\n '{"flowName":"...","flowType":"FLOW","contactType":"...","description":"...","version":0,"status":"Draft","nodes":[{"name":"...","activityName":"...","inputs":"...","outputs":"...","position":"..."}],"edges":[{"key":"...","from_node":"...","from_port":"...","to_node":"...","condition":"..."}]}'."""
332
+ api = get_api(debug=debug)
333
+ cc_base_url = get_cc_base_url()
334
+ org_id = get_cc_org_id(api.session)
335
+ url = f"{cc_base_url}/{org_id}/project/{project_id}/v2/flows/{flow_id}"
336
+ params = {}
337
+ if expected_version is not None:
338
+ params["expectedVersion"] = expected_version
339
+ if flow_type is not None:
340
+ params["flowType"] = flow_type
341
+ if json_body:
342
+ body = json.loads(json_body)
343
+ else:
344
+ body = {}
345
+ if flow_name is not None:
346
+ body["flowName"] = flow_name
347
+ if contact_type is not None:
348
+ body["contactType"] = contact_type
349
+ if description is not None:
350
+ body["description"] = description
351
+ if version is not None:
352
+ body["version"] = version
353
+ if status is not None:
354
+ body["status"] = status
355
+ try:
356
+ result = api.session.rest_post(url, json=body, params=params)
357
+ except WebexError as e:
358
+ handle_rest_error(e)
359
+ if output == "json":
360
+ print_json(result)
361
+ elif isinstance(result, dict) and "id" in result:
362
+ typer.echo(f"Created: {result['id']}")
363
+ elif not result or result == {}:
364
+ typer.echo("Created.")
365
+ else:
366
+ print_json(result)
367
+
368
+
369
+
370
+ @app.command("update")
371
+ def update(
372
+ project_id: str = typer.Argument(help="projectId"),
373
+ flow_id: str = typer.Argument(help="flowId"),
374
+ expected_version: str = typer.Option(None, "--expected-version", help="Expected current draft version for optimistic locking. The r"),
375
+ flow_type: str = typer.Option(None, "--flow-type", help="Either of 'FLOW' or 'SUBFLOW'."),
376
+ json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
377
+ debug: bool = typer.Option(False, "--debug"),
378
+ ):
379
+ """Patch a Flow Draft\n\nExample --json-body:\n '{"upsert_nodes":[{}],"upsert_edges":[{}],"remove_node_names":["..."],"remove_edge_keys":["..."]}'."""
380
+ api = get_api(debug=debug)
381
+ cc_base_url = get_cc_base_url()
382
+ org_id = get_cc_org_id(api.session)
383
+ url = f"{cc_base_url}/{org_id}/project/{project_id}/v2/flows/{flow_id}"
384
+ params = {}
385
+ if expected_version is not None:
386
+ params["expectedVersion"] = expected_version
387
+ if flow_type is not None:
388
+ params["flowType"] = flow_type
389
+ if json_body:
390
+ body = json.loads(json_body)
391
+ else:
392
+ body = {}
393
+ try:
394
+ result = api.session.rest_patch(url, json=body, params=params)
395
+ except WebexError as e:
396
+ handle_rest_error(e)
397
+ typer.echo(f"Updated.")
398
+
399
+
400
+
401
+ @app.command("list-validate")
402
+ def list_validate(
403
+ project_id: str = typer.Argument(help="projectId"),
404
+ flow_id: str = typer.Argument(help="flowId"),
405
+ version_id: str = typer.Option(None, "--version-id", help="Version to validate. Use 'draft' for the current draft, or a"),
406
+ flow_type: str = typer.Option(None, "--flow-type", help="Either of 'FLOW' or 'SUBFLOW'."),
407
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
408
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
409
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
410
+ debug: bool = typer.Option(False, "--debug"),
411
+ ):
412
+ """Validate an Existing Flow Draft."""
413
+ api = get_api(debug=debug)
414
+ cc_base_url = get_cc_base_url()
415
+ org_id = get_cc_org_id(api.session)
416
+ url = f"{cc_base_url}/{org_id}/project/{project_id}/v2/flows/{flow_id}:validate"
417
+ params = {}
418
+ if version_id is not None:
419
+ params["versionId"] = version_id
420
+ if flow_type is not None:
421
+ params["flowType"] = flow_type
422
+ if limit > 0:
423
+ params["max"] = limit
424
+ if offset > 0:
425
+ params["start"] = offset
426
+ result = None
427
+ try:
428
+ result = api.session.rest_get(url, params=params)
429
+ except WebexError as e:
430
+ handle_rest_error(e)
431
+ result = result or []
432
+ items = result.get("errors", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
433
+ if output == "json":
434
+ print_json(items)
435
+ else:
436
+ print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
437
+
438
+
439
+
440
+ @app.command("export")
441
+ def export(
442
+ project_id: str = typer.Argument(help="projectId"),
443
+ flow_id: str = typer.Argument(help="flowId"),
444
+ version: str = typer.Option(None, "--version", help="Version to export. Use 'latest' for the most recent publishe"),
445
+ flow_type: str = typer.Option(None, "--flow-type", help="Either of 'FLOW' or 'SUBFLOW'."),
446
+ output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
447
+ limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
448
+ offset: int = typer.Option(0, "--offset", help="Start offset"),
449
+ debug: bool = typer.Option(False, "--debug"),
450
+ ):
451
+ """Export a Flow."""
452
+ api = get_api(debug=debug)
453
+ cc_base_url = get_cc_base_url()
454
+ org_id = get_cc_org_id(api.session)
455
+ url = f"{cc_base_url}/{org_id}/project/{project_id}/v2/flows/{flow_id}:export"
456
+ params = {}
457
+ if version is not None:
458
+ params["version"] = version
459
+ if flow_type is not None:
460
+ params["flowType"] = flow_type
461
+ if limit > 0:
462
+ params["max"] = limit
463
+ if offset > 0:
464
+ params["start"] = offset
465
+ result = None
466
+ try:
467
+ result = api.session.rest_get(url, params=params)
468
+ except WebexError as e:
469
+ handle_rest_error(e)
470
+ result = result or []
471
+ items = result.get("nodes", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
472
+ if output == "json":
473
+ print_json(items)
474
+ else:
475
+ print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
476
+
477
+