@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
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: service-omni-presence-status-deploy
|
|
3
|
+
description: "Create the standard Available and Busy presence statuses needed for Omni-Channel routing. TRIGGER when users ask to deploy Omni presence statuses, configure agent availability, deploy Omni status metadata, create an Available status for Case, Incident, Messaging, or Voice, or add the standard Busy status. DO NOT TRIGGER for custom status designs, unsupported objects, or PresenceUserConfig capacity."
|
|
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-service-channel-configure"
|
|
14
|
+
accessCheck:
|
|
15
|
+
- type: license
|
|
16
|
+
value: ServiceCloud
|
|
17
|
+
cliTools:
|
|
18
|
+
- tool: ["jq"]
|
|
19
|
+
semver: ">=1.6"
|
|
20
|
+
- tool: ["sf"]
|
|
21
|
+
semver: ">=2.139.6"
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
# service-omni-presence-status-deploy
|
|
25
|
+
|
|
26
|
+
Deploy the sObject-specific `Available_<X>` ServicePresenceStatus bound to that sObject's ServiceChannel, and ensure a universal `Busy` status exists, in one atomic Metadata API deploy. Every Omni agent needs an "available for work" status carrying a channel (so incoming work routes) and an "off-work" status (so they can step away). A presence status binds to exactly one ServiceChannel by DeveloperName, so each routing target needs its own Available status. It pairs with `service-omni-permission-set-assign`, which grants agents access to the statuses this skill deploys, and is invoked once per routing target by `service-omni-channel-setup-coordinate`.
|
|
27
|
+
|
|
28
|
+
| Target sObject | Available status | Bound channel |
|
|
29
|
+
|---|---|---|
|
|
30
|
+
| `Case` | `Available_Case` | `Cases` |
|
|
31
|
+
| `Incident` | `Available_Incident` | `Incidents` |
|
|
32
|
+
| `MessagingSession` | `Available_Messaging` | `sfdc_livemessage` |
|
|
33
|
+
| `VoiceCall` | `Available_Voice` | `sfdc_phone` |
|
|
34
|
+
|
|
35
|
+
**Busy is UI-defined, not metadata-defined.** The Omni "Busy vs Online" option is not expressible via the Metadata API — a `ServicePresenceStatus` carries only `<label>` and `<channels>`, and a status with no channels is treated as busy. The conventional busy DeveloperName is simply `Busy`, and most orgs already have one, so this skill reuses an existing `Busy` and deploys the bundled asset only when none exists. A busy status is not required to receive work — the Available status carrying the channel is what routes it.
|
|
36
|
+
|
|
37
|
+
## Inputs
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
bash scripts/deploy-and-report.sh <org-alias> [Case|Incident|MessagingSession|VoiceCall]
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
- `org-alias` (required).
|
|
44
|
+
- `sobject_type` (optional, default `Case`).
|
|
45
|
+
|
|
46
|
+
## Preconditions and safety
|
|
47
|
+
|
|
48
|
+
- Target org authenticated via `sf` CLI (My Domain URL), Service Cloud license, `sf` CLI ≥ 2.139.6.
|
|
49
|
+
- Omni-Channel base settings enabled (`service-omni-base-settings-configure`) — otherwise the deploy fails with `INVALID_TYPE`.
|
|
50
|
+
- The target sObject's ServiceChannel exists (`service-omni-service-channel-configure`) — otherwise the deploy fails with `Value '<channel>' is not valid`. `Incident` requires the Incident Management feature enabled; `MessagingSession` requires Enhanced Messaging.
|
|
51
|
+
- The three-way `safe_to_write` production guard applies.
|
|
52
|
+
|
|
53
|
+
## Run
|
|
54
|
+
|
|
55
|
+
`deploy-and-report.sh` deploys the `Available_<X>` status (and `Busy` only when the org has none) in one Metadata API call, then maps the per-component `files[].state` to a skill status:
|
|
56
|
+
|
|
57
|
+
- `reused` — every component `Unchanged`.
|
|
58
|
+
- `updated` — any component `Changed`, none `Created`.
|
|
59
|
+
- `created` — any component `Created`.
|
|
60
|
+
|
|
61
|
+
There is no separate detect step — Salesforce's `files[].state` is authoritative. The deploy uses explicit `--metadata` flags (never `--source-dir`) so unrelated assets cannot piggyback.
|
|
62
|
+
|
|
63
|
+
## Behavior
|
|
64
|
+
|
|
65
|
+
**Reuse over redeploy.** An existing `Busy` is detected and left untouched; only a missing one is deployed. The skill never deletes or overwrites presence statuses it did not deploy — orgs often carry other custom statuses (`Omni_OnBreak`, `availableForMiaw`).
|
|
66
|
+
|
|
67
|
+
**Schema.** The XML omits `<statusType>` (removed in v66; online/busy is inferred from the presence of `<channels>`), and the skill never SOQL-queries `StatusType` (not queryable on v66 — only Id, DeveloperName, MasterLabel are reliable). `Available_Messaging` binds the standard `sfdc_livemessage` channel that Enhanced Messaging always ships.
|
|
68
|
+
|
|
69
|
+
## Output contract
|
|
70
|
+
|
|
71
|
+
A single JSON object with `status` ∈ `created` | `reused` | `updated` | `blocked`, `sobject_type`, a `presence_statuses` array (each with `developer_name`, `label`, `channels`, `state`, and — for `Busy` — `reused_existing`), `deploy_id`, `manual_actions`, and `blocking_issue`.
|
|
72
|
+
|
|
73
|
+
- `blocked` — deploy failed (missing prereq, schema mismatch, or org error); `manual_actions` names the prerequisite skill to run and `blocking_issue` is an operator-friendly translation of the Salesforce error.
|
|
74
|
+
- `deploy_id` is populated on every run (Salesforce issues one even for no-op deploys).
|
|
75
|
+
|
|
76
|
+
## Limitations
|
|
77
|
+
|
|
78
|
+
- Deploys only the `Available_<X>` + `Busy` pair — custom statuses require forking the `assets/servicePresenceStatuses/*.xml`.
|
|
79
|
+
- Only `Case`, `Incident`, `MessagingSession`, `VoiceCall` — extend by adding a dispatcher branch and a matching asset.
|
|
80
|
+
- Does not configure `PresenceUserConfig` capacity or decline reasons, and never deletes existing statuses.
|
|
81
|
+
|
|
82
|
+
## References
|
|
83
|
+
|
|
84
|
+
| File | When to read |
|
|
85
|
+
|---|---|
|
|
86
|
+
| `assets/package.xml` | Load when preparing the explicit Metadata API deployment for the selected Available status and the optional Busy status |
|
|
87
|
+
| `references/api-notes.md` | On unexpected deploy behavior — the v66 XML schema (no `statusType`), channel-binding rules, and MessagingSession-specific notes |
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
|
|
3
|
+
<types>
|
|
4
|
+
<members>Available_Case</members>
|
|
5
|
+
<members>Available_Incident</members>
|
|
6
|
+
<members>Available_Messaging</members>
|
|
7
|
+
<members>Available_Voice</members>
|
|
8
|
+
<members>Busy</members>
|
|
9
|
+
<name>ServicePresenceStatus</name>
|
|
10
|
+
</types>
|
|
11
|
+
<version>66.0</version>
|
|
12
|
+
</Package>
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# API notes — ServicePresenceStatus (v66)
|
|
2
|
+
|
|
3
|
+
## Schema
|
|
4
|
+
|
|
5
|
+
`ServicePresenceStatus` is a Metadata-only sObject. It has an sObject shell for SOQL discovery, but the interesting state (channels, decline-reason references) lives only in the XML metadata.
|
|
6
|
+
|
|
7
|
+
**v66 XML skeleton:**
|
|
8
|
+
|
|
9
|
+
```xml
|
|
10
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
11
|
+
<ServicePresenceStatus xmlns="http://soap.sforce.com/2006/04/metadata">
|
|
12
|
+
<label>Available for Cases</label>
|
|
13
|
+
<channels>
|
|
14
|
+
<channel>Cases</channel>
|
|
15
|
+
</channels>
|
|
16
|
+
</ServicePresenceStatus>
|
|
17
|
+
```text
|
|
18
|
+
|
|
19
|
+
**Two elements only:**
|
|
20
|
+
|
|
21
|
+
| Element | Required | Type | Notes |
|
|
22
|
+
|---|---|---|---|
|
|
23
|
+
| `<label>` | yes | string ≤ 80 chars | Display label. On v66 this is `<label>` (older API versions used `<masterLabel>`). |
|
|
24
|
+
| `<channels>` | no | 0..N of `<channel>` | If present, agent is online for those channels. If absent, agent is busy/away (no work received). Each `<channel>` references a `ServiceChannel.DeveloperName`. |
|
|
25
|
+
|
|
26
|
+
**Fields not present in v66 XML:**
|
|
27
|
+
|
|
28
|
+
- `<statusType>` — on v66 this is inferred from the presence of `<channels>`; adding `<statusType>` to the deploy XML causes `Element {statusType} not defined`.
|
|
29
|
+
- `<masterLabel>` — renamed to `<label>`.
|
|
30
|
+
|
|
31
|
+
## The StatusType concept is not settable or queryable on v66
|
|
32
|
+
|
|
33
|
+
The `StatusType` concept still exists in the Salesforce runtime (query it via `PresenceUserConfigProfile` joins or the presence-status-selector UI) but is not:
|
|
34
|
+
|
|
35
|
+
- Directly settable in metadata XML (v66) — inferred from `<channels>`
|
|
36
|
+
- Directly queryable in SOQL (see below)
|
|
37
|
+
- Directly readable via `sf project retrieve start` — the XML you retrieve is the canonical representation
|
|
38
|
+
|
|
39
|
+
**Runtime implication:** to give an agent a "Busy" status, deploy a `ServicePresenceStatus` with no `<channels>` element. To give an "Online for Cases" status, deploy one with `<channels><channel>Cases</channel></channels>`. Multiple busy statuses are allowed (e.g. `Omni_OnBreak`, `Busy` — both no channels, distinguishable only by label).
|
|
40
|
+
|
|
41
|
+
**Caveat — busy/online status option is UI-only:** the Omni-Channel "Status Options: Online / Busy" toggle is NOT part of the `ServicePresenceStatus` metadata type or a queryable field (confirmed via community + developer.salesforce.com — the type carries only `label` + `channels`). A no-channels status is treated as busy at runtime, but you cannot *set* the toggle headlessly. Because of this, our skill does not mint a custom `Omni_Busy`; it reuses the conventional `Busy` status if it exists (`SELECT Id FROM ServicePresenceStatus WHERE DeveloperName='Busy'`) and only deploys a bundled `Busy` when the org has none — never redeploying (and thus never risking) an existing, correctly-toggled `Busy`.
|
|
42
|
+
|
|
43
|
+
## StatusType is not SOQL-queryable on v66
|
|
44
|
+
|
|
45
|
+
`ServicePresenceStatus.StatusType` is not SOQL-queryable on v66:
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
SELECT Id, DeveloperName, StatusType FROM ServicePresenceStatus
|
|
49
|
+
^
|
|
50
|
+
ERROR at Row:1:Column:40
|
|
51
|
+
No such column 'StatusType' on entity 'ServicePresenceStatus'
|
|
52
|
+
```text
|
|
53
|
+
|
|
54
|
+
**Queryable fields:** `Id`, `DeveloperName`, `MasterLabel`.
|
|
55
|
+
|
|
56
|
+
**Fields that describe as existing on the sObject but fail in SOQL:** `StatusType`, `Channels` (relationship), various audit fields.
|
|
57
|
+
|
|
58
|
+
**Implication for the audit-log skill:** to enumerate the set of presence statuses on an org, use `SELECT Id, DeveloperName, MasterLabel FROM ServicePresenceStatus`, then optionally `sf project retrieve start` with a `ServicePresenceStatus:*` package to get the per-status XML for channel details.
|
|
59
|
+
|
|
60
|
+
## Deploy semantics
|
|
61
|
+
|
|
62
|
+
Metadata API deploy of `ServicePresenceStatus:Available_Case` returns per-file state:
|
|
63
|
+
|
|
64
|
+
- `Created` — new status record
|
|
65
|
+
- `Changed` — status existed, XML differed, record updated
|
|
66
|
+
- `Unchanged` — status existed, XML matched (byte-for-byte, after normalization)
|
|
67
|
+
|
|
68
|
+
For our skill's `Available_Case` XML:
|
|
69
|
+
|
|
70
|
+
```xml
|
|
71
|
+
<ServicePresenceStatus xmlns="http://soap.sforce.com/2006/04/metadata">
|
|
72
|
+
<channels>
|
|
73
|
+
<channel>Cases</channel>
|
|
74
|
+
</channels>
|
|
75
|
+
<label>Available for Cases</label>
|
|
76
|
+
</ServicePresenceStatus>
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
If the org has an existing `Available_Case` with a different label OR different channels OR extra child elements, deploy will report `Changed` and overwrite. This is intentional for our skill's "canonicalize state" semantics.
|
|
80
|
+
|
|
81
|
+
**XML element ordering does NOT affect Unchanged detection** — Salesforce normalizes before comparing. `<channels>` before `<label>` or after produces the same hash.
|
|
82
|
+
|
|
83
|
+
**Whitespace/indentation does NOT affect Unchanged detection** — Salesforce normalizes whitespace before comparing.
|
|
84
|
+
|
|
85
|
+
## Common deploy failures + translations
|
|
86
|
+
|
|
87
|
+
| Salesforce error | Skill translation | Prerequisite skill |
|
|
88
|
+
|---|---|---|
|
|
89
|
+
| `INVALID_TYPE: ServicePresenceStatus` | "OmniChannelSettings not enabled — run service-omni-base-settings-configure first" | `service-omni-base-settings-configure` |
|
|
90
|
+
| `INVALID_TYPE: OmniChannel` | Same as above | Same |
|
|
91
|
+
| `INVALID_FIELD_VALUE: 'Cases' is not valid` on `<channel>` reference | "Cases ServiceChannel does not exist — run service-omni-service-channel-configure first" | `service-omni-service-channel-configure` |
|
|
92
|
+
| `Element {statusType} not defined` | "Deploy XML contains `<statusType>` element — remove it; v66 infers status from `<channels>`" | (fix XML) |
|
|
93
|
+
| `INSUFFICIENT_ACCESS_OR_READONLY` | "Executing user lacks Metadata API permissions — re-authenticate as System Administrator" | (re-auth) |
|
|
94
|
+
| `UNKNOWN_EXCEPTION` (transient) | Retry once; if persists, escalate to Salesforce support | (retry) |
|
|
95
|
+
|
|
96
|
+
## Retrieve semantics
|
|
97
|
+
|
|
98
|
+
`sf project retrieve start --metadata "ServicePresenceStatus:*"` returns one XML file per status but can take several minutes for a handful of statuses (network + Salesforce packaging overhead), which is why this skill does not retrieve — it trusts `sf project deploy start`'s per-component `state` signal instead.
|
|
99
|
+
|
|
100
|
+
## Latency resilience (deploy async+poll; verify bounded-retry)
|
|
101
|
+
|
|
102
|
+
`deploy-and-report.sh` deploys **async** (`sf project deploy start --async`) then polls `sf project deploy report --job-id <id>` to a terminal state (~20 min budget = 80 × 15s), mirroring the sibling supervisor-config-deploy skill. A synchronous watched deploy raises `ClientTimeoutError` under org load and exits non-zero even when the server-side deploy Succeeds — a false red.
|
|
103
|
+
|
|
104
|
+
The post-deploy binding **verification** retrieve uses **bounded retry with backoff** (5 attempts, 10→40s increasing sleeps) so a single latency-induced *empty* retrieve does not immediately flip an otherwise-successful deploy to blocked. A retrieve that returns XML is authoritative and stops the loop at once (channel present → verified; channel absent → definitively unbound → blocked). The verify still fails closed: after the retry budget is exhausted with no XML, `BINDING_VERIFIED` stays `unverified` and the skill blocks — retries convert transient emptiness into a definitive read, they never assume success.
|
|
105
|
+
|
|
106
|
+
## Standard vs custom statuses
|
|
107
|
+
|
|
108
|
+
Salesforce ships several standard presence statuses out of the box on orgs with OmniChannel enabled:
|
|
109
|
+
|
|
110
|
+
- `availableForMessaging` (channels: sfdc_livemessage)
|
|
111
|
+
- `availableForMiaw` (channels: sfdc_livemessage)
|
|
112
|
+
|
|
113
|
+
These are shipped by Salesforce and always exist. Our skill's `Available_Case` is a custom addition — deploying it is idempotent (won't collide with the standard ones) but it does occupy the org's DeveloperName namespace. For the busy status we reuse the conventional `Busy` rather than adding a custom one.
|
|
114
|
+
|
|
115
|
+
**Naming convention rule:** never use a DeveloperName that looks like a standard status (e.g. `available*`, `busy*` without a prefix). Our skill uses `Available_Case` (with underscore + object suffix) and `Omni_*` (with `Omni_` prefix) to stay clear of the standard namespace.
|
|
@@ -0,0 +1,422 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# deploy-and-report.sh - deploy the sObject-specific "Available_<X>" presence status bound to the resolved ServiceChannel and reuse-or-deploy a universal "Busy" status (Busy is UI-only in the Metadata API). Args, channel binding, exit codes: SKILL.md.
|
|
3
|
+
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
|
|
6
|
+
if [ $# -lt 1 ]; then
|
|
7
|
+
echo '{"error":"Missing required argument: org-alias","usage":"bash deploy-and-report.sh <org-alias> [Case|Incident|MessagingSession|VoiceCall]"}' >&2
|
|
8
|
+
exit 1
|
|
9
|
+
fi
|
|
10
|
+
|
|
11
|
+
ORG="$1"
|
|
12
|
+
SOBJECT_TYPE="${2:-Case}"
|
|
13
|
+
CHANNEL_DN_ARG="${3:-}"
|
|
14
|
+
|
|
15
|
+
case "$SOBJECT_TYPE" in
|
|
16
|
+
Case)
|
|
17
|
+
AVAILABLE_STATUS_DN="Available_Case"
|
|
18
|
+
AVAILABLE_STATUS_LABEL="Available for Cases"
|
|
19
|
+
DEFAULT_CHANNEL_DN="Cases"
|
|
20
|
+
;;
|
|
21
|
+
Incident)
|
|
22
|
+
AVAILABLE_STATUS_DN="Available_Incident"
|
|
23
|
+
AVAILABLE_STATUS_LABEL="Available for Incidents"
|
|
24
|
+
DEFAULT_CHANNEL_DN="Incidents"
|
|
25
|
+
;;
|
|
26
|
+
MessagingSession)
|
|
27
|
+
AVAILABLE_STATUS_DN="Available_Messaging"
|
|
28
|
+
AVAILABLE_STATUS_LABEL="Available for Messaging"
|
|
29
|
+
DEFAULT_CHANNEL_DN="sfdc_livemessage"
|
|
30
|
+
;;
|
|
31
|
+
VoiceCall)
|
|
32
|
+
AVAILABLE_STATUS_DN="Available_Voice"
|
|
33
|
+
AVAILABLE_STATUS_LABEL="Available for Voice"
|
|
34
|
+
DEFAULT_CHANNEL_DN="sfdc_phone"
|
|
35
|
+
;;
|
|
36
|
+
*)
|
|
37
|
+
echo "{\"error\":\"Unsupported sobject_type: '$SOBJECT_TYPE'. Supported: Case, Incident, MessagingSession, VoiceCall\"}" >&2
|
|
38
|
+
exit 2
|
|
39
|
+
;;
|
|
40
|
+
esac
|
|
41
|
+
|
|
42
|
+
# Resolve the channel to bind: prefer the coordinator-supplied discovered channel, validate it,
|
|
43
|
+
# else fall back to the canonical default. Never silently ignore a passed-in channel name.
|
|
44
|
+
if [ -n "$CHANNEL_DN_ARG" ]; then
|
|
45
|
+
if ! [[ "$CHANNEL_DN_ARG" =~ ^[A-Za-z][A-Za-z0-9_]{0,79}$ ]]; then
|
|
46
|
+
echo "{\"error\":\"Invalid channel_developer_name '$CHANNEL_DN_ARG'. A Salesforce DeveloperName must start with a letter (allowed: A-Z a-z, then A-Z a-z 0-9 _; max 80).\"}" >&2
|
|
47
|
+
exit 1
|
|
48
|
+
fi
|
|
49
|
+
BOUND_CHANNEL_DN="$CHANNEL_DN_ARG"
|
|
50
|
+
CHANNEL_SOURCE="discovered"
|
|
51
|
+
else
|
|
52
|
+
BOUND_CHANNEL_DN="$DEFAULT_CHANNEL_DN"
|
|
53
|
+
CHANNEL_SOURCE="default"
|
|
54
|
+
fi
|
|
55
|
+
|
|
56
|
+
if ! sf org display --target-org "$ORG" --json >/dev/null 2>&1; then
|
|
57
|
+
echo "{\"error\":\"Org alias '$ORG' is not authenticated.\"}" >&2
|
|
58
|
+
exit 1
|
|
59
|
+
fi
|
|
60
|
+
|
|
61
|
+
# Write guard: deploys ServicePresenceStatus metadata, so self-enforce the production-org guard (directly invokable).
|
|
62
|
+
ORG_GUARD_JSON=$(sf data query --target-org "$ORG" \
|
|
63
|
+
--query "SELECT IsSandbox, TrialExpirationDate, OrganizationType FROM Organization LIMIT 1" \
|
|
64
|
+
--json 2>/dev/null) || {
|
|
65
|
+
echo "{\"error\":\"Failed to query Organization for safe_to_write guard: $(printf '%s' "$ORG_GUARD_JSON" | head -c 300)\"}" >&2
|
|
66
|
+
exit 1
|
|
67
|
+
}
|
|
68
|
+
IS_SANDBOX=$(echo "$ORG_GUARD_JSON" | jq -r '.result.records[0].IsSandbox')
|
|
69
|
+
TRIAL_EXP=$(echo "$ORG_GUARD_JSON" | jq -r '.result.records[0].TrialExpirationDate // "null"')
|
|
70
|
+
ORG_TYPE=$(echo "$ORG_GUARD_JSON" | jq -r '.result.records[0].OrganizationType')
|
|
71
|
+
|
|
72
|
+
SAFE_TO_WRITE=false
|
|
73
|
+
if [ "$IS_SANDBOX" = "true" ] \
|
|
74
|
+
|| [ "$TRIAL_EXP" != "null" ] \
|
|
75
|
+
|| [ "$ORG_TYPE" = "Developer Edition" ] \
|
|
76
|
+
|| [ "$ORG_TYPE" = "Base Edition" ]; then
|
|
77
|
+
SAFE_TO_WRITE=true
|
|
78
|
+
fi
|
|
79
|
+
|
|
80
|
+
if [ "$SAFE_TO_WRITE" != "true" ]; then
|
|
81
|
+
echo "{\"skill\":\"service-omni-presence-status-deploy\",\"status\":\"blocked\",\"blocking_issue\":\"Refusing to deploy presence statuses to a real production customer org (IsSandbox=$IS_SANDBOX, TrialExpirationDate=$TRIAL_EXP, OrganizationType=$ORG_TYPE). Target a sandbox, trial CDO, or Developer Edition org.\",\"safe_to_write\":false}" >&2
|
|
82
|
+
exit 1
|
|
83
|
+
fi
|
|
84
|
+
|
|
85
|
+
# emit read-only plan preview JSON and exit 0 (PLAN_ONLY mode)
|
|
86
|
+
emit_plan() {
|
|
87
|
+
local status="$1"
|
|
88
|
+
local detail="$2"
|
|
89
|
+
jq -n \
|
|
90
|
+
--arg status "$status" \
|
|
91
|
+
--arg detail "$detail" \
|
|
92
|
+
--arg sobject "$SOBJECT_TYPE" \
|
|
93
|
+
--arg chan "$BOUND_CHANNEL_DN" \
|
|
94
|
+
'{
|
|
95
|
+
skill: "service-omni-presence-status-deploy",
|
|
96
|
+
status: $status,
|
|
97
|
+
plan_mode: true,
|
|
98
|
+
plan_detail: $detail,
|
|
99
|
+
sobject_type: $sobject,
|
|
100
|
+
channel_developer_name: $chan,
|
|
101
|
+
manual_actions: [],
|
|
102
|
+
blocking_issue: null
|
|
103
|
+
}'
|
|
104
|
+
exit 0
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
# PLAN_ONLY: detect the two statuses via SOQL (read-only); the channel binding is verified only in a
|
|
108
|
+
# real run (its junction is not queryable), so plan uses existence as the preview signal.
|
|
109
|
+
if [ "${PLAN_ONLY:-}" = "1" ]; then
|
|
110
|
+
PS_EXIST_JSON=$(sf data query --target-org "$ORG" --json \
|
|
111
|
+
--query "SELECT DeveloperName FROM ServicePresenceStatus WHERE DeveloperName IN ('$AVAILABLE_STATUS_DN','Busy')" 2>/dev/null || echo '{}')
|
|
112
|
+
AVAIL_PRESENT=$(echo "$PS_EXIST_JSON" | jq -r --arg dn "$AVAILABLE_STATUS_DN" '[.result.records[].DeveloperName] | index($dn) != null' 2>/dev/null || echo false)
|
|
113
|
+
BUSY_PRESENT=$(echo "$PS_EXIST_JSON" | jq -r '[.result.records[].DeveloperName] | index("Busy") != null' 2>/dev/null || echo false)
|
|
114
|
+
if [ "$AVAIL_PRESENT" = "true" ] && [ "$BUSY_PRESENT" = "true" ]; then
|
|
115
|
+
emit_plan "reused" "Presence status '$AVAILABLE_STATUS_DN' and a 'Busy' status already exist."
|
|
116
|
+
elif [ "$AVAIL_PRESENT" = "true" ]; then
|
|
117
|
+
emit_plan "action_needed" "'$AVAILABLE_STATUS_DN' exists; would deploy a 'Busy' status (none found)."
|
|
118
|
+
elif [ "$BUSY_PRESENT" = "true" ]; then
|
|
119
|
+
emit_plan "action_needed" "A 'Busy' status exists (reuse); would deploy '$AVAILABLE_STATUS_DN' (→ channel '$BOUND_CHANNEL_DN')."
|
|
120
|
+
else
|
|
121
|
+
emit_plan "action_needed" "Would deploy presence status '$AVAILABLE_STATUS_DN' (→ channel '$BOUND_CHANNEL_DN') and a 'Busy' status."
|
|
122
|
+
fi
|
|
123
|
+
fi
|
|
124
|
+
|
|
125
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
126
|
+
ASSETS_DIR="$(cd "$SCRIPT_DIR/../assets" && pwd)"
|
|
127
|
+
|
|
128
|
+
if [ ! -f "$ASSETS_DIR/sfdx-project.json" ]; then
|
|
129
|
+
echo "{\"error\":\"Assets DX project not found at $ASSETS_DIR\"}" >&2
|
|
130
|
+
exit 1
|
|
131
|
+
fi
|
|
132
|
+
|
|
133
|
+
STATUS_XML="$ASSETS_DIR/force-app/main/default/servicePresenceStatuses/${AVAILABLE_STATUS_DN}.servicePresenceStatus-meta.xml"
|
|
134
|
+
if [ ! -f "$STATUS_XML" ]; then
|
|
135
|
+
echo "{\"error\":\"Asset not found for sobject_type=$SOBJECT_TYPE at $STATUS_XML\"}" >&2
|
|
136
|
+
exit 1
|
|
137
|
+
fi
|
|
138
|
+
|
|
139
|
+
# Materialize into a temp DX project so we can rewrite Available_<X>'s <channel> to the resolved
|
|
140
|
+
# channel. Reuse an existing "Busy" status if present (never redeploy it); deploy the bundled Busy only when absent.
|
|
141
|
+
BUSY_EXIST_JSON=$(sf data query --target-org "$ORG" --json \
|
|
142
|
+
--query "SELECT DeveloperName FROM ServicePresenceStatus WHERE DeveloperName='Busy'" 2>/dev/null || echo '{}')
|
|
143
|
+
BUSY_EXISTS=$(echo "$BUSY_EXIST_JSON" | jq -r '(.result.totalSize // 0) > 0' 2>/dev/null || echo false)
|
|
144
|
+
|
|
145
|
+
WORK="$(mktemp -d)"
|
|
146
|
+
trap 'rm -rf "$WORK"' EXIT
|
|
147
|
+
DEST_DIR="$WORK/force-app/main/default/servicePresenceStatuses"
|
|
148
|
+
mkdir -p "$DEST_DIR"
|
|
149
|
+
cp "$ASSETS_DIR/sfdx-project.json" "$WORK/sfdx-project.json"
|
|
150
|
+
|
|
151
|
+
# Available_<X>: rewrite the single <channel>...</channel> to the resolved channel DN.
|
|
152
|
+
# The XML has exactly one <channel> element (see asset); a targeted sed keeps the rest intact.
|
|
153
|
+
sed -E "s|<channel>[^<]*</channel>|<channel>${BOUND_CHANNEL_DN}</channel>|" \
|
|
154
|
+
"$STATUS_XML" > "$DEST_DIR/${AVAILABLE_STATUS_DN}.servicePresenceStatus-meta.xml"
|
|
155
|
+
|
|
156
|
+
# Build the --metadata deploy list. Always deploy the Available_<X> status; add "Busy" only when it
|
|
157
|
+
# does not already exist on the org.
|
|
158
|
+
DEPLOY_METADATA_ARGS=(--metadata "ServicePresenceStatus:${AVAILABLE_STATUS_DN}")
|
|
159
|
+
if [ "$BUSY_EXISTS" != "true" ]; then
|
|
160
|
+
cp "$ASSETS_DIR/force-app/main/default/servicePresenceStatuses/Busy.servicePresenceStatus-meta.xml" \
|
|
161
|
+
"$DEST_DIR/Busy.servicePresenceStatus-meta.xml"
|
|
162
|
+
DEPLOY_METADATA_ARGS+=(--metadata "ServicePresenceStatus:Busy")
|
|
163
|
+
fi
|
|
164
|
+
|
|
165
|
+
# One atomic deploy, explicit --metadata (no sibling piggyback). Deploy --async + poll so a
|
|
166
|
+
# synchronous ClientTimeoutError under org load can't produce a false red; budget ~20 min (80 * 15s).
|
|
167
|
+
START_JSON=$(cd "$WORK" && sf project deploy start --target-org "$ORG" \
|
|
168
|
+
"${DEPLOY_METADATA_ARGS[@]}" \
|
|
169
|
+
--async --json 2>/dev/null || true)
|
|
170
|
+
|
|
171
|
+
DEPLOY_ID=$(echo "$START_JSON" | jq -r '.result.id // ""' 2>/dev/null)
|
|
172
|
+
DEPLOY_JSON="$START_JSON"
|
|
173
|
+
|
|
174
|
+
if [ -n "$DEPLOY_ID" ] && [ "$DEPLOY_ID" != "null" ]; then
|
|
175
|
+
# Poll to a terminal state (Succeeded/Failed/SucceededPartial/Canceled); "in progress" is not blocked.
|
|
176
|
+
POLL_STATUS=""
|
|
177
|
+
for _ in $(seq 1 80); do
|
|
178
|
+
REPORT_JSON=$(cd "$WORK" && sf project deploy report --job-id "$DEPLOY_ID" --target-org "$ORG" --json 2>/dev/null || true)
|
|
179
|
+
CUR_STATUS=$(echo "$REPORT_JSON" | jq -r '.result.status // ""' 2>/dev/null)
|
|
180
|
+
case "$CUR_STATUS" in
|
|
181
|
+
Succeeded|Failed|SucceededPartial|Canceled)
|
|
182
|
+
DEPLOY_JSON="$REPORT_JSON"; POLL_STATUS="$CUR_STATUS"; break ;;
|
|
183
|
+
esac
|
|
184
|
+
sleep 15
|
|
185
|
+
done
|
|
186
|
+
|
|
187
|
+
if [ -z "$POLL_STATUS" ]; then
|
|
188
|
+
# No terminal state within budget - fail closed (don't report success on an unobserved deploy).
|
|
189
|
+
jq -n \
|
|
190
|
+
--arg deploy_id "$DEPLOY_ID" \
|
|
191
|
+
--arg sobject "$SOBJECT_TYPE" \
|
|
192
|
+
--arg avail_dn "$AVAILABLE_STATUS_DN" \
|
|
193
|
+
--arg avail_lbl "$AVAILABLE_STATUS_LABEL" \
|
|
194
|
+
--arg chan "$BOUND_CHANNEL_DN" \
|
|
195
|
+
'{
|
|
196
|
+
skill: "service-omni-presence-status-deploy",
|
|
197
|
+
status: "blocked",
|
|
198
|
+
sobject_type: $sobject,
|
|
199
|
+
presence_statuses: [
|
|
200
|
+
{developer_name: $avail_dn, label: $avail_lbl, channels: [$chan], state: "deploy_pending"},
|
|
201
|
+
{developer_name: "Busy", label: "Busy", channels: [], state: "deploy_pending"}
|
|
202
|
+
],
|
|
203
|
+
deploy_id: (if $deploy_id == "null" or $deploy_id == "" then null else $deploy_id end),
|
|
204
|
+
manual_actions: [("Inspect deploy: sf project deploy report --job-id " + $deploy_id)],
|
|
205
|
+
blocking_issue: ("Presence-status deploy job " + $deploy_id + " did not reach a terminal state within the ~20 min poll budget (org may still be processing under load). Not reporting success.")
|
|
206
|
+
}'
|
|
207
|
+
exit 1
|
|
208
|
+
fi
|
|
209
|
+
fi
|
|
210
|
+
|
|
211
|
+
# Re-derive from the report JSON (or START_JSON if the async start had no job id → failure branch below).
|
|
212
|
+
DEPLOY_SUCCESS=$(echo "$DEPLOY_JSON" | jq -r '.result.success // false' 2>/dev/null)
|
|
213
|
+
DEPLOY_ID=$(echo "$DEPLOY_JSON" | jq -r '.result.id // null' 2>/dev/null)
|
|
214
|
+
|
|
215
|
+
translate_deploy_error() {
|
|
216
|
+
local raw="$1"
|
|
217
|
+
case "$raw" in
|
|
218
|
+
*INVALID_TYPE*"ServicePresenceStatus"*|*INVALID_TYPE*"OmniChannel"*)
|
|
219
|
+
echo "OmniChannelSettings is not enabled on this org. Run service-omni-base-settings-configure first."
|
|
220
|
+
;;
|
|
221
|
+
*INVALID_FIELD_VALUE*"$BOUND_CHANNEL_DN"*|*"Value '$BOUND_CHANNEL_DN' is not valid"*)
|
|
222
|
+
echo "ServiceChannel '$BOUND_CHANNEL_DN' does not exist on this org. Run service-omni-service-channel-configure with sobject_type=$SOBJECT_TYPE first."
|
|
223
|
+
;;
|
|
224
|
+
*"Element {http://soap.sforce.com/2006/04/metadata}statusType"*)
|
|
225
|
+
echo "Deploy XML contains a <statusType> element that is not part of the ServicePresenceStatus schema. Remove <statusType> - online/busy is inferred from <channels> presence."
|
|
226
|
+
;;
|
|
227
|
+
*)
|
|
228
|
+
echo "$raw"
|
|
229
|
+
;;
|
|
230
|
+
esac
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if [ "$DEPLOY_SUCCESS" != "true" ]; then
|
|
234
|
+
FAILURES=$(echo "$DEPLOY_JSON" | jq -r '.result.details.componentFailures // [] | map(.problem) | join("; ")' 2>/dev/null)
|
|
235
|
+
if [ -z "$FAILURES" ] || [ "$FAILURES" = "null" ]; then
|
|
236
|
+
FAILURES=$(echo "$DEPLOY_JSON" | jq -r '.message // .result.errorMessage // "Unknown deploy error"' 2>/dev/null)
|
|
237
|
+
fi
|
|
238
|
+
FRIENDLY=$(translate_deploy_error "$FAILURES")
|
|
239
|
+
|
|
240
|
+
MANUAL_ACTIONS='[]'
|
|
241
|
+
case "$FRIENDLY" in
|
|
242
|
+
*"service-omni-base-settings-configure"*)
|
|
243
|
+
MANUAL_ACTIONS='[{"id":"MISSING_PREREQ","title":"Run prerequisite skill","target_skill":"service-omni-base-settings-configure"}]'
|
|
244
|
+
;;
|
|
245
|
+
*"service-omni-service-channel-configure"*)
|
|
246
|
+
MANUAL_ACTIONS='[{"id":"MISSING_PREREQ","title":"Run prerequisite skill","target_skill":"service-omni-service-channel-configure","sobject_type":"'"$SOBJECT_TYPE"'"}]'
|
|
247
|
+
;;
|
|
248
|
+
esac
|
|
249
|
+
|
|
250
|
+
jq -n \
|
|
251
|
+
--arg deploy_id "$DEPLOY_ID" \
|
|
252
|
+
--arg blocking "$FRIENDLY" \
|
|
253
|
+
--arg raw "$FAILURES" \
|
|
254
|
+
--arg sobject "$SOBJECT_TYPE" \
|
|
255
|
+
--arg avail_dn "$AVAILABLE_STATUS_DN" \
|
|
256
|
+
--arg avail_lbl "$AVAILABLE_STATUS_LABEL" \
|
|
257
|
+
--arg chan "$BOUND_CHANNEL_DN" \
|
|
258
|
+
--argjson ma "$MANUAL_ACTIONS" \
|
|
259
|
+
'{
|
|
260
|
+
skill: "service-omni-presence-status-deploy",
|
|
261
|
+
status: "blocked",
|
|
262
|
+
sobject_type: $sobject,
|
|
263
|
+
presence_statuses: [
|
|
264
|
+
{developer_name: $avail_dn, label: $avail_lbl, channels: [$chan], state: "not_deployed"},
|
|
265
|
+
{developer_name: "Busy", label: "Busy", channels: [], state: "not_deployed"}
|
|
266
|
+
],
|
|
267
|
+
deploy_id: (if $deploy_id == "null" or $deploy_id == "" then null else $deploy_id end),
|
|
268
|
+
manual_actions: $ma,
|
|
269
|
+
blocking_issue: $blocking,
|
|
270
|
+
raw_error: $raw
|
|
271
|
+
}'
|
|
272
|
+
exit 1
|
|
273
|
+
fi
|
|
274
|
+
|
|
275
|
+
get_state() {
|
|
276
|
+
local fullname="$1"
|
|
277
|
+
echo "$DEPLOY_JSON" | jq -r --arg fn "$fullname" \
|
|
278
|
+
'.result.files[] | select(.fullName == $fn and .type == "ServicePresenceStatus") | .state' 2>/dev/null | head -1
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
STATE_AVAILABLE=$(get_state "$AVAILABLE_STATUS_DN")
|
|
282
|
+
STATE_AVAILABLE="${STATE_AVAILABLE:-unknown}"
|
|
283
|
+
|
|
284
|
+
# Busy: if it already existed we reused it (never deployed) → treat as Unchanged. Otherwise read
|
|
285
|
+
# its deploy state from the response.
|
|
286
|
+
if [ "$BUSY_EXISTS" = "true" ]; then
|
|
287
|
+
STATE_BUSY="Unchanged"
|
|
288
|
+
else
|
|
289
|
+
STATE_BUSY=$(get_state "Busy")
|
|
290
|
+
STATE_BUSY="${STATE_BUSY:-unknown}"
|
|
291
|
+
fi
|
|
292
|
+
|
|
293
|
+
# Each component's state must be a known idempotency signal (Created/Changed/Unchanged); anything
|
|
294
|
+
# else means we can't prove it deployed - block rather than report success.
|
|
295
|
+
for pair in "AVAILABLE:$STATE_AVAILABLE" "BUSY:$STATE_BUSY"; do
|
|
296
|
+
comp="${pair%%:*}"; st="${pair#*:}"
|
|
297
|
+
case "$st" in
|
|
298
|
+
Created|Changed|Unchanged) ;;
|
|
299
|
+
*)
|
|
300
|
+
jq -n \
|
|
301
|
+
--arg sobject "$SOBJECT_TYPE" \
|
|
302
|
+
--arg avail_dn "$AVAILABLE_STATUS_DN" \
|
|
303
|
+
--arg chan "$BOUND_CHANNEL_DN" \
|
|
304
|
+
--arg deploy_id "$DEPLOY_ID" \
|
|
305
|
+
--arg comp "$comp" \
|
|
306
|
+
--arg st "$st" \
|
|
307
|
+
'{
|
|
308
|
+
skill: "service-omni-presence-status-deploy",
|
|
309
|
+
status: "blocked",
|
|
310
|
+
sobject_type: $sobject,
|
|
311
|
+
presence_statuses: [{developer_name: $avail_dn, channels: [$chan], state: $st}],
|
|
312
|
+
deploy_id: (if $deploy_id == "null" or $deploy_id == "" then null else $deploy_id end),
|
|
313
|
+
manual_actions: [],
|
|
314
|
+
blocking_issue: ("Deploy reported success overall but component " + $comp + " has an unrecognized/absent state \"" + $st + "\" (expected Created, Changed, or Unchanged). Not reporting success.")
|
|
315
|
+
}'
|
|
316
|
+
exit 1
|
|
317
|
+
;;
|
|
318
|
+
esac
|
|
319
|
+
done
|
|
320
|
+
|
|
321
|
+
# Post-verify the channel binding by RETRIEVING the deployed metadata and reading its <channel>
|
|
322
|
+
# back - the ServicePresenceStatusChannel junction is not queryable (INVALID_TYPE). Bounded retry
|
|
323
|
+
# with backoff so transient empty retrieves don't flip to blocked; XML present is authoritative,
|
|
324
|
+
# exhausted budget leaves "unverified" (fail closed below).
|
|
325
|
+
BINDING_VERIFIED="unverified"
|
|
326
|
+
VERIFY_ATTEMPTS=5
|
|
327
|
+
VERIFY_SLEEP=10
|
|
328
|
+
for verify_attempt in $(seq 1 "$VERIFY_ATTEMPTS"); do
|
|
329
|
+
VERIFY_WORK="$(mktemp -d)"
|
|
330
|
+
cp "$ASSETS_DIR/sfdx-project.json" "$VERIFY_WORK/sfdx-project.json"
|
|
331
|
+
mkdir -p "$VERIFY_WORK/force-app/main/default"
|
|
332
|
+
# Retrieve for its side effect (writes XML into VERIFY_WORK); verified by inspecting the file below.
|
|
333
|
+
( cd "$VERIFY_WORK" && sf project retrieve start --target-org "$ORG" \
|
|
334
|
+
--metadata "ServicePresenceStatus:${AVAILABLE_STATUS_DN}" --json >/dev/null 2>&1 || true )
|
|
335
|
+
RETRIEVED_XML=$(find "$VERIFY_WORK" -name "${AVAILABLE_STATUS_DN}.servicePresenceStatus-meta.xml" 2>/dev/null | head -1)
|
|
336
|
+
if [ -n "$RETRIEVED_XML" ] && [ -f "$RETRIEVED_XML" ]; then
|
|
337
|
+
# Extract every <channel>...</channel> value and test for an exact match to the resolved channel.
|
|
338
|
+
if grep -oE "<channel>[^<]*</channel>" "$RETRIEVED_XML" 2>/dev/null \
|
|
339
|
+
| sed -E 's|<channel>([^<]*)</channel>|\1|' \
|
|
340
|
+
| grep -qxF "$BOUND_CHANNEL_DN"; then
|
|
341
|
+
BINDING_VERIFIED="true"
|
|
342
|
+
else
|
|
343
|
+
BINDING_VERIFIED="false"
|
|
344
|
+
fi
|
|
345
|
+
rm -rf "$VERIFY_WORK"
|
|
346
|
+
break
|
|
347
|
+
fi
|
|
348
|
+
# No XML (likely transient latency) - back off and retry until the budget is exhausted.
|
|
349
|
+
rm -rf "$VERIFY_WORK"
|
|
350
|
+
if [ "$verify_attempt" -lt "$VERIFY_ATTEMPTS" ]; then
|
|
351
|
+
sleep "$VERIFY_SLEEP"
|
|
352
|
+
VERIFY_SLEEP=$((VERIFY_SLEEP + 10))
|
|
353
|
+
fi
|
|
354
|
+
done
|
|
355
|
+
|
|
356
|
+
# Block on both a confirmed-missing binding ("false") and an inconclusive read ("unverified"); a
|
|
357
|
+
# report to STDOUT so the coordinator captures it rather than seeing empty_output.
|
|
358
|
+
if [ "$BINDING_VERIFIED" != "true" ]; then
|
|
359
|
+
if [ "$BINDING_VERIFIED" = "false" ]; then
|
|
360
|
+
BIND_MSG="Deploy reported success but $AVAILABLE_STATUS_DN is not bound to ServiceChannel $BOUND_CHANNEL_DN on post-verify (the retrieved presence-status metadata does not list this channel). The channel DeveloperName may not exist on this org."
|
|
361
|
+
BIND_STATE="deployed_but_unbound"
|
|
362
|
+
else
|
|
363
|
+
BIND_MSG="Deploy reported success but the $AVAILABLE_STATUS_DN <-> $BOUND_CHANNEL_DN binding could not be verified (the metadata retrieve returned no presence-status XML - inconclusive). Not reporting success on an unverified binding."
|
|
364
|
+
BIND_STATE="deployed_binding_unverified"
|
|
365
|
+
fi
|
|
366
|
+
jq -n \
|
|
367
|
+
--arg sobject "$SOBJECT_TYPE" \
|
|
368
|
+
--arg avail_dn "$AVAILABLE_STATUS_DN" \
|
|
369
|
+
--arg chan "$BOUND_CHANNEL_DN" \
|
|
370
|
+
--arg deploy_id "$DEPLOY_ID" \
|
|
371
|
+
--arg bind_state "$BIND_STATE" \
|
|
372
|
+
--arg bind_msg "$BIND_MSG" \
|
|
373
|
+
'{
|
|
374
|
+
skill: "service-omni-presence-status-deploy",
|
|
375
|
+
status: "blocked",
|
|
376
|
+
sobject_type: $sobject,
|
|
377
|
+
presence_statuses: [{developer_name: $avail_dn, channels: [$chan], state: $bind_state}],
|
|
378
|
+
deploy_id: (if $deploy_id == "null" or $deploy_id == "" then null else $deploy_id end),
|
|
379
|
+
manual_actions: [],
|
|
380
|
+
blocking_issue: $bind_msg
|
|
381
|
+
}'
|
|
382
|
+
exit 1
|
|
383
|
+
fi
|
|
384
|
+
|
|
385
|
+
# Skill-level status: reused iff BOTH Unchanged; otherwise the "highest" activity wins (created > updated > reused).
|
|
386
|
+
STATUS="reused"
|
|
387
|
+
if [ "$STATE_AVAILABLE" != "Unchanged" ] || [ "$STATE_BUSY" != "Unchanged" ]; then
|
|
388
|
+
# If any component was Created → status: created; else: updated
|
|
389
|
+
if [ "$STATE_AVAILABLE" = "Created" ] || [ "$STATE_BUSY" = "Created" ]; then
|
|
390
|
+
STATUS="created"
|
|
391
|
+
else
|
|
392
|
+
STATUS="updated"
|
|
393
|
+
fi
|
|
394
|
+
fi
|
|
395
|
+
|
|
396
|
+
jq -n \
|
|
397
|
+
--arg status "$STATUS" \
|
|
398
|
+
--arg sobject "$SOBJECT_TYPE" \
|
|
399
|
+
--arg avail_dn "$AVAILABLE_STATUS_DN" \
|
|
400
|
+
--arg avail_lbl "$AVAILABLE_STATUS_LABEL" \
|
|
401
|
+
--arg chan "$BOUND_CHANNEL_DN" \
|
|
402
|
+
--arg state_avail "$STATE_AVAILABLE" \
|
|
403
|
+
--arg state_busy "$STATE_BUSY" \
|
|
404
|
+
--arg deploy_id "$DEPLOY_ID" \
|
|
405
|
+
--arg channel_source "$CHANNEL_SOURCE" \
|
|
406
|
+
--arg binding_verified "$BINDING_VERIFIED" \
|
|
407
|
+
--arg busy_exists "$BUSY_EXISTS" \
|
|
408
|
+
'{
|
|
409
|
+
skill: "service-omni-presence-status-deploy",
|
|
410
|
+
status: $status,
|
|
411
|
+
sobject_type: $sobject,
|
|
412
|
+
channel_developer_name: $chan,
|
|
413
|
+
channel_source: $channel_source,
|
|
414
|
+
binding_verified: ($binding_verified == "true"),
|
|
415
|
+
presence_statuses: [
|
|
416
|
+
{developer_name: $avail_dn, label: $avail_lbl, channels: [$chan], state: $state_avail},
|
|
417
|
+
{developer_name: "Busy", label: "Busy", channels: [], state: $state_busy, reused_existing: ($busy_exists == "true")}
|
|
418
|
+
],
|
|
419
|
+
deploy_id: $deploy_id,
|
|
420
|
+
manual_actions: [],
|
|
421
|
+
blocking_issue: null
|
|
422
|
+
}'
|