paxc compiles the pax DSL into Power Automate cloud flow definitions. Write terse, readable code; get the verbose definition.json that Power Automate expects. Companion interpreter paxr runs the same source locally for fast iteration.
For the full language reference, see REFERENCE.md.
3.6.0 shipped. Every construct listed in REFERENCE.md is implemented and tested, end-to-end deployment to Power Automate has been validated, and a legacy-format package target lets you import compiled flows directly through the Power Automate portal. Round-trip ingest from real PA exports is meaningfully complete: paxc --decode <flow.json> reads an exported PA flow JSON and writes a .pax source file plus a pa/ folder. Variables, Compose, container actions (if/foreach/until/switch/scope), on error-path handlers, terminate, and PA expressions including the standard accessors (triggerBody(), triggerOutputs(), parameters('X'), body('Foo'), iterationIndexes('Loop'), etc.) and slash- or index-style path expressions (triggerBody()?["body/email"], arr?[0]) all round-trip to pax source. PA action keys with characters outside pax's identifier rules (e.g. Send_an_email_(V2)) are normalized on decode and restored byte-for-byte on re-encode via pa/flow.json.actionNameMap. Connectors (OpenApiConnection, OpenApiConnectionWebhook, ParseJson, etc.) stay opaque as pa <Name> blocks with their bodies in the pa/ folder — the design endpoint, not a residual gap.
3.0.0 reflected a strategic reframing of the language: round-trip from existing PA flows is now the primary forward direction. Connector bodies, ParseJson, and any non-default trigger live in JSON files next to the source under a pa/ folder. pax owns the programmable parts (variables, control flow, expressions); files own the PA-specific parts. The raw{} escape hatch and the trigger ... syntax are gone, replaced by the file convention.
The Power Automate browser designer is slow and click-heavy. The underlying flow definition is JSON that's technically hand-editable but structured in ways that fight you: actions are a map keyed by name, dependencies are encoded as a runAfter graph, and expressions live inside escaped strings. pax is a small DSL that turns all of that into source code you can actually read and maintain, and paxc is the compiler that emits the JSON.
Equivalent pax and JSON for initializing a counter:
var counter: int = 1
{
"Initialize_counter": {
"type": "InitializeVariable",
"inputs": {
"variables": [
{ "name": "counter", "type": "Integer", "value": 1 }
]
},
"runAfter": {}
}
}The source is shorter, and more importantly, the runAfter dependency graph is inferred from source order so you never hand-wire it.
The language supports typed variables and let Compose bindings; assignment and compound assignment; arithmetic, boolean, and string concatenation expressions; member access; if/else if/else, foreach, until, switch, scope, terminate, and on <status> error-path handlers; function calls that pass through to Power Automate's expression language; a pa <Name> primitive for any PA-shaped action whose body lives in pa/<Name>.json next to the source (connectors, ParseJson, anything PA-designer-shaped); and a debug() statement that paxr prints at runtime and paxc strips at compile time.
Triggers are file-based: drop a single pa/<Name>.trigger.json next to the source to pick the trigger; without one, paxc generates a default manual ("Button") trigger. Connection references go in pa/connectionReferences.json and end up at the flow's top level on emit.
Add the Excelano apt repository once (one-time setup):
curl -fsSL https://excelano.com/apt/setup.sh | sudo shThen install the .deb, so apt upgrade keeps it current:
sudo apt install paxcBoth paxc and paxr are installed into /usr/bin/, with reference docs at /usr/share/doc/paxc/. Supported architectures: amd64, arm64.
On macOS or Linux, tap and trust the repository once — Homebrew gates third-party taps behind explicit trust (one-time setup):
brew tap excelano/tap
brew trust excelano/tapThen install it, so brew upgrade keeps it current. Installs both paxc and paxr:
brew install paxccurl -fsSL https://raw.githubusercontent.com/excelano/paxc/main/install.sh | shThe installer downloads the right tarball for your platform from the GitHub release, verifies its checksum, and drops both paxc and paxr into ~/.cargo/bin (or the equivalent on Windows). Releases also ship raw tarballs (paxc-*.tar.xz / .zip) for manual installation.
In PowerShell:
powershell -ExecutionPolicy ByPass -c "irm https://github.com/excelano/paxc/releases/latest/download/paxc-installer.ps1 | iex"If you have a Rust toolchain, install the latest release from crates.io. This builds and installs both paxc and paxr into ~/.cargo/bin:
cargo install paxcRequires Rust (edition 2024, toolchain 1.85+). If you don't have Rust, install it first via rustup.
git clone https://github.com/excelano/paxc
cd paxc
cargo build --releaseThe binaries will be at target/release/paxc and target/release/paxr.
How you remove paxc depends on how you installed it. It installs two binaries, paxc and paxr, and stores no configuration or state of its own, so removing the binaries is the whole job.
If you installed via apt, apt remove also clears the reference docs under /usr/share/doc/paxc/:
sudo apt remove paxcIf you installed via the shell installer, the uninstaller removes both binaries from ~/.cargo/bin/:
curl -fsSL https://raw.githubusercontent.com/excelano/paxc/main/uninstall.sh | shIf you used cargo install (or prefer to do it by hand), the binaries are in ~/.cargo/bin/:
rm ~/.cargo/bin/paxc ~/.cargo/bin/paxrIf you built from a clone, delete the checkout (or just cargo clean and rm the two target/release/ binaries if you copied them onto your PATH).
MIT. See LICENSE.