| 29 | type_key = "command" |
| 30 | |
| 31 | def execute(self, config: dict[str, Any], context: StepContext) -> StepResult: |
| 32 | command = config.get("command", "") |
| 33 | input_data = config.get("input", {}) |
| 34 | |
| 35 | # Resolve expressions in input |
| 36 | resolved_input: dict[str, Any] = {} |
| 37 | for key, value in input_data.items(): |
| 38 | resolved_input[key] = evaluate_expression(value, context) |
| 39 | |
| 40 | # Resolve integration (step → workflow default → project default) |
| 41 | integration = config.get("integration") or context.default_integration |
| 42 | if integration and isinstance(integration, str) and "{{" in integration: |
| 43 | integration = evaluate_expression(integration, context) |
| 44 | |
| 45 | # Resolve model |
| 46 | model = config.get("model") or context.default_model |
| 47 | if model and isinstance(model, str) and "{{" in model: |
| 48 | model = evaluate_expression(model, context) |
| 49 | |
| 50 | # Merge options (workflow defaults ← step overrides) |
| 51 | options = dict(context.default_options) |
| 52 | step_options = config.get("options", {}) |
| 53 | if step_options: |
| 54 | options.update(step_options) |
| 55 | |
| 56 | # Attempt CLI dispatch |
| 57 | args_str = str(resolved_input.get("args", "")) |
| 58 | dispatch_result = self._try_dispatch( |
| 59 | command, integration, model, args_str, context |
| 60 | ) |
| 61 | |
| 62 | output: dict[str, Any] = { |
| 63 | "command": command, |
| 64 | "integration": integration, |
| 65 | "model": model, |
| 66 | "options": options, |
| 67 | "input": resolved_input, |
| 68 | } |
| 69 | |
| 70 | if dispatch_result is not None: |
| 71 | output["exit_code"] = dispatch_result["exit_code"] |
| 72 | output["stdout"] = dispatch_result["stdout"] |
| 73 | output["stderr"] = dispatch_result["stderr"] |
| 74 | output["dispatched"] = True |
| 75 | if dispatch_result["exit_code"] != 0: |
| 76 | return StepResult( |
| 77 | status=StepStatus.FAILED, |
| 78 | output=output, |
| 79 | error=dispatch_result["stderr"] or f"Command exited with code {dispatch_result['exit_code']}", |
| 80 | ) |
| 81 | return StepResult( |
| 82 | status=StepStatus.COMPLETED, |
| 83 | output=output, |
| 84 | ) |
| 85 | else: |
| 86 | output["exit_code"] = 1 |
| 87 | output["dispatched"] = False |
| 88 | return StepResult( |