@rglabs/butterfly
v3.3.1
Published
CLI tool to download resources from the Butterfly platform
Readme
Butterfly CLI
CLI tool for downloading and synchronizing resources from the Butterfly platform.
Installation
# Global installation
npm install -g @rglabs/butterfly
# Or use with npx (no installation required)
npx @rglabs/butterfly <command>Quick Start
# 1. One-time: initialize git (enables IDE merge UI on conflicts)
butterfly init
# 2. Configure authentication
butterfly setup
# 3. Pull resources from the server
butterfly pull
# 4. Push your local changes back
butterfly push <path>
# 5. Check sync state at any time
butterfly status
# When status shows `divergent`, do a 3-way merge:
butterfly diff -t reports -n <name> --apply merge
pull/pushare guarded: they refuse to silently overwrite changes made by teammates between your last sync and now. The guard is powered by the server-sideobject_logsaudit table — see Sync Guard below. The legacydownload/uploadcommands still work and use the same guard.On
divergent,diff --apply mergedoes a git-style 3-way merge and stages conflicted files in the git index so your IDE (PhpStorm/IntelliJ/VSCode) opens its built-in merge resolver automatically.butterfly initenables this by ensuring the checkout is a git repo.
Commands
butterfly setup
Configure authentication for the Butterfly platform.
butterfly setupYou'll be prompted to enter:
- Endpoint: Your Butterfly platform URL (e.g.,
https://app.example.com) - Email: Your login email
- Password: Your password
Configuration is saved to .butterfly/config.json.
butterfly pull / butterfly push / butterfly status
Team-safe sync commands. They use the same logic as download / upload but
gate every operation through a sync guard backed by object_logs.
# Fetch remote changes — refuses to overwrite local edits
butterfly pull
butterfly pull -t reports
butterfly pull --force # overwrite local edits
# Push local changes — refuses to overwrite remote changes
butterfly push butterfly-resources/reports/foo
butterfly push <path> --force # overwrite remote (clobbers teammate work)
# Inspect: shows clean / local-only / remote-only / divergent / untracked
butterfly statusWhen a push is blocked, the CLI lists each divergent resource (with
+N remote logs summarizing how many writes happened on the server since
your last sync) and points you to butterfly diff for an interactive
field-by-field review.
See Sync Guard for internals.
butterfly download
Download resources from the Butterfly platform. Same as pull (sync guard is
active on both — pull is the recommended alias).
# Download all resource types
butterfly download
# Download specific resource type
butterfly download -t objects
butterfly download -t reports
butterfly download -t bfy_workflows
butterfly download -t bfy_ai_tasks
butterfly download -t bfy_state_machines
butterfly download -t pages
butterfly download -t bfy_cronjobs
butterfly download -t cms_email_templates
butterfly download -t cms_email_layouts
butterfly download -t webservices
butterfly download -t translations
butterfly download -t static_files
# Download specific resource by name
butterfly download -t objects -n users
# Custom output directory
butterfly download -o ./my-resources
# Clean existing files before downloading
butterfly download --cleanupOptions:
| Option | Description |
|--------|-------------|
| -t, --type <type> | Resource type to download |
| -n, --name <name> | Specific resource name |
| -o, --output <path> | Output directory (default: ./butterfly-resources) |
| --cleanup | Clean existing contents before downloading |
| --force | Bypass the sync guard and overwrite locally-edited files |
butterfly upload
Upload specific files or folders to the platform. Same as push (sync guard
is active on both — push is the recommended alias).
# Upload a single file
butterfly upload butterfly-resources/objects/app/users/name/spec.json
# Upload a folder
butterfly upload butterfly-resources/objects/app/users
# Upload multiple targets
butterfly upload file1.json folder1 file2.json
# Upload a static file (any extension under static_files/)
butterfly upload butterfly-resources/static_files/test/aa.html
# Bypass the sync guard (DANGEROUS — clobbers remote changes)
butterfly upload reports/foo --forceOptions:
| Option | Description |
|--------|-------------|
| --force | Bypass the sync guard and overwrite remote changes |
Sync Guard
pull / push / download / upload are protected by a sync guard that uses
the server-side object_logs audit table to detect concurrent edits between
teammates and prevent silent overwrites.
How it works
Manifest:
.butterfly/sync-manifest.json(auto-added to.gitignore, per-checkout). Each entry storeslast_log_id,local_hash(SHA-256 of the canonical resource payload, stripping transient fields likeupdated_at), andlast_synced_at.On
pull/download:- Hashes every tracked file and compares to its checkpoint.
- If any are dirty → blocks before overwriting, lists them, points to
git stash/--force. - On success: walks the output directory, resolves every
(table, id)pair it can identify (objects, specs, reports, queries, state machines, workflows, cronjobs, email templates, pages, webservices), and stamps a fresh checkpoint with the currentmax(object_logs.id)for that record.
On
push/upload:- Resolves each target file →
(table, id)→ queriesobject_logsfor rows withid > checkpoint.last_log_idfor that record. - Local-only changes: push proceeds.
- Remote-only or divergent: push is blocked. Output shows
+N remote log(s)so you know how much movement happened on the server. - On success: re-fetches the new
max(object_logs.id)and updates the checkpoint forward.
- Resolves each target file →
--forceon either command bypasses the guard but still updates the manifest after the operation.butterfly statuslists every checkpoint with its current state. Exit code1if anything is divergent — useful for CI.
Conflict workflow
$ butterfly push butterfly-resources/reports/foo
Sync check: 0 clean, 0 local-only, 0 remote-only, 1 divergent, 0 untracked
⚠ DIVERGENT cms_reports:11 (foo) (+1 remote log)
Push blocked — remote has changes since your last sync.
• cms_reports:11 (foo) [divergent]
Run `butterfly diff` to review, pull and resolve, or re-run with --force to overwrite.The guard is client-side — no Butterfly backend changes are required. It
queries object_logs via the existing /admin/ajax/cms_object/get endpoint
and filters by id > last_log_id in memory.
Static files: files under butterfly-resources/static_files/ are
mirrored from the static_files object. Downloads materialize each record
as a file at static_files/<custom_seo> (sub-paths become folders) with
file_content as the body. Uploads reverse the mapping — the path relative
to static_files/ becomes custom_seo, the file body becomes
file_content, and an existing record with that custom_seo is updated
(or a new one is created). Any file extension is accepted under this tree.
butterfly add
Create new resources via API and download them locally.
# Create a new workflow
butterfly add -t workflow --title "My Workflow" --system-name my_workflow
# Add a node to a workflow
butterfly add -t workflow-node -w my_workflow --title "Process Data" --node-type CustomScript --node-group Code
# Add a node and connect it to an existing node
butterfly add -t workflow-node -w my_workflow --title "Send Email" --node-type Email --node-group Output --connect-from 123
# Create a connection between nodes
butterfly add -t workflow-connection -w my_workflow --from 123 --to 456Options:
| Option | Description |
|--------|-------------|
| -t, --type <type> | Resource type (workflow, workflow-node, workflow-connection) |
| -w, --workflow <name> | Workflow name or ID |
| -v, --version <number> | Version number (defaults to latest) |
| --title <title> | Title/name of the resource |
| --node-type <type> | Node identifier (e.g., CustomScript, WebHook, RESTAPI) |
| --node-group <group> | Node group (e.g., Code, Trigger, Connector, Output) |
| --connect-from <spec> | Connect from existing node to new node |
| --connect-to <spec> | Connect from new node to existing node |
butterfly workflow-info
Get information about available workflow node types.
# List all node groups
butterfly workflow-info --groups
# List nodes in a specific group
butterfly workflow-info --nodes Database
# Get details for a specific node
butterfly workflow-info --details RESTAPI --group Connectorbutterfly diff
Compare local resources with remote server and apply changes interactively.
# Compare all objects
butterfly diff -t objects
# Compare specific object
butterfly diff -t objects -n users
# Compare reports
butterfly diff -t reports -n my_reportOptions:
| Option | Description |
|--------|-------------|
| -t, --type <type> | Resource type to compare (objects, reports) |
| -n, --name <name> | Specific resource name to compare |
| -o, --output <path> | Base path (default: ./butterfly-resources) |
butterfly record
Perform CRUD operations on any Butterfly table.
# Get a record by ID
butterfly record get users --id 123
# Get records by column filter
butterfly record get users --column email --value "[email protected]"
# Wildcard search
butterfly record get users --column name --value "john*"
# Add a new record
butterfly record add users --data '{"name": "John", "email": "[email protected]"}'
# Edit a record
butterfly record edit users --id 123 --data '{"name": "Jane"}'
# Delete a record (with confirmation)
butterfly record delete users --id 123
# Skip preview and auto-confirm
butterfly record edit users --id 123 --data '{"status": "active"}' --no-preview -yOptions:
| Option | Description |
|--------|-------------|
| --id <id> | Record ID |
| --column <column> | Filter column(s) for get |
| --value <value> | Filter value (use * for wildcard) |
| --columns <cols> | Select specific columns to return |
| --data <json> | JSON data for the operation |
| --file <path> | Path to JSON file with data |
| --db-alias <alias> | Database alias (default: default) |
| --no-preview | Skip preview |
| -y, --yes | Auto-confirm without prompting |
butterfly code
Execute Twig code on the server.
# Execute inline code
butterfly code -c "{{ db().table('users').count() }}"
# Execute from file
butterfly code -f query.bfy
# Get only variables
butterfly code -c "{% set users = db().table('users').get() %}" --vars
# Get raw output
butterfly code -c "{{ 'Hello World' }}" --output-only --rawOptions:
| Option | Description |
|--------|-------------|
| -c, --code <code> | Twig code to execute (inline) |
| -f, --file <path> | Path to file containing Twig code |
| -o, --output <path> | Save output to file |
| --vars | Output only the vars object |
| --output-only | Output only the output field |
| --raw | Output raw string (no JSON formatting) |
butterfly layout
Update object page layout (tab and field ordering).
# Update layout from file
butterfly layout -f layout.json
# Update layout inline
butterfly layout -d '{"object_id": 123, "tabs": [...]}'butterfly translate
Manage translations for multi-language support.
# List languages with translation stats
butterfly translate languages
# Get untranslated texts for a language
butterfly translate get-untranslated --lang tr
# Add a single translation
butterfly translate add --lang tr --source "Welcome" --text "Hoş geldiniz"
# Bulk import from CSV file (tab-delimited)
butterfly translate bulk --lang tr --file translations.csvOptions:
| Option | Description |
|--------|-------------|
| --lang <iso_code> | Language ISO code (e.g., tr, de, en) |
| --source <text> | Source text to translate |
| --text <translation> | Translated text |
| -f, --file <path> | CSV file for bulk operations |
| --format <format> | Output format: json or table |
| --limit <count> | Limit number of results |
Directory Structure
After downloading, resources are organized as follows:
butterfly-resources/
├── objects/
│ ├── butterfly/ # Core CMS objects
│ └── app/ # Application objects
│ └── [table_name]/
│ ├── object.json
│ ├── listing_query.bfy
│ └── [field_name]/
│ ├── spec.json
│ └── [code_files]
├── reports/
│ └── [report-alias]/
│ ├── report.json
│ ├── main_query.bfy
│ ├── queries/
│ └── specs/
├── bfy_workflows/
│ └── [workflow_name]/
│ ├── workflow.json
│ └── v1/
│ ├── version.json
│ ├── connections.json
│ └── nodes/
├── bfy_ai_tasks/
├── bfy_state_machines/
├── pages/
├── bfy_cronjobs/
├── cms_email_templates/
├── cms_email_layouts/
├── webservices/
│ └── [custom_seo]/
│ ├── webservice.json
│ └── queries/
└── static_files/
└── [custom_seo_path] # e.g. test/aa.html — file IS the resourceField Type Code Files
The CLI automatically extracts code from field specs:
| Field Type | Parameter | Output File |
|------------|-----------|-------------|
| calculated | val_1 | code.bfy |
| custom | val_1 | template_code.bfy |
| custom | val_2 | processing_code.bfy |
| filter | val_3 | filter_code.bfy |
| nested | val_1 | configuration.yaml |
| js_code | - | code.js |
| css_code | - | style.css |
Requirements
- Node.js >= 16.0.0
License
ISC
