@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,1542 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# integration-driver.sh - orchestrate the Omni-Channel skills in canonical order (--plan read-only detect, --run full execution requiring safe_to_write); array-based invocation (never eval), production-guarded. Modes, design, artifacts, exit codes: SKILL.md.
|
|
3
|
+
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
|
|
6
|
+
MODE=""
|
|
7
|
+
POSITIONAL=()
|
|
8
|
+
while [[ $# -gt 0 ]]; do
|
|
9
|
+
case "$1" in
|
|
10
|
+
--plan|--dry-run) MODE="plan"; shift ;;
|
|
11
|
+
--run) MODE="run"; shift ;;
|
|
12
|
+
-h|--help)
|
|
13
|
+
cat >&2 <<'EOF'
|
|
14
|
+
Usage:
|
|
15
|
+
bash integration-driver.sh --plan <org-alias> [<count>] [<routing_targets_csv>] [<supervisor_count>]
|
|
16
|
+
bash integration-driver.sh --run <org-alias> [<count>] [<routing_targets_csv>] [<supervisor_count>]
|
|
17
|
+
|
|
18
|
+
Modes:
|
|
19
|
+
--plan Read-only. Runs only detect scripts. Emits {dry_run: true, ...}.
|
|
20
|
+
--run Executes deploys/POSTs. Enforces safe_to_write. Emits {dry_run: false, ...}.
|
|
21
|
+
|
|
22
|
+
Positional args (same for both modes):
|
|
23
|
+
org-alias sf CLI alias for the target org (required).
|
|
24
|
+
count Number of agents / queue members expected 1..10 (default 3).
|
|
25
|
+
routing_targets_csv Comma-separated sObject list. Default "Case".
|
|
26
|
+
Supported: Case, Incident, MessagingSession, VoiceCall.
|
|
27
|
+
supervisor_count Number of supervisor users expected 1..5 (default 1).
|
|
28
|
+
EOF
|
|
29
|
+
exit 0
|
|
30
|
+
;;
|
|
31
|
+
--) shift; POSITIONAL+=("$@"); break ;;
|
|
32
|
+
-*)
|
|
33
|
+
echo "{\"error\":\"Unknown flag: $1. Use --plan or --run. See --help.\"}" >&2
|
|
34
|
+
exit 2
|
|
35
|
+
;;
|
|
36
|
+
*)
|
|
37
|
+
POSITIONAL+=("$1"); shift
|
|
38
|
+
;;
|
|
39
|
+
esac
|
|
40
|
+
done
|
|
41
|
+
|
|
42
|
+
if [ -z "$MODE" ]; then
|
|
43
|
+
echo '{"error":"Must specify --plan or --run. See --help."}' >&2
|
|
44
|
+
exit 2
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
# Reset positional params; guard empty-array expansion for bash 3.2 nounset.
|
|
48
|
+
if [ "${#POSITIONAL[@]}" -gt 0 ]; then
|
|
49
|
+
set -- "${POSITIONAL[@]}"
|
|
50
|
+
else
|
|
51
|
+
set --
|
|
52
|
+
fi
|
|
53
|
+
|
|
54
|
+
if [ $# -lt 1 ]; then
|
|
55
|
+
echo '{"error":"Missing required <org-alias>. See --help."}' >&2
|
|
56
|
+
exit 2
|
|
57
|
+
fi
|
|
58
|
+
|
|
59
|
+
ORG="$1"
|
|
60
|
+
COUNT="${2:-3}"
|
|
61
|
+
ROUTING_TARGETS_CSV="${3:-Case}"
|
|
62
|
+
SUPERVISOR_COUNT="${4:-1}"
|
|
63
|
+
|
|
64
|
+
# Agent/supervisor profile: a full Salesforce + Service Cloud profile (not Platform, which fails the
|
|
65
|
+
# ContactCenterSupervisor assignment). Override via OMNI_AGENT_PROFILE / OMNI_SUPERVISOR_PROFILE.
|
|
66
|
+
# Empty here means "resolve against the org after auth" (see resolve_profile below) rather than
|
|
67
|
+
# hard-coding "Service Cloud User", which does not exist on every org.
|
|
68
|
+
AGENT_PROFILE="${OMNI_AGENT_PROFILE:-}"
|
|
69
|
+
SUPERVISOR_PROFILE="${OMNI_SUPERVISOR_PROFILE:-}"
|
|
70
|
+
SUPERVISOR_CONFIG_DN="${OMNI_SUPERVISOR_CONFIG_DEVELOPER_NAME:-Omni_Supervisor}"
|
|
71
|
+
|
|
72
|
+
# A complete WorkSkillRouting mapping is an unambiguous SBR request. Infer SkillsBased so callers do
|
|
73
|
+
# not need a hidden second switch; an explicit OMNI_ROUTING_TYPE still wins.
|
|
74
|
+
ROUTING_TYPE="${OMNI_ROUTING_TYPE:-}"
|
|
75
|
+
WSR_INPUT_COUNT=0
|
|
76
|
+
for wsr_value in "${OMNI_WSR_ENTITY:-}" "${OMNI_WSR_FIELD:-}" "${OMNI_WSR_SKILL:-}" "${OMNI_WSR_VALUE:-}"; do
|
|
77
|
+
[ -z "$wsr_value" ] || WSR_INPUT_COUNT=$((WSR_INPUT_COUNT + 1))
|
|
78
|
+
done
|
|
79
|
+
if [ -z "$ROUTING_TYPE" ]; then
|
|
80
|
+
if [ "$WSR_INPUT_COUNT" -eq 4 ]; then
|
|
81
|
+
ROUTING_TYPE="SkillsBased"
|
|
82
|
+
else
|
|
83
|
+
ROUTING_TYPE="QueueBased"
|
|
84
|
+
fi
|
|
85
|
+
fi
|
|
86
|
+
SKILL_OPTION="${OMNI_SKILL_OPTION:-RunSBRRules}"
|
|
87
|
+
SBR_SKILL="${OMNI_SBR_SKILL:-${OMNI_WSR_SKILL:-}}"
|
|
88
|
+
|
|
89
|
+
case "$ROUTING_TYPE" in
|
|
90
|
+
QueueBased|SkillsBased) ;;
|
|
91
|
+
*) echo "{\"error\":\"Invalid OMNI_ROUTING_TYPE '$ROUTING_TYPE'. Supported: QueueBased, SkillsBased.\"}" >&2; exit 2 ;;
|
|
92
|
+
esac
|
|
93
|
+
|
|
94
|
+
if [ "$WSR_INPUT_COUNT" -gt 0 ] && [ "$WSR_INPUT_COUNT" -lt 4 ]; then
|
|
95
|
+
echo '{"error":"Incomplete WorkSkillRouting input. Set all four of OMNI_WSR_ENTITY, OMNI_WSR_FIELD, OMNI_WSR_SKILL, and OMNI_WSR_VALUE."}' >&2
|
|
96
|
+
exit 2
|
|
97
|
+
fi
|
|
98
|
+
|
|
99
|
+
if [ "$ROUTING_TYPE" = "SkillsBased" ]; then
|
|
100
|
+
if [ "$WSR_INPUT_COUNT" -ne 4 ]; then
|
|
101
|
+
echo '{"error":"SkillsBased routing requires a complete WorkSkillRouting mapping: OMNI_WSR_ENTITY, OMNI_WSR_FIELD, OMNI_WSR_SKILL, and OMNI_WSR_VALUE."}' >&2
|
|
102
|
+
exit 2
|
|
103
|
+
fi
|
|
104
|
+
if [ "$SKILL_OPTION" != "RunSBRRules" ]; then
|
|
105
|
+
echo "{\"error\":\"The coordinator supports OMNI_SKILL_OPTION=RunSBRRules for SkillsBased routing; got '$SKILL_OPTION'.\"}" >&2
|
|
106
|
+
exit 2
|
|
107
|
+
fi
|
|
108
|
+
if [ -z "$SBR_SKILL" ] || [ "$SBR_SKILL" != "${OMNI_WSR_SKILL:-}" ]; then
|
|
109
|
+
echo '{"error":"SkillsBased routing requires OMNI_SBR_SKILL to be unset or match OMNI_WSR_SKILL, so provisioned agents receive the skill required by the WorkSkillRouting rule."}' >&2
|
|
110
|
+
exit 2
|
|
111
|
+
fi
|
|
112
|
+
fi
|
|
113
|
+
|
|
114
|
+
# Optional fallback assignee for Case/VoiceCall QRCs (Username or 005 Id); exported to each QRC leaf.
|
|
115
|
+
if [ -n "${OMNI_OVERFLOW_ASSIGNEE:-}" ]; then export QRC_OVERFLOW_ASSIGNEE="$OMNI_OVERFLOW_ASSIGNEE"; fi
|
|
116
|
+
if [ -n "${OMNI_QRC_PUSH_TIMEOUT:-}" ]; then export QRC_PUSH_TIMEOUT="$OMNI_QRC_PUSH_TIMEOUT"; fi
|
|
117
|
+
if [ -n "${OMNI_QRC_CAPACITY_PERCENTAGE:-}" ]; then export QRC_CAPACITY_PERCENTAGE="$OMNI_QRC_CAPACITY_PERCENTAGE"; fi
|
|
118
|
+
|
|
119
|
+
if ! [[ "$ORG" =~ ^[A-Za-z0-9._@-]+$ ]]; then
|
|
120
|
+
echo "{\"error\":\"Invalid org-alias '$ORG'. Allowed: A-Z, a-z, 0-9, . _ @ -\"}" >&2
|
|
121
|
+
exit 2
|
|
122
|
+
fi
|
|
123
|
+
|
|
124
|
+
# Agent count 1..10 - matches the leaf contracts; validate before any org write.
|
|
125
|
+
if ! [[ "$COUNT" =~ ^([1-9]|10)$ ]]; then
|
|
126
|
+
echo "{\"error\":\"Invalid count '$COUNT'. Must be integer 1..10.\"}" >&2
|
|
127
|
+
exit 2
|
|
128
|
+
fi
|
|
129
|
+
|
|
130
|
+
# Supervisor count 1..5 - matches the leaf contracts; validate before any org write.
|
|
131
|
+
if ! [[ "$SUPERVISOR_COUNT" =~ ^[1-5]$ ]]; then
|
|
132
|
+
echo "{\"error\":\"Invalid supervisor_count '$SUPERVISOR_COUNT'. Must be integer 1..5.\"}" >&2
|
|
133
|
+
exit 2
|
|
134
|
+
fi
|
|
135
|
+
|
|
136
|
+
if ! [[ "$SUPERVISOR_CONFIG_DN" =~ ^[A-Za-z][A-Za-z0-9_]{0,79}$ ]]; then
|
|
137
|
+
echo "{\"error\":\"Invalid OMNI_SUPERVISOR_CONFIG_DEVELOPER_NAME '$SUPERVISOR_CONFIG_DN'. It must start with a letter and contain only A-Z, a-z, 0-9, or _ (maximum 80 characters).\"}" >&2
|
|
138
|
+
exit 2
|
|
139
|
+
fi
|
|
140
|
+
|
|
141
|
+
IFS=',' read -r -a ROUTING_TARGETS <<< "$ROUTING_TARGETS_CSV"
|
|
142
|
+
for target in "${ROUTING_TARGETS[@]}"; do
|
|
143
|
+
case "$target" in
|
|
144
|
+
Case|Incident|MessagingSession|VoiceCall) ;;
|
|
145
|
+
*)
|
|
146
|
+
echo "{\"error\":\"Unsupported routing target: '$target'. Supported: Case, Incident, MessagingSession, VoiceCall.\"}" >&2
|
|
147
|
+
exit 2
|
|
148
|
+
;;
|
|
149
|
+
esac
|
|
150
|
+
done
|
|
151
|
+
|
|
152
|
+
SKILLS_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
|
|
153
|
+
TIMESTAMP="$(date -u +%Y%m%dT%H%M%SZ)"
|
|
154
|
+
# Artifacts may hold generated credentials: 0700 parent + umask 077 (files default 0600) so nothing
|
|
155
|
+
# is group/other readable; mktemp -d avoids same-second $TIMESTAMP collisions.
|
|
156
|
+
umask 077
|
|
157
|
+
mkdir -p "/tmp/omni-driver"
|
|
158
|
+
chmod 700 "/tmp/omni-driver" 2>/dev/null || true
|
|
159
|
+
ARTIFACTS_DIR="$(mktemp -d "/tmp/omni-driver/${TIMESTAMP}.XXXXXX")"
|
|
160
|
+
chmod 700 "$ARTIFACTS_DIR"
|
|
161
|
+
|
|
162
|
+
# Secrets split out of per-skill JSON into one restricted file (see redact_credentials).
|
|
163
|
+
CREDENTIALS_FILE="$ARTIFACTS_DIR/CREDENTIALS.json"
|
|
164
|
+
# The user-create leaves escrow generated passwords directly to this restricted file instead of ever
|
|
165
|
+
# printing plaintext to stdout; point them at the coordinator's file so all credentials land here.
|
|
166
|
+
# redact_credentials still runs on artifacts as defense-in-depth (it is a no-op when none are present).
|
|
167
|
+
export OMNI_CREDENTIALS_FILE="$CREDENTIALS_FILE"
|
|
168
|
+
|
|
169
|
+
SKILL_RESULTS='[]'
|
|
170
|
+
OVERALL_STATUS="green"
|
|
171
|
+
BLOCKING_SKILL=""
|
|
172
|
+
FIRST_YELLOW_SKILL=""
|
|
173
|
+
ATTENTION_LEAVES='[]'
|
|
174
|
+
|
|
175
|
+
# Global safe_to_write guard - enforced BEFORE any skill runs (both --plan and --run)
|
|
176
|
+
if ! sf org display --target-org "$ORG" --json >/dev/null 2>&1; then
|
|
177
|
+
echo "{\"error\":\"Org alias '$ORG' is not authenticated.\"}" >&2
|
|
178
|
+
exit 1
|
|
179
|
+
fi
|
|
180
|
+
|
|
181
|
+
ORG_JSON=$(sf data query --target-org "$ORG" --json \
|
|
182
|
+
--query "SELECT Id, IsSandbox, TrialExpirationDate, OrganizationType FROM Organization LIMIT 1" 2>/dev/null) || {
|
|
183
|
+
echo "{\"error\":\"Failed to query Organization. SOQL error: $(printf '%s' "$ORG_JSON" | head -c 400)\"}" >&2
|
|
184
|
+
exit 1
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
IS_SANDBOX=$(echo "$ORG_JSON" | jq -r '.result.records[0].IsSandbox')
|
|
188
|
+
TRIAL_EXP=$(echo "$ORG_JSON" | jq -r '.result.records[0].TrialExpirationDate // "null"')
|
|
189
|
+
ORG_TYPE=$(echo "$ORG_JSON" | jq -r '.result.records[0].OrganizationType')
|
|
190
|
+
|
|
191
|
+
SAFE_TO_WRITE="false"
|
|
192
|
+
if [ "$IS_SANDBOX" = "true" ] \
|
|
193
|
+
|| [ "$TRIAL_EXP" != "null" ] \
|
|
194
|
+
|| [ "$ORG_TYPE" = "Developer Edition" ] \
|
|
195
|
+
|| [ "$ORG_TYPE" = "Base Edition" ]; then
|
|
196
|
+
SAFE_TO_WRITE="true"
|
|
197
|
+
fi
|
|
198
|
+
|
|
199
|
+
if [ "$SAFE_TO_WRITE" != "true" ]; then
|
|
200
|
+
jq -n \
|
|
201
|
+
--arg org "$ORG" \
|
|
202
|
+
--arg is_sandbox "$IS_SANDBOX" \
|
|
203
|
+
--arg trial_exp "$TRIAL_EXP" \
|
|
204
|
+
--arg org_type "$ORG_TYPE" \
|
|
205
|
+
--arg mode "$MODE" \
|
|
206
|
+
'{
|
|
207
|
+
error: "Refusing to run integration driver against a real production customer org.",
|
|
208
|
+
org_alias: $org,
|
|
209
|
+
mode: $mode,
|
|
210
|
+
org_shape: {
|
|
211
|
+
is_sandbox: ($is_sandbox == "true"),
|
|
212
|
+
trial_expiration_date: (if $trial_exp == "null" then null else $trial_exp end),
|
|
213
|
+
organization_type: $org_type
|
|
214
|
+
},
|
|
215
|
+
safe_to_write: false,
|
|
216
|
+
required_shape: "Target must have IsSandbox=true OR TrialExpirationDate!=null OR OrganizationType in (Developer Edition, Base Edition)."
|
|
217
|
+
}' >&2
|
|
218
|
+
exit 1
|
|
219
|
+
fi
|
|
220
|
+
|
|
221
|
+
echo "" >&2
|
|
222
|
+
echo "=== Omni Integration Driver (mode=$MODE) ===" >&2
|
|
223
|
+
echo "Org: $ORG" >&2
|
|
224
|
+
echo "Count: $COUNT" >&2
|
|
225
|
+
echo "Supervisor count: $SUPERVISOR_COUNT" >&2
|
|
226
|
+
echo "Supervisor config: $SUPERVISOR_CONFIG_DN" >&2
|
|
227
|
+
echo "Routing targets: ${ROUTING_TARGETS[*]}" >&2
|
|
228
|
+
echo "Timestamp: $TIMESTAMP" >&2
|
|
229
|
+
echo "Artifacts: $ARTIFACTS_DIR" >&2
|
|
230
|
+
echo "safe_to_write: true (IsSandbox=$IS_SANDBOX TrialExp=$TRIAL_EXP OrgType=$ORG_TYPE)" >&2
|
|
231
|
+
|
|
232
|
+
# Resolve a usable profile on THIS org rather than assuming "Service Cloud User" exists. An explicit
|
|
233
|
+
# override is trusted as-is (only warned if absent); otherwise prefer "Service Cloud User", then fall
|
|
234
|
+
# back to "Standard User" (a full-Salesforce-license profile present in every org).
|
|
235
|
+
ORG_PROFILES_JSON=$(sf data query --target-org "$ORG" --json \
|
|
236
|
+
--query "SELECT Name FROM Profile" 2>/dev/null || echo '{}')
|
|
237
|
+
profile_exists() { echo "$ORG_PROFILES_JSON" | jq -e --arg n "$1" 'any(.result.records[]?; .Name==$n)' >/dev/null 2>&1; }
|
|
238
|
+
resolve_profile() {
|
|
239
|
+
local want="$1" label="$2" p
|
|
240
|
+
if [ -n "$want" ]; then
|
|
241
|
+
profile_exists "$want" || echo " [!] $label profile '$want' not found on org; the create leaf may fail or fall back." >&2
|
|
242
|
+
printf '%s' "$want"; return 0
|
|
243
|
+
fi
|
|
244
|
+
for p in "Service Cloud User" "Standard User"; do
|
|
245
|
+
profile_exists "$p" && { printf '%s' "$p"; return 0; }
|
|
246
|
+
done
|
|
247
|
+
printf '%s' "Standard User"
|
|
248
|
+
}
|
|
249
|
+
AGENT_PROFILE="$(resolve_profile "$AGENT_PROFILE" "Agent")"
|
|
250
|
+
SUPERVISOR_PROFILE="$(resolve_profile "$SUPERVISOR_PROFILE" "Supervisor")"
|
|
251
|
+
echo "Agent profile: $AGENT_PROFILE" >&2
|
|
252
|
+
echo "Supervisor profile: $SUPERVISOR_PROFILE" >&2
|
|
253
|
+
echo "" >&2
|
|
254
|
+
|
|
255
|
+
echo " Preflight: entity availability" >&2
|
|
256
|
+
for target in "${ROUTING_TARGETS[@]}"; do
|
|
257
|
+
if [ "$target" = "Case" ]; then continue; fi
|
|
258
|
+
ENTITY_CHECK=$(sf data query --target-org "$ORG" --json \
|
|
259
|
+
--query "SELECT QualifiedApiName FROM EntityDefinition WHERE QualifiedApiName = '$target'" 2>/dev/null \
|
|
260
|
+
| jq -r '.result.totalSize // 0')
|
|
261
|
+
if [ "$ENTITY_CHECK" != "1" ]; then
|
|
262
|
+
echo " [x] Target sObject '$target' not available on this org." >&2
|
|
263
|
+
echo " Enable required feature (Incident Management / Enhanced Messaging) and retry." >&2
|
|
264
|
+
exit 2
|
|
265
|
+
fi
|
|
266
|
+
echo " [ok] $target" >&2
|
|
267
|
+
done
|
|
268
|
+
echo "" >&2
|
|
269
|
+
|
|
270
|
+
OVERALL_START=$(date +%s)
|
|
271
|
+
|
|
272
|
+
# --plan: export PLAN_ONLY so each writer self-gates to its read-only detection path.
|
|
273
|
+
if [ "$MODE" = "plan" ]; then export PLAN_ONLY=1; fi
|
|
274
|
+
|
|
275
|
+
# Dependency tracking: never write after a red prerequisite (green/yellow allow dependents).
|
|
276
|
+
BASE_OK="true"
|
|
277
|
+
AGENT_USERS_OK="true"
|
|
278
|
+
SUPERVISOR_USERS_OK="true"
|
|
279
|
+
SUPERVISOR_CONFIG_OK="true"
|
|
280
|
+
# Per-target flags without associative arrays (macOS bash 3.2 has no declare -A): namespaced
|
|
281
|
+
# scalars + indirect expansion.
|
|
282
|
+
_flag_var() { # $1=namespace $2=key -> safe scalar var name
|
|
283
|
+
local key; key=$(printf '%s' "$2" | tr -c 'A-Za-z0-9' '_')
|
|
284
|
+
printf '%s' "__FLAG_${1}_${key}"
|
|
285
|
+
}
|
|
286
|
+
set_flag() { # $1=namespace $2=key $3=value
|
|
287
|
+
local v; v=$(_flag_var "$1" "$2"); printf -v "$v" '%s' "$3"
|
|
288
|
+
}
|
|
289
|
+
get_flag() { # $1=namespace $2=key $3=default -> echoes value or default
|
|
290
|
+
# set +u guards indirect expansion of an unset key under nounset (bash 3.2); scoped to this subshell.
|
|
291
|
+
local v val; v=$(_flag_var "$1" "$2"); set +u; val="${!v}"; set -u; printf '%s' "${val:-$3}"
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
# Canonical QRC DeveloperName per target; only Case/VoiceCall get a QRC + created queue.
|
|
295
|
+
qrc_dn_for_target() {
|
|
296
|
+
case "$1" in
|
|
297
|
+
Case) printf 'Case_Routing_Config' ;;
|
|
298
|
+
VoiceCall) printf 'Voice_Routing_Config' ;;
|
|
299
|
+
Incident) printf 'Incident_Routing_Config' ;;
|
|
300
|
+
MessagingSession) printf 'Messaging_Routing_Config' ;;
|
|
301
|
+
esac
|
|
302
|
+
}
|
|
303
|
+
qrc_override_for_target() {
|
|
304
|
+
case "$1" in
|
|
305
|
+
Case) printf '%s' "${OMNI_CASE_ROUTING_CONFIG_DEVELOPER_NAME:-}" ;;
|
|
306
|
+
VoiceCall) printf '%s' "${OMNI_VOICE_ROUTING_CONFIG_DEVELOPER_NAME:-}" ;;
|
|
307
|
+
Incident) printf '%s' "${OMNI_INCIDENT_ROUTING_CONFIG_DEVELOPER_NAME:-}" ;;
|
|
308
|
+
MessagingSession) printf '%s' "${OMNI_MESSAGING_ROUTING_CONFIG_DEVELOPER_NAME:-}" ;;
|
|
309
|
+
esac
|
|
310
|
+
}
|
|
311
|
+
queue_override_for_target() {
|
|
312
|
+
case "$1" in
|
|
313
|
+
Case) printf '%s' "${OMNI_CASE_QUEUE_DEVELOPER_NAME:-}" ;;
|
|
314
|
+
VoiceCall) printf '%s' "${OMNI_VOICE_QUEUE_DEVELOPER_NAME:-}" ;;
|
|
315
|
+
Incident) printf '%s' "${OMNI_INCIDENT_QUEUE_DEVELOPER_NAME:-}" ;;
|
|
316
|
+
MessagingSession) printf '%s' "${OMNI_MESSAGING_QUEUE_DEVELOPER_NAME:-}" ;;
|
|
317
|
+
esac
|
|
318
|
+
}
|
|
319
|
+
resolve_qrc_dn_for_target() {
|
|
320
|
+
local target="$1" explicit queue_dn linked_json linked_id qrc_json linked_dn
|
|
321
|
+
explicit="$(qrc_override_for_target "$target")"
|
|
322
|
+
if [ -n "$explicit" ]; then
|
|
323
|
+
if ! [[ "$explicit" =~ ^[A-Za-z][A-Za-z0-9_]{0,79}$ ]]; then
|
|
324
|
+
echo "Invalid routing-config DeveloperName '$explicit' for $target." >&2
|
|
325
|
+
return 1
|
|
326
|
+
fi
|
|
327
|
+
printf '%s' "$explicit"
|
|
328
|
+
return 0
|
|
329
|
+
fi
|
|
330
|
+
|
|
331
|
+
queue_dn="$(queue_override_for_target "$target")"
|
|
332
|
+
if [ -z "$queue_dn" ]; then
|
|
333
|
+
qrc_dn_for_target "$target"
|
|
334
|
+
return 0
|
|
335
|
+
fi
|
|
336
|
+
if ! [[ "$queue_dn" =~ ^[A-Za-z0-9_]{1,80}$ ]]; then
|
|
337
|
+
echo "Invalid queue DeveloperName '$queue_dn' for $target." >&2
|
|
338
|
+
return 1
|
|
339
|
+
fi
|
|
340
|
+
|
|
341
|
+
linked_json=$(sf data query --target-org "$ORG" --json \
|
|
342
|
+
--query "SELECT QueueRoutingConfigId FROM Group WHERE Type='Queue' AND DeveloperName='$queue_dn' LIMIT 1" 2>/dev/null || echo '{}')
|
|
343
|
+
if ! echo "$linked_json" | jq -e '.result.records | arrays' >/dev/null 2>&1; then
|
|
344
|
+
echo "Queue-to-routing-config discovery was inconclusive for '$queue_dn'." >&2
|
|
345
|
+
return 1
|
|
346
|
+
fi
|
|
347
|
+
linked_id=$(echo "$linked_json" | jq -r '.result.records[0].QueueRoutingConfigId // ""')
|
|
348
|
+
if [ -z "$linked_id" ]; then
|
|
349
|
+
qrc_dn_for_target "$target"
|
|
350
|
+
return 0
|
|
351
|
+
fi
|
|
352
|
+
if ! [[ "$linked_id" =~ ^[A-Za-z0-9]{15,18}$ ]]; then
|
|
353
|
+
echo "Queue '$queue_dn' returned an invalid QueueRoutingConfigId." >&2
|
|
354
|
+
return 1
|
|
355
|
+
fi
|
|
356
|
+
|
|
357
|
+
qrc_json=$(sf data query --target-org "$ORG" --json \
|
|
358
|
+
--query "SELECT DeveloperName FROM QueueRoutingConfig WHERE Id='$linked_id' LIMIT 1" 2>/dev/null || echo '{}')
|
|
359
|
+
if ! echo "$qrc_json" | jq -e '.result.records | arrays' >/dev/null 2>&1; then
|
|
360
|
+
echo "Linked routing-config discovery was inconclusive for queue '$queue_dn'." >&2
|
|
361
|
+
return 1
|
|
362
|
+
fi
|
|
363
|
+
linked_dn=$(echo "$qrc_json" | jq -r '.result.records[0].DeveloperName // ""')
|
|
364
|
+
if [ -z "$linked_dn" ]; then
|
|
365
|
+
echo "Queue '$queue_dn' references a QueueRoutingConfig that could not be resolved." >&2
|
|
366
|
+
return 1
|
|
367
|
+
fi
|
|
368
|
+
if ! [[ "$linked_dn" =~ ^[A-Za-z][A-Za-z0-9_]{0,79}$ ]]; then
|
|
369
|
+
echo "Queue '$queue_dn' returned an invalid linked QueueRoutingConfig DeveloperName." >&2
|
|
370
|
+
return 1
|
|
371
|
+
fi
|
|
372
|
+
printf '%s' "$linked_dn"
|
|
373
|
+
}
|
|
374
|
+
# Whether this target participates in the create-queue + QRC + routing-flow steel thread.
|
|
375
|
+
target_has_routing_thread() {
|
|
376
|
+
case "$1" in Case|VoiceCall) return 0 ;; *) return 1 ;; esac
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
# --plan readiness flags: a downstream detector runs only when its prerequisites are present on the
|
|
380
|
+
# org; otherwise it is deferred as pending (yellow), since probing an Omni-dependent skill before
|
|
381
|
+
# Omni is enabled raises INVALID_TYPE and would mislead as red. Clean --plan (all reused) => org
|
|
382
|
+
# already configured; any action_needed (yellow) => real setup work remains.
|
|
383
|
+
BASE_ENABLED="false" # Omni-Channel settings already enabled (base plan .all_enabled)
|
|
384
|
+
AGENT_USERS_PRESENT="false" # all agent users already exist (agent-users plan status == reused)
|
|
385
|
+
SUPERVISOR_USERS_PRESENT="false" # all supervisor users already exist (supervisor plan == reused)
|
|
386
|
+
SUPERVISOR_CONFIG_PRESENT="false" # OmniSupervisorConfig already exists (config plan == reused)
|
|
387
|
+
|
|
388
|
+
# Record a plan skill deferred because a prerequisite isn't in place yet (pending/yellow, not blocked).
|
|
389
|
+
record_plan_pending() {
|
|
390
|
+
local name="$1" key="$2" desc="$3" reason="$4"
|
|
391
|
+
printf " [ 0s] %-55s → pending (yellow)\n" "$key" >&2
|
|
392
|
+
echo " -> $reason" >&2
|
|
393
|
+
SKILL_RESULTS=$(echo "$SKILL_RESULTS" | jq -c \
|
|
394
|
+
--arg name "$name" --arg key "$key" --arg desc "$desc" --arg reason "$reason" \
|
|
395
|
+
'. + [{name: $name, invocation_key: $key, description: $desc, status: "action_needed", color: "yellow", elapsed_sec: 0, artifact_path: null, exit_code: 0, action_required: $reason}]')
|
|
396
|
+
ATTENTION_LEAVES=$(echo "$ATTENTION_LEAVES" | jq -c --arg k "$key" '. + [$k]')
|
|
397
|
+
if [ -z "$FIRST_YELLOW_SKILL" ]; then FIRST_YELLOW_SKILL="$key"; fi
|
|
398
|
+
if [ "$OVERALL_STATUS" = "green" ]; then OVERALL_STATUS="yellow"; fi
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
# Record a skill that was intentionally NOT run because a prerequisite failed.
|
|
402
|
+
record_dependency_skip() {
|
|
403
|
+
local name="$1" key="$2" desc="$3" reason="$4"
|
|
404
|
+
printf " [ 0s] %-55s → blocked_by_dependency\n" "$key" >&2
|
|
405
|
+
echo " -> $reason" >&2
|
|
406
|
+
SKILL_RESULTS=$(echo "$SKILL_RESULTS" | jq -c \
|
|
407
|
+
--arg name "$name" --arg key "$key" --arg desc "$desc" --arg reason "$reason" \
|
|
408
|
+
'. + [{name: $name, invocation_key: $key, description: $desc, status: "blocked_by_dependency", color: "red", elapsed_sec: 0, artifact_path: null, exit_code: 0, blocking_issue: $reason}]')
|
|
409
|
+
OVERALL_STATUS="red"
|
|
410
|
+
if [ -z "$BLOCKING_SKILL" ]; then BLOCKING_SKILL="$key"; fi
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
# Move plaintext passwords from a per-skill artifact into restricted CREDENTIALS.json (0600),
|
|
414
|
+
# replacing them with "[redacted - see CREDENTIALS.json]"; handles top-level and nested .create shapes.
|
|
415
|
+
CREDENTIALS_WRITTEN="false"
|
|
416
|
+
redact_credentials() {
|
|
417
|
+
local artifact="$1" key="$2"
|
|
418
|
+
[ -s "$artifact" ] || return 0
|
|
419
|
+
# Fast path: nothing to redact if no password field is present anywhere.
|
|
420
|
+
if ! jq -e '.. | objects | select(has("password")) | .password | strings' "$artifact" >/dev/null 2>&1; then
|
|
421
|
+
return 0
|
|
422
|
+
fi
|
|
423
|
+
|
|
424
|
+
# Extract [{username,password}] pairs (both shapes) with non-null passwords.
|
|
425
|
+
local creds
|
|
426
|
+
creds=$(jq -c '
|
|
427
|
+
[ (.created_users // []), (.create.created_users // []) ]
|
|
428
|
+
| add // []
|
|
429
|
+
| map(select(.password != null) | {username: .username, password: .password})
|
|
430
|
+
' "$artifact" 2>/dev/null || echo '[]')
|
|
431
|
+
|
|
432
|
+
if [ "$(echo "$creds" | jq 'length')" -gt 0 ]; then
|
|
433
|
+
# Escrow credentials fail-closed: build to a temp file, verify, atomically replace, and on any
|
|
434
|
+
# failure delete the plaintext source artifact and abort so credentials never leak.
|
|
435
|
+
local existing='{}' credtmp
|
|
436
|
+
[ -f "$CREDENTIALS_FILE" ] && existing=$(cat "$CREDENTIALS_FILE" 2>/dev/null || echo '{}')
|
|
437
|
+
credtmp=$(mktemp)
|
|
438
|
+
if ! echo "$existing" | jq --arg k "$key" --argjson c "$creds" '. + {($k): $c}' > "$credtmp" 2>/dev/null \
|
|
439
|
+
|| [ ! -s "$credtmp" ] \
|
|
440
|
+
|| ! jq -e --arg k "$key" '.[$k] | length > 0' "$credtmp" >/dev/null 2>&1; then
|
|
441
|
+
rm -f "$credtmp"
|
|
442
|
+
rm -f "$artifact"
|
|
443
|
+
printf " [x] SECURITY: could not escrow credentials to CREDENTIALS.json; deleted plaintext artifact %s and aborting.\n" "$artifact" >&2
|
|
444
|
+
exit 1
|
|
445
|
+
fi
|
|
446
|
+
if ! mv "$credtmp" "$CREDENTIALS_FILE"; then
|
|
447
|
+
rm -f "$credtmp"
|
|
448
|
+
rm -f "$artifact"
|
|
449
|
+
printf " [x] SECURITY: could not persist CREDENTIALS.json; deleted plaintext artifact %s and aborting.\n" "$artifact" >&2
|
|
450
|
+
exit 1
|
|
451
|
+
fi
|
|
452
|
+
chmod 600 "$CREDENTIALS_FILE" 2>/dev/null || true
|
|
453
|
+
CREDENTIALS_WRITTEN="true"
|
|
454
|
+
fi
|
|
455
|
+
|
|
456
|
+
# Null every .password in place, fail-closed: on failure delete the plaintext original and abort.
|
|
457
|
+
local tmp
|
|
458
|
+
tmp=$(mktemp)
|
|
459
|
+
if ! jq '(.. | objects | select(has("password")) | .password) |= (if . == null then null else "[redacted - see CREDENTIALS.json]" end)' \
|
|
460
|
+
"$artifact" > "$tmp" 2>/dev/null; then
|
|
461
|
+
rm -f "$tmp"
|
|
462
|
+
rm -f "$artifact"
|
|
463
|
+
printf " [x] SECURITY: could not redact credentials in %s; deleted plaintext artifact and aborting.\n" "$artifact" >&2
|
|
464
|
+
exit 1
|
|
465
|
+
fi
|
|
466
|
+
# Verify the rewritten file is non-empty and that no original plaintext password value survived.
|
|
467
|
+
if [ ! -s "$tmp" ] || jq -e '.. | objects | select(has("password")) | .password
|
|
468
|
+
| strings | select(. != "[redacted - see CREDENTIALS.json]")' "$tmp" >/dev/null 2>&1; then
|
|
469
|
+
rm -f "$tmp"
|
|
470
|
+
rm -f "$artifact"
|
|
471
|
+
printf " [x] SECURITY: redaction verification failed for %s; deleted plaintext artifact and aborting.\n" "$artifact" >&2
|
|
472
|
+
exit 1
|
|
473
|
+
fi
|
|
474
|
+
# Atomic replace only after verification succeeded.
|
|
475
|
+
if ! mv "$tmp" "$artifact"; then
|
|
476
|
+
rm -f "$tmp"
|
|
477
|
+
rm -f "$artifact"
|
|
478
|
+
printf " [x] SECURITY: could not persist redacted artifact %s; deleted plaintext artifact and aborting.\n" "$artifact" >&2
|
|
479
|
+
exit 1
|
|
480
|
+
fi
|
|
481
|
+
chmod 600 "$artifact" 2>/dev/null || true
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
# Per-skill runner (array-based invocation - NO eval).
|
|
485
|
+
run_skill() {
|
|
486
|
+
local skill_name="$1"
|
|
487
|
+
local invocation_key="$2"
|
|
488
|
+
local skill_desc="$3"
|
|
489
|
+
local status_expr="$4"
|
|
490
|
+
local script_file="$5"
|
|
491
|
+
shift 5
|
|
492
|
+
local -a script_args=("$@")
|
|
493
|
+
|
|
494
|
+
local skill_dir="$SKILLS_DIR/$skill_name"
|
|
495
|
+
if [ ! -d "$skill_dir" ]; then
|
|
496
|
+
printf " [x] Skill directory missing: %s\n" "$skill_dir" >&2
|
|
497
|
+
SKILL_RESULTS=$(echo "$SKILL_RESULTS" | jq -c \
|
|
498
|
+
--arg name "$skill_name" \
|
|
499
|
+
--arg key "$invocation_key" \
|
|
500
|
+
--arg desc "$skill_desc" \
|
|
501
|
+
'. + [{name: $name, invocation_key: $key, description: $desc, status: "missing_skill", color: "red", elapsed_sec: 0, artifact_path: null, exit_code: 127}]')
|
|
502
|
+
OVERALL_STATUS="red"
|
|
503
|
+
if [ -z "$BLOCKING_SKILL" ]; then BLOCKING_SKILL="$invocation_key"; fi
|
|
504
|
+
return 1
|
|
505
|
+
fi
|
|
506
|
+
|
|
507
|
+
local script_dir="$skill_dir/scripts"
|
|
508
|
+
local script_path="$script_dir/$script_file"
|
|
509
|
+
if [ ! -x "$script_path" ] && [ ! -f "$script_path" ]; then
|
|
510
|
+
printf " [x] Skill script missing: %s\n" "$script_path" >&2
|
|
511
|
+
SKILL_RESULTS=$(echo "$SKILL_RESULTS" | jq -c \
|
|
512
|
+
--arg name "$skill_name" \
|
|
513
|
+
--arg key "$invocation_key" \
|
|
514
|
+
--arg desc "$skill_desc" \
|
|
515
|
+
'. + [{name: $name, invocation_key: $key, description: $desc, status: "missing_skill_script", color: "red", elapsed_sec: 0, artifact_path: null, exit_code: 127}]')
|
|
516
|
+
OVERALL_STATUS="red"
|
|
517
|
+
if [ -z "$BLOCKING_SKILL" ]; then BLOCKING_SKILL="$invocation_key"; fi
|
|
518
|
+
return 1
|
|
519
|
+
fi
|
|
520
|
+
|
|
521
|
+
local start_ts artifact stderr_log exit_code status end_ts elapsed
|
|
522
|
+
start_ts=$(date +%s)
|
|
523
|
+
artifact="$ARTIFACTS_DIR/${invocation_key}.json"
|
|
524
|
+
stderr_log="$ARTIFACTS_DIR/${invocation_key}.stderr.log"
|
|
525
|
+
|
|
526
|
+
exit_code=0
|
|
527
|
+
( cd "$skill_dir" && bash "$script_path" "${script_args[@]}" ) > "$artifact" 2> "$stderr_log" || exit_code=$?
|
|
528
|
+
# umask 077 above already forces 0600, but be explicit in case the skill pre-created the file.
|
|
529
|
+
chmod 600 "$artifact" "$stderr_log" 2>/dev/null || true
|
|
530
|
+
|
|
531
|
+
# Move any plaintext passwords out of the artifact into CREDENTIALS.json.
|
|
532
|
+
redact_credentials "$artifact" "$invocation_key"
|
|
533
|
+
|
|
534
|
+
end_ts=$(date +%s)
|
|
535
|
+
elapsed=$((end_ts - start_ts))
|
|
536
|
+
|
|
537
|
+
if [ -s "$artifact" ]; then
|
|
538
|
+
status=$(jq -r "$status_expr" "$artifact" 2>/dev/null || echo "parse_error")
|
|
539
|
+
else
|
|
540
|
+
status="empty_output"
|
|
541
|
+
fi
|
|
542
|
+
|
|
543
|
+
# Status → traffic light. Green success states (shared enum): reused/updated/created/deployed/
|
|
544
|
+
# configured/bound/assigned/ok/success/skipped.
|
|
545
|
+
local color
|
|
546
|
+
case "$status" in
|
|
547
|
+
reused|updated|created|deployed|configured|bound|assigned|ok|success|skipped)
|
|
548
|
+
color="green"
|
|
549
|
+
;;
|
|
550
|
+
action_needed|partial|unsupported_v1|not_evaluated)
|
|
551
|
+
color="yellow"
|
|
552
|
+
;;
|
|
553
|
+
blocked|blocked_by_dependency|empty_output|parse_error)
|
|
554
|
+
color="red"
|
|
555
|
+
;;
|
|
556
|
+
*)
|
|
557
|
+
color="unknown"
|
|
558
|
+
;;
|
|
559
|
+
esac
|
|
560
|
+
|
|
561
|
+
if [ "$exit_code" -ne 0 ] && [ "$color" != "red" ]; then
|
|
562
|
+
printf " [!] skill exit_code=%d contradicts status='%s' - escalating to blocked\n" "$exit_code" "$status" >&2
|
|
563
|
+
status="crashed_after_reporting_${status}"
|
|
564
|
+
color="red"
|
|
565
|
+
fi
|
|
566
|
+
if [ "$color" = "unknown" ]; then
|
|
567
|
+
printf " [!] unrecognized skill status='%s' - treating as attention_required\n" "$status" >&2
|
|
568
|
+
color="yellow"
|
|
569
|
+
fi
|
|
570
|
+
|
|
571
|
+
printf " [%3ds] %-55s → %s (%s)\n" "$elapsed" "$invocation_key" "$status" "$color" >&2
|
|
572
|
+
if [ "$color" = "red" ]; then
|
|
573
|
+
local msg
|
|
574
|
+
msg=$(jq -r '.blocking_issue // .error // .message // "no message"' "$artifact" 2>/dev/null || echo "(unparseable)")
|
|
575
|
+
echo " -> $msg" >&2
|
|
576
|
+
elif [ "$color" = "yellow" ]; then
|
|
577
|
+
local msg
|
|
578
|
+
msg=$(jq -r '.action_required // .manual_actions // .blocking_issue // ""' "$artifact" 2>/dev/null || echo "")
|
|
579
|
+
if [ -n "$msg" ] && [ "$msg" != "null" ] && [ "$msg" != "[]" ]; then
|
|
580
|
+
echo " -> action needed: $msg" >&2
|
|
581
|
+
fi
|
|
582
|
+
fi
|
|
583
|
+
|
|
584
|
+
SKILL_RESULTS=$(echo "$SKILL_RESULTS" | jq -c \
|
|
585
|
+
--arg name "$skill_name" \
|
|
586
|
+
--arg key "$invocation_key" \
|
|
587
|
+
--arg desc "$skill_desc" \
|
|
588
|
+
--arg status "$status" \
|
|
589
|
+
--arg color "$color" \
|
|
590
|
+
--argjson elapsed "$elapsed" \
|
|
591
|
+
--arg artifact "$artifact" \
|
|
592
|
+
--argjson exit_code "$exit_code" \
|
|
593
|
+
'. + [{name: $name, invocation_key: $key, description: $desc, status: $status, color: $color, elapsed_sec: $elapsed, artifact_path: $artifact, exit_code: $exit_code}]')
|
|
594
|
+
|
|
595
|
+
case "$color" in
|
|
596
|
+
red)
|
|
597
|
+
if [ -z "$BLOCKING_SKILL" ]; then BLOCKING_SKILL="$invocation_key"; fi
|
|
598
|
+
OVERALL_STATUS="red"
|
|
599
|
+
return 1
|
|
600
|
+
;;
|
|
601
|
+
yellow)
|
|
602
|
+
ATTENTION_LEAVES=$(echo "$ATTENTION_LEAVES" | jq -c --arg k "$invocation_key" '. + [$k]')
|
|
603
|
+
if [ -z "$FIRST_YELLOW_SKILL" ]; then FIRST_YELLOW_SKILL="$invocation_key"; fi
|
|
604
|
+
if [ "$OVERALL_STATUS" = "green" ]; then OVERALL_STATUS="yellow"; fi
|
|
605
|
+
;;
|
|
606
|
+
green)
|
|
607
|
+
:
|
|
608
|
+
;;
|
|
609
|
+
esac
|
|
610
|
+
|
|
611
|
+
return 0
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
# ---- Base settings ----
|
|
615
|
+
# Plan detects; run deploys the 5 OmniChannelSettings toggles + re-verifies. Hard prerequisite for
|
|
616
|
+
# every write skill below (INVALID_TYPE when Omni is off), so a red here fails fast.
|
|
617
|
+
if [ "$MODE" = "plan" ]; then
|
|
618
|
+
run_skill "service-omni-base-settings-configure" \
|
|
619
|
+
"service-omni-base-settings-configure" \
|
|
620
|
+
"Omni base settings (detect-existing)" \
|
|
621
|
+
'(if .all_enabled == true then "reused" else "action_needed" end)' \
|
|
622
|
+
configure-and-report.sh plan "$ORG" \
|
|
623
|
+
|| BASE_OK="false"
|
|
624
|
+
base_art="$ARTIFACTS_DIR/service-omni-base-settings-configure.json"
|
|
625
|
+
if [ -f "$base_art" ] && [ "$(jq -r '.all_enabled // false' "$base_art" 2>/dev/null)" = "true" ]; then
|
|
626
|
+
BASE_ENABLED="true"
|
|
627
|
+
fi
|
|
628
|
+
else
|
|
629
|
+
run_skill "service-omni-base-settings-configure" \
|
|
630
|
+
"service-omni-base-settings-configure" \
|
|
631
|
+
"Omni base settings (detect → deploy → verify)" \
|
|
632
|
+
'.status' \
|
|
633
|
+
configure-and-report.sh run "$ORG" \
|
|
634
|
+
|| BASE_OK="false"
|
|
635
|
+
fi
|
|
636
|
+
|
|
637
|
+
# ---- Agent users (detect-and-create.sh chains detect → run-create in --run mode) ----
|
|
638
|
+
# Agents provision on Service Cloud User (Omni requires a Service Cloud license).
|
|
639
|
+
run_skill "service-omni-agent-users-create" \
|
|
640
|
+
"service-omni-agent-users-create" \
|
|
641
|
+
"Agent users (detect + create when missing)" \
|
|
642
|
+
'.status' \
|
|
643
|
+
detect-and-create.sh "$MODE" "$ORG" "$COUNT" "$AGENT_PROFILE" \
|
|
644
|
+
|| AGENT_USERS_OK="false"
|
|
645
|
+
|
|
646
|
+
if [ "$MODE" = "plan" ]; then
|
|
647
|
+
du_art="$ARTIFACTS_DIR/service-omni-agent-users-create.json"
|
|
648
|
+
if [ -f "$du_art" ] && [ "$(jq -r '.status // ""' "$du_art" 2>/dev/null)" = "reused" ]; then
|
|
649
|
+
AGENT_USERS_PRESENT="true"
|
|
650
|
+
fi
|
|
651
|
+
fi
|
|
652
|
+
|
|
653
|
+
# ---- Service channel (per target) ----
|
|
654
|
+
for target in "${ROUTING_TARGETS[@]}"; do
|
|
655
|
+
if [ "$MODE" = "plan" ]; then
|
|
656
|
+
if [ "$BASE_ENABLED" = "true" ]; then
|
|
657
|
+
run_skill "service-omni-service-channel-configure" \
|
|
658
|
+
"service-omni-service-channel-configure-${target}" \
|
|
659
|
+
"ServiceChannel for $target (plan detect)" \
|
|
660
|
+
'.status' \
|
|
661
|
+
deploy-and-report.sh "$ORG" "$target" || true
|
|
662
|
+
else
|
|
663
|
+
record_plan_pending "service-omni-service-channel-configure" \
|
|
664
|
+
"service-omni-service-channel-configure-${target}" \
|
|
665
|
+
"ServiceChannel for $target (pending)" \
|
|
666
|
+
"Omni-Channel is not yet enabled; the ServiceChannel for $target will be configured after base settings."
|
|
667
|
+
fi
|
|
668
|
+
set_flag SC_OK "$target" "true"
|
|
669
|
+
elif [ "$BASE_OK" != "true" ]; then
|
|
670
|
+
record_dependency_skip "service-omni-service-channel-configure" \
|
|
671
|
+
"service-omni-service-channel-configure-${target}" \
|
|
672
|
+
"ServiceChannel for $target (skipped)" \
|
|
673
|
+
"Prerequisite service-omni-base-settings-configure did not succeed; skipping ServiceChannel deploy for $target."
|
|
674
|
+
set_flag SC_OK "$target" "false"
|
|
675
|
+
else
|
|
676
|
+
if run_skill "service-omni-service-channel-configure" \
|
|
677
|
+
"service-omni-service-channel-configure-${target}" \
|
|
678
|
+
"ServiceChannel for $target (Metadata deploy)" \
|
|
679
|
+
'.status' \
|
|
680
|
+
deploy-and-report.sh "$ORG" "$target"; then
|
|
681
|
+
set_flag SC_OK "$target" "true"
|
|
682
|
+
else
|
|
683
|
+
set_flag SC_OK "$target" "false"
|
|
684
|
+
fi
|
|
685
|
+
fi
|
|
686
|
+
done
|
|
687
|
+
|
|
688
|
+
# ---- QRC (per target: Case + VoiceCall) ----
|
|
689
|
+
# Each routing-thread target gets its own QRC; the leaf derives naming from QRC_ROUTING_TARGET.
|
|
690
|
+
for target in "${ROUTING_TARGETS[@]}"; do
|
|
691
|
+
set_flag QRC_OK "$target" "true"
|
|
692
|
+
target_has_routing_thread "$target" || continue
|
|
693
|
+
if ! qrc_dn="$(resolve_qrc_dn_for_target "$target")"; then
|
|
694
|
+
record_dependency_skip "service-omni-queue-routing-config-deploy" \
|
|
695
|
+
"service-omni-queue-routing-config-deploy-${target}" \
|
|
696
|
+
"QueueRoutingConfig resolution for $target (blocked)" \
|
|
697
|
+
"Could not safely resolve the existing or requested QueueRoutingConfig DeveloperName for $target."
|
|
698
|
+
set_flag QRC_OK "$target" "false"
|
|
699
|
+
continue
|
|
700
|
+
fi
|
|
701
|
+
set_flag QRC_DN "$target" "$qrc_dn"
|
|
702
|
+
if [ "$MODE" = "plan" ]; then
|
|
703
|
+
if [ "$BASE_ENABLED" = "true" ]; then
|
|
704
|
+
QRC_ROUTING_TARGET="$target" QRC_DEVELOPER_NAME="$qrc_dn" run_skill "service-omni-queue-routing-config-deploy" \
|
|
705
|
+
"service-omni-queue-routing-config-deploy-${target}" \
|
|
706
|
+
"$qrc_dn (plan detect)" \
|
|
707
|
+
'.status' \
|
|
708
|
+
upsert-and-report.sh "$ORG" || set_flag QRC_OK "$target" "false"
|
|
709
|
+
else
|
|
710
|
+
record_plan_pending "service-omni-queue-routing-config-deploy" \
|
|
711
|
+
"service-omni-queue-routing-config-deploy-${target}" \
|
|
712
|
+
"$qrc_dn (pending)" \
|
|
713
|
+
"Omni-Channel is not yet enabled; the QueueRoutingConfig for $target will be created after base settings."
|
|
714
|
+
fi
|
|
715
|
+
elif [ "$BASE_OK" != "true" ]; then
|
|
716
|
+
record_dependency_skip "service-omni-queue-routing-config-deploy" \
|
|
717
|
+
"service-omni-queue-routing-config-deploy-${target}" \
|
|
718
|
+
"$qrc_dn (skipped)" \
|
|
719
|
+
"Prerequisite service-omni-base-settings-configure did not succeed; skipping QueueRoutingConfig upsert for $target."
|
|
720
|
+
set_flag QRC_OK "$target" "false"
|
|
721
|
+
else
|
|
722
|
+
QRC_ROUTING_TARGET="$target" QRC_DEVELOPER_NAME="$qrc_dn" run_skill "service-omni-queue-routing-config-deploy" \
|
|
723
|
+
"service-omni-queue-routing-config-deploy-${target}" \
|
|
724
|
+
"$qrc_dn (Data API upsert)" \
|
|
725
|
+
'.status' \
|
|
726
|
+
upsert-and-report.sh "$ORG" \
|
|
727
|
+
|| set_flag QRC_OK "$target" "false"
|
|
728
|
+
fi
|
|
729
|
+
done
|
|
730
|
+
|
|
731
|
+
# ---- Queue (per target) ----
|
|
732
|
+
# Needs base settings; routing-thread targets also need their QRC first (the queue binds to it).
|
|
733
|
+
# --create-if-missing builds + binds a queue on fresh orgs instead of hard-blocking.
|
|
734
|
+
for target in "${ROUTING_TARGETS[@]}"; do
|
|
735
|
+
set_flag QUEUE_OK "$target" "true"
|
|
736
|
+
qrc_dn="$(get_flag QRC_DN "$target" "$(qrc_dn_for_target "$target")")"
|
|
737
|
+
queue_override="$(queue_override_for_target "$target")"
|
|
738
|
+
in_thread="false"; target_has_routing_thread "$target" && in_thread="true"
|
|
739
|
+
if [ "$MODE" = "plan" ]; then
|
|
740
|
+
if [ "$BASE_ENABLED" != "true" ]; then
|
|
741
|
+
record_plan_pending "service-omni-queue-deploy" \
|
|
742
|
+
"service-omni-queue-deploy-${target}" \
|
|
743
|
+
"Queue verify+align for $target (pending)" \
|
|
744
|
+
"Omni-Channel is not yet enabled; the queue verify+align for $target will run after base settings."
|
|
745
|
+
set_flag QUEUE_OK "$target" "false"
|
|
746
|
+
elif [ "$in_thread" = "true" ] && [ "$(get_flag QRC_OK "$target" true)" != "true" ]; then
|
|
747
|
+
record_plan_pending "service-omni-queue-deploy" \
|
|
748
|
+
"service-omni-queue-deploy-${target}" \
|
|
749
|
+
"Queue verify+align for $target (pending)" \
|
|
750
|
+
"QueueRoutingConfig is not yet present; the $target queue create/align will run after it is created."
|
|
751
|
+
set_flag QUEUE_OK "$target" "false"
|
|
752
|
+
elif [ "$in_thread" = "true" ]; then
|
|
753
|
+
run_skill "service-omni-queue-deploy" \
|
|
754
|
+
"service-omni-queue-deploy-${target}" \
|
|
755
|
+
"Queue create+align for $target (plan detect)" \
|
|
756
|
+
'.status' \
|
|
757
|
+
verify-and-align.sh "$ORG" "$target" "$qrc_dn" "$queue_override" --create-if-missing \
|
|
758
|
+
|| set_flag QUEUE_OK "$target" "false"
|
|
759
|
+
else
|
|
760
|
+
run_skill "service-omni-queue-deploy" \
|
|
761
|
+
"service-omni-queue-deploy-${target}" \
|
|
762
|
+
"Queue verify+align for $target (plan detect)" \
|
|
763
|
+
'.status' \
|
|
764
|
+
verify-and-align.sh "$ORG" "$target" "" "$queue_override" \
|
|
765
|
+
|| set_flag QUEUE_OK "$target" "false"
|
|
766
|
+
fi
|
|
767
|
+
elif [ "$BASE_OK" != "true" ]; then
|
|
768
|
+
record_dependency_skip "service-omni-queue-deploy" \
|
|
769
|
+
"service-omni-queue-deploy-${target}" \
|
|
770
|
+
"Queue verify+align for $target (skipped)" \
|
|
771
|
+
"Prerequisite service-omni-base-settings-configure did not succeed; skipping queue verify+align for $target."
|
|
772
|
+
set_flag QUEUE_OK "$target" "false"
|
|
773
|
+
elif [ "$in_thread" = "true" ] && [ "$(get_flag QRC_OK "$target" true)" != "true" ]; then
|
|
774
|
+
record_dependency_skip "service-omni-queue-deploy" \
|
|
775
|
+
"service-omni-queue-deploy-${target}" \
|
|
776
|
+
"Queue verify+align for $target (skipped)" \
|
|
777
|
+
"Prerequisite service-omni-queue-routing-config-deploy did not succeed for $target; cannot bind the queue to $qrc_dn."
|
|
778
|
+
set_flag QUEUE_OK "$target" "false"
|
|
779
|
+
else
|
|
780
|
+
if [ "$in_thread" = "true" ]; then
|
|
781
|
+
run_skill "service-omni-queue-deploy" \
|
|
782
|
+
"service-omni-queue-deploy-${target}" \
|
|
783
|
+
"Queue create+align for $target (Metadata + Data API)" \
|
|
784
|
+
'.status' \
|
|
785
|
+
verify-and-align.sh "$ORG" "$target" "$qrc_dn" "$queue_override" --create-if-missing \
|
|
786
|
+
|| set_flag QUEUE_OK "$target" "false"
|
|
787
|
+
else
|
|
788
|
+
run_skill "service-omni-queue-deploy" \
|
|
789
|
+
"service-omni-queue-deploy-${target}" \
|
|
790
|
+
"Queue verify+align for $target (Data API)" \
|
|
791
|
+
'.status' \
|
|
792
|
+
verify-and-align.sh "$ORG" "$target" "" "$queue_override" \
|
|
793
|
+
|| set_flag QUEUE_OK "$target" "false"
|
|
794
|
+
fi
|
|
795
|
+
fi
|
|
796
|
+
done
|
|
797
|
+
|
|
798
|
+
# ---- Queue members (per target - chained to queue-deploy's discovered queue DN) ----
|
|
799
|
+
# Needs both the queue (queue-deploy ok) and the agent users to exist.
|
|
800
|
+
for target in "${ROUTING_TARGETS[@]}"; do
|
|
801
|
+
if [ "$MODE" = "plan" ]; then
|
|
802
|
+
if [ "$(get_flag QUEUE_OK "$target" true)" != "true" ]; then
|
|
803
|
+
record_plan_pending "service-omni-queue-members-assign" \
|
|
804
|
+
"service-omni-queue-members-assign-${target}" \
|
|
805
|
+
"Queue members for $target (pending)" \
|
|
806
|
+
"The $target queue is not yet in place; member binding will run after the queue is aligned."
|
|
807
|
+
continue
|
|
808
|
+
fi
|
|
809
|
+
if [ "$AGENT_USERS_PRESENT" != "true" ]; then
|
|
810
|
+
record_plan_pending "service-omni-queue-members-assign" \
|
|
811
|
+
"service-omni-queue-members-assign-${target}" \
|
|
812
|
+
"Queue members for $target (pending)" \
|
|
813
|
+
"Agent users are not yet created; member binding will run after they exist."
|
|
814
|
+
continue
|
|
815
|
+
fi
|
|
816
|
+
q_artifact="$ARTIFACTS_DIR/service-omni-queue-deploy-${target}.json"
|
|
817
|
+
queue_dn=""
|
|
818
|
+
[ -f "$q_artifact" ] && queue_dn=$(jq -r '.developer_name // ""' "$q_artifact" 2>/dev/null || true)
|
|
819
|
+
if [ -z "$queue_dn" ] || [ "$queue_dn" = "null" ] || ! [[ "$queue_dn" =~ ^[A-Za-z0-9_]{1,80}$ ]]; then
|
|
820
|
+
record_plan_pending "service-omni-queue-members-assign" \
|
|
821
|
+
"service-omni-queue-members-assign-${target}" \
|
|
822
|
+
"Queue members for $target (pending)" \
|
|
823
|
+
"Queue DeveloperName was not resolved in plan; member binding will run once the queue is confirmed."
|
|
824
|
+
continue
|
|
825
|
+
fi
|
|
826
|
+
run_skill "service-omni-queue-members-assign" \
|
|
827
|
+
"service-omni-queue-members-assign-${target}" \
|
|
828
|
+
"Queue members for $target (plan detect, queue $queue_dn)" \
|
|
829
|
+
'.status' \
|
|
830
|
+
verify-and-bind.sh "$ORG" "$queue_dn" "$COUNT" \
|
|
831
|
+
|| true
|
|
832
|
+
# Plan: only "reused" means members are already bound; else a --run would bind them first.
|
|
833
|
+
mem_status=$(jq -r '.status // ""' "$ARTIFACTS_DIR/service-omni-queue-members-assign-${target}.json" 2>/dev/null || echo "")
|
|
834
|
+
if [ "$mem_status" = "reused" ]; then
|
|
835
|
+
set_flag QUEUE_MEMBERS_OK "$target" "true"
|
|
836
|
+
else
|
|
837
|
+
set_flag QUEUE_MEMBERS_OK "$target" "false"
|
|
838
|
+
fi
|
|
839
|
+
continue
|
|
840
|
+
fi
|
|
841
|
+
if [ "$(get_flag QUEUE_OK "$target" true)" != "true" ]; then
|
|
842
|
+
record_dependency_skip "service-omni-queue-members-assign" \
|
|
843
|
+
"service-omni-queue-members-assign-${target}" \
|
|
844
|
+
"Queue members for $target (skipped)" \
|
|
845
|
+
"Prerequisite service-omni-queue-deploy did not succeed for $target; skipping member binding."
|
|
846
|
+
continue
|
|
847
|
+
fi
|
|
848
|
+
if [ "$AGENT_USERS_OK" != "true" ]; then
|
|
849
|
+
record_dependency_skip "service-omni-queue-members-assign" \
|
|
850
|
+
"service-omni-queue-members-assign-${target}" \
|
|
851
|
+
"Queue members for $target (skipped)" \
|
|
852
|
+
"Prerequisite service-omni-agent-users-create did not succeed; no agent users to bind as queue members."
|
|
853
|
+
continue
|
|
854
|
+
fi
|
|
855
|
+
|
|
856
|
+
q_artifact="$ARTIFACTS_DIR/service-omni-queue-deploy-${target}.json"
|
|
857
|
+
queue_dn=""
|
|
858
|
+
if [ -f "$q_artifact" ]; then
|
|
859
|
+
queue_dn=$(jq -r '.developer_name // ""' "$q_artifact" 2>/dev/null || true)
|
|
860
|
+
fi
|
|
861
|
+
|
|
862
|
+
if [ -z "$queue_dn" ] || [ "$queue_dn" = "null" ]; then
|
|
863
|
+
printf " [ 0s] %-55s → skipped (queue not resolved)\n" "service-omni-queue-members-assign-${target}" >&2
|
|
864
|
+
SKILL_RESULTS=$(echo "$SKILL_RESULTS" | jq -c \
|
|
865
|
+
--arg name "service-omni-queue-members-assign" \
|
|
866
|
+
--arg key "service-omni-queue-members-assign-${target}" \
|
|
867
|
+
--arg desc "Queue members for $target (skipped)" \
|
|
868
|
+
'. + [{name: $name, invocation_key: $key, description: $desc, status: "skipped", color: "green", elapsed_sec: 0, artifact_path: null, exit_code: 0}]')
|
|
869
|
+
continue
|
|
870
|
+
fi
|
|
871
|
+
|
|
872
|
+
if ! [[ "$queue_dn" =~ ^[A-Za-z0-9_]{1,80}$ ]]; then
|
|
873
|
+
printf " [ 0s] %-55s → blocked (invalid queue_dn: %q)\n" "service-omni-queue-members-assign-${target}" "$queue_dn" >&2
|
|
874
|
+
SKILL_RESULTS=$(echo "$SKILL_RESULTS" | jq -c \
|
|
875
|
+
--arg name "service-omni-queue-members-assign" \
|
|
876
|
+
--arg key "service-omni-queue-members-assign-${target}" \
|
|
877
|
+
--arg desc "Queue members for $target (blocked: bad DN)" \
|
|
878
|
+
'. + [{name: $name, invocation_key: $key, description: $desc, status: "blocked", color: "red", elapsed_sec: 0, artifact_path: null, exit_code: 1}]')
|
|
879
|
+
OVERALL_STATUS="red"
|
|
880
|
+
if [ -z "$BLOCKING_SKILL" ]; then BLOCKING_SKILL="service-omni-queue-members-assign-${target}"; fi
|
|
881
|
+
continue
|
|
882
|
+
fi
|
|
883
|
+
|
|
884
|
+
run_skill "service-omni-queue-members-assign" \
|
|
885
|
+
"service-omni-queue-members-assign-${target}" \
|
|
886
|
+
"Queue members bind to $queue_dn for $target" \
|
|
887
|
+
'.status' \
|
|
888
|
+
verify-and-bind.sh "$ORG" "$queue_dn" "$COUNT" \
|
|
889
|
+
|| true
|
|
890
|
+
# Record whether member binding fully succeeded; a partial bind blocks the routing-flow gate below.
|
|
891
|
+
mem_status=$(jq -r '.status // ""' "$ARTIFACTS_DIR/service-omni-queue-members-assign-${target}.json" 2>/dev/null || echo "")
|
|
892
|
+
case "$mem_status" in
|
|
893
|
+
bound|reused) set_flag QUEUE_MEMBERS_OK "$target" "true" ;;
|
|
894
|
+
*) set_flag QUEUE_MEMBERS_OK "$target" "false" ;;
|
|
895
|
+
esac
|
|
896
|
+
done
|
|
897
|
+
|
|
898
|
+
# Resolve provisioned and reused agent usernames once. SkillsBased routing needs these bindings before
|
|
899
|
+
# the WorkSkillRouting rule and routing flow are activated; rep-experience stages reuse the same list.
|
|
900
|
+
REP_AGENT_USERNAMES=""
|
|
901
|
+
au_art="$ARTIFACTS_DIR/service-omni-agent-users-create.json"
|
|
902
|
+
if [ -f "$au_art" ]; then
|
|
903
|
+
REP_AGENT_USERNAMES=$(jq -r '
|
|
904
|
+
[ (.created_users // []), (.create.created_users // []), (.reused_users // []),
|
|
905
|
+
(.detect.existing_users // []), (.users // []) ]
|
|
906
|
+
| add // [] | map(.username // empty) | unique | join(",")
|
|
907
|
+
' "$au_art" 2>/dev/null || echo "")
|
|
908
|
+
fi
|
|
909
|
+
|
|
910
|
+
# ---- SkillsBased prerequisites ----
|
|
911
|
+
# The Skill/agent bindings and WorkSkillRouting rule must exist before the trigger flow is activated.
|
|
912
|
+
# Otherwise an immediate record insert can produce a PSR with no SkillRequirement rows.
|
|
913
|
+
SBR_READY="true"
|
|
914
|
+
WSR_READY="true"
|
|
915
|
+
if [ "$ROUTING_TYPE" = "SkillsBased" ]; then
|
|
916
|
+
SBR_READY="false"
|
|
917
|
+
WSR_READY="false"
|
|
918
|
+
|
|
919
|
+
if [ "$MODE" = "plan" ]; then
|
|
920
|
+
if [ "$BASE_ENABLED" = "true" ] && [ "$AGENT_USERS_PRESENT" = "true" ] && [ -n "$REP_AGENT_USERNAMES" ]; then
|
|
921
|
+
run_skill "service-omni-skills-based-routing-configure" \
|
|
922
|
+
"service-omni-skills-based-routing-configure" \
|
|
923
|
+
"Skills-based routing for $SBR_SKILL (plan detect)" \
|
|
924
|
+
'.status' \
|
|
925
|
+
configure-and-report.sh "$ORG" "$SBR_SKILL" "$REP_AGENT_USERNAMES" || true
|
|
926
|
+
else
|
|
927
|
+
record_plan_pending "service-omni-skills-based-routing-configure" \
|
|
928
|
+
"service-omni-skills-based-routing-configure" \
|
|
929
|
+
"Skills-based routing for $SBR_SKILL (pending)" \
|
|
930
|
+
"Skills-based routing will run after Omni is enabled and agent users exist."
|
|
931
|
+
fi
|
|
932
|
+
elif [ "$BASE_OK" != "true" ] || [ "$AGENT_USERS_OK" != "true" ]; then
|
|
933
|
+
record_dependency_skip "service-omni-skills-based-routing-configure" \
|
|
934
|
+
"service-omni-skills-based-routing-configure" \
|
|
935
|
+
"Skills-based routing (skipped)" \
|
|
936
|
+
"Prerequisites (base settings + agent users) did not succeed; skipping agent bindings for $SBR_SKILL."
|
|
937
|
+
elif [ -z "$REP_AGENT_USERNAMES" ]; then
|
|
938
|
+
record_dependency_skip "service-omni-skills-based-routing-configure" \
|
|
939
|
+
"service-omni-skills-based-routing-configure" \
|
|
940
|
+
"Skills-based routing (skipped)" \
|
|
941
|
+
"No active agent usernames were resolved; refusing to activate SkillsBased routing without a skilled agent."
|
|
942
|
+
else
|
|
943
|
+
run_skill "service-omni-skills-based-routing-configure" \
|
|
944
|
+
"service-omni-skills-based-routing-configure" \
|
|
945
|
+
"Skills-based routing bind $SBR_SKILL" \
|
|
946
|
+
'.status' \
|
|
947
|
+
configure-and-report.sh "$ORG" "$SBR_SKILL" "$REP_AGENT_USERNAMES" || true
|
|
948
|
+
sbr_status=$(jq -r '.status // ""' "$ARTIFACTS_DIR/service-omni-skills-based-routing-configure.json" 2>/dev/null || echo "")
|
|
949
|
+
case "$sbr_status" in configured|reused) SBR_READY="true" ;; esac
|
|
950
|
+
fi
|
|
951
|
+
|
|
952
|
+
WSR_LABEL="${OMNI_WSR_LABEL:-}"
|
|
953
|
+
if [ "$MODE" = "plan" ]; then
|
|
954
|
+
if [ "$BASE_ENABLED" = "true" ]; then
|
|
955
|
+
run_skill "service-omni-work-skill-routing-configure" \
|
|
956
|
+
"service-omni-work-skill-routing-configure" \
|
|
957
|
+
"Field-based skill routing $OMNI_WSR_ENTITY.$OMNI_WSR_FIELD=$OMNI_WSR_VALUE -> $OMNI_WSR_SKILL (plan detect)" \
|
|
958
|
+
'.status' \
|
|
959
|
+
configure-and-report.sh "$ORG" "$OMNI_WSR_ENTITY" "$OMNI_WSR_FIELD" "$OMNI_WSR_SKILL" "$OMNI_WSR_VALUE" "$WSR_LABEL" || true
|
|
960
|
+
else
|
|
961
|
+
record_plan_pending "service-omni-work-skill-routing-configure" \
|
|
962
|
+
"service-omni-work-skill-routing-configure" \
|
|
963
|
+
"Field-based skill routing (pending)" \
|
|
964
|
+
"Omni-Channel is not yet enabled; the WorkSkillRouting rule will be authored after base settings."
|
|
965
|
+
fi
|
|
966
|
+
elif [ "$SBR_READY" != "true" ]; then
|
|
967
|
+
record_dependency_skip "service-omni-work-skill-routing-configure" \
|
|
968
|
+
"service-omni-work-skill-routing-configure" \
|
|
969
|
+
"Field-based skill routing (skipped)" \
|
|
970
|
+
"Agent skill provisioning did not succeed; refusing to activate a WorkSkillRouting rule with no eligible skilled agents."
|
|
971
|
+
else
|
|
972
|
+
run_skill "service-omni-work-skill-routing-configure" \
|
|
973
|
+
"service-omni-work-skill-routing-configure" \
|
|
974
|
+
"Field-based skill routing $OMNI_WSR_ENTITY.$OMNI_WSR_FIELD=$OMNI_WSR_VALUE -> $OMNI_WSR_SKILL" \
|
|
975
|
+
'.status' \
|
|
976
|
+
configure-and-report.sh "$ORG" "$OMNI_WSR_ENTITY" "$OMNI_WSR_FIELD" "$OMNI_WSR_SKILL" "$OMNI_WSR_VALUE" "$WSR_LABEL" || true
|
|
977
|
+
wsr_status=$(jq -r '.status // ""' "$ARTIFACTS_DIR/service-omni-work-skill-routing-configure.json" 2>/dev/null || echo "")
|
|
978
|
+
case "$wsr_status" in created|updated|reused) WSR_READY="true" ;; esac
|
|
979
|
+
fi
|
|
980
|
+
fi
|
|
981
|
+
|
|
982
|
+
# ---- Routing flow (per routing-thread target: Case + VoiceCall) ----
|
|
983
|
+
# Record-triggered flow, last leg of the thread; runs only after queue + members exist. Passes the
|
|
984
|
+
# discovered ServiceChannel/Queue/QRC DeveloperNames so the flow binds the same resources, not its
|
|
985
|
+
# defaults. OMNI_RUNTIME_PROOF=1 fires the trigger (fail-soft); OMNI_RUNTIME_PROOF_REQUIRED=1 blocks on failure.
|
|
986
|
+
RF_FLAGS=(--routing-type "$ROUTING_TYPE")
|
|
987
|
+
[ "$ROUTING_TYPE" != "SkillsBased" ] || RF_FLAGS+=(--skill-option "$SKILL_OPTION")
|
|
988
|
+
[ "${OMNI_RUNTIME_PROOF:-0}" = "1" ] && RF_FLAGS+=(--runtime-proof)
|
|
989
|
+
[ "${OMNI_RUNTIME_PROOF_REQUIRED:-0}" = "1" ] && RF_FLAGS+=(--require-proof)
|
|
990
|
+
for target in "${ROUTING_TARGETS[@]}"; do
|
|
991
|
+
target_has_routing_thread "$target" || continue
|
|
992
|
+
rf_qrc="$(get_flag QRC_DN "$target" "$(qrc_dn_for_target "$target")")"
|
|
993
|
+
# Resolve the queue + channel DeveloperNames this run established, from artifacts.
|
|
994
|
+
rf_queue=""
|
|
995
|
+
q_art="$ARTIFACTS_DIR/service-omni-queue-deploy-${target}.json"
|
|
996
|
+
[ -f "$q_art" ] && rf_queue=$(jq -r '.developer_name // ""' "$q_art" 2>/dev/null || true)
|
|
997
|
+
rf_channel=""
|
|
998
|
+
sc_art="$ARTIFACTS_DIR/service-omni-service-channel-configure-${target}.json"
|
|
999
|
+
[ -f "$sc_art" ] && rf_channel=$(jq -r '.channel_developer_name // ""' "$sc_art" 2>/dev/null || true)
|
|
1000
|
+
if [ "$MODE" = "plan" ]; then
|
|
1001
|
+
if [ "$(get_flag QUEUE_OK "$target" true)" != "true" ]; then
|
|
1002
|
+
record_plan_pending "service-omni-routing-flow-deploy" \
|
|
1003
|
+
"service-omni-routing-flow-deploy-${target}" \
|
|
1004
|
+
"Routing flow for $target (pending)" \
|
|
1005
|
+
"The $target queue is not yet in place; the routing flow will deploy after the queue + members."
|
|
1006
|
+
elif [ "$(get_flag QUEUE_MEMBERS_OK "$target" false)" != "true" ]; then
|
|
1007
|
+
record_plan_pending "service-omni-routing-flow-deploy" \
|
|
1008
|
+
"service-omni-routing-flow-deploy-${target}" \
|
|
1009
|
+
"Routing flow for $target (pending)" \
|
|
1010
|
+
"The $target queue has no members bound yet; the routing flow will deploy after queue members are assigned (routing to an empty queue is refused)."
|
|
1011
|
+
elif [ "$ROUTING_TYPE" = "SkillsBased" ]; then
|
|
1012
|
+
record_plan_pending "service-omni-routing-flow-deploy" \
|
|
1013
|
+
"service-omni-routing-flow-deploy-${target}" \
|
|
1014
|
+
"SkillsBased routing flow for $target (pending)" \
|
|
1015
|
+
"The Skill bindings and WorkSkillRouting rule will be created before the SkillsBased trigger flow is activated."
|
|
1016
|
+
else
|
|
1017
|
+
SERVICE_CHANNEL_DEVELOPER_NAME="$rf_channel" QUEUE_DEVELOPER_NAME="$rf_queue" ROUTING_CONFIG_DEVELOPER_NAME="$rf_qrc" \
|
|
1018
|
+
run_skill "service-omni-routing-flow-deploy" \
|
|
1019
|
+
"service-omni-routing-flow-deploy-${target}" \
|
|
1020
|
+
"Routing flow for $target (plan detect)" \
|
|
1021
|
+
'.status' \
|
|
1022
|
+
deploy-and-report.sh "$ORG" --target "$target" --trigger "${RF_FLAGS[@]}" \
|
|
1023
|
+
|| true
|
|
1024
|
+
fi
|
|
1025
|
+
elif [ "$(get_flag QUEUE_OK "$target" true)" != "true" ]; then
|
|
1026
|
+
record_dependency_skip "service-omni-routing-flow-deploy" \
|
|
1027
|
+
"service-omni-routing-flow-deploy-${target}" \
|
|
1028
|
+
"Routing flow for $target (skipped)" \
|
|
1029
|
+
"Prerequisite service-omni-queue-deploy did not succeed for $target; the routing flow has no queue to route to."
|
|
1030
|
+
elif [ "$(get_flag QUEUE_MEMBERS_OK "$target" false)" != "true" ]; then
|
|
1031
|
+
# Never route to an empty queue: members must be fully bound (bound/reused) first.
|
|
1032
|
+
record_dependency_skip "service-omni-routing-flow-deploy" \
|
|
1033
|
+
"service-omni-routing-flow-deploy-${target}" \
|
|
1034
|
+
"Routing flow for $target (skipped)" \
|
|
1035
|
+
"Prerequisite service-omni-queue-members-assign did not fully succeed for $target; refusing to deploy the routing flow to a queue with no bound members."
|
|
1036
|
+
elif [ "$ROUTING_TYPE" = "SkillsBased" ] && { [ "$SBR_READY" != "true" ] || [ "$WSR_READY" != "true" ]; }; then
|
|
1037
|
+
record_dependency_skip "service-omni-routing-flow-deploy" \
|
|
1038
|
+
"service-omni-routing-flow-deploy-${target}" \
|
|
1039
|
+
"SkillsBased routing flow for $target (skipped)" \
|
|
1040
|
+
"SkillsBased prerequisites did not complete: agent skill bindings ready=$SBR_READY, WorkSkillRouting ready=$WSR_READY. Refusing to activate a flow that could create a PSR without SkillRequirement rows."
|
|
1041
|
+
else
|
|
1042
|
+
SERVICE_CHANNEL_DEVELOPER_NAME="$rf_channel" QUEUE_DEVELOPER_NAME="$rf_queue" ROUTING_CONFIG_DEVELOPER_NAME="$rf_qrc" \
|
|
1043
|
+
run_skill "service-omni-routing-flow-deploy" \
|
|
1044
|
+
"service-omni-routing-flow-deploy-${target}" \
|
|
1045
|
+
"Routing flow for $target (record-triggered deploy)" \
|
|
1046
|
+
'.status' \
|
|
1047
|
+
deploy-and-report.sh "$ORG" --target "$target" --trigger "${RF_FLAGS[@]}" \
|
|
1048
|
+
|| true
|
|
1049
|
+
fi
|
|
1050
|
+
done
|
|
1051
|
+
|
|
1052
|
+
# ---- Presence status (per target - chained to service-channel's discovered channel DN) ----
|
|
1053
|
+
# Needs base settings + the target's ServiceChannel (the Available_<X> status binds to it).
|
|
1054
|
+
for target in "${ROUTING_TARGETS[@]}"; do
|
|
1055
|
+
if [ "$MODE" = "plan" ]; then
|
|
1056
|
+
if [ "$BASE_ENABLED" != "true" ]; then
|
|
1057
|
+
record_plan_pending "service-omni-presence-status-deploy" \
|
|
1058
|
+
"service-omni-presence-status-deploy-${target}" \
|
|
1059
|
+
"Presence statuses for $target (pending)" \
|
|
1060
|
+
"Omni-Channel is not yet enabled; presence statuses for $target will deploy after base settings."
|
|
1061
|
+
else
|
|
1062
|
+
sc_artifact="$ARTIFACTS_DIR/service-omni-service-channel-configure-${target}.json"
|
|
1063
|
+
channel_dn=""
|
|
1064
|
+
[ -f "$sc_artifact" ] && channel_dn=$(jq -r '.channel_developer_name // ""' "$sc_artifact" 2>/dev/null || true)
|
|
1065
|
+
if [ -n "$channel_dn" ] && [[ "$channel_dn" =~ ^[A-Za-z0-9_]{1,80}$ ]]; then
|
|
1066
|
+
run_skill "service-omni-presence-status-deploy" \
|
|
1067
|
+
"service-omni-presence-status-deploy-${target}" \
|
|
1068
|
+
"Presence statuses for $target (plan detect, channel $channel_dn)" \
|
|
1069
|
+
'.status' \
|
|
1070
|
+
deploy-and-report.sh "$ORG" "$target" "$channel_dn" || true
|
|
1071
|
+
else
|
|
1072
|
+
run_skill "service-omni-presence-status-deploy" \
|
|
1073
|
+
"service-omni-presence-status-deploy-${target}" \
|
|
1074
|
+
"Presence statuses for $target (plan detect, default channel)" \
|
|
1075
|
+
'.status' \
|
|
1076
|
+
deploy-and-report.sh "$ORG" "$target" || true
|
|
1077
|
+
fi
|
|
1078
|
+
fi
|
|
1079
|
+
elif [ "$BASE_OK" != "true" ]; then
|
|
1080
|
+
record_dependency_skip "service-omni-presence-status-deploy" \
|
|
1081
|
+
"service-omni-presence-status-deploy-${target}" \
|
|
1082
|
+
"Presence statuses for $target (skipped)" \
|
|
1083
|
+
"Prerequisite service-omni-base-settings-configure did not succeed; skipping presence-status deploy for $target."
|
|
1084
|
+
elif [ "$(get_flag SC_OK "$target" true)" != "true" ]; then
|
|
1085
|
+
record_dependency_skip "service-omni-presence-status-deploy" \
|
|
1086
|
+
"service-omni-presence-status-deploy-${target}" \
|
|
1087
|
+
"Presence statuses for $target (skipped)" \
|
|
1088
|
+
"Prerequisite service-omni-service-channel-configure did not succeed for $target; the Available_$target status has no channel to bind to."
|
|
1089
|
+
else
|
|
1090
|
+
sc_artifact="$ARTIFACTS_DIR/service-omni-service-channel-configure-${target}.json"
|
|
1091
|
+
channel_dn=""
|
|
1092
|
+
if [ -f "$sc_artifact" ]; then
|
|
1093
|
+
channel_dn=$(jq -r '.channel_developer_name // ""' "$sc_artifact" 2>/dev/null || true)
|
|
1094
|
+
fi
|
|
1095
|
+
if [ -n "$channel_dn" ] && [[ "$channel_dn" =~ ^[A-Za-z0-9_]{1,80}$ ]]; then
|
|
1096
|
+
run_skill "service-omni-presence-status-deploy" \
|
|
1097
|
+
"service-omni-presence-status-deploy-${target}" \
|
|
1098
|
+
"Presence statuses for $target (bound to $channel_dn)" \
|
|
1099
|
+
'.status' \
|
|
1100
|
+
deploy-and-report.sh "$ORG" "$target" "$channel_dn" \
|
|
1101
|
+
|| true
|
|
1102
|
+
else
|
|
1103
|
+
run_skill "service-omni-presence-status-deploy" \
|
|
1104
|
+
"service-omni-presence-status-deploy-${target}" \
|
|
1105
|
+
"Presence statuses for $target (channel DN unresolved, using skill default)" \
|
|
1106
|
+
'.status' \
|
|
1107
|
+
deploy-and-report.sh "$ORG" "$target" \
|
|
1108
|
+
|| true
|
|
1109
|
+
fi
|
|
1110
|
+
fi
|
|
1111
|
+
done
|
|
1112
|
+
|
|
1113
|
+
# Optional rep-experience stages (opt-in OMNI_REP_EXPERIENCE=1, default off): presence-user-config
|
|
1114
|
+
# and sidebar-enable. SkillsBased prerequisites run earlier whenever SkillsBased routing is requested.
|
|
1115
|
+
if [ "${OMNI_REP_EXPERIENCE:-0}" = "1" ]; then
|
|
1116
|
+
# ---- Presence user config (decline + decline reason + ACW), once ----
|
|
1117
|
+
if [ "$MODE" = "plan" ]; then
|
|
1118
|
+
if [ "$BASE_ENABLED" = "true" ]; then
|
|
1119
|
+
AGENT_USERNAMES_CSV="$REP_AGENT_USERNAMES" run_skill "service-omni-presence-user-config-deploy" \
|
|
1120
|
+
"service-omni-presence-user-config-deploy" \
|
|
1121
|
+
"Presence user config (plan detect)" \
|
|
1122
|
+
'.status' \
|
|
1123
|
+
deploy-and-report.sh "$ORG" || true
|
|
1124
|
+
else
|
|
1125
|
+
record_plan_pending "service-omni-presence-user-config-deploy" \
|
|
1126
|
+
"service-omni-presence-user-config-deploy" \
|
|
1127
|
+
"Presence user config (pending)" \
|
|
1128
|
+
"Omni-Channel is not yet enabled; the presence user config will deploy after base settings."
|
|
1129
|
+
fi
|
|
1130
|
+
elif [ "$BASE_OK" != "true" ]; then
|
|
1131
|
+
record_dependency_skip "service-omni-presence-user-config-deploy" \
|
|
1132
|
+
"service-omni-presence-user-config-deploy" \
|
|
1133
|
+
"Presence user config (skipped)" \
|
|
1134
|
+
"Prerequisite service-omni-base-settings-configure did not succeed; skipping presence user config."
|
|
1135
|
+
else
|
|
1136
|
+
AGENT_USERNAMES_CSV="$REP_AGENT_USERNAMES" run_skill "service-omni-presence-user-config-deploy" \
|
|
1137
|
+
"service-omni-presence-user-config-deploy" \
|
|
1138
|
+
"Presence user config (Metadata deploy)" \
|
|
1139
|
+
'.status' \
|
|
1140
|
+
deploy-and-report.sh "$ORG" \
|
|
1141
|
+
|| true
|
|
1142
|
+
fi
|
|
1143
|
+
|
|
1144
|
+
# ---- Omni sidebar ---- (empty OMNI_CONSOLE_APP => leaf auto-detects the console app)
|
|
1145
|
+
SIDEBAR_APP="${OMNI_CONSOLE_APP:-}"
|
|
1146
|
+
if [ "$MODE" = "plan" ]; then
|
|
1147
|
+
if [ "$BASE_ENABLED" = "true" ]; then
|
|
1148
|
+
run_skill "service-omni-sidebar-configure" \
|
|
1149
|
+
"service-omni-sidebar-configure" \
|
|
1150
|
+
"Omni sidebar (plan detect)" \
|
|
1151
|
+
'.status' \
|
|
1152
|
+
enable-and-report.sh "$ORG" "$SIDEBAR_APP" || true
|
|
1153
|
+
else
|
|
1154
|
+
record_plan_pending "service-omni-sidebar-configure" \
|
|
1155
|
+
"service-omni-sidebar-configure" \
|
|
1156
|
+
"Omni sidebar (pending)" \
|
|
1157
|
+
"Omni-Channel is not yet enabled; the sidebar will be enabled after base settings."
|
|
1158
|
+
fi
|
|
1159
|
+
elif [ "$BASE_OK" != "true" ]; then
|
|
1160
|
+
record_dependency_skip "service-omni-sidebar-configure" \
|
|
1161
|
+
"service-omni-sidebar-configure" \
|
|
1162
|
+
"Omni sidebar (skipped)" \
|
|
1163
|
+
"Prerequisite service-omni-base-settings-configure did not succeed; skipping sidebar enablement."
|
|
1164
|
+
else
|
|
1165
|
+
run_skill "service-omni-sidebar-configure" \
|
|
1166
|
+
"service-omni-sidebar-configure" \
|
|
1167
|
+
"Omni sidebar (CustomApplication deploy)" \
|
|
1168
|
+
'.status' \
|
|
1169
|
+
enable-and-report.sh "$ORG" "$SIDEBAR_APP" \
|
|
1170
|
+
|| true
|
|
1171
|
+
fi
|
|
1172
|
+
fi
|
|
1173
|
+
|
|
1174
|
+
# ---- Permission set assign (Omni_Agent) ----
|
|
1175
|
+
# Needs agent users to exist.
|
|
1176
|
+
if [ "$MODE" = "plan" ]; then
|
|
1177
|
+
if [ "$AGENT_USERS_PRESENT" = "true" ]; then
|
|
1178
|
+
run_skill "service-omni-permission-set-assign" \
|
|
1179
|
+
"service-omni-permission-set-assign" \
|
|
1180
|
+
"Omni_Agent PSA (plan detect)" \
|
|
1181
|
+
'.status' \
|
|
1182
|
+
verify-and-assign.sh "$ORG" "$COUNT" "Omni_Agent" || true
|
|
1183
|
+
else
|
|
1184
|
+
record_plan_pending "service-omni-permission-set-assign" \
|
|
1185
|
+
"service-omni-permission-set-assign" \
|
|
1186
|
+
"Omni_Agent PSA (pending)" \
|
|
1187
|
+
"Agent users are not yet created; the Omni_Agent permission-set assignment will run after they exist."
|
|
1188
|
+
fi
|
|
1189
|
+
elif [ "$AGENT_USERS_OK" != "true" ]; then
|
|
1190
|
+
record_dependency_skip "service-omni-permission-set-assign" \
|
|
1191
|
+
"service-omni-permission-set-assign" \
|
|
1192
|
+
"Omni_Agent PSA (skipped)" \
|
|
1193
|
+
"Prerequisite service-omni-agent-users-create did not succeed; no agent users to assign Omni_Agent to."
|
|
1194
|
+
else
|
|
1195
|
+
run_skill "service-omni-permission-set-assign" \
|
|
1196
|
+
"service-omni-permission-set-assign" \
|
|
1197
|
+
"Omni_Agent to users (Data API POST)" \
|
|
1198
|
+
'.status' \
|
|
1199
|
+
verify-and-assign.sh "$ORG" "$COUNT" "Omni_Agent" \
|
|
1200
|
+
|| true
|
|
1201
|
+
fi
|
|
1202
|
+
|
|
1203
|
+
# ---- Supervisor users (create N; independent of routing targets) ----
|
|
1204
|
+
# On Service Cloud User (Omni Supervisor is a Service Cloud feature; the Salesforce license pool is
|
|
1205
|
+
# often exhausted on demo orgs). detect-and-create runs a real read-only detector in --plan.
|
|
1206
|
+
run_skill "service-omni-supervisor-users-create" \
|
|
1207
|
+
"service-omni-supervisor-users-create" \
|
|
1208
|
+
"Supervisor users (detect + create when missing)" \
|
|
1209
|
+
'.status' \
|
|
1210
|
+
detect-and-create.sh "$MODE" "$ORG" "$SUPERVISOR_COUNT" "$SUPERVISOR_PROFILE" \
|
|
1211
|
+
|| SUPERVISOR_USERS_OK="false"
|
|
1212
|
+
|
|
1213
|
+
if [ "$MODE" = "plan" ]; then
|
|
1214
|
+
su_art="$ARTIFACTS_DIR/service-omni-supervisor-users-create.json"
|
|
1215
|
+
if [ -f "$su_art" ] && [ "$(jq -r '.status // ""' "$su_art" 2>/dev/null)" = "reused" ]; then
|
|
1216
|
+
SUPERVISOR_USERS_PRESENT="true"
|
|
1217
|
+
fi
|
|
1218
|
+
fi
|
|
1219
|
+
|
|
1220
|
+
# ---- Supervisor permset (ContactCenterSupervisor to supervisor users) ----
|
|
1221
|
+
# Needs supervisor users. Use standard ContactCenterSupervisor, not custom Omni_Supervisor (which
|
|
1222
|
+
# re-declares license-gated perms that fail on standard licenses); the leaf already defaults to it.
|
|
1223
|
+
if [ "$MODE" = "plan" ]; then
|
|
1224
|
+
if [ "$SUPERVISOR_USERS_PRESENT" = "true" ]; then
|
|
1225
|
+
run_skill "service-omni-supervisor-permset-assign" \
|
|
1226
|
+
"service-omni-supervisor-permset-assign" \
|
|
1227
|
+
"ContactCenterSupervisor PSA (plan detect)" \
|
|
1228
|
+
'.status' \
|
|
1229
|
+
verify-and-assign.sh "$ORG" "$SUPERVISOR_COUNT" "ContactCenterSupervisor" || true
|
|
1230
|
+
else
|
|
1231
|
+
record_plan_pending "service-omni-supervisor-permset-assign" \
|
|
1232
|
+
"service-omni-supervisor-permset-assign" \
|
|
1233
|
+
"ContactCenterSupervisor PSA (pending)" \
|
|
1234
|
+
"Supervisor users are not yet created; the ContactCenterSupervisor permission-set assignment will run after they exist."
|
|
1235
|
+
fi
|
|
1236
|
+
elif [ "$SUPERVISOR_USERS_OK" != "true" ]; then
|
|
1237
|
+
record_dependency_skip "service-omni-supervisor-permset-assign" \
|
|
1238
|
+
"service-omni-supervisor-permset-assign" \
|
|
1239
|
+
"ContactCenterSupervisor PSA (skipped)" \
|
|
1240
|
+
"Prerequisite service-omni-supervisor-users-create did not succeed; no supervisor users to assign ContactCenterSupervisor to."
|
|
1241
|
+
else
|
|
1242
|
+
run_skill "service-omni-supervisor-permset-assign" \
|
|
1243
|
+
"service-omni-supervisor-permset-assign" \
|
|
1244
|
+
"ContactCenterSupervisor to supervisor users (Data API POST)" \
|
|
1245
|
+
'.status' \
|
|
1246
|
+
verify-and-assign.sh "$ORG" "$SUPERVISOR_COUNT" "ContactCenterSupervisor" \
|
|
1247
|
+
|| true
|
|
1248
|
+
fi
|
|
1249
|
+
|
|
1250
|
+
# ---- Command Center surface analysis (read-only advisory) ----
|
|
1251
|
+
# Reports whether the org is on the classic Omni Supervisor surface or Command Center V2 (Enhanced
|
|
1252
|
+
# Omni-Channel). Advisory only: this coordinator configures the classic surface; V2 enablement is an
|
|
1253
|
+
# org-preference flip outside the Metadata API. It never writes, so it runs identically in plan and
|
|
1254
|
+
# run and never hard-blocks - ambiguous detection surfaces as yellow, a clean read as green.
|
|
1255
|
+
run_skill "service-omni-command-center-analyze" \
|
|
1256
|
+
"service-omni-command-center-analyze" \
|
|
1257
|
+
"Command Center surface analysis (classic vs V2, read-only)" \
|
|
1258
|
+
'(if .status=="detected" then "ok" else "action_needed" end)' \
|
|
1259
|
+
analyze.sh "$ORG" \
|
|
1260
|
+
|| true
|
|
1261
|
+
|
|
1262
|
+
# ---- Supervisor config (deploy OmniSupervisorConfig binding supervisors + queues) ----
|
|
1263
|
+
# Needs base settings + supervisor users. Pass the actual discovered queue DeveloperNames so it
|
|
1264
|
+
# binds the real (possibly reused) queues, not the skill's canonical defaults.
|
|
1265
|
+
if [ "$MODE" = "plan" ]; then
|
|
1266
|
+
if [ "$BASE_ENABLED" != "true" ]; then
|
|
1267
|
+
record_plan_pending "service-omni-supervisor-config-deploy" \
|
|
1268
|
+
"service-omni-supervisor-config-deploy" \
|
|
1269
|
+
"OmniSupervisorConfig (pending)" \
|
|
1270
|
+
"Omni-Channel is not yet enabled; the OmniSupervisorConfig will deploy after base settings."
|
|
1271
|
+
elif [ "$SUPERVISOR_USERS_PRESENT" != "true" ]; then
|
|
1272
|
+
record_plan_pending "service-omni-supervisor-config-deploy" \
|
|
1273
|
+
"service-omni-supervisor-config-deploy" \
|
|
1274
|
+
"OmniSupervisorConfig (pending)" \
|
|
1275
|
+
"Supervisor users are not yet created; the OmniSupervisorConfig will deploy after they exist."
|
|
1276
|
+
else
|
|
1277
|
+
DISCOVERED_QUEUES_CSV=""
|
|
1278
|
+
for target in "${ROUTING_TARGETS[@]}"; do
|
|
1279
|
+
[ "$(get_flag QUEUE_OK "$target" true)" = "true" ] || continue
|
|
1280
|
+
q_artifact="$ARTIFACTS_DIR/service-omni-queue-deploy-${target}.json"
|
|
1281
|
+
[ -f "$q_artifact" ] || continue
|
|
1282
|
+
qdn=$(jq -r '.developer_name // ""' "$q_artifact" 2>/dev/null || true)
|
|
1283
|
+
if [ -n "$qdn" ] && [ "$qdn" != "null" ] && [[ "$qdn" =~ ^[A-Za-z0-9_]{1,80}$ ]]; then
|
|
1284
|
+
if [ -z "$DISCOVERED_QUEUES_CSV" ]; then
|
|
1285
|
+
DISCOVERED_QUEUES_CSV="$qdn"
|
|
1286
|
+
else
|
|
1287
|
+
DISCOVERED_QUEUES_CSV="$DISCOVERED_QUEUES_CSV,$qdn"
|
|
1288
|
+
fi
|
|
1289
|
+
fi
|
|
1290
|
+
done
|
|
1291
|
+
run_skill "service-omni-supervisor-config-deploy" \
|
|
1292
|
+
"service-omni-supervisor-config-deploy" \
|
|
1293
|
+
"OmniSupervisorConfig (plan detect)" \
|
|
1294
|
+
'.status' \
|
|
1295
|
+
deploy-and-report.sh "$ORG" "$SUPERVISOR_COUNT" "$DISCOVERED_QUEUES_CSV" "" "" "$SUPERVISOR_CONFIG_DN" || true
|
|
1296
|
+
fi
|
|
1297
|
+
elif [ "$BASE_OK" != "true" ]; then
|
|
1298
|
+
record_dependency_skip "service-omni-supervisor-config-deploy" \
|
|
1299
|
+
"service-omni-supervisor-config-deploy" \
|
|
1300
|
+
"OmniSupervisorConfig (skipped)" \
|
|
1301
|
+
"Prerequisite service-omni-base-settings-configure did not succeed; skipping OmniSupervisorConfig deploy."
|
|
1302
|
+
elif [ "$SUPERVISOR_USERS_OK" != "true" ]; then
|
|
1303
|
+
record_dependency_skip "service-omni-supervisor-config-deploy" \
|
|
1304
|
+
"service-omni-supervisor-config-deploy" \
|
|
1305
|
+
"OmniSupervisorConfig (skipped)" \
|
|
1306
|
+
"Prerequisite service-omni-supervisor-users-create did not succeed; no supervisor users to bind into the OmniSupervisorConfig."
|
|
1307
|
+
else
|
|
1308
|
+
# Actual queue DeveloperNames from succeeded queue-deploy artifacts (authoritative over defaults).
|
|
1309
|
+
DISCOVERED_QUEUES_CSV=""
|
|
1310
|
+
for target in "${ROUTING_TARGETS[@]}"; do
|
|
1311
|
+
[ "$(get_flag QUEUE_OK "$target" true)" = "true" ] || continue
|
|
1312
|
+
q_artifact="$ARTIFACTS_DIR/service-omni-queue-deploy-${target}.json"
|
|
1313
|
+
[ -f "$q_artifact" ] || continue
|
|
1314
|
+
qdn=$(jq -r '.developer_name // ""' "$q_artifact" 2>/dev/null || true)
|
|
1315
|
+
if [ -n "$qdn" ] && [ "$qdn" != "null" ] && [[ "$qdn" =~ ^[A-Za-z0-9_]{1,80}$ ]]; then
|
|
1316
|
+
if [ -z "$DISCOVERED_QUEUES_CSV" ]; then
|
|
1317
|
+
DISCOVERED_QUEUES_CSV="$qdn"
|
|
1318
|
+
else
|
|
1319
|
+
DISCOVERED_QUEUES_CSV="$DISCOVERED_QUEUES_CSV,$qdn"
|
|
1320
|
+
fi
|
|
1321
|
+
fi
|
|
1322
|
+
done
|
|
1323
|
+
|
|
1324
|
+
run_skill "service-omni-supervisor-config-deploy" \
|
|
1325
|
+
"service-omni-supervisor-config-deploy" \
|
|
1326
|
+
"OmniSupervisorConfig deploy (Metadata API)" \
|
|
1327
|
+
'.status' \
|
|
1328
|
+
deploy-and-report.sh "$ORG" "$SUPERVISOR_COUNT" "$DISCOVERED_QUEUES_CSV" "" "" "$SUPERVISOR_CONFIG_DN" \
|
|
1329
|
+
|| true
|
|
1330
|
+
fi
|
|
1331
|
+
|
|
1332
|
+
# Derive the supervisor-config disposition (from its artifact) to gate the surface stage below.
|
|
1333
|
+
# plan: PRESENT iff the config already exists (status reused); run: OK iff it now exists.
|
|
1334
|
+
supcfg_art="$ARTIFACTS_DIR/service-omni-supervisor-config-deploy.json"
|
|
1335
|
+
supcfg_status=""
|
|
1336
|
+
SUPERVISOR_CONFIG_RESOLVED_DN="$SUPERVISOR_CONFIG_DN"
|
|
1337
|
+
[ -f "$supcfg_art" ] && supcfg_status=$(jq -r '.status // ""' "$supcfg_art" 2>/dev/null || echo "")
|
|
1338
|
+
if [ -f "$supcfg_art" ]; then
|
|
1339
|
+
artifact_config_dn=$(jq -r '.config_developer_name // ""' "$supcfg_art" 2>/dev/null || echo "")
|
|
1340
|
+
if [[ "$artifact_config_dn" =~ ^[A-Za-z][A-Za-z0-9_]{0,79}$ ]]; then
|
|
1341
|
+
SUPERVISOR_CONFIG_RESOLVED_DN="$artifact_config_dn"
|
|
1342
|
+
fi
|
|
1343
|
+
fi
|
|
1344
|
+
if [ "$MODE" = "plan" ]; then
|
|
1345
|
+
[ "$supcfg_status" = "reused" ] && SUPERVISOR_CONFIG_PRESENT="true"
|
|
1346
|
+
else
|
|
1347
|
+
case "$supcfg_status" in created|updated|reused) SUPERVISOR_CONFIG_OK="true" ;; *) SUPERVISOR_CONFIG_OK="false" ;; esac
|
|
1348
|
+
fi
|
|
1349
|
+
|
|
1350
|
+
# ---- Supervisor surface (Action + Tab companions on the OmniSupervisorConfig) ----
|
|
1351
|
+
# Needs base settings + an OmniSupervisorConfig. Inserts the standard action/tab rows via the Data
|
|
1352
|
+
# API against the parent config's Id - independent of the config metadata file and idempotent.
|
|
1353
|
+
if [ "$MODE" = "plan" ]; then
|
|
1354
|
+
if [ "$BASE_ENABLED" != "true" ]; then
|
|
1355
|
+
record_plan_pending "service-omni-supervisor-surface-deploy" \
|
|
1356
|
+
"service-omni-supervisor-surface-deploy" \
|
|
1357
|
+
"Supervisor action/tab surface (pending)" \
|
|
1358
|
+
"Omni-Channel is not yet enabled; the supervisor action/tab surface will be populated after base settings."
|
|
1359
|
+
elif [ "$SUPERVISOR_CONFIG_PRESENT" != "true" ]; then
|
|
1360
|
+
record_plan_pending "service-omni-supervisor-surface-deploy" \
|
|
1361
|
+
"service-omni-supervisor-surface-deploy" \
|
|
1362
|
+
"Supervisor action/tab surface (pending)" \
|
|
1363
|
+
"OmniSupervisorConfig is not yet present; the supervisor action/tab surface will be populated after the config is deployed."
|
|
1364
|
+
else
|
|
1365
|
+
run_skill "service-omni-supervisor-surface-deploy" \
|
|
1366
|
+
"service-omni-supervisor-surface-deploy" \
|
|
1367
|
+
"Supervisor action/tab surface (plan detect)" \
|
|
1368
|
+
'.status' \
|
|
1369
|
+
deploy-and-report.sh "$ORG" "$SUPERVISOR_CONFIG_RESOLVED_DN" || true
|
|
1370
|
+
fi
|
|
1371
|
+
elif [ "$BASE_OK" != "true" ]; then
|
|
1372
|
+
record_dependency_skip "service-omni-supervisor-surface-deploy" \
|
|
1373
|
+
"service-omni-supervisor-surface-deploy" \
|
|
1374
|
+
"Supervisor action/tab surface (skipped)" \
|
|
1375
|
+
"Prerequisite service-omni-base-settings-configure did not succeed; skipping the supervisor action/tab surface."
|
|
1376
|
+
elif [ "$SUPERVISOR_CONFIG_OK" != "true" ]; then
|
|
1377
|
+
record_dependency_skip "service-omni-supervisor-surface-deploy" \
|
|
1378
|
+
"service-omni-supervisor-surface-deploy" \
|
|
1379
|
+
"Supervisor action/tab surface (skipped)" \
|
|
1380
|
+
"Prerequisite service-omni-supervisor-config-deploy did not succeed; no OmniSupervisorConfig to populate the action/tab surface on."
|
|
1381
|
+
else
|
|
1382
|
+
run_skill "service-omni-supervisor-surface-deploy" \
|
|
1383
|
+
"service-omni-supervisor-surface-deploy" \
|
|
1384
|
+
"Supervisor action/tab surface (Data API inserts)" \
|
|
1385
|
+
'.status' \
|
|
1386
|
+
deploy-and-report.sh "$ORG" "$SUPERVISOR_CONFIG_RESOLVED_DN" || true
|
|
1387
|
+
fi
|
|
1388
|
+
|
|
1389
|
+
OVERALL_END=$(date +%s)
|
|
1390
|
+
TOTAL_ELAPSED=$((OVERALL_END - OVERALL_START))
|
|
1391
|
+
|
|
1392
|
+
# Run artifacts (run mode only): run.log (sanitized sequence + result), deploy_ids.json (provenance
|
|
1393
|
+
# manifest per skill), teardown.sh (dry-run reverse-order plan; makes NO changes - removal is manual).
|
|
1394
|
+
RUN_LOG=""
|
|
1395
|
+
DEPLOY_IDS=""
|
|
1396
|
+
TEARDOWN=""
|
|
1397
|
+
if [ "$MODE" = "run" ]; then
|
|
1398
|
+
RUN_LOG="$ARTIFACTS_DIR/run.log"
|
|
1399
|
+
DEPLOY_IDS="$ARTIFACTS_DIR/deploy_ids.json"
|
|
1400
|
+
TEARDOWN="$ARTIFACTS_DIR/teardown.sh"
|
|
1401
|
+
|
|
1402
|
+
{
|
|
1403
|
+
echo "# Omni coordinator run log"
|
|
1404
|
+
echo "# org=$ORG mode=$MODE timestamp=$TIMESTAMP"
|
|
1405
|
+
echo "# overall_status=$OVERALL_STATUS elapsed=${TOTAL_ELAPSED}s"
|
|
1406
|
+
echo "#"
|
|
1407
|
+
echo "$SKILL_RESULTS" | jq -r '.[] | "[\(.elapsed_sec)s] \(.invocation_key) → \(.status) (\(.color))"'
|
|
1408
|
+
} > "$RUN_LOG"
|
|
1409
|
+
chmod 600 "$RUN_LOG" 2>/dev/null || true
|
|
1410
|
+
|
|
1411
|
+
DEPLOY_MANIFEST='[]'
|
|
1412
|
+
while IFS= read -r res; do
|
|
1413
|
+
[ -z "$res" ] && continue
|
|
1414
|
+
key=$(echo "$res" | jq -r '.invocation_key')
|
|
1415
|
+
skill=$(echo "$res" | jq -r '.name')
|
|
1416
|
+
status=$(echo "$res" | jq -r '.status')
|
|
1417
|
+
color=$(echo "$res" | jq -r '.color')
|
|
1418
|
+
art=$(echo "$res" | jq -r '.artifact_path // ""')
|
|
1419
|
+
case "$status" in
|
|
1420
|
+
created|deployed) prov="created" ;;
|
|
1421
|
+
updated) prov="updated" ;;
|
|
1422
|
+
reused|assigned|bound|configured|ok|success|skipped) prov="reused" ;;
|
|
1423
|
+
*) prov="none" ;;
|
|
1424
|
+
esac
|
|
1425
|
+
ids='[]'; before='null'; devname='null'
|
|
1426
|
+
# Require a NON-EMPTY artifact (-s, not -f): a leaf that crashed can leave a 0-byte file, and
|
|
1427
|
+
# jq over empty input yields an empty string (not "null"), which would make --argjson below throw
|
|
1428
|
+
# and abort the whole run under set -e before the final report. Empty-string results are also
|
|
1429
|
+
# normalized back to valid JSON as a second guard.
|
|
1430
|
+
if [ -n "$art" ] && [ "$art" != "null" ] && [ -s "$art" ]; then
|
|
1431
|
+
ids=$(jq -c '[ .id?, .developer_name?, .channel_developer_name?,
|
|
1432
|
+
(.created_users[]?.id), (.agents[]?.service_resource_id),
|
|
1433
|
+
(.assignments[]?.id) ] | map(select(. != null and . != "" and . != "null"))' "$art" 2>/dev/null || echo '[]')
|
|
1434
|
+
before=$(jq -c '.before? // null' "$art" 2>/dev/null || echo 'null')
|
|
1435
|
+
devname=$(jq -r '.developer_name // "null"' "$art" 2>/dev/null || echo 'null')
|
|
1436
|
+
[ -z "$ids" ] && ids='[]'
|
|
1437
|
+
[ -z "$before" ] && before='null'
|
|
1438
|
+
[ -z "$devname" ] && devname='null'
|
|
1439
|
+
fi
|
|
1440
|
+
DEPLOY_MANIFEST=$(echo "$DEPLOY_MANIFEST" | jq -c \
|
|
1441
|
+
--arg key "$key" --arg skill "$skill" --arg status "$status" --arg color "$color" \
|
|
1442
|
+
--arg prov "$prov" --arg art "$art" --arg dn "$devname" \
|
|
1443
|
+
--argjson ids "$ids" --argjson before "$before" \
|
|
1444
|
+
'. + [{invocation_key:$key, skill:$skill, status:$status, color:$color, provenance:$prov, developer_name:(if $dn=="null" then null else $dn end), resource_ids:$ids, before:$before, artifact_path:(if $art=="" then null else $art end)}]')
|
|
1445
|
+
done < <(echo "$SKILL_RESULTS" | jq -c '.[]')
|
|
1446
|
+
|
|
1447
|
+
jq -n --arg org "$ORG" --arg ts "$TIMESTAMP" --argjson entries "$DEPLOY_MANIFEST" \
|
|
1448
|
+
'{coordinator:"service-omni-channel-setup-coordinate", org_alias:$org, timestamp:$ts, note:"Provenance manifest. Per-skill artifacts remain the source of truth. Only entries with provenance=created were created by this run.", entries:$entries}' > "$DEPLOY_IDS"
|
|
1449
|
+
chmod 600 "$DEPLOY_IDS" 2>/dev/null || true
|
|
1450
|
+
|
|
1451
|
+
cat > "$TEARDOWN" <<TDEOF
|
|
1452
|
+
#!/usr/bin/env bash
|
|
1453
|
+
# AUTO-GENERATED teardown plan for the Omni coordinator run at $TIMESTAMP (org $ORG).
|
|
1454
|
+
# DRY-RUN: prints a reverse-order plan, makes NO changes. Removal is manual - metadata components
|
|
1455
|
+
# need destructiveChanges deploys, only run-created resources may be removed, reused records never.
|
|
1456
|
+
set -euo pipefail
|
|
1457
|
+
ORG="$ORG"
|
|
1458
|
+
MANIFEST="\$(cd "\$(dirname "\$0")" && pwd)/deploy_ids.json"
|
|
1459
|
+
if [ ! -f "\$MANIFEST" ]; then echo "manifest not found: \$MANIFEST" >&2; exit 1; fi
|
|
1460
|
+
echo "Teardown plan (reverse dependency order) for org \$ORG"
|
|
1461
|
+
echo "Source manifest: \$MANIFEST"
|
|
1462
|
+
echo ""
|
|
1463
|
+
jq -r '.entries | reverse | .[]
|
|
1464
|
+
| if .provenance=="created" then
|
|
1465
|
+
"REMOVE \(.invocation_key): created \((.developer_name // (.resource_ids | join(", "))) // "-") [artifact: \(.artifact_path // "-")]"
|
|
1466
|
+
elif .provenance=="updated" then
|
|
1467
|
+
"RESTORE \(.invocation_key): restore prior values \(.before | tojson) [artifact: \(.artifact_path // "-")]"
|
|
1468
|
+
else
|
|
1469
|
+
"KEEP \(.invocation_key): \(.status) - adopted/reused, do not delete"
|
|
1470
|
+
end' "\$MANIFEST"
|
|
1471
|
+
echo ""
|
|
1472
|
+
echo "Plan only - no changes made. Reverse each REMOVE using its per-skill artifact; metadata"
|
|
1473
|
+
echo "components need a destructiveChanges deploy; data records use sf api request rest --method DELETE."
|
|
1474
|
+
TDEOF
|
|
1475
|
+
chmod 700 "$TEARDOWN" 2>/dev/null || true
|
|
1476
|
+
fi
|
|
1477
|
+
|
|
1478
|
+
echo "" >&2
|
|
1479
|
+
# The create leaves now escrow directly into CREDENTIALS_FILE, so recognise that too (redact_credentials
|
|
1480
|
+
# only flips CREDENTIALS_WRITTEN when it finds plaintext to move, which no longer happens).
|
|
1481
|
+
if [ "$CREDENTIALS_WRITTEN" != "true" ] && [ -s "$CREDENTIALS_FILE" ]; then
|
|
1482
|
+
chmod 600 "$CREDENTIALS_FILE" 2>/dev/null || true
|
|
1483
|
+
CREDENTIALS_WRITTEN="true"
|
|
1484
|
+
fi
|
|
1485
|
+
if [ "$CREDENTIALS_WRITTEN" = "true" ]; then
|
|
1486
|
+
echo " Generated credentials written to (mode 0600): $CREDENTIALS_FILE" >&2
|
|
1487
|
+
echo " Read them once, distribute securely, then delete: rm -f \"$CREDENTIALS_FILE\"" >&2
|
|
1488
|
+
echo " Passwords have been redacted from all other artifacts." >&2
|
|
1489
|
+
echo "" >&2
|
|
1490
|
+
fi
|
|
1491
|
+
jq -n \
|
|
1492
|
+
--arg org "$ORG" \
|
|
1493
|
+
--arg mode "$MODE" \
|
|
1494
|
+
--arg timestamp "$TIMESTAMP" \
|
|
1495
|
+
--arg artifacts_dir "$ARTIFACTS_DIR" \
|
|
1496
|
+
--arg overall_status "$OVERALL_STATUS" \
|
|
1497
|
+
--arg blocking_skill "$BLOCKING_SKILL" \
|
|
1498
|
+
--arg first_yellow_skill "$FIRST_YELLOW_SKILL" \
|
|
1499
|
+
--argjson attention_leaves "$ATTENTION_LEAVES" \
|
|
1500
|
+
--argjson total_elapsed "$TOTAL_ELAPSED" \
|
|
1501
|
+
--argjson results "$SKILL_RESULTS" \
|
|
1502
|
+
--argjson requested_count "$COUNT" \
|
|
1503
|
+
--argjson requested_supervisor_count "$SUPERVISOR_COUNT" \
|
|
1504
|
+
--arg routing_targets "$ROUTING_TARGETS_CSV" \
|
|
1505
|
+
--argjson safe_to_write true \
|
|
1506
|
+
--arg credentials_file "$CREDENTIALS_FILE" \
|
|
1507
|
+
--arg credentials_written "$CREDENTIALS_WRITTEN" \
|
|
1508
|
+
--arg run_log "$RUN_LOG" \
|
|
1509
|
+
--arg deploy_ids_file "$DEPLOY_IDS" \
|
|
1510
|
+
--arg teardown_script "$TEARDOWN" \
|
|
1511
|
+
'{
|
|
1512
|
+
coordinator: "service-omni-channel-setup-coordinate",
|
|
1513
|
+
mode: $mode,
|
|
1514
|
+
dry_run: ($mode == "plan"),
|
|
1515
|
+
org_alias: $org,
|
|
1516
|
+
timestamp: $timestamp,
|
|
1517
|
+
safe_to_write: $safe_to_write,
|
|
1518
|
+
run_log: (if $run_log == "" then null else $run_log end),
|
|
1519
|
+
deploy_ids_file: (if $deploy_ids_file == "" then null else $deploy_ids_file end),
|
|
1520
|
+
teardown_script: (if $teardown_script == "" then null else $teardown_script end),
|
|
1521
|
+
requested_count: $requested_count,
|
|
1522
|
+
requested_supervisor_count: $requested_supervisor_count,
|
|
1523
|
+
routing_targets: ($routing_targets | split(",")),
|
|
1524
|
+
artifacts_dir: $artifacts_dir,
|
|
1525
|
+
credentials_file: (if $credentials_written == "true" then $credentials_file else null end),
|
|
1526
|
+
credentials_note: (if $credentials_written == "true" then "Generated passwords are in credentials_file (mode 0600) and redacted from all other artifacts. Read once, distribute securely, then delete." else null end),
|
|
1527
|
+
overall_status: $overall_status,
|
|
1528
|
+
blocking_skill: (if $blocking_skill == "" then null else $blocking_skill end),
|
|
1529
|
+
first_attention_skill: (if $first_yellow_skill == "" then null else $first_yellow_skill end),
|
|
1530
|
+
attention_leaves: $attention_leaves,
|
|
1531
|
+
total_elapsed_sec: $total_elapsed,
|
|
1532
|
+
leaves_executed: ($results | length),
|
|
1533
|
+
skills: $results,
|
|
1534
|
+
exit_semantics: "exit 0 iff overall_status == green. yellow exits 2 (setup work still required). red exits 1 (blocked)."
|
|
1535
|
+
}'
|
|
1536
|
+
|
|
1537
|
+
case "$OVERALL_STATUS" in
|
|
1538
|
+
green) exit 0 ;;
|
|
1539
|
+
yellow) exit 2 ;;
|
|
1540
|
+
red) exit 1 ;;
|
|
1541
|
+
*) exit 1 ;;
|
|
1542
|
+
esac
|