Automation builder
No-code automation
- 16
- Step kinds
- 4 levels
- Nesting depth
- up to 8 per run
- Profiles at once
- 100 per profile per run
- Screenshots
A flow is an ordered list of steps
A flow has a name, an ordered list of steps, its own variables, the profiles it targets, and how many of them to drive at once. All of that belongs to the flow itself. Switching between flows never carries one flow's profile selection into another, and the selection survives a restart.
Flows live on your computer, in rpa-flows.json inside the app data folder. The file is written with owner-only permissions, because steps often carry a login or a form value. Every save passes through a sanitizer that keeps the known fields and drops everything else, so the code that runs the flow can trust the shape it reads back.
The ceilings are deliberate: 200 flows per file, 500 steps per list, and container steps nested at most 4 levels deep. They exist so a hand-edited or imported file cannot make the app recurse without end.
Sixteen kinds of step exist today. Ten are the everyday ones. Six are the advanced ones: branching, list iteration, tabs, cookies, your own JavaScript and network waits.
| Step | What it does | What it needs |
|---|---|---|
| Go to URL | Loads an address in the current tab | An http or https address, or a variable that expands into one |
| Wait | Pauses for a set time, or until an element appears | Milliseconds, or a CSS selector and a timeout |
| Click | Scrolls the element into view, then presses its centre | A CSS selector |
| Input text | Focuses a field, clears it, types the value | A selector and a value |
| Scroll | Scrolls by a number of pixels, or to an element | An amount, or a selector |
| Extract text | Reads an element's text into a variable | A selector and a variable name |
| Take screenshot | Saves a PNG of the page | Nothing |
| Press key | Sends one key press to the page | A key name, Enter by default |
| Random delay | Waits a random time between two bounds | Two values in milliseconds |
| Repeat | Runs its body a fixed number of times | 1 to 1000 passes |
| If / else | Tests the page or a variable, runs one of two branches | One of five tests |
| For each element | Runs its body once per matched element | A selector, a cap, a variable |
| Tab control | Opens, switches or closes a tab of the same profile | An address, an index or a URL fragment |
| Cookies | Reads, writes, deletes or clears cookies in this profile's session | A name, plus an open page or a domain |
| Run JavaScript | Runs your code in the page and stores the result | Code, up to 20000 characters |
| Wait for request | Waits until the page makes a request whose URL contains a fragment | A fragment and a timeout |

You build the flow by hand, and the builder names what is broken
There is no recorder. ARMANOS does not watch you click and turn that into steps. You add each step from a form: choose the kind, fill the fields it needs, save. Steps move up and down with two buttons, and deleting a container tells you first how many nested steps go with it.
What the builder gives you instead is a check that runs while you edit. It looks for the silent breakages, the ones that only show up mid run on a live account: a click with no target, a wait with neither a time nor an element, an extract with nowhere to put the result, a tab step with no address, a JavaScript step with no code.
Every remark says which step number it is about. Errors are marked apart from warnings, and the remarks sit right under the step list.
The check never blocks saving. An unfinished flow is a normal working state, you come back to it tomorrow, and refusing to save would only lose your work.
How a flow leaves this computer
Variables put a different value into every profile
Any text field of a step can carry {{name}}. At run time it is replaced from the flow's variable map, and an unknown name becomes an empty string rather than an error.
The map is built once per profile, in three layers: the flow's default values, then the built-in profile fields, then the per-profile overrides you typed into the matrix under the target chips. That is how one flow signs into twenty accounts without twenty copies of the flow.
Variable names may be written in any script. The app ships in 22 languages, and a name like gorod, 都市 or مدينة survives a save and substitutes at run time. Both halves had to be fixed together: a name that saves but does not substitute makes a flow look correct and quietly do nothing.
Reading fills variables too. Extract text, the cookie read and the JavaScript step all write their result into the name you give, up to 4000 characters. A value that came from a variable is written to the run log as dots, because that log lands on disk and is shown on screen.
profile.name
The profile's name, as it appears in your list.
profile.group
The group the profile belongs to.
profile.totp
A one-time code from the profile's own secret. It is computed at the moment of substitution, not when the run starts, because a code lives thirty seconds and a run is longer than that.
Repeat, if / else and for each
Repeat runs its body a fixed number of passes, from 1 to 1000. The loop itself is not counted as work, its children are, so progress reflects what actually happens.
If / else offers five tests: an element exists, an element is missing, an element's text contains something, a variable equals something, the URL contains something. Only the winning branch runs. Which branch wins is not knowable in advance, so the run's total grows the moment it is decided, and the progress bar lands on the end instead of stalling short.
For each takes a selector and a cap, ten by default. Each pass marks the current element in the page with an attribute carrying the loop depth, so nested loops never overwrite each other. Inside the body, @item points at that element, and @item .title works as well. The list is re-queried on every pass, so a page that re-renders between passes still works, and if the list shrinks the loop ends early instead of failing.
Outside a for each, @item deliberately resolves to a selector that matches nothing. Quietly falling back to the whole document would make a misplaced step click something you never chose. At the deepest nesting level the builder stops offering container steps, so it cannot produce a body the store would drop on save.
One flow, many profiles, and a queue that waits
Target profiles are chips under the step list, with select all and clear. Ids of profiles you deleted are pruned when the card is drawn, so Run is never enabled against a profile that no longer exists.
Concurrency is 1, 2, 3, 5 or 8. The run starts that many workers, but never more than your machine's open-profile limit, which is derived from memory: total RAM minus 3 GB for the system and the app, then one browser per gigabyte, floor 4, ceiling 32. You can override that number by hand if you know your hardware.
When a worker meets the limit, the profile is not written off. The worker waits for a free slot, checking every five seconds for up to thirty minutes, and only then reports the failure. Before this, a night warm-up of thirty profiles could collapse in a fraction of a second, because the first worker to hit the ceiling marked its profile failed and immediately took the next one.
Each profile is launched if it is not already open, then the steps run against its first window. You can walk away to another flow: the run keeps going, and a banner with Stop follows you. Stop is cooperative, checked before every step at every nesting level, so a loop unwinds instead of being killed mid action.
- Concurrency choices
- 1, 2, 3, 5, 8
- Workers actually started
- never more than the open-profile limit
- Open-profile limit
- (RAM minus 3 GB) per gigabyte, floor 4, ceiling 32
- Waiting for a slot
- polled every 5 s, up to 30 min
- Run log
- 600 lines kept, last 400 sent to the window
- Finished runs on disk
- last 20
Numbers taken from the run engine and the memory-based limit in the desktop app.
When a step fails
A failed step does not end the run. The step is logged with its error, a counter for that profile goes up, and the next step is tried. A flow of thirty steps tells you which ones broke, instead of stopping at the first one and hiding the rest.
Errors the engine names get a translated label: no element, wait timeout, navigation failed, request timeout, no cookie, no tab, too many screenshots. Anything else, including a message from the browser itself, is shown exactly as it came back, never swallowed and never rewritten.
The outcome is honest. Done means the written steps ran. If any step of a profile failed, that profile is marked failed and says how many. If any profile failed, the run is failed, and the Task Center notification is red rather than green. Earlier this was judged only by whether the profile itself crashed, so a flow of three steps where all three broke reported success.
That count comes from a counter kept beside the run, not from the log, because the log is trimmed to the last 600 lines. On a long run the early failures were pushed out before you could read them, and the run turned green again exactly where it is long and unattended.
Finished runs are written to rpa-runs.json, the last twenty of them. A run that happened overnight can still be read in the morning, after a restart, which is the only moment a run log really matters. The JavaScript step carries a sixty second limit for the same reason: a promise in page code that never settles used to hang the run forever, and Stop did not help.
Screenshots and where they land
The screenshot step captures the page and writes a PNG. On the built-in engine that is the window's own capture; on ARMANOS Browser it is Page.captureScreenshot over the debugging channel. One step, one code path, two engines.
Files go to the rpa-screenshots folder inside the app data folder. The folder is created with owner-only permissions and every PNG is written the same way. A screenshot of a profile is an open session: mail, messages, and a one-time code if one happens to be on screen.
There is a ceiling of 100 shots per profile per run. A screenshot step inside a repeat inside a repeat can otherwise fill a disk overnight. Passing it raises too many screenshots as an ordinary step error, so you see it in the log rather than discovering it by running out of space.
Open screenshots folder sits in the run card header and on the empty state, so the PNGs stay reachable after a restart, when no run is loaded on screen.
The debugging domains we do not turn on
ARMANOS Browser is a separate Chromium process, 153.0.7978.0, so a flow drives it through the DevTools protocol on loopback. The built-in Electron engine is driven directly through its own window handle. Both are wrapped in one shape, so each step is written once and the fork is a first-class automation target rather than a refusal.
Two domains are never enabled: Runtime.enable and Page.enable. Enabling them makes Chromium install hooks that page JavaScript can observe, which is what the console.debug and Error.stack timing probes look for.
Nothing here needs them. Runtime.evaluate, Page.navigate, Input events and Network commands all work without enabling their domain. Page readiness is polled with document.readyState instead of subscribing to lifecycle events. Network.enable is switched on only while a wait-for-request step is pending, and it is browser side, leaving no JavaScript-visible trace.
The WebSocket handshake also sends no Origin header, because sending one makes Chromium demand an extra command-line flag, and the command line is visible. On the built-in engine, the unprotected debugging port is opened only when that engine is the one running: any program on the same machine under the same user could otherwise reach it.
On a mobile profile, the click step sends touch events instead of mouse events. Measured on a live window: the engine turns mouse presses into touches, the call never returns, and every flow on a mobile profile used to stall on its first click and fail on a timeout.
What this buys you, and what it does not
Scheduling a flow
A schedule belongs to a flow and names the profiles, the time and how many profiles run at once, up to five. Three modes: daily at a time, weekly on chosen days, or every N hours.
Times are local. After a clock change, nine in the morning is still nine in the morning, not eight.
Missed runs are not caught up. If the computer slept for three days, three runs did not happen, and firing them all at once is exactly the burst you want to avoid. The number of missed runs is recorded, so the window can tell you about it without acting on it. A profile you have open right now is left alone.
A schedule works only while ARMANOS is open. There is no system service, the window says so plainly, and we would rather say it than let you find out on a Monday.
The outcome is written back when the run ends, not when it starts, and it is stored as a token: ok, stopped, error:2/5. Words are chosen at display time, so a schedule that travels to a machine set to another language still reads correctly. All schedules are also listed in one window, ordered by what happens soonest.
Schedules arriving from your account land switched off on this machine on purpose: two open copies of the app would otherwise run one flow twice. There is a button to switch it on here, in that same list.
- 1
Pick the flow
Open the clock button on its row in the flow list.
- 2
Pick when
Daily, weekly on chosen days, or every N hours, in your local time.
- 3
Pick who
Tick the profiles, then choose how many run at once, 1 to 5.
- 4
Leave the app open
The schedule fires from the running app. The next due time is shown on the flow row.
- 5
Read the outcome
The row shows the last run, its result, and how many runs were missed.
What this does not do
- There is no recorder. Nothing watches your clicks and turns them into steps. Every step is added by hand from a form.
- A flow is a script, not a person. It repeats what you wrote, at the pauses you set, fixed or random. It does not read a page the way you do, and a site that changes its layout breaks a selector.
- No captcha solving, no image recognition, no machine learning step. If a site asks a human question, the flow stops at it.
- The scheduler runs only while the app is open. There is no background service, a closed laptop misses its runs, and missed runs are never replayed in a batch.
- Keeping the detectable debugging domains off removes one signal. It is not a claim about detection in general, and nothing here promises that a site will treat your run as a person.
How to check
Each claim, the file that implements it, and the stand that guards it.
- The go to step accepts only http and https, and the address is checked again at run time, after variables expand
- apps/desktop/src/lib/rpa.js · apps/desktop/test/rpa-reach.js
- The builder names what is broken before a run, and never blocks saving
- apps/desktop/src/lib/flowCheck.js · apps/desktop/test/flow-check.js
- Done means the steps ran: a profile whose steps failed is marked failed, counted from a counter and not from the trimmed log
- apps/desktop/src/main/main.js · apps/desktop/test/run-result-honest.js
- A run that meets the open-profile limit waits for a slot instead of writing the profile off as failed
- apps/desktop/src/main/main.js · apps/desktop/test/сценарий-ждёт-места.js
- A flow really runs on ARMANOS Browser over the debugging channel, with Runtime.enable and Page.enable never called
- apps/desktop/src/lib/cdp.js · apps/desktop/src/lib/forkPage.js · apps/desktop/test/сценарий-живьём.js
Nearby
Scheduler
Daily, weekly or hourly runs in local time, with missed runs left missed.
Local API
List flows and start a run from your own program on this machine.
Synchronizer
Mirror what you do in one open profile into the others, live, with no script.
Cookie robot
A flow that hands every profile a different browsing order.
Build one flow, run it on ten profiles
The builder, the run log and the scheduler are in the app itself. Nothing about a flow leaves your computer unless you export it or sync it to your account.