TypeScript runtime for duckflux workflows. Dual-purpose: CLI tool (@duckflux/runner) and importable library (@duckflux/core).
Spec version: v0.7
| Package | Description |
|---|---|
@duckflux/core | Engine, parser, CEL, event hub (in-memory) |
@duckflux/runner | CLI tool (quack run, quack lint, quack validate) |
@duckflux/hub-nats | Optional NATS JetStream event hub backend |
@duckflux/hub-redis | Optional Redis Streams event hub backend |
# Core library
bun add @duckflux/core
# CLI runner
bun add @duckflux/runner
# Optional event hub backends
bun add @duckflux/hub-nats
bun add @duckflux/hub-redis# Run a workflow
quack run workflow.yaml
quack run workflow.yaml --input name=World --input count=3
quack run workflow.yaml --input-file inputs.json
quack run workflow.yaml --cwd /path/to/workdir
cat inputs.json | quack run workflow.yaml
# Validate (schema + semantics)
quack lint workflow.yaml
# Validate with inputs
quack validate workflow.yaml --input name=World
# Print version
quack version--input > --input-file > stdin
quack run workflow.yaml --event-backend memory # default (in-memory MemoryHub)
quack run workflow.yaml --event-backend nats --nats-url nats://localhost:4222
quack run workflow.yaml --event-backend redis --redis-addr localhost:6379import{parseWorkflow,validateSchema,validateSemantic,executeWorkflow,}from"@duckflux/core";import{MemoryHub}from"@duckflux/core/eventhub";constyaml=`flow: - type: exec as: greet run: echo "Hello, World!"output: greet.output`;constworkflow=parseWorkflow(yaml);// Validateconstschema=validateSchema(workflow);constsemantic=awaitvalidateSemantic(workflow,".");// Execute with event hubconsthub=newMemoryHub();constresult=awaitexecuteWorkflow(workflow,{},".",{ hub });console.log(result.output);// "Hello, World!\n"console.log(result.success);// trueawaithub.close();import{parseWorkflow}from"@duckflux/core/parser";import{evaluateCel}from"@duckflux/core/cel";import{executeWorkflow}from"@duckflux/core/engine";import{MemoryHub}from"@duckflux/core/eventhub";- Participant types —
exec,http,emit,workflow(+mcpstub) - Exec input semantics — map input becomes env vars, string input becomes stdin
- Input merge on flow override — chain < participant base input < flow override input
setconstruct — write values toexecution.contextvia CEL expressions- Inline participants — define steps directly in the flow without a
participantsblock - Implicit I/O chain — step output automatically flows as input to the next step
waitsteps — sleep, poll conditions, or wait for eventsemitparticipant — publish events with optional acknowledgment- Event hub — in-memory (default), NATS JetStream, or Redis Streams backends
loop— withmax,until, andas(renamed loop context)parallel— concurrent branch execution with abort on failureif/else— conditional flowwhenguard — skip steps based on CEL conditionworkflow.inputs.*namespace — inputs accessed asworkflow.inputs.<field>in CEL- Participant-scoped
input/output—inputandoutputin CEL refer to the current step - Boolean strictness —
if.condition,when,loop.untilmust evaluate to boolean - Input coercion & constraints —
enum,min,max,pattern,format, etc. - CWD precedence —
participant.cwd>defaults.cwd>--cwd>process.cwd() - Error strategies —
fail,skip,retry(exponential backoff), redirect to fallback participant - Timeout resolution — flow override > participant > defaults > none
- Output schema validation — validate step and workflow output against schema definitions
- Circular sub-workflow detection — prevents infinite recursion in nested workflows
- CEL standard library —
has,size,matches,contains,startsWith,endsWith,lowerAscii,upperAscii,replace,split,join,filter,map,exists,exists_one,all,timestamp,duration
| Backend | Package | Use Case |
|---|---|---|
memory | @duckflux/core (built-in) | Development, testing, single-process |
nats | @duckflux/hub-nats | Distributed, multi-process |
redis | @duckflux/hub-redis | Distributed with persistence |
# Run tests
bun test# Build for npm publishing (ESM + type declarations)
bun run buildSee repository root.