@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
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: automation-sandbox-post-copy-configure
|
|
3
|
-
description: "Apply a Salesforce sandbox post-copy automation JSON config against a target org. For each entry, the skill derives the correct Tooling API sobject from the entry's `ConfigurationName`, verifies the derivation via a describe probe, resolves the record Id via SOQL-over-REST, then PATCHes the record via the compound `Metadata` field using `sf api request rest`. Use when the user asks to apply, run, execute, dry-run, or preview a post-copy or post-refresh config file (e.g. `post-copy-config.json`) against a sandbox. Trigger phrases: \"apply post-copy config\", \"run post-copy automation\", \"execute sandbox post-refresh JSON\", \"apply sandbox refresh config\", \"configure sandbox after refresh\". DO NOT TRIGGER for generating the config JSON from an SOP (delegate to `automation-sandbox-post-copy-config-generate`), or for deploying metadata XML."
|
|
3
|
+
description: "Apply a Salesforce sandbox post-copy automation JSON config against a target org. For each entry, the skill derives the correct Tooling API sobject from the entry's `ConfigurationName`, verifies the derivation via a describe probe, resolves the record Id via SOQL-over-REST, then PATCHes the record via the compound `Metadata` field using `sf api request rest`. `ScheduledApex` entries run anonymous Apex `System.schedule(...)` via `sf apex run` (because `CronTrigger` is read-only) and verify via a `CronTrigger` SOQL read-back. Use when the user asks to apply, run, execute, dry-run, or preview a post-copy or post-refresh config file (e.g. `post-copy-config.json`) against a sandbox. Trigger phrases: \"apply post-copy config\", \"run post-copy automation\", \"execute sandbox post-refresh JSON\", \"apply sandbox refresh config\", \"configure sandbox after refresh\". DO NOT TRIGGER for generating the config JSON from an SOP (delegate to `automation-sandbox-post-copy-config-generate`), or for deploying metadata XML."
|
|
4
4
|
allowed-tools: Bash Read Write
|
|
5
5
|
metadata:
|
|
6
6
|
relatedSkills:
|
|
7
7
|
- "automation-sandbox-post-copy-config-generate"
|
|
8
8
|
- "dx-org-permission-set-assign"
|
|
9
9
|
- "platform-metadata-deploy"
|
|
10
|
-
version: "1.
|
|
10
|
+
version: "1.1"
|
|
11
11
|
domains: ["Automation"]
|
|
12
12
|
cliTools:
|
|
13
13
|
- tool: ["curl"]
|
|
@@ -23,15 +23,15 @@ metadata:
|
|
|
23
23
|
# Automation: Sandbox Post-Copy Configure
|
|
24
24
|
|
|
25
25
|
Apply a Salesforce sandbox post-copy automation JSON config to a target
|
|
26
|
-
org.
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
entry — pinned or derived — must
|
|
32
|
-
|
|
33
|
-
Entries whose API cannot be
|
|
34
|
-
summary and skipped —
|
|
26
|
+
org. Three canonical `ConfigurationName` values are pinned:
|
|
27
|
+
`OutboundMessages` and `RemoteSiteSettings` take the compound-`Metadata`
|
|
28
|
+
PATCH route (Steps A–E); `ScheduledApex` takes the anonymous-Apex route
|
|
29
|
+
(Step F), because `CronTrigger` is read-only in the Tooling API. Any
|
|
30
|
+
other `ConfigurationName` is derived and describe-verified. Every A–E
|
|
31
|
+
entry — pinned or derived — must pass Step B (describe returns 200 with
|
|
32
|
+
a `Metadata` compound field) before any PATCH is planned; Step B does
|
|
33
|
+
not apply to `ScheduledApex`. Entries whose API cannot be verified are
|
|
34
|
+
surfaced in the summary and skipped — never guessed at.
|
|
35
35
|
|
|
36
36
|
## Tool Restrictions
|
|
37
37
|
|
|
@@ -92,6 +92,7 @@ substitute a different sobject name):**
|
|
|
92
92
|
|-------------------|---------------------|--------------------|
|
|
93
93
|
| `OutboundMessages` | `WorkflowOutboundMessage` | `SELECT Id, FullName FROM WorkflowOutboundMessage WHERE EntityDefinition.QualifiedApiName = '<Fields.Object>'` — then client-side pick the row whose `FullName == '<Fields.Object>.<Label>'`. SOQL cannot filter on `FullName` directly for this sobject. |
|
|
94
94
|
| `RemoteSiteSettings` | `RemoteProxy` | `SELECT Id, SiteName FROM RemoteProxy WHERE SiteName = '<Label>'`. |
|
|
95
|
+
| `ScheduledApex` | `CronTrigger` (read-only — Anonymous Apex route, see Step F) | See Step F — pre-flight `SELECT Id FROM CronTrigger WHERE CronJobDetail.Name = '<escaped>'` gate. |
|
|
95
96
|
|
|
96
97
|
For canonical entries use the pinned sobject and SOQL as-is; skip
|
|
97
98
|
"derive". Do not invent `OutboundMessage`, `RemoteSiteSetting`, or
|
|
@@ -208,6 +209,13 @@ If the read-back value doesn't match the requested value, record
|
|
|
208
209
|
`FAILED_VERIFY` — the PATCH returned 204 but the effect is not
|
|
209
210
|
visible (usually a naming or permission issue).
|
|
210
211
|
|
|
212
|
+
### Step F — Scheduled Apex (anonymous Apex route)
|
|
213
|
+
|
|
214
|
+
`ScheduledApex` entries target the read-only `CronTrigger` sobject —
|
|
215
|
+
Steps A–E do not apply. **Read `references/scheduled_apex_path.md`
|
|
216
|
+
before executing** — it owns the full F-1 → F-4 recipe (pre-flight
|
|
217
|
+
AMBIGUOUS gate, snippet build, `sf apex run`, verify, dry-run).
|
|
218
|
+
|
|
211
219
|
---
|
|
212
220
|
|
|
213
221
|
## IsActive semantics
|
|
@@ -233,22 +241,21 @@ side-files (`plan/phases.json`, `requests/*.request.json`, etc.) —
|
|
|
233
241
|
inline every planned/actual request in the Markdown.
|
|
234
242
|
|
|
235
243
|
**Phase enumeration is script-owned.** Run `node scripts/plan-phases.mjs
|
|
236
|
-
<config.json>` and consume its `phases[]`
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
values collapse (`1, 2, 5` → ordinals `1, 2, 3`). `IsActive:false`
|
|
244
|
+
<config.json>` and consume its `phases[]` verbatim — each entry carries
|
|
245
|
+
`ordinal` (1-indexed for headings) and `executionOrder` (raw, for
|
|
246
|
+
`(ExecutionOrder = <raw>)`); sparse values collapse; `IsActive:false`
|
|
240
247
|
entries are pre-marked `SKIP_INACTIVE`. See
|
|
241
248
|
`references/execution_phasing.md` for the worked example.
|
|
242
249
|
|
|
243
|
-
For dry-run entries the `HTTP` column is `—` (em-dash). End the
|
|
244
|
-
summary with: `No PATCH requests were issued. To apply, re-run
|
|
245
|
-
without the dry-run flag.`
|
|
246
|
-
|
|
247
250
|
**Target-org resolution is script-owned.** Run `node
|
|
248
|
-
scripts/resolve-target-org.mjs`; substitute the returned `.alias`
|
|
249
|
-
|
|
251
|
+
scripts/resolve-target-org.mjs`; substitute the returned `.alias` into
|
|
252
|
+
the header. Never emit `<env:SF_TARGET_ORG>` or `$SF_TARGET_ORG`
|
|
250
253
|
verbatim.
|
|
251
254
|
|
|
255
|
+
For dry-run entries the `HTTP` column is `—`. End the summary with:
|
|
256
|
+
`No PATCH requests were issued. To apply, re-run without the dry-run
|
|
257
|
+
flag.`
|
|
258
|
+
|
|
252
259
|
```markdown
|
|
253
260
|
# Post-Copy Configure Run — <N> entries <planned|applied> against `<alias>`
|
|
254
261
|
|
|
@@ -287,12 +294,9 @@ glance. Outcome vocabulary: `SUCCESS`, `NOT_FOUND`, `AMBIGUOUS`,
|
|
|
287
294
|
`SKIPPED_INACTIVE`, `SKIPPED`, `DRY_RUN`, `DELETE_NOT_SUPPORTED`,
|
|
288
295
|
`NOT_ATTEMPTED`.
|
|
289
296
|
|
|
290
|
-
**Scripts are internal.**
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
output, or "I ran node …" narration into the summary Markdown or
|
|
294
|
-
the printed response. Consume the JSON, use the returned values, and
|
|
295
|
-
render the summary in the exact shape above.
|
|
297
|
+
**Scripts are internal.** Do **not** inline raw `node scripts/…` stdout
|
|
298
|
+
or "I ran node …" narration into the summary or the printed response —
|
|
299
|
+
consume the JSON, use the returned values, render the exact shape above.
|
|
296
300
|
|
|
297
301
|
## Scope
|
|
298
302
|
|
|
@@ -326,24 +330,17 @@ Gather or infer before applying:
|
|
|
326
330
|
the target sandbox. Never assume the default org — always confirm.
|
|
327
331
|
If the user has not supplied one, list available orgs with `sf org
|
|
328
332
|
list --json` and ask which to use.
|
|
329
|
-
- **Dry-run flag** (optional, default `false`):
|
|
330
|
-
**
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
fails (401, 404, `NamedOrgNotFoundError`), surface the error and
|
|
341
|
-
stop — do not fall back to a from-memory plan. Use dry-run on the
|
|
342
|
-
first pass unless the user has explicitly asked to apply.
|
|
343
|
-
- **Continue-on-error** (optional, default `true`): When true, a
|
|
344
|
-
failing entry does not abort the phase — the remaining entries in
|
|
345
|
-
the phase still execute, and the failure is reported in the
|
|
346
|
-
summary. When false, a failure aborts execution mid-phase.
|
|
333
|
+
- **Dry-run flag** (optional, default `false`): **reads ALLOWED, writes
|
|
334
|
+
FORBIDDEN** — not "no network calls". Still run every read (Step B
|
|
335
|
+
describe `GET`, Step C SOQL, Step D-1 record `GET`); the plan's
|
|
336
|
+
would-be PATCH body is only accurate against the real Metadata block.
|
|
337
|
+
The ONLY skipped calls are Step D-3 (`PATCH`) and Step E (verify).
|
|
338
|
+
Never fabricate current Metadata or invent keys. On a read failure
|
|
339
|
+
(401, 404, `NamedOrgNotFoundError`), surface and stop — never fall
|
|
340
|
+
back to a from-memory plan. Prefer dry-run on the first pass.
|
|
341
|
+
- **Continue-on-error** (optional, default `true`): true = failing
|
|
342
|
+
entries don't abort the phase (failure reported in the summary);
|
|
343
|
+
false = abort mid-phase.
|
|
347
344
|
|
|
348
345
|
If the user supplies a clear config path and target alias, proceed
|
|
349
346
|
without further questions.
|
|
@@ -352,9 +349,8 @@ without further questions.
|
|
|
352
349
|
|
|
353
350
|
## Workflow
|
|
354
351
|
|
|
355
|
-
Every step executes real `sf` CLI commands via
|
|
356
|
-
|
|
357
|
-
this skill is to mutate the target org, not to describe how.
|
|
352
|
+
Every step executes real `sf` CLI commands via Bash. Do NOT narrate the
|
|
353
|
+
plan without running the commands — this skill mutates the target org.
|
|
358
354
|
|
|
359
355
|
1. **Read and validate the config JSON** — load the file with the
|
|
360
356
|
Read tool. Every entry must be a JSON object with the five
|
|
@@ -371,18 +367,21 @@ this skill is to mutate the target org, not to describe how.
|
|
|
371
367
|
`references/execution_phasing.md` for the concurrency cap.
|
|
372
368
|
|
|
373
369
|
4. **Per-entry describe-verify pass** — for each distinct
|
|
374
|
-
`ConfigurationName`, run Step A +
|
|
375
|
-
verified sobject. Any `ConfigurationName`
|
|
376
|
-
every entry with that name as
|
|
370
|
+
`ConfigurationName` NOT equal to `ScheduledApex`, run Step A +
|
|
371
|
+
Step B once and cache the verified sobject. Any `ConfigurationName`
|
|
372
|
+
failing Step B marks every entry with that name as
|
|
373
|
+
`API_NOT_IDENTIFIED`. `ScheduledApex` skips describe-verify — it
|
|
374
|
+
uses the Anonymous Apex route (Step F), not compound-`Metadata`
|
|
375
|
+
PATCH.
|
|
377
376
|
|
|
378
377
|
5. **Per-entry apply pass** — for each entry inside each phase:
|
|
379
|
-
-
|
|
378
|
+
- `SKIP_INACTIVE` pre-marked by plan-phases → record
|
|
380
379
|
`SKIPPED_INACTIVE`, add a Follow-ups bullet, continue.
|
|
381
|
-
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
`DRY_RUN`.
|
|
380
|
+
- `ScheduledApex` → Step F (F-1 pre-flight, F-2 build, F-3 run,
|
|
381
|
+
F-4 verify). Dry-run: run F-1 + F-2 only; record `DRY_RUN`.
|
|
382
|
+
- All others → Step C (Id), Step D (GET+mutate+PATCH), Step E
|
|
383
|
+
(verify). Dry-run: run C + D-1/D-2 (so the printed plan reflects
|
|
384
|
+
the real merged payload); skip D-3 and E; record `DRY_RUN`.
|
|
386
385
|
|
|
387
386
|
6. **Between phases** — wait for every entry in the current phase to
|
|
388
387
|
complete before starting the next.
|
|
@@ -415,9 +414,9 @@ that file before deviating from the Step A–E procedure.
|
|
|
415
414
|
| Need | Delegate to |
|
|
416
415
|
|------|-------------|
|
|
417
416
|
| Turn a customer SOP into the JSON config this skill consumes | `automation-sandbox-post-copy-config-generate` |
|
|
418
|
-
| Deploy
|
|
419
|
-
| Create new records that do not yet exist on the target org | `platform-metadata-deploy`
|
|
420
|
-
| Assign permission sets required
|
|
417
|
+
| Deploy metadata XML outside the compound-Metadata Tooling pattern | Matching `generating-*` skill + a metadata deploy flow |
|
|
418
|
+
| Create new records that do not yet exist on the target org | `platform-metadata-deploy` |
|
|
419
|
+
| Assign permission sets required for the API calls | `dx-org-permission-set-assign` |
|
|
421
420
|
|
|
422
421
|
---
|
|
423
422
|
|
|
@@ -425,14 +424,19 @@ that file before deviating from the Step A–E procedure.
|
|
|
425
424
|
|
|
426
425
|
| File | When to read |
|
|
427
426
|
|------|-------------|
|
|
428
|
-
| `references/api_endpoints.md` | Steps A–E —
|
|
429
|
-
| `references/
|
|
430
|
-
| `
|
|
431
|
-
| `
|
|
432
|
-
| `
|
|
433
|
-
| `scripts/
|
|
434
|
-
| `
|
|
435
|
-
| `
|
|
436
|
-
| `
|
|
437
|
-
| `
|
|
438
|
-
| `
|
|
427
|
+
| `references/api_endpoints.md` | Steps A–E — full generic recipe with OBM / RSS worked examples and the camelCase-field convention |
|
|
428
|
+
| `references/scheduled_apex_path.md` | Step F — anonymous Apex route for `ScheduledApex` entries |
|
|
429
|
+
| `references/execution_phasing.md` | Step 3 (workflow) — phase-grouping model behind `scripts/plan-phases.mjs` |
|
|
430
|
+
| `references/authentication.md` | Step 2 — session check + how to handle 401 mid-run |
|
|
431
|
+
| `references/rules_gotchas.md` | Before deviating from Step A–E — invariants and runtime-failure responses |
|
|
432
|
+
| `scripts/plan-phases.mjs` | Step 3 — deterministic phase planner |
|
|
433
|
+
| `scripts/map-metadata-key.mjs` | Step D-2 — deterministic Metadata-key resolver |
|
|
434
|
+
| `scripts/classify-patch-result.mjs` | Step D-4 — deterministic HTTP-outcome classifier |
|
|
435
|
+
| `scripts/resolve-target-org.mjs` | Canonical output shape — target-org alias resolver |
|
|
436
|
+
| `scripts/build-scheduled-apex.mjs` | Step F-2 — anonymous-Apex snippet builder |
|
|
437
|
+
| `scripts/soql-escape-job-name.mjs` | Step F-1/F-4 — SOQL-escape `JobName` |
|
|
438
|
+
| `assets/api_request_templates.json` | Steps A–E — generic describe / lookup / GET+PATCH template |
|
|
439
|
+
| `assets/scheduled_apex_template.apex` | Step F-2 — anonymous Apex template with `{{JOB_NAME}}` / `{{CRON_EXPRESSION}}` / `{{APEX_CLASS_NAME}}` placeholders |
|
|
440
|
+
| `examples/sample_config_input.json` | Step 1 — shape of the config JSON this skill consumes |
|
|
441
|
+
| `examples/sample_scheduled_apex_config.json` | Step 1 — shape of a `ScheduledApex` config entry |
|
|
442
|
+
| `examples/sample_execution_summary.md` | Step 7 — shape of the summary report shown to the user |
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Anonymous Apex template — schedules a Salesforce Schedulable class.
|
|
2
|
+
//
|
|
3
|
+
// Placeholders (single-quote-escaped by scripts/build-scheduled-apex.mjs):
|
|
4
|
+
// {{JOB_NAME}} — value of CronTrigger.CronJobDetail.Name after scheduling.
|
|
5
|
+
// {{CRON_EXPRESSION}} — 6- or 7-field Salesforce cron expression.
|
|
6
|
+
// {{APEX_CLASS_NAME}} — bare identifier of a class that implements Schedulable.
|
|
7
|
+
//
|
|
8
|
+
// Prerequisites (enforced upstream — see references/scheduled_apex_path.md):
|
|
9
|
+
// 1. Step F-1 has already asserted no CronTrigger with CronJobDetail.Name = '{{JOB_NAME}}'
|
|
10
|
+
// exists on the target org — otherwise System.schedule throws.
|
|
11
|
+
// 2. The {{APEX_CLASS_NAME}} class exists on the target org — sandbox refresh
|
|
12
|
+
// copies class metadata, so this is normally satisfied post-refresh.
|
|
13
|
+
System.schedule('{{JOB_NAME}}', '{{CRON_EXPRESSION}}', new {{APEX_CLASS_NAME}}());
|
package/skills/automation-sandbox-post-copy-configure/examples/sample_scheduled_apex_config.json
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"ConfigurationName": "ScheduledApex",
|
|
4
|
+
"Label": "Nightly Data Sync",
|
|
5
|
+
"Fields": {
|
|
6
|
+
"ApexClassName": "NightlyDataSyncScheduler",
|
|
7
|
+
"CronExpression": "0 0 2 * * ?",
|
|
8
|
+
"JobName": "Nightly Data Sync"
|
|
9
|
+
},
|
|
10
|
+
"IsActive": true,
|
|
11
|
+
"ExecutionOrder": 3
|
|
12
|
+
}
|
|
13
|
+
]
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
# Step F — Scheduled Apex (anonymous Apex route)
|
|
2
|
+
|
|
3
|
+
`ScheduledApex` entries in the post-copy config target the `CronTrigger`
|
|
4
|
+
sobject, which is **read-only** in the Tooling API — there is no
|
|
5
|
+
compound `Metadata` field, so Steps A–E (describe → SOQL → GET+PATCH)
|
|
6
|
+
cannot apply. Instead this route runs anonymous Apex
|
|
7
|
+
`System.schedule(...)` via `sf apex run` and verifies via a
|
|
8
|
+
`CronTrigger` SOQL read-back.
|
|
9
|
+
|
|
10
|
+
## Fields shape
|
|
11
|
+
|
|
12
|
+
Every `ScheduledApex` entry must carry these three keys under `Fields`:
|
|
13
|
+
|
|
14
|
+
| Key | Meaning |
|
|
15
|
+
|-----|---------|
|
|
16
|
+
| `ApexClassName` | Bare Apex identifier of the class that implements `Schedulable`. |
|
|
17
|
+
| `CronExpression` | Salesforce cron expression (6- or 7-field). |
|
|
18
|
+
| `JobName` | Human-readable job name. Appears in Setup > Scheduled Jobs; equals `CronTrigger.CronJobDetail.Name` post-schedule. |
|
|
19
|
+
|
|
20
|
+
If any of the three is missing, mark the entry `API_NOT_IDENTIFIED` and
|
|
21
|
+
skip. Never fabricate one from the others.
|
|
22
|
+
|
|
23
|
+
## Prerequisites
|
|
24
|
+
|
|
25
|
+
The Apex `Schedulable` class named by `ApexClassName` must already exist
|
|
26
|
+
on the target sandbox. Sandbox refresh copies class metadata from source
|
|
27
|
+
to target, so this is normally satisfied on a fresh refresh — the reason
|
|
28
|
+
scheduled *jobs* need re-creation is that `CronTrigger` records (runtime
|
|
29
|
+
state) don't copy, not that the classes don't.
|
|
30
|
+
|
|
31
|
+
If the class is genuinely missing, F-3 (`sf apex run`) fails at compile
|
|
32
|
+
with `Variable does not exist: <ApexClassName>` — the outcome becomes
|
|
33
|
+
`FAILED` and the customer follows the Cross-Skill Integration row on
|
|
34
|
+
`platform-metadata-deploy` to deploy the class before re-running this
|
|
35
|
+
skill.
|
|
36
|
+
|
|
37
|
+
## F-1 — Pre-flight AMBIGUOUS check
|
|
38
|
+
|
|
39
|
+
`System.schedule` throws `AlreadyScheduledException` if a job with the
|
|
40
|
+
same name already exists. Query first to avoid that path — and per skill
|
|
41
|
+
policy, do NOT auto-abort an existing job; surface it as `AMBIGUOUS` so
|
|
42
|
+
the customer decides.
|
|
43
|
+
|
|
44
|
+
`JobName` is human-readable and may legitimately contain an apostrophe
|
|
45
|
+
(e.g. `Owner's nightly job`). Never inline it into the SOQL literal
|
|
46
|
+
directly — escape it via the deterministic helper first, which also
|
|
47
|
+
rejects the same characters `scripts/build-scheduled-apex.mjs` rejects
|
|
48
|
+
(newlines/backslashes), so an input that passes F-1 will also pass F-2:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
JOB_NAME_LITERAL=$(node scripts/soql-escape-job-name.mjs "<Fields.JobName>") || {
|
|
52
|
+
# Non-zero exit → outcome FAILED with stderr in Follow-ups; skip F-2/F-3/F-4.
|
|
53
|
+
exit 1
|
|
54
|
+
}
|
|
55
|
+
SOQL="SELECT Id FROM CronTrigger WHERE CronJobDetail.Name = '$JOB_NAME_LITERAL'"
|
|
56
|
+
sf data query --json --query "$SOQL" > /tmp/entry-<Slug>-cron.json
|
|
57
|
+
COUNT=$(jq -r '.result.totalSize' /tmp/entry-<Slug>-cron.json)
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Outcomes:
|
|
61
|
+
|
|
62
|
+
- `COUNT == 0` → proceed to F-2.
|
|
63
|
+
- `COUNT >= 1` → outcome `AMBIGUOUS`. Add a Follow-ups bullet listing
|
|
64
|
+
the entry's `Fields.JobName` and every returned `CronTrigger.Id`.
|
|
65
|
+
Do not attempt to schedule.
|
|
66
|
+
|
|
67
|
+
In dry-run, still run F-1 — it is a read.
|
|
68
|
+
|
|
69
|
+
## F-2 — Build the anonymous Apex snippet
|
|
70
|
+
|
|
71
|
+
Never author the snippet from memory — Apex string escaping is a common
|
|
72
|
+
foot-gun. Use the deterministic builder:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
node scripts/build-scheduled-apex.mjs \
|
|
76
|
+
"<Fields.JobName>" \
|
|
77
|
+
"<Fields.CronExpression>" \
|
|
78
|
+
"<Fields.ApexClassName>" \
|
|
79
|
+
assets/scheduled_apex_template.apex \
|
|
80
|
+
> /tmp/entry-<Slug>.apex
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
The script:
|
|
84
|
+
|
|
85
|
+
- Verifies `CronExpression` is 6- or 7-field whitespace-separated.
|
|
86
|
+
- Verifies `ApexClassName` matches `^[A-Za-z_][A-Za-z0-9_]*$`.
|
|
87
|
+
- Rejects newlines and backslashes in any input (would either break the
|
|
88
|
+
string literal or enable Apex injection).
|
|
89
|
+
- Single-quote-escapes `JobName` and `CronExpression` for embedding in
|
|
90
|
+
the Apex single-quoted string.
|
|
91
|
+
- Writes the substituted `.apex` snippet to stdout.
|
|
92
|
+
|
|
93
|
+
Non-zero exit → outcome `FAILED` with stderr in Follow-ups.
|
|
94
|
+
|
|
95
|
+
## F-3 — Execute the snippet
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
sf apex run --file /tmp/entry-<Slug>.apex --json \
|
|
99
|
+
> /tmp/entry-<Slug>-apex.json
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Classify from the JSON response:
|
|
103
|
+
|
|
104
|
+
| `.result.compiled` | `.result.success` | Outcome | Follow-ups body |
|
|
105
|
+
|--------------------|-------------------|---------|-----------------|
|
|
106
|
+
| `false` | (any) | `FAILED` | `.result.compileProblem`, `.result.line`, `.result.column` |
|
|
107
|
+
| `true` | `false` | `FAILED` | `.result.exceptionMessage`, `.result.exceptionStackTrace` |
|
|
108
|
+
| `true` | `true` | proceed to F-4 | — |
|
|
109
|
+
|
|
110
|
+
Skip F-3 entirely in dry-run and record `DRY_RUN`.
|
|
111
|
+
|
|
112
|
+
## F-4 — Verify the job scheduled
|
|
113
|
+
|
|
114
|
+
Reuse `$JOB_NAME_LITERAL` from F-1 — it is the same JobName, already
|
|
115
|
+
SOQL-escaped and validated:
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
SOQL="SELECT Id, State, NextFireTime, CronExpression FROM CronTrigger WHERE CronJobDetail.Name = '$JOB_NAME_LITERAL'"
|
|
119
|
+
sf data query --json --query "$SOQL" > /tmp/entry-<Slug>-verify.json
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Outcomes:
|
|
123
|
+
|
|
124
|
+
- 0 rows → `FAILED_VERIFY` (apex reported success but no CronTrigger
|
|
125
|
+
materialized — usually a permission or platform limits issue).
|
|
126
|
+
- ≥1 row where `.CronExpression == '<Fields.CronExpression>'` → `SUCCESS`.
|
|
127
|
+
- ≥1 row with a different `CronExpression` → `FAILED_VERIFY` (something
|
|
128
|
+
else claimed the name after F-1 saw it clear).
|
|
129
|
+
|
|
130
|
+
Skip F-4 entirely in dry-run (nothing to verify).
|
|
131
|
+
|
|
132
|
+
## Summary shape for a Step F entry
|
|
133
|
+
|
|
134
|
+
Reuse the phase table from the canonical output shape:
|
|
135
|
+
|
|
136
|
+
- `Sobject` column reads `CronTrigger`.
|
|
137
|
+
- `Describe` column reads `—` (no describe probe on this route).
|
|
138
|
+
- "Planned request" / "Request executed" is the anonymous Apex snippet
|
|
139
|
+
path (`/tmp/entry-<Slug>.apex`) and its contents inline, not a PATCH
|
|
140
|
+
path/body.
|
|
141
|
+
- `HTTP` column reads `—` (no direct HTTP status; `sf apex run` bundles
|
|
142
|
+
compile+execute into one response).
|
|
143
|
+
|
|
144
|
+
## Dry-run recap
|
|
145
|
+
|
|
146
|
+
| Sub-step | Dry-run behavior |
|
|
147
|
+
|----------|------------------|
|
|
148
|
+
| F-1 SOQL pre-flight | run (read) |
|
|
149
|
+
| F-2 snippet build (writes to `/tmp` only) | run |
|
|
150
|
+
| F-3 `sf apex run` | SKIP → record `DRY_RUN` |
|
|
151
|
+
| F-4 verify SOQL | SKIP (nothing to verify) |
|
|
152
|
+
|
|
153
|
+
The "Planned request" printed in the dry-run summary for a Step F entry
|
|
154
|
+
is the full contents of the built `.apex` snippet, so the customer can
|
|
155
|
+
audit exactly what would have executed.
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Deterministic Step F-2 helper: render assets/scheduled_apex_template.apex
|
|
3
|
+
// with a config entry's JobName / CronExpression / ApexClassName. Emits
|
|
4
|
+
// the substituted snippet to stdout; non-zero exit on any validation
|
|
5
|
+
// failure with a single-line human-readable reason on stderr.
|
|
6
|
+
//
|
|
7
|
+
// Kept out of SKILL.md prose per authoring standard A9 — string escaping
|
|
8
|
+
// and cron/identifier validation are deterministic and belong in scripts/.
|
|
9
|
+
|
|
10
|
+
import { readFileSync } from 'node:fs';
|
|
11
|
+
|
|
12
|
+
const [, , jobName, cronExpression, apexClassName, templatePath] = process.argv;
|
|
13
|
+
|
|
14
|
+
if (!jobName || !cronExpression || !apexClassName || !templatePath) {
|
|
15
|
+
process.stderr.write(
|
|
16
|
+
'usage: node build-scheduled-apex.mjs <JobName> <CronExpression> <ApexClassName> <template-path>\n'
|
|
17
|
+
);
|
|
18
|
+
process.exit(2);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Reject characters that break out of the Apex single-quoted string
|
|
22
|
+
// literal we substitute into. Newlines are a compile error; backslash
|
|
23
|
+
// is not a general escape in Apex string literals and lets a crafted
|
|
24
|
+
// value inject arbitrary Apex.
|
|
25
|
+
const forbidden = /[\r\n\\]/;
|
|
26
|
+
for (const [name, value] of [
|
|
27
|
+
['JobName', jobName],
|
|
28
|
+
['CronExpression', cronExpression],
|
|
29
|
+
['ApexClassName', apexClassName],
|
|
30
|
+
]) {
|
|
31
|
+
if (forbidden.test(value)) {
|
|
32
|
+
process.stderr.write(
|
|
33
|
+
`ERROR: ${name} contains a newline or backslash: ${JSON.stringify(value)}\n`
|
|
34
|
+
);
|
|
35
|
+
process.exit(2);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const cronFields = cronExpression.trim().split(/\s+/).length;
|
|
40
|
+
if (cronFields !== 6 && cronFields !== 7) {
|
|
41
|
+
process.stderr.write(
|
|
42
|
+
`ERROR: CronExpression must be 6 or 7 whitespace-separated fields; got ${cronFields}: ${JSON.stringify(cronExpression)}\n`
|
|
43
|
+
);
|
|
44
|
+
process.exit(2);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(apexClassName)) {
|
|
48
|
+
process.stderr.write(
|
|
49
|
+
`ERROR: ApexClassName must be a bare Apex identifier (letters, digits, underscore, not starting with a digit): ${JSON.stringify(apexClassName)}\n`
|
|
50
|
+
);
|
|
51
|
+
process.exit(2);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const escapeApexSingleQuoted = (s) => s.replaceAll("'", "\\'");
|
|
55
|
+
|
|
56
|
+
const template = readFileSync(templatePath, 'utf8');
|
|
57
|
+
const rendered = template
|
|
58
|
+
.replaceAll('{{JOB_NAME}}', escapeApexSingleQuoted(jobName))
|
|
59
|
+
.replaceAll('{{CRON_EXPRESSION}}', escapeApexSingleQuoted(cronExpression))
|
|
60
|
+
.replaceAll('{{APEX_CLASS_NAME}}', apexClassName);
|
|
61
|
+
|
|
62
|
+
process.stdout.write(rendered);
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Deterministic JobName → SOQL-safe literal helper for Step F-1 / F-4
|
|
3
|
+
// pre-flight and verify SOQL. A human-readable JobName may legitimately
|
|
4
|
+
// contain characters (e.g. an apostrophe in "Owner's nightly job") that
|
|
5
|
+
// break out of a raw single-quoted SOQL literal. This helper rejects
|
|
6
|
+
// characters that would also break the F-2 Apex snippet (kept in
|
|
7
|
+
// lockstep with build-scheduled-apex.mjs) and escapes single quotes for
|
|
8
|
+
// SOQL, so both the pre-flight query and the anonymous Apex snippet
|
|
9
|
+
// operate on the same validated JobName.
|
|
10
|
+
//
|
|
11
|
+
// Exits 0 with the escaped literal on stdout, or non-zero with a
|
|
12
|
+
// single-line reason on stderr.
|
|
13
|
+
|
|
14
|
+
const [, , jobName] = process.argv;
|
|
15
|
+
|
|
16
|
+
if (jobName === undefined) {
|
|
17
|
+
process.stderr.write('usage: node soql-escape-job-name.mjs <JobName>\n');
|
|
18
|
+
process.exit(2);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (/[\r\n\\]/.test(jobName)) {
|
|
22
|
+
process.stderr.write(
|
|
23
|
+
`ERROR: JobName contains a newline or backslash: ${JSON.stringify(jobName)}\n`
|
|
24
|
+
);
|
|
25
|
+
process.exit(2);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
process.stdout.write(jobName.replaceAll("'", "\\'"));
|