@salesforce/afv-skills 1.59.0 → 1.61.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 (174) hide show
  1. package/package.json +1 -1
  2. package/skills/agentforce-architecture-analyze/SKILL.md +66 -17
  3. package/skills/agentforce-architecture-analyze/assets/cli/show_access_token.yaml +4 -7
  4. package/skills/agentforce-architecture-analyze/references/contract.json +1 -1
  5. package/skills/agentforce-architecture-analyze/scripts/emit_env.py +2 -2
  6. package/skills/agentforce-architecture-analyze/scripts/main.py +40 -44
  7. package/skills/agentforce-architecture-analyze/scripts/rest_client.py +11 -3
  8. package/skills/agentforce-architecture-analyze/scripts/sf_cli.py +77 -10
  9. package/skills/agentforce-architecture-analyze/scripts/tests/test_end_to_end_fixture.py +1 -0
  10. package/skills/agentforce-architecture-analyze/scripts/tests/test_flow_children_inflation.py +1 -0
  11. package/skills/agentforce-architecture-analyze/scripts/tests/test_main_pipeline.py +53 -2
  12. package/skills/agentforce-architecture-analyze/scripts/tests/test_resolve_creds.py +184 -36
  13. package/skills/agentforce-architecture-analyze/scripts/tests/test_rest_client.py +17 -0
  14. package/skills/agentforce-architecture-analyze/scripts/tests/test_sf_cli.py +165 -11
  15. package/skills/agentforce-architecture-analyze/scripts/tests/test_skill_md_pipeline_block.py +319 -0
  16. package/skills/agentforce-d360-analyze/SKILL.md +28 -10
  17. package/skills/agentforce-d360-analyze/scripts/dc.py +200 -50
  18. package/skills/agentforce-d360-analyze/scripts/discover_sessions.py +10 -6
  19. package/skills/agentforce-d360-analyze/scripts/fetch_dc.py +22 -6
  20. package/skills/agentforce-d360-analyze/scripts/resolve_session.py +21 -13
  21. package/skills/agentforce-d360-analyze/scripts/tests/test_dc_and_resolve_session.py +298 -76
  22. package/skills/agentforce-d360-analyze/scripts/tests/test_default_target_org.py +432 -0
  23. package/skills/agentforce-d360-analyze/scripts/tests/test_discover_sessions.py +1 -1
  24. package/skills/agentforce-d360-analyze/scripts/tests/test_resolve_session_main.py +9 -3
  25. package/skills/agentforce-d360-analyze/scripts/tests/test_skill_md_prefix_block.py +176 -0
  26. package/skills/consumer-goods-accruals-datakit-deploy/SKILL.md +17 -27
  27. package/skills/consumer-goods-accruals-datakit-deploy/scripts/setup.js +7 -17
  28. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/00-check-prerequisites.js +47 -63
  29. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/01-download-static-resource.js +10 -13
  30. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/02-replace-org-id.js +46 -46
  31. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/03-deploy-metadata.js +4 -21
  32. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/06-create-dataspace.js +58 -82
  33. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/07-deploy-accruals-reports.js +16 -315
  34. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/08-deploy-ui.js +9 -24
  35. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/09-completion.js +44 -44
  36. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/utils.js +11 -63
  37. package/skills/consumer-goods-rtr-datacloud-export-configure/SKILL.md +2 -1
  38. package/skills/consumer-goods-tpe-dashboard-configure/SKILL.md +2 -1
  39. package/skills/consumer-goods-tpe-dashboard-custom-kpi-configure/SKILL.md +2 -1
  40. package/skills/consumer-goods-tpe-datakit-deploy/SKILL.md +2 -1
  41. package/skills/data360-schema-get/SKILL.md +3 -3
  42. package/skills/design-systems-slds-validate/references/report-format.md +1 -1
  43. package/skills/dx-org-manage/SKILL.md +1 -0
  44. package/skills/dx-org-shape-manage/SKILL.md +1 -0
  45. package/skills/dx-org-snapshot-manage/SKILL.md +172 -0
  46. package/skills/dx-org-snapshot-manage/examples/create_error_output.json +16 -0
  47. package/skills/dx-org-snapshot-manage/examples/create_success_output.json +19 -0
  48. package/skills/dx-org-snapshot-manage/examples/delete_output.json +9 -0
  49. package/skills/dx-org-snapshot-manage/examples/get_output.json +19 -0
  50. package/skills/dx-org-snapshot-manage/examples/list_output.json +36 -0
  51. package/skills/dx-org-snapshot-manage/references/cli_flags.md +179 -0
  52. package/skills/dx-org-snapshot-manage/references/snapshot_usage.md +55 -0
  53. package/skills/dx-org-trial-expiration-check/SKILL.md +1 -1
  54. package/skills/dx-org-trial-expiration-check/scripts/check_expiration.sh +2 -2
  55. package/skills/education-cloud-student-recruitment-agent-configure/SKILL.md +7 -5
  56. package/skills/education-cloud-student-recruitment-agent-configure/references/prerequisites.md +11 -11
  57. package/skills/experience-content-media-image-generate/SKILL.md +83 -0
  58. package/skills/experience-content-media-image-generate/scripts/download-image.sh +84 -0
  59. package/skills/external-diagram-mermaid-generate/SKILL.md +12 -12
  60. package/skills/field-service-data-capture-migrate/references/component-mapping.md +1 -1
  61. package/skills/field-service-data-capture-migrate/references/fsm-dc-capability-catalog.md +1 -2
  62. package/skills/field-service-data-capture-migrate/references/known-deploy-errors.md +1 -0
  63. package/skills/field-service-data-capture-migrate/references/transformer-rules.md +1 -1
  64. package/skills/field-service-data-capture-migrate/scripts/transform_flow.py +12 -1
  65. package/skills/field-service-data-capture-migrate/scripts/vendor/build_flow.py +4 -1
  66. package/skills/field-service-setup-orchestrator-get/SKILL.md +0 -1
  67. package/skills/insurance-brokerage-agency-billing-configure/SKILL.md +233 -0
  68. package/skills/insurance-brokerage-agency-billing-configure/references/accounting-setup.md +117 -0
  69. package/skills/insurance-brokerage-agency-billing-configure/references/billing-rules-config.md +237 -0
  70. package/skills/insurance-brokerage-agency-billing-configure/references/billing-settings.md +271 -0
  71. package/skills/insurance-brokerage-agency-billing-configure/references/demo-policy-config.md +138 -0
  72. package/skills/insurance-brokerage-agency-billing-configure/references/org-defaults.md +64 -0
  73. package/skills/insurance-brokerage-agency-billing-configure/references/permissions-config.md +226 -0
  74. package/skills/insurance-brokerage-agency-billing-configure/references/prerequisites.md +142 -0
  75. package/skills/life-sciences-fieldsalesrep-coordinate/SKILL.md +3 -2
  76. package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-5-visit-creation-execution-state-and-recovery.md +3 -3
  77. package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-5-visit-creation-overview.md +68 -45
  78. package/skills/life-sciences-fieldsalesrep-coordinate/references/stage-5-visit-creation-visit-creation-data.md +17 -18
  79. package/skills/life-sciences-kam-coordinate/references/stage-5-data-and-plan-templates-overview.md +16 -3
  80. package/skills/life-sciences-kam-coordinate/references/stage-5-data-creation-data.md +16 -13
  81. package/skills/marketing-message-test/SKILL.md +194 -0
  82. package/skills/marketing-message-test/references/api-contract.md +333 -0
  83. package/skills/omnistudio-callable-apex-generate/SKILL.md +3 -3
  84. package/skills/omnistudio-datamapper-generate/SKILL.md +6 -6
  85. package/skills/omnistudio-datapacks-deploy/SKILL.md +11 -11
  86. package/skills/omnistudio-dependencies-analyze/SKILL.md +5 -5
  87. package/skills/omnistudio-epc-catalog-generate/SKILL.md +6 -6
  88. package/skills/omnistudio-flexcard-generate/SKILL.md +6 -6
  89. package/skills/omnistudio-integration-procedure-generate/SKILL.md +6 -6
  90. package/skills/omnistudio-omniscript-generate/SKILL.md +6 -5
  91. package/skills/platform-apex-logs-debug/SKILL.md +1 -0
  92. package/skills/platform-metadata-deploy/SKILL.md +1 -0
  93. package/skills/platform-models-api-configure/SKILL.md +5 -5
  94. package/skills/platform-soql-query/SKILL.md +1 -0
  95. package/skills/service-itsm-agentic-setup-cmdb-coordinate/SKILL.md +17 -6
  96. package/skills/service-itsm-agentic-setup-incident-sla-configure/SKILL.md +107 -192
  97. package/skills/service-itsm-agentic-setup-incident-sla-configure/assets/attach-milestone-action.json +6 -6
  98. package/skills/service-itsm-agentic-setup-incident-sla-configure/examples/milestone-patterns.md +49 -20
  99. package/skills/service-itsm-agentic-setup-incident-sla-configure/examples/output-templates.md +4 -4
  100. package/skills/service-itsm-agentic-setup-incident-sla-configure/references/mcp-invocation.md +224 -290
  101. package/skills/service-itsm-agentic-setup-incident-sla-configure/references/workflow.md +295 -0
  102. package/skills/service-itsm-incident-priority-configure/SKILL.md +1 -1
  103. package/skills/service-itsm-slack-itservice-configure/SKILL.md +2 -1
  104. package/skills/service-itsm-slack-itservice-configure/references/manage-slack-connection.md +2 -2
  105. package/skills/service-omni-channel-setup-coordinate/SKILL.md +20 -13
  106. package/skills/service-omni-channel-setup-coordinate/scripts/integration-driver.sh +5 -55
  107. package/skills/service-omni-channel-setup-coordinate/scripts/tests/test_omni_contracts.py +0 -38
  108. package/skills/service-omni-command-center-analyze/SKILL.md +2 -3
  109. package/skills/service-omni-command-center-analyze/scripts/analyze.sh +3 -3
  110. package/skills/tableau-next-asset-create/README.md +65 -0
  111. package/skills/tableau-next-asset-create/SKILL.md +409 -0
  112. package/skills/tableau-next-asset-create/references/admin-users.md +149 -0
  113. package/skills/tableau-next-asset-create/references/ai-readiness.md +355 -0
  114. package/skills/tableau-next-asset-create/references/alerts.md +111 -0
  115. package/skills/tableau-next-asset-create/references/build-workflow.md +819 -0
  116. package/skills/tableau-next-asset-create/references/dashboard-authoring.md +302 -0
  117. package/skills/tableau-next-asset-create/references/dashboard-design-principles.md +352 -0
  118. package/skills/tableau-next-asset-create/references/data-transform-gotchas.md +210 -0
  119. package/skills/tableau-next-asset-create/references/data-understanding.md +387 -0
  120. package/skills/tableau-next-asset-create/references/deep-reference-index.md +74 -0
  121. package/skills/tableau-next-asset-create/references/destructive-operations.md +62 -0
  122. package/skills/tableau-next-asset-create/references/edit-dashboard.md +273 -0
  123. package/skills/tableau-next-asset-create/references/edit-visualization.md +529 -0
  124. package/skills/tableau-next-asset-create/references/empty-source-handling.md +104 -0
  125. package/skills/tableau-next-asset-create/references/ingest-and-metric-gotchas.md +638 -0
  126. package/skills/tableau-next-asset-create/references/insight-bundle.md +69 -0
  127. package/skills/tableau-next-asset-create/references/large-flat-file-handling.md +331 -0
  128. package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/_envelope.json +54 -0
  129. package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/_shared-widget-fields.json +122 -0
  130. package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/add_page.json +29 -0
  131. package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/delete_page.json +27 -0
  132. package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/discard_dashboard.json +11 -0
  133. package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/move_widget.json +30 -0
  134. package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/move_widget_to_page.json +30 -0
  135. package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/place_widget_on_page.json +30 -0
  136. package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/remove_widget_from_dashboard.json +16 -0
  137. package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/remove_widget_from_page.json +26 -0
  138. package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/rename_page.json +28 -0
  139. package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/save_dashboard.json +18 -0
  140. package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/undo.json +11 -0
  141. package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/upsert_button_widget.json +69 -0
  142. package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/upsert_container_widget.json +88 -0
  143. package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/upsert_filter_widget.json +146 -0
  144. package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/upsert_metric_widget.json +78 -0
  145. package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/upsert_navigation_widget.json +80 -0
  146. package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/upsert_parameter_widget.json +160 -0
  147. package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/upsert_text_widget.json +103 -0
  148. package/skills/tableau-next-asset-create/references/schemas/edit-dashboard/upsert_visualization_widget.json +68 -0
  149. package/skills/tableau-next-asset-create/references/sdm-tool-reference.md +721 -0
  150. package/skills/tableau-next-asset-create/references/semantic-query-and-enrichment.md +355 -0
  151. package/skills/tableau-next-asset-create/references/shared-gates.md +322 -0
  152. package/skills/tableau-next-asset-create/references/sharing-and-promotion.md +306 -0
  153. package/skills/tableau-next-asset-create/references/tasks/ai-readiness-audit.md +51 -0
  154. package/skills/tableau-next-asset-create/references/tasks/analyze-data.md +48 -0
  155. package/skills/tableau-next-asset-create/references/tasks/build-dashboard.md +66 -0
  156. package/skills/tableau-next-asset-create/references/tasks/build-end-to-end.md +73 -0
  157. package/skills/tableau-next-asset-create/references/tasks/create-data-transform.md +51 -0
  158. package/skills/tableau-next-asset-create/references/tasks/create-viz.md +58 -0
  159. package/skills/tableau-next-asset-create/references/tasks/edit-dashboard.md +80 -0
  160. package/skills/tableau-next-asset-create/references/tasks/edit-sdm.md +106 -0
  161. package/skills/tableau-next-asset-create/references/tasks/edit-viz.md +40 -0
  162. package/skills/tableau-next-asset-create/references/tasks/enrich-model.md +68 -0
  163. package/skills/tableau-next-asset-create/references/tasks/ingest-flat-file.md +68 -0
  164. package/skills/tableau-next-asset-create/references/tasks/manage-alert.md +35 -0
  165. package/skills/tableau-next-asset-create/references/tasks/profile-dataset.md +47 -0
  166. package/skills/tableau-next-asset-create/references/tasks/promote-or-reuse.md +46 -0
  167. package/skills/tableau-next-asset-create/references/tasks/provision-user.md +39 -0
  168. package/skills/tableau-next-asset-create/references/tasks/query-model.md +92 -0
  169. package/skills/tableau-next-asset-create/references/tasks/review-verified-questions.md +42 -0
  170. package/skills/tableau-next-asset-create/references/tasks/share-asset.md +37 -0
  171. package/skills/tableau-next-asset-create/references/verified-questions.md +125 -0
  172. package/skills/tableau-next-asset-create/references/viz-authoring.md +992 -0
  173. package/skills/service-de-channel-consent-configure/SKILL.md +0 -230
  174. package/skills/service-itsm-agentic-setup-incident-sla-configure/assets/predefined-incident-policy.json +0 -102
@@ -15,6 +15,8 @@ metadata:
15
15
  semver: ">=22.0.0"
16
16
  - tool: ["sf"]
17
17
  semver: ">=2.0.0"
18
+ - tool: ["sfdx"]
19
+ semver: ">=7.0.0"
18
20
  ---
19
21
 
20
22
  ## MANDATORY: Always write the Phase 6 report
@@ -134,37 +136,30 @@ is empty — `WHERE CreatedDate > null` is an `INVALID_QUERY_FILTER_OPERATOR` er
134
136
  Salesforce SOQL and will abort the query.
135
137
 
136
138
  ### 1.7 Org-side manual prerequisites
137
- The setup script (Step 0) displays the full prerequisites list and waits for user
138
- confirmation before proceeding. If the user's prompt already confirms these, treat them
139
- as confirmed — do **not** re-ask. Only ask for items the user has not already addressed:
139
+ If the user's prompt already confirms these prerequisites, treat them as confirmed — do
140
+ **not** re-ask. Only ask for items the user has not already addressed:
140
141
  - Data Cloud and Analytics Studio enabled (Setup → Data Cloud Setup)
141
142
  - Connectors feature enabled (Data Cloud Setup → Feature Manager)
142
143
  - A user assigned to the **Accrual Ingestion Process** in Processing Services Pairing
143
144
  with the **Data Cloud Architect** permission set
144
- - OAuth scope **`cdp_ingest_api`** configured on the integration app used by TPM Offcore.
145
- This may be a **Connected App** or an **External Client App** depending on the org —
146
- the app name varies by configuration. Verify via Setup → App Manager (Connected Apps)
147
- or Setup → External Client Apps. Do NOT assume any specific app name.
148
- - Permission sets assigned to the System Admin (and to the Accrual Ingestion Process user):
149
- - **Data Cloud Architect**
150
- - CRM Analytics must be enabled first (Setup → Feature Settings → Analytics → Analytics → Getting Started → "Enable CRM Analytics"), then assign to the admin:
151
- - **CRM Analytics Plus Admin**
145
+ - OAuth scope **`cdp_ingest_api`** on the TPM Offcore connected app
146
+ - Licenses: `AccrualsProcessingAddOn`, `AccrualsProcessingUserAddOn`,
147
+ `TpmAccrualsAddOn`, `TpmAccrualsUserAddOn` (advisory — record what the user confirmed)
152
148
 
153
149
  ---
154
150
 
155
151
  ## Phase 2 — Verify the Data Cloud connection (best-effort)
156
152
 
157
- The OAuth client used by TPM Offcore can be a **Connected App** or an **External Client
158
- App**, and the app name varies by org configuration — it cannot be queried reliably by
159
- name. Skip the programmatic check. Instead, if the user has not already confirmed
160
- `cdp_ingest_api` as part of Phase 1.7, ask them to verify it manually:
153
+ Try to verify `cdp_ingest_api` by query — treat "cannot verify" as WARN, not STOP:
161
154
 
162
- > "Please confirm that `cdp_ingest_api` is in the selected OAuth scopes for the
163
- > integration app used by TPM Offcore. You can check under Setup → App Manager
164
- > (for Connected Apps) or Setup → External Client Apps."
155
+ ```bash
156
+ sf data query --target-org <alias> \
157
+ --query "SELECT Id, Name FROM ConnectedApplication WHERE Name LIKE '%TPM%'"
158
+ ```
165
159
 
166
- Continue once the user confirms. Do not block on this — treat unconfirmed as WARN in
167
- the Phase 6 report.
160
+ If the query errors on permissions, ask the user to confirm `cdp_ingest_api` is in
161
+ Setup → App Manager → (edit TPM Offcore connected app) → OAuth Policies →
162
+ Selected OAuth Scopes. Continue only after the user confirms.
168
163
 
169
164
  ---
170
165
 
@@ -228,13 +223,8 @@ Evaluate the rows:
228
223
  failing component name and its `DeploymentError` value. Do not proceed to Phase 5.
229
224
  Record Phase 5 as `not run — deployment failures unresolved` in the Phase 6 report.
230
225
  - Any `InProgress` row → wait 60 s and re-query. **Do not advance to Phase 5 until all
231
- rows are in a terminal state** (`Successful` or `Failure`). DataKit deployments on
232
- production orgs typically take 60–90 minutes. The setup script (`node scripts/setup.js`)
233
- has an internal 90-minute polling timeout — if the script exits with a timeout error,
234
- **do not treat this as a failure**: query `DataKitDeploymentLog` directly in the agent
235
- Phase 4 loop (as shown above) and continue polling until terminal state or a genuine
236
- liveness signal is lost (org unreachable, explicit Failure rows). Surface live counts
237
- periodically so the user can see progress.
226
+ rows are in a terminal state** (`Successful` or `Failure`). If any row is still
227
+ `InProgress` after 30 minutes, stop and surface the live counts.
238
228
  - Zero rows → the deployment has not produced log entries yet; re-query every 60 s up to
239
229
  30 minutes. Treat persistent zero rows as a deployment problem — stop and report.
240
230
  - All rows `Successful` → proceed to Phase 5.
@@ -179,16 +179,7 @@ function getConfig() {
179
179
  * @param {string} salesOrg - Must be exactly 4 alphanumeric characters.
180
180
  * @param {string|null} namespaceOverride - When non-null, must be 1-15 alphanumeric characters.
181
181
  */
182
- function validateInputs(salesOrg, namespaceOverride, orgAlias) {
183
- // Org alias: Salesforce allows alphanumeric, hyphens, underscores, dots, and @ (usernames).
184
- // Restrict to safe characters to prevent shell metacharacter injection on Windows cmd.exe.
185
- // Allow alphanumeric, hyphens, underscores, dots, @, and + (valid in Salesforce usernames, e.g. user+ci@example.com).
186
- // None of these characters are cmd.exe metacharacters, so shell injection via org alias is prevented.
187
- if (!/^[A-Za-z0-9_\-@.+]+$/.test(orgAlias)) {
188
- console.error(`Error: Invalid org alias "${orgAlias}". Must contain only alphanumeric characters, hyphens, underscores, dots, @, or +.`);
189
- process.exit(1);
190
- }
191
-
182
+ function validateInputs(salesOrg, namespaceOverride) {
192
183
  if (!/^[A-Za-z0-9]{4}$/.test(salesOrg)) {
193
184
  console.error('Error: Invalid sales org.');
194
185
  process.exit(1);
@@ -232,7 +223,7 @@ async function main() {
232
223
  const { orgAlias, skipDataSpace, salesOrg, dryRun, namespaceOverride } = config;
233
224
 
234
225
  // Validate inputs before any org access or step execution
235
- validateInputs(salesOrg, namespaceOverride, orgAlias);
226
+ validateInputs(salesOrg, namespaceOverride);
236
227
 
237
228
  const { dataSpaceName, dataSpacePrefix } = getDataSpaceConfig(salesOrg);
238
229
 
@@ -274,6 +265,8 @@ async function main() {
274
265
  }
275
266
 
276
267
  const setupDir = __dirname;
268
+ const dataKitPath = path.join(setupDir, 'CGCloudAddons', 'TPM', 'Accruals', 'TPM Accruals Data Kit');
269
+ const scriptsDir = path.join(setupDir, 'CGCloudAddons', 'TPM', 'Accruals', 'Accruals Data Kit Deployment Scripts');
277
270
 
278
271
  console.log(`Sales Org: ${salesOrg}`);
279
272
  if (!skipDataSpace) {
@@ -289,10 +282,7 @@ async function main() {
289
282
  }
290
283
 
291
284
  // Run all steps in order
292
- // Step 1 returns the extraction dir; derive dataKitPath and scriptsDir from it.
293
- const cgcloudAddonsDir = await downloadStaticResource(orgAlias, setupDir);
294
- const dataKitPath = path.join(cgcloudAddonsDir, 'TPM', 'Accruals', 'TPM Accruals Data Kit');
295
- const scriptsDir = path.join(cgcloudAddonsDir, 'TPM', 'Accruals', 'Accruals Data Kit Deployment Scripts');
285
+ await downloadStaticResource(orgAlias, setupDir);
296
286
  await replaceOrgId(orgId, dataKitPath);
297
287
  await deployMetadata(orgAlias, dataKitPath, dryRun);
298
288
  await deployEngine(orgAlias, scriptsDir, orgId, namespace, dryRun);
@@ -306,10 +296,10 @@ async function main() {
306
296
  }
307
297
 
308
298
  // Step 7: Deploy Accruals Reports with Data Space replacements
309
- await deployAccrualsReports(orgAlias, cgcloudAddonsDir, skipDataSpace, dataSpaceName, dataSpacePrefix, namespace, dryRun);
299
+ await deployAccrualsReports(orgAlias, setupDir, skipDataSpace, dataSpaceName, dataSpacePrefix, namespace, dryRun);
310
300
 
311
301
  // Step 8: Deploy UI components (TPMAccrualTacticSummary)
312
- await deployUI(orgAlias, cgcloudAddonsDir, skipDataSpace, dataSpaceName, dataSpacePrefix, salesOrg, namespace, dryRun);
302
+ await deployUI(orgAlias, setupDir, skipDataSpace, dataSpaceName, dataSpacePrefix, salesOrg, namespace, dryRun);
313
303
 
314
304
  // Step 9: Completion and next steps
315
305
  await completion(dryRun);
@@ -1,63 +1,47 @@
1
- const readline = require('readline');
2
-
3
- /**
4
- * Step 0: Check Prerequisites
5
- * Presents prerequisites to the user and waits for confirmation
6
- */
7
- async function checkPrerequisites() {
8
- console.log('\n=== Step 0: Prerequisites ===');
9
-
10
- console.log('\nBefore proceeding, confirm ALL of the following are complete in your org.\n');
11
-
12
- console.log('--- PERMISSIONS ---');
13
- console.log('\n1. Assign permission set "Data Cloud Architect" to:');
14
- console.log(' - The System Admin user');
15
- console.log(' - The user assigned to the Accrual Ingestion Process');
16
- console.log('\n2. Enable CRM Analytics (if you need Accruals dashboards):');
17
- console.log(' Setup > Feature Settings > Analytics > Analytics > Getting Started > Enable CRM Analytics');
18
- console.log(' Then assign permission set "CRM Analytics Plus Admin" to the System Admin.');
19
-
20
- console.log('\n--- DATA CLOUD ---');
21
- console.log('\n3. Data Cloud is enabled and set up (via the Data Cloud app, not Setup).');
22
- console.log('\n4. Salesforce CRM connector is active:');
23
- console.log(' Data Cloud app > Data Cloud Setup > Salesforce CRM > enable Home Org connection.');
24
- console.log(' Also activate any other required connectors using the dropdown button.');
25
- console.log('\n5. A user is assigned to the Accrual Ingestion Process:');
26
- console.log(' Setup > Processing Services Pairing > Accrual Ingestion Process.');
27
-
28
- console.log('\n--- INTEGRATION ---');
29
- console.log('\n6. OAuth scope "cdp_ingest_api" is configured on the integration app used by TPM Offcore.');
30
- console.log(' Check Setup > App Manager (Connected Apps) or Setup > External Client Apps.');
31
- console.log(' The app name varies by org — locate the one used by TPM Offcore.');
32
-
33
- console.log('\n');
34
-
35
- // Wait for user confirmation
36
- await waitForUserConfirmation();
37
-
38
- console.log('[OK] Prerequisites confirmed. Proceeding to next step...\n');
39
- }
40
-
41
- /**
42
- * Wait for user to press Enter.
43
- * Auto-confirms immediately when stdin is not a TTY (piped / headless run).
44
- */
45
- function waitForUserConfirmation() {
46
- if (!process.stdin.isTTY) {
47
- console.log('[Auto-confirmed: running non-interactively]');
48
- return Promise.resolve();
49
- }
50
- return new Promise((resolve) => {
51
- const rl = readline.createInterface({
52
- input: process.stdin,
53
- output: process.stdout
54
- });
55
-
56
- rl.question('Press Enter when you have confirmed all prerequisites are met...', () => {
57
- rl.close();
58
- resolve();
59
- });
60
- });
61
- }
62
-
63
- module.exports = checkPrerequisites;
1
+ const readline = require('readline');
2
+
3
+ /**
4
+ * Step 0: Check Prerequisites
5
+ * Presents prerequisites to the user and waits for confirmation
6
+ */
7
+ async function checkPrerequisites() {
8
+ console.log('\n=== Step 0: Prerequisites ===');
9
+
10
+ console.log('\nBefore proceeding with the setup, please ensure the following prerequisites are met:');
11
+ console.log('\n1. Enable Data Cloud and Analytics Studio');
12
+ console.log(' - Navigate to Setup > Data Cloud Setup');
13
+ console.log(' - Ensure Data Cloud and Analytics Studio are enabled');
14
+ console.log('\n2. Ensure there is a user assigned in the Processing Services Pairing app to the Accrual Ingestion Process');
15
+ console.log(' - The user must have the Data Cloud Architect permission set');
16
+ console.log(' - Alternatively, a System Admin or user with Data Cloud Architect can be assigned to all processes');
17
+ console.log('\n3. Ensure the connected app or external client app for TPM Offcore has the oauth scope "cdp_ingest_api"');
18
+ console.log(' - Navigate to Setup > App Manager');
19
+ console.log(' - Edit your connected app and verify the oauth scope includes "cdp_ingest_api"');
20
+ console.log(' - Note: Depending on your org setup, this permission may already be included');
21
+
22
+ console.log('\n');
23
+
24
+ // Wait for user confirmation
25
+ await waitForUserConfirmation();
26
+
27
+ console.log('[OK] Prerequisites confirmed. Proceeding to next step...\n');
28
+ }
29
+
30
+ /**
31
+ * Wait for user to press Enter
32
+ */
33
+ function waitForUserConfirmation() {
34
+ return new Promise((resolve) => {
35
+ const rl = readline.createInterface({
36
+ input: process.stdin,
37
+ output: process.stdout
38
+ });
39
+
40
+ rl.question('Press Enter when you have confirmed all prerequisites are met...', () => {
41
+ rl.close();
42
+ resolve();
43
+ });
44
+ });
45
+ }
46
+
47
+ module.exports = checkPrerequisites;
@@ -1,6 +1,5 @@
1
1
  const fs = require('fs');
2
2
  const https = require('https');
3
- const os = require('os');
4
3
  const path = require('path');
5
4
  const { execFileSync } = require('child_process');
6
5
  const { execSfJson, getOrgDetails } = require('./utils');
@@ -12,8 +11,6 @@ async function downloadStaticResource(orgAlias, setupDir) {
12
11
  console.log('\n=== Step 1: Downloading CGCloudAddons static resource ===');
13
12
 
14
13
  const staticResourceName = 'CGCloudAddons';
15
- // Extract to os.tmpdir() to avoid EBUSY locks in the script tree on Windows
16
- const extractBase = path.join(os.tmpdir(), `tpm_accruals_extract_${Date.now()}`);
17
14
 
18
15
  // Get org details for REST API call
19
16
  console.log('Getting org connection details...');
@@ -33,8 +30,7 @@ async function downloadStaticResource(orgAlias, setupDir) {
33
30
 
34
31
  // Download the static resource body using REST API
35
32
  console.log('Downloading static resource content...');
36
- fs.mkdirSync(extractBase, { recursive: true });
37
- const targetFile = path.join(extractBase, `${staticResourceName}.zip`);
33
+ const targetFile = path.join(setupDir, `${staticResourceName}.zip`);
38
34
  const bodyUrl = `${orgDetails.instanceUrl}/services/data/v${orgDetails.apiVersion}/sobjects/StaticResource/${staticResourceId}/Body`;
39
35
 
40
36
  await new Promise((resolve, reject) => {
@@ -66,15 +62,17 @@ async function downloadStaticResource(orgAlias, setupDir) {
66
62
 
67
63
  // Extract the zip file
68
64
  console.log('Extracting static resource...');
69
- const extractDir = path.join(extractBase, staticResourceName);
70
- fs.mkdirSync(extractDir, { recursive: true });
65
+ const extractDir = path.join(setupDir, staticResourceName);
71
66
 
72
- // Extract: use Windows System32 bsdtar on Windows (Git Bash's GNU tar cannot
73
- // read ZIP and misinterprets Windows drive letters as URLs). Convert paths to
74
- // forward-slash POSIX form so bsdtar receives them correctly.
75
- function toPosix(p) { return p.replace(/\\/g, '/'); }
67
+ // Remove existing extraction directory if it exists
68
+ if (fs.existsSync(extractDir)) {
69
+ fs.rmSync(extractDir, { recursive: true });
70
+ }
71
+
72
+ // Extract: tar on Windows (bsdtar handles ZIP), unzip on macOS/Linux.
73
+ // GNU tar on Linux cannot read ZIP archives.
76
74
  const [unzipCmd, unzipArgs] = process.platform === 'win32'
77
- ? ['C:\\Windows\\System32\\tar.exe', ['-xf', toPosix(targetFile), '-C', toPosix(extractDir)]]
75
+ ? ['tar', ['-xf', targetFile, '-C', extractDir]]
78
76
  : ['unzip', ['-o', '-q', targetFile, '-d', extractDir]];
79
77
  try {
80
78
  execFileSync(unzipCmd, unzipArgs, { stdio: 'pipe' });
@@ -94,7 +92,6 @@ async function downloadStaticResource(orgAlias, setupDir) {
94
92
  }
95
93
 
96
94
  console.log('[OK] CGCloudAddons static resource downloaded and extracted successfully\n');
97
- return extractDir;
98
95
  }
99
96
 
100
97
  module.exports = downloadStaticResource;
@@ -1,46 +1,46 @@
1
- const fs = require('fs');
2
- const path = require('path');
3
-
4
- function walkFiles(dir, extensions) {
5
- const results = [];
6
- for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
7
- const full = path.join(dir, entry.name);
8
- if (entry.isDirectory()) results.push(...walkFiles(full, extensions));
9
- else if (extensions.some(ext => entry.name.endsWith(ext))) results.push(full);
10
- }
11
- return results;
12
- }
13
-
14
- /**
15
- * Step 2: Replace __SF_ORG_ID__ placeholder with actual org ID in all metadata files
16
- */
17
- async function replaceOrgId(orgId, dataKitPath) {
18
- console.log('\n=== Step 2: Replacing org ID placeholders ===');
19
- console.log(`Target directory: ${dataKitPath}`);
20
- console.log(`Org ID: ${orgId} (truncated to 15 chars: ${orgId.slice(0, 15)})`);
21
-
22
- // Truncate org ID to 15 characters
23
- const truncatedOrgId = orgId.slice(0, 15);
24
-
25
- // Find all XML and JSON files
26
- const files = walkFiles(dataKitPath, ['.xml', '.json']);
27
-
28
- let replacementCount = 0;
29
-
30
- files.forEach(file => {
31
- try {
32
- const content = fs.readFileSync(file, 'utf8');
33
- if (content.includes('__SF_ORG_ID__')) {
34
- const newContent = content.replace(/__SF_ORG_ID__/g, truncatedOrgId);
35
- fs.writeFileSync(file, newContent, 'utf8');
36
- replacementCount++;
37
- }
38
- } catch (error) {
39
- console.error(`Warning: Failed to process file ${file}: ${error.message}`);
40
- }
41
- });
42
-
43
- console.log(`[OK] Replaced org ID placeholder in ${replacementCount} file(s)\n`);
44
- }
45
-
46
- module.exports = replaceOrgId;
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+
4
+ function walkFiles(dir, extensions) {
5
+ const results = [];
6
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
7
+ const full = path.join(dir, entry.name);
8
+ if (entry.isDirectory()) results.push(...walkFiles(full, extensions));
9
+ else if (extensions.some(ext => entry.name.endsWith(ext))) results.push(full);
10
+ }
11
+ return results;
12
+ }
13
+
14
+ /**
15
+ * Step 2: Replace __SF_ORG_ID__ placeholder with actual org ID in all metadata files
16
+ */
17
+ async function replaceOrgId(orgId, dataKitPath) {
18
+ console.log('\n=== Step 2: Replacing org ID placeholders ===');
19
+ console.log(`Target directory: ${dataKitPath}`);
20
+ console.log(`Org ID: ${orgId} (truncated to 15 chars: ${orgId.slice(0, 15)})`);
21
+
22
+ // Truncate org ID to 15 characters
23
+ const truncatedOrgId = orgId.slice(0, 15);
24
+
25
+ // Find all XML and JSON files
26
+ const files = walkFiles(dataKitPath, ['.xml', '.json']);
27
+
28
+ let replacementCount = 0;
29
+
30
+ files.forEach(file => {
31
+ try {
32
+ const content = fs.readFileSync(file, 'utf8');
33
+ if (content.includes('__SF_ORG_ID__')) {
34
+ const newContent = content.replace(/__SF_ORG_ID__/g, truncatedOrgId);
35
+ fs.writeFileSync(file, newContent, 'utf8');
36
+ replacementCount++;
37
+ }
38
+ } catch (error) {
39
+ console.error(`Warning: Failed to process file ${file}: ${error.message}`);
40
+ }
41
+ });
42
+
43
+ console.log(`[OK] Replaced org ID placeholder in ${replacementCount} file(s)\n`);
44
+ }
45
+
46
+ module.exports = replaceOrgId;
@@ -1,7 +1,4 @@
1
- const os = require('os');
2
- const fs = require('fs');
3
- const path = require('path');
4
- const { exec, execDeployJson, execSfJson } = require('./utils');
1
+ const { exec, execDeployJson, getSfBin } = require('./utils');
5
2
 
6
3
  /**
7
4
  * Step 3: Deploy TPM Accruals Data Kit metadata to the org
@@ -21,24 +18,10 @@ async function deployMetadata(orgAlias, dataKitPath, dryRun = false) {
21
18
  return;
22
19
  }
23
20
 
24
- // Convert to MDAPI format first, then deploy via --metadata-dir.
25
- // This bypasses SF CLI source-tracking ("NothingToDeploy" errors on non-scratch
26
- // orgs where tracking state diverges from org reality).
27
- const mdapiOut = path.join(os.tmpdir(), `tpm_accruals_mdapi_${Date.now()}`);
28
- console.log('Converting source to MDAPI format...');
29
- const convertResult = execSfJson(['project', 'convert', 'source', '--output-dir', mdapiOut, '--json'], { cwd: dataKitPath });
30
- if (convertResult.status !== 0) {
31
- throw new Error(`Source conversion failed: ${convertResult.message || JSON.stringify(convertResult)}`);
32
- }
33
- console.log(`Converted to: ${convertResult.result && convertResult.result.location || mdapiOut}`);
34
-
21
+ // Deploy using sf project deploy start from the data kit root so the
22
+ // sfdx-project.json sourceApiVersion is respected.
35
23
  console.log('Deploying metadata...');
36
- execDeployJson(['project', 'deploy', 'start', '--metadata-dir', mdapiOut, '--target-org', orgAlias]);
37
-
38
- // Clean up temp dir
39
- try { fs.rmSync(mdapiOut, { recursive: true }); } catch (cleanupErr) {
40
- console.warn(`[WARN] Could not remove temp directory ${mdapiOut}: ${cleanupErr.message}`);
41
- }
24
+ execDeployJson(['project', 'deploy', 'start', '--source-dir', 'force-app', '--target-org', orgAlias], { cwd: dataKitPath });
42
25
 
43
26
  console.log('[OK] TPM Accruals Data Kit deployed successfully\n');
44
27
  }
@@ -1,82 +1,58 @@
1
- const readline = require('readline');
2
- const { execSfJson } = require('./utils');
3
-
4
- /**
5
- * Step 6: Manual Data Space creation with DLO configuration
6
- * @param {string} orgAlias - The Salesforce org alias
7
- * @param {string} salesOrg - The sales org value (e.g., "0001")
8
- * @param {string} dataSpaceName - The data space name (e.g., "DataSpace_0001")
9
- * @param {string} dataSpacePrefix - The data space prefix (e.g., "DS1")
10
- * @param {boolean} dryRun - If true, skip user prompt
11
- */
12
- async function createDataSpace(orgAlias, salesOrg, dataSpaceName, dataSpacePrefix, dryRun = false) {
13
- console.log('\n=== Step 6: Create Data Space (Manual Step) ===');
14
-
15
- console.log('\nData Space configuration:');
16
- console.log(` - Name: ${dataSpaceName}`);
17
- console.log(` - Prefix: ${dataSpacePrefix}`);
18
- console.log(` - DLO: accrualoutput_decorated__dll`);
19
- console.log(` - Filter: SalesOrg equals "${salesOrg}"`);
20
-
21
- if (dryRun) {
22
- console.log('\n[DRY RUN] Skipping manual step prompt');
23
- console.log('[OK] Step 6 completed (dry run)\n');
24
- return;
25
- }
26
-
27
- console.log('\nPlease complete the following manual steps in your Salesforce org:');
28
- console.log('\n1. Navigate to the Data Spaces tab in your org');
29
- console.log('2. Create a new Data Space with the details shown above');
30
- console.log('3. Go into the newly created Data Space');
31
- console.log('4. Add the DLO: accrualoutput_decorated__dll');
32
- console.log('5. Apply the filter shown above');
33
-
34
- console.log('\n');
35
-
36
- // Wait for user confirmation
37
- await waitForUserConfirmation();
38
-
39
- // In headless mode, verify the Data Space actually exists before continuing.
40
- // DataSpace is a queryable SOQL object — query it to confirm creation.
41
- if (!process.stdin.isTTY) {
42
- const verifyResult = execSfJson([
43
- 'data', 'query', '--target-org', orgAlias, '--json',
44
- '--query', `SELECT Id, Status FROM DataSpace WHERE DataSpaceApiName = '${dataSpaceName}' LIMIT 1`
45
- ]);
46
- const records = (verifyResult.result && verifyResult.result.records) || [];
47
- if (records.length === 0) {
48
- throw new Error(
49
- `Data Space '${dataSpaceName}' not found in org. ` +
50
- `Create it manually (Data Cloud app → Data Spaces → New, prefix '${dataSpacePrefix}', ` +
51
- `add DLO accrualoutput_decorated__dll with SalesOrg = '${salesOrg}') before re-running.`
52
- );
53
- }
54
- console.log(`[OK] Data Space '${dataSpaceName}' verified (Status: ${records[0].Status}).\n`);
55
- } else {
56
- console.log('[OK] Data Space creation confirmed. Proceeding to next step...\n');
57
- }
58
- }
59
-
60
- /**
61
- * Wait for user to press Enter.
62
- * Auto-confirms immediately when stdin is not a TTY (piped / headless run).
63
- */
64
- function waitForUserConfirmation() {
65
- if (!process.stdin.isTTY) {
66
- console.log('[Auto-confirmed: running non-interactively]');
67
- return Promise.resolve();
68
- }
69
- return new Promise((resolve) => {
70
- const rl = readline.createInterface({
71
- input: process.stdin,
72
- output: process.stdout
73
- });
74
-
75
- rl.question('Press Enter when you have completed these steps...', () => {
76
- rl.close();
77
- resolve();
78
- });
79
- });
80
- }
81
-
82
- module.exports = createDataSpace;
1
+ const readline = require('readline');
2
+
3
+ /**
4
+ * Step 6: Manual Data Space creation with DLO configuration
5
+ * @param {string} orgAlias - The Salesforce org alias
6
+ * @param {string} salesOrg - The sales org value (e.g., "0001")
7
+ * @param {string} dataSpaceName - The data space name (e.g., "DataSpace_0001")
8
+ * @param {string} dataSpacePrefix - The data space prefix (e.g., "DS1")
9
+ * @param {boolean} dryRun - If true, skip user prompt
10
+ */
11
+ async function createDataSpace(orgAlias, salesOrg, dataSpaceName, dataSpacePrefix, dryRun = false) {
12
+ console.log('\n=== Step 6: Create Data Space (Manual Step) ===');
13
+
14
+ console.log('\nData Space configuration:');
15
+ console.log(` - Name: ${dataSpaceName}`);
16
+ console.log(` - Prefix: ${dataSpacePrefix}`);
17
+ console.log(` - DLO: accrualoutput_decorated__dll`);
18
+ console.log(` - Filter: SalesOrg equals "${salesOrg}"`);
19
+
20
+ if (dryRun) {
21
+ console.log('\n[DRY RUN] Skipping manual step prompt');
22
+ console.log('[OK] Step 6 completed (dry run)\n');
23
+ return;
24
+ }
25
+
26
+ console.log('\nPlease complete the following manual steps in your Salesforce org:');
27
+ console.log('\n1. Navigate to the Data Spaces tab in your org');
28
+ console.log('2. Create a new Data Space with the details shown above');
29
+ console.log('3. Go into the newly created Data Space');
30
+ console.log('4. Add the DLO: accrualoutput_decorated__dll');
31
+ console.log('5. Apply the filter shown above');
32
+
33
+ console.log('\n');
34
+
35
+ // Wait for user confirmation
36
+ await waitForUserConfirmation();
37
+
38
+ console.log('[OK] Data Space creation confirmed. Proceeding to next step...\n');
39
+ }
40
+
41
+ /**
42
+ * Wait for user to press Enter
43
+ */
44
+ function waitForUserConfirmation() {
45
+ return new Promise((resolve) => {
46
+ const rl = readline.createInterface({
47
+ input: process.stdin,
48
+ output: process.stdout
49
+ });
50
+
51
+ rl.question('Press Enter when you have completed these steps...', () => {
52
+ rl.close();
53
+ resolve();
54
+ });
55
+ });
56
+ }
57
+
58
+ module.exports = createDataSpace;