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.
- wxcli/__init__.py +4 -0
- wxcli/_playbook/.claude/agents/migration-advisor.md +239 -0
- wxcli/_playbook/.claude/agents/wxc-calling-builder.md +946 -0
- wxcli/_playbook/.claude/rules/cleanup.md +29 -0
- wxcli/_playbook/.claude/rules/cucm-migration.md +58 -0
- wxcli/_playbook/.claude/rules/org-health.md +14 -0
- wxcli/_playbook/.claude/settings.json +8 -0
- wxcli/_playbook/.claude/skills/audit-compliance/SKILL.md +515 -0
- wxcli/_playbook/.claude/skills/call-control/SKILL.md +747 -0
- wxcli/_playbook/.claude/skills/configure-features/SKILL.md +623 -0
- wxcli/_playbook/.claude/skills/configure-routing/SKILL.md +627 -0
- wxcli/_playbook/.claude/skills/contact-center/SKILL.md +1181 -0
- wxcli/_playbook/.claude/skills/cucm-migrate/SKILL.md +820 -0
- wxcli/_playbook/.claude/skills/customer-assist/SKILL.md +443 -0
- wxcli/_playbook/.claude/skills/device-platform/SKILL.md +407 -0
- wxcli/_playbook/.claude/skills/manage-call-settings/SKILL.md +757 -0
- wxcli/_playbook/.claude/skills/manage-devices/SKILL.md +790 -0
- wxcli/_playbook/.claude/skills/manage-identity/SKILL.md +638 -0
- wxcli/_playbook/.claude/skills/manage-licensing/SKILL.md +528 -0
- wxcli/_playbook/.claude/skills/manage-meetings/SKILL.md +708 -0
- wxcli/_playbook/.claude/skills/messaging-bots/SKILL.md +365 -0
- wxcli/_playbook/.claude/skills/messaging-spaces/SKILL.md +520 -0
- wxcli/_playbook/.claude/skills/org-health/SKILL.md +159 -0
- wxcli/_playbook/.claude/skills/provision-calling/SKILL.md +421 -0
- wxcli/_playbook/.claude/skills/query-live/SKILL.md +183 -0
- wxcli/_playbook/.claude/skills/query-live/domains/call-flow-trace.md +223 -0
- wxcli/_playbook/.claude/skills/query-live/domains/call-history.md +44 -0
- wxcli/_playbook/.claude/skills/query-live/domains/features.md +169 -0
- wxcli/_playbook/.claude/skills/query-live/domains/numbers.md +93 -0
- wxcli/_playbook/.claude/skills/query-live/domains/people-and-settings.md +146 -0
- wxcli/_playbook/.claude/skills/query-live/domains/routing.md +124 -0
- wxcli/_playbook/.claude/skills/reporting/SKILL.md +501 -0
- wxcli/_playbook/.claude/skills/reporting/references/cdr-recipes.md +1628 -0
- wxcli/_playbook/.claude/skills/reporting-cc/SKILL.md +251 -0
- wxcli/_playbook/.claude/skills/reporting-meetings/SKILL.md +290 -0
- wxcli/_playbook/.claude/skills/teardown/SKILL.md +214 -0
- wxcli/_playbook/.claude/skills/video-mesh/SKILL.md +379 -0
- wxcli/_playbook/.claude/skills/wxc-calling-debug/SKILL.md +475 -0
- wxcli/_playbook/CLAUDE.md +373 -0
- wxcli/_playbook/docs/reference/CLAUDE.md +25 -0
- wxcli/_playbook/docs/reference/admin-apps-data.md +643 -0
- wxcli/_playbook/docs/reference/admin-audit-security.md +322 -0
- wxcli/_playbook/docs/reference/admin-hybrid.md +383 -0
- wxcli/_playbook/docs/reference/admin-identity-scim.md +1007 -0
- wxcli/_playbook/docs/reference/admin-licensing.md +414 -0
- wxcli/_playbook/docs/reference/admin-org-management.md +586 -0
- wxcli/_playbook/docs/reference/admin-partner.md +357 -0
- wxcli/_playbook/docs/reference/archive/wxc-sdk-patterns.md +1229 -0
- wxcli/_playbook/docs/reference/archive/wxcadm-advanced.md +835 -0
- wxcli/_playbook/docs/reference/archive/wxcadm-core.md +743 -0
- wxcli/_playbook/docs/reference/archive/wxcadm-devices-workspaces.md +1305 -0
- wxcli/_playbook/docs/reference/archive/wxcadm-features.md +849 -0
- wxcli/_playbook/docs/reference/archive/wxcadm-locations.md +938 -0
- wxcli/_playbook/docs/reference/archive/wxcadm-person.md +1255 -0
- wxcli/_playbook/docs/reference/archive/wxcadm-routing.md +1262 -0
- wxcli/_playbook/docs/reference/authentication.md +965 -0
- wxcli/_playbook/docs/reference/call-control.md +1205 -0
- wxcli/_playbook/docs/reference/call-features-additional.md +2541 -0
- wxcli/_playbook/docs/reference/call-features-major.md +1320 -0
- wxcli/_playbook/docs/reference/call-routing.md +2304 -0
- wxcli/_playbook/docs/reference/contact-center-analytics.md +1049 -0
- wxcli/_playbook/docs/reference/contact-center-core.md +1614 -0
- wxcli/_playbook/docs/reference/contact-center-journey.md +457 -0
- wxcli/_playbook/docs/reference/contact-center-routing.md +1138 -0
- wxcli/_playbook/docs/reference/devices-core.md +1869 -0
- wxcli/_playbook/docs/reference/devices-dect.md +1150 -0
- wxcli/_playbook/docs/reference/devices-platform.md +467 -0
- wxcli/_playbook/docs/reference/devices-workspaces.md +1426 -0
- wxcli/_playbook/docs/reference/emergency-services.md +1122 -0
- wxcli/_playbook/docs/reference/location-calling-core.md +2037 -0
- wxcli/_playbook/docs/reference/location-calling-media.md +1334 -0
- wxcli/_playbook/docs/reference/location-recording-advanced.md +1482 -0
- wxcli/_playbook/docs/reference/meetings-content.md +473 -0
- wxcli/_playbook/docs/reference/meetings-core.md +903 -0
- wxcli/_playbook/docs/reference/meetings-infrastructure.md +899 -0
- wxcli/_playbook/docs/reference/meetings-settings.md +850 -0
- wxcli/_playbook/docs/reference/messaging-bots.md +1091 -0
- wxcli/_playbook/docs/reference/messaging-spaces.md +695 -0
- wxcli/_playbook/docs/reference/person-call-settings-behavior.md +1639 -0
- wxcli/_playbook/docs/reference/person-call-settings-handling.md +1394 -0
- wxcli/_playbook/docs/reference/person-call-settings-media.md +1898 -0
- wxcli/_playbook/docs/reference/person-call-settings-permissions.md +1329 -0
- wxcli/_playbook/docs/reference/provisioning.md +1366 -0
- wxcli/_playbook/docs/reference/reporting-analytics.md +1318 -0
- wxcli/_playbook/docs/reference/self-service-call-settings.md +1020 -0
- wxcli/_playbook/docs/reference/virtual-lines.md +1132 -0
- wxcli/_playbook/docs/reference/webhooks-events.md +1444 -0
- wxcli/_playbook/docs/reference/wxcadm-xsi-realtime.md +953 -0
- wxcli/_version.py +24 -0
- wxcli/auth.py +137 -0
- wxcli/commands/__init__.py +0 -0
- wxcli/commands/_registry.py +181 -0
- wxcli/commands/activation_email.py +103 -0
- wxcli/commands/admin_recordings.py +585 -0
- wxcli/commands/analytics.py +105 -0
- wxcli/commands/announcement_playlists.py +219 -0
- wxcli/commands/announcements.py +519 -0
- wxcli/commands/archive_users.py +36 -0
- wxcli/commands/attachment_actions.py +60 -0
- wxcli/commands/audit_events.py +83 -0
- wxcli/commands/authorizations.py +113 -0
- wxcli/commands/auto_attendant.py +642 -0
- wxcli/commands/broadworks_billing_reports.py +126 -0
- wxcli/commands/broadworks_enterprises.py +160 -0
- wxcli/commands/broadworks_subscribers.py +319 -0
- wxcli/commands/broadworks_workspaces.py +108 -0
- wxcli/commands/call_controls.py +1304 -0
- wxcli/commands/call_park.py +494 -0
- wxcli/commands/call_pickup.py +229 -0
- wxcli/commands/call_queue.py +1878 -0
- wxcli/commands/call_recording.py +707 -0
- wxcli/commands/call_routing.py +1610 -0
- wxcli/commands/call_settings_for_me_phase_5.py +212 -0
- wxcli/commands/caller_reputation.py +169 -0
- wxcli/commands/calling_service.py +305 -0
- wxcli/commands/cc_address_book.py +770 -0
- wxcli/commands/cc_agent_greetings.py +242 -0
- wxcli/commands/cc_agent_summaries.py +39 -0
- wxcli/commands/cc_agent_wellbeing.py +216 -0
- wxcli/commands/cc_agents.py +425 -0
- wxcli/commands/cc_ai_assistant.py +55 -0
- wxcli/commands/cc_ai_feature.py +294 -0
- wxcli/commands/cc_audio_files.py +149 -0
- wxcli/commands/cc_auto_csat.py +311 -0
- wxcli/commands/cc_aux_code.py +458 -0
- wxcli/commands/cc_business_hour.py +353 -0
- wxcli/commands/cc_call_monitoring.py +228 -0
- wxcli/commands/cc_callbacks.py +194 -0
- wxcli/commands/cc_campaign.py +212 -0
- wxcli/commands/cc_captures.py +39 -0
- wxcli/commands/cc_contact_list.py +169 -0
- wxcli/commands/cc_contact_number.py +295 -0
- wxcli/commands/cc_data_sources.py +235 -0
- wxcli/commands/cc_desktop_layout.py +380 -0
- wxcli/commands/cc_desktop_profile.py +322 -0
- wxcli/commands/cc_dial_number.py +511 -0
- wxcli/commands/cc_dial_plan.py +347 -0
- wxcli/commands/cc_dnc.py +101 -0
- wxcli/commands/cc_entry_point.py +545 -0
- wxcli/commands/cc_ewt.py +51 -0
- wxcli/commands/cc_flow.py +477 -0
- wxcli/commands/cc_global_vars.py +430 -0
- wxcli/commands/cc_holiday_list.py +335 -0
- wxcli/commands/cc_journey.py +1261 -0
- wxcli/commands/cc_legacy_flows.py +49 -0
- wxcli/commands/cc_multimedia_profile.py +436 -0
- wxcli/commands/cc_notification.py +51 -0
- wxcli/commands/cc_outdial_ani.py +604 -0
- wxcli/commands/cc_overrides.py +341 -0
- wxcli/commands/cc_queue.py +1083 -0
- wxcli/commands/cc_queue_stats.py +55 -0
- wxcli/commands/cc_realtime.py +51 -0
- wxcli/commands/cc_resource_collection.py +312 -0
- wxcli/commands/cc_search.py +46 -0
- wxcli/commands/cc_site.py +400 -0
- wxcli/commands/cc_skill.py +350 -0
- wxcli/commands/cc_skill_profile.py +289 -0
- wxcli/commands/cc_subscriptions.py +426 -0
- wxcli/commands/cc_summaries.py +130 -0
- wxcli/commands/cc_tasks.py +844 -0
- wxcli/commands/cc_team.py +325 -0
- wxcli/commands/cc_user_profiles.py +349 -0
- wxcli/commands/cc_users.py +701 -0
- wxcli/commands/cc_work_types.py +363 -0
- wxcli/commands/cdr.py +89 -0
- wxcli/commands/classifications.py +38 -0
- wxcli/commands/cleanup.py +1427 -0
- wxcli/commands/client_settings.py +72 -0
- wxcli/commands/conference.py +328 -0
- wxcli/commands/configure.py +72 -0
- wxcli/commands/converged_recordings.py +347 -0
- wxcli/commands/converged_recordings_export.py +310 -0
- wxcli/commands/cq_playlists.py +42 -0
- wxcli/commands/cucm.py +2794 -0
- wxcli/commands/cucm_config.py +129 -0
- wxcli/commands/customer_assist.py +373 -0
- wxcli/commands/data_sources.py +227 -0
- wxcli/commands/dect_devices.py +787 -0
- wxcli/commands/device_configurations.py +77 -0
- wxcli/commands/device_dynamic_settings.py +320 -0
- wxcli/commands/device_settings.py +1489 -0
- wxcli/commands/devices.py +280 -0
- wxcli/commands/domains.py +142 -0
- wxcli/commands/ecm.py +178 -0
- wxcli/commands/emergency_services.py +827 -0
- wxcli/commands/events.py +83 -0
- wxcli/commands/external_voicemail.py +52 -0
- wxcli/commands/groups.py +206 -0
- wxcli/commands/guest_management.py +76 -0
- wxcli/commands/hds.py +340 -0
- wxcli/commands/hot_desk.py +67 -0
- wxcli/commands/hot_desking_members.py +118 -0
- wxcli/commands/hot_desking_portal.py +124 -0
- wxcli/commands/hunt_group.py +577 -0
- wxcli/commands/hybrid_clusters.py +71 -0
- wxcli/commands/hybrid_connectors.py +67 -0
- wxcli/commands/identity_org.py +159 -0
- wxcli/commands/init_playbook.py +155 -0
- wxcli/commands/licenses.py +106 -0
- wxcli/commands/live_monitoring.py +40 -0
- wxcli/commands/location_call_handling.py +577 -0
- wxcli/commands/location_schedules.py +341 -0
- wxcli/commands/location_settings.py +1297 -0
- wxcli/commands/location_voicemail.py +494 -0
- wxcli/commands/locations.py +336 -0
- wxcli/commands/meeting_captions.py +110 -0
- wxcli/commands/meeting_chats.py +63 -0
- wxcli/commands/meeting_invitees.py +234 -0
- wxcli/commands/meeting_messages.py +28 -0
- wxcli/commands/meeting_participants.py +278 -0
- wxcli/commands/meeting_polls.py +111 -0
- wxcli/commands/meeting_preferences.py +515 -0
- wxcli/commands/meeting_qa.py +78 -0
- wxcli/commands/meeting_qualities.py +43 -0
- wxcli/commands/meeting_reports.py +101 -0
- wxcli/commands/meeting_session_types.py +105 -0
- wxcli/commands/meeting_site.py +57 -0
- wxcli/commands/meeting_slido.py +44 -0
- wxcli/commands/meeting_summaries.py +92 -0
- wxcli/commands/meeting_tracking_codes.py +233 -0
- wxcli/commands/meeting_transcripts.py +230 -0
- wxcli/commands/meetings.py +1997 -0
- wxcli/commands/memberships.py +164 -0
- wxcli/commands/messages.py +213 -0
- wxcli/commands/mode_management.py +244 -0
- wxcli/commands/my_call_settings.py +3577 -0
- wxcli/commands/numbers.py +405 -0
- wxcli/commands/operating_modes.py +410 -0
- wxcli/commands/org_contacts.py +284 -0
- wxcli/commands/org_health_cli.py +55 -0
- wxcli/commands/org_settings.py +71 -0
- wxcli/commands/organizations.py +83 -0
- wxcli/commands/paging_group.py +257 -0
- wxcli/commands/partner_admins.py +141 -0
- wxcli/commands/partner_reports.py +188 -0
- wxcli/commands/partner_tags.py +216 -0
- wxcli/commands/people.py +279 -0
- wxcli/commands/person_call_settings.py +71 -0
- wxcli/commands/pstn.py +280 -0
- wxcli/commands/recording_report.py +154 -0
- wxcli/commands/report_templates.py +38 -0
- wxcli/commands/reports.py +144 -0
- wxcli/commands/resource_group_memberships.py +160 -0
- wxcli/commands/resource_groups.py +67 -0
- wxcli/commands/roles.py +63 -0
- wxcli/commands/room_tabs.py +160 -0
- wxcli/commands/rooms.py +225 -0
- wxcli/commands/scim_bulk.py +46 -0
- wxcli/commands/scim_groups.py +246 -0
- wxcli/commands/scim_schemas.py +82 -0
- wxcli/commands/scim_users.py +283 -0
- wxcli/commands/security_audit.py +54 -0
- wxcli/commands/service_apps.py +51 -0
- wxcli/commands/single_number_reach.py +209 -0
- wxcli/commands/team_memberships.py +155 -0
- wxcli/commands/teams.py +153 -0
- wxcli/commands/ucm_profile.py +42 -0
- wxcli/commands/update.py +219 -0
- wxcli/commands/user_settings.py +5116 -0
- wxcli/commands/video_mesh.py +1090 -0
- wxcli/commands/virtual_extensions.py +502 -0
- wxcli/commands/virtual_line_settings.py +2072 -0
- wxcli/commands/webhooks.py +176 -0
- wxcli/commands/wholesale_billing_reports.py +147 -0
- wxcli/commands/wholesale_provisioning.py +523 -0
- wxcli/commands/workspace_locations.py +339 -0
- wxcli/commands/workspace_metrics.py +103 -0
- wxcli/commands/workspace_personalization.py +70 -0
- wxcli/commands/workspace_settings.py +3347 -0
- wxcli/commands/workspaces.py +279 -0
- wxcli/commands/xapi.py +114 -0
- wxcli/config.py +139 -0
- wxcli/errors.py +77 -0
- wxcli/main.py +210 -0
- wxcli/migration/CLAUDE.md +106 -0
- wxcli/migration/__init__.py +1 -0
- wxcli/migration/advisory/CLAUDE.md +196 -0
- wxcli/migration/advisory/__init__.py +46 -0
- wxcli/migration/advisory/advisor.py +107 -0
- wxcli/migration/advisory/advisory_patterns.py +2552 -0
- wxcli/migration/advisory/recommendation_rules.py +835 -0
- wxcli/migration/cucm/CLAUDE.md +74 -0
- wxcli/migration/cucm/__init__.py +5 -0
- wxcli/migration/cucm/connection.py +265 -0
- wxcli/migration/cucm/discovery.py +225 -0
- wxcli/migration/cucm/extractors/__init__.py +1 -0
- wxcli/migration/cucm/extractors/announcements.py +92 -0
- wxcli/migration/cucm/extractors/base.py +90 -0
- wxcli/migration/cucm/extractors/device_profiles.py +98 -0
- wxcli/migration/cucm/extractors/devices.py +193 -0
- wxcli/migration/cucm/extractors/e911.py +103 -0
- wxcli/migration/cucm/extractors/features.py +309 -0
- wxcli/migration/cucm/extractors/helpers.py +73 -0
- wxcli/migration/cucm/extractors/informational.py +295 -0
- wxcli/migration/cucm/extractors/locations.py +221 -0
- wxcli/migration/cucm/extractors/moh.py +100 -0
- wxcli/migration/cucm/extractors/remote_destinations.py +96 -0
- wxcli/migration/cucm/extractors/routing.py +493 -0
- wxcli/migration/cucm/extractors/shared_lines.py +146 -0
- wxcli/migration/cucm/extractors/templates.py +229 -0
- wxcli/migration/cucm/extractors/tier4.py +173 -0
- wxcli/migration/cucm/extractors/users.py +240 -0
- wxcli/migration/cucm/extractors/voicemail.py +243 -0
- wxcli/migration/cucm/extractors/workspaces.py +88 -0
- wxcli/migration/cucm/unity_connection.py +304 -0
- wxcli/migration/execute/CLAUDE.md +372 -0
- wxcli/migration/execute/__init__.py +303 -0
- wxcli/migration/execute/batch.py +277 -0
- wxcli/migration/execute/dependency.py +483 -0
- wxcli/migration/execute/engine.py +964 -0
- wxcli/migration/execute/handlers.py +2232 -0
- wxcli/migration/execute/planner.py +1939 -0
- wxcli/migration/execute/runtime.py +571 -0
- wxcli/migration/export/CLAUDE.md +49 -0
- wxcli/migration/export/__init__.py +8 -0
- wxcli/migration/export/csv_export.py +126 -0
- wxcli/migration/export/deployment_plan.py +479 -0
- wxcli/migration/export/json_export.py +60 -0
- wxcli/migration/models.py +941 -0
- wxcli/migration/phone_models.py +50 -0
- wxcli/migration/preflight/CLAUDE.md +52 -0
- wxcli/migration/preflight/__init__.py +89 -0
- wxcli/migration/preflight/checks.py +911 -0
- wxcli/migration/preflight/runner.py +293 -0
- wxcli/migration/rate_limiter.py +144 -0
- wxcli/migration/report/CLAUDE.md +245 -0
- wxcli/migration/report/__init__.py +7 -0
- wxcli/migration/report/appendix.py +2437 -0
- wxcli/migration/report/assembler.py +183 -0
- wxcli/migration/report/charts.py +263 -0
- wxcli/migration/report/executive.py +776 -0
- wxcli/migration/report/explainer.py +704 -0
- wxcli/migration/report/helpers.py +74 -0
- wxcli/migration/report/ingest.py +152 -0
- wxcli/migration/report/notice_templates.py +192 -0
- wxcli/migration/report/score.py +375 -0
- wxcli/migration/report/styles.py +1052 -0
- wxcli/migration/report/user_diff.py +1052 -0
- wxcli/migration/report/user_notice.py +425 -0
- wxcli/migration/state.py +139 -0
- wxcli/migration/store.py +860 -0
- wxcli/migration/transform/CLAUDE.md +107 -0
- wxcli/migration/transform/__init__.py +12 -0
- wxcli/migration/transform/analysis_pipeline.py +453 -0
- wxcli/migration/transform/analyzers/__init__.py +142 -0
- wxcli/migration/transform/analyzers/css_permission.py +189 -0
- wxcli/migration/transform/analyzers/css_routing.py +335 -0
- wxcli/migration/transform/analyzers/device_compatibility.py +122 -0
- wxcli/migration/transform/analyzers/dn_ambiguity.py +131 -0
- wxcli/migration/transform/analyzers/duplicate_user.py +221 -0
- wxcli/migration/transform/analyzers/extension_conflict.py +173 -0
- wxcli/migration/transform/analyzers/feature_approximation.py +227 -0
- wxcli/migration/transform/analyzers/layout_overflow.py +174 -0
- wxcli/migration/transform/analyzers/location_ambiguity.py +184 -0
- wxcli/migration/transform/analyzers/missing_data.py +300 -0
- wxcli/migration/transform/analyzers/selective_call_handling.py +394 -0
- wxcli/migration/transform/analyzers/shared_line.py +170 -0
- wxcli/migration/transform/analyzers/voicemail_compatibility.py +267 -0
- wxcli/migration/transform/analyzers/workspace_license.py +212 -0
- wxcli/migration/transform/cross_reference.py +1136 -0
- wxcli/migration/transform/cucm_pattern.py +246 -0
- wxcli/migration/transform/decisions.py +301 -0
- wxcli/migration/transform/e164.py +145 -0
- wxcli/migration/transform/engine.py +256 -0
- wxcli/migration/transform/mappers/CLAUDE.md +123 -0
- wxcli/migration/transform/mappers/__init__.py +76 -0
- wxcli/migration/transform/mappers/announcement_mapper.py +122 -0
- wxcli/migration/transform/mappers/base.py +190 -0
- wxcli/migration/transform/mappers/button_template_mapper.py +186 -0
- wxcli/migration/transform/mappers/call_forwarding_mapper.py +228 -0
- wxcli/migration/transform/mappers/call_settings_mapper.py +144 -0
- wxcli/migration/transform/mappers/css_mapper.py +868 -0
- wxcli/migration/transform/mappers/dect_mapper.py +367 -0
- wxcli/migration/transform/mappers/device_layout_mapper.py +289 -0
- wxcli/migration/transform/mappers/device_mapper.py +227 -0
- wxcli/migration/transform/mappers/device_profile_mapper.py +190 -0
- wxcli/migration/transform/mappers/device_settings_mapper.py +464 -0
- wxcli/migration/transform/mappers/e911_mapper.py +129 -0
- wxcli/migration/transform/mappers/ecbn_mapper.py +263 -0
- wxcli/migration/transform/mappers/executive_assistant_mapper.py +156 -0
- wxcli/migration/transform/mappers/feature_mapper.py +1179 -0
- wxcli/migration/transform/mappers/line_mapper.py +310 -0
- wxcli/migration/transform/mappers/location_mapper.py +235 -0
- wxcli/migration/transform/mappers/moh_mapper.py +114 -0
- wxcli/migration/transform/mappers/monitoring_mapper.py +221 -0
- wxcli/migration/transform/mappers/receptionist_mapper.py +245 -0
- wxcli/migration/transform/mappers/routing_mapper.py +821 -0
- wxcli/migration/transform/mappers/snr_mapper.py +201 -0
- wxcli/migration/transform/mappers/softkey_mapper.py +205 -0
- wxcli/migration/transform/mappers/user_mapper.py +264 -0
- wxcli/migration/transform/mappers/voicemail_group_mapper.py +286 -0
- wxcli/migration/transform/mappers/voicemail_mapper.py +514 -0
- wxcli/migration/transform/mappers/workspace_mapper.py +464 -0
- wxcli/migration/transform/normalizers.py +2008 -0
- wxcli/migration/transform/pattern_converter.py +106 -0
- wxcli/migration/transform/pipeline.py +271 -0
- wxcli/migration/transform/rules.py +255 -0
- wxcli/org_health/CLAUDE.md +63 -0
- wxcli/org_health/__init__.py +7 -0
- wxcli/org_health/analyze.py +93 -0
- wxcli/org_health/checks.py +458 -0
- wxcli/org_health/collector.py +62 -0
- wxcli/org_health/models.py +78 -0
- wxcli/org_health/report.py +300 -0
- wxcli/output.py +101 -0
- wxcli-1.2.0.dist-info/METADATA +505 -0
- wxcli-1.2.0.dist-info/RECORD +413 -0
- wxcli-1.2.0.dist-info/WHEEL +5 -0
- wxcli-1.2.0.dist-info/entry_points.txt +2 -0
- wxcli-1.2.0.dist-info/licenses/LICENSE +191 -0
- wxcli-1.2.0.dist-info/scm_file_list.json +751 -0
- wxcli-1.2.0.dist-info/scm_version.json +8 -0
- wxcli-1.2.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
"""Configuration management for CUCM migration projects.
|
|
2
|
+
|
|
3
|
+
Manages config.json in the project directory. Keys control pipeline behavior
|
|
4
|
+
(country code, language, auto-rules, etc.).
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import json
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Any
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
# Default auto-resolution rules for clear-cut, low-risk decisions.
|
|
15
|
+
#
|
|
16
|
+
# These reduce manual review burden without risking incorrect choices.
|
|
17
|
+
# Override per-project by writing "auto_rules" to config.json.
|
|
18
|
+
#
|
|
19
|
+
# IMPORTANT: Defaults are seeded into a project's `config.json` ONCE by
|
|
20
|
+
# `wxcli cucm init`. They are NOT applied at runtime — `load_config()`
|
|
21
|
+
# reads the saved file literally. Operators who edit `config.json` to
|
|
22
|
+
# remove a default can restore it via `wxcli cucm config reset auto_rules`
|
|
23
|
+
# (which clobbers any custom rules in `auto_rules` — preserve them by
|
|
24
|
+
# hand-editing instead).
|
|
25
|
+
#
|
|
26
|
+
# To add a new default rule WITHOUT regressing existing in-flight projects:
|
|
27
|
+
# 1. Append the rule here.
|
|
28
|
+
# 2. Document the upgrade procedure in the relevant runbook.
|
|
29
|
+
# 3. Operators on existing projects must either (a) hand-edit their
|
|
30
|
+
# config.json to append the new rule, or (b) run
|
|
31
|
+
# `wxcli cucm config reset auto_rules` (destructive to custom rules).
|
|
32
|
+
DEFAULT_AUTO_RULES: list[dict[str, Any]] = [
|
|
33
|
+
# Incompatible devices have no migration path — always skip
|
|
34
|
+
{"type": "DEVICE_INCOMPATIBLE", "choice": "skip",
|
|
35
|
+
"reason": "No MPP migration path exists for this device"},
|
|
36
|
+
# NOTE: DEVICE_FIRMWARE_CONVERTIBLE was removed 2026-04-15. Convertibility
|
|
37
|
+
# is now a phone model classification, not an operator choice — the
|
|
38
|
+
# planner emits a create_activation_code op unconditionally for every
|
|
39
|
+
# convertible device.
|
|
40
|
+
# Hotdesk DN conflicts — primary DN always wins
|
|
41
|
+
{"type": "HOTDESK_DN_CONFLICT", "choice": "keep_primary",
|
|
42
|
+
"reason": "Primary DN takes precedence over hotdesk conflict"},
|
|
43
|
+
# CUCM-only forwarding variants — accept the loss (rarely configured)
|
|
44
|
+
{"type": "FORWARDING_LOSSY", "choice": "accept",
|
|
45
|
+
"reason": "CUCM-specific forwarding variant has no Webex equivalent"},
|
|
46
|
+
# SNR timer controls — accept Webex simplification
|
|
47
|
+
{"type": "SNR_LOSSY", "choice": "accept",
|
|
48
|
+
"reason": "SNR timer controls are simplified in Webex"},
|
|
49
|
+
# Unmappable CUCM button types — no Webex equivalent exists
|
|
50
|
+
{"type": "BUTTON_UNMAPPABLE", "choice": "accept_loss",
|
|
51
|
+
"reason": "CUCM button type has no Webex equivalent"},
|
|
52
|
+
# Calling permissions with 0 affected users — orphaned profile
|
|
53
|
+
# Analyzer writes "assigned_users_count" in context (css_permission.py line 128)
|
|
54
|
+
{"type": "CALLING_PERMISSION_MISMATCH",
|
|
55
|
+
"match": {"assigned_users_count": 0}, "choice": "skip",
|
|
56
|
+
"reason": "Orphaned permission profile — 0 users affected"},
|
|
57
|
+
# Missing data on devices that are already incompatible — skip
|
|
58
|
+
# (fixing missing data on a device we're not migrating is pointless)
|
|
59
|
+
# `is_on_incompatible_device` is written by
|
|
60
|
+
# enrich_cross_decision_context() during analysis_pipeline step 3.5.
|
|
61
|
+
{"type": "MISSING_DATA",
|
|
62
|
+
"match": {"is_on_incompatible_device": True}, "choice": "skip",
|
|
63
|
+
"reason": "Missing data on incompatible device — skipping device anyway"},
|
|
64
|
+
]
|
|
65
|
+
|
|
66
|
+
DEFAULT_CONFIG: dict[str, Any] = {
|
|
67
|
+
"country_code": "+1",
|
|
68
|
+
"default_language": "en_us",
|
|
69
|
+
"default_country": "US",
|
|
70
|
+
"outside_dial_digit": "9",
|
|
71
|
+
"create_method": "people_api",
|
|
72
|
+
"include_phoneless_users": False,
|
|
73
|
+
"auto_rules": DEFAULT_AUTO_RULES,
|
|
74
|
+
"recording_vendor": "Webex",
|
|
75
|
+
"site_prefix_rules": [],
|
|
76
|
+
"category_rules": None,
|
|
77
|
+
# Execution tuning (from 2026-04-10-bulk-operations spec §5b).
|
|
78
|
+
# Device count at/above which the planner replaces per-device ops with
|
|
79
|
+
# Webex bulk job submissions. Set to 0 to force bulk always; set to a
|
|
80
|
+
# very large number (e.g., 999999) to disable bulk.
|
|
81
|
+
"bulk_device_threshold": 100,
|
|
82
|
+
# FedRAMP / Webex for Government: rebuildPhones is not supported.
|
|
83
|
+
# Set to True to omit bulk_rebuild_phones ops from the plan.
|
|
84
|
+
"skip_rebuild_phones": False,
|
|
85
|
+
"e911": {
|
|
86
|
+
"auto_configure_ecbn": True,
|
|
87
|
+
"notification_email": None,
|
|
88
|
+
"redsky_enabled": False,
|
|
89
|
+
"primary_did_strategy": "first_line",
|
|
90
|
+
},
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def load_config(project_dir: Path) -> dict[str, Any]:
|
|
95
|
+
"""Load config.json from the project directory, with defaults."""
|
|
96
|
+
config_path = project_dir / "config.json"
|
|
97
|
+
config = dict(DEFAULT_CONFIG)
|
|
98
|
+
if config_path.exists():
|
|
99
|
+
with open(config_path) as f:
|
|
100
|
+
saved = json.load(f)
|
|
101
|
+
config.update(saved)
|
|
102
|
+
return config
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def save_config(project_dir: Path, config: dict[str, Any]) -> None:
|
|
106
|
+
"""Write config.json to the project directory."""
|
|
107
|
+
config_path = project_dir / "config.json"
|
|
108
|
+
with open(config_path, "w") as f:
|
|
109
|
+
json.dump(config, f, indent=2)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def coerce_value(value: str) -> Any:
|
|
113
|
+
"""Coerce a string CLI value to the appropriate Python type."""
|
|
114
|
+
if value.lower() in ("true", "yes"):
|
|
115
|
+
return True
|
|
116
|
+
if value.lower() in ("false", "no"):
|
|
117
|
+
return False
|
|
118
|
+
try:
|
|
119
|
+
n = int(value)
|
|
120
|
+
# Only coerce if round-trip matches (preserves "+44" as string)
|
|
121
|
+
if str(n) == value:
|
|
122
|
+
return n
|
|
123
|
+
except ValueError:
|
|
124
|
+
pass
|
|
125
|
+
try:
|
|
126
|
+
return json.loads(value)
|
|
127
|
+
except (json.JSONDecodeError, ValueError):
|
|
128
|
+
pass
|
|
129
|
+
return value
|
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import typer
|
|
3
|
+
from wxcli.auth import get_api
|
|
4
|
+
from wxcli.errors import WebexError, handle_rest_error
|
|
5
|
+
from wxcli.output import print_table, print_json
|
|
6
|
+
from wxcli.config import get_org_id
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
app = typer.Typer(help="Manage Webex Calling customer-assist.")
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@app.command("list")
|
|
13
|
+
def cmd_list(
|
|
14
|
+
output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
|
|
15
|
+
limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
|
|
16
|
+
offset: int = typer.Option(0, "--offset", help="Start offset"),
|
|
17
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
18
|
+
):
|
|
19
|
+
"""List Wrap Up Reasons."""
|
|
20
|
+
api = get_api(debug=debug)
|
|
21
|
+
url = f"https://webexapis.com/v1/telephony/config/cxEssentials/wrapup/reasons"
|
|
22
|
+
params = {}
|
|
23
|
+
if limit > 0:
|
|
24
|
+
params["max"] = limit
|
|
25
|
+
if offset > 0:
|
|
26
|
+
params["start"] = offset
|
|
27
|
+
result = None
|
|
28
|
+
try:
|
|
29
|
+
result = api.session.rest_get(url, params=params)
|
|
30
|
+
except WebexError as e:
|
|
31
|
+
handle_rest_error(e)
|
|
32
|
+
result = result or []
|
|
33
|
+
items = result.get("wrapupReasons", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
|
|
34
|
+
if output == "json":
|
|
35
|
+
print_json(items)
|
|
36
|
+
else:
|
|
37
|
+
print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@app.command("create")
|
|
42
|
+
def create(
|
|
43
|
+
name: str = typer.Option(None, "--name", help="(required) Name of the wrap-up reason."),
|
|
44
|
+
description: str = typer.Option(None, "--description", help="Description of the wrap-up reason."),
|
|
45
|
+
assign_all_queues_enabled: bool = typer.Option(None, "--assign-all-queues-enabled/--no-assign-all-queues-enabled", help="Denotes whether all queues are assigned to the wrap-up reaso"),
|
|
46
|
+
json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
|
|
47
|
+
output: str = typer.Option("id", "--output", "-o", help="Output format: id|json"),
|
|
48
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
49
|
+
):
|
|
50
|
+
"""Create Wrap Up Reason\n\nExample --json-body:\n '{"name":"...","description":"...","queues":["..."],"assignAllQueuesEnabled":true}'."""
|
|
51
|
+
api = get_api(debug=debug)
|
|
52
|
+
url = f"https://webexapis.com/v1/telephony/config/cxEssentials/wrapup/reasons"
|
|
53
|
+
if json_body:
|
|
54
|
+
body = json.loads(json_body)
|
|
55
|
+
else:
|
|
56
|
+
body = {}
|
|
57
|
+
if name is not None:
|
|
58
|
+
body["name"] = name
|
|
59
|
+
if description is not None:
|
|
60
|
+
body["description"] = description
|
|
61
|
+
if assign_all_queues_enabled is not None:
|
|
62
|
+
body["assignAllQueuesEnabled"] = assign_all_queues_enabled
|
|
63
|
+
_missing = [f for f in ['name'] if f not in body or body[f] is None]
|
|
64
|
+
if _missing:
|
|
65
|
+
typer.echo("Error: Missing required fields: " + ", ".join(_missing), err=True)
|
|
66
|
+
raise typer.Exit(1)
|
|
67
|
+
try:
|
|
68
|
+
result = api.session.rest_post(url, json=body)
|
|
69
|
+
except WebexError as e:
|
|
70
|
+
handle_rest_error(e)
|
|
71
|
+
if output == "json":
|
|
72
|
+
print_json(result)
|
|
73
|
+
elif isinstance(result, dict) and "id" in result:
|
|
74
|
+
typer.echo(f"Created: {result['id']}")
|
|
75
|
+
elif not result or result == {}:
|
|
76
|
+
typer.echo("Created.")
|
|
77
|
+
else:
|
|
78
|
+
print_json(result)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
@app.command("show")
|
|
83
|
+
def show(
|
|
84
|
+
wrapup_reason_id: str = typer.Argument(help="wrapupReasonId"),
|
|
85
|
+
output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
|
|
86
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
87
|
+
):
|
|
88
|
+
"""Read Wrap Up Reason."""
|
|
89
|
+
api = get_api(debug=debug)
|
|
90
|
+
url = f"https://webexapis.com/v1/telephony/config/cxEssentials/wrapup/reasons/{wrapup_reason_id}"
|
|
91
|
+
try:
|
|
92
|
+
result = api.session.rest_get(url)
|
|
93
|
+
except WebexError as e:
|
|
94
|
+
handle_rest_error(e)
|
|
95
|
+
if output == "json":
|
|
96
|
+
print_json(result)
|
|
97
|
+
else:
|
|
98
|
+
if isinstance(result, dict):
|
|
99
|
+
print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
|
|
100
|
+
elif isinstance(result, list):
|
|
101
|
+
print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
|
|
102
|
+
else:
|
|
103
|
+
print_json(result)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
@app.command("update")
|
|
108
|
+
def update(
|
|
109
|
+
wrapup_reason_id: str = typer.Argument(help="wrapupReasonId"),
|
|
110
|
+
name: str = typer.Option(None, "--name", help="Name of the wrap-up reason."),
|
|
111
|
+
description: str = typer.Option(None, "--description", help="Description of the wrap-up reason."),
|
|
112
|
+
assign_all_queues_enabled: bool = typer.Option(None, "--assign-all-queues-enabled/--no-assign-all-queues-enabled", help="Denotes whether all queues are assigned to the wrap-up reaso"),
|
|
113
|
+
unassign_all_queues_enabled: bool = typer.Option(None, "--unassign-all-queues-enabled/--no-unassign-all-queues-enabled", help="Denotes whether all queues are unassigned from the wrap-up r"),
|
|
114
|
+
json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
|
|
115
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
116
|
+
):
|
|
117
|
+
"""Update Wrap Up Reason\n\nExample --json-body:\n '{"name":"...","description":"...","queuesToAssign":["..."],"queuesToUnassign":["..."],"assignAllQueuesEnabled":true,"unassignAllQueuesEnabled":true}'."""
|
|
118
|
+
api = get_api(debug=debug)
|
|
119
|
+
url = f"https://webexapis.com/v1/telephony/config/cxEssentials/wrapup/reasons/{wrapup_reason_id}"
|
|
120
|
+
if json_body:
|
|
121
|
+
body = json.loads(json_body)
|
|
122
|
+
else:
|
|
123
|
+
body = {}
|
|
124
|
+
if name is not None:
|
|
125
|
+
body["name"] = name
|
|
126
|
+
if description is not None:
|
|
127
|
+
body["description"] = description
|
|
128
|
+
if assign_all_queues_enabled is not None:
|
|
129
|
+
body["assignAllQueuesEnabled"] = assign_all_queues_enabled
|
|
130
|
+
if unassign_all_queues_enabled is not None:
|
|
131
|
+
body["unassignAllQueuesEnabled"] = unassign_all_queues_enabled
|
|
132
|
+
try:
|
|
133
|
+
result = api.session.rest_put(url, json=body)
|
|
134
|
+
except WebexError as e:
|
|
135
|
+
handle_rest_error(e)
|
|
136
|
+
typer.echo(f"Updated.")
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
@app.command("delete")
|
|
141
|
+
def delete(
|
|
142
|
+
wrapup_reason_id: str = typer.Argument(help="wrapupReasonId"),
|
|
143
|
+
force: bool = typer.Option(False, "--force", help="Skip confirmation"),
|
|
144
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
145
|
+
):
|
|
146
|
+
"""Delete Wrap Up Reason."""
|
|
147
|
+
if not force:
|
|
148
|
+
typer.confirm(f"Delete {wrapup_reason_id}?", abort=True)
|
|
149
|
+
api = get_api(debug=debug)
|
|
150
|
+
url = f"https://webexapis.com/v1/telephony/config/cxEssentials/wrapup/reasons/{wrapup_reason_id}"
|
|
151
|
+
try:
|
|
152
|
+
api.session.rest_delete(url)
|
|
153
|
+
except WebexError as e:
|
|
154
|
+
handle_rest_error(e)
|
|
155
|
+
typer.echo(f"Deleted: {wrapup_reason_id}")
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
@app.command("validate-wrap-up")
|
|
160
|
+
def validate_wrap_up(
|
|
161
|
+
name: str = typer.Option(None, "--name", help="Name of the wrap-up reason."),
|
|
162
|
+
json_body: str = typer.Option(None, "--json-body", help="Full JSON body"),
|
|
163
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
164
|
+
):
|
|
165
|
+
"""Validate Wrap Up Reason."""
|
|
166
|
+
api = get_api(debug=debug)
|
|
167
|
+
url = f"https://webexapis.com/v1/telephony/config/cxEssentials/wrapup/reasons/actions/validateName/invoke"
|
|
168
|
+
if json_body:
|
|
169
|
+
body = json.loads(json_body)
|
|
170
|
+
else:
|
|
171
|
+
body = {}
|
|
172
|
+
if name is not None:
|
|
173
|
+
body["name"] = name
|
|
174
|
+
try:
|
|
175
|
+
result = api.session.rest_post(url, json=body)
|
|
176
|
+
except WebexError as e:
|
|
177
|
+
handle_rest_error(e)
|
|
178
|
+
print_json(result)
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
@app.command("list-available-queues")
|
|
183
|
+
def list_available_queues(
|
|
184
|
+
wrapup_reason_id: str = typer.Argument(help="wrapupReasonId"),
|
|
185
|
+
output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
|
|
186
|
+
limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
|
|
187
|
+
offset: int = typer.Option(0, "--offset", help="Start offset"),
|
|
188
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
189
|
+
):
|
|
190
|
+
"""Read Available Queues."""
|
|
191
|
+
api = get_api(debug=debug)
|
|
192
|
+
url = f"https://webexapis.com/v1/telephony/config/cxEssentials/wrapup/reasons/{wrapup_reason_id}/availableQueues"
|
|
193
|
+
params = {}
|
|
194
|
+
if limit > 0:
|
|
195
|
+
params["max"] = limit
|
|
196
|
+
if offset > 0:
|
|
197
|
+
params["start"] = offset
|
|
198
|
+
result = None
|
|
199
|
+
try:
|
|
200
|
+
result = api.session.rest_get(url, params=params)
|
|
201
|
+
except WebexError as e:
|
|
202
|
+
handle_rest_error(e)
|
|
203
|
+
result = result or []
|
|
204
|
+
items = result.get("queues", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
|
|
205
|
+
if output == "json":
|
|
206
|
+
print_json(items)
|
|
207
|
+
else:
|
|
208
|
+
print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
@app.command("list-settings")
|
|
213
|
+
def list_settings(
|
|
214
|
+
location_id: str = typer.Argument(help="locationId"),
|
|
215
|
+
queue_id: str = typer.Argument(help="queueId"),
|
|
216
|
+
output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
|
|
217
|
+
limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
|
|
218
|
+
offset: int = typer.Option(0, "--offset", help="Start offset"),
|
|
219
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
220
|
+
):
|
|
221
|
+
"""Read Wrap Up Reason Settings."""
|
|
222
|
+
api = get_api(debug=debug)
|
|
223
|
+
url = f"https://webexapis.com/v1/telephony/config/cxEssentials/locations/{location_id}/queues/{queue_id}/wrapup/settings"
|
|
224
|
+
params = {}
|
|
225
|
+
if limit > 0:
|
|
226
|
+
params["max"] = limit
|
|
227
|
+
if offset > 0:
|
|
228
|
+
params["start"] = offset
|
|
229
|
+
result = None
|
|
230
|
+
try:
|
|
231
|
+
result = api.session.rest_get(url, params=params)
|
|
232
|
+
except WebexError as e:
|
|
233
|
+
handle_rest_error(e)
|
|
234
|
+
result = result or []
|
|
235
|
+
items = result.get("wrapupReasons", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
|
|
236
|
+
if output == "json":
|
|
237
|
+
print_json(items)
|
|
238
|
+
else:
|
|
239
|
+
print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
@app.command("update-settings")
|
|
244
|
+
def update_settings(
|
|
245
|
+
location_id: str = typer.Argument(help="locationId"),
|
|
246
|
+
queue_id: str = typer.Argument(help="queueId"),
|
|
247
|
+
default_wrapup_reason_id: str = typer.Option(None, "--default-wrapup-reason-id", help="Unique wrap-up identifier."),
|
|
248
|
+
wrapup_timer_enabled: bool = typer.Option(None, "--wrapup-timer-enabled/--no-wrapup-timer-enabled", help="Denotes whether the wrap-up timer is enabled."),
|
|
249
|
+
wrapup_timer: str = typer.Option(None, "--wrapup-timer", help="Wrap up timer value in seconds."),
|
|
250
|
+
json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
|
|
251
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
252
|
+
):
|
|
253
|
+
"""Update Wrap Up Reason Settings\n\nExample --json-body:\n '{"wrapupReasons":["..."],"defaultWrapupReasonId":"...","wrapupTimerEnabled":true,"wrapupTimer":0}'."""
|
|
254
|
+
api = get_api(debug=debug)
|
|
255
|
+
url = f"https://webexapis.com/v1/telephony/config/cxEssentials/locations/{location_id}/queues/{queue_id}/wrapup/settings"
|
|
256
|
+
if json_body:
|
|
257
|
+
body = json.loads(json_body)
|
|
258
|
+
else:
|
|
259
|
+
body = {}
|
|
260
|
+
if default_wrapup_reason_id is not None:
|
|
261
|
+
body["defaultWrapupReasonId"] = default_wrapup_reason_id
|
|
262
|
+
if wrapup_timer_enabled is not None:
|
|
263
|
+
body["wrapupTimerEnabled"] = wrapup_timer_enabled
|
|
264
|
+
if wrapup_timer is not None:
|
|
265
|
+
body["wrapupTimer"] = wrapup_timer
|
|
266
|
+
try:
|
|
267
|
+
result = api.session.rest_put(url, json=body)
|
|
268
|
+
except WebexError as e:
|
|
269
|
+
handle_rest_error(e)
|
|
270
|
+
typer.echo(f"Updated.")
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
@app.command("show-screen-pop")
|
|
275
|
+
def show_screen_pop(
|
|
276
|
+
location_id: str = typer.Argument(help="locationId"),
|
|
277
|
+
queue_id: str = typer.Argument(help="queueId"),
|
|
278
|
+
output: str = typer.Option("json", "--output", "-o", help="Output format: table|json"),
|
|
279
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
280
|
+
):
|
|
281
|
+
"""Read Screen Pop Configuration."""
|
|
282
|
+
api = get_api(debug=debug)
|
|
283
|
+
url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/queues/{queue_id}/cxEssentials/screenPop"
|
|
284
|
+
params = {}
|
|
285
|
+
org_id = get_org_id()
|
|
286
|
+
if org_id is not None:
|
|
287
|
+
params["orgId"] = org_id
|
|
288
|
+
try:
|
|
289
|
+
result = api.session.rest_get(url, params=params)
|
|
290
|
+
except WebexError as e:
|
|
291
|
+
handle_rest_error(e)
|
|
292
|
+
if output == "json":
|
|
293
|
+
print_json(result)
|
|
294
|
+
else:
|
|
295
|
+
if isinstance(result, dict):
|
|
296
|
+
print_table([result], columns=[("Key", ""), ("Value", "")], limit=0)
|
|
297
|
+
elif isinstance(result, list):
|
|
298
|
+
print_table(result, columns=[("ID", "id"), ("Name", "name")], limit=0)
|
|
299
|
+
else:
|
|
300
|
+
print_json(result)
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
@app.command("update-screen-pop")
|
|
305
|
+
def update_screen_pop(
|
|
306
|
+
location_id: str = typer.Argument(help="locationId"),
|
|
307
|
+
queue_id: str = typer.Argument(help="queueId"),
|
|
308
|
+
enabled: bool = typer.Option(None, "--enabled/--no-enabled", help="Enable/disable screen pop."),
|
|
309
|
+
screen_pop_url: str = typer.Option(None, "--screen-pop-url", help="The screen pop URL that integrates Webex calls with other bu"),
|
|
310
|
+
desktop_label: str = typer.Option(None, "--desktop-label", help="A label for the screen pop configuration."),
|
|
311
|
+
json_body: str = typer.Option(None, "--json-body", help="Full JSON body (overrides other options)"),
|
|
312
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
313
|
+
):
|
|
314
|
+
"""Update Screen Pop Configuration\n\nExample --json-body:\n '{"enabled":true,"screenPopUrl":"...","desktopLabel":"...","queryParams":{"example_param_1":"...","example_param_2":"...","example_param_3":"..."}}'."""
|
|
315
|
+
api = get_api(debug=debug)
|
|
316
|
+
url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/queues/{queue_id}/cxEssentials/screenPop"
|
|
317
|
+
params = {}
|
|
318
|
+
org_id = get_org_id()
|
|
319
|
+
if org_id is not None:
|
|
320
|
+
params["orgId"] = org_id
|
|
321
|
+
if json_body:
|
|
322
|
+
body = json.loads(json_body)
|
|
323
|
+
else:
|
|
324
|
+
body = {}
|
|
325
|
+
if enabled is not None:
|
|
326
|
+
body["enabled"] = enabled
|
|
327
|
+
if screen_pop_url is not None:
|
|
328
|
+
body["screenPopUrl"] = screen_pop_url
|
|
329
|
+
if desktop_label is not None:
|
|
330
|
+
body["desktopLabel"] = desktop_label
|
|
331
|
+
try:
|
|
332
|
+
result = api.session.rest_put(url, json=body, params=params)
|
|
333
|
+
except WebexError as e:
|
|
334
|
+
handle_rest_error(e)
|
|
335
|
+
typer.echo(f"Updated.")
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
@app.command("list-available-agents")
|
|
340
|
+
def list_available_agents(
|
|
341
|
+
location_id: str = typer.Argument(help="locationId"),
|
|
342
|
+
has_cx_essentials: str = typer.Option(None, "--has-cx-essentials", help="Returns only the list of available agents with Customer Assi"),
|
|
343
|
+
output: str = typer.Option("table", "--output", "-o", help="Output format: table|json"),
|
|
344
|
+
limit: int = typer.Option(0, "--limit", help="Max results (0=all for paginated endpoints, API default for non-paginated)"),
|
|
345
|
+
offset: int = typer.Option(0, "--offset", help="Start offset"),
|
|
346
|
+
debug: bool = typer.Option(False, "--debug"),
|
|
347
|
+
):
|
|
348
|
+
"""List Available Agents."""
|
|
349
|
+
api = get_api(debug=debug)
|
|
350
|
+
url = f"https://webexapis.com/v1/telephony/config/locations/{location_id}/cxEssentials/agents/availableAgents"
|
|
351
|
+
params = {}
|
|
352
|
+
if has_cx_essentials is not None:
|
|
353
|
+
params["hasCxEssentials"] = has_cx_essentials
|
|
354
|
+
if limit > 0:
|
|
355
|
+
params["max"] = limit
|
|
356
|
+
if offset > 0:
|
|
357
|
+
params["start"] = offset
|
|
358
|
+
org_id = get_org_id()
|
|
359
|
+
if org_id is not None:
|
|
360
|
+
params["orgId"] = org_id
|
|
361
|
+
result = None
|
|
362
|
+
try:
|
|
363
|
+
result = api.session.rest_get(url, params=params)
|
|
364
|
+
except WebexError as e:
|
|
365
|
+
handle_rest_error(e)
|
|
366
|
+
result = result or []
|
|
367
|
+
items = result.get("agents", result.get("data", result if isinstance(result, list) else [])) if isinstance(result, dict) else (result if isinstance(result, list) else [])
|
|
368
|
+
if output == "json":
|
|
369
|
+
print_json(items)
|
|
370
|
+
else:
|
|
371
|
+
print_table(items, columns=[("ID", "id"), ("Name", "name")], limit=limit)
|
|
372
|
+
|
|
373
|
+
|