feat(automations): "Run a script" action + JSON result variables (#3653)#3746
Conversation
Adds the deferred RunScript capability — the last major Automation Engine gap.
- action.runScript: runs a script file from $DATA_DIR/scripts via the existing
scriptRunner (traversal-protected, timeout, interpreter/deps handling). The
trigger context is passed as MM_* env vars (+ MESSAGE/FROM_NODE… aliases so
legacy scripts work). On non-zero exit the action throws (action:error). Wired
as an injectable ActionDeps.runScript; the simulator stubs it so dry-runs
never spawn a process.
- New 'json' variable type (codec: JSON.stringify/parse). A runScript action can
store the script's parsed JSON output into a result variable.
- Nested variable access: {{ var.name.a.b }} traverses a JSON variable's value
(shared resolveVarValue helper used by interpolation AND condition.variable);
a whole object renders as JSON. Variable names are now validated as dot-free
identifiers (client + server) so the name/path split is unambiguous.
- Builder: "Run a script" action with a script dropdown (new scriptselect kind,
populated from GET /api/scripts), optional result variable + timeout; Variables
UI gains the json type; substitutions help documents nested access.
Tests: triggerEnv mapping, runScript dep call + result-variable write + failure
throw + scriptPath required; json codec round-trip; nested {{ var.x.y.z }}
interpolation + nested condition.variable; simulator records runScript without
spawning. Full suite green (7544).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VBLhGGNh35oMwTL53va1Y5
|
Claude finished @Yeraze's task —— View job PR Review: feat(automations): "Run a script" action + JSON result variablesTasks:
SummaryThis is a well-structured PR that completes the Automation Engine's "Run a script" action. The implementation is clean, well-tested, and follows existing project conventions. Below are observations across the review areas. Code Quality & Best PracticesStrengths:
Minor Issues:
Potential Bugs
Performance Considerations
Security ConsiderationsThe security story here is solid:
One observation:
Test CoverageCoverage is good overall:
Gaps worth considering:
SummaryThis is a well-implemented feature. The core concerns are:
None of the above are blockers — the PR is in good shape to merge once the author has reviewed these points. |
Adds the deferred RunScript capability — the last major Automation Engine gap (plan §1/§8).
What it does
A new "Run a script" action runs a script file from the server's
$DATA_DIR/scriptsfolder when an automation fires, passing the trigger context asMM_*env vars. The script's JSON output can be captured into a variable, and any later step can index into it with{{ var.name.a.b }}.Backend
action.runScriptreuses the existingscriptRunner(resolveScriptPathtraversal protection, interpreter pick, deps dirs, timeout). Env =triggerEnv(ctx):MM_TRIGGER_TYPE/MM_SOURCE_ID/MM_NODE_NUM/MM_TIMESTAMP+ each trigger field asMM_<UPPER_SNAKE>(objects JSON-stringified) + message-compat aliases (MESSAGE,FROM_NODE,…). Non-zero exit → throws → recordedaction:error. InjectableActionDeps.runScript; the simulator stubs it so dry-runs never spawn a process.jsonvariable type (codec:JSON.stringify/JSON.parse). The action storesreturnValueinto the optional result variable.{{ var.name.a.b }}via a sharedresolveVarValuehelper used by both text interpolation andcondition.variable; a whole object renders as JSON. Variable names are validated as dot-free identifiers (client + server) so the name/path split is unambiguous.Frontend
scriptselectkind, populated fromGET /api/scripts), optional result variable + timeout. Variables UI gains the json type; the substitutions help documents{{ var.name.field }}.Decisions (per discussion)
{{ var.x.y.z }}(no{{ script.* }}token; the script doesn't send messages — a separate Send-a-message action does).Tests
triggerEnvmapping; runScript dep call + result-variable write + failure-throws + scriptPath-required; json codec round-trip + malformed→null; nested{{ var.x.y.z }}interpolation + whole-object-as-JSON + nestedcondition.variable; simulator records runScript without spawning. Full suite 7544 passed, 0 failures; tsc + build clean.🤖 Generated with Claude Code