@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,296 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# run-create.sh - INTERNAL (invoke via detect-and-create.sh, which enforces the safe_to_write guard): render Apex tokens, execute, parse created users, set passwords via separate System.setPassword; on password failure the user stays active + reset_required (never deactivated). Contract + security: SKILL.md + references/apex-template-notes.md.
|
|
3
|
+
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
|
|
6
|
+
if [ $# -lt 4 ]; then
|
|
7
|
+
echo '{"error":"Usage: bash run-create.sh <org-alias> <count> <profile-id> <org-suffix>"}' >&2
|
|
8
|
+
exit 1
|
|
9
|
+
fi
|
|
10
|
+
|
|
11
|
+
ORG="$1"
|
|
12
|
+
COUNT="$2"
|
|
13
|
+
PROFILE_ID="$3"
|
|
14
|
+
SUFFIX="$4"
|
|
15
|
+
|
|
16
|
+
# Validate every arg BEFORE it is sed-substituted into anonymous Apex. These tokens are interpolated
|
|
17
|
+
# into executable Apex, so anything outside a strict grammar (Apex/sed metacharacters, code) is
|
|
18
|
+
# rejected up front rather than executed.
|
|
19
|
+
if ! [[ "$COUNT" =~ ^[1-9][0-9]{0,2}$ ]]; then
|
|
20
|
+
echo "{\"error\":\"Invalid count '$COUNT' (expected a positive integer 1-999).\"}" >&2; exit 1
|
|
21
|
+
fi
|
|
22
|
+
if ! [[ "$PROFILE_ID" =~ ^[A-Za-z0-9]{15}([A-Za-z0-9]{3})?$ ]]; then
|
|
23
|
+
echo "{\"error\":\"Invalid profile-id '$PROFILE_ID' (expected a 15- or 18-char Salesforce Id).\"}" >&2; exit 1
|
|
24
|
+
fi
|
|
25
|
+
if ! [[ "$SUFFIX" =~ ^[A-Za-z0-9]{1,18}$ ]]; then
|
|
26
|
+
echo "{\"error\":\"Invalid org-suffix '$SUFFIX' (expected 1-18 alphanumeric chars).\"}" >&2; exit 1
|
|
27
|
+
fi
|
|
28
|
+
|
|
29
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
30
|
+
TEMPLATE="$SCRIPT_DIR/../assets/create-supervisors.apex.template"
|
|
31
|
+
|
|
32
|
+
if [ ! -f "$TEMPLATE" ]; then
|
|
33
|
+
echo "{\"error\":\"Apex template not found: $TEMPLATE\"}" >&2
|
|
34
|
+
exit 1
|
|
35
|
+
fi
|
|
36
|
+
|
|
37
|
+
WORK="$(mktemp -d)"
|
|
38
|
+
trap 'rm -rf "$WORK"' EXIT
|
|
39
|
+
RESOLVED="$WORK/create-supervisors.resolved.apex"
|
|
40
|
+
|
|
41
|
+
sed -e "s|__COUNT__|${COUNT}|g" \
|
|
42
|
+
-e "s|__PROFILE_ID__|${PROFILE_ID}|g" \
|
|
43
|
+
-e "s|__SUFFIX__|${SUFFIX}|g" \
|
|
44
|
+
"$TEMPLATE" > "$RESOLVED"
|
|
45
|
+
|
|
46
|
+
APEX_JSON=$(sf apex run --target-org "$ORG" --file "$RESOLVED" --json 2>/dev/null || true)
|
|
47
|
+
|
|
48
|
+
# Strip C0 control chars: sf embeds an unescaped debug log in .result.logs, which jq>=1.7 rejects.
|
|
49
|
+
# This merges the log to one line; the grep below is anchored to tolerate that.
|
|
50
|
+
APEX_JSON=$(printf '%s' "$APEX_JSON" | tr -d '\000-\037')
|
|
51
|
+
|
|
52
|
+
CLI_STATUS=$(echo "$APEX_JSON" | jq -r '.status // 0' 2>/dev/null || echo "0")
|
|
53
|
+
COMPILE_SUCCESS=$(echo "$APEX_JSON" | jq -r '.result.compiled // false' 2>/dev/null || echo "false")
|
|
54
|
+
EXEC_SUCCESS=$(echo "$APEX_JSON" | jq -r '.result.success // false' 2>/dev/null || echo "false")
|
|
55
|
+
|
|
56
|
+
translate_error() {
|
|
57
|
+
local raw="$1"
|
|
58
|
+
case "$raw" in
|
|
59
|
+
*LICENSE_LIMIT_EXCEEDED*)
|
|
60
|
+
echo "Org has reached its user license limit. Free up a license slot (deactivate unused users in Setup > Users) or request a larger CDO from OrgFarm."
|
|
61
|
+
;;
|
|
62
|
+
*DUPLICATE_USERNAME*)
|
|
63
|
+
echo "Username collision with another Salesforce org (globally unique constraint). Rare - retry the skill; if it persists, another agent may be concurrently running against the same suffix."
|
|
64
|
+
;;
|
|
65
|
+
*INVALID_EMAIL_ADDRESS*)
|
|
66
|
+
echo "Salesforce rejected the @example.com email. This should never happen - likely a bug in the skill's Apex template. Escalate."
|
|
67
|
+
;;
|
|
68
|
+
*INSUFFICIENT_ACCESS*|*NoAccessException*)
|
|
69
|
+
echo "Executing user lacks required permission. Ensure the sf-CLI-authenticated user has System Administrator profile or equivalent."
|
|
70
|
+
;;
|
|
71
|
+
*INVALID_LOGIN*|*INVALID_SESSION_ID*)
|
|
72
|
+
echo "Salesforce session expired. Re-authenticate: sf org login web -a $ORG -r <my-domain-url>"
|
|
73
|
+
;;
|
|
74
|
+
*)
|
|
75
|
+
echo "$raw"
|
|
76
|
+
;;
|
|
77
|
+
esac
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if [ "$CLI_STATUS" != "0" ]; then
|
|
81
|
+
CLI_ERR=$(echo "$APEX_JSON" | jq -r '.message // "Unknown CLI error"' 2>/dev/null)
|
|
82
|
+
FRIENDLY=$(translate_error "$CLI_ERR")
|
|
83
|
+
echo "{\"error\":\"$FRIENDLY\",\"raw\":$(echo "$CLI_ERR" | jq -R .)}" >&2
|
|
84
|
+
exit 1
|
|
85
|
+
fi
|
|
86
|
+
|
|
87
|
+
if [ "$COMPILE_SUCCESS" != "true" ]; then
|
|
88
|
+
COMPILE_ERR=$(echo "$APEX_JSON" | jq -r '.result.compileProblem // "Unknown compile error"' 2>/dev/null)
|
|
89
|
+
COMPILE_LINE=$(echo "$APEX_JSON" | jq -r '.result.line // "?"' 2>/dev/null)
|
|
90
|
+
echo "{\"error\":\"Apex compile failed at line $COMPILE_LINE: $COMPILE_ERR\"}" >&2
|
|
91
|
+
exit 1
|
|
92
|
+
fi
|
|
93
|
+
|
|
94
|
+
if [ "$EXEC_SUCCESS" != "true" ]; then
|
|
95
|
+
EXEC_ERR=$(echo "$APEX_JSON" | jq -r '.result.exceptionMessage // "Unknown execution error"' 2>/dev/null)
|
|
96
|
+
if [ -z "$EXEC_ERR" ] || [ "$EXEC_ERR" = "Unknown execution error" ] || [ "$EXEC_ERR" = "null" ]; then
|
|
97
|
+
EXEC_ERR=$(echo "$APEX_JSON" | jq -r '.message // "Unknown execution error"' 2>/dev/null)
|
|
98
|
+
fi
|
|
99
|
+
FRIENDLY=$(translate_error "$EXEC_ERR")
|
|
100
|
+
echo "{\"error\":\"$FRIENDLY\",\"raw\":$(echo "$EXEC_ERR" | jq -R .)}" >&2
|
|
101
|
+
exit 1
|
|
102
|
+
fi
|
|
103
|
+
|
|
104
|
+
# Parse SUPERVISOR_CREATED|<userId>|<username>|<email> from the log; decode the HTML-encoded pipe
|
|
105
|
+
# (|) that sf emits, else the grep misses every user.
|
|
106
|
+
LOG=$(echo "$APEX_JSON" | jq -r '.result.logs // ""' 2>/dev/null | sed 's/|/|/g')
|
|
107
|
+
|
|
108
|
+
# Anchor on a real 005 Id + @example.com domain so the match ignores the Apex source echoed in the merged log.
|
|
109
|
+
CREATED_RAW=$(echo "$LOG" | grep -oE 'SUPERVISOR_CREATED\|005[a-zA-Z0-9]{15}\|[^|]+@example\.com\|[^|]+@example\.com' || true)
|
|
110
|
+
|
|
111
|
+
if [ -z "$CREATED_RAW" ]; then
|
|
112
|
+
CREATED_USERS_JSON='[]'
|
|
113
|
+
else
|
|
114
|
+
CREATED_USERS_JSON=$(echo "$CREATED_RAW" \
|
|
115
|
+
| awk -F'|' '
|
|
116
|
+
BEGIN { print "[" }
|
|
117
|
+
NR > 1 { printf "," }
|
|
118
|
+
{
|
|
119
|
+
printf "{\"id\":\"%s\",\"username\":\"%s\",\"email\":\"%s\"}", $2, $3, $4
|
|
120
|
+
}
|
|
121
|
+
END { print "]" }
|
|
122
|
+
')
|
|
123
|
+
if ! echo "$CREATED_USERS_JSON" | jq -c '.' >/dev/null 2>&1; then
|
|
124
|
+
echo "{\"error\":\"Internal parse error: SUPERVISOR_CREATED lines produced invalid JSON. Users may have been created but this script cannot capture them.\",\"raw_lines\":$(echo "$CREATED_RAW" | jq -Rs .)}" >&2
|
|
125
|
+
exit 1
|
|
126
|
+
fi
|
|
127
|
+
CREATED_USERS_JSON=$(echo "$CREATED_USERS_JSON" | jq -c '.')
|
|
128
|
+
fi
|
|
129
|
+
|
|
130
|
+
# Set each User's password via System.setPassword (the only working path for Apex-inserted users).
|
|
131
|
+
# Fail-closed on the TraceFlag gate below; on failure the User stays active + reset_required.
|
|
132
|
+
# Full security rationale: references/apex-template-notes.md.
|
|
133
|
+
ENRICHED_USERS='[]'
|
|
134
|
+
NEEDS_RESET='[]'
|
|
135
|
+
ROLLED_BACK='[]'
|
|
136
|
+
|
|
137
|
+
# Generated passwords are written to a restricted (0600) file and NEVER returned in stdout, so
|
|
138
|
+
# plaintext cannot enter an AI/tool transcript when this leaf is invoked directly. The coordinator
|
|
139
|
+
# passes OMNI_CREDENTIALS_FILE/OMNI_CREDENTIALS_KEY to escrow into its own file; standalone runs
|
|
140
|
+
# default to ./CREDENTIALS.json. File format: { "<key>": [ {username, password}, ... ] }.
|
|
141
|
+
# Secrets never touch argv/env (both visible via process inspection): each {username,password} pair is
|
|
142
|
+
# appended as one JSON line to this 0600 temp file (inside $WORK, mode 0700) and fed to jq only via
|
|
143
|
+
# stdin / --slurpfile. $WORK is removed by the EXIT trap, so the plaintext file never persists.
|
|
144
|
+
CRED_PAIRS_FILE="$WORK/cred-pairs.jsonl"
|
|
145
|
+
: > "$CRED_PAIRS_FILE"; chmod 600 "$CRED_PAIRS_FILE" 2>/dev/null || true
|
|
146
|
+
CRED_COUNT=0
|
|
147
|
+
CRED_FILE="${OMNI_CREDENTIALS_FILE:-$PWD/CREDENTIALS.json}"
|
|
148
|
+
CRED_KEY="${OMNI_CREDENTIALS_KEY:-service-omni-supervisor-users-create}"
|
|
149
|
+
CRED_WRITTEN="false"
|
|
150
|
+
write_credentials() {
|
|
151
|
+
# $1 = path to a JSONL file of {username,password} objects. Secret-bearing pairs are read via
|
|
152
|
+
# --slurpfile (a file, never argv); the existing credentials file is piped in via stdin.
|
|
153
|
+
local pairs_file="$1" existing='{}' tmp
|
|
154
|
+
[ -f "$CRED_FILE" ] && existing=$(cat "$CRED_FILE" 2>/dev/null || echo '{}')
|
|
155
|
+
tmp=$(mktemp) || return 1
|
|
156
|
+
if ! printf '%s' "$existing" | jq --arg k "$CRED_KEY" --slurpfile c "$pairs_file" '. + {($k): $c}' > "$tmp" 2>/dev/null || [ ! -s "$tmp" ]; then
|
|
157
|
+
rm -f "$tmp"; return 1
|
|
158
|
+
fi
|
|
159
|
+
if ! mv "$tmp" "$CRED_FILE" 2>/dev/null; then rm -f "$tmp"; return 1; fi
|
|
160
|
+
chmod 600 "$CRED_FILE" 2>/dev/null || true
|
|
161
|
+
return 0
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
set_password_via_apex() {
|
|
165
|
+
# $1=user_id $2=plaintext_password. Echoes "ok" on success, else the exception text.
|
|
166
|
+
local uid="$1" pw="$2" apex_file res
|
|
167
|
+
apex_file="$WORK/setpw.apex"
|
|
168
|
+
# uid (18-char Id) and pw ([A-Za-z0-9]) can't break out of the Apex string literal.
|
|
169
|
+
printf "System.setPassword('%s', '%s');\n" "$uid" "$pw" > "$apex_file"
|
|
170
|
+
res=$(sf apex run --target-org "$ORG" --file "$apex_file" --json 2>/dev/null | tr -d '\000-\037' || true)
|
|
171
|
+
rm -f "$apex_file"
|
|
172
|
+
if [ "$(echo "$res" | jq -r '.result.success // false' 2>/dev/null)" = "true" ]; then
|
|
173
|
+
echo "ok"
|
|
174
|
+
else
|
|
175
|
+
echo "$res" | jq -r '.result.exceptionMessage // .message // "setPassword failed"' 2>/dev/null
|
|
176
|
+
fi
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
# TraceFlag gate (fail-closed): prove no active TraceFlag exists for the running user BEFORE any
|
|
180
|
+
# System.setPassword, else a password literal could persist to a queryable ApexLog. Cannot prove safe
|
|
181
|
+
# => no password set, users left active + reset_required. See references/apex-template-notes.md.
|
|
182
|
+
TRACEFLAG_SAFE=false
|
|
183
|
+
TRACEFLAG_REASON="TraceFlag state could not be determined"
|
|
184
|
+
ME_UNAME=$(sf org display --target-org "$ORG" --json 2>/dev/null | jq -r '.result.username // empty' 2>/dev/null || true)
|
|
185
|
+
if [ -z "${ME_UNAME:-}" ]; then
|
|
186
|
+
TRACEFLAG_REASON="could not resolve the running user (sf org display failed)"
|
|
187
|
+
else
|
|
188
|
+
ME_UID=$(sf data query --target-org "$ORG" \
|
|
189
|
+
--query "SELECT Id FROM User WHERE Username='$ME_UNAME' LIMIT 1" \
|
|
190
|
+
--json 2>/dev/null | jq -r '.result.records[0].Id // empty' 2>/dev/null || true)
|
|
191
|
+
if [ -z "${ME_UID:-}" ]; then
|
|
192
|
+
TRACEFLAG_REASON="could not resolve the running user's Id"
|
|
193
|
+
else
|
|
194
|
+
NOW_UTC=$(date -u +%Y-%m-%dT%H:%M:%SZ)
|
|
195
|
+
if TF_JSON=$(sf data query --target-org "$ORG" --use-tooling-api \
|
|
196
|
+
--query "SELECT Id FROM TraceFlag WHERE TracedEntityId='$ME_UID' AND ExpirationDate > $NOW_UTC" \
|
|
197
|
+
--json 2>/dev/null) && [ -n "$TF_JSON" ]; then
|
|
198
|
+
TF_STATUS=$(echo "$TF_JSON" | jq -r '.status // 1' 2>/dev/null || echo 1)
|
|
199
|
+
if [ "$TF_STATUS" = "0" ]; then
|
|
200
|
+
TF_COUNT=$(echo "$TF_JSON" | jq -r 'if (.result.totalSize != null) then .result.totalSize elif (.result.records != null) then (.result.records | length) else "unknown" end' 2>/dev/null || echo "unknown")
|
|
201
|
+
if [ "$TF_COUNT" = "0" ]; then
|
|
202
|
+
TRACEFLAG_SAFE=true
|
|
203
|
+
TRACEFLAG_REASON=""
|
|
204
|
+
elif [ "$TF_COUNT" = "unknown" ]; then
|
|
205
|
+
TRACEFLAG_REASON="the TraceFlag query returned an unparseable result"
|
|
206
|
+
else
|
|
207
|
+
TRACEFLAG_REASON="an active Apex debug TraceFlag exists for the running user ($TF_COUNT found)"
|
|
208
|
+
fi
|
|
209
|
+
else
|
|
210
|
+
TRACEFLAG_REASON="the TraceFlag Tooling API query returned an error status"
|
|
211
|
+
fi
|
|
212
|
+
else
|
|
213
|
+
TRACEFLAG_REASON="the TraceFlag Tooling API query failed"
|
|
214
|
+
fi
|
|
215
|
+
fi
|
|
216
|
+
fi
|
|
217
|
+
|
|
218
|
+
if [ "$(echo "$CREATED_USERS_JSON" | jq -r 'length')" -gt 0 ]; then
|
|
219
|
+
while IFS= read -r row; do
|
|
220
|
+
USER_ID=$(echo "$row" | jq -r '.id')
|
|
221
|
+
UNAME=$(echo "$row" | jq -r '.username')
|
|
222
|
+
EMAIL=$(echo "$row" | jq -r '.email')
|
|
223
|
+
|
|
224
|
+
if [ "$TRACEFLAG_SAFE" != "true" ]; then
|
|
225
|
+
# Fail closed: skip setPassword, keep the User active + flag for manual reset.
|
|
226
|
+
NEEDS_RESET=$(echo "$NEEDS_RESET" | jq -c --arg u "$UNAME" '. + [$u]')
|
|
227
|
+
ENRICHED_USERS=$(echo "$ENRICHED_USERS" | jq -c \
|
|
228
|
+
--arg id "$USER_ID" --arg u "$UNAME" --arg e "$EMAIL" \
|
|
229
|
+
'. + [{id: $id, username: $u, email: $e, password: null, password_status: "reset_required", password_error: "skipped: TraceFlag safety could not be proven (fail-closed)"}]')
|
|
230
|
+
continue
|
|
231
|
+
fi
|
|
232
|
+
|
|
233
|
+
# Strong [A-Za-z0-9] password + fixed mixed-class suffix (Salesforce complexity). The || true
|
|
234
|
+
# swallows tr's SIGPIPE (head closes the pipe) so pipefail+set -e don't abort mid-insert.
|
|
235
|
+
RAND="$(LC_ALL=C tr -dc 'A-Za-z0-9' < /dev/urandom 2>/dev/null | head -c 14 || true)"
|
|
236
|
+
PW="${RAND}aB3"
|
|
237
|
+
SETPW_RESULT=$(set_password_via_apex "$USER_ID" "$PW")
|
|
238
|
+
|
|
239
|
+
if [ "$SETPW_RESULT" = "ok" ]; then
|
|
240
|
+
# Feed the SECRET via stdin (never argv/env); username is not secret so --arg is fine.
|
|
241
|
+
printf '%s' "$PW" | jq -Rsc --arg u "$UNAME" '{username: $u, password: .}' >> "$CRED_PAIRS_FILE"
|
|
242
|
+
CRED_COUNT=$((CRED_COUNT + 1))
|
|
243
|
+
ENRICHED_USERS=$(echo "$ENRICHED_USERS" | jq -c \
|
|
244
|
+
--arg id "$USER_ID" --arg u "$UNAME" --arg e "$EMAIL" \
|
|
245
|
+
'. + [{id: $id, username: $u, email: $e, password: null, password_status: "generated"}]')
|
|
246
|
+
else
|
|
247
|
+
# Non-destructive: keep the active User, flag it for a manual password reset.
|
|
248
|
+
NEEDS_RESET=$(echo "$NEEDS_RESET" | jq -c --arg u "$UNAME" '. + [$u]')
|
|
249
|
+
ENRICHED_USERS=$(echo "$ENRICHED_USERS" | jq -c \
|
|
250
|
+
--arg id "$USER_ID" --arg u "$UNAME" --arg e "$EMAIL" --arg r "$SETPW_RESULT" \
|
|
251
|
+
'. + [{id: $id, username: $u, email: $e, password: null, password_status: "reset_required", password_error: $r}]')
|
|
252
|
+
fi
|
|
253
|
+
done < <(echo "$CREATED_USERS_JSON" | jq -c '.[]')
|
|
254
|
+
fi
|
|
255
|
+
|
|
256
|
+
# Escrow generated passwords to the restricted file; never emit plaintext in stdout. Fail-closed:
|
|
257
|
+
# if escrow fails, the passwords were set on-org but cannot be handed back, so flag those users for a
|
|
258
|
+
# manual reset rather than leaking the plaintext.
|
|
259
|
+
CRED_WARNING=""
|
|
260
|
+
if [ "$CRED_COUNT" -gt 0 ]; then
|
|
261
|
+
if write_credentials "$CRED_PAIRS_FILE"; then
|
|
262
|
+
CRED_WRITTEN="true"
|
|
263
|
+
else
|
|
264
|
+
CRED_WARNING="Passwords were set on the created users but could NOT be escrowed to the credentials file ($CRED_FILE); plaintext was deliberately NOT emitted to stdout. Reset these users' passwords in Setup > Users."
|
|
265
|
+
# Escrow failed → the plaintext is unrecoverable, so these users genuinely need a manual reset:
|
|
266
|
+
# add them to users_needing_password_reset (not just the per-user status) before flipping status.
|
|
267
|
+
ESCROW_FAILED_UNAMES=$(echo "$ENRICHED_USERS" | jq -c '[ .[] | select(.password_status=="generated") | .username ]')
|
|
268
|
+
NEEDS_RESET=$(jq -c -n --argjson a "$NEEDS_RESET" --argjson b "$ESCROW_FAILED_UNAMES" '$a + $b | unique')
|
|
269
|
+
ENRICHED_USERS=$(echo "$ENRICHED_USERS" | jq -c '[ .[] | if .password_status=="generated" then (.password_status="reset_required" | .password_error="escrow_failed") else . end ]')
|
|
270
|
+
fi
|
|
271
|
+
fi
|
|
272
|
+
|
|
273
|
+
# Surface why password generation was skipped when the TraceFlag gate failed.
|
|
274
|
+
SECURITY_WARNING=""
|
|
275
|
+
if [ "$TRACEFLAG_SAFE" != "true" ]; then
|
|
276
|
+
SECURITY_WARNING="Password generation was SKIPPED (fail-closed) because ${TRACEFLAG_REASON}. No System.setPassword was executed, so no plaintext could enter an ApexLog. The created users are ACTIVE and flagged password_status:\"reset_required\" - set their passwords in Setup > Users after confirming no debug TraceFlag is active, or disable the TraceFlag and re-run."
|
|
277
|
+
fi
|
|
278
|
+
[ -n "$CRED_WARNING" ] && SECURITY_WARNING="${SECURITY_WARNING:+$SECURITY_WARNING }$CRED_WARNING"
|
|
279
|
+
|
|
280
|
+
jq -n \
|
|
281
|
+
--argjson created "$ENRICHED_USERS" \
|
|
282
|
+
--argjson needs_reset "$NEEDS_RESET" \
|
|
283
|
+
--argjson rolled_back "$ROLLED_BACK" \
|
|
284
|
+
--arg security_warning "$SECURITY_WARNING" \
|
|
285
|
+
--arg cred_written "$CRED_WRITTEN" \
|
|
286
|
+
--arg cred_file "$CRED_FILE" \
|
|
287
|
+
'{
|
|
288
|
+
created_users: $created,
|
|
289
|
+
created_count: ($created | length),
|
|
290
|
+
users_needing_password_reset: $needs_reset,
|
|
291
|
+
rolled_back_users: $rolled_back,
|
|
292
|
+
credentials_written: ($cred_written == "true"),
|
|
293
|
+
credentials_file: (if $cred_written == "true" then $cred_file else null end),
|
|
294
|
+
security_warning: (if $security_warning == "" then null else $security_warning end),
|
|
295
|
+
security_note: "Passwords are set via anonymous Apex System.setPassword. The plaintext appears in the inline executeAnonymous debug log and, ONLY when a debug-log TraceFlag is active for the running user, in a queryable ApexLog row. To avoid ever persisting plaintext to a log, this script FAILS CLOSED BEFORE the first System.setPassword: it proves via a SOQL-filtered Tooling API query (ExpirationDate > now) that no active TraceFlag exists for the running user. If safety cannot be positively proven - the running user cannot be resolved, the Tooling API query fails or is unparseable, or any active TraceFlag exists - NO password is generated at all; the created users are left ACTIVE and flagged password_status:\"reset_required\", and security_warning explains why. Generated passwords are NEVER returned in this output; they are written to a restricted 0600 credentials_file (default ./CREDENTIALS.json, or OMNI_CREDENTIALS_FILE) so plaintext cannot enter a tool/AI transcript. Read the file once, distribute securely, then delete it. Set passwords for reset_required users in Setup > Users after ensuring no debug TraceFlag is active (or disable the TraceFlag and re-run)."
|
|
296
|
+
}'
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: service-omni-work-skill-routing-configure
|
|
3
|
+
description: "Use to author an Omni WorkSkillRouting rule (field-based skill criteria): map a related-entity field value to a required Omni Skill (e.g. add the Voice skill when Case.Origin = Phone), deployed as one WorkSkillRouting record via the Metadata API. Idempotent via the Metadata API files[].state signal; one rule per related entity. Triggers: add field-based skill routing, route by a Case field to a skill, route by a MessagingSession field to a skill, configure attribute-based skills routing, require an Omni skill for a field value. Do not use to create the Skill or bind agents to it (service-omni-skills-based-routing-configure), to create agent users, or to configure queues."
|
|
4
|
+
allowed-tools: Bash Read Write Edit Glob Grep
|
|
5
|
+
metadata:
|
|
6
|
+
version: "1.0"
|
|
7
|
+
domains: ["Service"]
|
|
8
|
+
minApiVersion: "66.0"
|
|
9
|
+
relatedSkills:
|
|
10
|
+
- "service-omni-base-settings-configure"
|
|
11
|
+
- "service-omni-channel-setup-coordinate"
|
|
12
|
+
- "service-omni-skills-based-routing-configure"
|
|
13
|
+
accessCheck:
|
|
14
|
+
- type: license
|
|
15
|
+
value: ServiceCloud
|
|
16
|
+
cliTools:
|
|
17
|
+
- tool: ["jq"]
|
|
18
|
+
semver: ">=1.6"
|
|
19
|
+
- tool: ["python3"]
|
|
20
|
+
semver: ">=3.8"
|
|
21
|
+
- tool: ["sf"]
|
|
22
|
+
semver: ">=2.139.6"
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
# service-omni-work-skill-routing-configure
|
|
26
|
+
|
|
27
|
+
Author an Omni **WorkSkillRouting** rule so work is routed to agents by a **field value on the work record**, not just by skills the agent happens to hold. Where `service-omni-skills-based-routing-configure` makes an agent *routable* (Skill + ServiceResource + ServiceResourceSkill), this skill decides *which skill a work item requires* based on one of its fields — for example "when `Case.Origin` = `Phone`, require the `Voice` skill". The rule is one `WorkSkillRouting` record (a header plus one `workSkillRoutingAttributes` mapping) deployed via the Metadata API. It runs after the referenced Skill exists and belongs to the same routing family coordinated by `service-omni-channel-setup-coordinate`.
|
|
28
|
+
|
|
29
|
+
## Inputs
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
bash scripts/configure-and-report.sh <org-alias> <related_entity> <field> <skill> <value> [rule_master_label]
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
- `org-alias` (required).
|
|
36
|
+
- `related_entity` (required). The sObject the rule applies to (e.g. `Case`, `MessagingSession`, `LiveChatTranscript`, `Lead`, `VoiceCall`).
|
|
37
|
+
- `field` (required). The field the attribute evaluates (e.g. `Case.Origin` or `Origin`). Bare standard fields are qualified with the related entity before deploy, so `Origin` becomes `Case.Origin` instead of being misread as a custom-field metadata name.
|
|
38
|
+
- `skill` (required). The Omni `Skill` DeveloperName required when the field matches (e.g. `Voice`).
|
|
39
|
+
- `value` (required). The field value that maps to the skill (e.g. `Phone`). Plain values only; XML-reserved characters are not supported.
|
|
40
|
+
- `rule_master_label` (optional). MasterLabel for the rule; defaults to `<related_entity> Skill Routing`.
|
|
41
|
+
|
|
42
|
+
Env overrides: `RULE_DEVELOPER_NAME` (fullName of the rule, default the related-entity name — one rule per entity), `IS_ACTIVE` (default `true`), `SKILL_LEVEL` (0–10), `SKILL_PRIORITY` (0–10, additional-skill drop order), `IS_ADDITIONAL_SKILL` (`true`/`false`).
|
|
43
|
+
|
|
44
|
+
## Preconditions and safety
|
|
45
|
+
|
|
46
|
+
- Target org authenticated via `sf` CLI, Service Cloud license, `sf` CLI ≥ 2.139.6.
|
|
47
|
+
- Omni-Channel base settings enabled with **skills-based routing** turned on (`service-omni-base-settings-configure`).
|
|
48
|
+
- The referenced `Skill` already exists (`service-omni-skills-based-routing-configure`).
|
|
49
|
+
- The three-way `safe_to_write` production guard applies.
|
|
50
|
+
|
|
51
|
+
## Run
|
|
52
|
+
|
|
53
|
+
1. **safe_to_write** — query `Organization`; refuse to write to a real production org.
|
|
54
|
+
2. **Skill prereq** — query `Skill` by DeveloperName; if absent, block and point at `service-omni-skills-based-routing-configure`.
|
|
55
|
+
3. **Merge** — read the existing `WorkSkillRouting.Metadata` through the Tooling API, preserve unrelated attributes, and replace or append only the requested `(field, value)` mapping. An inconclusive read blocks the write; `WSR_REPLACE=1` is the explicit destructive-replacement escape hatch.
|
|
56
|
+
4. **Deploy** — materialize the merged `WorkSkillRouting` record (elements in XSD/alphabetical order) and deploy it via the Metadata API (async + poll to a terminal state).
|
|
57
|
+
5. **Classify** — read `files[].state` (`Created` / `Changed` / `Unchanged`) to report `created` / `updated` / `reused`.
|
|
58
|
+
|
|
59
|
+
## Behavior
|
|
60
|
+
|
|
61
|
+
**Idempotent + merge-safe.** WorkSkillRouting permits a single rule per related entity, so the rule's fullName defaults to the entity name. Re-running the same mapping reports `reused`; a new mapping is merged into the existing attribute list; changing the same field/value mapping updates only that entry. The skill never silently replaces an existing rule when its current metadata could not be read.
|
|
62
|
+
|
|
63
|
+
**Graceful degradation.** WorkSkillRouting is not provisioned on every org (some trials/CDOs). A deploy failure shaped like `INVALID_TYPE` / "not available" / "WorkSkillRouting" is reported as `blocked` with a clear limitation message and a pointer to base settings — the skill surfaces the limitation rather than crashing.
|
|
64
|
+
|
|
65
|
+
## Output contract
|
|
66
|
+
|
|
67
|
+
A single JSON object: `status` ∈ `created` | `updated` | `reused` | `blocked` (plus `action_needed` in plan mode), `rule` (`{developer_name, master_label, related_entity, is_active, state}`), `attribute` (`{field, skill, value, skill_level, skill_priority, is_additional_skill}`), `merge` (`{merged_from_existing, attribute_count, preserved_count}`), `deploy_id`, `manual_actions`, `blocking_issue`.
|
|
68
|
+
|
|
69
|
+
## Limitations
|
|
70
|
+
|
|
71
|
+
- One field→skill mapping per invocation and one rule per related entity. Multiple invocations safely accumulate attributes on that rule.
|
|
72
|
+
- Does not create the Skill, ServiceResources, agent users, queues, or presence configuration.
|
|
73
|
+
- Field/value correctness is validated by the platform at deploy time; an invalid field or value surfaces as a `blocked` deploy failure.
|
|
74
|
+
|
|
75
|
+
## References
|
|
76
|
+
|
|
77
|
+
| File | When to read |
|
|
78
|
+
|---|---|
|
|
79
|
+
| `references/api-notes.md` | WorkSkillRouting metadata shape, XSD element order, one-rule-per-entity constraint, attribute fields, and graceful-degradation triggers |
|
|
80
|
+
| `scripts/tests/test_work_skill_routing_contracts.py` | When validating changes — run `python3 scripts/tests/test_work_skill_routing_contracts.py` from this skill directory |
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# WorkSkillRouting - API notes
|
|
2
|
+
|
|
3
|
+
## Metadata type
|
|
4
|
+
|
|
5
|
+
`WorkSkillRouting` is a Metadata API type (also queryable via the Tooling API). It stores a set
|
|
6
|
+
of `WorkSkillRoutingAttribute` mappings that route a work item to an agent who holds the required
|
|
7
|
+
skills. It extends `Metadata` and inherits `fullName`.
|
|
8
|
+
|
|
9
|
+
Source path convention used by this skill:
|
|
10
|
+
|
|
11
|
+
```text
|
|
12
|
+
force-app/main/default/workSkillRoutings/<fullName>.workSkillRouting-meta.xml
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Record shape (XSD / alphabetical element order)
|
|
16
|
+
|
|
17
|
+
```xml
|
|
18
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
19
|
+
<WorkSkillRouting xmlns="http://soap.sforce.com/2006/04/metadata">
|
|
20
|
+
<isActive>true</isActive>
|
|
21
|
+
<masterLabel>Case Skill Routing</masterLabel>
|
|
22
|
+
<relatedEntity>Case</relatedEntity>
|
|
23
|
+
<workSkillRoutingAttributes>
|
|
24
|
+
<field>Case.Origin</field>
|
|
25
|
+
<skill>Voice</skill>
|
|
26
|
+
<value>Phone</value>
|
|
27
|
+
</workSkillRoutingAttributes>
|
|
28
|
+
</WorkSkillRouting>
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Header fields:
|
|
32
|
+
|
|
33
|
+
| Field | Type | Notes |
|
|
34
|
+
|---|---|---|
|
|
35
|
+
| `isActive` | boolean | Required. Whether the rule is evaluated. |
|
|
36
|
+
| `masterLabel` | string | Required. Internal (untranslated) label. |
|
|
37
|
+
| `relatedEntity` | string | Required. The sObject the attributes apply to. |
|
|
38
|
+
| `workSkillRoutingAttributes` | array | One or more field-value -> skill mappings. |
|
|
39
|
+
|
|
40
|
+
Attribute fields (emitted alphabetically: `field`, `isAdditionalSkill`, `skill`, `skillLevel`,
|
|
41
|
+
`skillPriority`, `value`):
|
|
42
|
+
|
|
43
|
+
| Field | Type | Notes |
|
|
44
|
+
|---|---|---|
|
|
45
|
+
| `field` | string | Required. Field the attribute evaluates. |
|
|
46
|
+
| `isAdditionalSkill` | boolean | Additional skills are dropped after the routing-config timeout so a best-match agent can still take the work. |
|
|
47
|
+
| `skill` | string | Required. Skill DeveloperName required when the field matches `value`. |
|
|
48
|
+
| `skillLevel` | int | 0–10. |
|
|
49
|
+
| `skillPriority` | int | 0–10. Drop order for additional skills (9–10 dropped first, 0–1 last). API 49.0+. |
|
|
50
|
+
| `value` | string | Required. Field value mapped to the skill. |
|
|
51
|
+
|
|
52
|
+
## One rule per entity
|
|
53
|
+
|
|
54
|
+
Only one `WorkSkillRouting` rule is permitted per `relatedEntity`. This skill therefore defaults the
|
|
55
|
+
rule fullName to the related-entity name (overridable via `RULE_DEVELOPER_NAME`), so re-runs converge
|
|
56
|
+
on the same record instead of creating duplicates. Idempotency is read from the Metadata API
|
|
57
|
+
`files[].state`: `Created` -> `created`, `Changed` -> `updated`, `Unchanged` -> `reused`.
|
|
58
|
+
|
|
59
|
+
## Prerequisites and graceful degradation
|
|
60
|
+
|
|
61
|
+
- The referenced `Skill` must exist first (`service-omni-skills-based-routing-configure`); the skill
|
|
62
|
+
queries `Skill` by DeveloperName and blocks with that pointer when it is missing.
|
|
63
|
+
- Skills-based routing must be enabled in Omni-Channel settings
|
|
64
|
+
(`service-omni-base-settings-configure`).
|
|
65
|
+
- WorkSkillRouting is not provisioned on every org (some trials/CDOs). Deploy failures shaped like
|
|
66
|
+
`INVALID_TYPE`, "not available", or naming `WorkSkillRouting` are reported as `blocked` with a
|
|
67
|
+
limitation message rather than crashing, so a coordinator can continue.
|