feat: add a shell node and ShellRunner capability - #28
Conversation
A `shell` node runs a shell script as a workflow step — inline via
`config.source`, or from a file via `config.script_path` — with an
optional `interpreter` (sh/bash), `cwd`, and `env`. A non-zero exit
fails the step with a tail of stderr quoted in the message; a successful
run emits one item of { exit_code, stdout, stderr, stdout_json }.
Shell execution is a new `ShellRunner` capability rather than another
`CodeLanguage`: a shell step needs a working directory, an environment,
and the process's exit status and both streams, none of which the code
capability's (language, source, input) -> Value shape can carry.
The engine stays host-agnostic in the strongest sense here. It never
resolves a script path, chooses an environment, or spawns anything: it
parses a node's config into a validated ShellRequest and hands it over.
Path and cwd strings are documented as untrusted authoring input the
host must validate. `Capabilities::shell` is optional, so a host that
does not want workflows running shell scripts leaves it `None` and the
node fails with a capability error naming what is missing.
Co-authored-by: Medulla <medulla@tinyhumans.ai>
There was a problem hiding this comment.
senamakel has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.
|
Warning Review limit reachedYou’ve reached a temporary PR review limit under our Fair Usage Limits Policy. Next review available in: 30 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (14)
Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 52d35f5398
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| #[async_trait] | ||
| impl NodeExecutor for ShellNode { | ||
| async fn execute(&self, ctx: NodeContext<'_>) -> Result<NodeOutput> { | ||
| let config = &ctx.node.config; |
There was a problem hiding this comment.
Resolve shell configuration expressions before dispatch
When source, script_path, cwd, or an env value is an = expression such as =item.script, this reads the raw node config and forwards the expression text literally to the runner, unlike the established config-binding behavior. Such workflows therefore execute the wrong script or pass the wrong environment/path. Resolve the config through resolve_config_traced before parsing it, and attach the resulting diagnostics to the output.
Useful? React with 👍 / 👎.
|
Context update: the downstream Medulla work for tinyhumansai/medulla#91 landed as tinyhumansai/medulla#147, which is standalone and needs nothing from here — it extends Medulla's existing This PR is therefore optional rather than blocking. It is worth taking on its own terms if a first-class |
What
Adds a
shellnode kind and theShellRunnercapability behind it, so aworkflow can run a shell script as a step.
A
shellnode runs a script inline (config.source) or from a file(
config.script_path), with an optionalinterpreter(sh/bash),cwd,and
env. The node's input items are handed to the script as a JSON file namedby its first argument.
the message.
{ exit_code, stdout, stderr, stdout_json }—stdout_jsonis the parsedstandard output when the script printed JSON,
nullotherwise. Both a scriptthat pipes text and one that emits JSON are ordinary uses, so neither is made
to look like the exception.
Why a new capability instead of another
CodeLanguageCodeRunner's(language, source, input) -> Valueshape cannot carry a workingdirectory, an environment, an exit status, or two output streams. Folding shell
execution into it would have meant smuggling all four through the
inputvalue.ShellRunnertakes aShellRequestand returns aShellOutcome, so anon-zero exit is reported rather than raised — which keeps "the script ran and
failed" distinguishable from "the host refused to run it" in a run record.
Host-agnostic by construction
The engine never resolves a script path, chooses an environment, or spawns
anything. It parses a node's config into a validated
ShellRequestand hands itover;
ShellScript::PathandShellRequest.cwdare documented as untrustedauthoring input the host must validate.
Capabilities::shellisOption, following theagentprecedent: a host thatdoes not want workflows running shell scripts leaves it
Noneand the nodefails with a capability error naming what is missing.
Breaking change
Capabilitiesgained ashellfield. Hosts constructing the struct literallyadd
shell: None(or their own runner). Noted in the changelog.Example
{ "id": "build", "kind": "shell", "name": "Build", "config": { "interpreter": "bash", "cwd": "checkout", "env": { "PROFILE": "release" }, "source": "set -euo pipefail\ncargo build --profile \"$PROFILE\"\nprintf '{\"built\":true}'" } }Validation
Downstream of tinyhumansai/medulla#91, which needs a shell step in Medulla
workflows.