Skip to content

feat(plugins): add builder action nodes for common script patterns - #2364

Draft
joelorzet wants to merge 2 commits into
stagingfrom
feat/KEEP-1197-builder-action-nodes
Draft

feat(plugins): add builder action nodes for common script patterns#2364
joelorzet wants to merge 2 commits into
stagingfrom
feat/KEEP-1197-builder-action-nodes

Conversation

@joelorzet

Copy link
Copy Markdown
Contributor

Adds a data plugin and two math actions covering data-shaping work that today gets copy-pasted into Code nodes, plus retry support on the HTTP Request node.

Data plugin (new)

Category "Data", no credentials, egress: "none".

data/encode "Encode / Decode"

Text to padded hex or base64 and back. Kept format-agnostic rather than bytes32-only.

Config Type Notes
operation select encode (default) / decode
value template-textarea a single string, or a JSON array of strings
format select bytes32 / bytes16 / bytes8 / hex (unpadded) / base64
padding select right (default, Solidity string) / left; fixed sizes only

Outputs result (string for one input, array for an array), map keyed by the original value, count, operation, format, error. Overflow fails the step rather than truncating, and decode strips padding from the named side so bytes32 round-trips.

data/extract-fields "Extract Fields"

Config Type Notes
source template-textarea one upstream output, or a JSON object combining several
paths template-textarea one per line, name = path or a bare path
mode select object (default) / array
onMissing select null (default) / empty / fail

Outputs fields, items, missing, foundCount, itemCount, error. Numeric segments index arrays (rows.1.amount). This removes the need to reach into a nested path inside a template ref on an opaque node output, which cannot be validated before the run.

data/flatten-findings "Flatten Findings"

Config Type Notes
sources template-textarea JSON array of {label, value, severity?, itemsPath?}
defaultSeverity template-input default warning
hashField template-input default transactionHash
maxFindings number default 100

value accepts an object carrying events / transactions / logs / rows / items / results, a bare array, a boolean, or any other object. Outputs anyFound, count (true total, even when the list is capped), findings[], labels, firstHash, summary, truncated, error.

data/static-config "Static Config"

Single value field on the Monaco JSON editor. Outputs result, keys, count, valueType, error.

Math plugin (two new actions)

math/compare-tolerance "Compare With Tolerance"

Config: actual, expected, mode (percent default / absolute), tolerance, precision (default 6).

All arithmetic is BigInt fixed-point via a new plugins/math/steps/decimal-core.ts. The percent check is |diff| * 100 * 10^td <= tol * |expected|, so nothing divides first and RAD or WAD magnitude values compare exactly. Outputs withinTolerance, breached, direction, difference, absoluteDifference, percentDifference (null when expected is zero), and the normalised actual / expected / tolerance / mode.

math/format-number "Format Number"

Config: value, decimals (0 default, 18 for wei), notation (compact / plain), precision (2), unit. Outputs formatted, value (full unrounded decimal string), magnitude, notation, error.

HTTP Request retry

Added to the existing system node rather than shipping a second HTTP node, which would have duplicated the SSRF guard, timeout and failOnError semantics.

Config Notes
retryAttempts extra attempts after the first, 0 to 5, default 0
retryDelay base seconds, 0 to 30, default 1, linear backoff (attempt N waits delay times N). Only rendered when attempts > 0

Retries connection errors, timeouts and 408/425/429/500/502/503/504. A blocked URL, a malformed URL and other 4xx are never retried. perform.ts is restructured around an AttemptOutcome union so the loop separates transient from fatal without re-parsing error strings; failOnError still applies to the final outcome. Documented in the MCP schema so the agent path sees it.

Registration

Plugin registration is fully generated. pnpm discover-plugins updated plugins/index.ts, lib/types/integration.ts, plugins/plugin-allowlist.json and the gitignored registries. Both plugins appear in the action palette, canvas, config panel, template autocomplete and MCP schemas with no hand-edited registry.

Docs

New docs/plugins/data.md, two new sections in docs/plugins/math.md, plus the overview row and the _meta.ts nav entry.

Verification

  • 614 unit test files pass, 22,174 tests, no failures
  • pnpm type-check clean
  • pnpm check clean
  • node scripts/token-audit.js reports no new errors from these files

Adds a Data plugin and two Math actions covering data-shaping work that
previously had to be copy-pasted into Code nodes across workflows.

Data plugin:
- Encode / Decode: text to padded hex (bytes8/16/32), raw hex or base64,
  and back. Takes a single value or a JSON array, returns a lookup map
  keyed by the original value. Format-agnostic rather than bytes32-only.
- Extract Fields: pick named values out of upstream output by dotted
  path, returning null on a missing path instead of aborting the run.
  Supports combining several upstream nodes through one JSON source and
  mapping the same paths over an array.
- Flatten Findings: merge several monitoring results into one labelled
  findings list with per-source severity, plus count, first hash and an
  alert-ready summary.
- Static Config: hold static JSON on the canvas without a script wrapper.

Math plugin:
- Compare With Tolerance: actual against expected with a percentage or
  absolute tolerance. Fixed-point BigInt arithmetic throughout, so RAD
  and WAD magnitude values compare without float precision loss.
- Format Number: scale token decimals and render compact K/M/B/T or
  grouped plain notation with an optional unit.

HTTP Request node gains retryAttempts and retryDelay. Retries connection
errors, timeouts and 408/425/429/5xx with linear backoff; a blocked URL,
a malformed URL and other 4xx are never retried. perform.ts is
restructured around an attempt-outcome union so the loop separates
transient from fatal failures without re-parsing error strings.
export async function extractFieldsStep(
input: ExtractFieldsInput
): Promise<ExtractFieldsResult> {
"use step";
export async function staticConfigStep(
input: StaticConfigInput
): Promise<StaticConfigResult> {
"use step";
}

export async function encodeStep(input: EncodeInput): Promise<EncodeResult> {
"use step";
export async function flattenFindingsStep(
input: FlattenFindingsInput
): Promise<FlattenFindingsResult> {
"use step";
export async function compareToleranceStep(
input: CompareToleranceInput
): Promise<CompareToleranceResult> {
"use step";
export async function formatNumberStep(
input: FormatNumberInput
): Promise<FormatNumberResult> {
"use step";
itemsFromObject had two branches returning the same value; collapse to one
with a comment on why an unrecognised object is itself the finding.

The fatal attempt outcome declared an optional status that was never set,
so toResult passed status: undefined where the pre-retry code passed no
status key at all. Remove the field.
@github-actions

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

PR Environment Deployed

Your PR environment has been deployed!

Environment Details:

Components:

  • Keeperhub Application
  • PostgreSQL Database (isolated instance)
  • LocalStack (SQS emulation)
  • Redis (isolated instance)
  • Schedule Dispatcher (staging image)
  • Block Dispatcher (staging image)
  • Event Tracker (staging image)

The environment will be automatically cleaned up when this PR is closed or merged.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

deploy-pr-environment no-issue-required PR exempt from the issue-first gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant