@salesforce/afv-skills 1.55.0 → 1.57.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-generate/SKILL.md +8 -0
- package/skills/agentforce-generate/references/actions-reference.md +2 -2
- package/skills/agentforce-generate/references/voice-modality-reference.md +51 -15
- package/skills/agentforce-generate/references/voice-telephony-cli.md +193 -0
- package/skills/agentforce-observe/SKILL.md +13 -9
- package/skills/agentforce-observe/references/ahm-alerts.md +193 -14
- package/skills/automation-sandbox-post-copy-config-generate/SKILL.md +24 -15
- package/skills/automation-sandbox-post-copy-config-generate/assets/config_template.json +11 -0
- package/skills/automation-sandbox-post-copy-config-generate/assets/json_schema.json +33 -1
- package/skills/automation-sandbox-post-copy-config-generate/references/configuration_catalog.md +32 -1
- package/skills/automation-sandbox-post-copy-configure/SKILL.md +75 -71
- package/skills/automation-sandbox-post-copy-configure/assets/scheduled_apex_template.apex +13 -0
- package/skills/automation-sandbox-post-copy-configure/examples/sample_scheduled_apex_config.json +13 -0
- package/skills/automation-sandbox-post-copy-configure/references/scheduled_apex_path.md +155 -0
- package/skills/automation-sandbox-post-copy-configure/scripts/build-scheduled-apex.mjs +62 -0
- package/skills/automation-sandbox-post-copy-configure/scripts/soql-escape-job-name.mjs +28 -0
- package/skills/commerce-b2b-open-code-components-integrate/SKILL.md +1 -1
- package/skills/commerce-b2b-open-code-components-replace/SKILL.md +16 -16
- package/skills/consumer-goods-accruals-datakit-deploy/SKILL.md +383 -0
- package/skills/consumer-goods-accruals-datakit-deploy/scripts/setup.js +324 -0
- package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/00-check-prerequisites.js +63 -0
- package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/01-download-static-resource.js +100 -0
- package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/02-replace-org-id.js +46 -0
- package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/03-deploy-metadata.js +46 -0
- package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/04-deploy-engine.js +198 -0
- package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/05-deploy-tpm-accruals.js +231 -0
- package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/06-create-dataspace.js +82 -0
- package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/07-deploy-accruals-reports.js +517 -0
- package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/08-deploy-ui.js +416 -0
- package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/09-completion.js +44 -0
- package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/utils.js +514 -0
- package/skills/consumer-goods-sync-management-configure/SKILL.md +137 -0
- package/skills/consumer-goods-sync-management-configure/examples/scenarios.md +294 -0
- package/skills/consumer-goods-sync-management-configure/references/act1-setup-sync.md +333 -0
- package/skills/consumer-goods-sync-management-configure/references/act2-assign-users.md +324 -0
- package/skills/consumer-goods-sync-management-configure/references/act3-plan-verify.md +113 -0
- package/skills/consumer-goods-sync-management-configure/references/readiness-and-enablement.md +164 -0
- package/skills/consumer-goods-sync-management-configure/references/sync-management-app-install-backbone.md +159 -0
- package/skills/consumer-goods-sync-management-configure/references/transports-and-namespace.md +220 -0
- package/skills/consumer-goods-sync-management-configure/references/verify-and-smoke.md +175 -0
- package/skills/dx-devops-project-manage/SKILL.md +1 -1
- package/skills/dx-org-analyze/SKILL.md +0 -1
- package/skills/dx-org-analyze/scripts/collect_org_data.py +2 -2
- package/skills/dx-org-analyze/scripts/compute_diff.py +2 -4
- package/skills/dx-org-analyze/scripts/introspect_org.py +4 -40
- package/skills/dx-org-manage/SKILL.md +6 -50
- package/skills/dx-org-manage/examples/README.md +10 -16
- package/skills/dx-org-manage/references/scratch-org-create.md +1 -1
- package/skills/dx-org-manage/references/scratch-org-operations.md +1 -1
- package/skills/dx-org-shape-manage/SKILL.md +4 -3
- package/skills/education-cloud-domain-configure/references/gotchas-extended.md +1 -0
- package/skills/experience-accessibility-validate/SKILL.md +8 -5
- package/skills/experience-accessibility-validate/references/reviewers/sc-1-1-1-non-text-content.md +1 -4
- package/skills/experience-accessibility-validate/references/reviewers/sc-1-3-1-i-lists.md +0 -4
- package/skills/experience-accessibility-validate/references/reviewers/sc-1-3-1-ii-tables.md +20 -85
- package/skills/experience-accessibility-validate/references/reviewers/sc-1-3-1-iii-form-labels.md +4 -4
- package/skills/experience-accessibility-validate/references/reviewers/sc-1-3-1-iv-regions.md +0 -4
- package/skills/experience-accessibility-validate/references/reviewers/sc-1-3-1-v-groups.md +2 -4
- package/skills/experience-accessibility-validate/references/reviewers/sc-1-3-5-identify-input.md +0 -4
- package/skills/experience-accessibility-validate/references/reviewers/sc-1-4-3-contrast.md +4 -14
- package/skills/experience-accessibility-validate/references/reviewers/sc-2-1-1-keyboard.md +9 -5
- package/skills/experience-accessibility-validate/references/reviewers/sc-2-4-4-link-purpose.md +1 -4
- package/skills/experience-accessibility-validate/references/reviewers/sc-2-4-6-headings-labels.md +0 -4
- package/skills/experience-accessibility-validate/references/reviewers/sc-2-5-1-pointer-gestures.md +0 -4
- package/skills/experience-accessibility-validate/references/reviewers/sc-2-5-2-pointer-cancellation.md +0 -4
- package/skills/experience-accessibility-validate/references/reviewers/sc-2-5-3-label-in-name.md +0 -4
- package/skills/experience-accessibility-validate/references/reviewers/sc-2-5-7-dragging-movement.md +0 -4
- package/skills/experience-accessibility-validate/references/reviewers/sc-3-2-1-on-focus.md +0 -4
- package/skills/experience-accessibility-validate/references/reviewers/sc-3-2-2-on-input.md +1 -4
- package/skills/experience-accessibility-validate/references/reviewers/sc-3-3-1-error-identification.md +1 -4
- package/skills/experience-accessibility-validate/references/reviewers/sc-3-3-2-labels-instructions.md +1 -4
- package/skills/experience-accessibility-validate/references/reviewers/sc-3-3-3-error-suggestion.md +1 -4
- package/skills/experience-accessibility-validate/references/reviewers/sc-4-1-2-i-name.md +4 -5
- package/skills/experience-accessibility-validate/references/reviewers/sc-4-1-2-ii-role.md +1 -4
- package/skills/experience-accessibility-validate/references/reviewers/sc-4-1-2-iii-value.md +0 -4
- package/skills/experience-cms-content-generate/SKILL.md +1 -1
- package/skills/experience-cms-content-render/SKILL.md +3 -3
- package/skills/experience-cms-content-render/assets/angular/MediaRenderer.component.ts +35 -7
- package/skills/experience-cms-content-render/assets/angular/cms-item.service.ts +13 -4
- package/skills/experience-cms-content-render/assets/react/MediaRenderer.tsx +30 -8
- package/skills/experience-cms-content-render/assets/react/useCmsItem.ts +14 -4
- package/skills/experience-cms-content-render/assets/shared/cmsCore.types.ts +11 -1
- package/skills/experience-cms-content-render/assets/shared/externalRefs.ts +1 -1
- package/skills/experience-cms-content-render/assets/shared/mediaLabels.ts +62 -0
- package/skills/experience-cms-content-render/references/init-scaffold.md +13 -6
- package/skills/experience-content-media-stock-image-search/SKILL.md +0 -1
- package/skills/experience-lwc-accessibility-jest-run/SKILL.md +104 -25
- package/skills/experience-lwc-accessibility-jest-run/assets/jest.config.js +4 -0
- package/skills/experience-lwc-accessibility-jest-run/assets/sa11y-jest-setup.js +5 -0
- package/skills/experience-lwc-design-generate/SKILL.md +1 -1
- package/skills/experience-lwc-legacy-migrate/SKILL.md +212 -0
- package/skills/experience-lwc-legacy-migrate/assets/lo20-host-page-template.html +192 -0
- package/skills/experience-lwc-legacy-migrate/references/aura-to-lwc-completeness-checklist.md +188 -0
- package/skills/experience-lwc-legacy-migrate/references/lightning-out-2-system-reference.md +495 -0
- package/skills/experience-lwc-legacy-migrate/references/lightning-out-beta-to-2-migration.md +824 -0
- package/skills/experience-lwc-legacy-migrate/scripts/convert-lo-names.py +94 -0
- package/skills/experience-lwc-legacy-migrate/scripts/validate-lo20-page.py +133 -0
- package/skills/experience-ui-bundle-app-coordinate/SKILL.md +5 -5
- package/skills/experience-ui-bundle-features-generate/SKILL.md +65 -36
- package/skills/experience-ui-bundle-features-generate/references/angular/features.md +74 -0
- package/skills/experience-ui-bundle-features-generate/references/react/features.md +59 -0
- package/skills/experience-ui-bundle-features-generate/scripts/detect-framework.sh +73 -0
- package/skills/experience-ui-bundle-frontend-generate/SKILL.md +2 -2
- package/skills/experience-ui-bundle-metadata-generate/SKILL.md +26 -14
- package/skills/experience-ui-bundle-metadata-generate/references/angular-metadata-generate.md +37 -0
- package/skills/experience-ui-bundle-metadata-generate/references/react-metadata-generate.md +32 -0
- package/skills/experience-ui-bundle-metadata-generate/scripts/detect-framework.sh +73 -0
- package/skills/experience-ui-bundle-metadata-generate/scripts/verify-bundle-location.sh +30 -8
- package/skills/experience-ui-bundle-project-generate/SKILL.md +1 -1
- package/skills/field-service-data-capture-migrate/SKILL.md +391 -0
- package/skills/field-service-data-capture-migrate/assets/sample-legacy-flow.flow-meta.xml +163 -0
- package/skills/field-service-data-capture-migrate/references/architecture-notes.md +40 -0
- package/skills/field-service-data-capture-migrate/references/component-mapping.md +278 -0
- package/skills/field-service-data-capture-migrate/references/converter-builder-contract.md +183 -0
- package/skills/field-service-data-capture-migrate/references/dc-platform-rules.md +130 -0
- package/skills/field-service-data-capture-migrate/references/fsm-dc-capability-catalog.md +215 -0
- package/skills/field-service-data-capture-migrate/references/known-deploy-errors.md +36 -0
- package/skills/field-service-data-capture-migrate/references/migration-checklist.md +121 -0
- package/skills/field-service-data-capture-migrate/references/transformer-rules.md +132 -0
- package/skills/field-service-data-capture-migrate/scripts/analyze_flow.py +277 -0
- package/skills/field-service-data-capture-migrate/scripts/convert_to_dc_spec.py +1311 -0
- package/skills/field-service-data-capture-migrate/scripts/cud_analysis.py +402 -0
- package/skills/field-service-data-capture-migrate/scripts/deploy_flow.sh +230 -0
- package/skills/field-service-data-capture-migrate/scripts/discover_subflow_tree.py +230 -0
- package/skills/field-service-data-capture-migrate/scripts/flow_input.py +186 -0
- package/skills/field-service-data-capture-migrate/scripts/flow_xml_utils.py +39 -0
- package/skills/field-service-data-capture-migrate/scripts/fsm_dc_catalog.py +413 -0
- package/skills/field-service-data-capture-migrate/scripts/generate_summary.py +199 -0
- package/skills/field-service-data-capture-migrate/scripts/json_to_flow_xml.py +146 -0
- package/skills/field-service-data-capture-migrate/scripts/manual_review_flags.py +42 -0
- package/skills/field-service-data-capture-migrate/scripts/retrieve_flow_rest.sh +155 -0
- package/skills/field-service-data-capture-migrate/scripts/subflow_names.py +49 -0
- package/skills/field-service-data-capture-migrate/scripts/transform_flow.py +1661 -0
- package/skills/field-service-data-capture-migrate/scripts/validate_flow.sh +147 -0
- package/skills/field-service-data-capture-migrate/scripts/vendor/build_flow.py +1258 -0
- package/skills/integration-connectivity-connected-app-configure/SKILL.md +5 -5
- package/skills/integration-connectivity-generate/SKILL.md +10 -10
- package/skills/integration-connectivity-generate/scripts/configure-named-credential.sh +2 -2
- package/skills/life-sciences-fieldsalesrep-coordinate/SKILL.md +1 -1
- package/skills/life-sciences-fieldsalesrep-coordinate/references/orchestration-flow.md +1 -1
- package/skills/life-sciences-kam-coordinate/references/stage-5-data-and-plan-templates-overview.md +14 -1
- package/skills/mobile-apps-create/SKILL.md +1 -1
- package/skills/{platform-agentsetup-categories-fetch → platform-agenticsetup-categories-get}/SKILL.md +7 -7
- package/skills/platform-apex-test-generate/SKILL.md +1 -1
- package/skills/platform-apex-test-run/SKILL.md +1 -1
- package/skills/platform-custom-field-generate/SKILL.md +1 -1
- package/skills/platform-custom-lightning-type-generate/SKILL.md +1 -1
- package/skills/platform-custom-metadata-type-generate/SKILL.md +1 -1
- package/skills/platform-custom-object-generate/SKILL.md +1 -1
- package/skills/platform-custom-report-type-generate/SKILL.md +1 -1
- package/skills/platform-custom-setting-generate/SKILL.md +1 -1
- package/skills/platform-data-and-tooling-api-context-get/SKILL.md +1 -1
- package/skills/platform-data-manage/SKILL.md +10 -10
- package/skills/platform-flexipage-generate/SKILL.md +1 -1
- package/skills/platform-lightning-app-coordinate/SKILL.md +1 -1
- package/skills/platform-metadata-api-context-get/SKILL.md +1 -1
- package/skills/platform-metadata-retrieve/SKILL.md +1 -1
- package/skills/platform-permission-set-generate/SKILL.md +1 -1
- package/skills/platform-report-generate/SKILL.md +1 -1
- package/skills/platform-salesforce-connect-adapter-generate/SKILL.md +4 -4
- package/skills/platform-sandbox-configure/SKILL.md +108 -133
- package/skills/platform-sandbox-configure/references/api-response-shapes.md +39 -0
- package/skills/platform-sandbox-configure/references/definition-file-approach.md +73 -0
- package/skills/platform-sharing-owd-configure/SKILL.md +1 -1
- package/skills/platform-sharing-rules-generate/SKILL.md +1 -1
- package/skills/platform-soql-query/SKILL.md +1 -1
- package/skills/platform-value-set-generate/SKILL.md +1 -1
- package/skills/platform-widget-generate/SKILL.md +26 -18
- package/skills/platform-widget-generate/examples/formulas.json +191 -0
- package/skills/platform-widget-generate/references/widget-formulas.md +123 -0
- package/skills/platform-widget-generate/references/widget-meta-directives.md +4 -4
- package/skills/sales-call-scoring-configure/SKILL.md +318 -0
- package/skills/sales-call-scoring-configure/assets/best-practice-competencies.json +67 -0
- package/skills/sales-call-scoring-configure/assets/ootb-competencies.json +58 -0
- package/skills/sales-call-scoring-configure/references/admin-communication.md +79 -0
- package/skills/sales-call-scoring-configure/references/competency-crud.md +130 -0
- package/skills/sales-call-scoring-configure/scripts/create-competency.sh +166 -0
- package/skills/sales-call-scoring-configure/scripts/create-custom-competency.sh +166 -0
- package/skills/sales-call-scoring-configure/scripts/edit-competency.sh +185 -0
- package/skills/sales-call-scoring-configure/scripts/edit-custom-competency.sh +190 -0
- package/skills/sales-call-scoring-configure/scripts/enable-call-scoring.sh +436 -0
- package/skills/sales-call-scoring-configure/scripts/get-competency.sh +72 -0
- package/skills/sales-call-scoring-configure/scripts/get-custom-competency.sh +78 -0
- package/skills/sales-call-scoring-configure/scripts/install-best-practice-competencies.sh +223 -0
- package/skills/sales-call-scoring-configure/scripts/install-ootb-competencies.sh +237 -0
- package/skills/sales-call-scoring-configure/scripts/list-competencies.sh +99 -0
- package/skills/sales-call-scoring-configure/scripts/shared/auth.sh +116 -0
- package/skills/sales-call-scoring-configure/scripts/shared/cap.sh +86 -0
- package/skills/sales-call-scoring-configure/scripts/shared/soap.sh +267 -0
- package/skills/sales-call-scoring-configure/scripts/shared/soql.sh +25 -0
- package/skills/sales-call-scoring-configure/scripts/toggle-competency.sh +112 -0
- package/skills/sales-call-scoring-configure/scripts/toggle-custom-competency.sh +125 -0
- package/skills/sales-call-scoring-configure/scripts/verify-ootb-competencies.sh +116 -0
- package/skills/service-agentforce-human-escalation-configure/SKILL.md +4 -0
- package/skills/service-agentforce-human-escalation-configure/scripts/tests/_bootstrap.py +26 -6
- package/skills/service-agentforce-human-escalation-configure/scripts/tests/test_escalation_contracts.py +5 -4
- package/skills/service-concierge-portal-generate/references/portal-deploy-runbook.md +1 -0
- package/skills/service-de-channel-activate/SKILL.md +6 -5
- package/skills/service-de-channel-create/SKILL.md +3 -3
- package/skills/service-de-channel-routing-configure/SKILL.md +3 -3
- package/skills/service-de-channel-settings-configure/SKILL.md +195 -0
- package/skills/service-de-channel-settings-configure/references/channel-settings-facets.md +152 -0
- package/skills/{service-de-channel-consent-configure → service-de-channel-settings-configure}/references/gotchas.md +3 -3
- package/skills/{service-de-channel-consent-configure → service-de-channel-settings-configure}/references/language-keywords.md +4 -4
- package/skills/{service-de-channel-consent-configure → service-de-channel-settings-configure}/references/worked-examples.md +14 -14
- package/skills/service-de-headless-channel-configure/SKILL.md +6 -6
- package/skills/service-de-headless-channel-configure/references/worked-examples.md +1 -1
- package/skills/service-de-waba-integrate/SKILL.md +2 -1
- package/skills/service-digital-engagement-deployment-configure/SKILL.md +2 -0
- package/skills/service-digital-engagement-deployment-configure/scripts/check-api-version.sh +29 -0
- package/skills/service-email-to-case-configure/SKILL.md +1 -1
- package/skills/service-helpagent-coordinate/SKILL.md +3 -2
- package/skills/service-helpagent-coordinate/references/channel-voice.md +15 -4
- package/skills/service-itsm-agentic-setup-agentforce-coordinate/SKILL.md +49 -31
- package/skills/service-itsm-agentic-setup-agentforce-coordinate/examples/output-templates.md +71 -6
- package/skills/service-itsm-agentic-setup-agentforce-coordinate/scripts/verify-child-verdict.mjs +26 -4
- package/skills/service-itsm-agentic-setup-cmdb-access-assign/SKILL.md +35 -10
- package/skills/service-itsm-agentic-setup-cmdb-access-assign/references/mcp-invocation.md +25 -5
- package/skills/service-itsm-agentic-setup-cmdb-bundle-deploy/SKILL.md +1 -1
- package/skills/service-itsm-agentic-setup-cmdb-configure/SKILL.md +5 -5
- package/skills/service-itsm-agentic-setup-cmdb-configure/references/mcp-invocation.md +1 -1
- package/skills/service-itsm-agentic-setup-cmdb-coordinate/SKILL.md +2 -2
- package/skills/service-itsm-agentic-setup-cmdb-coordinate/examples/output-templates.md +1 -1
- package/skills/service-itsm-agentic-setup-cmdb-discovery-configure/SKILL.md +25 -15
- package/skills/service-itsm-agentic-setup-employee-agent-configure/SKILL.md +8 -5
- package/skills/service-itsm-agentic-setup-employee-agent-configure/references/report-format.md +4 -2
- package/skills/service-itsm-agentic-setup-employee-agent-configure/references/workflow-detail.md +1 -1
- package/skills/service-itsm-agentic-setup-employee-agent-configure/scripts/render-report.mjs +29 -16
- package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/SKILL.md +8 -5
- package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/references/report-format.md +4 -2
- package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/references/workflow-detail.md +1 -1
- package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/scripts/render-report.mjs +29 -16
- package/skills/service-itsm-agentic-setup-incident-management/SKILL.md +138 -68
- package/skills/service-itsm-agentic-setup-incident-management/examples/output-templates.md +31 -15
- package/skills/service-itsm-agentic-setup-incident-management/references/incident-persona-psg.md +156 -0
- package/skills/service-itsm-agentic-setup-incident-management/references/incident-preferences.md +148 -0
- package/skills/service-itsm-agentic-setup-incident-management/references/major-incident-management.md +162 -0
- package/skills/service-itsm-agentic-setup-incident-management/references/service-mgmt-privilege.md +149 -0
- package/skills/service-itsm-agentic-setup-incident-sla-configure/SKILL.md +103 -89
- package/skills/service-itsm-agentic-setup-incident-sla-configure/assets/attach-milestone.json +2 -2
- package/skills/service-itsm-agentic-setup-incident-sla-configure/assets/predefined-incident-policy.json +21 -39
- package/skills/service-itsm-agentic-setup-incident-sla-configure/examples/milestone-patterns.md +3 -3
- package/skills/service-itsm-agentic-setup-incident-sla-configure/examples/output-templates.md +18 -1
- package/skills/service-itsm-agentic-setup-incident-sla-configure/references/mcp-invocation.md +63 -37
- package/skills/service-omni-attribute-routing-configure/SKILL.md +53 -0
- package/skills/service-omni-attribute-routing-configure/references/api-notes.md +7 -0
- package/skills/service-omni-attribute-routing-configure/scripts/configure-and-report.sh +224 -0
- package/skills/service-omni-attribute-routing-configure/scripts/tests/test_attribute_routing_contracts.py +137 -0
- package/skills/service-omni-channel-inventory-analyze/SKILL.md +96 -0
- package/skills/service-omni-channel-inventory-analyze/references/api-notes.md +52 -0
- package/skills/service-omni-channel-inventory-analyze/scripts/analyze.sh +185 -0
- package/skills/service-omni-channel-inventory-analyze/scripts/tests/_bootstrap.py +84 -0
- package/skills/service-omni-channel-inventory-analyze/scripts/tests/test_inventory_analyze_contracts.py +141 -0
- package/skills/service-omni-channel-limits-analyze/SKILL.md +70 -0
- package/skills/service-omni-channel-limits-analyze/references/api-notes.md +22 -0
- package/skills/service-omni-channel-limits-analyze/scripts/analyze.sh +159 -0
- package/skills/service-omni-channel-limits-analyze/scripts/tests/_bootstrap.py +87 -0
- package/skills/service-omni-channel-limits-analyze/scripts/tests/test_limits_analyze_contracts.py +99 -0
- package/skills/service-omni-channel-setup-coordinate/SKILL.md +12 -20
- package/skills/service-omni-channel-setup-coordinate/scripts/integration-driver.sh +55 -5
- package/skills/service-omni-channel-setup-coordinate/scripts/tests/test_omni_contracts.py +38 -0
- package/skills/service-omni-command-center-analyze/SKILL.md +2 -2
- package/skills/service-omni-command-center-analyze/scripts/analyze.sh +3 -3
- package/skills/dx-org-manage/examples/snapshots/error_output.json +0 -9
- package/skills/dx-org-manage/examples/snapshots/success_output.json +0 -15
- package/skills/dx-org-manage/references/cli_flags.md +0 -67
- package/skills/dx-org-manage/references/creating-snapshot.md +0 -103
- package/skills/dx-org-manage/references/snapshot_usage.md +0 -74
- package/skills/experience-accessibility-validate/scripts/contrast-ratio.py +0 -153
- package/skills/experience-lwc-accessibility-jest-run/references/running-sa11y-jest-tests.md +0 -205
- package/skills/experience-ui-bundle-features-generate/scripts/verify-react-bundle.sh +0 -14
- /package/skills/experience-ui-bundle-features-generate/references/{conflict-resolution-schema.json → common/conflict-resolution-schema.json} +0 -0
- /package/skills/{platform-agentsetup-categories-fetch → platform-agenticsetup-categories-get}/references/api-response-schema.md +0 -0
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: service-omni-channel-inventory-analyze
|
|
3
|
+
description: "Use to inventory configured Omni-Channel instances across Voice, Email, Chat, SMS, Facebook, WhatsApp, and In-App, including each channel's Queue, Flow, or Other routing target, without changing the org. TRIGGER when: list Omni channels, inventory Omni routing, inspect channel routing targets, analyze Omni-Channel setup, or find configured service channels. Do not use to create, update, activate, or delete channels or routing configuration."
|
|
4
|
+
allowed-tools: Bash Read Write Grep Glob
|
|
5
|
+
metadata:
|
|
6
|
+
version: "1.0"
|
|
7
|
+
domains: ["Service"]
|
|
8
|
+
minApiVersion: "66.0"
|
|
9
|
+
relatedSkills:
|
|
10
|
+
- "service-omni-channel-setup-coordinate"
|
|
11
|
+
- "service-omni-queue-routing-config-deploy"
|
|
12
|
+
- "service-omni-routing-flow-deploy"
|
|
13
|
+
- "service-omni-service-channel-configure"
|
|
14
|
+
accessCheck:
|
|
15
|
+
- type: license
|
|
16
|
+
value: ServiceCloud
|
|
17
|
+
cliTools:
|
|
18
|
+
- tool: ["jq"]
|
|
19
|
+
semver: ">=1.6"
|
|
20
|
+
- tool: ["python3"]
|
|
21
|
+
semver: ">=3.8"
|
|
22
|
+
- tool: ["sf"]
|
|
23
|
+
semver: ">=2.139.6"
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
# service-omni-channel-inventory-analyze
|
|
27
|
+
|
|
28
|
+
Read the org's complete Omni-Channel setup-home inventory without making changes. The supported headless operation combines Voice, Email, Live Chat, and Messaging channel families and returns their derived routing type and routing target. Use this analyzer before changing an unfamiliar org or when a user asks what Omni channels and routing are already configured.
|
|
29
|
+
|
|
30
|
+
## Inputs
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
bash scripts/analyze.sh <org-alias> [--type Voice|Email|Chat|SMS|Facebook|WhatsApp|InApp]
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
- `org-alias` is required and must already be authenticated with Salesforce CLI.
|
|
37
|
+
- `--type` is optional and case-insensitive. It filters the returned inventory without changing the source data.
|
|
38
|
+
|
|
39
|
+
## Preconditions and safety
|
|
40
|
+
|
|
41
|
+
- Target org authenticated through `sf` CLI.
|
|
42
|
+
- API v66.0 or later.
|
|
43
|
+
- The running user must have **View Setup and Configuration** and **Customize Application**.
|
|
44
|
+
- This skill is read-only. The shared Headless dispatcher uses an HTTP POST envelope, but the invoked `getChannelInstances` controller action performs no org mutation.
|
|
45
|
+
- Use `sf api request rest`; never extract or print an access token.
|
|
46
|
+
|
|
47
|
+
## Run
|
|
48
|
+
|
|
49
|
+
Invoke `scripts/analyze.sh` with the target org and optional channel type. The script POSTs the approved `OmniChannelInstancesController.getChannelInstances` envelope to the shared Headless dispatcher, validates the inner response, optionally filters it, and returns a stable summary plus the matching channel instances.
|
|
50
|
+
|
|
51
|
+
The inventory includes these source fields when the platform supplies them:
|
|
52
|
+
|
|
53
|
+
| Field | Meaning |
|
|
54
|
+
|---|---|
|
|
55
|
+
| `id`, `label` | Channel identity and display label |
|
|
56
|
+
| `omniChannelInstanceType` | Voice, Email, Chat, SMS, Facebook, WhatsApp, or InApp |
|
|
57
|
+
| `routingType` | Queue, Flow, or Other |
|
|
58
|
+
| `routingRequirementId`, `routingRequirementLabel` | Queue, flow, or other routing target |
|
|
59
|
+
| `channelUrl`, `routingRequirementUrl` | Setup navigation links synthesized by the platform |
|
|
60
|
+
| `icon`, `messages` | Display metadata and platform-provided notices |
|
|
61
|
+
|
|
62
|
+
## Output contract
|
|
63
|
+
|
|
64
|
+
The script emits one JSON object:
|
|
65
|
+
|
|
66
|
+
- `skill`: `service-omni-channel-inventory-analyze`
|
|
67
|
+
- `status`: `analyzed` or `blocked`
|
|
68
|
+
- `filter`: canonical channel type or `null`
|
|
69
|
+
- `summary.total_configured`: number of instances returned by the platform before filtering
|
|
70
|
+
- `summary.returned`: number of instances in `instances`
|
|
71
|
+
- `summary.by_channel_type`: counts for the returned inventory
|
|
72
|
+
- `summary.by_routing_type`: Queue, Flow, and Other counts for the returned inventory
|
|
73
|
+
- `instances`: normalized channel instance objects
|
|
74
|
+
- `blocking_issue`: actionable failure reason or `null`
|
|
75
|
+
|
|
76
|
+
An empty `instances` array with `status: analyzed` is a valid inventory result. Authentication, authorization, endpoint, or malformed-response failures return `status: blocked` and exit non-zero; never reinterpret them as an empty org.
|
|
77
|
+
|
|
78
|
+
## Boundaries
|
|
79
|
+
|
|
80
|
+
- Do not replace this operation with independent SOQL queries. No single public object API reproduces the platform's cross-family aggregation, routing-type derivation, and setup URL generation.
|
|
81
|
+
- Do not infer missing routing fields. Preserve `null` when the platform does not supply a value.
|
|
82
|
+
- Do not use the returned setup URLs as mutation endpoints.
|
|
83
|
+
- For configuration changes, hand off to the appropriate leaf skill only after presenting the inventory to the user.
|
|
84
|
+
|
|
85
|
+
| Requested follow-up | Skill |
|
|
86
|
+
|---|---|
|
|
87
|
+
| Coordinate a complete Omni setup | `service-omni-channel-setup-coordinate` |
|
|
88
|
+
| Create or reuse a Service Channel | `service-omni-service-channel-configure` |
|
|
89
|
+
| Create or update a queue routing configuration | `service-omni-queue-routing-config-deploy` |
|
|
90
|
+
| Deploy a QueueBased or SkillsBased routing flow | `service-omni-routing-flow-deploy` |
|
|
91
|
+
|
|
92
|
+
## References
|
|
93
|
+
|
|
94
|
+
| File | When to read |
|
|
95
|
+
|---|---|
|
|
96
|
+
| `references/api-notes.md` | Supported endpoint, permission boundary, response fields, and why the headless operation is canonical |
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Omni-Channel inventory API notes
|
|
2
|
+
|
|
3
|
+
## Supported operation
|
|
4
|
+
|
|
5
|
+
```text
|
|
6
|
+
POST /services/data/v66.0/headless/invoke
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
Call it through `sf api request rest --method POST --target-org <alias>` with this invoke envelope:
|
|
10
|
+
|
|
11
|
+
```json
|
|
12
|
+
{
|
|
13
|
+
"className": "ui.omnichannel.home.controller.OmniChannelInstancesController",
|
|
14
|
+
"action": "getChannelInstances"
|
|
15
|
+
}
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
The Project Codey route is logically read-only even though the shared dispatcher transport is POST-only. Salesforce CLI owns authentication, so do not extract the org access token or build an Authorization header. Treat the inner `status_code` as authoritative and read the inventory from `body`.
|
|
19
|
+
|
|
20
|
+
Project Codey's approved `OmniChannelHome` source-of-record identifies this Aura-backed headless operation as canonical. The owning-team review was approved on 2026-08-20. The operation aggregates providers for Live Chat buttons, Messaging channels, Service channels, and Voice, then derives routing labels and setup URLs. Independent Connect, Tooling, Metadata, or sObject calls do not reproduce that contract.
|
|
21
|
+
|
|
22
|
+
## Permission boundary
|
|
23
|
+
|
|
24
|
+
The running user needs both customer-facing permissions:
|
|
25
|
+
|
|
26
|
+
- View Setup and Configuration
|
|
27
|
+
- Customize Application
|
|
28
|
+
|
|
29
|
+
A permission failure is not evidence that the org has no channel instances. Return `status: blocked` and ask the user to run with an appropriately authorized setup user.
|
|
30
|
+
|
|
31
|
+
## Response fields
|
|
32
|
+
|
|
33
|
+
Each channel instance can include:
|
|
34
|
+
|
|
35
|
+
```text
|
|
36
|
+
id
|
|
37
|
+
label
|
|
38
|
+
channelUrl
|
|
39
|
+
omniChannelInstanceType
|
|
40
|
+
routingType
|
|
41
|
+
icon
|
|
42
|
+
routingRequirementId
|
|
43
|
+
routingRequirementLabel
|
|
44
|
+
routingRequirementUrl
|
|
45
|
+
messages
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Known channel-type labels are `Voice`, `Email`, `Chat`, `SMS`, `Facebook`, `WhatsApp`, and `InApp`. Known routing labels are `Queue`, `Flow`, and `Other`. The script accepts these labels case-insensitively and emits their canonical display spelling.
|
|
49
|
+
|
|
50
|
+
## Response handling
|
|
51
|
+
|
|
52
|
+
The supported operation's contract is an array of channel instances. The analyzer also tolerates a raw array or the same array nested under a standard `body`, `result`, `data`, `items`, `records`, `value`, or `channelInstances` envelope so Salesforce CLI response wrapping does not silently turn valid results into failures. Any other shape is blocked as malformed.
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
usage() {
|
|
5
|
+
echo "Usage: bash analyze.sh <org-alias> [--type Voice|Email|Chat|SMS|Facebook|WhatsApp|InApp]" >&2
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
emit_blocked() {
|
|
9
|
+
local issue="$1"
|
|
10
|
+
jq -n \
|
|
11
|
+
--argjson filter "$FILTER_JSON" \
|
|
12
|
+
--arg issue "$issue" \
|
|
13
|
+
'{
|
|
14
|
+
skill: "service-omni-channel-inventory-analyze",
|
|
15
|
+
status: "blocked",
|
|
16
|
+
filter: $filter,
|
|
17
|
+
summary: {total_configured: null, returned: 0, by_channel_type: {}, by_routing_type: {}},
|
|
18
|
+
instances: [],
|
|
19
|
+
blocking_issue: $issue
|
|
20
|
+
}'
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
canonical_type() {
|
|
24
|
+
local normalized
|
|
25
|
+
normalized=$(printf '%s' "$1" | tr '[:upper:]' '[:lower:]')
|
|
26
|
+
case "$normalized" in
|
|
27
|
+
voice) echo "Voice" ;;
|
|
28
|
+
email) echo "Email" ;;
|
|
29
|
+
chat) echo "Chat" ;;
|
|
30
|
+
sms) echo "SMS" ;;
|
|
31
|
+
facebook) echo "Facebook" ;;
|
|
32
|
+
whatsapp) echo "WhatsApp" ;;
|
|
33
|
+
inapp) echo "InApp" ;;
|
|
34
|
+
*) return 1 ;;
|
|
35
|
+
esac
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if [ $# -lt 1 ]; then
|
|
39
|
+
usage
|
|
40
|
+
exit 1
|
|
41
|
+
fi
|
|
42
|
+
|
|
43
|
+
ORG="$1"
|
|
44
|
+
shift
|
|
45
|
+
FILTER=""
|
|
46
|
+
FILTER_JSON="null"
|
|
47
|
+
|
|
48
|
+
while [ $# -gt 0 ]; do
|
|
49
|
+
case "$1" in
|
|
50
|
+
--type)
|
|
51
|
+
if [ $# -lt 2 ] || [ -z "$2" ]; then
|
|
52
|
+
usage
|
|
53
|
+
exit 1
|
|
54
|
+
fi
|
|
55
|
+
if ! FILTER=$(canonical_type "$2"); then
|
|
56
|
+
FILTER_JSON="null"
|
|
57
|
+
emit_blocked "Unsupported channel type '$2'. Use Voice, Email, Chat, SMS, Facebook, WhatsApp, or InApp."
|
|
58
|
+
exit 1
|
|
59
|
+
fi
|
|
60
|
+
FILTER_JSON=$(jq -n --arg value "$FILTER" '$value')
|
|
61
|
+
shift 2
|
|
62
|
+
;;
|
|
63
|
+
--help|-h)
|
|
64
|
+
usage
|
|
65
|
+
exit 0
|
|
66
|
+
;;
|
|
67
|
+
*)
|
|
68
|
+
usage
|
|
69
|
+
exit 1
|
|
70
|
+
;;
|
|
71
|
+
esac
|
|
72
|
+
done
|
|
73
|
+
|
|
74
|
+
if ! FORCE_COLOR=0 sf org display --target-org "$ORG" --json >/dev/null 2>&1; then
|
|
75
|
+
emit_blocked "The target org is not authenticated or cannot be reached. Authenticate it with Salesforce CLI and retry."
|
|
76
|
+
exit 1
|
|
77
|
+
fi
|
|
78
|
+
|
|
79
|
+
WORK_DIR=$(mktemp -d "${TMPDIR:-/tmp}/omni-channel-inventory.XXXXXX")
|
|
80
|
+
trap 'rm -rf "$WORK_DIR"' EXIT
|
|
81
|
+
RAW_RESPONSE="$WORK_DIR/response.json"
|
|
82
|
+
ERROR_RESPONSE="$WORK_DIR/error.txt"
|
|
83
|
+
ENDPOINT="/services/data/v66.0/headless/invoke"
|
|
84
|
+
INVOKE_BODY=$(jq -cn '{
|
|
85
|
+
className:"ui.omnichannel.home.controller.OmniChannelInstancesController",
|
|
86
|
+
action:"getChannelInstances"
|
|
87
|
+
}')
|
|
88
|
+
|
|
89
|
+
if ! FORCE_COLOR=0 sf api request rest "$ENDPOINT" \
|
|
90
|
+
--method POST \
|
|
91
|
+
--body "$INVOKE_BODY" \
|
|
92
|
+
--target-org "$ORG" \
|
|
93
|
+
>"$RAW_RESPONSE" 2>"$ERROR_RESPONSE"; then
|
|
94
|
+
if grep -qiE 'insufficient|permission|access denied|forbidden' "$ERROR_RESPONSE"; then
|
|
95
|
+
emit_blocked "The inventory read was denied. Run it as a user with View Setup and Configuration and Customize Application."
|
|
96
|
+
else
|
|
97
|
+
emit_blocked "The supported Omni-Channel inventory operation failed. Verify org connectivity and feature availability, then retry."
|
|
98
|
+
fi
|
|
99
|
+
exit 1
|
|
100
|
+
fi
|
|
101
|
+
|
|
102
|
+
if ! jq -e . "$RAW_RESPONSE" >/dev/null 2>&1; then
|
|
103
|
+
emit_blocked "The inventory operation returned malformed JSON. No channel state was inferred."
|
|
104
|
+
exit 1
|
|
105
|
+
fi
|
|
106
|
+
|
|
107
|
+
INNER_STATUS=$(jq -r 'if type=="object" then .status_code // empty else empty end' "$RAW_RESPONSE")
|
|
108
|
+
if [ -n "$INNER_STATUS" ] && { ! [[ "$INNER_STATUS" =~ ^[0-9]+$ ]] || [ "$INNER_STATUS" -lt 200 ] || [ "$INNER_STATUS" -ge 300 ]; }; then
|
|
109
|
+
INNER_ERROR=$(jq -r '.body.message // .body[0].message // .body.error // "unknown error"' "$RAW_RESPONSE")
|
|
110
|
+
if printf '%s' "$INNER_ERROR" | grep -qiE 'insufficient|permission|access denied|forbidden'; then
|
|
111
|
+
emit_blocked "The inventory read was denied. Run it as a user with View Setup and Configuration and Customize Application."
|
|
112
|
+
else
|
|
113
|
+
emit_blocked "The supported Omni-Channel inventory operation failed: $INNER_ERROR."
|
|
114
|
+
fi
|
|
115
|
+
exit 1
|
|
116
|
+
fi
|
|
117
|
+
|
|
118
|
+
EXTRACT_FILTER='def inventory:
|
|
119
|
+
if type == "array" then .
|
|
120
|
+
elif (.body? | type) == "array" then .body
|
|
121
|
+
elif (.channelInstances? | type) == "array" then .channelInstances
|
|
122
|
+
elif (.result? | type) == "array" then .result
|
|
123
|
+
elif (.result?.channelInstances? | type) == "array" then .result.channelInstances
|
|
124
|
+
elif (.data? | type) == "array" then .data
|
|
125
|
+
elif (.items? | type) == "array" then .items
|
|
126
|
+
elif (.records? | type) == "array" then .records
|
|
127
|
+
elif (.value? | type) == "array" then .value
|
|
128
|
+
else null
|
|
129
|
+
end;
|
|
130
|
+
inventory'
|
|
131
|
+
|
|
132
|
+
if ! jq -e "$EXTRACT_FILTER | type == \"array\" and all(.[]; type == \"object\")" "$RAW_RESPONSE" >/dev/null 2>&1; then
|
|
133
|
+
emit_blocked "The inventory operation returned an unsupported response shape. No channel state was inferred."
|
|
134
|
+
exit 1
|
|
135
|
+
fi
|
|
136
|
+
|
|
137
|
+
jq \
|
|
138
|
+
--arg filter "$FILTER" \
|
|
139
|
+
"$EXTRACT_FILTER as \$source |
|
|
140
|
+
def channel_type:
|
|
141
|
+
(. // null) as \$value |
|
|
142
|
+
if (\$value | type) != \"string\" then null
|
|
143
|
+
elif (\$value | ascii_downcase) == \"voice\" then \"Voice\"
|
|
144
|
+
elif (\$value | ascii_downcase) == \"email\" then \"Email\"
|
|
145
|
+
elif (\$value | ascii_downcase) == \"chat\" then \"Chat\"
|
|
146
|
+
elif (\$value | ascii_downcase) == \"sms\" then \"SMS\"
|
|
147
|
+
elif (\$value | ascii_downcase) == \"facebook\" then \"Facebook\"
|
|
148
|
+
elif (\$value | ascii_downcase) == \"whatsapp\" then \"WhatsApp\"
|
|
149
|
+
elif (\$value | ascii_downcase) == \"inapp\" then \"InApp\"
|
|
150
|
+
else \$value
|
|
151
|
+
end;
|
|
152
|
+
def routing_type:
|
|
153
|
+
(. // null) as \$value |
|
|
154
|
+
if (\$value | type) != \"string\" then null
|
|
155
|
+
elif (\$value | ascii_downcase) == \"queue\" then \"Queue\"
|
|
156
|
+
elif (\$value | ascii_downcase) == \"flow\" then \"Flow\"
|
|
157
|
+
elif (\$value | ascii_downcase) == \"other\" then \"Other\"
|
|
158
|
+
else \$value
|
|
159
|
+
end;
|
|
160
|
+
[\$source[] | {
|
|
161
|
+
id: (.id // null),
|
|
162
|
+
label: (.label // null),
|
|
163
|
+
channelUrl: (.channelUrl // null),
|
|
164
|
+
omniChannelInstanceType: (.omniChannelInstanceType | channel_type),
|
|
165
|
+
routingType: (.routingType | routing_type),
|
|
166
|
+
icon: (.icon // null),
|
|
167
|
+
routingRequirementId: (.routingRequirementId // null),
|
|
168
|
+
routingRequirementLabel: (.routingRequirementLabel // null),
|
|
169
|
+
routingRequirementUrl: (.routingRequirementUrl // null),
|
|
170
|
+
messages: (if (.messages | type) == \"array\" then .messages else [] end)
|
|
171
|
+
}] as \$all |
|
|
172
|
+
(if \$filter == \"\" then \$all else [\$all[] | select(.omniChannelInstanceType == \$filter)] end) as \$returned |
|
|
173
|
+
{
|
|
174
|
+
skill: \"service-omni-channel-inventory-analyze\",
|
|
175
|
+
status: \"analyzed\",
|
|
176
|
+
filter: (if \$filter == \"\" then null else \$filter end),
|
|
177
|
+
summary: {
|
|
178
|
+
total_configured: (\$all | length),
|
|
179
|
+
returned: (\$returned | length),
|
|
180
|
+
by_channel_type: (reduce \$returned[] as \$item ({}; .[(\$item.omniChannelInstanceType // \"Unknown\")] += 1)),
|
|
181
|
+
by_routing_type: (reduce \$returned[] as \$item ({}; .[(\$item.routingType // \"Unknown\")] += 1))
|
|
182
|
+
},
|
|
183
|
+
instances: \$returned,
|
|
184
|
+
blocking_issue: null
|
|
185
|
+
}" "$RAW_RESPONSE"
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"""Fake Salesforce CLI harness for the Omni-Channel inventory analyzer."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import json
|
|
5
|
+
import os
|
|
6
|
+
import shutil
|
|
7
|
+
import subprocess
|
|
8
|
+
import tempfile
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
SKILLS_ROOT = Path(__file__).resolve().parents[3]
|
|
12
|
+
SCRIPT = SKILLS_ROOT / "service-omni-channel-inventory-analyze/scripts/analyze.sh"
|
|
13
|
+
|
|
14
|
+
_FAKE_SF = r'''#!/usr/bin/env bash
|
|
15
|
+
command_name="${1:-} ${2:-}"
|
|
16
|
+
case "$command_name" in
|
|
17
|
+
"org display")
|
|
18
|
+
if [ "${FAKE_AUTH:-ok}" = "fail" ]; then
|
|
19
|
+
echo '{"status":1,"message":"not authenticated"}' >&2
|
|
20
|
+
exit 1
|
|
21
|
+
fi
|
|
22
|
+
echo '{"status":0,"result":{}}'
|
|
23
|
+
;;
|
|
24
|
+
"api request")
|
|
25
|
+
printf '%s\n' "$*" >> "${FAKE_SF_LOG:?}"
|
|
26
|
+
if [ "${FAKE_API_STATUS:-ok}" = "fail" ]; then
|
|
27
|
+
echo "${FAKE_API_ERROR:-request failed}" >&2
|
|
28
|
+
exit 1
|
|
29
|
+
fi
|
|
30
|
+
printf '%s\n' "${FAKE_API_BODY:-[]}"
|
|
31
|
+
;;
|
|
32
|
+
*)
|
|
33
|
+
echo '{"status":1,"message":"unexpected command"}' >&2
|
|
34
|
+
exit 1
|
|
35
|
+
;;
|
|
36
|
+
esac
|
|
37
|
+
'''
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class FakeSf:
|
|
41
|
+
def __enter__(self):
|
|
42
|
+
self._directory = tempfile.mkdtemp(prefix="omni-inventory-fake-sf-")
|
|
43
|
+
self.log_path = Path(self._directory) / "commands.log"
|
|
44
|
+
sf_path = Path(self._directory) / "sf"
|
|
45
|
+
sf_path.write_text(_FAKE_SF)
|
|
46
|
+
sf_path.chmod(0o755)
|
|
47
|
+
return self
|
|
48
|
+
|
|
49
|
+
def run(self, args: list[str], **values):
|
|
50
|
+
env = dict(os.environ)
|
|
51
|
+
env["PATH"] = self._directory + os.pathsep + env.get("PATH", "")
|
|
52
|
+
env["FAKE_SF_LOG"] = str(self.log_path)
|
|
53
|
+
for key, value in values.items():
|
|
54
|
+
env[key] = json.dumps(value) if key == "FAKE_API_BODY" and not isinstance(value, str) else str(value)
|
|
55
|
+
process = subprocess.run(
|
|
56
|
+
["bash", str(SCRIPT), *args],
|
|
57
|
+
capture_output=True,
|
|
58
|
+
text=True,
|
|
59
|
+
env=env,
|
|
60
|
+
cwd=str(SKILLS_ROOT),
|
|
61
|
+
)
|
|
62
|
+
commands = self.log_path.read_text() if self.log_path.exists() else ""
|
|
63
|
+
return process.returncode, process.stdout + process.stderr, commands
|
|
64
|
+
|
|
65
|
+
def __exit__(self, *_):
|
|
66
|
+
shutil.rmtree(self._directory, ignore_errors=True)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def last_json(output: str) -> dict:
|
|
70
|
+
decoder = json.JSONDecoder()
|
|
71
|
+
parsed = None
|
|
72
|
+
position = 0
|
|
73
|
+
while position < len(output):
|
|
74
|
+
if output[position] == "{":
|
|
75
|
+
try:
|
|
76
|
+
candidate, end = decoder.raw_decode(output, position)
|
|
77
|
+
parsed = candidate
|
|
78
|
+
position = end
|
|
79
|
+
continue
|
|
80
|
+
except json.JSONDecodeError:
|
|
81
|
+
pass
|
|
82
|
+
position += 1
|
|
83
|
+
assert parsed is not None, f"No JSON object found in output:\n{output}"
|
|
84
|
+
return parsed
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import unittest
|
|
5
|
+
|
|
6
|
+
from _bootstrap import FakeSf, last_json
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
CHANNELS = [
|
|
10
|
+
{
|
|
11
|
+
"id": "0N9xx0000000001AAA",
|
|
12
|
+
"label": "Case Email",
|
|
13
|
+
"channelUrl": "/lightning/setup/ServiceChannelSettings/page?address=one",
|
|
14
|
+
"omniChannelInstanceType": "EMAIL",
|
|
15
|
+
"routingType": "QUEUE",
|
|
16
|
+
"routingRequirementId": "00Gxx0000000001AAA",
|
|
17
|
+
"routingRequirementLabel": "Case Queue",
|
|
18
|
+
"routingRequirementUrl": "/lightning/setup/Queues/page?address=one",
|
|
19
|
+
"messages": [],
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"id": "0M0xx0000000002AAA",
|
|
23
|
+
"label": "Support Voice",
|
|
24
|
+
"omniChannelInstanceType": "Voice",
|
|
25
|
+
"routingType": "Flow",
|
|
26
|
+
"routingRequirementLabel": "Voice Routing",
|
|
27
|
+
"messages": [{"severity": "info"}],
|
|
28
|
+
},
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class InputContractTests(unittest.TestCase):
|
|
33
|
+
def test_missing_org_is_rejected(self):
|
|
34
|
+
with FakeSf() as fake:
|
|
35
|
+
return_code, output, commands = fake.run([])
|
|
36
|
+
self.assertEqual(return_code, 1, output)
|
|
37
|
+
self.assertIn("Usage", output)
|
|
38
|
+
self.assertEqual(commands, "")
|
|
39
|
+
|
|
40
|
+
def test_invalid_type_is_blocked_before_api_call(self):
|
|
41
|
+
with FakeSf() as fake:
|
|
42
|
+
return_code, output, commands = fake.run(["org", "--type", "CarrierPigeon"])
|
|
43
|
+
self.assertEqual(return_code, 1, output)
|
|
44
|
+
result = last_json(output)
|
|
45
|
+
self.assertEqual(result["status"], "blocked")
|
|
46
|
+
self.assertIn("Unsupported channel type", result["blocking_issue"])
|
|
47
|
+
self.assertEqual(commands, "")
|
|
48
|
+
|
|
49
|
+
def test_unauthenticated_org_is_blocked(self):
|
|
50
|
+
with FakeSf() as fake:
|
|
51
|
+
return_code, output, commands = fake.run(["org"], FAKE_AUTH="fail")
|
|
52
|
+
self.assertEqual(return_code, 1, output)
|
|
53
|
+
self.assertEqual(last_json(output)["status"], "blocked")
|
|
54
|
+
self.assertEqual(commands, "")
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class InventoryContractTests(unittest.TestCase):
|
|
58
|
+
def test_raw_array_is_normalized_and_summarized(self):
|
|
59
|
+
with FakeSf() as fake:
|
|
60
|
+
return_code, output, commands = fake.run(["org"], FAKE_API_BODY=CHANNELS)
|
|
61
|
+
self.assertEqual(return_code, 0, output)
|
|
62
|
+
result = last_json(output)
|
|
63
|
+
self.assertEqual(result["status"], "analyzed")
|
|
64
|
+
self.assertEqual(result["summary"]["total_configured"], 2)
|
|
65
|
+
self.assertEqual(result["summary"]["returned"], 2)
|
|
66
|
+
self.assertEqual(result["summary"]["by_channel_type"], {"Email": 1, "Voice": 1})
|
|
67
|
+
self.assertEqual(result["summary"]["by_routing_type"], {"Flow": 1, "Queue": 1})
|
|
68
|
+
self.assertIn("--method POST", commands)
|
|
69
|
+
self.assertIn(
|
|
70
|
+
"/services/data/v66.0/headless/invoke",
|
|
71
|
+
commands,
|
|
72
|
+
)
|
|
73
|
+
self.assertIn("OmniChannelInstancesController", commands)
|
|
74
|
+
self.assertIn("getChannelInstances", commands)
|
|
75
|
+
self.assertNotRegex(commands, r"--method (GET|PATCH|DELETE)")
|
|
76
|
+
|
|
77
|
+
def test_direct_invocation_envelope_is_unwrapped(self):
|
|
78
|
+
with FakeSf() as fake:
|
|
79
|
+
return_code, output, _ = fake.run(
|
|
80
|
+
["org"],
|
|
81
|
+
FAKE_API_BODY={"status_code": 200, "body": CHANNELS},
|
|
82
|
+
)
|
|
83
|
+
self.assertEqual(return_code, 0, output)
|
|
84
|
+
result = last_json(output)
|
|
85
|
+
self.assertEqual(result["status"], "analyzed")
|
|
86
|
+
self.assertEqual(result["summary"]["total_configured"], 2)
|
|
87
|
+
|
|
88
|
+
def test_type_filter_is_case_insensitive(self):
|
|
89
|
+
with FakeSf() as fake:
|
|
90
|
+
return_code, output, _ = fake.run(["org", "--type", "voice"], FAKE_API_BODY={"result": CHANNELS})
|
|
91
|
+
self.assertEqual(return_code, 0, output)
|
|
92
|
+
result = last_json(output)
|
|
93
|
+
self.assertEqual(result["filter"], "Voice")
|
|
94
|
+
self.assertEqual(result["summary"]["total_configured"], 2)
|
|
95
|
+
self.assertEqual(result["summary"]["returned"], 1)
|
|
96
|
+
self.assertEqual(result["instances"][0]["label"], "Support Voice")
|
|
97
|
+
|
|
98
|
+
def test_empty_inventory_is_valid(self):
|
|
99
|
+
with FakeSf() as fake:
|
|
100
|
+
return_code, output, _ = fake.run(["org"], FAKE_API_BODY=[])
|
|
101
|
+
self.assertEqual(return_code, 0, output)
|
|
102
|
+
result = last_json(output)
|
|
103
|
+
self.assertEqual(result["status"], "analyzed")
|
|
104
|
+
self.assertEqual(result["instances"], [])
|
|
105
|
+
self.assertEqual(result["summary"]["total_configured"], 0)
|
|
106
|
+
|
|
107
|
+
def test_permission_failure_is_not_reported_as_empty(self):
|
|
108
|
+
with FakeSf() as fake:
|
|
109
|
+
return_code, output, _ = fake.run(
|
|
110
|
+
["org"],
|
|
111
|
+
FAKE_API_STATUS="fail",
|
|
112
|
+
FAKE_API_ERROR="INSUFFICIENT_ACCESS: permission denied",
|
|
113
|
+
)
|
|
114
|
+
self.assertEqual(return_code, 1, output)
|
|
115
|
+
result = last_json(output)
|
|
116
|
+
self.assertEqual(result["status"], "blocked")
|
|
117
|
+
self.assertIsNone(result["summary"]["total_configured"])
|
|
118
|
+
self.assertIn("View Setup", result["blocking_issue"])
|
|
119
|
+
|
|
120
|
+
def test_inner_dispatch_failure_is_blocked(self):
|
|
121
|
+
with FakeSf() as fake:
|
|
122
|
+
return_code, output, _ = fake.run(
|
|
123
|
+
["org"],
|
|
124
|
+
FAKE_API_BODY={"status_code": 403, "body": {"message": "permission denied"}},
|
|
125
|
+
)
|
|
126
|
+
self.assertEqual(return_code, 1, output)
|
|
127
|
+
result = last_json(output)
|
|
128
|
+
self.assertEqual(result["status"], "blocked")
|
|
129
|
+
self.assertIn("View Setup", result["blocking_issue"])
|
|
130
|
+
|
|
131
|
+
def test_malformed_response_is_blocked(self):
|
|
132
|
+
with FakeSf() as fake:
|
|
133
|
+
return_code, output, _ = fake.run(["org"], FAKE_API_BODY={"unexpected": "shape"})
|
|
134
|
+
self.assertEqual(return_code, 1, output)
|
|
135
|
+
result = last_json(output)
|
|
136
|
+
self.assertEqual(result["status"], "blocked")
|
|
137
|
+
self.assertIn("unsupported response shape", result["blocking_issue"])
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
if __name__ == "__main__":
|
|
141
|
+
unittest.main(verbosity=2)
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: service-omni-channel-limits-analyze
|
|
3
|
+
description: "Use to report Omni-Channel Pending Service Routing usage without changing the org: query the current PendingServiceRouting count, compare it with an admin-supplied maximum or a labeled routing-model reference default, and explain which live values are unavailable through supported APIs. TRIGGER when: check Omni-Channel limits, inspect PSR limits, analyze PendingServiceRouting usage, review pending routing capacity, or report queued work usage. Do not use to change limits or scrape Salesforce Setup."
|
|
4
|
+
allowed-tools: Bash Read Write Grep Glob
|
|
5
|
+
metadata:
|
|
6
|
+
version: "1.0"
|
|
7
|
+
domains: ["Service"]
|
|
8
|
+
minApiVersion: "66.0"
|
|
9
|
+
relatedSkills:
|
|
10
|
+
- "service-omni-channel-inventory-analyze"
|
|
11
|
+
- "service-omni-channel-setup-coordinate"
|
|
12
|
+
accessCheck:
|
|
13
|
+
- type: license
|
|
14
|
+
value: ServiceCloud
|
|
15
|
+
cliTools:
|
|
16
|
+
- tool: ["jq"]
|
|
17
|
+
semver: ">=1.6"
|
|
18
|
+
- tool: ["python3"]
|
|
19
|
+
semver: ">=3.8"
|
|
20
|
+
- tool: ["sf"]
|
|
21
|
+
semver: ">=2.139.6"
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
# service-omni-channel-limits-analyze
|
|
25
|
+
|
|
26
|
+
Report the Pending Service Routing usage that can be obtained safely through supported customer-org APIs. The Salesforce **Setup → Omni-Channel → Limits** page is read-only and also displays internal values that are not exposed through REST, Tooling, Metadata, or Connect APIs. This skill reports the live PSR count and labels every non-verifiable value explicitly.
|
|
27
|
+
|
|
28
|
+
## Inputs
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
bash scripts/analyze.sh <org-alias> [--routing-model enhanced|legacy] [--max-psrs <integer>]
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
- `org-alias` is required and must already be authenticated with Salesforce CLI.
|
|
35
|
+
- `--routing-model` defaults to `enhanced`. It selects only the reference maximum used when `--max-psrs` is absent.
|
|
36
|
+
- `--max-psrs` accepts an administrator-provided maximum from 1 through 1,000,000. It is still labeled as not API-verified.
|
|
37
|
+
|
|
38
|
+
## Run
|
|
39
|
+
|
|
40
|
+
Invoke `scripts/analyze.sh` with the target org and rely on its JSON result for the live count, labeled comparison values, unavailable hourly values, and blocking details. The script invokes `sf org display` and `sf data query` internally; do not reproduce their query or calculation logic outside the script.
|
|
41
|
+
|
|
42
|
+
To verify the bundled script contract after a change, run `python3 scripts/tests/test_limits_analyze_contracts.py`; the test module loads its local `scripts/tests/_bootstrap.py` fixture automatically.
|
|
43
|
+
|
|
44
|
+
## Output contract
|
|
45
|
+
|
|
46
|
+
The script emits one JSON object:
|
|
47
|
+
|
|
48
|
+
- `skill`: `service-omni-channel-limits-analyze`
|
|
49
|
+
- `status`: `analyzed` or `blocked`
|
|
50
|
+
- `routing_model`: `enhanced` or `legacy`
|
|
51
|
+
- `metrics.current_pending_service_routings`: live count, comparison maximum, approximate percentage, and source labels
|
|
52
|
+
- `metrics.pending_service_routing_rate_per_hour`: unavailable live value, reference maximum, and source labels
|
|
53
|
+
- `caveats`: mandatory accuracy boundaries
|
|
54
|
+
- `blocking_issue`: actionable failure reason or `null`
|
|
55
|
+
|
|
56
|
+
If `PendingServiceRouting` cannot be queried, return `status: blocked`. Do not reinterpret an unavailable object, missing feature, or permission failure as zero usage.
|
|
57
|
+
|
|
58
|
+
## Boundaries
|
|
59
|
+
|
|
60
|
+
- Never scrape, replay, or submit the Visualforce Setup page. Its session state and controller are not a supported customer API.
|
|
61
|
+
- Never describe a reference default as the org's configured maximum.
|
|
62
|
+
- Never fabricate the live hourly rate or compute its utilization percentage.
|
|
63
|
+
- Never mutate routing, queues, agents, limits, or metadata.
|
|
64
|
+
- Treat the PSR count as a point-in-time snapshot that can change while work is routed.
|
|
65
|
+
- For a complete channel and routing inventory, use `service-omni-channel-inventory-analyze`.
|
|
66
|
+
- For a user-approved end-to-end Omni setup, hand off to `service-omni-channel-setup-coordinate`; this limits analyzer remains read-only.
|
|
67
|
+
|
|
68
|
+
## Reference
|
|
69
|
+
|
|
70
|
+
Read `references/api-notes.md` when explaining why the report cannot exactly reproduce every value on the Setup page.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Omni-Channel limits API notes
|
|
2
|
+
|
|
3
|
+
## Supported value
|
|
4
|
+
|
|
5
|
+
`SELECT COUNT() FROM PendingServiceRouting` provides a live, point-in-time PSR row count when Omni-Channel is enabled and the running user can access the object.
|
|
6
|
+
|
|
7
|
+
## Values not exposed by a supported API
|
|
8
|
+
|
|
9
|
+
The read-only Omni-Channel Limits Setup page also displays the org's effective maximum PSR count and a rolling hourly enqueue meter. Those values are read by Salesforce's internal page controller and are not exposed as fields on `PendingServiceRouting`, Tooling objects, Metadata types, Connect resources, or the standard REST org-limits response.
|
|
10
|
+
|
|
11
|
+
The skill therefore uses these values only as labeled references:
|
|
12
|
+
|
|
13
|
+
| Routing model | Reference maximum PSRs |
|
|
14
|
+
|---|---:|
|
|
15
|
+
| Enhanced | 300,000 |
|
|
16
|
+
| Legacy | 200,000 |
|
|
17
|
+
|
|
18
|
+
The reference hourly maximum is 45,000. Actual org values can differ, and live hourly usage is not retrievable. An administrator can supply a known PSR maximum with `--max-psrs`, but the script still records that it was not verified through an API.
|
|
19
|
+
|
|
20
|
+
## Full-fidelity product path
|
|
21
|
+
|
|
22
|
+
Exact reporting requires a Salesforce Core product API that exposes both maximums and the rolling hourly meter, ideally through the standard REST org-limits resource. Until that contract exists, do not scrape Setup HTML or model a fictional limits sObject.
|