
Automation in TypeScript
Workflows and state machines with proper autocomplete and type inference. No YAML. No drag-and-drop. Just code.
A Quick Look
A state machine with typed transitions and task outputs
1import { Machine } from "@fkws/klonk";
2
3// Declare your states upfront — transitions autocomplete from these
4const machine = Machine.create<{ count: number; result?: string }>()
5 .withStates("fetch", "process", "done")
6
7 .addState("fetch", node => node
8 .setPlaylist(p => p
9 .addTask(new FetchDataTask("data"))
10 .input((state) => ({ id: state.count }))
11 )
12 .addTransition({
13 to: "process", // ← autocomplete works here
14 condition: async () => true,
15 weight: 1
16 })
17 , { initial: true })
18
19 .addState("process", node => node
20 .setPlaylist(p => p
21 .addTask(new TransformTask("transform"))
22 .input((state, outputs) => {
23 // outputs["data"] is typed as Result<FetchOutput> | null
24 if (!outputs.data || outputs.data.isErr()) return null;
25 return { raw: outputs.data.data };
26 })
27 .finally((state, outputs) => {
28 if (outputs.transform && outputs.transform.isOk()) {
29 state.result = outputs.transform.data.value;
30 }
31 })
32 )
33 .addTransition({ to: "done", condition: async () => true, weight: 1 })
34 )
35
36 .addState("done", node => node)
37 .finalize({ ident: "example-machine" });
38
39// Run it
40const finalState = await machine.run({ count: 42 }, { mode: "leaf" });
41console.log(finalState.result);Why Klonk?
TypeScript automation that doesn't fight the type system
Actually Type-Safe
Task outputs are typed by their string ident. Transition targets autocomplete from declared states. The compiler catches mistakes before runtime.
Code-First
Define automations in TypeScript. Version control, code review, refactoring—all the things you'd expect.
Workflows
Connect triggers (file events, webhooks, schedules) to task playlists. Each task's input builder sees typed outputs from previous tasks.
State Machines
Build stateful agents with typed transitions. Declare states upfront, get autocomplete everywhere. Good for loops, retries, and multi-step processes.
The Primitives
A small set of building blocks that compose into workflows and machines
Task
A unit of work with validateInput() and run() methods. Returns Result<T> for explicit error handling.
Playlist
Tasks chained together. Each task's input builder has typed access to outputs from all previous tasks.
Trigger
An event source—file watcher, webhook, interval, whatever. Pushes events that kick off workflows.
Workflow
Trigger + Playlist. When the trigger fires, the playlist runs with the event data.
Machine
A state machine. Each state has a Playlist and weighted transitions to other states. Carries mutable state throughout.
StateNode
One state in a Machine. Has its own Playlist, outgoing transitions, and retry configuration.
Compositional Architecture
Simple primitives that compose into powerful automation systems
Workflow Composition
Machine Composition
Klonk provides two primary composition patterns for building automation systems:
Workflow Pattern
A Workflow connects one or more Triggers to a single Playlist, which contains a sequence of Tasks.
Machine Pattern
A Machine orchestrates one or more StateNodes. Each StateNode contains a Playlist of Tasks.
Klonkworks
Pre-built Primitives for Klonk
Grab the building blocks you need - AI, browser automation, OS integrations, utilities - published as standalone packages.
Get Started
Install the package and follow the tutorial
# Using bun
bun add @fkws/klonk
# Using npm
npm install @fkws/klonkLearn by building
The tutorial walks through tasks, triggers, playlists, workflows, and machines—from simple to advanced.