计划执行与检查节点
executing-plans
按既定实施计划完成工作,保持过程检查和反馈。
- Global scope (use inside pipes)\n\nSee **[`references/expressions.md`](references/expressions.md)** for the full expression reference — patterns, string/array/object operations, type conversions, and utilities.\n\n## IMPORTANT: Expression Complexity Limits\n\nExpressions have a complexity limit. If you get \"Expression contains too many elements\" error, break complex object construction into intermediate `Void` steps:\n\nToo complex — avoid this:\n\n```json\n\"output\": {\n \"counts\": \"$({\\\"a\\\": .x, \\\"b\\\": .y, \\\"c\\\": .z, \\\"d\\\": .w, \\\"e\\\": .v, ...})\"\n}\n```\n\nUse a `Void` step instead:\n\n```json\n\"executors\": [\n {\n \"execute\": \"utils-sapcp:Void:1\",\n \"alias\": \"buildCounts\",\n \"input\": {\n \"message\": \"{\\\"a\\\": $(.x), \\\"b\\\": $(.y), \\\"c\\\": $(.z)}\"\n }\n }\n],\n\"output\": {\n \"counts\": \"$(.buildCounts.output.message | toObject)\"\n}\n```\n\n**Rule of thumb**: If building an object with more than 5-6 fields from different step outputs, use a `Void` step.\n\n## Conditional Execution (when)\n\nSkip steps based on conditions:\n\n```json\n\"when\": {\n \"semantic\": \"OR\",\n \"conditions\": [\n {\n \"semantic\": \"OR\",\n \"cases\": [\n {\n \"expression\": \"$(.execution.input.skipStep)\",\n \"operator\": \"EQUALS\",\n \"semantic\": \"OR\",\n \"values\": [\"false\"]\n }\n ]\n }\n ]\n}\n```\n\nOperators: `EQUALS`, `NOT_EQUALS`, `CONTAINS`, `NOT_CONTAINS`, `STARTS_WITH`, `ENDS_WITH`\n\n## Validation\n\nAssert conditions on step output:\n\n```json\n\"validate\": {\n \"semantic\": \"OR\",\n \"conditions\": [\n {\n \"semantic\": \"OR\",\n \"cases\": [\n {\n \"expression\": \"$(.step.output.status)\",\n \"operator\": \"EQUALS\",\n \"semantic\": \"OR\",\n \"values\": [\"200\", \"201\"]\n }\n ]\n }\n ]\n}\n```\n\n## Auto Retry\n\nRetry on transient failures. Valid `logic` values: `FIXED` (constant delay) or `INCREMENTAL` (increasing delay).\n\n```json\n\"autoRetry\": {\n \"maxCount\": 3,\n \"delay\": \"5s\",\n \"logic\": \"FIXED\",\n \"applyOnValidation\": false,\n \"when\": {\n \"semantic\": \"OR\",\n \"conditions\": [\n {\n \"semantic\": \"OR\",\n \"cases\": [\n {\n \"expression\": \"$([408, 429, 500, 502, 503, 504, -1] | filter(. == $.step.output.status) | length)\",\n \"operator\": \"EQUALS\",\n \"semantic\": \"OR\",\n \"values\": [\"1\"]\n }\n ]\n }\n ]\n }\n}\n```\n\n## Repeat (Polling)\n\nPoll until condition is met:\n\n```json\n\"repeat\": {\n \"maxCount\": 100,\n \"delay\": \"15s\",\n \"failOnMaxCount\": true,\n \"until\": {\n \"semantic\": \"OR\",\n \"conditions\": [\n {\n \"semantic\": \"OR\",\n \"cases\": [\n {\n \"expression\": \"$(.step.output.body | toObject.state)\",\n \"operator\": \"EQUALS\",\n \"semantic\": \"OR\",\n \"values\": [\"COMPLETE\"]\n }\n ]\n }\n ]\n }\n}\n```\n\n## Error Messages\n\nProvide custom error messages:\n\n```json\n\"errorMessages\": [\n {\n \"message\": \"Operation failed: $(.step.output.body | toObject.error)\",\n \"when\": {\n \"semantic\": \"OR\",\n \"conditions\": [\n {\n \"semantic\": \"OR\",\n \"cases\": [\n {\n \"expression\": \"$(.step.output.status)\",\n \"operator\": \"NOT_EQUALS\",\n \"semantic\": \"OR\",\n \"values\": [\"200\"]\n }\n ]\n }\n ]\n }\n }\n]\n```\n\n## Development Workflow\n\n1. **Identify requirements** - Inputs, outputs, steps needed\n2. **Find reference commands** - Use catalog-explorer skill or check `references/catalogs.md`\n3. **Design the flow** - Map out executors and data transformations\n4. **Write expressions** - Use jq syntax for data manipulation\n5. **Add error handling** - validate, autoRetry, errorMessages\n6. **Test incrementally** - Verify each step works (use `feature:dryRun` tag)\n\n## Mandatory Description Patterns\n\n⚠️ **When creating commands, use these exact description patterns for standard parameters.** No variations or creative rewording allowed!\n\nSee the complete authoritative list in **[`references/description-patterns.md`](references/description-patterns.md)**.\n\n## Examples\n\n### Example 1: HTTP health check command\n\nUser: \"Create a command that checks if an API endpoint is healthy\"\n\n1. Create a composite command with one `http-sapcp:HttpRequest:1` executor\n2. Add `url` and `expectedStatus` as input keys\n3. Add `validate` to assert response status matches expected\n4. Add `autoRetry` for transient failures (429, 500, 502, 503, 504)\n5. Include `dryRun` with a mock 200 response\n6. Use `\u003c\u003c\u003cTENANT_ID>>>` suffix for the catalog\n\n### Example 2: Batch processing with ForEach\n\nUser: \"Build a command that processes a list of items in parallel batches of 5\"\n\n1. Create a main composite command with an `items` input key (array type)\n2. Create a sub-command for processing a single item\n3. Use `utils-sapcp:ForEach:2` (never `ForEach:1`) with `batchSize: \"5\"`\n4. Pass items via the `inputs` parameter\n5. Include `dryRun` on both the ForEach executor and the sub-command executors\n\n### Example 3: Trigger-and-poll workflow\n\nUser: \"Create a command that starts an async operation and polls until it completes\"\n\n1. Create a composite command with a trigger step (`HttpRequest` PATCH/POST)\n2. Add a polling step with `repeat` configuration\n3. Set `repeat.until` to check for completion states (e.g., `succeeded`, `failed`)\n4. Set `repeat.maxCount` and `repeat.delay` for timeout protection\n5. Set `failOnMaxCount: true` so the command fails if polling exceeds the limit\n6. Extract operation ID from the trigger response and pass to the poll URL\n\n---\n\n## Troubleshooting\n\n**Error:** \"Expression contains too many elements\"\n**Cause:** A single expression builds an object with too many fields (>5-6 from different step outputs).\n**Solution:** Break into intermediate `utils-sapcp:Void:1` steps to build partial objects, then combine.\n\n**Error:** Command references `ForEach:1`\n**Cause:** Using the deprecated version.\n**Solution:** Always use `utils-sapcp:ForEach:2`. Version 1 is deprecated and must never be used.\n\n**Error:** Catalog suffix uses `-sapcp` for a generated command\n**Cause:** Applying the wrong suffix convention.\n**Solution:** Generated commands use `\u003c\u003c\u003cTENANT_ID>>>` suffix. Only SAP-provided built-in commands use `-sapcp`.\n\n**Error:** Missing `dryRun` field on an executor\n**Cause:** Executor defined without a `dryRun` field.\n**Solution:** Always include `\"dryRun\": null` unless mock output propagation through a chain is needed.\n\n---\n\n## Additional Resources\n\n### Reference Files\n\nFor detailed patterns and complete expression reference:\n- **`references/expressions.md`** - Complete dynamic expressions guide\n- **`references/patterns.md`** - Common command patterns\n- **`references/catalogs.md`** - Available commands by catalog\n- **`references/supported-data-manipulation-expressions.pdf`** - Official SAP documentation\n\n### Example Files\n\nWorking command examples in `examples/` (note PascalCase naming):\n- **`CreateResourceWithServiceKey.command.json`** - POST with service key / clientCert auth, autoRetry, errorMessages, and conditional initialDelay\n- **`GetResourceWithRetry.command.json`** - HTTP GET with OAuth, retry, validate, and errorMessages\n- **`WaitForOperation.command.json`** - Polling pattern with repeat\n- **`ProcessAppsBatch.command.json`** - Batch processing with ForEach\n","licenseDocuments":[]}}
automation-pilot-command-generation【效率与协作】Generate SAP Automation Pilot commands with dynamic expressions, jq transformations, and composite workflows. Use when creating commands, building orchestration flows, or working with Automation Pilot expressions and script execution (Bash, Python, Node.js, PowerShell).
独立 GitHub 快照 · 自动分类 · 以原作者文档为准
This skill provides guidance for creating SAP Automation Pilot commands by composing existing reference commands from the content library.
Deployed commands start in DRAFT state. Do not release automatically — only release when:
Draft state allows safe iteration without affecting production.
SAP Automation Pilot commands are JSON definitions that automate operations on SAP BTP. Commands can be:
HttpRequest, SendEmail)examples/ — Pattern examples (HTTP, ForEach, polling workflows)references/patterns.md — Common implementation patternsreferences/expressions.md — Expression language referenceUse the catalog-explorer skill to discover available executors via API:
# List available catalogs
curl -s -u "$AUTOPI_USERNAME:$AUTOPI_PASSWORD" \
"https://$AUTOPI_HOSTNAME/api/v1/catalogs?own=false"
# Find commands in a catalog
curl -s -u "$AUTOPI_USERNAME:$AUTOPI_PASSWORD" \
"https://$AUTOPI_HOSTNAME/api/v1/commands?catalog=applm-sapcp"
# Get full command definition (inputs, outputs, executors)
curl -s -u "$AUTOPI_USERNAME:$AUTOPI_PASSWORD" \
"https://$AUTOPI_HOSTNAME/api/v1/commands/applm-sapcp:RestartCfApp:1"
This ensures you always use valid executor names and correct parameters, even for newly added catalogs.
dryRun is always present on every executor. Set it to null unless you have a specific reason to provide mock output.
"dryRun": null
The dryRun: {output: {...}} form (with mock values) is only needed if a downstream executor references this executor's output AND you want dry-run mode to propagate realistic values through the chain. In practice, null is the standard.
See references/catalogs.md for the complete catalog reference. Key rules:
<<<TENANT_ID>>> suffix (e.g., mycommands-<<<TENANT_ID>>>:MyCommand:1)sapcp suffix (e.g., http-sapcp:HttpRequest:1)ForEach:2, never ForEach:1 (deprecated)| Element | Convention | Example |
|---|---|---|
| Command name | PascalCase | RestartCfApp, GetHanaInstance |
| Input name | PascalCase | BtpCredentials, JiraConfig |
| Input/output keys | camelCase | resourceName, subAccount |
| Aliases | camelCase | getResource, createInstance |
| Catalog ID | kebab-case | mycommands-xxx |
Names must not contain spaces (causes API issues). PascalCase matches SAP's built-in commands.
{
"id": "mycommands-<<<TENANT_ID>>>:CommandName:1",
"catalog": "mycommands-<<<TENANT_ID>>>",
"name": "CommandName",
"description": "What the command does",
"version": 1,
"inputKeys": { },
"outputKeys": { },
"configuration": null,
"tags": { }
}
Define command parameters:
"inputKeys": {
"paramName": {
"type": "string", // string, number, boolean, array, object
"description": "Description",
"required": true,
"sensitive": false, // true masks in logs
"defaultValue": "value",
"minValue": 1, // for numbers
"maxValue": 100
}
}
Additional optional fields exist: allowedValues (fixed set of valid options), suggestedValues (hints shown in UI), allowedValuesFromInputKeys and suggestedValuesFromInputKeys (dynamic lists from an input reference). Omit them when not needed.
defaultValue is always a JSON string on the wire regardless of the declared type. For non-string types, stringify the JSON: "defaultValue": "5" for a number, "defaultValue": "true" for a boolean, "defaultValue": "[\"a\",\"b\"]" for an array, "defaultValue": "{\"key\":\"val\"}" for an object.
Region inputs must always use allowedValuesFromInputKeys to constrain the value to the SAP-provided region list:
"allowedValuesFromInputKeys": ["metadata-sapcp:CfRegionData:1"]"allowedValuesFromInputKeys": ["metadata-sapcp:NeoRegionData:1"]This ensures only valid Automation Pilot regions can be entered.
Define command outputs:
"outputKeys": {
"result": {
"type": "string",
"description": "The result",
"sensitive": false
}
}
Commands with configuration orchestrate multiple steps:
"configuration": {
"values": [], // Pre-computed values from input references
"output": {}, // Final output mapping
"executors": [], // Steps to execute
"listeners": [] // Event handlers
}
Load reusable credentials or metadata at execution time. Three forms of inputKey:
"values": [
{
"alias": "regionData",
"valueFrom": {
"inputReference": "metadata-sapcp:CfRegionData:1",
"inputKey": "$(.execution.input.region)"
}
},
{
"alias": "fullInputObject",
"valueFrom": {
"inputReference": "mycatalog-<<<TENANT_ID>>>:MyCredentials:1",
"inputKey": null
}
},
{
"alias": "ServiceAccountPassword",
"valueFrom": {
"inputReference": "mycatalog-<<<TENANT_ID>>>:MyCredentials:1",
"inputKey": "password"
}
}
]
"$(.execution.input.region)") — dynamically selects which key to load based on inputnull — loads the entire input reference as an object (access its fields via $(.alias.fieldName))"password") — loads a single named key directlyEach executor runs a command with mapped inputs.
{
"execute": "http-sapcp:HttpRequest:1",
"alias": "getResource",
"input": {
"url": "$(.regionData.cfApiUrl)/v3/apps",
"method": "GET",
"user": "$(.execution.input.user)",
"password": "$(.execution.input.password)",
"tokenUrl": "$(.regionData.uaaTokenUrl)",
"clientId": "cf",
"timeout": "20"
},
"description": null,
"progressMessage": null,
"initialDelay": null,
"pause": null,
"when": null,
"validate": null,
"autoRetry": null,
"repeat": null,
"errorMessages": [],
"dryRun": null
}
Expressions use jq 1.6 syntax wrapped in $(). Access data with:
.execution.input.key - Input values.stepAlias.output.key - Previous step outputs.regionData.field - Values from input references$ - Global scope (use inside pipes)See references/expressions.md for the full expression reference — patterns, string/array/object operations, type conversions, and utilities.
Expressions have a complexity limit. If you get "Expression contains too many elements" error, break complex object construction into intermediate Void steps:
Too complex — avoid this:
"output": {
"counts": "$({\"a\": .x, \"b\": .y, \"c\": .z, \"d\": .w, \"e\": .v, ...})"
}
Use a Void step instead:
"executors": [
{
"execute": "utils-sapcp:Void:1",
"alias": "buildCounts",
"input": {
"message": "{\"a\": $(.x), \"b\": $(.y), \"c\": $(.z)}"
}
}
],
"output": {
"counts": "$(.buildCounts.output.message | toObject)"
}
Rule of thumb: If building an object with more than 5-6 fields from different step outputs, use a Void step.
Skip steps based on conditions:
"when": {
"semantic": "OR",
"conditions": [
{
"semantic": "OR",
"cases": [
{
"expression": "$(.execution.input.skipStep)",
"operator": "EQUALS",
"semantic": "OR",
"values": ["false"]
}
]
}
]
}
Operators: EQUALS, NOT_EQUALS, CONTAINS, NOT_CONTAINS, STARTS_WITH, ENDS_WITH
Assert conditions on step output:
"validate": {
"semantic": "OR",
"conditions": [
{
"semantic": "OR",
"cases": [
{
"expression": "$(.step.output.status)",
"operator": "EQUALS",
"semantic": "OR",
"values": ["200", "201"]
}
]
}
]
}
Retry on transient failures. Valid logic values: FIXED (constant delay) or INCREMENTAL (increasing delay).
"autoRetry": {
"maxCount": 3,
"delay": "5s",
"logic": "FIXED",
"applyOnValidation": false,
"when": {
"semantic": "OR",
"conditions": [
{
"semantic": "OR",
"cases": [
{
"expression": "$([408, 429, 500, 502, 503, 504, -1] | filter(. == $.step.output.status) | length)",
"operator": "EQUALS",
"semantic": "OR",
"values": ["1"]
}
]
}
]
}
}
Poll until condition is met:
"repeat": {
"maxCount": 100,
"delay": "15s",
"failOnMaxCount": true,
"until": {
"semantic": "OR",
"conditions": [
{
"semantic": "OR",
"cases": [
{
"expression": "$(.step.output.body | toObject.state)",
"operator": "EQUALS",
"semantic": "OR",
"values": ["COMPLETE"]
}
]
}
]
}
}
Provide custom error messages:
"errorMessages": [
{
"message": "Operation failed: $(.step.output.body | toObject.error)",
"when": {
"semantic": "OR",
"conditions": [
{
"semantic": "OR",
"cases": [
{
"expression": "$(.step.output.status)",
"operator": "NOT_EQUALS",
"semantic": "OR",
"values": ["200"]
}
]
}
]
}
}
]
references/catalogs.mdfeature:dryRun tag)⚠️ When creating commands, use these exact description patterns for standard parameters. No variations or creative rewording allowed!
See the complete authoritative list in references/description-patterns.md.
User: "Create a command that checks if an API endpoint is healthy"
http-sapcp:HttpRequest:1 executorurl and expectedStatus as input keysvalidate to assert response status matches expectedautoRetry for transient failures (429, 500, 502, 503, 504)dryRun with a mock 200 response<<<TENANT_ID>>> suffix for the catalogUser: "Build a command that processes a list of items in parallel batches of 5"
items input key (array type)utils-sapcp:ForEach:2 (never ForEach:1) with batchSize: "5"inputs parameterdryRun on both the ForEach executor and the sub-command executorsUser: "Create a command that starts an async operation and polls until it completes"
HttpRequest PATCH/POST)repeat configurationrepeat.until to check for completion states (e.g., succeeded, failed)repeat.maxCount and repeat.delay for timeout protectionfailOnMaxCount: true so the command fails if polling exceeds the limitError: "Expression contains too many elements"
Cause: A single expression builds an object with too many fields (>5-6 from different step outputs).
Solution: Break into intermediate utils-sapcp:Void:1 steps to build partial objects, then combine.
Error: Command references ForEach:1
Cause: Using the deprecated version.
Solution: Always use utils-sapcp:ForEach:2. Version 1 is deprecated and must never be used.
Error: Catalog suffix uses -sapcp for a generated command
Cause: Applying the wrong suffix convention.
Solution: Generated commands use <<<TENANT_ID>>> suffix. Only SAP-provided built-in commands use -sapcp.
Error: Missing dryRun field on an executor
Cause: Executor defined without a dryRun field.
Solution: Always include "dryRun": null unless mock output propagation through a chain is needed.
For detailed patterns and complete expression reference:
references/expressions.md - Complete dynamic expressions guidereferences/patterns.md - Common command patternsreferences/catalogs.md - Available commands by catalogreferences/supported-data-manipulation-expressions.pdf - Official SAP documentationWorking command examples in examples/ (note PascalCase naming):
CreateResourceWithServiceKey.command.json - POST with service key / clientCert auth, autoRetry, errorMessages, and conditional initialDelayGetResourceWithRetry.command.json - HTTP GET with OAuth, retry, validate, and errorMessagesWaitForOperation.command.json - Polling pattern with repeatProcessAppsBatch.command.json - Batch processing with ForEach