@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.
Files changed (220) hide show
  1. package/package.json +1 -1
  2. package/skills/agentforce-generate/SKILL.md +8 -0
  3. package/skills/agentforce-generate/references/actions-reference.md +2 -2
  4. package/skills/agentforce-generate/references/voice-modality-reference.md +51 -15
  5. package/skills/agentforce-generate/references/voice-telephony-cli.md +193 -0
  6. package/skills/agentforce-observe/SKILL.md +13 -9
  7. package/skills/agentforce-observe/references/ahm-alerts.md +193 -14
  8. package/skills/automation-sandbox-post-copy-config-generate/SKILL.md +24 -15
  9. package/skills/automation-sandbox-post-copy-config-generate/assets/config_template.json +11 -0
  10. package/skills/automation-sandbox-post-copy-config-generate/assets/json_schema.json +33 -1
  11. package/skills/automation-sandbox-post-copy-config-generate/references/configuration_catalog.md +32 -1
  12. package/skills/automation-sandbox-post-copy-configure/SKILL.md +75 -71
  13. package/skills/automation-sandbox-post-copy-configure/assets/scheduled_apex_template.apex +13 -0
  14. package/skills/automation-sandbox-post-copy-configure/examples/sample_scheduled_apex_config.json +13 -0
  15. package/skills/automation-sandbox-post-copy-configure/references/scheduled_apex_path.md +155 -0
  16. package/skills/automation-sandbox-post-copy-configure/scripts/build-scheduled-apex.mjs +62 -0
  17. package/skills/automation-sandbox-post-copy-configure/scripts/soql-escape-job-name.mjs +28 -0
  18. package/skills/consumer-goods-accruals-datakit-deploy/SKILL.md +383 -0
  19. package/skills/consumer-goods-accruals-datakit-deploy/scripts/setup.js +324 -0
  20. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/00-check-prerequisites.js +63 -0
  21. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/01-download-static-resource.js +100 -0
  22. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/02-replace-org-id.js +46 -0
  23. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/03-deploy-metadata.js +46 -0
  24. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/04-deploy-engine.js +198 -0
  25. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/05-deploy-tpm-accruals.js +231 -0
  26. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/06-create-dataspace.js +82 -0
  27. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/07-deploy-accruals-reports.js +517 -0
  28. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/08-deploy-ui.js +416 -0
  29. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/09-completion.js +44 -0
  30. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/utils.js +514 -0
  31. package/skills/consumer-goods-sync-management-configure/SKILL.md +137 -0
  32. package/skills/consumer-goods-sync-management-configure/examples/scenarios.md +294 -0
  33. package/skills/consumer-goods-sync-management-configure/references/act1-setup-sync.md +333 -0
  34. package/skills/consumer-goods-sync-management-configure/references/act2-assign-users.md +324 -0
  35. package/skills/consumer-goods-sync-management-configure/references/act3-plan-verify.md +113 -0
  36. package/skills/consumer-goods-sync-management-configure/references/readiness-and-enablement.md +164 -0
  37. package/skills/consumer-goods-sync-management-configure/references/sync-management-app-install-backbone.md +159 -0
  38. package/skills/consumer-goods-sync-management-configure/references/transports-and-namespace.md +220 -0
  39. package/skills/consumer-goods-sync-management-configure/references/verify-and-smoke.md +175 -0
  40. package/skills/dx-org-analyze/SKILL.md +0 -1
  41. package/skills/dx-org-analyze/scripts/collect_org_data.py +2 -2
  42. package/skills/dx-org-analyze/scripts/compute_diff.py +2 -4
  43. package/skills/dx-org-analyze/scripts/introspect_org.py +4 -40
  44. package/skills/dx-org-manage/SKILL.md +6 -50
  45. package/skills/dx-org-manage/examples/README.md +10 -16
  46. package/skills/dx-org-manage/references/scratch-org-create.md +1 -1
  47. package/skills/dx-org-manage/references/scratch-org-operations.md +1 -1
  48. package/skills/dx-org-shape-manage/SKILL.md +4 -3
  49. package/skills/education-cloud-domain-configure/references/gotchas-extended.md +1 -0
  50. package/skills/experience-accessibility-validate/SKILL.md +8 -5
  51. package/skills/experience-accessibility-validate/references/reviewers/sc-1-1-1-non-text-content.md +1 -4
  52. package/skills/experience-accessibility-validate/references/reviewers/sc-1-3-1-i-lists.md +0 -4
  53. package/skills/experience-accessibility-validate/references/reviewers/sc-1-3-1-ii-tables.md +20 -85
  54. package/skills/experience-accessibility-validate/references/reviewers/sc-1-3-1-iii-form-labels.md +4 -4
  55. package/skills/experience-accessibility-validate/references/reviewers/sc-1-3-1-iv-regions.md +0 -4
  56. package/skills/experience-accessibility-validate/references/reviewers/sc-1-3-1-v-groups.md +2 -4
  57. package/skills/experience-accessibility-validate/references/reviewers/sc-1-3-5-identify-input.md +0 -4
  58. package/skills/experience-accessibility-validate/references/reviewers/sc-1-4-3-contrast.md +4 -14
  59. package/skills/experience-accessibility-validate/references/reviewers/sc-2-1-1-keyboard.md +9 -5
  60. package/skills/experience-accessibility-validate/references/reviewers/sc-2-4-4-link-purpose.md +1 -4
  61. package/skills/experience-accessibility-validate/references/reviewers/sc-2-4-6-headings-labels.md +0 -4
  62. package/skills/experience-accessibility-validate/references/reviewers/sc-2-5-1-pointer-gestures.md +0 -4
  63. package/skills/experience-accessibility-validate/references/reviewers/sc-2-5-2-pointer-cancellation.md +0 -4
  64. package/skills/experience-accessibility-validate/references/reviewers/sc-2-5-3-label-in-name.md +0 -4
  65. package/skills/experience-accessibility-validate/references/reviewers/sc-2-5-7-dragging-movement.md +0 -4
  66. package/skills/experience-accessibility-validate/references/reviewers/sc-3-2-1-on-focus.md +0 -4
  67. package/skills/experience-accessibility-validate/references/reviewers/sc-3-2-2-on-input.md +1 -4
  68. package/skills/experience-accessibility-validate/references/reviewers/sc-3-3-1-error-identification.md +1 -4
  69. package/skills/experience-accessibility-validate/references/reviewers/sc-3-3-2-labels-instructions.md +1 -4
  70. package/skills/experience-accessibility-validate/references/reviewers/sc-3-3-3-error-suggestion.md +1 -4
  71. package/skills/experience-accessibility-validate/references/reviewers/sc-4-1-2-i-name.md +4 -5
  72. package/skills/experience-accessibility-validate/references/reviewers/sc-4-1-2-ii-role.md +1 -4
  73. package/skills/experience-accessibility-validate/references/reviewers/sc-4-1-2-iii-value.md +0 -4
  74. package/skills/experience-lwc-accessibility-jest-run/SKILL.md +104 -25
  75. package/skills/experience-lwc-accessibility-jest-run/assets/jest.config.js +4 -0
  76. package/skills/experience-lwc-accessibility-jest-run/assets/sa11y-jest-setup.js +5 -0
  77. package/skills/experience-ui-bundle-app-coordinate/SKILL.md +5 -5
  78. package/skills/experience-ui-bundle-features-generate/SKILL.md +65 -36
  79. package/skills/experience-ui-bundle-features-generate/references/angular/features.md +74 -0
  80. package/skills/experience-ui-bundle-features-generate/references/react/features.md +59 -0
  81. package/skills/experience-ui-bundle-features-generate/scripts/detect-framework.sh +73 -0
  82. package/skills/experience-ui-bundle-frontend-generate/SKILL.md +2 -2
  83. package/skills/experience-ui-bundle-metadata-generate/SKILL.md +26 -14
  84. package/skills/experience-ui-bundle-metadata-generate/references/angular-metadata-generate.md +37 -0
  85. package/skills/experience-ui-bundle-metadata-generate/references/react-metadata-generate.md +32 -0
  86. package/skills/experience-ui-bundle-metadata-generate/scripts/detect-framework.sh +73 -0
  87. package/skills/experience-ui-bundle-metadata-generate/scripts/verify-bundle-location.sh +30 -8
  88. package/skills/field-service-data-capture-migrate/SKILL.md +391 -0
  89. package/skills/field-service-data-capture-migrate/assets/sample-legacy-flow.flow-meta.xml +163 -0
  90. package/skills/field-service-data-capture-migrate/references/architecture-notes.md +40 -0
  91. package/skills/field-service-data-capture-migrate/references/component-mapping.md +278 -0
  92. package/skills/field-service-data-capture-migrate/references/converter-builder-contract.md +183 -0
  93. package/skills/field-service-data-capture-migrate/references/dc-platform-rules.md +130 -0
  94. package/skills/field-service-data-capture-migrate/references/fsm-dc-capability-catalog.md +215 -0
  95. package/skills/field-service-data-capture-migrate/references/known-deploy-errors.md +36 -0
  96. package/skills/field-service-data-capture-migrate/references/migration-checklist.md +121 -0
  97. package/skills/field-service-data-capture-migrate/references/transformer-rules.md +132 -0
  98. package/skills/field-service-data-capture-migrate/scripts/analyze_flow.py +277 -0
  99. package/skills/field-service-data-capture-migrate/scripts/convert_to_dc_spec.py +1311 -0
  100. package/skills/field-service-data-capture-migrate/scripts/cud_analysis.py +402 -0
  101. package/skills/field-service-data-capture-migrate/scripts/deploy_flow.sh +230 -0
  102. package/skills/field-service-data-capture-migrate/scripts/discover_subflow_tree.py +230 -0
  103. package/skills/field-service-data-capture-migrate/scripts/flow_input.py +186 -0
  104. package/skills/field-service-data-capture-migrate/scripts/flow_xml_utils.py +39 -0
  105. package/skills/field-service-data-capture-migrate/scripts/fsm_dc_catalog.py +413 -0
  106. package/skills/field-service-data-capture-migrate/scripts/generate_summary.py +199 -0
  107. package/skills/field-service-data-capture-migrate/scripts/json_to_flow_xml.py +146 -0
  108. package/skills/field-service-data-capture-migrate/scripts/manual_review_flags.py +42 -0
  109. package/skills/field-service-data-capture-migrate/scripts/retrieve_flow_rest.sh +155 -0
  110. package/skills/field-service-data-capture-migrate/scripts/subflow_names.py +49 -0
  111. package/skills/field-service-data-capture-migrate/scripts/transform_flow.py +1661 -0
  112. package/skills/field-service-data-capture-migrate/scripts/validate_flow.sh +147 -0
  113. package/skills/field-service-data-capture-migrate/scripts/vendor/build_flow.py +1258 -0
  114. package/skills/life-sciences-fieldsalesrep-coordinate/SKILL.md +1 -1
  115. package/skills/life-sciences-fieldsalesrep-coordinate/references/orchestration-flow.md +1 -1
  116. package/skills/life-sciences-kam-coordinate/references/stage-5-data-and-plan-templates-overview.md +14 -1
  117. package/skills/mobile-apps-create/SKILL.md +1 -1
  118. package/skills/{platform-agentsetup-categories-fetch → platform-agenticsetup-categories-get}/SKILL.md +7 -7
  119. package/skills/platform-sandbox-configure/SKILL.md +108 -133
  120. package/skills/platform-sandbox-configure/references/api-response-shapes.md +39 -0
  121. package/skills/platform-sandbox-configure/references/definition-file-approach.md +73 -0
  122. package/skills/platform-widget-generate/SKILL.md +26 -18
  123. package/skills/platform-widget-generate/examples/formulas.json +191 -0
  124. package/skills/platform-widget-generate/references/widget-formulas.md +123 -0
  125. package/skills/platform-widget-generate/references/widget-meta-directives.md +4 -4
  126. package/skills/sales-call-scoring-configure/SKILL.md +318 -0
  127. package/skills/sales-call-scoring-configure/assets/best-practice-competencies.json +67 -0
  128. package/skills/sales-call-scoring-configure/assets/ootb-competencies.json +58 -0
  129. package/skills/sales-call-scoring-configure/references/admin-communication.md +79 -0
  130. package/skills/sales-call-scoring-configure/references/competency-crud.md +130 -0
  131. package/skills/sales-call-scoring-configure/scripts/create-competency.sh +166 -0
  132. package/skills/sales-call-scoring-configure/scripts/create-custom-competency.sh +166 -0
  133. package/skills/sales-call-scoring-configure/scripts/edit-competency.sh +185 -0
  134. package/skills/sales-call-scoring-configure/scripts/edit-custom-competency.sh +190 -0
  135. package/skills/sales-call-scoring-configure/scripts/enable-call-scoring.sh +436 -0
  136. package/skills/sales-call-scoring-configure/scripts/get-competency.sh +72 -0
  137. package/skills/sales-call-scoring-configure/scripts/get-custom-competency.sh +78 -0
  138. package/skills/sales-call-scoring-configure/scripts/install-best-practice-competencies.sh +223 -0
  139. package/skills/sales-call-scoring-configure/scripts/install-ootb-competencies.sh +237 -0
  140. package/skills/sales-call-scoring-configure/scripts/list-competencies.sh +99 -0
  141. package/skills/sales-call-scoring-configure/scripts/shared/auth.sh +116 -0
  142. package/skills/sales-call-scoring-configure/scripts/shared/cap.sh +86 -0
  143. package/skills/sales-call-scoring-configure/scripts/shared/soap.sh +267 -0
  144. package/skills/sales-call-scoring-configure/scripts/shared/soql.sh +25 -0
  145. package/skills/sales-call-scoring-configure/scripts/toggle-competency.sh +112 -0
  146. package/skills/sales-call-scoring-configure/scripts/toggle-custom-competency.sh +125 -0
  147. package/skills/sales-call-scoring-configure/scripts/verify-ootb-competencies.sh +116 -0
  148. package/skills/service-concierge-portal-generate/references/portal-deploy-runbook.md +1 -0
  149. package/skills/service-de-channel-activate/SKILL.md +6 -5
  150. package/skills/service-de-channel-create/SKILL.md +3 -3
  151. package/skills/service-de-channel-routing-configure/SKILL.md +3 -3
  152. package/skills/service-de-channel-settings-configure/SKILL.md +195 -0
  153. package/skills/service-de-channel-settings-configure/references/channel-settings-facets.md +152 -0
  154. package/skills/{service-de-channel-consent-configure → service-de-channel-settings-configure}/references/gotchas.md +3 -3
  155. package/skills/{service-de-channel-consent-configure → service-de-channel-settings-configure}/references/language-keywords.md +4 -4
  156. package/skills/{service-de-channel-consent-configure → service-de-channel-settings-configure}/references/worked-examples.md +14 -14
  157. package/skills/service-de-headless-channel-configure/SKILL.md +6 -6
  158. package/skills/service-de-headless-channel-configure/references/worked-examples.md +1 -1
  159. package/skills/service-helpagent-coordinate/SKILL.md +3 -2
  160. package/skills/service-helpagent-coordinate/references/channel-voice.md +15 -4
  161. package/skills/service-itsm-agentic-setup-agentforce-coordinate/SKILL.md +49 -31
  162. package/skills/service-itsm-agentic-setup-agentforce-coordinate/examples/output-templates.md +71 -6
  163. package/skills/service-itsm-agentic-setup-agentforce-coordinate/scripts/verify-child-verdict.mjs +26 -4
  164. package/skills/service-itsm-agentic-setup-cmdb-access-assign/SKILL.md +35 -10
  165. package/skills/service-itsm-agentic-setup-cmdb-access-assign/references/mcp-invocation.md +25 -5
  166. package/skills/service-itsm-agentic-setup-cmdb-bundle-deploy/SKILL.md +1 -1
  167. package/skills/service-itsm-agentic-setup-cmdb-configure/SKILL.md +5 -5
  168. package/skills/service-itsm-agentic-setup-cmdb-configure/references/mcp-invocation.md +1 -1
  169. package/skills/service-itsm-agentic-setup-cmdb-coordinate/SKILL.md +2 -2
  170. package/skills/service-itsm-agentic-setup-cmdb-coordinate/examples/output-templates.md +1 -1
  171. package/skills/service-itsm-agentic-setup-cmdb-discovery-configure/SKILL.md +25 -15
  172. package/skills/service-itsm-agentic-setup-employee-agent-configure/SKILL.md +8 -5
  173. package/skills/service-itsm-agentic-setup-employee-agent-configure/references/report-format.md +4 -2
  174. package/skills/service-itsm-agentic-setup-employee-agent-configure/references/workflow-detail.md +1 -1
  175. package/skills/service-itsm-agentic-setup-employee-agent-configure/scripts/render-report.mjs +29 -16
  176. package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/SKILL.md +8 -5
  177. package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/references/report-format.md +4 -2
  178. package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/references/workflow-detail.md +1 -1
  179. package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/scripts/render-report.mjs +29 -16
  180. package/skills/service-itsm-agentic-setup-incident-management/SKILL.md +138 -68
  181. package/skills/service-itsm-agentic-setup-incident-management/examples/output-templates.md +31 -15
  182. package/skills/service-itsm-agentic-setup-incident-management/references/incident-persona-psg.md +156 -0
  183. package/skills/service-itsm-agentic-setup-incident-management/references/incident-preferences.md +148 -0
  184. package/skills/service-itsm-agentic-setup-incident-management/references/major-incident-management.md +162 -0
  185. package/skills/service-itsm-agentic-setup-incident-management/references/service-mgmt-privilege.md +149 -0
  186. package/skills/service-itsm-agentic-setup-incident-sla-configure/SKILL.md +103 -89
  187. package/skills/service-itsm-agentic-setup-incident-sla-configure/assets/attach-milestone.json +2 -2
  188. package/skills/service-itsm-agentic-setup-incident-sla-configure/assets/predefined-incident-policy.json +21 -39
  189. package/skills/service-itsm-agentic-setup-incident-sla-configure/examples/milestone-patterns.md +3 -3
  190. package/skills/service-itsm-agentic-setup-incident-sla-configure/examples/output-templates.md +18 -1
  191. package/skills/service-itsm-agentic-setup-incident-sla-configure/references/mcp-invocation.md +63 -37
  192. package/skills/service-omni-attribute-routing-configure/SKILL.md +53 -0
  193. package/skills/service-omni-attribute-routing-configure/references/api-notes.md +7 -0
  194. package/skills/service-omni-attribute-routing-configure/scripts/configure-and-report.sh +224 -0
  195. package/skills/service-omni-attribute-routing-configure/scripts/tests/test_attribute_routing_contracts.py +137 -0
  196. package/skills/service-omni-channel-inventory-analyze/SKILL.md +96 -0
  197. package/skills/service-omni-channel-inventory-analyze/references/api-notes.md +52 -0
  198. package/skills/service-omni-channel-inventory-analyze/scripts/analyze.sh +185 -0
  199. package/skills/service-omni-channel-inventory-analyze/scripts/tests/_bootstrap.py +84 -0
  200. package/skills/service-omni-channel-inventory-analyze/scripts/tests/test_inventory_analyze_contracts.py +141 -0
  201. package/skills/service-omni-channel-limits-analyze/SKILL.md +70 -0
  202. package/skills/service-omni-channel-limits-analyze/references/api-notes.md +22 -0
  203. package/skills/service-omni-channel-limits-analyze/scripts/analyze.sh +159 -0
  204. package/skills/service-omni-channel-limits-analyze/scripts/tests/_bootstrap.py +87 -0
  205. package/skills/service-omni-channel-limits-analyze/scripts/tests/test_limits_analyze_contracts.py +99 -0
  206. package/skills/service-omni-channel-setup-coordinate/SKILL.md +12 -20
  207. package/skills/service-omni-channel-setup-coordinate/scripts/integration-driver.sh +55 -5
  208. package/skills/service-omni-channel-setup-coordinate/scripts/tests/test_omni_contracts.py +38 -0
  209. package/skills/service-omni-command-center-analyze/SKILL.md +2 -2
  210. package/skills/service-omni-command-center-analyze/scripts/analyze.sh +3 -3
  211. package/skills/dx-org-manage/examples/snapshots/error_output.json +0 -9
  212. package/skills/dx-org-manage/examples/snapshots/success_output.json +0 -15
  213. package/skills/dx-org-manage/references/cli_flags.md +0 -67
  214. package/skills/dx-org-manage/references/creating-snapshot.md +0 -103
  215. package/skills/dx-org-manage/references/snapshot_usage.md +0 -74
  216. package/skills/experience-accessibility-validate/scripts/contrast-ratio.py +0 -153
  217. package/skills/experience-lwc-accessibility-jest-run/references/running-sa11y-jest-tests.md +0 -205
  218. package/skills/experience-ui-bundle-features-generate/scripts/verify-react-bundle.sh +0 -14
  219. /package/skills/experience-ui-bundle-features-generate/references/{conflict-resolution-schema.json → common/conflict-resolution-schema.json} +0 -0
  220. /package/skills/{platform-agentsetup-categories-fetch → platform-agenticsetup-categories-get}/references/api-response-schema.md +0 -0
@@ -0,0 +1,517 @@
1
+ const fs = require('fs');
2
+ const os = require('os');
3
+ const path = require('path');
4
+ const { execDeployJson, execSfJson } = require('./utils');
5
+
6
+ const OBJECT_SOURCE_TARGET_MAPS_DIR = 'objectSourceTargetMaps';
7
+
8
+ /**
9
+ * Step 7: Deploy Accruals Reports with Data Space replacements.
10
+ *
11
+ * The Accruals Reports payload contains references between objects (DLOs/DLMs)
12
+ * and ObjectSourceTargetMap definitions. The maps reference fields that only
13
+ * exist after the other folders (objects, wave, ...) are deployed, so we must
14
+ * deploy them in two passes: first everything except objectSourceTargetMaps,
15
+ * then objectSourceTargetMaps on its own.
16
+ *
17
+ * @param {string} orgAlias - The Salesforce org alias
18
+ * @param {string} setupDir - The setup directory path
19
+ * @param {boolean} skipDataSpace - Whether to skip data space configuration
20
+ * @param {string} configuredDataSpaceName - The data space name from config
21
+ * @param {string} configuredDataSpacePrefix - The data space prefix from config
22
+ * @param {string} namespace - Org namespace prefix (e.g. "cgcloud_dev__"), or '' for unmanaged orgs
23
+ * @param {boolean} dryRun - If true, skip deployment but keep temp files
24
+ */
25
+ async function deployAccrualsReports(
26
+ orgAlias,
27
+ setupDir,
28
+ skipDataSpace,
29
+ configuredDataSpaceName,
30
+ configuredDataSpacePrefix,
31
+ namespace = '',
32
+ dryRun = false
33
+ ) {
34
+ console.log('\n=== Step 7: Deploying Accruals Reports ===');
35
+
36
+ // Use "default" and empty prefix if skipping data space, otherwise use configured values
37
+ const dataSpaceName = skipDataSpace ? 'default' : configuredDataSpaceName;
38
+ const dataSpacePrefix = skipDataSpace ? '' : configuredDataSpacePrefix;
39
+
40
+ console.log(`Data Space Name: ${dataSpaceName}`);
41
+ console.log(`Data Space Prefix: ${dataSpacePrefix || '(empty)'}`);
42
+ console.log(`Org Namespace: ${namespace || '(none)'}`);
43
+
44
+ // setupDir is already the CGCloudAddons extraction dir (e.g. <tmp>/CGCloudAddons)
45
+ const sourceDir = path.join(setupDir, 'TPM', 'Accruals', 'Accruals Reports');
46
+ const tempDir = path.join(setupDir, 'temp-accruals-reports');
47
+
48
+ if (fs.existsSync(tempDir)) {
49
+ console.log('Cleaning up existing temp directory...');
50
+ fs.rmSync(tempDir, { recursive: true });
51
+ }
52
+
53
+ console.log('Copying Accruals Reports to temp directory...');
54
+ copyDirectory(sourceDir, tempDir);
55
+
56
+ console.log('Replacing Data Space placeholders and namespace in file names and contents...');
57
+ replaceDataSpacePlaceholders(tempDir, dataSpaceName, dataSpacePrefix, namespace);
58
+
59
+ const deployPath = path.join(tempDir, 'force-app');
60
+ const defaultDir = path.join(deployPath, 'main', 'default');
61
+ const objectSourceTargetMapsPath = path.join(defaultDir, OBJECT_SOURCE_TARGET_MAPS_DIR);
62
+ const hasMaps = fs.existsSync(objectSourceTargetMapsPath);
63
+
64
+ // objectSourceTargetMaps reference Data Cloud DLO/DLM objects by name.
65
+ //
66
+ // The template uses _ORG_NS_ (replaced above with the org namespace, e.g. "cgcloud__")
67
+ // and _DATASPACE_PREFIX_ (replaced with e.g. "DS2_").
68
+ //
69
+ // For DLO (__dll) and DLM (__dlm) object names and their field names, _ORG_NS_ is wrong:
70
+ // Data Cloud objects (MktDataLakeObject / MktDataModelObject) carry their OWN
71
+ // NamespacePrefix, which is independent of the org/package namespace. We query the
72
+ // actual DLO NamespacePrefix from the org at runtime and use that for correction.
73
+ //
74
+ // For targetObjectName the DataSpace prefix (DS2_) must be KEPT — it is part of the
75
+ // DataSpace-scoped DMO name (e.g. DS2_accrualoutput_decorated__dlm).
76
+ //
77
+ // Field names inside DLO/DLM references also need the DLO namespace, not the org namespace.
78
+ if (hasMaps) {
79
+ // Query the actual NamespacePrefix of the DLO from the org (may differ from org namespace)
80
+ let dloNamespace = '';
81
+ try {
82
+ const dloQuery = execSfJson([
83
+ 'data', 'query', '--target-org', orgAlias, '--json',
84
+ '--query', "SELECT NamespacePrefix FROM MktDataLakeObject WHERE DeveloperName='accrualoutput_decorated' LIMIT 1"
85
+ ]);
86
+ const dloRecs = (dloQuery.result && dloQuery.result.records) || [];
87
+ const raw = dloRecs.length > 0 ? (dloRecs[0].NamespacePrefix || '') : '';
88
+ dloNamespace = raw ? (raw.endsWith('__') ? raw : raw + '__') : '';
89
+ } catch (dloNsErr) {
90
+ // MktDataLakeObject may not be accessible in all org configurations — fall back to no DLO namespace.
91
+ // Log so operators can see the root cause rather than hitting a silent mis-mapping later.
92
+ console.warn(`[WARN] Could not query DLO namespace (falling back to empty): ${dloNsErr.message}`);
93
+ dloNamespace = '';
94
+ }
95
+
96
+ // The org namespace (applied by replaceDataSpacePlaceholders) that we need to undo on
97
+ // DLO/DLM names if it differs from the actual DLO namespace.
98
+ const appliedNs = namespace || ''; // what _ORG_NS_ was replaced with
99
+
100
+ const fixObjectName = (name) => {
101
+ let n = name;
102
+ // If the org namespace was applied to DLO/DLM names, undo it, then re-apply the
103
+ // correct DLO namespace (which may be different, e.g. '' for Data Cloud objects
104
+ // even in a namespaced org).
105
+ if (appliedNs && appliedNs !== dloNamespace) {
106
+ const escapedApplied = appliedNs.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
107
+ // Undo on DLO/DLM object names: cgcloud__accrualX__dll → <dloNs>accrualX__dll
108
+ n = n.replace(new RegExp(escapedApplied + '(\\w+__dl[lm])', 'g'), dloNamespace + '$1');
109
+ // Undo on field names inside DLO/DLM refs: __dll.cgcloud__Field__c → __dll.<dloNs>Field__c
110
+ n = n.replace(new RegExp('(__dl[lm]\\.)' + escapedApplied + '(\\w+__c)', 'g'), '$1' + dloNamespace + '$2');
111
+ }
112
+ // NOTE: do NOT touch the DataSpace prefix on __dlm names.
113
+ // The MDAPI targetObjectName must include the DataSpace prefix (e.g. DS2_accrualoutput_decorated__dlm)
114
+ // to resolve to the DataSpace-scoped DMO. Removing it causes error 1923914934.
115
+ return n;
116
+ };
117
+ for (const entry of fs.readdirSync(objectSourceTargetMapsPath)) {
118
+ const fp = path.join(objectSourceTargetMapsPath, entry);
119
+ if (!fs.statSync(fp).isFile()) continue;
120
+ // Fix file contents: fix object/field names, then strip the filterOperationType
121
+ // tag from fieldSourceTargetMaps entries. That tag is present in the template but
122
+ // is not present in working org-deployed maps and causes an "unexpected error" on deploy.
123
+ let fileContent = fixObjectName(fs.readFileSync(fp, 'utf8'));
124
+ fileContent = fileContent.replace(
125
+ /\s*<filterOperationType>[^<]*<\/filterOperationType>/g, ''
126
+ );
127
+ fs.writeFileSync(fp, fileContent, 'utf8');
128
+ // Fix the filename: strip any leading DataSpace prefix from the stem so the
129
+ // MDAPI DeveloperName (= file stem) matches the masterLabel inside the XML.
130
+ // The template uses _DATASPACE_PREFIX_ as a file-naming hint; after replacement
131
+ // the file starts with e.g. "DS2_" but the DeveloperName should not have that.
132
+ let fixedName = entry;
133
+ if (dataSpacePrefix && fixedName.startsWith(dataSpacePrefix)) {
134
+ fixedName = fixedName.slice(dataSpacePrefix.length);
135
+ }
136
+ if (namespace && fixedName.startsWith(namespace)) {
137
+ fixedName = fixedName.slice(namespace.length);
138
+ }
139
+ if (fixedName !== entry) {
140
+ fs.renameSync(fp, path.join(objectSourceTargetMapsPath, fixedName));
141
+ }
142
+ }
143
+ }
144
+
145
+ if (dryRun) {
146
+ console.log('[DRY RUN] Skipping deployment - files prepared at:');
147
+ console.log(` ${tempDir}`);
148
+ if (hasMaps) {
149
+ console.log(`[DRY RUN] objectSourceTargetMaps would deploy in a second pass from:`);
150
+ console.log(` ${objectSourceTargetMapsPath}`);
151
+ }
152
+ console.log('[DRY RUN] Temp directory preserved for inspection');
153
+ console.log('[OK] Step 7 completed (dry run)\n');
154
+ return;
155
+ }
156
+
157
+ // Deployment order:
158
+ // Pass 1 — objects (DLM fields) only: must deploy before objectSourceTargetMaps
159
+ // (the maps reference fields that only exist once the DLM objects are in place).
160
+ // Wave (CRM Analytics) folder is staged aside so it doesn't cause
161
+ // "Not available for deploy" failures if CRM Analytics is not enabled.
162
+ // Pass 2 — objectSourceTargetMaps
163
+ // Pass 3 — wave (CRM Analytics dashboards), best-effort: warns but does not crash
164
+ // if CRM Analytics is not enabled in the org.
165
+
166
+ const wavePath = path.join(defaultDir, 'wave');
167
+ const hasWave = fs.existsSync(wavePath);
168
+ const stagedWavePath = path.join(tempDir, '__staged-wave');
169
+
170
+ // Stage wave aside so it doesn't contaminate the first two passes
171
+ if (hasWave) {
172
+ if (fs.existsSync(stagedWavePath)) fs.rmSync(stagedWavePath, { recursive: true });
173
+ console.log('Staging wave folder aside for the third deployment pass...');
174
+ fs.renameSync(wavePath, stagedWavePath);
175
+ }
176
+
177
+ let stagedMapsPath = null;
178
+ if (hasMaps) {
179
+ stagedMapsPath = path.join(tempDir, '__staged-objectSourceTargetMaps');
180
+ if (fs.existsSync(stagedMapsPath)) fs.rmSync(stagedMapsPath, { recursive: true });
181
+ console.log('Setting aside objectSourceTargetMaps for the second deployment pass...');
182
+ fs.renameSync(objectSourceTargetMapsPath, stagedMapsPath);
183
+ }
184
+
185
+ // Helper: convert tempDir source to MDAPI and deploy it, then clean up mdapi temp.
186
+ function convertAndDeploy(label) {
187
+ const mdapiOut = path.join(os.tmpdir(), `tpm_accruals_reports_mdapi_${label}_${Date.now()}`);
188
+ try {
189
+ const conv = execSfJson(['project', 'convert', 'source', '--output-dir', mdapiOut, '--json'], { cwd: tempDir });
190
+ if (conv.status !== 0) {
191
+ throw new Error(`Source conversion failed (${label}): ${conv.message || JSON.stringify(conv)}`);
192
+ }
193
+ execDeployJson(['project', 'deploy', 'start', '--metadata-dir', mdapiOut, '--target-org', orgAlias]);
194
+ } finally {
195
+ try { fs.rmSync(mdapiOut, { recursive: true }); } catch (cleanupErr) {
196
+ console.warn(`[WARN] Could not remove temp directory ${mdapiOut}: ${cleanupErr.message}`);
197
+ }
198
+ }
199
+ }
200
+
201
+ try {
202
+ // Pass 1: objects (DLM fields)
203
+ console.log('Deploying Accruals Reports metadata (pass 1: objects)...');
204
+ convertAndDeploy('pass1');
205
+
206
+ // Pass 2: objectSourceTargetMaps.
207
+ // The ObjectSourceTargetMap MDAPI deploy requires the DataSpace-scoped DMO
208
+ // (DS2_accrualoutput_decorated) to have IsEnabled=true in MktDataModelObject.
209
+ // The DataKit deployment leaves it as INACTIVE. We activate it here via
210
+ // the Tooling API before deploying the map.
211
+ if (stagedMapsPath) {
212
+ console.log('Restoring objectSourceTargetMaps for the second deployment pass...');
213
+ fs.renameSync(stagedMapsPath, objectSourceTargetMapsPath);
214
+ stagedMapsPath = null;
215
+
216
+ // Activate the DataSpace-scoped DMO (MktDataModelObject) so MDAPI can deploy the map.
217
+ if (!skipDataSpace && dataSpacePrefix) {
218
+ console.log('Activating DataSpace-scoped DMO for objectSourceTargetMaps deploy...');
219
+ // DMO must be active before MDAPI will accept the ObjectSourceTargetMap.
220
+ // If activation fails, the map deploy will fail with opaque error 1923914934 — throw early with a clear message.
221
+ activateDmoForDataSpace(orgAlias, dataSpacePrefix, namespace);
222
+ }
223
+
224
+ console.log('Deploying Accruals Reports metadata (pass 2: objectSourceTargetMaps)...');
225
+ try {
226
+ convertAndDeploy('pass2');
227
+ } catch (err) {
228
+ if (err.message && (err.message.includes('no CustomObject named') || err.message.includes('Not available for deploy'))) {
229
+ console.warn('[WARN] objectSourceTargetMaps deploy skipped — Data Cloud DLO/DLM objects not accessible via MDAPI in this org.');
230
+ console.warn('[WARN] This is expected when Data Cloud is not fully provisioned.');
231
+ console.warn(`[WARN] Original error: ${err.message.split('\n')[0]}`);
232
+ } else {
233
+ throw err;
234
+ }
235
+ }
236
+ }
237
+ } finally {
238
+ // Restore any staged folders so the temp tree is consistent for inspection.
239
+ if (stagedMapsPath && fs.existsSync(stagedMapsPath) && !fs.existsSync(objectSourceTargetMapsPath)) {
240
+ fs.renameSync(stagedMapsPath, objectSourceTargetMapsPath);
241
+ }
242
+ if (hasWave && fs.existsSync(stagedWavePath) && !fs.existsSync(wavePath)) {
243
+ fs.renameSync(stagedWavePath, wavePath);
244
+ }
245
+ }
246
+
247
+ // Pass 3: wave (CRM Analytics) — split into two sub-passes:
248
+ // 3a: WaveApplication (.wapp) + WaveDashboards (.wdash/.wdash-meta.xml) only
249
+ // These can deploy even before any data has flowed through the pipeline.
250
+ // 3b: WaveXmd (.xmd-meta.xml) best-effort
251
+ // XMDs annotate datasets that only exist after the pipeline has run once.
252
+ // They are expected to fail on fresh installs; this is non-fatal.
253
+ if (hasWave) {
254
+ // Move wave back into place for conversion
255
+ if (fs.existsSync(stagedWavePath)) fs.renameSync(stagedWavePath, wavePath);
256
+ // Stage everything except wave aside so only wave deploys
257
+ const stageForWave = path.join(tempDir, '__staged-non-wave');
258
+ if (fs.existsSync(stageForWave)) fs.rmSync(stageForWave, { recursive: true });
259
+ fs.mkdirSync(stageForWave, { recursive: true });
260
+ const defaultEntries = fs.readdirSync(defaultDir, { withFileTypes: true });
261
+ for (const e of defaultEntries) {
262
+ if (e.name !== 'wave') {
263
+ fs.renameSync(path.join(defaultDir, e.name), path.join(stageForWave, e.name));
264
+ }
265
+ }
266
+
267
+ // Stage XMD files aside for pass 3b (they need datasets that don't exist yet on fresh install)
268
+ const xmdStageDir = path.join(tempDir, '__staged-xmd');
269
+ if (fs.existsSync(xmdStageDir)) fs.rmSync(xmdStageDir, { recursive: true });
270
+ fs.mkdirSync(xmdStageDir, { recursive: true });
271
+ const xmdFiles = fs.readdirSync(wavePath).filter(n => n.endsWith('.xmd-meta.xml'));
272
+ for (const xmd of xmdFiles) {
273
+ fs.renameSync(path.join(wavePath, xmd), path.join(xmdStageDir, xmd));
274
+ }
275
+
276
+ console.log('Deploying Accruals Reports metadata (pass 3a: WaveApplication + WaveDashboards)...');
277
+ let waveDeployOk = false;
278
+ try {
279
+ convertAndDeploy('pass3a-wave');
280
+ waveDeployOk = true;
281
+ } catch (err) {
282
+ if (err.message && err.message.includes('Not available for deploy for this organization')) {
283
+ console.warn('[WARN] Wave dashboards skipped — CRM Analytics is not enabled/licensed in this org.');
284
+ console.warn('[WARN] Enable Analytics Studio / CRM Analytics and re-run Step 7 to deploy the dashboards.');
285
+ } else {
286
+ console.warn(`[WARN] Wave dashboard deploy failed: ${err.message}`);
287
+ }
288
+ } finally {
289
+ // Restore staged non-wave folders
290
+ for (const e of fs.readdirSync(stageForWave, { withFileTypes: true })) {
291
+ const dest = path.join(defaultDir, e.name);
292
+ if (!fs.existsSync(dest)) {
293
+ fs.renameSync(path.join(stageForWave, e.name), dest);
294
+ }
295
+ }
296
+ try { fs.rmSync(stageForWave, { recursive: true }); } catch (cleanupErr) {
297
+ console.warn(`[WARN] Could not remove temp staging directory: ${cleanupErr.message}`);
298
+ }
299
+ }
300
+
301
+ if (waveDeployOk) {
302
+ console.log('[OK] Wave dashboards deployed.\n');
303
+ }
304
+
305
+ // Pass 3b: XMDs — best-effort, expected to fail if datasets don't exist yet
306
+ if (xmdFiles.length > 0) {
307
+ // Restore XMDs into wave folder
308
+ for (const xmd of xmdFiles) {
309
+ fs.renameSync(path.join(xmdStageDir, xmd), path.join(wavePath, xmd));
310
+ }
311
+ // Stage all non-XMD wave files aside
312
+ const stageNonXmd = path.join(tempDir, '__staged-non-xmd');
313
+ if (fs.existsSync(stageNonXmd)) fs.rmSync(stageNonXmd, { recursive: true });
314
+ fs.mkdirSync(stageNonXmd, { recursive: true });
315
+ const waveEntries = fs.readdirSync(wavePath, { withFileTypes: true });
316
+ for (const e of waveEntries) {
317
+ if (!e.name.endsWith('.xmd-meta.xml')) {
318
+ fs.renameSync(path.join(wavePath, e.name), path.join(stageNonXmd, e.name));
319
+ }
320
+ }
321
+ // Also stage non-wave default dirs aside
322
+ const stageForXmd = path.join(tempDir, '__staged-non-wave-for-xmd');
323
+ if (fs.existsSync(stageForXmd)) fs.rmSync(stageForXmd, { recursive: true });
324
+ fs.mkdirSync(stageForXmd, { recursive: true });
325
+ const defaultEntriesForXmd = fs.readdirSync(defaultDir, { withFileTypes: true });
326
+ for (const e of defaultEntriesForXmd) {
327
+ if (e.name !== 'wave') {
328
+ fs.renameSync(path.join(defaultDir, e.name), path.join(stageForXmd, e.name));
329
+ }
330
+ }
331
+
332
+ console.log('Deploying Accruals Reports metadata (pass 3b: WaveXmd, best-effort)...');
333
+ try {
334
+ convertAndDeploy('pass3b-xmd');
335
+ console.log('[OK] WaveXmd metadata deployed.\n');
336
+ } catch (err) {
337
+ console.warn('[WARN] WaveXmd deploy skipped — datasets not yet available (expected on fresh install).');
338
+ console.warn('[WARN] XMD files can be re-deployed after the accrual pipeline has run at least once.');
339
+ console.warn(`[WARN] Original error: ${err.message.split('\n')[0]}`);
340
+ } finally {
341
+ // Restore all staged items
342
+ for (const e of fs.readdirSync(stageNonXmd, { withFileTypes: true })) {
343
+ const dest = path.join(wavePath, e.name);
344
+ if (!fs.existsSync(dest)) fs.renameSync(path.join(stageNonXmd, e.name), dest);
345
+ }
346
+ try { fs.rmSync(stageNonXmd, { recursive: true }); } catch (_) {}
347
+ for (const e of fs.readdirSync(stageForXmd, { withFileTypes: true })) {
348
+ const dest = path.join(defaultDir, e.name);
349
+ if (!fs.existsSync(dest)) fs.renameSync(path.join(stageForXmd, e.name), dest);
350
+ }
351
+ try { fs.rmSync(stageForXmd, { recursive: true }); } catch (_) {}
352
+ }
353
+ }
354
+ try { fs.rmSync(xmdStageDir, { recursive: true }); } catch (_) {}
355
+ }
356
+
357
+ console.log('Cleaning up temp directory...');
358
+ try { fs.rmSync(tempDir, { recursive: true }); } catch (_) {}
359
+
360
+ console.log('[OK] Accruals Reports deployed successfully\n');
361
+ }
362
+
363
+ /**
364
+ * Recursively copy a directory
365
+ */
366
+ function copyDirectory(src, dest) {
367
+ if (!fs.existsSync(dest)) {
368
+ fs.mkdirSync(dest, { recursive: true });
369
+ }
370
+
371
+ const entries = fs.readdirSync(src, { withFileTypes: true });
372
+
373
+ for (const entry of entries) {
374
+ const srcPath = path.join(src, entry.name);
375
+ const destPath = path.join(dest, entry.name);
376
+
377
+ if (entry.isDirectory()) {
378
+ copyDirectory(srcPath, destPath);
379
+ } else {
380
+ fs.copyFileSync(srcPath, destPath);
381
+ }
382
+ }
383
+ }
384
+
385
+ /**
386
+ * Replace placeholders in a name (file or directory)
387
+ * Handles double underscore pattern to avoid DS2__ becoming DS2___
388
+ */
389
+ function replaceNamePlaceholders(name, dataSpaceName, dataSpacePrefix, namespace) {
390
+ let newName = name;
391
+ if (dataSpacePrefix === '') {
392
+ newName = newName.replace(/_DATASPACE_PREFIX__/g, '');
393
+ } else {
394
+ newName = newName.replace(/_DATASPACE_PREFIX__/g, dataSpacePrefix);
395
+ }
396
+ newName = newName
397
+ .replace(/_DATASPACE_PREFIX_/g, dataSpacePrefix)
398
+ .replace(/_DATASPACE_NAME_/g, dataSpaceName)
399
+ .replace(/_ORG_NS_/g, namespace || '');
400
+ return newName;
401
+ }
402
+
403
+ /**
404
+ * Replace Data Space placeholders in both file names and file contents
405
+ */
406
+ function replaceDataSpacePlaceholders(dir, dataSpaceName, dataSpacePrefix, namespace) {
407
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
408
+
409
+ for (const entry of entries) {
410
+ const oldPath = path.join(dir, entry.name);
411
+
412
+ if (entry.isDirectory()) {
413
+ replaceDataSpacePlaceholders(oldPath, dataSpaceName, dataSpacePrefix, namespace);
414
+
415
+ if (entry.name.includes('_DATASPACE_PREFIX_') || entry.name.includes('_DATASPACE_NAME_') || entry.name.includes('_ORG_NS_')) {
416
+ const newName = replaceNamePlaceholders(entry.name, dataSpaceName, dataSpacePrefix, namespace);
417
+ const newPath = path.join(dir, newName);
418
+ fs.renameSync(oldPath, newPath);
419
+ }
420
+ } else {
421
+ replaceInFile(oldPath, dataSpaceName, dataSpacePrefix, namespace);
422
+
423
+ if (entry.name.includes('_DATASPACE_PREFIX_') || entry.name.includes('_DATASPACE_NAME_') || entry.name.includes('_ORG_NS_')) {
424
+ const newName = replaceNamePlaceholders(entry.name, dataSpaceName, dataSpacePrefix, namespace);
425
+ const newPath = path.join(dir, newName);
426
+ fs.renameSync(oldPath, newPath);
427
+ }
428
+ }
429
+ }
430
+ }
431
+
432
+ /**
433
+ * Replace placeholders in file contents
434
+ */
435
+ function replaceInFile(filePath, dataSpaceName, dataSpacePrefix, namespace) {
436
+ try {
437
+ let content = fs.readFileSync(filePath, 'utf8');
438
+
439
+ if (content.includes('_DATASPACE_PREFIX_') || content.includes('_DATASPACE_NAME_') || content.includes('_ORG_NS_')) {
440
+ if (dataSpacePrefix === '') {
441
+ // If dataspace prefix is empty, remove double underscore pattern entirely
442
+ content = content.replace(/_DATASPACE_PREFIX__/g, '');
443
+ } else {
444
+ // Replace double underscore pattern first to avoid double underscores in output
445
+ // _DATASPACE_PREFIX__ with DS2_ results in DS2_ (not DS2__)
446
+ content = content.replace(/_DATASPACE_PREFIX__/g, dataSpacePrefix);
447
+ }
448
+
449
+ content = content
450
+ .replace(/_DATASPACE_PREFIX_/g, dataSpacePrefix)
451
+ .replace(/_DATASPACE_NAME_/g, dataSpaceName)
452
+ .replace(/_ORG_NS_/g, namespace || '');
453
+
454
+ fs.writeFileSync(filePath, content, 'utf8');
455
+ }
456
+ } catch (error) {
457
+ if (error.code === 'EISDIR') {
458
+ // Directory entry — skip silently, expected when walking mixed trees
459
+ return;
460
+ }
461
+ // Unexpected I/O failure (missing file, permission error, write error)
462
+ throw new Error(`Failed to process template file ${filePath}: ${error.message}`, { cause: error });
463
+ }
464
+ }
465
+
466
+ /**
467
+ * Activate the DataSpace-scoped DMO (MktDataModelObject) via the Tooling API.
468
+ *
469
+ * The DataKit deployment creates the DS2_<name> DMO in INACTIVE state. MDAPI refuses to
470
+ * deploy an ObjectSourceTargetMap for an INACTIVE DMO (opaque error 1923914934). This
471
+ * function queries the MktDataModelObject by DeveloperName and sets IsEnabled=true via PATCH.
472
+ */
473
+ function activateDmoForDataSpace(orgAlias, dataSpacePrefix, namespace) {
474
+ // DeveloperName = dataSpacePrefix (without trailing _) + '_accrualoutput_decorated'
475
+ const pfxBase = dataSpacePrefix.replace(/_$/, '');
476
+ const dmoDevName = `${pfxBase}_accrualoutput_decorated`;
477
+
478
+ // Query via sf data query --use-tooling-api — CLI supplies auth, no token extraction.
479
+ // Returns: status=0 on success, result.records as array.
480
+ const queryResult = execSfJson([
481
+ 'data', 'query',
482
+ '--query', `SELECT Id,IsEnabled,DataModelObjectStatus FROM MktDataModelObject WHERE DeveloperName='${dmoDevName}'`,
483
+ '--use-tooling-api', '--target-org', orgAlias, '--json'
484
+ ]);
485
+ const records = queryResult?.result?.records;
486
+ if (queryResult?.status !== 0 || !Array.isArray(records)) {
487
+ throw new Error(`Tooling query for DMO '${dmoDevName}' failed: ${queryResult?.message || JSON.stringify(queryResult)}`);
488
+ }
489
+
490
+ if (records.length === 0) {
491
+ console.warn(`[WARN] DMO '${dmoDevName}' not found — skipping activation.`);
492
+ return;
493
+ }
494
+
495
+ const { Id: dmoId, IsEnabled, DataModelObjectStatus } = records[0];
496
+ if (IsEnabled && DataModelObjectStatus === 'ACTIVE') {
497
+ console.log(`DMO '${dmoDevName}' already active — skipping.`);
498
+ return;
499
+ }
500
+
501
+ console.log(`Activating DMO '${dmoDevName}' (was IsEnabled=${IsEnabled}, status=${DataModelObjectStatus})...`);
502
+ // Update via sf data update record --use-tooling-api — CLI supplies auth, no token extraction.
503
+ // Returns: status=0 on success, result.success=true.
504
+ const updateResult = execSfJson([
505
+ 'data', 'update', 'record',
506
+ '--sobject', 'MktDataModelObject',
507
+ '--record-id', dmoId,
508
+ '--values', `IsEnabled=true DataModelObjectStatus=ACTIVE RefEntityDeveloperName=accrualoutput_decorated`,
509
+ '--use-tooling-api', '--target-org', orgAlias, '--json'
510
+ ]);
511
+ if (updateResult?.status !== 0 || !updateResult?.result?.success) {
512
+ throw new Error(`Tooling update for DMO '${dmoDevName}' failed: ${updateResult?.message || JSON.stringify(updateResult?.result)}`);
513
+ }
514
+ console.log(`[OK] DMO '${dmoDevName}' activated.`);
515
+ }
516
+
517
+ module.exports = deployAccrualsReports;