@salesforce/afv-skills 1.55.0 → 1.56.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/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-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-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-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/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/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-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-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-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-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,190 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# sales_call_scoring: story="custom-competency-crud" owner="W-CUSTOM-CRUD"
|
|
3
|
+
#
|
|
4
|
+
# Edit a Coaching Competency's MasterLabel, Description, and/or
|
|
5
|
+
# EvaluationInstructions.
|
|
6
|
+
# Preserves Id, DeveloperName, IsActive, EvaluationScope, SourceCompetencyTemplate.
|
|
7
|
+
#
|
|
8
|
+
# --label renames the admin-facing display name (MasterLabel). DeveloperName
|
|
9
|
+
# is NOT renamed — it's the API name used by any downstream references and
|
|
10
|
+
# would risk breaking integrations to change.
|
|
11
|
+
#
|
|
12
|
+
# Companion of create-custom-competency.sh. Kept as its own file so the
|
|
13
|
+
# custom-CRUD story can extend it (e.g. accept scope change to
|
|
14
|
+
# SpecificCallsOnly + EvaluationConditions) without conflicting with the
|
|
15
|
+
# OOTB story's edit surface.
|
|
16
|
+
#
|
|
17
|
+
# Usage:
|
|
18
|
+
# ./edit-custom-competency.sh <org-alias> <name-or-id> --label "<new-label>"
|
|
19
|
+
# ./edit-custom-competency.sh <org-alias> <name-or-id> --instructions-file <file>
|
|
20
|
+
# ./edit-custom-competency.sh <org-alias> <name-or-id> --description "<new-desc>"
|
|
21
|
+
# ./edit-custom-competency.sh <org-alias> <name-or-id> --instructions "<literal>"
|
|
22
|
+
# (combine flags to update multiple fields at once)
|
|
23
|
+
|
|
24
|
+
set -euo pipefail
|
|
25
|
+
|
|
26
|
+
export NO_COLOR=1
|
|
27
|
+
export FORCE_COLOR=0
|
|
28
|
+
|
|
29
|
+
ORG_ALIAS="${1:-}"
|
|
30
|
+
IDENT="${2:-}"
|
|
31
|
+
shift 2 || true
|
|
32
|
+
|
|
33
|
+
if [[ -z "$ORG_ALIAS" || -z "$IDENT" ]]; then
|
|
34
|
+
echo "Usage: $0 <org-alias> <name-or-id> [--label \"<text>\"] [--instructions-file <file>] [--description \"<text>\"] [--instructions \"<text>\"]"
|
|
35
|
+
exit 1
|
|
36
|
+
fi
|
|
37
|
+
|
|
38
|
+
NEW_LABEL=""
|
|
39
|
+
NEW_INSTRUCTIONS=""
|
|
40
|
+
NEW_DESCRIPTION=""
|
|
41
|
+
|
|
42
|
+
while [[ $# -gt 0 ]]; do
|
|
43
|
+
case "$1" in
|
|
44
|
+
--label)
|
|
45
|
+
NEW_LABEL="$2"
|
|
46
|
+
shift 2
|
|
47
|
+
;;
|
|
48
|
+
--instructions-file)
|
|
49
|
+
if [[ ! -f "$2" ]]; then
|
|
50
|
+
echo "File not found: $2"; exit 1
|
|
51
|
+
fi
|
|
52
|
+
NEW_INSTRUCTIONS=$(cat "$2")
|
|
53
|
+
shift 2
|
|
54
|
+
;;
|
|
55
|
+
--instructions)
|
|
56
|
+
NEW_INSTRUCTIONS="$2"
|
|
57
|
+
shift 2
|
|
58
|
+
;;
|
|
59
|
+
--description)
|
|
60
|
+
NEW_DESCRIPTION="$2"
|
|
61
|
+
shift 2
|
|
62
|
+
;;
|
|
63
|
+
*)
|
|
64
|
+
echo "Error: Unknown flag: $1"; exit 1
|
|
65
|
+
;;
|
|
66
|
+
esac
|
|
67
|
+
done
|
|
68
|
+
|
|
69
|
+
if [[ -z "$NEW_LABEL" && -z "$NEW_INSTRUCTIONS" && -z "$NEW_DESCRIPTION" ]]; then
|
|
70
|
+
echo "Nothing to update — pass --label, --instructions[-file], and/or --description"
|
|
71
|
+
exit 1
|
|
72
|
+
fi
|
|
73
|
+
|
|
74
|
+
if [[ -n "$NEW_LABEL" ]]; then
|
|
75
|
+
LEN=${#NEW_LABEL}
|
|
76
|
+
# MasterLabel is a standard platform label field, capped at 80 chars.
|
|
77
|
+
if [[ "$LEN" -gt 80 ]]; then
|
|
78
|
+
echo "MasterLabel is ${LEN} chars — max is 80."
|
|
79
|
+
exit 1
|
|
80
|
+
fi
|
|
81
|
+
fi
|
|
82
|
+
|
|
83
|
+
if [[ -n "$NEW_INSTRUCTIONS" ]]; then
|
|
84
|
+
LEN=${#NEW_INSTRUCTIONS}
|
|
85
|
+
if [[ "$LEN" -gt 16000 ]]; then
|
|
86
|
+
echo "EvaluationInstructions is ${LEN} chars — max is 16000."
|
|
87
|
+
exit 1
|
|
88
|
+
fi
|
|
89
|
+
fi
|
|
90
|
+
|
|
91
|
+
if [[ -n "$NEW_DESCRIPTION" ]]; then
|
|
92
|
+
LEN=${#NEW_DESCRIPTION}
|
|
93
|
+
if [[ "$LEN" -gt 255 ]]; then
|
|
94
|
+
echo "Description is ${LEN} chars — max is 255."
|
|
95
|
+
exit 1
|
|
96
|
+
fi
|
|
97
|
+
fi
|
|
98
|
+
|
|
99
|
+
# ---------------------------------------------------------------------------
|
|
100
|
+
# Auth
|
|
101
|
+
# ---------------------------------------------------------------------------
|
|
102
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
103
|
+
# shellcheck source=shared/auth.sh
|
|
104
|
+
source "$SCRIPT_DIR/shared/auth.sh"
|
|
105
|
+
# shellcheck source=shared/soql.sh
|
|
106
|
+
source "$SCRIPT_DIR/shared/soql.sh"
|
|
107
|
+
|
|
108
|
+
resolve_org_auth "$ORG_ALIAS" || exit 1
|
|
109
|
+
|
|
110
|
+
TOOLING_BASE="services/data/v68.0/tooling"
|
|
111
|
+
|
|
112
|
+
# ---------------------------------------------------------------------------
|
|
113
|
+
# Resolve identifier
|
|
114
|
+
# ---------------------------------------------------------------------------
|
|
115
|
+
if [[ "$IDENT" =~ ^1nA[0-9a-zA-Z]{12,15}$ ]]; then
|
|
116
|
+
WHERE="Id='${IDENT}'"
|
|
117
|
+
else
|
|
118
|
+
SAFE=$(soql_escape "$IDENT")
|
|
119
|
+
WHERE="DeveloperName='${SAFE}' OR MasterLabel='${SAFE}'"
|
|
120
|
+
fi
|
|
121
|
+
QUERY_ENC=$(printf 'SELECT Id,MasterLabel,IsActive FROM EnablementCompetencyDef WHERE %s' "$WHERE" | jq -sRr @uri)
|
|
122
|
+
LOOKUP=$(sf api request rest "${TOOLING_BASE}/query/?q=${QUERY_ENC}" --target-org "$ORG_ALIAS" 2>/dev/null || true)
|
|
123
|
+
RC=$(echo "$LOOKUP" | jq -r '.totalSize // 0' 2>/dev/null || echo "0")
|
|
124
|
+
|
|
125
|
+
if [[ "$RC" == "0" ]]; then echo "No competency matched: $IDENT"; exit 1; fi
|
|
126
|
+
if [[ "$RC" != "1" ]]; then
|
|
127
|
+
echo "Multiple competencies matched '$IDENT' — refusing to bulk-edit."
|
|
128
|
+
echo "$LOOKUP" | jq -r '.records[] | " \(.Id) \(.MasterLabel)"'
|
|
129
|
+
exit 1
|
|
130
|
+
fi
|
|
131
|
+
|
|
132
|
+
REC_ID=$(echo "$LOOKUP" | jq -r '.records[0].Id')
|
|
133
|
+
REC_LABEL=$(echo "$LOOKUP" | jq -r '.records[0].MasterLabel')
|
|
134
|
+
REC_ACTIVE=$(echo "$LOOKUP" | jq -r '.records[0].IsActive')
|
|
135
|
+
|
|
136
|
+
echo "$REC_LABEL ($REC_ID) — IsActive=$REC_ACTIVE"
|
|
137
|
+
|
|
138
|
+
# ---------------------------------------------------------------------------
|
|
139
|
+
# Uniqueness gate for rename — mirrors the check create-custom-competency.sh
|
|
140
|
+
# runs on create. Excludes the target record's own Id so a no-op rename
|
|
141
|
+
# doesn't self-collide.
|
|
142
|
+
# ---------------------------------------------------------------------------
|
|
143
|
+
if [[ -n "$NEW_LABEL" && "$NEW_LABEL" != "$REC_LABEL" ]]; then
|
|
144
|
+
LABEL_ENC=$(printf "SELECT Id FROM EnablementCompetencyDef WHERE MasterLabel='%s' AND Id!='%s'" \
|
|
145
|
+
"$(soql_escape "$NEW_LABEL")" "$REC_ID" | jq -sRr @uri)
|
|
146
|
+
DUP=$( (sf api request rest "${TOOLING_BASE}/query/?q=${LABEL_ENC}" --target-org "$ORG_ALIAS" 2>/dev/null || true) \
|
|
147
|
+
| jq -r '.totalSize // 0' 2>/dev/null)
|
|
148
|
+
DUP="${DUP:-0}"
|
|
149
|
+
if [[ "$DUP" != "0" ]]; then
|
|
150
|
+
echo "Another competency already uses label '$NEW_LABEL' — refusing to duplicate."
|
|
151
|
+
echo " Choose a different label, or edit the existing record instead."
|
|
152
|
+
exit 1
|
|
153
|
+
fi
|
|
154
|
+
fi
|
|
155
|
+
|
|
156
|
+
# ---------------------------------------------------------------------------
|
|
157
|
+
# Build PATCH payload
|
|
158
|
+
# ---------------------------------------------------------------------------
|
|
159
|
+
PAYLOAD='{}'
|
|
160
|
+
if [[ -n "$NEW_LABEL" ]]; then
|
|
161
|
+
PAYLOAD=$(echo "$PAYLOAD" | jq --arg v "$NEW_LABEL" '. + {MasterLabel: $v}')
|
|
162
|
+
fi
|
|
163
|
+
if [[ -n "$NEW_INSTRUCTIONS" ]]; then
|
|
164
|
+
PAYLOAD=$(echo "$PAYLOAD" | jq --arg v "$NEW_INSTRUCTIONS" '. + {EvaluationInstructions: $v}')
|
|
165
|
+
fi
|
|
166
|
+
if [[ -n "$NEW_DESCRIPTION" ]]; then
|
|
167
|
+
PAYLOAD=$(echo "$PAYLOAD" | jq --arg v "$NEW_DESCRIPTION" '. + {Description: $v}')
|
|
168
|
+
fi
|
|
169
|
+
|
|
170
|
+
# sf api request rest exits non-zero on HTTP failure; success on Tooling
|
|
171
|
+
# PATCH is HTTP 204 (empty body). Capture both stdout and exit status.
|
|
172
|
+
if RESP_BODY=$(sf api request rest "${TOOLING_BASE}/sobjects/EnablementCompetencyDef/${REC_ID}" \
|
|
173
|
+
--target-org "$ORG_ALIAS" \
|
|
174
|
+
--method PATCH \
|
|
175
|
+
--header 'Content-Type: application/json' \
|
|
176
|
+
--body "$PAYLOAD" \
|
|
177
|
+
2>&1); then
|
|
178
|
+
if [[ -n "$NEW_LABEL" && "$NEW_LABEL" != "$REC_LABEL" ]]; then
|
|
179
|
+
echo "Renamed '$REC_LABEL' → '$NEW_LABEL'"
|
|
180
|
+
else
|
|
181
|
+
echo "Updated $REC_LABEL"
|
|
182
|
+
fi
|
|
183
|
+
[[ -n "$NEW_INSTRUCTIONS" ]] && echo " EvaluationInstructions: ${#NEW_INSTRUCTIONS} chars"
|
|
184
|
+
[[ -n "$NEW_DESCRIPTION" ]] && echo " Description: ${#NEW_DESCRIPTION} chars"
|
|
185
|
+
exit 0
|
|
186
|
+
else
|
|
187
|
+
echo "Error: PATCH failed"
|
|
188
|
+
echo " $RESP_BODY"
|
|
189
|
+
exit 1
|
|
190
|
+
fi
|
|
@@ -0,0 +1,436 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# Enable Call Coaching (a.k.a. AI Call Scoring / Coaching Competencies) on a
|
|
4
|
+
# Salesforce org via SOAP Metadata API.
|
|
5
|
+
#
|
|
6
|
+
# Handles the PRD-scoped org states for the ECI arm:
|
|
7
|
+
# 1. Not available → refuse with PRD-verbatim message.
|
|
8
|
+
# 2. Available, not enabled → enable Einstein GenAI if off, flip
|
|
9
|
+
# enableECICallScoring.
|
|
10
|
+
# 3. Enabled, no competencies → exit 0 with "ready for competency setup".
|
|
11
|
+
# 4. Enabled, competencies exist → exit 0 with "already enabled, N competencies".
|
|
12
|
+
#
|
|
13
|
+
# River Rush (Momentum) arm detection is deferred until that arm goes live —
|
|
14
|
+
# see SKILL.md. Re-add it as a 5th state / advisory when it does.
|
|
15
|
+
#
|
|
16
|
+
# Usage: ./enable-call-scoring.sh <org-alias>
|
|
17
|
+
|
|
18
|
+
set -euo pipefail
|
|
19
|
+
|
|
20
|
+
export NO_COLOR=1
|
|
21
|
+
export FORCE_COLOR=0
|
|
22
|
+
|
|
23
|
+
ORG_ALIAS="${1:-}"
|
|
24
|
+
|
|
25
|
+
if [[ -z "$ORG_ALIAS" ]]; then
|
|
26
|
+
echo "Error: Missing org alias"
|
|
27
|
+
echo "Usage: $0 <org-alias>"
|
|
28
|
+
exit 1
|
|
29
|
+
fi
|
|
30
|
+
|
|
31
|
+
echo "Enabling Call Coaching for org: $ORG_ALIAS"
|
|
32
|
+
echo ""
|
|
33
|
+
|
|
34
|
+
# ---------------------------------------------------------------------------
|
|
35
|
+
# Auth
|
|
36
|
+
# ---------------------------------------------------------------------------
|
|
37
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
38
|
+
# shellcheck source=shared/auth.sh
|
|
39
|
+
source "$SCRIPT_DIR/shared/auth.sh"
|
|
40
|
+
|
|
41
|
+
# SOAP Metadata API embeds the session token in the request body (see
|
|
42
|
+
# soap_read below). This is the only caller in the skill that still needs
|
|
43
|
+
# the raw ACCESS_TOKEN in shell scope — everything else goes through
|
|
44
|
+
# `sf api request rest`. See shared/auth.sh header for the rationale.
|
|
45
|
+
resolve_org_auth_with_token "$ORG_ALIAS" || exit 1
|
|
46
|
+
|
|
47
|
+
# Deliberately no "Org ID: ..." / "Instance URL: ..." banner here — those are
|
|
48
|
+
# admin-comms leaks (rule #5). The org alias is already the admin's mental
|
|
49
|
+
# handle; the record Id and pod URL belong in --verbose debug output only.
|
|
50
|
+
|
|
51
|
+
# ---------------------------------------------------------------------------
|
|
52
|
+
# Phase 0: ECI purchase gate — ConversationPilot OrgPermission
|
|
53
|
+
# ---------------------------------------------------------------------------
|
|
54
|
+
# ConversationPilot reflects whether the org is licensed for Einstein
|
|
55
|
+
# Conversation Insights at all, independent of whether an admin has flipped
|
|
56
|
+
# enableCallCoaching on in Setup. Checking it first lets us tell "you don't
|
|
57
|
+
# have ECI, go buy it" apart from "you have ECI, just turn it on" instead of
|
|
58
|
+
# inferring a license gap from a readMetadata fault on enableCallCoaching.
|
|
59
|
+
#
|
|
60
|
+
# Some org shapes (older sandboxes, non-EinsteinConversationInsights-provisioned
|
|
61
|
+
# orgs) reject the OrganizationSettingsDetail query with INVALID_TYPE. That's
|
|
62
|
+
# not a licensing signal — it's a metadata-availability signal. Treat any
|
|
63
|
+
# non-conclusive result as "unknown, defer to SOAP" and only refuse when the
|
|
64
|
+
# query explicitly comes back "false" (W-24022554).
|
|
65
|
+
CONVERSATIONPILOT_ENABLED=""
|
|
66
|
+
if CONVERSATIONPILOT_RAW=$(sf data query --target-org "$ORG_ALIAS" --use-tooling-api \
|
|
67
|
+
--query "SELECT SettingValue FROM OrganizationSettingsDetail WHERE SettingName='ConversationPilot' AND DataType='OrgPermissionSettings'" \
|
|
68
|
+
--json 2>/dev/null); then
|
|
69
|
+
CONVERSATIONPILOT_JSON=$(echo "$CONVERSATIONPILOT_RAW" | awk '/^{/,EOF')
|
|
70
|
+
if echo "$CONVERSATIONPILOT_JSON" | jq -e '.status == 0' >/dev/null 2>&1; then
|
|
71
|
+
CONVERSATIONPILOT_ENABLED=$(echo "$CONVERSATIONPILOT_JSON" | jq -r '.result.records[0].SettingValue // empty')
|
|
72
|
+
fi
|
|
73
|
+
fi
|
|
74
|
+
|
|
75
|
+
if [[ "$CONVERSATIONPILOT_ENABLED" == "false" ]]; then
|
|
76
|
+
echo "Call Coaching couldn't be turned on because this org doesn't have Einstein Conversation"
|
|
77
|
+
echo " Insights, which is required to use Call Coaching. To proceed, an admin can go to Setup →"
|
|
78
|
+
echo " Salesforce Go → Einstein Conversation Insights, or contact their sales representative,"
|
|
79
|
+
echo " to understand how to purchase ECI."
|
|
80
|
+
exit 1
|
|
81
|
+
fi
|
|
82
|
+
# If unknown (query rejected / no row), skip this pre-check and let the SOAP
|
|
83
|
+
# state detection below classify the org. TYPE_UNAVAILABLE on the SOAP read
|
|
84
|
+
# is the authoritative "you need Agentforce for Sales" signal.
|
|
85
|
+
|
|
86
|
+
# enableECICallScoring exposes at v68+ only.
|
|
87
|
+
SOAP_ENDPOINT="${INSTANCE_URL}/services/Soap/m/68.0"
|
|
88
|
+
|
|
89
|
+
# Bounds every curl call below so a hung network call can't stall the run
|
|
90
|
+
# indefinitely. 30s matches the Pipeline reference skill's setup-all.sh
|
|
91
|
+
# CURL_TIMEOUT — the only existing timeout precedent in this repo's
|
|
92
|
+
# SOAP-calling scripts.
|
|
93
|
+
readonly CURL_TIMEOUT=30
|
|
94
|
+
|
|
95
|
+
# ---------------------------------------------------------------------------
|
|
96
|
+
# SOAP helpers
|
|
97
|
+
# ---------------------------------------------------------------------------
|
|
98
|
+
# shellcheck source=shared/soap.sh
|
|
99
|
+
source "$SCRIPT_DIR/shared/soap.sh"
|
|
100
|
+
|
|
101
|
+
soap_read() {
|
|
102
|
+
local settings_type="$1"
|
|
103
|
+
local full_name="$2"
|
|
104
|
+
soap_call "readMetadata" "<?xml version='1.0' encoding='utf-8'?>
|
|
105
|
+
<soapenv:Envelope xmlns:soapenv='http://schemas.xmlsoap.org/soap/envelope/' xmlns:met='http://soap.sforce.com/2006/04/metadata'>
|
|
106
|
+
<soapenv:Header><met:SessionHeader><met:sessionId>${ACCESS_TOKEN}</met:sessionId></met:SessionHeader></soapenv:Header>
|
|
107
|
+
<soapenv:Body><met:readMetadata><met:type>${settings_type}</met:type><met:fullNames>${full_name}</met:fullNames></met:readMetadata></soapenv:Body>
|
|
108
|
+
</soapenv:Envelope>"
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
# classify_field / normalize_class / describe_failure now live in
|
|
112
|
+
# shared/soap.sh (sourced above) so install-ootb-competencies.sh's SOAP
|
|
113
|
+
# fallback can reuse them without duplicating this logic.
|
|
114
|
+
|
|
115
|
+
# ---------------------------------------------------------------------------
|
|
116
|
+
# Availability detection: read all three settings + count competencies.
|
|
117
|
+
# ---------------------------------------------------------------------------
|
|
118
|
+
echo "Detecting org state..."
|
|
119
|
+
echo ""
|
|
120
|
+
|
|
121
|
+
CI_RAW=$(soap_read "ConversationalIntelligenceSettings" "ConversationalIntelligence")
|
|
122
|
+
ECI_CLASS=$(classify_field "$CI_RAW" "enableCallCoaching")
|
|
123
|
+
CS_CLASS=$(classify_field "$CI_RAW" "enableECICallScoring")
|
|
124
|
+
ECI_ENABLED=$(normalize_class "$ECI_CLASS")
|
|
125
|
+
CALL_SCORING_ENABLED=$(normalize_class "$CS_CLASS")
|
|
126
|
+
|
|
127
|
+
GENAI_RAW=$(soap_read "EinsteinGptSettings" "EinsteinGpt")
|
|
128
|
+
GENAI_CLASS=$(classify_field "$GENAI_RAW" "enableEinsteinGptPlatform")
|
|
129
|
+
GENAI_ENABLED=$(normalize_class "$GENAI_CLASS")
|
|
130
|
+
|
|
131
|
+
# River Rush (Momentum) detection deferred until that arm goes live.
|
|
132
|
+
|
|
133
|
+
# Competency count via Tooling API. (sf may prepend a warning to stdout; strip.)
|
|
134
|
+
COMPETENCY_COUNT=""
|
|
135
|
+
if COMP_RAW=$(sf data query --target-org "$ORG_ALIAS" --use-tooling-api \
|
|
136
|
+
--query "SELECT COUNT() FROM EnablementCompetencyDef" --json 2>/dev/null); then
|
|
137
|
+
COMP_JSON=$(echo "$COMP_RAW" | awk '/^{/,EOF')
|
|
138
|
+
COMPETENCY_COUNT=$(echo "$COMP_JSON" | jq -r '.result.totalSize // empty' 2>/dev/null || echo "")
|
|
139
|
+
fi
|
|
140
|
+
|
|
141
|
+
echo " Einstein Conversation Insights: ${ECI_ENABLED:-unknown}"
|
|
142
|
+
echo " Einstein Generative AI Platform: ${GENAI_ENABLED:-unknown}"
|
|
143
|
+
echo " Call Coaching: ${CALL_SCORING_ENABLED:-unknown}"
|
|
144
|
+
echo " Configured competencies: ${COMPETENCY_COUNT:-unknown}"
|
|
145
|
+
echo ""
|
|
146
|
+
|
|
147
|
+
# ---------------------------------------------------------------------------
|
|
148
|
+
# State machine (PRD § "Behavior by org state")
|
|
149
|
+
# ---------------------------------------------------------------------------
|
|
150
|
+
|
|
151
|
+
# State 1a: NOT AVAILABLE — CI settings unreadable. Distinguish *why*: a
|
|
152
|
+
# confirmed licensing gap (TYPE_UNAVAILABLE) is a different admin action than
|
|
153
|
+
# a transient auth/network failure, which should be retried, not reported as
|
|
154
|
+
# "you need Agentforce for Sales."
|
|
155
|
+
if [[ -z "$ECI_ENABLED" ]]; then
|
|
156
|
+
case "$ECI_CLASS" in
|
|
157
|
+
TYPE_UNAVAILABLE:*)
|
|
158
|
+
echo "AI call coaching is not available in this org and they need Agentforce for Sales."
|
|
159
|
+
echo ""
|
|
160
|
+
echo " (This skill covers Einstein Conversation Insights (ECI) orgs only —"
|
|
161
|
+
echo " the org's ECI license appears to be missing.)"
|
|
162
|
+
;;
|
|
163
|
+
AUTH_ERROR:*)
|
|
164
|
+
echo "Could not check org state — authentication/permission error:"
|
|
165
|
+
echo " ${ECI_CLASS#AUTH_ERROR:}"
|
|
166
|
+
echo ""
|
|
167
|
+
echo " Verify the CLI session is valid (sf org login web --alias $ORG_ALIAS) and re-run."
|
|
168
|
+
;;
|
|
169
|
+
*)
|
|
170
|
+
echo "Could not reach Salesforce to check org state (network/timeout error)."
|
|
171
|
+
echo " Try again in a moment; if it persists, check the org's availability."
|
|
172
|
+
;;
|
|
173
|
+
esac
|
|
174
|
+
exit 1
|
|
175
|
+
fi
|
|
176
|
+
|
|
177
|
+
# State 4: ENABLED, competencies exist.
|
|
178
|
+
if [[ "$CALL_SCORING_ENABLED" == "true" && -n "$COMPETENCY_COUNT" && "$COMPETENCY_COUNT" -gt 0 ]]; then
|
|
179
|
+
echo "Call Coaching is already enabled — $COMPETENCY_COUNT competency record(s) exist."
|
|
180
|
+
echo " Ready for edit / deactivate / create-new / improvement-suggestion flows."
|
|
181
|
+
echo ""
|
|
182
|
+
echo "Next: $SCRIPT_DIR/list-competencies.sh \"$ORG_ALIAS\""
|
|
183
|
+
exit 0
|
|
184
|
+
fi
|
|
185
|
+
|
|
186
|
+
# State 3.5: ENABLED, competency count unreadable. Distinguish "we couldn't
|
|
187
|
+
# read the count" from "the count is genuinely zero" — falling into State 3
|
|
188
|
+
# tells the admin "no competencies configured" when the org may actually
|
|
189
|
+
# have several. Route to list-competencies to let the admin see the truth.
|
|
190
|
+
if [[ "$CALL_SCORING_ENABLED" == "true" && -z "$COMPETENCY_COUNT" ]]; then
|
|
191
|
+
echo "Call Coaching is already enabled — competency count could not be read."
|
|
192
|
+
echo " Ready for edit / deactivate / create-new flows."
|
|
193
|
+
echo ""
|
|
194
|
+
echo "Next: $SCRIPT_DIR/list-competencies.sh \"$ORG_ALIAS\" (to inspect the configured set)"
|
|
195
|
+
exit 0
|
|
196
|
+
fi
|
|
197
|
+
|
|
198
|
+
# State 3: ENABLED, no competencies.
|
|
199
|
+
if [[ "$CALL_SCORING_ENABLED" == "true" ]]; then
|
|
200
|
+
echo "Call Coaching is already enabled — no competencies configured yet."
|
|
201
|
+
echo " Ready for OOTB competency install / custom competency creation."
|
|
202
|
+
echo ""
|
|
203
|
+
echo "Next: $SCRIPT_DIR/install-ootb-competencies.sh --list (show the OOTB set, then ask OOTB vs custom)"
|
|
204
|
+
exit 0
|
|
205
|
+
fi
|
|
206
|
+
|
|
207
|
+
# State 2: AVAILABLE, NOT ENABLED — enable path.
|
|
208
|
+
|
|
209
|
+
# 2a-pre. License gate — catch unlicensed orgs BEFORE the "ECI not enabled"
|
|
210
|
+
# refusal below. ConversationalIntelligenceSettings returns HTTP 200 with
|
|
211
|
+
# enableCallCoaching=false on BOTH licensed-ECI-off and genuinely unlicensed
|
|
212
|
+
# orgs (the two are indistinguishable at that read), so State 1a above
|
|
213
|
+
# doesn't catch the unlicensed case. EinsteinGptSettings faulting with
|
|
214
|
+
# TYPE_UNAVAILABLE is the strongest license-gap signal we have without
|
|
215
|
+
# doing a write — Einstein GenAI Platform is a hard prerequisite for AI
|
|
216
|
+
# Call Scoring, so an org that can't even see that settings type won't be
|
|
217
|
+
# able to enable Call Coaching regardless of what the ECI toggle says.
|
|
218
|
+
# W-24026047.
|
|
219
|
+
#
|
|
220
|
+
# Not perfect: an org could have the GenAI platform present but still lack
|
|
221
|
+
# the specific Call Scoring license. State 2c's ECICallScoringAI
|
|
222
|
+
# OrgPermission check catches that residual case after GenAI is enabled.
|
|
223
|
+
if [[ "$GENAI_CLASS" == TYPE_UNAVAILABLE:* ]]; then
|
|
224
|
+
echo "AI call coaching is not available in this org."
|
|
225
|
+
echo ""
|
|
226
|
+
echo " You need Agentforce for Sales to enable AI Call Scoring."
|
|
227
|
+
echo " Without Agentforce for Sales, you can still set up manual call scoring"
|
|
228
|
+
echo " by navigating to Salesforce Go setup in your org."
|
|
229
|
+
exit 1
|
|
230
|
+
fi
|
|
231
|
+
|
|
232
|
+
echo "State: AVAILABLE, NOT ENABLED — proceeding with enable flow."
|
|
233
|
+
echo ""
|
|
234
|
+
|
|
235
|
+
# 2a. License gate — ECICallScoringAI OrgPermission is the authoritative
|
|
236
|
+
# license signal and must be checked BEFORE the "ECI not enabled" refusal
|
|
237
|
+
# below. ConversationalIntelligenceSettings reads enableCallCoaching=false on
|
|
238
|
+
# BOTH a licensed-but-toggled-off org and a genuinely unlicensed one — those
|
|
239
|
+
# are indistinguishable at that read. Checking here, ahead of 2b, prevents a
|
|
240
|
+
# no-license org from being misdiagnosed as "just flip ECI in Setup" (a
|
|
241
|
+
# regression this skill's eval guards against — see the NoLicense dataset).
|
|
242
|
+
ECICALLSCORINGAI_ENABLED=""
|
|
243
|
+
if ECICALLSCORINGAI_RAW=$(sf data query --target-org "$ORG_ALIAS" --use-tooling-api \
|
|
244
|
+
--query "SELECT SettingValue FROM OrganizationSettingsDetail WHERE SettingName='ECICallScoringAI' AND DataType='OrgPermissionSettings'" \
|
|
245
|
+
--json 2>/dev/null); then
|
|
246
|
+
ECICALLSCORINGAI_JSON=$(echo "$ECICALLSCORINGAI_RAW" | awk '/^{/,EOF')
|
|
247
|
+
if echo "$ECICALLSCORINGAI_JSON" | jq -e '.status == 0' >/dev/null 2>&1; then
|
|
248
|
+
ECICALLSCORINGAI_ENABLED=$(echo "$ECICALLSCORINGAI_JSON" | jq -r '.result.records[0].SettingValue // "false"')
|
|
249
|
+
fi
|
|
250
|
+
fi
|
|
251
|
+
|
|
252
|
+
if [[ -z "$ECICALLSCORINGAI_ENABLED" ]]; then
|
|
253
|
+
echo "Could not determine AI call coaching license state — ECICallScoringAI query failed."
|
|
254
|
+
echo " Verify the CLI session is valid (sf org login web --alias $ORG_ALIAS) and re-run."
|
|
255
|
+
exit 1
|
|
256
|
+
fi
|
|
257
|
+
|
|
258
|
+
if [[ "$ECICALLSCORINGAI_ENABLED" != "true" ]]; then
|
|
259
|
+
echo "AI call coaching is not available in this org and org requires Agentforce for Sales license."
|
|
260
|
+
exit 1
|
|
261
|
+
fi
|
|
262
|
+
|
|
263
|
+
# 2b. ECI must be enabled — this skill is ECI-only.
|
|
264
|
+
if [[ "$ECI_ENABLED" != "true" ]]; then
|
|
265
|
+
echo "Error: ECI (Einstein Conversation Insights) is not enabled."
|
|
266
|
+
echo " Setup → Salesforce Go → Einstein Conversation Insights → Turn ON, then re-run."
|
|
267
|
+
exit 1
|
|
268
|
+
fi
|
|
269
|
+
|
|
270
|
+
# 2c. Enable Einstein GenAI if off (PRD explicitly authorizes this).
|
|
271
|
+
if [[ "$GENAI_ENABLED" != "true" ]]; then
|
|
272
|
+
echo "Enabling Einstein Generative AI Platform (required for AI scoring)..."
|
|
273
|
+
GENAI_ENABLE_REQ=$(cat <<'EOF'
|
|
274
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
275
|
+
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:met="http://soap.sforce.com/2006/04/metadata">
|
|
276
|
+
<soapenv:Header>
|
|
277
|
+
<met:SessionHeader><met:sessionId>{{ACCESS_TOKEN}}</met:sessionId></met:SessionHeader>
|
|
278
|
+
</soapenv:Header>
|
|
279
|
+
<soapenv:Body>
|
|
280
|
+
<met:updateMetadata>
|
|
281
|
+
<met:metadata xsi:type="met:EinsteinGptSettings" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
|
282
|
+
<met:fullName>EinsteinGpt</met:fullName>
|
|
283
|
+
<met:enableEinsteinGptPlatform>true</met:enableEinsteinGptPlatform>
|
|
284
|
+
</met:metadata>
|
|
285
|
+
</met:updateMetadata>
|
|
286
|
+
</soapenv:Body>
|
|
287
|
+
</soapenv:Envelope>
|
|
288
|
+
EOF
|
|
289
|
+
)
|
|
290
|
+
GENAI_ENABLE_REQ="${GENAI_ENABLE_REQ//\{\{ACCESS_TOKEN\}\}/$ACCESS_TOKEN}"
|
|
291
|
+
GENAI_RESULT=$(classify_field "$(soap_call "update" "$GENAI_ENABLE_REQ")" "success")
|
|
292
|
+
if [[ "$GENAI_RESULT" == "true" ]]; then
|
|
293
|
+
echo " Einstein GenAI enabled"
|
|
294
|
+
else
|
|
295
|
+
case "$GENAI_RESULT" in
|
|
296
|
+
TYPE_UNAVAILABLE:*|false)
|
|
297
|
+
# Org isn't licensed for EinsteinGptSettings, or the update was
|
|
298
|
+
# explicitly refused with no fault and no <errors> detail — either
|
|
299
|
+
# way, this is a licensing gap, not a transient error, so point at
|
|
300
|
+
# the actual fix instead of telling the admin to just retry.
|
|
301
|
+
echo " Einstein Generative AI Platform cannot be enabled — $(describe_failure "$GENAI_RESULT")."
|
|
302
|
+
echo ""
|
|
303
|
+
echo " You need Agentforce for Sales to enable AI Call Scoring."
|
|
304
|
+
echo " Without Agentforce for Sales, you can still set up manual call scoring"
|
|
305
|
+
echo " by navigating to Salesforce Go setup in your org."
|
|
306
|
+
;;
|
|
307
|
+
UPDATE_REJECTED:*)
|
|
308
|
+
# Salesforce returned success=false with an <errors> block — surface
|
|
309
|
+
# the actual statusCode/message instead of guessing at a cause.
|
|
310
|
+
echo " Einstein GenAI enable failed: $(describe_failure "$GENAI_RESULT")"
|
|
311
|
+
;;
|
|
312
|
+
AUTH_ERROR:*)
|
|
313
|
+
echo " Einstein GenAI enable failed: $(describe_failure "$GENAI_RESULT")"
|
|
314
|
+
echo ""
|
|
315
|
+
echo " Verify the CLI session is valid (sf org login web --alias $ORG_ALIAS) and re-run."
|
|
316
|
+
;;
|
|
317
|
+
*)
|
|
318
|
+
echo " Einstein GenAI enable failed: $(describe_failure "$GENAI_RESULT")"
|
|
319
|
+
echo ""
|
|
320
|
+
echo " Try again in a moment; if it persists, check the org's availability."
|
|
321
|
+
;;
|
|
322
|
+
esac
|
|
323
|
+
exit 1
|
|
324
|
+
fi
|
|
325
|
+
echo ""
|
|
326
|
+
else
|
|
327
|
+
echo "Einstein Generative AI Platform is already enabled — skipping."
|
|
328
|
+
echo ""
|
|
329
|
+
fi
|
|
330
|
+
|
|
331
|
+
# Enable Call Coaching (enableECICallScoring) — skip the update if it's
|
|
332
|
+
# already on (state detection above only short-circuits when competencies
|
|
333
|
+
# also exist; an org can have enableECICallScoring=true with zero
|
|
334
|
+
# competencies and still reach here if that read raced with an out-of-band
|
|
335
|
+
# change).
|
|
336
|
+
if [[ "$CALL_SCORING_ENABLED" != "true" ]]; then
|
|
337
|
+
echo "Enabling Call Coaching..."
|
|
338
|
+
CS_ENABLE_REQ=$(cat <<'EOF'
|
|
339
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
340
|
+
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:met="http://soap.sforce.com/2006/04/metadata">
|
|
341
|
+
<soapenv:Header>
|
|
342
|
+
<met:SessionHeader><met:sessionId>{{ACCESS_TOKEN}}</met:sessionId></met:SessionHeader>
|
|
343
|
+
</soapenv:Header>
|
|
344
|
+
<soapenv:Body>
|
|
345
|
+
<met:updateMetadata>
|
|
346
|
+
<met:metadata xsi:type="met:ConversationalIntelligenceSettings" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
|
347
|
+
<met:fullName>ConversationalIntelligence</met:fullName>
|
|
348
|
+
<met:enableECICallScoring>true</met:enableECICallScoring>
|
|
349
|
+
</met:metadata>
|
|
350
|
+
</met:updateMetadata>
|
|
351
|
+
</soapenv:Body>
|
|
352
|
+
</soapenv:Envelope>
|
|
353
|
+
EOF
|
|
354
|
+
)
|
|
355
|
+
CS_ENABLE_REQ="${CS_ENABLE_REQ//\{\{ACCESS_TOKEN\}\}/$ACCESS_TOKEN}"
|
|
356
|
+
CS_RESULT=$(classify_field "$(soap_call "update" "$CS_ENABLE_REQ")" "success")
|
|
357
|
+
|
|
358
|
+
if [[ "$CS_RESULT" == "true" ]]; then
|
|
359
|
+
echo " Call Coaching enabled"
|
|
360
|
+
else
|
|
361
|
+
# ECI enablement is asynchronous — for a short window after the ECI toggle
|
|
362
|
+
# flips on, the dependent enableECICallScoring field is server-side
|
|
363
|
+
# read-only and updateMetadata returns INSUFFICIENT_ACCESS_OR_READONLY.
|
|
364
|
+
# This is a transient state that self-resolves in a few minutes, not a
|
|
365
|
+
# permanent failure — surface a wait-and-retry message instead of the
|
|
366
|
+
# generic error. W-23968462.
|
|
367
|
+
case "$CS_RESULT" in
|
|
368
|
+
WRITE_ERROR:INSUFFICIENT_ACCESS_OR_READONLY*)
|
|
369
|
+
echo " Einstein Conversation Insights is still finishing turning on."
|
|
370
|
+
echo " Wait a few minutes, then re-run this to enable Call Coaching."
|
|
371
|
+
;;
|
|
372
|
+
*)
|
|
373
|
+
echo " Call Coaching enable failed: $(describe_failure "$CS_RESULT")"
|
|
374
|
+
;;
|
|
375
|
+
esac
|
|
376
|
+
exit 1
|
|
377
|
+
fi
|
|
378
|
+
else
|
|
379
|
+
echo "Call Coaching is already enabled — skipping."
|
|
380
|
+
fi
|
|
381
|
+
|
|
382
|
+
echo ""
|
|
383
|
+
|
|
384
|
+
# Enable the CallCoachingReady OrgPermission — not a Metadata API type, so
|
|
385
|
+
# it's flipped via Tooling API PATCH on OrganizationSettingsDetail (the same
|
|
386
|
+
# object the OrgPermission reads above use), looked up by Id first.
|
|
387
|
+
CCR_LOOKUP=$(sf api request rest \
|
|
388
|
+
"services/data/v68.0/tooling/query/?q=SELECT+Id,SettingValue+FROM+OrganizationSettingsDetail+WHERE+SettingName='CallCoachingReady'+AND+DataType='OrgPermissionSettings'" \
|
|
389
|
+
--target-org "$ORG_ALIAS" 2>/dev/null) || true
|
|
390
|
+
|
|
391
|
+
CCR_COUNT=$(echo "$CCR_LOOKUP" | jq -r '.totalSize // 0' 2>/dev/null || echo 0)
|
|
392
|
+
if [[ "$CCR_COUNT" == "0" ]]; then
|
|
393
|
+
echo "Could not locate the CallCoachingReady OrgPermission record on this org."
|
|
394
|
+
exit 1
|
|
395
|
+
fi
|
|
396
|
+
|
|
397
|
+
CCR_ID=$(echo "$CCR_LOOKUP" | jq -r '.records[0].Id')
|
|
398
|
+
CCR_CURRENT=$(echo "$CCR_LOOKUP" | jq -r '.records[0].SettingValue')
|
|
399
|
+
|
|
400
|
+
if [[ "$CCR_CURRENT" == "true" ]]; then
|
|
401
|
+
echo "CallCoachingReady is already enabled — skipping."
|
|
402
|
+
else
|
|
403
|
+
echo "Enabling CallCoachingReady..."
|
|
404
|
+
if CCR_PATCH_RESP=$(sf api request rest \
|
|
405
|
+
"services/data/v68.0/tooling/sobjects/OrganizationSettingsDetail/${CCR_ID}" \
|
|
406
|
+
--target-org "$ORG_ALIAS" \
|
|
407
|
+
--method PATCH \
|
|
408
|
+
--header 'Content-Type: application/json' \
|
|
409
|
+
--body '{"SettingValue": true}' \
|
|
410
|
+
2>/dev/null); then
|
|
411
|
+
echo " CallCoachingReady enabled"
|
|
412
|
+
else
|
|
413
|
+
echo " Failed to enable CallCoachingReady"
|
|
414
|
+
echo " $CCR_PATCH_RESP"
|
|
415
|
+
exit 1
|
|
416
|
+
fi
|
|
417
|
+
fi
|
|
418
|
+
|
|
419
|
+
echo ""
|
|
420
|
+
|
|
421
|
+
# 2d. Verify.
|
|
422
|
+
echo "Verifying..."
|
|
423
|
+
sleep 2
|
|
424
|
+
VERIFY_CLASS=$(classify_field "$(soap_read "ConversationalIntelligenceSettings" "ConversationalIntelligence")" "enableECICallScoring")
|
|
425
|
+
|
|
426
|
+
if [[ "$VERIFY_CLASS" == "true" ]]; then
|
|
427
|
+
echo " Confirmed: Call Coaching is now enabled"
|
|
428
|
+
echo ""
|
|
429
|
+
echo "Call Coaching enabled. Ready for OOTB competency install / custom creation."
|
|
430
|
+
echo ""
|
|
431
|
+
echo "Next: $SCRIPT_DIR/install-ootb-competencies.sh --list (show the OOTB set, then ask OOTB vs custom)"
|
|
432
|
+
exit 0
|
|
433
|
+
else
|
|
434
|
+
echo " Error: Verification failed: $(describe_failure "$VERIFY_CLASS")"
|
|
435
|
+
exit 1
|
|
436
|
+
fi
|