@salesforce/afv-skills 1.46.0 → 1.48.0
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.
- package/package.json +1 -1
- package/skills/agentforce-observe/SKILL.md +32 -4
- package/skills/agentforce-observe/references/ahm-alerts.md +719 -0
- package/skills/consumer-goods-promotion-bo-api-deploy/SKILL.md +275 -0
- package/skills/consumer-goods-promotion-bo-api-deploy/assets/set-comment-value/README.md +32 -0
- package/skills/consumer-goods-promotion-bo-api-deploy/assets/set-comment-value/SetCommentValue.cls +75 -0
- package/skills/consumer-goods-promotion-bo-api-deploy/assets/set-comment-value/SetCommentValue.cls-meta.xml +5 -0
- package/skills/consumer-goods-promotion-bo-api-deploy/assets/set-comment-value/interview-answers.json +13 -0
- package/skills/consumer-goods-promotion-bo-api-deploy/assets/set-comment-value/payloads/copy.json +10 -0
- package/skills/consumer-goods-promotion-bo-api-deploy/assets/set-comment-value/payloads/create.json +20 -0
- package/skills/consumer-goods-promotion-bo-api-deploy/assets/set-comment-value/payloads/update.json +16 -0
- package/skills/consumer-goods-promotion-bo-api-deploy/references/conventions-and-payload-rules.md +273 -0
- package/skills/consumer-goods-promotion-bo-api-deploy/references/generate-and-wire.md +236 -0
- package/skills/consumer-goods-promotion-bo-api-deploy/references/reference-example-set-comment-value.md +132 -0
- package/skills/consumer-goods-promotion-bo-api-deploy/references/smoke-and-verify.md +211 -0
- package/skills/dx-devops-project-manage/SKILL.md +197 -0
- package/skills/dx-devops-project-manage/examples/common-workflows.md +197 -0
- package/skills/dx-devops-project-manage/references/cli-commands.md +295 -0
- package/skills/dx-devops-project-manage/scripts/create-project.sh +48 -0
- package/skills/dx-devops-project-manage/scripts/list-projects.sh +51 -0
- package/skills/dx-devops-project-manage/scripts/update-project.sh +96 -0
- package/skills/education-cloud-academic-calendar-generate/SKILL.md +225 -0
- package/skills/education-cloud-academic-calendar-generate/examples/quarter-calendar.json +47 -0
- package/skills/education-cloud-academic-calendar-generate/examples/sample-output.md +57 -0
- package/skills/education-cloud-academic-calendar-generate/examples/semester-calendar.json +54 -0
- package/skills/education-cloud-academic-calendar-generate/references/calendar-systems.md +127 -0
- package/skills/education-cloud-academic-calendar-generate/references/date-validation.md +222 -0
- package/skills/education-cloud-academic-calendar-generate/references/foundation_prerequisites.md +40 -0
- package/skills/education-cloud-academic-calendar-generate/scripts/validate_calendar_dates.py +143 -0
- package/skills/education-cloud-course-catalog-migrate/SKILL.md +321 -0
- package/skills/education-cloud-course-catalog-migrate/references/gotchas-detail.md +16 -0
- package/skills/education-cloud-course-catalog-migrate/references/gotchas.md +16 -0
- package/skills/education-cloud-course-catalog-migrate/references/large-catalog-handling.md +42 -0
- package/skills/education-cloud-course-catalog-migrate/scripts/batch_courses.py +36 -0
- package/skills/education-cloud-course-catalog-migrate/scripts/detect_linked_courses.py +51 -0
- package/skills/education-cloud-course-catalog-migrate/scripts/detect_modality_variants.py +48 -0
- package/skills/education-cloud-course-catalog-migrate/scripts/resolve_api_version.py +43 -0
- package/skills/education-cloud-course-catalog-migrate/scripts/split_course_code.py +39 -0
- package/skills/education-cloud-course-catalog-migrate/scripts/validate_completeness.py +54 -0
- package/skills/education-cloud-domain-configure/SKILL.md +234 -0
- package/skills/education-cloud-domain-configure/references/dependent-features.md +198 -0
- package/skills/education-cloud-domain-configure/references/domain-features/academic-operations.md +386 -0
- package/skills/education-cloud-domain-configure/references/domain-features/alumni-relations.md +155 -0
- package/skills/education-cloud-domain-configure/references/domain-features/cross-domain-dependencies.md +18 -0
- package/skills/education-cloud-domain-configure/references/domain-features/industriessettings-toggle-reference.md +43 -0
- package/skills/education-cloud-domain-configure/references/domain-features/mentoring.md +198 -0
- package/skills/education-cloud-domain-configure/references/domain-features/recruitment-admissions.md +315 -0
- package/skills/education-cloud-domain-configure/references/domain-features/student-management.md +155 -0
- package/skills/education-cloud-domain-configure/references/domain-features/student-success.md +249 -0
- package/skills/education-cloud-domain-configure/references/domain-features.md +27 -0
- package/skills/education-cloud-domain-configure/references/gotchas-extended.md +23 -0
- package/skills/education-cloud-domain-configure/references/toggle-write-path.md +67 -0
- package/skills/education-cloud-domain-configure/scripts/check_license_provisioned.py +62 -0
- package/skills/education-cloud-domain-configure/scripts/check_org_edition.py +44 -0
- package/skills/education-cloud-domain-configure/scripts/check_permission_assigned.py +39 -0
- package/skills/education-cloud-multi-campus-configure/SKILL.md +7 -0
- package/skills/education-cloud-multi-campus-configure/references/foundation_prerequisites.md +1 -2
- package/skills/education-cloud-student-recruitment-agent-configure/SKILL.md +177 -0
- package/skills/education-cloud-student-recruitment-agent-configure/references/agent-and-subagents.md +151 -0
- package/skills/education-cloud-student-recruitment-agent-configure/references/customer-narration.md +34 -0
- package/skills/education-cloud-student-recruitment-agent-configure/references/execution-model.md +54 -0
- package/skills/education-cloud-student-recruitment-agent-configure/references/flows.md +82 -0
- package/skills/education-cloud-student-recruitment-agent-configure/references/grounding.md +199 -0
- package/skills/education-cloud-student-recruitment-agent-configure/references/permissions.md +183 -0
- package/skills/education-cloud-student-recruitment-agent-configure/references/platform-enablement.md +82 -0
- package/skills/education-cloud-student-recruitment-agent-configure/references/prerequisites.md +158 -0
- package/skills/education-cloud-student-recruitment-agent-configure/references/routing.md +141 -0
- package/skills/experience-cms-brand-apply/SKILL.md +5 -5
- package/skills/experience-cms-brand-create/SKILL.md +2 -2
- package/skills/experience-cms-content-generate/SKILL.md +1 -0
- package/skills/experience-cms-content-render/SKILL.md +173 -0
- package/skills/experience-cms-content-render/assets/angular/DetailPage.component.ts +25 -0
- package/skills/experience-cms-content-render/assets/angular/MediaRenderer.component.ts +133 -0
- package/skills/experience-cms-content-render/assets/angular/TypeList.component.ts +38 -0
- package/skills/experience-cms-content-render/assets/angular/TypeRenderer.component.ts +90 -0
- package/skills/experience-cms-content-render/assets/angular/cms-content.component.ts +248 -0
- package/skills/experience-cms-content-render/assets/angular/cms-item.service.ts +100 -0
- package/skills/experience-cms-content-render/assets/react/DetailPage.tsx +20 -0
- package/skills/experience-cms-content-render/assets/react/MediaRenderer.tsx +129 -0
- package/skills/experience-cms-content-render/assets/react/TypeList.tsx +40 -0
- package/skills/experience-cms-content-render/assets/react/TypeRenderer.tsx +64 -0
- package/skills/experience-cms-content-render/assets/react/heuristicRenderer.tsx +310 -0
- package/skills/experience-cms-content-render/assets/react/useCmsItem.ts +129 -0
- package/skills/experience-cms-content-render/assets/shared/cmsContentType.ts +49 -0
- package/skills/experience-cms-content-render/assets/shared/cmsCore.types.ts +96 -0
- package/skills/experience-cms-content-render/assets/shared/externalRefs.ts +55 -0
- package/skills/experience-cms-content-render/references/bulk-loading.md +60 -0
- package/skills/experience-cms-content-render/references/codegen-guardrails.md +111 -0
- package/skills/experience-cms-content-render/references/detail-pages.md +87 -0
- package/skills/experience-cms-content-render/references/embed-recipes.md +127 -0
- package/skills/experience-cms-content-render/references/failure-modes.md +96 -0
- package/skills/experience-cms-content-render/references/heuristic-render-rules.md +131 -0
- package/skills/experience-cms-content-render/references/init-scaffold.md +122 -0
- package/skills/experience-cms-content-render/references/interaction-model.md +173 -0
- package/skills/experience-cms-content-render/references/package-api.md +106 -0
- package/skills/experience-cms-content-render/references/schema-sync.md +114 -0
- package/skills/experience-cms-content-render/references/styling-scopes.md +65 -0
- package/skills/experience-cms-content-render/references/verify.md +49 -0
- package/skills/experience-cms-content-type-generate/SKILL.md +2 -2
- package/skills/experience-content-media-stock-image-search/SKILL.md +5 -4
- package/skills/experience-search-coordinate/SKILL.md +198 -0
- package/skills/experience-search-coordinate/assets/search-payload-template.json +25 -0
- package/skills/experience-search-coordinate/references/content-route.md +313 -0
- package/skills/experience-search-coordinate/references/content-type-discovery.md +57 -0
- package/skills/experience-search-coordinate/references/media-route.md +172 -0
- package/skills/experience-search-coordinate/references/scope-resolution.md +14 -0
- package/skills/experience-ui-bundle-localize/SKILL.md +1 -1
- package/skills/experience-ui-bundle-localize/references/i18n-setup.md +5 -3
- package/skills/experience-ui-bundle-project-generate/SKILL.md +18 -14
- package/skills/experience-ui-bundle-project-generate/references/angular-project-generate.md +22 -0
- package/skills/experience-ui-bundle-project-generate/references/react-project-generate.md +20 -0
- package/skills/experience-ui-bundle-salesforce-data-access/SKILL.md +58 -54
- package/skills/experience-ui-bundle-salesforce-data-access/references/caching.md +6 -0
- package/skills/experience-ui-bundle-salesforce-data-access/references/graphiti-cli.md +2 -2
- package/skills/experience-ui-bundle-salesforce-data-access/references/migration.md +6 -0
- package/skills/experience-ui-bundle-salesforce-data-access/references/rest-and-integration.md +2 -1
- package/skills/experience-ui-bundle-salesforce-data-access/references/sdk-api.md +6 -0
- package/skills/experience-ui-bundle-site-generate/SKILL.md +59 -8
- package/skills/experience-ui-bundle-site-generate/references/configure-metadata-digital-experience.md +8 -3
- package/skills/experience-ui-bundle-site-generate/references/configure-metadata-language-settings.md +120 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/SKILL.md +336 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/references/orchestration-flow.md +143 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-2-starter-config-application-flexipage-mapping.md +127 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-2-starter-config-deploy-commands.md +116 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-2-starter-config-lifesci-metadata-deploy.md +111 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-2-starter-config-overview.md +312 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-2-starter-config-profile-layout-assignments.md +171 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-2-starter-config-state-tracking.md +64 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-2-starter-config-trigger-handlers.md +122 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-4-user-provisioning-overview.md +335 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-4-user-provisioning-user-provisioning-details.md +140 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-5-visit-creation-execution-state-and-recovery.md +196 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-5-visit-creation-metadata-cache-generation.md +155 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-5-visit-creation-overview.md +307 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-5-visit-creation-visit-creation-data.md +211 -0
- package/skills/life-sciences-fieldsalesrep-coordinate/references/state-machine-and-changes.md +108 -0
- package/skills/life-sciences-kam-coordinate/SKILL.md +241 -0
- package/skills/life-sciences-kam-coordinate/references/orchestration-flow.md +152 -0
- package/skills/life-sciences-kam-coordinate/references/stage-2-starter-config-application-flexipage-mapping.md +79 -0
- package/skills/life-sciences-kam-coordinate/references/stage-2-starter-config-deploy-commands.md +131 -0
- package/skills/life-sciences-kam-coordinate/references/stage-2-starter-config-kam-config-records.md +85 -0
- package/skills/life-sciences-kam-coordinate/references/stage-2-starter-config-lifesci-metadata-deploy.md +112 -0
- package/skills/life-sciences-kam-coordinate/references/stage-2-starter-config-overview.md +202 -0
- package/skills/life-sciences-kam-coordinate/references/stage-2-starter-config-profile-layout-assignments.md +67 -0
- package/skills/life-sciences-kam-coordinate/references/stage-2-starter-config-state-tracking.md +65 -0
- package/skills/life-sciences-kam-coordinate/references/stage-2-starter-config-trigger-handlers.md +123 -0
- package/skills/life-sciences-kam-coordinate/references/stage-4-participant-role-and-sprint.md +89 -0
- package/skills/life-sciences-kam-coordinate/references/stage-5-data-and-plan-templates-overview.md +337 -0
- package/skills/life-sciences-kam-coordinate/references/stage-5-data-creation-data.md +248 -0
- package/skills/life-sciences-kam-coordinate/references/stage-6-ipad-validation-script.md +35 -0
- package/skills/life-sciences-kam-coordinate/references/stage-6-metadata-cache-generation.md +155 -0
- package/skills/life-sciences-kam-coordinate/references/stage-6-user-provisioning-details.md +146 -0
- package/skills/life-sciences-kam-coordinate/references/stage-6-user-provisioning-overview.md +89 -0
- package/skills/life-sciences-kam-coordinate/references/state-machine-and-changes.md +114 -0
- package/skills/life-sciences-prerequisites-validate/SKILL.md +138 -0
- package/skills/life-sciences-prerequisites-validate/references/checks-org-settings.md +190 -0
- package/skills/life-sciences-prerequisites-validate/references/checks-user-and-package.md +211 -0
- package/skills/life-sciences-territory-configure/SKILL.md +217 -0
- package/skills/life-sciences-territory-configure/references/territory-metadata.md +262 -0
- package/skills/platform-dsar-policy-manage/SKILL.md +272 -0
- package/skills/platform-dsar-policy-manage/references/configure.md +106 -0
- package/skills/platform-dsar-policy-manage/references/export-and-history.md +123 -0
- package/skills/platform-dsar-policy-manage/references/gap-analysis-guide.md +150 -0
- package/skills/platform-dsar-policy-manage/references/gap-scan.md +129 -0
- package/skills/platform-dsar-policy-manage/references/headless-sor.md +59 -0
- package/skills/platform-dsar-policy-manage/references/report-format.md +59 -0
- package/skills/platform-dsar-policy-manage/scripts/tests/__init__.py +0 -0
- package/skills/platform-dsar-policy-manage/scripts/tests/test_validate_policy_tree.py +76 -0
- package/skills/platform-dsar-policy-manage/scripts/validate-policy-tree.py +130 -0
- package/skills/platform-salesforce-connect-adapter-generate/SKILL.md +359 -0
- package/skills/platform-salesforce-connect-adapter-generate/references/official-examples.md +69 -0
- package/skills/platform-salesforce-connect-adapter-generate/references/scenarios.md +187 -0
- package/skills/service-agentforce-channel-configure/SKILL.md +33 -28
- package/skills/service-agentforce-human-escalation-configure/SKILL.md +205 -0
- package/skills/service-agentforce-human-escalation-configure/assets/escalation-thresholds.instructions.md +37 -0
- package/skills/service-agentforce-human-escalation-configure/assets/force-app/main/default/flows/Human_Escalation_Outbound_Flow.flow-meta.xml +173 -0
- package/skills/service-agentforce-human-escalation-configure/assets/package.xml +8 -0
- package/skills/service-agentforce-human-escalation-configure/assets/sfdx-project.json +5 -0
- package/skills/service-agentforce-human-escalation-configure/references/classifier-contracts.md +103 -0
- package/skills/service-agentforce-human-escalation-configure/references/escalation-mechanism.md +63 -0
- package/skills/service-agentforce-human-escalation-configure/references/failure-policy.md +44 -0
- package/skills/service-agentforce-human-escalation-configure/references/nga-escalation.md +57 -0
- package/skills/service-agentforce-human-escalation-configure/references/runtime-verification.md +72 -0
- package/skills/service-agentforce-human-escalation-configure/scripts/classify-agent-active.mjs +49 -0
- package/skills/service-agentforce-human-escalation-configure/scripts/classify-nga-escalation.mjs +166 -0
- package/skills/service-agentforce-human-escalation-configure/scripts/extract-outbound-route.mjs +67 -0
- package/skills/service-agentforce-human-escalation-configure/scripts/patch-escalation-surfaces.mjs +131 -0
- package/skills/service-agentforce-human-escalation-configure/scripts/tests/__init__.py +0 -0
- package/skills/service-agentforce-human-escalation-configure/scripts/tests/_bootstrap.py +249 -0
- package/skills/service-agentforce-human-escalation-configure/scripts/tests/test_escalation_contracts.py +899 -0
- package/skills/service-agentforce-human-escalation-configure/scripts/verify-and-configure.sh +705 -0
- package/skills/service-agentforce-human-escalation-configure/scripts/verify-escalation-config.mjs +89 -0
- package/skills/service-de-channel-routing-configure/SKILL.md +13 -5
- package/skills/service-de-channel-routing-configure/scripts/tests/test_skill_contract.py +25 -0
- package/skills/service-itsm-agentic-setup-agentforce-coordinate/SKILL.md +23 -10
- package/skills/service-itsm-agentic-setup-agentforce-coordinate/examples/output-templates.md +63 -48
- package/skills/service-itsm-agentic-setup-agentforce-coordinate/scripts/verify-child-verdict.mjs +6 -2
- package/skills/service-itsm-agentic-setup-cmdb-coordinate/SKILL.md +20 -27
- package/skills/service-native-voice-recording-transcription-configure/SKILL.md +47 -27
- package/skills/service-native-voice-recording-transcription-configure/references/thunderbird-voice-settings.md +13 -9
- package/skills/service-native-voice-recording-transcription-configure/scripts/enable-recording-transcription.sh +104 -45
- package/skills/service-omni-agent-users-create/SKILL.md +96 -0
- package/skills/service-omni-agent-users-create/assets/create-users.apex.template +80 -0
- package/skills/service-omni-agent-users-create/references/apex-patterns.md +135 -0
- package/skills/service-omni-agent-users-create/references/apex-template-notes.md +40 -0
- package/skills/service-omni-agent-users-create/scripts/detect-and-create.sh +206 -0
- package/skills/service-omni-agent-users-create/scripts/detect-existing.sh +122 -0
- package/skills/service-omni-agent-users-create/scripts/run-create.sh +298 -0
- package/skills/service-omni-agent-users-create/scripts/tests/test_user_create_security.py +171 -0
- package/skills/service-omni-agent-work-sharing-configure/SKILL.md +78 -0
- package/skills/service-omni-agent-work-sharing-configure/references/api-notes.md +31 -0
- package/skills/service-omni-agent-work-sharing-configure/scripts/configure-and-report.sh +186 -0
- package/skills/service-omni-agent-work-sharing-configure/scripts/tests/_bootstrap.py +120 -0
- package/skills/service-omni-agent-work-sharing-configure/scripts/tests/test_agent_work_sharing_contracts.py +93 -0
- package/skills/service-omni-base-settings-configure/SKILL.md +77 -0
- package/skills/service-omni-base-settings-configure/assets/force-app/main/default/settings/OmniChannel.settings-meta.xml +8 -0
- package/skills/service-omni-base-settings-configure/assets/package.xml +8 -0
- package/skills/service-omni-base-settings-configure/assets/sfdx-project.json +12 -0
- package/skills/service-omni-base-settings-configure/references/api-notes.md +121 -0
- package/skills/service-omni-base-settings-configure/scripts/configure-and-report.sh +164 -0
- package/skills/service-omni-base-settings-configure/scripts/detect-existing.sh +69 -0
- package/skills/service-omni-channel-setup-coordinate/SKILL.md +168 -0
- package/skills/service-omni-channel-setup-coordinate/examples/output-templates.md +161 -0
- package/skills/service-omni-channel-setup-coordinate/references/gap-catalog.md +99 -0
- package/skills/service-omni-channel-setup-coordinate/scripts/integration-driver.sh +1542 -0
- package/skills/service-omni-channel-setup-coordinate/scripts/tests/__init__.py +0 -0
- package/skills/service-omni-channel-setup-coordinate/scripts/tests/_bootstrap.py +223 -0
- package/skills/service-omni-channel-setup-coordinate/scripts/tests/test_omni_contracts.py +804 -0
- package/skills/service-omni-command-center-analyze/SKILL.md +84 -0
- package/skills/service-omni-command-center-analyze/references/api-notes.md +54 -0
- package/skills/service-omni-command-center-analyze/scripts/analyze.sh +198 -0
- package/skills/service-omni-command-center-analyze/scripts/tests/_bootstrap.py +115 -0
- package/skills/service-omni-command-center-analyze/scripts/tests/test_command_center_analyze_contracts.py +165 -0
- package/skills/service-omni-permission-set-assign/SKILL.md +88 -0
- package/skills/service-omni-permission-set-assign/assets/package.xml +8 -0
- package/skills/service-omni-permission-set-assign/references/api-notes.md +143 -0
- package/skills/service-omni-permission-set-assign/scripts/verify-and-assign.sh +397 -0
- package/skills/service-omni-presence-status-deploy/SKILL.md +87 -0
- package/skills/service-omni-presence-status-deploy/assets/package.xml +12 -0
- package/skills/service-omni-presence-status-deploy/references/api-notes.md +115 -0
- package/skills/service-omni-presence-status-deploy/scripts/deploy-and-report.sh +422 -0
- package/skills/service-omni-presence-user-config-deploy/SKILL.md +80 -0
- package/skills/service-omni-presence-user-config-deploy/references/api-notes.md +60 -0
- package/skills/service-omni-presence-user-config-deploy/scripts/deploy-and-report.sh +261 -0
- package/skills/service-omni-presence-user-config-deploy/scripts/tests/_bootstrap.py +106 -0
- package/skills/service-omni-presence-user-config-deploy/scripts/tests/test_presence_user_config_contracts.py +142 -0
- package/skills/service-omni-queue-deploy/SKILL.md +91 -0
- package/skills/service-omni-queue-deploy/references/api-notes.md +140 -0
- package/skills/service-omni-queue-deploy/scripts/verify-and-align.sh +470 -0
- package/skills/service-omni-queue-members-assign/SKILL.md +93 -0
- package/skills/service-omni-queue-members-assign/references/api-notes.md +144 -0
- package/skills/service-omni-queue-members-assign/scripts/tests/_bootstrap.py +67 -0
- package/skills/service-omni-queue-members-assign/scripts/tests/test_queue_members_contracts.py +73 -0
- package/skills/service-omni-queue-members-assign/scripts/verify-and-bind.sh +362 -0
- package/skills/service-omni-queue-routing-config-deploy/SKILL.md +101 -0
- package/skills/service-omni-queue-routing-config-deploy/references/api-notes.md +127 -0
- package/skills/service-omni-queue-routing-config-deploy/scripts/tests/_bootstrap.py +90 -0
- package/skills/service-omni-queue-routing-config-deploy/scripts/tests/test_queue_routing_config_contracts.py +70 -0
- package/skills/service-omni-queue-routing-config-deploy/scripts/upsert-and-report.sh +538 -0
- package/skills/service-omni-routing-flow-deploy/SKILL.md +111 -0
- package/skills/service-omni-routing-flow-deploy/assets/force-app/main/default/flows/Omni_Route_Case_Trigger.SkillsBased.flow-meta.xml +94 -0
- package/skills/service-omni-routing-flow-deploy/assets/force-app/main/default/flows/Omni_Route_Case_Trigger.flow-meta.xml +88 -0
- package/skills/service-omni-routing-flow-deploy/assets/force-app/main/default/flows/Omni_Route_Cases.flow-meta.xml +144 -0
- package/skills/service-omni-routing-flow-deploy/assets/force-app/main/default/flows/Omni_Route_VoiceCall_Trigger.SkillsBased.flow-meta.xml +94 -0
- package/skills/service-omni-routing-flow-deploy/assets/force-app/main/default/flows/Omni_Route_VoiceCall_Trigger.flow-meta.xml +88 -0
- package/skills/service-omni-routing-flow-deploy/assets/force-app/main/default/flows/Omni_Route_VoiceCalls.flow-meta.xml +144 -0
- package/skills/service-omni-routing-flow-deploy/assets/package.xml +11 -0
- package/skills/service-omni-routing-flow-deploy/assets/sfdx-project.json +11 -0
- package/skills/service-omni-routing-flow-deploy/references/api-notes.md +265 -0
- package/skills/service-omni-routing-flow-deploy/scripts/deploy-and-report.sh +684 -0
- package/skills/service-omni-service-channel-configure/SKILL.md +83 -0
- package/skills/service-omni-service-channel-configure/assets/force-app/main/default/serviceChannels/Cases.serviceChannel-meta.xml +9 -0
- package/skills/service-omni-service-channel-configure/assets/force-app/main/default/serviceChannels/Incidents.serviceChannel-meta.xml +9 -0
- package/skills/service-omni-service-channel-configure/assets/force-app/main/default/serviceChannels/MessagingSessions.serviceChannel-meta.xml +9 -0
- package/skills/service-omni-service-channel-configure/assets/force-app/main/default/serviceChannels/VoiceCalls.serviceChannel-meta.xml +9 -0
- package/skills/service-omni-service-channel-configure/assets/package.xml +11 -0
- package/skills/service-omni-service-channel-configure/assets/sfdx-project.json +12 -0
- package/skills/service-omni-service-channel-configure/references/api-notes.md +84 -0
- package/skills/service-omni-service-channel-configure/scripts/deploy-and-report.sh +383 -0
- package/skills/service-omni-sidebar-configure/SKILL.md +77 -0
- package/skills/service-omni-sidebar-configure/references/api-notes.md +51 -0
- package/skills/service-omni-sidebar-configure/scripts/enable-and-report.sh +315 -0
- package/skills/service-omni-sidebar-configure/scripts/tests/_bootstrap.py +142 -0
- package/skills/service-omni-sidebar-configure/scripts/tests/test_sidebar_contracts.py +139 -0
- package/skills/service-omni-skills-based-routing-configure/SKILL.md +81 -0
- package/skills/service-omni-skills-based-routing-configure/references/api-notes.md +50 -0
- package/skills/service-omni-skills-based-routing-configure/scripts/configure-and-report.sh +270 -0
- package/skills/service-omni-skills-based-routing-configure/scripts/tests/_bootstrap.py +120 -0
- package/skills/service-omni-skills-based-routing-configure/scripts/tests/test_sbr_contracts.py +129 -0
- package/skills/service-omni-supervisor-config-deploy/SKILL.md +97 -0
- package/skills/service-omni-supervisor-config-deploy/assets/force-app/main/default/omniSupervisorConfigs/Omni_Supervisor.omniSupervisorConfig-meta.xml +9 -0
- package/skills/service-omni-supervisor-config-deploy/assets/package.xml +8 -0
- package/skills/service-omni-supervisor-config-deploy/assets/sfdx-project.json +12 -0
- package/skills/service-omni-supervisor-config-deploy/references/api-notes.md +205 -0
- package/skills/service-omni-supervisor-config-deploy/scripts/deploy-and-report.sh +586 -0
- package/skills/service-omni-supervisor-config-deploy/scripts/tests/_bootstrap.py +153 -0
- package/skills/service-omni-supervisor-config-deploy/scripts/tests/test_supervisor_config_contracts.py +116 -0
- package/skills/service-omni-supervisor-permset-assign/SKILL.md +88 -0
- package/skills/service-omni-supervisor-permset-assign/references/api-notes.md +48 -0
- package/skills/service-omni-supervisor-permset-assign/scripts/verify-and-assign.sh +316 -0
- package/skills/service-omni-supervisor-surface-deploy/SKILL.md +85 -0
- package/skills/service-omni-supervisor-surface-deploy/references/api-notes.md +47 -0
- package/skills/service-omni-supervisor-surface-deploy/scripts/deploy-and-report.sh +216 -0
- package/skills/service-omni-supervisor-surface-deploy/scripts/tests/_bootstrap.py +95 -0
- package/skills/service-omni-supervisor-surface-deploy/scripts/tests/test_supervisor_surface_contracts.py +154 -0
- package/skills/service-omni-supervisor-users-create/SKILL.md +92 -0
- package/skills/service-omni-supervisor-users-create/assets/create-supervisors.apex.template +72 -0
- package/skills/service-omni-supervisor-users-create/references/apex-patterns.md +73 -0
- package/skills/service-omni-supervisor-users-create/references/apex-template-notes.md +41 -0
- package/skills/service-omni-supervisor-users-create/scripts/detect-and-create.sh +197 -0
- package/skills/service-omni-supervisor-users-create/scripts/detect-existing.sh +112 -0
- package/skills/service-omni-supervisor-users-create/scripts/run-create.sh +296 -0
- package/skills/service-omni-work-skill-routing-configure/SKILL.md +80 -0
- package/skills/service-omni-work-skill-routing-configure/references/api-notes.md +67 -0
- package/skills/service-omni-work-skill-routing-configure/scripts/configure-and-report.sh +262 -0
- package/skills/service-omni-work-skill-routing-configure/scripts/tests/_bootstrap.py +121 -0
- package/skills/service-omni-work-skill-routing-configure/scripts/tests/test_work_skill_routing_contracts.py +161 -0
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: service-native-voice-recording-transcription-configure
|
|
3
|
-
description: "
|
|
3
|
+
description: "Configure (enable OR disable) call recording and call transcription for Native Voice (Thunderbird Voice) programmatically via the Metadata API, for headless / API-driven support where no human uses the Service Console UI. Either preference can be turned on or off independently. Use this skill when a user asks to turn on/off, enable/disable, or configure Native Voice recording and/or transcription for headless or automated voice calls. TRIGGER when: users mention Thunderbird Voice, Native Voice, ThunderbirdVoiceSettings, enableCallRecording, enableCallTranscription, 'enable recording and transcription', 'turn off call recording', 'disable transcription', 'turn on call recording via API', headless voice recording, or configuring voice-call capture without clicking through Setup. DO NOT TRIGGER when: the user wants to redact or scrub PII from existing recordings, transcribe a supplied audio file, or author a Flow/Apex — those are out of scope for this skill."
|
|
4
4
|
metadata:
|
|
5
|
-
version: "1.
|
|
5
|
+
version: "1.2"
|
|
6
6
|
domains: ["Service"]
|
|
7
7
|
minApiVersion: "68.0"
|
|
8
|
+
accessCheck:
|
|
9
|
+
- type: "orgPerm"
|
|
10
|
+
value: "ThunderbirdVoice.orgHasNativeVoiceAllowed"
|
|
8
11
|
cliTools:
|
|
9
12
|
- tool: ["jq"]
|
|
10
13
|
semver: ">=1.6"
|
|
@@ -12,20 +15,23 @@ metadata:
|
|
|
12
15
|
semver: ">=2.0.0"
|
|
13
16
|
---
|
|
14
17
|
|
|
15
|
-
#
|
|
18
|
+
# Configure Native Voice Recording & Transcription (headless)
|
|
16
19
|
|
|
17
|
-
Turn
|
|
18
|
-
Voice) on an org, programmatically, so
|
|
19
|
-
support sessions that never touch the Service Console UI.
|
|
20
|
-
are **two separate preferences** — this skill
|
|
21
|
-
|
|
20
|
+
Turn call **recording** and call **transcription** for Native Voice (Thunderbird
|
|
21
|
+
Voice) **on or off** on an org, programmatically, so the change takes effect for
|
|
22
|
+
headless / API-driven support sessions that never touch the Service Console UI.
|
|
23
|
+
Recording and transcription are **two separate preferences** — this skill can set
|
|
24
|
+
either or both to the requested state (leaving the other untouched) and returns a
|
|
25
|
+
structured JSON confirmation.
|
|
22
26
|
|
|
23
27
|
## Scope
|
|
24
28
|
|
|
25
|
-
- **In scope**:
|
|
29
|
+
- **In scope**: setting `enableCallRecording` and/or `enableCallTranscription` to a
|
|
30
|
+
requested state — **`true` (enable) or `false` (disable)** — on the
|
|
26
31
|
`ThunderbirdVoiceSettings` metadata via an sf CLI Metadata API deploy (the generic
|
|
27
32
|
`Settings` container, member `ThunderbirdVoice`); returning a JSON confirmation of
|
|
28
|
-
the resulting configuration.
|
|
33
|
+
the resulting configuration. A preference the user does not name is left at its
|
|
34
|
+
current value.
|
|
29
35
|
- **Out of scope**: changing any other `ThunderbirdVoiceSettings` boolean (e.g.
|
|
30
36
|
call-recording redaction, SIP recording) — siblings are read only to be preserved,
|
|
31
37
|
never set or reported; redacting/scrubbing PII from existing recordings or
|
|
@@ -43,8 +49,11 @@ Gather or infer before proceeding:
|
|
|
43
49
|
uses this existing `sf` login; no session id or access token is requested. Default:
|
|
44
50
|
the org's default target. Confirm connectivity with `sf org display -o <alias>`.
|
|
45
51
|
- **Which preferences** — recording, transcription, or both. Default: **both**, since
|
|
46
|
-
headless capture typically needs recording *and* transcription.
|
|
47
|
-
|
|
52
|
+
headless capture typically needs recording *and* transcription. If the user named
|
|
53
|
+
just one, target only that one and leave the other at its current value.
|
|
54
|
+
- **Desired state** — enable (`on`/`true`) or disable (`off`/`false`) for each named
|
|
55
|
+
preference. Default: **enable**, unless the user asks to turn something off. Honor a
|
|
56
|
+
disable request literally — never re-enable a preference the user asked to turn off.
|
|
48
57
|
|
|
49
58
|
---
|
|
50
59
|
|
|
@@ -58,13 +67,18 @@ missing org context.
|
|
|
58
67
|
booleans on the same metadata type — never collapse them into one combined flag.
|
|
59
68
|
The final confirmation must report each independently.
|
|
60
69
|
|
|
61
|
-
2. **Run the
|
|
62
|
-
`scripts/enable-recording-transcription.sh --target-org <alias
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
70
|
+
2. **Run the configure script.** Run
|
|
71
|
+
`scripts/enable-recording-transcription.sh --target-org <alias>` with the desired
|
|
72
|
+
state per preference: `--recording on|off` and/or `--transcription on|off`. With no
|
|
73
|
+
preference flags it defaults to turning **both on**. Examples: turn both off →
|
|
74
|
+
`--recording off --transcription off`; enable recording only →
|
|
75
|
+
`--recording on` (transcription left as-is); disable transcription only →
|
|
76
|
+
`--transcription off`. The script retrieves `ThunderbirdVoiceSettings` (via the
|
|
77
|
+
`Settings` container), checks the org-perm gate, sets **only the named** flag(s) to
|
|
78
|
+
the requested value while preserving every other field, validates with a dry-run
|
|
79
|
+
deploy, deploys, and verifies by round-trip. Use `--dry-run` to retrieve + validate
|
|
80
|
+
without persisting. Run `scripts/enable-recording-transcription.sh --help` for all
|
|
81
|
+
options.
|
|
68
82
|
|
|
69
83
|
3. **For the Metadata API contract**, read `references/thunderbird-voice-settings.md` —
|
|
70
84
|
it documents the type, the two booleans, the `Settings`-container manifest, the
|
|
@@ -75,15 +89,20 @@ missing org context.
|
|
|
75
89
|
> mdApiType **without** the `Settings` suffix (`ThunderbirdVoice`), and the manifest
|
|
76
90
|
> version MUST be **>= 68.0** — the fields are not exposed at any lower version.
|
|
77
91
|
|
|
78
|
-
4. **Change only the
|
|
79
|
-
|
|
80
|
-
`
|
|
81
|
-
|
|
92
|
+
4. **Change only the named target field(s), to the requested state.** The deployed
|
|
93
|
+
settings file sets exactly the preference(s) the user named — `enableCallRecording`
|
|
94
|
+
and/or `enableCallTranscription` — to the requested value (`true` to enable, `false`
|
|
95
|
+
to disable); every other `ThunderbirdVoiceSettings` boolean, INCLUDING a target
|
|
96
|
+
preference the user did not name, keeps its retrieved value. Do not add or flip
|
|
97
|
+
other booleans, and never coerce a disable request back to `true` — they are out of
|
|
98
|
+
this skill's scope or contrary to the request.
|
|
82
99
|
|
|
83
100
|
5. **Return the confirmation JSON.** Emit a single JSON object matching
|
|
84
101
|
`assets/confirmation-output.json`: the metadata `type` and `fullName`, that it was
|
|
85
102
|
applied via a headless/API deploy, and — for **each** of the two preferences
|
|
86
|
-
separately — its field name and enabled state
|
|
103
|
+
separately — its field name and its resulting enabled state (`true`/`false`, as it
|
|
104
|
+
now stands after the deploy, whether it was changed or left as-is), plus an overall
|
|
105
|
+
`status` (`ENABLED`, `DISABLED`, or `CONFIGURED`). It also
|
|
87
106
|
includes a **`preserved`** array echoing every sibling boolean (e.g.
|
|
88
107
|
`enableCallRecRedaction`, `enableSipRecording`) with the value it was retrieved
|
|
89
108
|
with — this attests the siblings were carried through **unchanged**, which
|
|
@@ -102,8 +121,9 @@ missing org context.
|
|
|
102
121
|
| Deploy through the generic `Settings` container with member `ThunderbirdVoice` | `ThunderbirdVoiceSettings` is not a retrievable top-level type name; the `Settings` container (member = mdApiType minus the `Settings` suffix) is the sf CLI path and uses the org's `sf` login — no session id. |
|
|
103
122
|
| Manifest version must be >= 68.0 | `ThunderbirdVoiceSettings` fields are only exposed at API v68.0+; below that the retrieve returns the type empty/absent. |
|
|
104
123
|
| Retrieve before deploy; preserve all sibling fields | The deploy is a full-file replace — starting from the retrieved file keeps other prefs intact and lets the gate be checked. |
|
|
105
|
-
| Report the preserved siblings in a `preserved` block, but never change them | Compliance callers need explicit "nothing else moved" assurance; the confirmation echoes each sibling's retrieved value unchanged. Echoing a value is not setting it — the deployed file
|
|
106
|
-
|
|
|
124
|
+
| Report the preserved siblings in a `preserved` block, but never change them | Compliance callers need explicit "nothing else moved" assurance; the confirmation echoes each sibling's retrieved value unchanged. Echoing a value is not setting it — the deployed file changes only the named target field(s). |
|
|
125
|
+
| Honor the requested state literally — enable means `true`, disable means `false` | A disable request that silently re-enables the preference (or reports success while leaving it on) is a correctness failure; set exactly what was asked and report the value the org actually returns. |
|
|
126
|
+
| Verify by round-trip before reporting the status | A "Succeeded" deploy is not proof; only report the resulting state after a re-retrieve returns each named preference at its requested value. |
|
|
107
127
|
| Output must be a single valid JSON object | The confirmation is consumed programmatically by headless callers; prose-only answers are not usable. |
|
|
108
128
|
| Do not include a session id, access token, phone number, recording URL, or transcript content in the output | These are secrets / PII; the confirmation reports configuration state only. |
|
|
109
129
|
|
|
@@ -140,7 +160,7 @@ project and reports the result.
|
|
|
140
160
|
|
|
141
161
|
| File | When to read |
|
|
142
162
|
|------|-------------|
|
|
143
|
-
| `scripts/enable-recording-transcription.sh` | Step 2 — run to retrieve →
|
|
163
|
+
| `scripts/enable-recording-transcription.sh` | Step 2 — run to retrieve → set named field(s) → validate → deploy → verify (supports `--recording on\|off`, `--transcription on\|off`, `--dry-run`, `--api-version`, `--help`; defaults to both on). |
|
|
144
164
|
| `references/thunderbird-voice-settings.md` | Step 3 — for the metadata type, the two booleans, the `Settings` manifest, the v68.0+ floor, and the gate check. |
|
|
145
165
|
| `assets/package.xml` | Step 3 — the Metadata API manifest template the script generates; read to understand or hand-build the retrieve/deploy. |
|
|
146
166
|
| `assets/confirmation-output.json` | Step 5 — the exact shape of the JSON confirmation to return. |
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
# ThunderbirdVoiceSettings — Metadata API contract
|
|
2
2
|
|
|
3
|
-
Reference for enabling Native Voice (Thunderbird
|
|
4
|
-
via the Metadata API, using the sf CLI
|
|
5
|
-
Read this when constructing or debugging
|
|
3
|
+
Reference for configuring — enabling **or disabling** — Native Voice (Thunderbird
|
|
4
|
+
Voice) recording and transcription via the Metadata API, using the sf CLI
|
|
5
|
+
`Settings`-container retrieve/deploy path. Read this when constructing or debugging
|
|
6
|
+
`scripts/enable-recording-transcription.sh`.
|
|
6
7
|
|
|
7
8
|
## The metadata type
|
|
8
9
|
|
|
@@ -16,12 +17,13 @@ This skill manages exactly two boolean preferences:
|
|
|
16
17
|
|
|
17
18
|
| Field | Meaning | This skill |
|
|
18
19
|
|-------|---------|------------|
|
|
19
|
-
| `enableCallRecording` | Capture audio of Native Voice calls | **set `true
|
|
20
|
-
| `enableCallTranscription` | Produce transcripts of Native Voice calls | **set `true
|
|
20
|
+
| `enableCallRecording` | Capture audio of Native Voice calls | **set to the requested state** (`true`/`false`) when named (target) |
|
|
21
|
+
| `enableCallTranscription` | Produce transcripts of Native Voice calls | **set to the requested state** (`true`/`false`) when named (target) |
|
|
21
22
|
|
|
22
23
|
Recording and transcription are independent — a call can be recorded without being
|
|
23
24
|
transcribed and vice versa — so they are always modeled and reported as two separate
|
|
24
|
-
preferences.
|
|
25
|
+
preferences, and each can be enabled or disabled on its own. A target preference the
|
|
26
|
+
caller does not name is left at its retrieved value, exactly like a sibling.
|
|
25
27
|
|
|
26
28
|
The type may expose other booleans (e.g. `enableCallRecRedaction`,
|
|
27
29
|
`enableRealTimeStreamingRecording`, `enableSipRecording`). They are **out of scope** to
|
|
@@ -74,11 +76,13 @@ SFDX project). Two things matter:
|
|
|
74
76
|
(no fields), the org-perm gate `ThunderbirdVoice.orgHasNativeVoiceAllowed` is **off**
|
|
75
77
|
— Native Voice is not provisioned. Stop; this is enabled via provisioning/Blacktab,
|
|
76
78
|
not this skill. If the fields are listed, the gate is on — proceed.
|
|
77
|
-
3. **
|
|
78
|
-
|
|
79
|
+
3. **Set** each named target (`enableCallRecording` and/or `enableCallTranscription`)
|
|
80
|
+
to its requested value (`true` to enable, `false` to disable); leave every other
|
|
81
|
+
field — siblings and any un-named target — at its retrieved value.
|
|
79
82
|
4. **Validate** with `deploy --dry-run` (never skip).
|
|
80
83
|
5. **Deploy**.
|
|
81
|
-
6. **Verify** by re-retrieving and confirming the org *returns*
|
|
84
|
+
6. **Verify** by re-retrieving and confirming the org *returns* each named field at
|
|
85
|
+
its requested value.
|
|
82
86
|
|
|
83
87
|
## Distinguishing the two failure modes
|
|
84
88
|
|
|
@@ -1,27 +1,35 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
#
|
|
3
|
-
#
|
|
4
|
-
# Metadata API, using the sf CLI Settings-container retrieve/deploy path.
|
|
3
|
+
# Configure Native Voice (Thunderbird Voice) call recording + transcription via
|
|
4
|
+
# the Metadata API, using the sf CLI Settings-container retrieve/deploy path.
|
|
5
5
|
#
|
|
6
|
-
# Scope: this skill
|
|
7
|
-
# enableCallTranscription.
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
#
|
|
6
|
+
# Scope: this skill configures exactly two preferences — enableCallRecording and
|
|
7
|
+
# enableCallTranscription — to a requested ON or OFF state. Either or both may be
|
|
8
|
+
# targeted (`--recording on|off`, `--transcription on|off`); a preference that is
|
|
9
|
+
# NOT named is left at its retrieved value (preserved), so you can turn one on or
|
|
10
|
+
# off without disturbing the other. With NO preference flags the script defaults
|
|
11
|
+
# to turning BOTH on (the original enable-both behavior). Every OTHER
|
|
12
|
+
# ThunderbirdVoiceSettings boolean is read only to be preserved verbatim — none
|
|
13
|
+
# is changed. The siblings ARE reported back (unchanged) in a `preserved` block
|
|
14
|
+
# so compliance callers can confirm nothing else moved; reporting a value is not
|
|
15
|
+
# the same as setting it.
|
|
11
16
|
#
|
|
12
17
|
# Why sf CLI (not raw SOAP): the generic `Settings` metadata type IS in the sf
|
|
13
18
|
# CLI registry (member = the type name without the `Settings` suffix, i.e.
|
|
14
19
|
# `ThunderbirdVoice`). Deploying through it authenticates with the org's existing
|
|
15
20
|
# `sf` login — no browser session id / access token is ever handled. Retrieving
|
|
16
|
-
# first lets us (a) check the org-perm gate and (b) preserve
|
|
21
|
+
# first lets us (a) check the org-perm gate and (b) preserve non-targeted prefs.
|
|
17
22
|
#
|
|
18
|
-
# Sequence: retrieve -> gate check ->
|
|
19
|
-
# (dry-run) -> deploy -> verify by round-trip -> print a JSON
|
|
23
|
+
# Sequence: retrieve -> gate check -> set the targeted field(s) to the requested
|
|
24
|
+
# state -> validate (dry-run) -> deploy -> verify by round-trip -> print a JSON
|
|
25
|
+
# confirmation reporting the resulting state of BOTH preferences.
|
|
20
26
|
#
|
|
21
27
|
# Deps: sf, jq. (No curl, no session id.)
|
|
22
28
|
#
|
|
23
29
|
set -euo pipefail
|
|
24
30
|
|
|
31
|
+
fail() { echo "ERROR: $*" >&2; exit 1; }
|
|
32
|
+
|
|
25
33
|
# ThunderbirdVoiceSettings fields are only exposed at API v68.0+; below that the
|
|
26
34
|
# Settings type does not appear in a retrieve at all. Default to exactly 68.0 — the
|
|
27
35
|
# field-exposure floor. A retrieve/deploy at a version ABOVE the org's max supported
|
|
@@ -31,6 +39,9 @@ set -euo pipefail
|
|
|
31
39
|
API_VERSION="68.0"
|
|
32
40
|
TARGET_ORG=""
|
|
33
41
|
DRY_RUN="false"
|
|
42
|
+
# Desired state per target field: "" = not targeted (preserve), else "true"/"false".
|
|
43
|
+
REC_STATE=""
|
|
44
|
+
TRANS_STATE=""
|
|
34
45
|
|
|
35
46
|
# mdApiType ThunderbirdVoiceSettings -> package.xml member without the "Settings" suffix.
|
|
36
47
|
MEMBER="ThunderbirdVoice"
|
|
@@ -38,39 +49,60 @@ SETTINGS_FILE_REL="force-app/main/default/settings/${MEMBER}.settings-meta.xml"
|
|
|
38
49
|
|
|
39
50
|
usage() {
|
|
40
51
|
cat <<'EOF'
|
|
41
|
-
|
|
52
|
+
Configure Native Voice call recording + transcription (sf CLI Settings deploy).
|
|
42
53
|
|
|
43
54
|
Usage:
|
|
44
|
-
enable-recording-transcription.sh --target-org <alias>
|
|
55
|
+
enable-recording-transcription.sh --target-org <alias> \
|
|
56
|
+
[--recording on|off] [--transcription on|off] [--dry-run] [--api-version X.Y]
|
|
45
57
|
|
|
46
58
|
Options:
|
|
47
|
-
--target-org <alias>
|
|
48
|
-
--
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
+
--target-org <alias> Org alias/username the sf CLI is authenticated to. Required.
|
|
60
|
+
--recording on|off Desired state for call recording (enableCallRecording).
|
|
61
|
+
--transcription on|off Desired state for call transcription (enableCallTranscription).
|
|
62
|
+
If NEITHER --recording nor --transcription is given, the
|
|
63
|
+
script defaults to turning BOTH on. A preference that is
|
|
64
|
+
not named is left at its current (retrieved) value.
|
|
65
|
+
--api-version <X.Y> Metadata API version (default: 68.0; must be >= 68.0, the
|
|
66
|
+
version at which ThunderbirdVoiceSettings is exposed). Do NOT
|
|
67
|
+
exceed the target org's max supported version — a retrieve
|
|
68
|
+
above it silently returns empty.
|
|
69
|
+
--dry-run Retrieve + validate the change (deploy --dry-run) but do NOT
|
|
70
|
+
persist it. Prints the JSON confirmation with status VALIDATED.
|
|
71
|
+
-h, --help Show this help.
|
|
72
|
+
|
|
73
|
+
On success prints a JSON confirmation object: BOTH preferences reported separately
|
|
74
|
+
with their resulting state, plus a `preserved` block echoing every sibling boolean
|
|
75
|
+
(unchanged) so callers can confirm nothing else moved.
|
|
59
76
|
Uses the org's sf login; no session id / access token is handled or printed.
|
|
60
77
|
EOF
|
|
61
78
|
}
|
|
62
79
|
|
|
80
|
+
# Normalize an on/off style value to the literal "true"/"false" used in the XML.
|
|
81
|
+
onoff() { # $1 = flag name (for errors), $2 = value
|
|
82
|
+
case "$2" in
|
|
83
|
+
on|true|enable|enabled) echo "true" ;;
|
|
84
|
+
off|false|disable|disabled) echo "false" ;;
|
|
85
|
+
*) fail "invalid value for $1: '$2' (expected on|off)." ;;
|
|
86
|
+
esac
|
|
87
|
+
}
|
|
88
|
+
|
|
63
89
|
while [ $# -gt 0 ]; do
|
|
64
90
|
case "$1" in
|
|
65
91
|
--target-org) TARGET_ORG="$2"; shift 2 ;;
|
|
66
92
|
--api-version) API_VERSION="$2"; shift 2 ;;
|
|
93
|
+
--recording) REC_STATE="$(onoff --recording "$2")"; shift 2 ;;
|
|
94
|
+
--transcription) TRANS_STATE="$(onoff --transcription "$2")"; shift 2 ;;
|
|
67
95
|
--dry-run) DRY_RUN="true"; shift ;;
|
|
68
96
|
-h|--help) usage; exit 0 ;;
|
|
69
97
|
*) echo "ERROR: unknown argument: $1" >&2; usage >&2; exit 2 ;;
|
|
70
98
|
esac
|
|
71
99
|
done
|
|
72
100
|
|
|
73
|
-
|
|
101
|
+
# Default: if neither preference was named, target BOTH on — the original
|
|
102
|
+
# enable-both behavior, so existing callers keep working unchanged.
|
|
103
|
+
if [ -z "$REC_STATE" ] && [ -z "$TRANS_STATE" ]; then
|
|
104
|
+
REC_STATE="true"; TRANS_STATE="true"
|
|
105
|
+
fi
|
|
74
106
|
|
|
75
107
|
command -v sf >/dev/null 2>&1 || fail "sf CLI not found (required)."
|
|
76
108
|
command -v jq >/dev/null 2>&1 || fail "jq not found (required)."
|
|
@@ -100,6 +132,15 @@ EOF
|
|
|
100
132
|
|
|
101
133
|
SETTINGS_FILE="$PROJ/$SETTINGS_FILE_REL"
|
|
102
134
|
|
|
135
|
+
# Read the literal true/false a boolean field currently holds in the settings
|
|
136
|
+
# file; echoes "false" if the field is absent (guarded so an empty grep under
|
|
137
|
+
# `set -o pipefail` does not abort the script).
|
|
138
|
+
read_pref() { # $1 = settings xml file, $2 = field name
|
|
139
|
+
local v
|
|
140
|
+
v="$({ grep -oE "<$2>(true|false)</$2>" "$1" || true; } | sed -E "s#<$2>([a-z]+)</$2>#\1#" | head -n1)"
|
|
141
|
+
echo "${v:-false}"
|
|
142
|
+
}
|
|
143
|
+
|
|
103
144
|
# --- step 1: retrieve current settings (gate check + sibling snapshot) --------
|
|
104
145
|
( cd "$PROJ" && sf project retrieve start -x package.xml -o "$TARGET_ORG" >/dev/null 2>&1 ) \
|
|
105
146
|
|| fail "retrieve failed for org '$TARGET_ORG'. Check the alias is connected (sf org display -o $TARGET_ORG)."
|
|
@@ -113,24 +154,27 @@ if ! grep -q '<enableCallRecording>' "$SETTINGS_FILE"; then
|
|
|
113
154
|
fail "Native Voice is not provisioned on '$TARGET_ORG' (ThunderbirdVoiceSettings returned no fields). This is an org-perm gate enabled through provisioning/Blacktab, not something this skill can flip."
|
|
114
155
|
fi
|
|
115
156
|
|
|
116
|
-
# --- step 2:
|
|
117
|
-
#
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
157
|
+
# --- step 2: set ONLY the targeted field(s) to the requested state ------------
|
|
158
|
+
# A field that is not targeted is left at its retrieved value (preserved), just
|
|
159
|
+
# like the siblings. Version-suffixed sed -i for BSD (macOS) + GNU portability.
|
|
160
|
+
SED_ARGS=()
|
|
161
|
+
[ -n "$REC_STATE" ] && SED_ARGS+=(-e "s|<enableCallRecording>[^<]*</enableCallRecording>|<enableCallRecording>${REC_STATE}</enableCallRecording>|")
|
|
162
|
+
[ -n "$TRANS_STATE" ] && SED_ARGS+=(-e "s|<enableCallTranscription>[^<]*</enableCallTranscription>|<enableCallTranscription>${TRANS_STATE}</enableCallTranscription>|")
|
|
163
|
+
sed -E -i.bak "${SED_ARGS[@]}" "$SETTINGS_FILE"
|
|
122
164
|
rm -f "${SETTINGS_FILE}.bak"
|
|
123
165
|
|
|
124
|
-
|
|
125
|
-
|
|
166
|
+
# A targeted field must now hold the requested value; if the tag was absent the
|
|
167
|
+
# sed matched nothing (a provisioned org exposes both, so this is an edge case).
|
|
168
|
+
[ -z "$REC_STATE" ] || grep -q "<enableCallRecording>${REC_STATE}</enableCallRecording>" "$SETTINGS_FILE" || fail "could not set enableCallRecording=${REC_STATE} in the retrieved file (field absent?)."
|
|
169
|
+
[ -z "$TRANS_STATE" ] || grep -q "<enableCallTranscription>${TRANS_STATE}</enableCallTranscription>" "$SETTINGS_FILE" || fail "could not set enableCallTranscription=${TRANS_STATE} in the retrieved file (field absent?)."
|
|
126
170
|
|
|
127
171
|
# --- step 3: validate (dry-run). Never skip. ---------------------------------
|
|
128
172
|
( cd "$PROJ" && sf project deploy start -x package.xml -o "$TARGET_ORG" --dry-run --ignore-conflicts >/dev/null 2>&1 ) \
|
|
129
173
|
|| fail "validation (deploy --dry-run) failed — the org rejected the change. Re-check the gate (step 1)."
|
|
130
174
|
|
|
131
175
|
# Collect every boolean sibling (<tag>true|false</tag>) EXCEPT the two target
|
|
132
|
-
# prefs from the
|
|
133
|
-
#
|
|
176
|
+
# prefs from the settings file, as a JSON array [{name, enabled}], so the
|
|
177
|
+
# confirmation can attest each sibling was retrieved and carried through
|
|
134
178
|
# unchanged. `grep -o` isolates each pair; `#`-delimited sed avoids colliding
|
|
135
179
|
# with the `|` alternation; the `|| true` guards keep an empty result (no
|
|
136
180
|
# siblings) from tripping `set -o pipefail`.
|
|
@@ -141,8 +185,16 @@ extract_preserved() { # $1 = settings xml file -> JSON array on stdout
|
|
|
141
185
|
| jq -R -s 'split("\n") | map(select(length > 0) | split(" ") | { name: .[0], enabled: (.[1] == "true") })'
|
|
142
186
|
}
|
|
143
187
|
|
|
144
|
-
|
|
145
|
-
|
|
188
|
+
# Overall status word from the resulting state of the two target preferences.
|
|
189
|
+
compute_status() { # $1 = recording bool, $2 = transcription bool
|
|
190
|
+
if [ "$1" = "true" ] && [ "$2" = "true" ]; then echo "ENABLED"
|
|
191
|
+
elif [ "$1" = "false" ] && [ "$2" = "false" ]; then echo "DISABLED"
|
|
192
|
+
else echo "CONFIGURED"; fi
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
emit_json() { # $1 = recording bool, $2 = transcription bool, $3 = status, $4 = preserved JSON
|
|
196
|
+
jq -n --arg apiVersion "$API_VERSION" --arg status "$3" \
|
|
197
|
+
--argjson recording "$1" --argjson transcription "$2" --argjson preserved "$4" \
|
|
146
198
|
'{
|
|
147
199
|
type: "ThunderbirdVoiceSettings",
|
|
148
200
|
fullName: "ThunderbirdVoice",
|
|
@@ -150,8 +202,8 @@ emit_json() { # $1 = status, $2 = preserved siblings JSON array
|
|
|
150
202
|
method: "sf project deploy (Settings/ThunderbirdVoice)",
|
|
151
203
|
headless: true,
|
|
152
204
|
preferences: [
|
|
153
|
-
{ name: "enableCallRecording", enabled:
|
|
154
|
-
{ name: "enableCallTranscription", enabled:
|
|
205
|
+
{ name: "enableCallRecording", enabled: $recording },
|
|
206
|
+
{ name: "enableCallTranscription", enabled: $transcription }
|
|
155
207
|
],
|
|
156
208
|
preserved: $preserved,
|
|
157
209
|
status: $status
|
|
@@ -159,9 +211,11 @@ emit_json() { # $1 = status, $2 = preserved siblings JSON array
|
|
|
159
211
|
}
|
|
160
212
|
|
|
161
213
|
if [ "$DRY_RUN" = "true" ]; then
|
|
162
|
-
#
|
|
163
|
-
#
|
|
164
|
-
|
|
214
|
+
# The local file already reflects the requested change (targets flipped, the
|
|
215
|
+
# rest at their retrieved values); report both prefs from it.
|
|
216
|
+
REC_FINAL="$(read_pref "$SETTINGS_FILE" enableCallRecording)"
|
|
217
|
+
TRANS_FINAL="$(read_pref "$SETTINGS_FILE" enableCallTranscription)"
|
|
218
|
+
emit_json "$REC_FINAL" "$TRANS_FINAL" "VALIDATED" "$(extract_preserved "$SETTINGS_FILE")"
|
|
165
219
|
exit 0
|
|
166
220
|
fi
|
|
167
221
|
|
|
@@ -173,9 +227,14 @@ fi
|
|
|
173
227
|
rm -f "$SETTINGS_FILE"
|
|
174
228
|
( cd "$PROJ" && sf project retrieve start -x package.xml -o "$TARGET_ORG" >/dev/null 2>&1 ) \
|
|
175
229
|
|| fail "post-deploy retrieve failed; cannot verify."
|
|
176
|
-
|
|
177
|
-
|
|
230
|
+
|
|
231
|
+
REC_FINAL="$(read_pref "$SETTINGS_FILE" enableCallRecording)"
|
|
232
|
+
TRANS_FINAL="$(read_pref "$SETTINGS_FILE" enableCallTranscription)"
|
|
233
|
+
# Each targeted field must come back at the requested state (untargeted fields
|
|
234
|
+
# are whatever the org holds — reported, not asserted).
|
|
235
|
+
[ -z "$REC_STATE" ] || [ "$REC_FINAL" = "$REC_STATE" ] || fail "verification failed: org did not return enableCallRecording=${REC_STATE} (got ${REC_FINAL})."
|
|
236
|
+
[ -z "$TRANS_STATE" ] || [ "$TRANS_FINAL" = "$TRANS_STATE" ] || fail "verification failed: org did not return enableCallTranscription=${TRANS_STATE} (got ${TRANS_FINAL})."
|
|
178
237
|
|
|
179
238
|
# Report siblings from the post-deploy round-tripped file — their real, current
|
|
180
239
|
# (unchanged) values, proving nothing else moved.
|
|
181
|
-
emit_json "
|
|
240
|
+
emit_json "$REC_FINAL" "$TRANS_FINAL" "$(compute_status "$REC_FINAL" "$TRANS_FINAL")" "$(extract_preserved "$SETTINGS_FILE")"
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: service-omni-agent-users-create
|
|
3
|
+
description: "Create reusable agent users for Omni-Channel setup and routing validation. TRIGGER when users ask to create Omni agents, provision Omni test users, seed sandbox users for routing, create Omni-Channel routing agents, or repair missing demo agents. DO NOT TRIGGER for queue membership, permission-set assignment, or supervisor-user creation."
|
|
4
|
+
allowed-tools: Bash Read Write Edit Glob Grep
|
|
5
|
+
metadata:
|
|
6
|
+
version: "1.0"
|
|
7
|
+
domains: ["Service"]
|
|
8
|
+
minApiVersion: "66.0"
|
|
9
|
+
relatedSkills:
|
|
10
|
+
- "service-omni-base-settings-configure"
|
|
11
|
+
- "service-omni-channel-setup-coordinate"
|
|
12
|
+
- "service-omni-permission-set-assign"
|
|
13
|
+
- "service-omni-queue-members-assign"
|
|
14
|
+
- "service-omni-supervisor-users-create"
|
|
15
|
+
accessCheck:
|
|
16
|
+
- type: license
|
|
17
|
+
value: ServiceCloud
|
|
18
|
+
cliTools:
|
|
19
|
+
- tool: ["jq"]
|
|
20
|
+
semver: ">=1.6"
|
|
21
|
+
- tool: ["python3"]
|
|
22
|
+
semver: ">=3.9"
|
|
23
|
+
- tool: ["sf"]
|
|
24
|
+
semver: ">=2.139.6"
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
# service-omni-agent-users-create
|
|
28
|
+
|
|
29
|
+
Create N agent users on a Salesforce org via Anonymous Apex so Omni-Channel has agents to route work to. Usernames follow a deterministic per-org pattern, detection is SOQL-based, and the skill inserts only the users that are missing — so it is safe to re-run. It is invoked by `service-omni-channel-setup-coordinate` after `service-omni-base-settings-configure` enables Omni-Channel; assigning permission sets (`service-omni-permission-set-assign`) and adding users to queues (`service-omni-queue-members-assign`) are separate leaves. Supervisor users come from `service-omni-supervisor-users-create`.
|
|
30
|
+
|
|
31
|
+
## Inputs
|
|
32
|
+
|
|
33
|
+
Confirm once, up front:
|
|
34
|
+
|
|
35
|
+
- `org-alias` (required, no default) — must resolve via `sf org display`.
|
|
36
|
+
- User count (optional, default `3`, range `1..10`).
|
|
37
|
+
- Profile name (optional, default `Standard User` for portability). The coordinator overrides this to a Service Cloud profile so agents consume Service Cloud licenses; on many CDOs the Salesforce license pool is saturated while Service Cloud has free slots.
|
|
38
|
+
|
|
39
|
+
Usernames and passwords are never accepted from the operator — both are generated (usernames from the org suffix, passwords via Anonymous Apex). Operator-supplied credentials would break re-run detection and risk weak or leaked secrets.
|
|
40
|
+
|
|
41
|
+
## Preconditions and safety
|
|
42
|
+
|
|
43
|
+
- Target org authenticated via `sf` CLI (My Domain URL, not `.lightning.force.com`), Service Cloud license present, `sf` CLI ≥ 2.139.6.
|
|
44
|
+
- The executing user has `PermissionsModifyAllData` and `PermissionsManagePasswordPolicies` (standard on System Administrator).
|
|
45
|
+
- **Production guardrail:** the detect script computes `safe_to_write` as `IsSandbox` OR `TrialExpirationDate != null` OR `OrganizationType` in {Developer Edition, Base Edition}, and the skill blocks with no override when it is false. CDOs, scratch orgs, and dev orgs are permitted.
|
|
46
|
+
|
|
47
|
+
**Password handling (fail-closed).** Passwords are set by Anonymous Apex `System.setPassword` (`sf user password generate` cannot target Apex-inserted users — it fails with `NamedOrgNotFoundError`). The password literal appears in the inline executeAnonymous debug log and, only when a debug-log TraceFlag is active for the running user, in a queryable `ApexLog`. The wrapper therefore fails closed *before* the first `System.setPassword`: it proves via a SOQL-filtered Tooling API query (`ExpirationDate > now`) that no active TraceFlag exists. If safety cannot be positively proven — the running user is unresolved, the query fails or is unparseable, or any active TraceFlag exists — it generates no password at all; the user is left ACTIVE, flagged `password_status:"reset_required"`, and a `security_warning` explains why. It never deletes logs, so no plaintext can reach an `ApexLog` and unrelated audit logs are untouched. If `System.setPassword` itself fails for a user, that user is kept ACTIVE and flagged for reset.
|
|
48
|
+
|
|
49
|
+
## Run
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
# read-only preview (never writes)
|
|
53
|
+
bash scripts/detect-and-create.sh plan <org-alias> [count] [profile-name]
|
|
54
|
+
# detect, enforce safe_to_write, then insert only the missing users
|
|
55
|
+
bash scripts/detect-and-create.sh run <org-alias> [count=3] [profile-name="Standard User"]
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
`detect-and-create.sh` is the canonical entry point: it re-runs detection, enforces the production guard, and only then inserts. Do not call `scripts/run-create.sh` directly — it is internal and does not enforce the guard on its own.
|
|
59
|
+
|
|
60
|
+
## Behavior
|
|
61
|
+
|
|
62
|
+
**Detection.** The detector derives an 8-char suffix from `Organization.Id` (`substring(10,18)`, lowercased), resolves the profile by name, and queries `User` for `agent{i}.<suffix>@example.com` to find which of the `count` slot indexes are occupied. A stable, deterministic pattern is what makes re-runs idempotent — the suffix is never a timestamp or UUID.
|
|
63
|
+
|
|
64
|
+
**Insertion.** The Apex loads `assets/create-users.apex.template`, substitutes `__COUNT__`/`__PROFILE_ID__`/`__SUFFIX__`, and inserts only the missing indexes. It re-checks existing users inside the transaction, which prevents a single run from double-inserting; across *concurrent* runs the in-transaction check is not a guarantee (both can pass their pre-query before either commits), so duplicate protection there relies on the global username-uniqueness constraint plus the `DUPLICATE_USERNAME` retry (see references/apex-template-notes.md). It enables the Service Cloud feature (`UserPermissionsSupportUser=true`) so users can go online in Omni; if the profile's license does not allow it, the Apex strips the flag and retries (users are still created, but need a Service-Cloud-license profile for full Omni). Each created user is reported via `AGENT_USER_CREATED|<id>|<username>|<email>` (no password in the marker — passwords are set by the separate `System.setPassword` submission).
|
|
65
|
+
|
|
66
|
+
**Verification.** After insertion the detector re-runs and must show `missing_count == 0`; otherwise the skill fails (the Apex reported success but the users did not persist).
|
|
67
|
+
|
|
68
|
+
## Output contract
|
|
69
|
+
|
|
70
|
+
`detect-and-create.sh` emits a single JSON object with `status` ∈ `created` | `partial` | `reused` | `action_needed` | `blocked`, plus `detect` (mirrors the detector), `create` (mirrors the inserter, `null` when nothing was created), top-level `reused_users`, `created_count`, `reused_count`, `total_present_after`, `users_needing_password_reset`, `action_required`, and `safe_to_write`. The coordinator combines `reused_users` with newly created users so mixed runs configure presence and skills for the complete requested agent set.
|
|
71
|
+
|
|
72
|
+
- `created` — every missing index landed with a working password.
|
|
73
|
+
- `partial` — some landed but not all, or any user needs a manual password reset, or inactive occupants were found.
|
|
74
|
+
- `reused` — all requested users already existed; nothing created.
|
|
75
|
+
- `action_needed` — plan mode only; reports missing indexes without writing.
|
|
76
|
+
- `blocked` — precondition failed (`safe_to_write=false`, unresolved profile/suffix, or nothing inserted).
|
|
77
|
+
|
|
78
|
+
`create.created_users[].password` is populated only for users created this run whose `System.setPassword` succeeded; `password_status:"reset_required"` means the user is ACTIVE but has no working password yet. Exit code is 0 for `created`/`partial`/`action_needed`/`reused`, 1 for `blocked`.
|
|
79
|
+
|
|
80
|
+
Generated passwords are a secret: the returned JSON is the only place they appear. Any caller that persists stdout must write it only to a restricted `CREDENTIALS.json` (mode 0600), redact it from every other artifact, and delete it after distribution — the coordinator does this automatically; standalone callers own it. Reused users' passwords are not retrievable; recover via Setup → Users → Reset Password.
|
|
81
|
+
|
|
82
|
+
## Limitations
|
|
83
|
+
|
|
84
|
+
- Username pattern and org suffix are fixed and never operator-configurable — that determinism is what enables idempotent re-runs.
|
|
85
|
+
- Creates only agent users on the given profile; it never deletes, deactivates, or mutates existing users.
|
|
86
|
+
- Common User errors (`DUPLICATE_USERNAME`, `INVALID_EMAIL`, `LICENSE_LIMIT_EXCEEDED`) are translated into operator-friendly messages rather than surfaced raw.
|
|
87
|
+
|
|
88
|
+
## References
|
|
89
|
+
|
|
90
|
+
| File | When to read |
|
|
91
|
+
|---|---|
|
|
92
|
+
| `references/apex-patterns.md` | Before running the Apex — Apex structure, User field defaults, password policy, and the profile-localization risk |
|
|
93
|
+
| `references/apex-template-notes.md` | When user creation returns a duplicate, license, or password error — explains template substitutions and retry behavior |
|
|
94
|
+
| `assets/create-users.apex.template` | Loaded by `scripts/run-create.sh` when missing agent users must be inserted |
|
|
95
|
+
| `scripts/detect-existing.sh` | Loaded by the canonical entry point for the read-only org, profile, safety, and existing-user checks |
|
|
96
|
+
| `scripts/tests/test_user_create_security.py` | Run after changing the user-creation scripts to verify production refusal and password-handling contracts |
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// service-omni-agent-users-create - Anonymous Apex asset template. Tokens __COUNT__/__PROFILE_ID__/__SUFFIX__ resolved by run-create.sh. Idempotency, password/TraceFlag security note, output contract: references/apex-template-notes.md.
|
|
2
|
+
|
|
3
|
+
Integer requested = __COUNT__;
|
|
4
|
+
String profileId = '__PROFILE_ID__';
|
|
5
|
+
String suffix = '__SUFFIX__';
|
|
6
|
+
|
|
7
|
+
Map<String, Integer> expectedByUsername = new Map<String, Integer>();
|
|
8
|
+
for (Integer i = 1; i <= requested; i++) {
|
|
9
|
+
expectedByUsername.put('agent' + i + '.' + suffix + '@example.com', i);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
Set<String> existingUsernames = new Set<String>();
|
|
13
|
+
for (User u : [SELECT Username FROM User WHERE Username IN :expectedByUsername.keySet()]) {
|
|
14
|
+
existingUsernames.add(u.Username.toLowerCase());
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
List<User> toInsert = new List<User>();
|
|
18
|
+
|
|
19
|
+
for (Integer i = 1; i <= requested; i++) {
|
|
20
|
+
String uname = 'agent' + i + '.' + suffix + '@example.com';
|
|
21
|
+
if (existingUsernames.contains(uname.toLowerCase())) {
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Alias must be <= 8 chars - 'omag' (4) + index + trailing suffix chars.
|
|
26
|
+
String aliasStr;
|
|
27
|
+
if (i < 10) {
|
|
28
|
+
aliasStr = 'omag' + i + suffix.substring(suffix.length() - 3);
|
|
29
|
+
} else {
|
|
30
|
+
aliasStr = 'omag' + i + suffix.substring(suffix.length() - 2);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
User u = new User(
|
|
34
|
+
FirstName = 'Omni',
|
|
35
|
+
LastName = 'Agent' + i,
|
|
36
|
+
Email = uname,
|
|
37
|
+
Username = uname,
|
|
38
|
+
Alias = aliasStr,
|
|
39
|
+
ProfileId = profileId,
|
|
40
|
+
IsActive = true,
|
|
41
|
+
// Service Cloud feature license - required for the user to actually go online in
|
|
42
|
+
// Omni-Channel and to hold Omni permissions. Only allowed on user licenses that
|
|
43
|
+
// support it (e.g. Salesforce); the try/catch below strips it and retries when the
|
|
44
|
+
// profile's user license rejects it (e.g. the limited "Service Cloud" user license).
|
|
45
|
+
UserPermissionsSupportUser = true,
|
|
46
|
+
TimeZoneSidKey = 'America/Los_Angeles',
|
|
47
|
+
LocaleSidKey = 'en_US',
|
|
48
|
+
EmailEncodingKey = 'UTF-8',
|
|
49
|
+
LanguageLocaleKey = 'en_US'
|
|
50
|
+
);
|
|
51
|
+
toInsert.add(u);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (toInsert.isEmpty()) {
|
|
55
|
+
System.debug('AGENT_USERS_ALL_EXIST - no inserts required');
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
try {
|
|
60
|
+
insert toInsert;
|
|
61
|
+
System.debug('AGENT_USERS_SERVICE_CLOUD_FEATURE|enabled');
|
|
62
|
+
} catch (DmlException e) {
|
|
63
|
+
// The user license behind this profile does not allow the Service Cloud feature.
|
|
64
|
+
// Fall back to creating the users without it so the skill still provisions them; the
|
|
65
|
+
// operator must move them to a Salesforce license + Service Cloud feature for full Omni.
|
|
66
|
+
if (e.getMessage() != null && e.getMessage().contains('Service Cloud User')) {
|
|
67
|
+
for (User u : toInsert) { u.UserPermissionsSupportUser = false; }
|
|
68
|
+
insert toInsert;
|
|
69
|
+
System.debug('AGENT_USERS_SERVICE_CLOUD_FEATURE|skipped_license_not_allowed');
|
|
70
|
+
} else {
|
|
71
|
+
throw e;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Emit machine-parseable lines. No password field - passwords are set by a separate
|
|
76
|
+
// `System.setPassword` Apex submission on the CLI side (run-create.sh), whose source
|
|
77
|
+
// Salesforce does not persist to any queryable ApexLog.
|
|
78
|
+
for (User u : toInsert) {
|
|
79
|
+
System.debug('AGENT_USER_CREATED|' + u.Id + '|' + u.Username + '|' + u.Email);
|
|
80
|
+
}
|