Build Wireflow
KINDAreplaces $24/mosaves $288/yrback to the verdict
A local visual pipeline runner whose hard problems are never spending money by accident, surviving a restart with in-flight jobs intact, and keeping outputs after the provider's URLs have expired.
Before step 1
Everything below is assumed from the first step. Tick each one when you actually have it, not when you plan to.
- installfree
Why Runs both the Vite frontend and the Express server from one repository.
Get it nodejs.org, or nvm. open ↗
Verify
node --version prints v20 or higher and both dev servers start - API keypay per generation; check the current per-model prices before running a graph
Why Every generate node costs money at submission, so this is the prerequisite that makes the ceiling work in the last phase meaningful rather than theoretical.
Get it Create an account, add a key, and fund it with a small amount you are willing to lose while testing. open ↗
Verify
A single image generation submits, completes and returns a result - have readyfree
Why The cost estimate on the run plan is only useful if the figures behind it are real, and provider prices change without notice.
Get it Read the provider's pricing for each model path in models.json and record the figure with the date checked.
Verify
models.json carries a price and a checked date for every entry - installfree
Why Restart recovery, duplicate submission and ceiling behaviour all need a provider you can kill, stall and count calls against, and doing that against the real one costs money each time.
Get it A small local server implementing submit, status and result with configurable delays.
Verify
The suite can submit, restart the server mid-job and observe recovery - decidefree
Why They are asserted by tests and they refuse real submissions, so choosing them after an expensive evening means choosing whatever that evening cost.
Get it Write down the most you are willing to spend in a day and in one run.
Verify
Both appear in the environment and are named in the ceiling tests
Data model
Create these before the first phase that stores anything. Changing a table later is the expensive kind of change.
- graphs(id, name, nodes_json, edges_json, updated_at) · autosaved on every canvas change, and autosaving never triggers a run - nodes(graph_id, node_id, type, settings_json) · type is text, image_import, prompt_template, image_generate, video_generate or preview - runs(id, graph_id, started_at, finished_at, state, estimated_cost, nodes_executed, nodes_from_cache) - jobs(id, run_id, node_id, input_hash, provider, model_path, request_id, state, submitted_at, completed_at, error) · request_id is written before the first poll - outputs(node_id, input_hash, kind, local_path, provider_url, bytes, created_at) · unique on node id and input hash, which is what makes a re-run free - assets(sha256, path, mime, bytes) · imported images and downloaded results, stored locally because remote URLs expire - The cache key is the node id plus a hash of its resolved inputs, so changing a prompt three nodes upstream re-executes everything downstream of it and nothing else.
Environment variables
These go in a .env file the app reads at startup. The pack's .env.example is this table as a file · copy it, never commit the filled-in version.
| Variable | Needed | Example | Where the value comes from |
|---|---|---|---|
FAL_KEYsecret | required | fal-...redacted | The provider key. Server side only; the canvas never sees it. |
DATABASE_PATH | required | ./data/wireflow.db | Graphs, runs, jobs and outputs. |
STORAGE_DIR | required | ./storage | Imported images and downloaded results, stored by content hash. |
MODELS_FILE | required | ./models.json | Model paths, port definitions and prices with the date each was checked. |
DAILY_SPEND_CEILING | required | 5.00 | Checked before every submission, counting jobs already in flight. |
RUN_SPEND_CEILING | required | 1.00 | Per run. Crossing it stops at the submission that would cross it. |
POLL_MAX_SECONDS | required | 900 | How long a job is polled before it is marked as needing manual checking. |
The build, in order
A canvas that saves and cannot spend
The graph edits, autosaves and reloads, and no code path can call the provider yet.
Every change persists; none of them executes anything.
Files
src/canvas/Graph.tsxserver/db/schema.sqlAdding a model must never mean writing a component.
Files
models.jsonsrc/canvas/NodeFromModel.tsxA dependency test is the cheapest version of this rule.
Files
tests/no-provider-in-ui.test.ts
done when · tick each as it passeswatch out- Reaching for a reactive graph library that recomputes downstream nodes when an input changes is the natural choice here and it is the one that empties your account, because a generate node is just another computation to it. Keep execution out of the component tree entirely: the canvas edits data, the server executes, and only an explicit request crosses between them.
Running on purpose, and knowing the price first
Run executes a topologically sorted graph layer by layer, reusing cached outputs, after showing what it will cost.
The difference between a correct cache and a confusing one.
Files
server/run/hash.tstests/cache-invalidation.test.tsA cycle plus a generate node is an unbounded bill.
Files
server/run/topo.tsThe last moment where the run is still free.
Files
src/run/Plan.tsxmodels.json
done when · tick each as it passeswatch out- Hashing only a node's own settings means changing a prompt template three nodes upstream leaves every downstream generate node believing it is cached, and you spend an evening wondering why the video still shows the old prompt. Hash the resolved inputs, which include everything upstream, so a change anywhere invalidates exactly the nodes below it and nothing more.
Jobs that survive the server restarting
A submitted job is recoverable from its identifier alone, from a cold start.
The order is the whole guarantee.
Files
server/provider/submit.tstests/restart-recovery.test.tsThe table is the record; the poller is not.
Files
server/run/resume.tsTwo clicks on run should cost what one costs.
Files
server/run/execute.ts
done when · tick each as it passeswatch out- Polling in a loop that holds the request identifier only in memory means a restart during a two minute video generation orphans a job you have already paid for, and the only way to find it is the provider's dashboard. Write the identifier first, resume from the table on boot, and treat the in-memory poller as a convenience rather than the record.
Keeping what you paid for
Every output lives on your disk before the run is called finished.
Complete should mean the bytes are yours.
Files
server/assets/download.tstests/assets.test.tsIdentical outputs cost one copy.
Files
server/assets/store.tsA preview that hits the provider is a preview that expires.
Files
src/nodes/Preview.tsx
done when · tick each as it passeswatch out- Storing the provider's URL as the output and rendering previews from it makes a graph look fine for a few days and then quietly fill with broken images, and the run that produced them has already been paid for. Download on completion, store by hash, keep the provider URL only as a note about where it came from, and have the preview fall back to a clear missing-asset state rather than a broken image.
Ceilings, history and handing the graph to someone elseproduct builder
Spending is bounded and visible, and a graph plus its models is portable.
Once per run is not a ceiling, it is a greeting.
Files
server/cost/ceiling.tstests/ceiling.test.tsThe gap between the two is what you learn from.
Files
server/cost/ledger.tsA graph without its model entries is not reproducible.
Files
server/export.tstests/graph-roundtrip.test.ts
done when · tick each as it passeswatch out- A ceiling checked once at the start of a run does not stop a graph that fans out into forty video generations, because the check passed when the total was zero. Check the ceiling before every submission, count the committed cost of jobs already in flight, and refuse the next submission rather than cancelling the run halfway.
That is the whole plan for Wireflow. What it deliberately does not cover is below · check the gaps before you call it a replacement.
- Multiplayer on the canvas.
- A compositor or keyframe timeline.
- Credits and billing.
- Iterators and fan-out over arrays.
- a compositor and keyframe timeline, with hosted rendering
- live multiplayer on the canvas and inside the editor
- hundreds of model schemas kept current for you
- paid steps that never fire by accident, and jobs that survive a closed tab
- a library of ready-made pipelines to fork, and boards you can share
- A third and fourth model added to models.json, which should need no code at all if Phase 1 was done properly
- Fan-out over a list of prompts, but only once the ceiling has stopped a real run at least once
- Seed pinning and a side-by-side comparison of two runs, which the input hash already makes possible
Need the files? The project pack on the verdict page hands your agent the whole brief · more generative media.