@revoengine/cli
v1.0.29
Published
CLI package for the RevoEngine Platform API
Readme
@revoengine/cli
Development and GitLab CI use Node.js 24.21.0. Run nvm use in this repository to select the checked-in .nvmrc.
Official command-line interface for moving RevoEngine configuration between an instance, a Git repository, and another instance.
The CLI has three main capabilities:
- Repository workflow — pull an instance into version-controlled files, review changes, and deploy the repository to a target instance.
- Project migration — plan, run, and verify a direct migration between two saved projects with
--fromand--to. - Resource synchronization — work with one supported resource family through its dedicated
pull,plan, andpushcommands.
Version 1.0.18
Repository plan/deploy --prune now releases occupied Job Template names before upserts by deleting the reviewed obsolete templates and their obsolete Event/Schedule consumers. Retained references block this early phase; remaining deletions follow verified upserts. Saved plans include deletion timing, so regenerate older replacement plans before deployment. See the repository workflow below for approval, scope and failure behavior.
Repository CI reports now classify converged definitions as operation: "noop" and report missing or stale lock baselines separately through baselineAction, preserving dependency and permission checks.
Requirements and installation
- Node.js 24 or newer
- A RevoEngine API key
npm install -g @revoengine/cliThe package installs revo and the compatibility alias revoengine. The examples below use revo.
revo --version
revo --help
revo -iAuthentication and connection settings
Commands that access RevoEngine need an API URL and API key. The instance ID is normally inferred from the authenticated /api/v1/me response. Repository pull, diff, plan, deploy, and resource commands using the active connection explicitly reject a configured instance ID that differs from the authenticated instance.
Stored login
Use a stored login for local interactive work:
revo auth login
revo auth status
revo auth logoutauth login prompts for a masked API key, validates it, and saves the default connection in the operating system's config directory. To replace an existing login, log out first.
You can also provide the values without prompts:
revo auth login \
--url https://api.revoengine.com \
--token "$SOURCE_REVO_TOKEN"Stored configuration is located under $XDG_CONFIG_HOME/revoengine (or ~/.config/revoengine) on macOS and Linux and %APPDATA%/revoengine on Windows. config.json holds the default login, named projects, and the active project; auth-state.json is a separate validation cache. Logging out removes the default login but preserves named projects.
Saved projects
Save a connection and make it active for ordinary commands, including repository pull, plan, diff, and deploy:
revo projects add source --url https://api.revoengine.com
revo projects list
revo projects set source
revo projects set default
revo projects remove sourceUse --project <name> to select a saved connection for one command without changing the active project. The default project uses the original stored login and environment variable precedence. revo env and --env remain compatible aliases. Adding a project prompts for a masked API key unless --token is supplied.
Agent terminal
Pair a local workspace with AI Agents → Terminals, approve its grant there, and run the terminal in the foreground:
revo terminal connect --workspace "$HOME/projects/my-project" --name "Work Mac"
revo terminal statusFor an existing registration, connect --terminal-id <uuid> --realtime-url <url> overrides its Realtime endpoint for that invocation without pairing again or changing the saved registration. The URL is normalized to the runner WebSocket route; remote endpoints require TLS.
An approved terminal can run as a per-user background service on macOS or Linux. The service uses the active project at installation, or an explicit --project <name>; use the same project when uninstalling after a switch.
revo terminal service install --workspace "$HOME/projects/my-project" --name "Work Mac"
revo terminal service uninstall --workspace "$HOME/projects/my-project" --name "Work Mac"
revo terminal disconnect --workspace "$HOME/projects/my-project" --name "Work Mac"Stop the foreground runner before installing its service. The service starts at login, reconnects automatically, and pins the selected terminal UUID and project profile at installation. Multiple terminals can share one machine and workspace when they have distinct names; pass the same --name to connect, service install|uninstall, and disconnect. Without --name, the CLI uses the machine hostname. If two registrations have the same name, the CLI refuses an ambiguous choice; use revo terminal status to find the UUID and pass --terminal-id <uuid>. A legacy workspace service must be uninstalled before installing a terminal-specific service. Uninstall stops the selected service but keeps its grant and local registration. disconnect stops/removes the selected terminal-specific service, revokes its grant and removes its local credential. Uninstall a legacy workspace service first so it cannot affect a different terminal. On macOS, service logs are in ~/Library/Logs/RevoEngine/. On Linux, inspect them with journalctl --user -u <unit-name>; install prints the unit name. Linux user services normally stop after logout unless an administrator enables user lingering.
The runner retries temporary API startup failures and WebSocket outages without a retry-count limit, using exponential backoff capped at 30 seconds with jitter. Startup reads have a 30-second request deadline; pairing and other writes are not automatically repeated. Ctrl+C or SIGTERM interrupts both a pending API request and a reconnect wait. Realtime restarts and Cloud Run connection timeouts renew transport without revoking the device. A newly supported ACL-change renewal immediately stops work under the old connection and reconnects against the current approved grant; it never extends the old command permission. Older runners stop on that change and need restarting. Actual grant/account/API-key revocation, expiration or invalid frames stop the runner; background services do not restart these deliberate security cutoffs. Availability has no fixed 30-day runner expiry, but depends on the machine, network, valid credentials and supervisor remaining available. An outage longer than the two-minute (120-second) recovery window stops existing processes and Browser sessions; the runner continues reconnecting afterward.
Runner credentials are stored in runner.json beside the CLI configuration. Each executed shell command writes raw output to a private runner-logs/<executionId>.log there, capped at 10 MB per file. Logs older than 24 hours are removed when the next command starts; a runner left idle does not perform timed cleanup. Optional CLI HTTP tracing writes sanitized request metadata under the project .revoengine/logs/http-trace/. These are separate from the Assistant's server-side tool history and do not form a complete local transcript. Treat runner logs as sensitive because command output may contain secrets.
Terminal connection and execution metadata is also written under the CLI configuration at logs/terminal/<agentTerminalId>/debug.log. Each line has [ISO UTC timestamp][severity][assistantThreadId][assistantMessageId] event followed by allowlisted metadata such as the execution ID, operation, sandbox, exit code, and reconnect delay. Commands, browser URLs, input, output, and credentials are excluded. On the first write after UTC midnight or after a restart, the completed day moves to YYYY-MM-DD-debug.log.gz; archives older than 30 days are removed. The log directory is private (0700) and files are 0600. Runner/service stderr remains available for startup failures that occur before this operational log is ready.
The Agent terminal has independent command and network grants. Grants are ceilings; each execution frame carries the requested sandbox and networkAccess=none|local|full, so a Full grant does not silently widen a workspace or local-only call. Workspace commands keep writes inside the registered workspace and a private temporary directory; a workspace shell with external networking uses workspace-network. An explicit unrestricted command requests host file access without adding network access. A localhost development server must request Local network access to bind its port. macOS uses Seatbelt for workspace isolation and host plus local-only network; Linux uses bwrap and fails closed for shell Local access because enforceable localhost-only isolation is unavailable there. Missing isolation support fails closed. The runner requires a complete terminal_policy_v2 execution frame from Realtime, including requested command/network modes, grant revision and optional expiry. New runners advertise terminal_policy_selective_v1: an increased grant takes effect on subsequent calls without disconnecting; a reduced grant stops only commands and Browser sessions no longer covered by the current grant or an unexpired temporary scope. Temporary scope expiry stops affected work and closes Browser sessions using that scope. Workspace patches and stdin for a running process are supported. A personal API key is required.
Browser actions use Playwright's Chromium headless shell. The CLI's npm install downloads that browser into Playwright's per-user cache without requiring Python 3; a separate Chrome installation is not required. If npm scripts were disabled or the download was interrupted, reinstall the CLI with scripts enabled before using Browser. Linux hosts still need Playwright's system libraries; an administrator can install them with npx playwright install-deps chromium.
On macOS, workspace commands may use Unix-domain sockets only inside their own private temporary directory. This lets build tools exchange worker messages without enabling IP networking or access to workspace, other-command or host-service sockets. Local development servers still need Local network access and must bind to localhost. Ordinary process deadlines cover the whole lifetime, not startup. The runner advertises background_shell_v1: terminal.start with background=true and timeout_ms=null keeps a dev server or watcher on the existing execution ID without the ordinary lifetime timer. Status, output, stdin, PTY and cancel remain available; hiding the terminal view does not stop the process. Scope expiry, revocation, runner stop and the two-minute (120-second) disconnect grace still apply. Reuse a healthy project server or check fresh HTTP readiness after starting it.
The terminal approval UI supports Allow once, Allow for 15 minutes, Allow for 1 hour, Allow for 8 hours, Allow for 24 hours and Allow for this thread. Whole-thread access requires this runner's terminal_thread_scope_v1 capability. It covers only the requested Commands and Network levels, survives later messages and short reconnects, and carries no hourly deadline. Disconnect, thread deletion and a device grant revision change revoke it. Realtime rechecks the consent on heartbeat, including idle Browser contexts; scope revocation closes only the affected context. Process background mode and the one-hour Browser idle retention are independent of this approval duration.
The runner keeps a separate browser context for each connected assistant chat, so navigation, DOM inspection, clicks, form controls, waiting, scrolling, and screenshots share that chat's cookies and page state. Completed actions return a stable browserSessionId, activeTabId and tab inventory (ID, title, sanitized URL and active flag), plus sessionCreated. Use tabs, new_tab with a URL, select_tab or close_tab with tabId, and close to manage the session. Other actions accept tabId. Selecting a tab brings it to the foreground without navigation; popup tabs receive IDs and return to their opener when closed. Tab-aware operations require browser_tabs_v1 in Realtime and Gateway. Browser sessions expire after one hour idle (up to four per runner and sixteen tabs per session), on a Local or Full network lease expiry, or when the runner stops or its access is revoked. A one-time Browser network approval closes its context immediately after that action finishes. DOM results include bounded selector hints for interactive elements without their field values. The Browser action supports fill, check, select, press, scroll, and wait_for as well as navigation, DOM, and click. Ordinary fill rejects password and secret-like controls; the Assistant may request a one-time secure credential dialog, which encrypts the value to this runner and fills it in memory without sending it in model tool arguments. The tab-aware handoff authenticates its target tab ID and rejects a switched page even on the same origin. Screenshots can capture the viewport (default), the full page, one selected element, or a bounded viewport crop. For viewport capture, an optional selector limits only the returned DOM text; the image still covers the viewport. Element capture requires a selector. Full-page and crop capture reject selectors. A screenshot is uploaded and attached automatically; calling save_image on its local path uploads a second copy. For custom Playwright E2E scripts, run them as ordinary terminal commands in the project, using the requested sandbox and network grant; the Browser action does not execute arbitrary JavaScript.
The Browser bridge advertises browser_timeout_v1. Agent calls can set action_timeout_ms between 1 and 120000 ms for Playwright navigation, interactions, DOM reads and capture. Omitted values retain navigation 30 s, interaction/DOM 10 s and capture 20 s defaults. Selector waits accept wait_timeout_ms up to 120000 ms and are capped by the operation timeout when both are supplied. These budgets never extend access expiry or retry an interaction. At a capacity limit the agent can list and close a selected tab, or close its own chat Browser session; closing a session discards its cookies.
Device workspaces and direct transfer
The saved agentTerminalId identifies the device across terminal restarts. deviceId is an alias. Connecting another directory with the same profile and terminal name reuses the saved device secret and registers a pending workspace under that device. Approve its access in AI Agents → Terminals → Workspaces. A running device refreshes its contract; it does not need a second runner. Ambiguous older duplicate registrations require --terminal-id. Services remain pinned to the device UUID and original service directory.
Current runners advertise terminal_workspaces_v1, browser_control_v1, file_transfer_v1 and terminal_resume_durable_v1. When Realtime confirms durable resume support, output is retained until the server acknowledges its PostgreSQL commit; reconnect resumes the same execution from the last committed output sequence. Redis loss alone does not stop the PTY. Older servers remain compatible without advertising durable recovery. This protocol preserves a running CLI process; it does not restore processes after a CLI or machine crash. Gateway can request socket reconnect without stopping the CLI. Temporary transport/dependency failures retry and resume within two minutes (120 seconds); authentication failures and revocation stop retry. Newly installed services stop on fatal authentication and restart other failed processes. Reinstall existing services to adopt that supervisor behavior. CLI stop does not persist local processes or browser cookies. Changing workspace grant revision conservatively terminates workspace-bound work and contexts.
Browser inventory exposes its owning chat and workspace. The model can explicitly close permitted same-actor sessions/tabs to free the four-session/sixteen-tab capacity; active sessions are never silently evicted. Inventory/cleanup preserves the existing network lease. Local/full scope changes recreate the browser context and require browser workflow recovery.
upload_file and download_file stream an explicit authorized workspace file directly to/from GCS HTTPS signed/resumable URLs with permitted headers. Full network permission is required. WebSocket carries metadata only. Resumable uploads report committed offsets and partial completion; do not finalize a Storage upload before completion or blindly repeat an uncertain upload. Downloads commit atomically and do not overwrite unless explicitly requested. The stream's idle timeout is 90 seconds; grant expiry and runner lifetime still apply. Signed URLs/headers are credentials and must not be copied into logs or source files.
Interactive PTY commands use the optional native node-pty package. An unavailable native build does not block CLI installation; PTY requests return pty_unavailable until the optional package can be installed. To skip native installation when PTY is not needed, install locally with npm install --omit=optional @revoengine/cli and run npx revo; npm 11.9 may still install the optional dependency during a global installation. On macOS, the runner verifies the spawn helper's executable permission when a PTY starts. Ordinary commands and local development servers run with pipes.
Local Browser mode filters HTTP and WebSocket requests and disables WebRTC, WebTransport, Worker and SharedWorker before page scripts. Worker-dependent pages may not work in this mode; Chromium is not enclosed in an OS network sandbox. A temporary Local or Full network lease closes the Browser context at expiry when the current permanent grant does not cover it, including before a queued secure credential fill. A lost WebSocket connection, a controlled Realtime restart, or a temporary dependency outage retains running processes and Browser contexts for one two-minute (120-second) reconnect period; repeated failures do not renew it. Receiving ready alone does not reset that period while executions still await resume confirmation. Handshake, ready and execution resume each have a ten-second transport deadline. Access revocation and malformed runner frames stop the runner and its processes. Grant revision changes reject older execute frames and stop only work outside the remaining permanent or temporary scope. A network upgrade preserves the original temporary lease for a subsequent downgrade.
save_image validates the canonical path of the opened file descriptor and reads the image from that same descriptor (up to 30 MiB). It rejects the upload if the descriptor path cannot be verified, including when lsof is unavailable on macOS or /proc/self/fd cannot be resolved on Linux.
Environment variables
Environment variables are the recommended authentication method for CI/CD:
export REVO_URL="https://api.revoengine.com"
export REVO_TOKEN="$SOURCE_REVO_TOKEN"
revo -i
revo diffSupported variables:
| Purpose | Preferred name | Compatible aliases |
| --- | --- | --- |
| API URL | REVO_URL | REVO_BASE_URL, REVOENGINE_URL, REVOENGINE_BASE_URL |
| Instance ID | REVO_INSTANCE | REVOENGINE_INSTANCE |
| API key | REVO_TOKEN | REVO_API_KEY, REVOENGINE_TOKEN, REVOENGINE_API_KEY |
| Disable update checks | REVO_NO_UPDATE_NOTIFIER | NO_UPDATE_NOTIFIER |
Command-line flags
Flags are useful for one-off commands and override all other runtime configuration:
revo \
--url https://api.revoengine.com \
--instance "$REVO_INSTANCE_ID" \
--token "$REVO_API_TOKEN" \
pull --all --yesGlobal connection flags:
--project <name>(or the compatible--env <name>)--url <url>or--baseUrl <url>--instance <uuid>--token <apiKey>
Avoid putting tokens directly in shell history. Prefer environment variables for automation.
Cross-environment migration
Saved projects are required by revo migrate --from/--to and are also accepted through --project or the compatible --env alias. Examples use source and target for migration roles. Both may use the production API URL shown below, with separate API keys for their respective instances; the profile name itself does not select an API.
revo projects add source --url https://api.revoengine.com
revo projects add target --url https://api.revoengine.com
revo projects listprojects add prompts for an API key and validates the profile. Use --token <apiKey> to supply the key directly or --offline to skip validation. Remove an unused profile with revo projects remove <name>.
Runtime configuration precedence is:
- command-line flags;
- a selected named profile;
- environment variables;
- the stored default login;
https://api.revoengine.comas the default URL.
A named profile is isolated: it never inherits a URL, instance, or token from environment variables or the default login. The special profile name default uses the normal flag → environment variable → stored login chain, but full revo migrate commands require two real named profiles.
Project setup and portable identity
Initialize a repository before using repository deployment or full migrations:
revo project init . \
--identity-mode stableKey \
--stable-key-name stableKey \
--resources allInitialization authenticates and downloads editor types from the instance's sandbox endpoint. It creates or updates:
.revoengine/revo.json— project settings, workspace path, identity mode, and tracked resources;.revoengine/types/— generated editor types;- editor configuration, Git ignore entries, and local debug scaffolding.
Subsequent resource synchronization creates .revoengine/revo.lock.json baselines and directories such as Components/, DatabaseTables/, and Endpoints/ in the configured workspace. With Stable Key identity, project init also backfills metadata for selected resource families except Database Tables; their keys require an explicit metadata plan/apply review.
Use revo project update to refresh an existing project. When the RevoEngine workspace is nested, pass its path, for example revo project init ./backend.
Stable Keys are the durable identity used across instances. UUIDs and versions remain instance-local and are not used to match portable resources. Before the first migration, inspect and apply missing keys on the source:
revo metadata plan --project source --json
revo metadata apply --project source --yesYou can limit the operation:
revo metadata plan --project source --resources component,endpoint
revo metadata apply --project source --resources component,endpoint --yesExisting resources on another instance must use the exact same Stable Key as their source counterpart. Missing keys are treated as unmanaged; duplicate or ambiguous keys block writes. Database Table and Database View metadata changes deserve an explicit review because their backend update contracts are broader than ordinary metadata-only writes.
Use --no-metadata-backfill with project init when initialization must not write remote metadata.
1. Repository workflow
Use this workflow when a Git repository is the reviewed source of truth.
RevoEngine source -> revo pull -> repository -> revo diff -> revo plan -> revo deploy -> RevoEngine targetPull source state into the repository
Authenticate to the source through flags, environment variables, or the stored login, then pull all resources:
export REVO_URL="https://api.revoengine.com"
export REVO_TOKEN="$SOURCE_REVO_TOKEN"
revo pull --all --yesOr pull only selected resource families:
revo pull \
--resources database-table,database-view,component,job-template,endpoint \
--yespull requires either --all or --resources. It writes resources in dependency order and records the source baseline under the authenticated instance ID. Add --skip-groups to omit Group and Role Group resources from the selected scope.
Repository pull, plan, and deployment use Stable Keys for Component identity, including projects with a legacy UUID lock or no explicit componentIdentity setting. Pull reports resources without a Stable Key as unmanaged WIP and skips them while continuing with managed resources; --force does not assign missing keys or include WIP resources.
A normal pull protects locally changed files and does not remove source-deleted manifests. After review, pull --force replaces managed resource directories and prunes managed manifests that no longer exist on the source. Keep hand-written files outside generated resource directories.
Review and deploy the repository
Commit and review the generated files. Then authenticate to the target:
export REVO_URL="https://api.revoengine.com"
export REVO_TOKEN="$TARGET_REVO_TOKEN"
revo diff
revo plan
revo deploy --yesdiff and plan compare the local repository workspace to the active target without changing resource files, baselines, or target resources. Authentication caching and enabled HTTP tracing may still write local diagnostic files. diff shows content differences, including unified line diffs for Component source and RAW_SQL queries. It omits warnings, blockers, unchanged resources, and readiness checks, so success does not mean deployment is safe or the target is converged. It rejects --force; use plan --force to review gated operations.
plan reports ordered create, update, adopt, excluded, and blocked operations with warnings and a readiness verdict; it exits unsuccessfully on blockers. --hints adds informational details. Sensitive fields in change details are redacted. diff --json returns a content-diff report, while plan --json returns the full deployment plan. deploy recomputes the live plan, applies resources in dependency order, and verifies writes by reading them back.
The target can also come from a saved project (revo plan --project target), a stored login, or explicit CI credentials:
revo --url "$REVO_API_URL" --instance "$REVO_INSTANCE_ID" --token "$REVO_API_TOKEN" plan --jsonplan --strict exits unsuccessfully when create or update operations are pending. Omit --strict in a CI pre-deployment job that is expected to approve deployable changes. The repository plan always evaluates all tracked resource families and their dependencies; --changed --base remains specific to component plan.
The deployment scope comes from .revoengine/revo.json.trackedResources:
revo project update \
--resources database-table,database-view,component,job-template,endpoint,schedule,eventFor CI output, use JSON:
revo diff --json
revo plan --json
revo deploy --yes --jsonRepository plan and deploy reports separate resource operations from lock bookkeeping across all twelve resource families. An existing resource whose managed definition matches the repository is operation: "noop", including when its baseline is absent or stale. Per-item baselineAction is "adopt" for a missing baseline, "refresh" for a stale baseline, or "none". It describes separate bookkeeping for converged resources; create/update operations still save their verified baselines through the existing deployment flow. Use revo plan --hints to see this diagnostic in terminal output.
The JSON extension retains schema version 2, raw family status/counts, and operations.adopt. Repository summaries count converged resources as operations.noop; CI consumers should use item operation or aggregate operations rather than raw family status to identify resource changes. Validation failures, owner/role changes, and activation exclusions still take precedence over content equality. Dedicated resource commands and named migrate keep their existing contract.
plan does not save manifests or baselines. deploy recomputes live state and may save a verified baseline in the runner checkout without modifying a converged resource. The next fresh checkout can therefore report the same noop without committing that lock back to Git. View validation can still issue a read-only preview POST when the runner lacks a trusted dependency fingerprint.
Repository deployment uses desired-source-wins upsert: it can adopt a missing baseline or overwrite target content drift with the repository state. Its Database Table policy permits supported table renames, column additions and changes, and copying foreign-reference declarations as metadata. Copying those declarations does not create physical SQL foreign-key constraints.
Use --activate on both repository plan and deploy to align tracked Event and Schedule active values with the manifests. The saved plan includes this activation policy; changing the flag between planning and deployment is rejected before writes. Automation is activated or disabled only after all definition writes and pruning succeed, using verified candidates and readback. Without this flag, target activation is preserved; new automations are created inactive and a remaining activation difference fails deployment verification. Named migrations and dedicated resource commands keep their existing activation behavior.
revo plan --prune --activate --out reviewed-plan.json
revo deploy --prune --activate --yes --reviewed-plan reviewed-plan.jsondeploy --force additionally approves supported high-impact operations reported by plan, including column removal, managed partition deletion, Materialized View Schedule writes, Schedule target-type changes, and Group or Role Group writes. Review them with revo plan --force before deployment. Force does not bypass invalid identity, missing dependencies, unsupported DDL, ambiguity, or failed verification. Full repository reconciliation explicitly selects these supported operations with --prune; it does not require --force.
To reconcile all tracked families, review the complete plan and approve its deletion list:
revo plan --prune --out reviewed-plan.json
revo deploy --prune --reviewed-plan reviewed-plan.json
# CI: approval is supplied by the pipeline's confirmation policy
revo deploy --prune --yes --reviewed-plan reviewed-plan.json
revo version --json--prune makes local manifests authoritative for the configured trackedResources. A keyed target resource absent locally is a deletion candidate even without an old lock entry. Unkeyed resources are unmanaged and preserved. This covers Components, Job Templates, Endpoints, Events, Schedules, Database Tables (including partitions), Database Views, Groups, Role Groups and Service Accounts when selected. Runtime Job executions/history and Webhook deliveries are outside this definition-sync scope; their existing lifecycle is unaffected. Choose only resource families owned by the repository: an empty selected family intentionally means deleting all its managed target definitions. Environment-local Service Accounts should be excluded from trackedResources.
The planner lists exact target IDs, names and Stable Keys in deletion order. Duplicate identity, retained local/unmanaged/out-of-scope consumers, system resources and deletion of the authenticated Service Account block the plan. When a desired Job Template name is occupied by a different Stable Key scheduled for deletion, the plan explicitly marks that template and its obsolete Event/Schedule consumers for deletion before upserts. An inactive consumer still retains its reference. Retained, unmanaged or out-of-scope consumers block this early deletion, including retained consumers that would otherwise rebind during upsert. An occupied name outside the approved prune scope or duplicate desired names block planning instead of failing later with HTTP 409.
All other deletions run after upserts and their readback verification, allowing a retained Job Template or Endpoint to move to a replacement Component. Pruning removes obsolete consumers before dependencies. API errors or failed readback stop further deletes; a failed early delete prevents upserts, and a failed upsert prevents the remaining prune. Early deletions can remain applied if a later write fails, so review both deletion phases before approval. The owning APIs retain their existing permission and reference guards and soft-delete semantics, including the table subtree lifecycle and view automation deactivation. Root deletion is blocked by any retained or unmanaged partition. No arbitrary implicit consumer cleanup is performed.
plan --out <file> saves the plan without mutation or lock changes. deploy --reviewed-plan <file> requires --prune and fences the whole reviewed deployment: desired and target definition hashes for every tracked family (including noops), managed target identities, versions and activation, authenticated instance, policy, tracked scope, and the deletion list with its timing (beforeUpserts: true where required). Drift or an older artifact without reviewedPlanVersion: 1 blocks all writes and requires a fresh reviewed plan. The same comparison runs again after confirmation. Component writes retain the reviewed target identity, hash and version through execution: metadata and element saves obtain API mutation reviews and send their tokens in x-revo-mutation-review. A later version change, unavailable review route, or malformed review stops the deployment instead of refreshing the expected version or forcing the write. Regenerate reviewed plans saved by older CLI versions for replacements that now require early deletion. Each early deletion rechecks the remaining Job Template/Event/Schedule graph. Upserts are followed by another live comparison of the remaining prune plan. Each deletion rechecks identity and definition, verifies disappearance from the active catalog and clears only the selected target baseline; verified partition deletion retains its lock tombstone. Cascading table/view operations additionally recheck the remaining dependency graph. Deployment remains a sequence of API operations, not one server-side transaction: concurrent edits can cause a guarded rejection after earlier verified writes. Metadata and elements are separate guarded writes; the elements expect only the version increment made by this deployment. Readback verifies the accepted result but never authorizes overwriting newer target changes. A fresh plan is required after target drift or a failed partial deployment.
deploy always shows the plan before interactive confirmation; --yes supplies noninteractive approval. Ordinary resource rows, field names, dependency hints and metadata use one line per item. Field/value diffs, list changes and code/SQL diffs retain multiline Git-style formatting. revo version --json advertises workspacePrune, reviewedPrunePlan, reviewedDeploymentPlan and componentMutationReview so CI can reject older releases that ignore unknown flags. Without --prune, repository deployment remains upsert-only.
Component-only reconciliation remains available through revo component plan --all --prune and revo component push --all --prune --yes; interactive confirmation is also supported. This matches the configured identity, rechecks local reappearance and live target drift, verifies deletion and clears only the selected environment baseline. --force is not required for explicit Component pruning. Named migrate and legacy env apply retain their existing scope and confirmation rules; migrate --prune is rejected.
2. Project-to-project migration
Use this workflow for a direct migration between two saved projects. It does not require committing the source snapshot to the repository.
revo projects add source --url https://api.revoengine.com
revo projects add target --url https://api.revoengine.comPlan, run, and verify the migration:
revo migrate plan --from source --to target
revo migrate run --from source --to target --yes
revo migrate verify --from source --to targetplanpulls a fresh source snapshot into a temporary project and compares it with the live target, leaving the real workspace and lock unchanged.migrate run --dry-runfollows this same read-only flow.runforce-pulls the selected source families into the configured workspace, replacing managed local files and pruning source-deleted local manifests, then performs plan → ordered writes → readback verification. Preserve intended local edits before running it.verifypulls the source again into a temporary project and fails on blockers or remaining changes.
Select a subset with a comma-separated or repeated --resources flag:
revo migrate plan \
--from source \
--to target \
--resources component,job-template,endpoint \
--json
revo migrate run \
--from source \
--to target \
--resources database-table \
--yes \
--forceUse --skip-groups to omit both group and role-group:
revo migrate plan --from source --to target --skip-groups
revo migrate run --from source --to target --skip-groups --yesThe migration model is source-wins upsert. Missing target resources are created and changed resources are updated. Target-only resources are reported as exclusions and are not deleted. A failed phase prevents all later phases from writing; earlier verified writes are not rolled back. Named migration includes Materialized View and access-group writes in its selected scope without the additional repository force gates; destructive column removal still requires --force.
Plans project earlier phase results into later dependencies: a Schedule can plan against a Job Template or Materialized View that an earlier phase will create. After each phase verifies its writes, the next phase resolves the actual target UUIDs and versions again before writing. A failed readback stops subsequent phases; verify uses a fresh source pull to detect remaining changes.
revo env diff/apply is a separate, component-only synchronization command retained for focused component mirroring. Use revo migrate for the complete multi-resource workflow.
3. Supported resources
Full pull, deployment, and migration use this fixed dependency order:
| Order | Resource selector | Dedicated command | Workspace directory | Important behavior |
| ---: | --- | --- | --- | --- |
| 1 | database-table | database-tables | DatabaseTables/ | Tables, columns, and partitions; destructive DDL remains guarded. |
| 2 | database-view | database-views | DatabaseViews/ | STRUCTURED and RAW_SQL views, including materialized views; target schemas must be ready first. |
| 3 | component | component | Components/ | Portable code, libraries, and elements. Libraries are deployed before executable components. |
| 4 | service-account | service-accounts | ServiceAccounts/ | Owner identities are resolved before dependent Groups and Job Templates; secrets are target-local. |
| 5 | agent-plugin | agent-plugins | AgentPlugins/ | Tools, MCP servers, skills and packages; dependencies precede consumers and authentication remains target-local. |
| 6 | agent | agents | Agents/ | Portable config/policy and execution identity; memory and runtime state remain target-local. |
| 7 | job-template | job-templates | JobTemplates/ | Resolves Component and Service Account dependencies by Stable Key. Pull skips WIP templates without a Stable Key or with an unkeyed Component and reports each skip. |
| 8 | endpoint | endpoints | Endpoints/ | Resolves Component dependencies and route ordering. |
| 9 | schedule | schedules | Schedules/ | Targets Job Templates or Materialized Views. New schedules are inactive by default. |
| 10 | event | events | Events/ | Supports Job Template event targets. Manifests retain custom: true/false alongside type; new events are inactive by default. |
| 11 | group | groups | Groups/ | Group owners use Stable Keys; Service Account owners resolve early; human users and memberships remain target-local. |
| 12 | role-group | role-groups | RoleGroups/ | Role assignments run last; members and user identifiers remain target-local. |
Active Materialized View refresh Schedules are reported in Database View plans but do not block push, repository deployment, or named migration. Updating a view preserves Schedule activation; API write failures and readback verification failures are still reported.
--resources all selects all twelve families. Singular, plural, and common compact aliases are accepted where the command parser documents them, but the selectors above are the canonical values.
Existing database-schema selectors and revo database-schemas commands remain accepted as compatibility aliases. New table manifests use DatabaseTables/**/database-table.json; older DatabaseSchemas/**/database-schema.json files remain readable and are relocated by the next successful table pull. A locally edited manifest still needs --force before pull replaces it. The manifest kind and databaseSchemas lock and plan keys retain their existing schema names for format compatibility.
Older Event manifests without custom must be refreshed with revo events pull from the source environment or edited to set custom explicitly. The pull upgrades an unchanged, locked manifest without --force. Check the type too: a legacy CUSTOM_EXPORT event now uses type: "EXPORT", custom: true when that is the source definition. An Event plan blocks manifests with no custom classifier.
Event and Schedule manifests retain runtime input (a JSON object) and inputMode at the top level. Pull, plan, push, migration and readback verification include both fields. Supported modes are override (shallow overlay onto Job template input), deep_override (nested object overlay, replacing arrays and retaining null values), and replace (whole-input replacement, including clearing it with {}). The CLI migrates these definitions; the backend resolves the Job input. Unknown modes, null/scalar/array input and duplicate top-level/extension fields block writes, including with --force.
Previously pulled Event manifests with extensions.input and extensions.inputMode remain accepted; only those extension fields are writable. A fresh pull writes them at the top level. Old Event lock hashes are recognized using the complete live input values, so they cannot hide remote drift. Other extension fields still block deployment.
Omitted fields mean input: {} and inputMode: "override" in the CLI's desired definition, including updates. Older Schedule pulls did not export these fields: re-pull from the source before promotion if that source uses nondefault automation input. A plan shows any difference from the target; approving an overwrite applies the manifest defaults. Regenerate saved reviewed plans after updating the CLI because input and mode now participate in definition hashes.
Work with one resource family
Every resource family has a dedicated synchronization flow. With a stored login or REVO_URL and REVO_TOKEN, omit --project to use the active connection:
revo database-tables pull --all
# Edit a file in the configured workspace.
revo database-tables plan --all
revo database-tables push --all --yesFor resource families other than legacy component sync, the active connection's baseline is stored under its authenticated instance ID. Switching the active login therefore does not reuse another instance's baseline. pull protects local edits; use --force only after reviewing the replacement. Run commands from the repository containing .revoengine/revo.json.
When migrating Schedules after Database Views, review the plan and use this full push with the active login (no --project):
revo schedules plan --all
revo schedules push --all --yes --force--force permits adopting a missing Schedule baseline or overwriting reviewed target drift. It also approves a reviewed transition between Job Template and Materialized View targets under one Schedule Stable Key. Materialized View refresh Schedules are included with the other Schedules and identify their Database View by Stable Key and refresh mode, never by a source UUID. User-managed JOB_TEMPLATE and PLATFORM_OPERATION schedules of kind refresh_materialized_view are supported. Other platform operations, AGENT, and system-managed Schedules are excluded; --strict treats exclusions as incomplete coverage.
Pull initializes each Schedule's editable activation candidate from the source. A new target Schedule is created inactive, while a configuration update preserves its target activation state. activationCandidate records the desired active state. Plans report activation-only differences as activation-drift, including without a target lock baseline. Repository plan/deploy without --activate verifies definition convergence and preserves target activation; activation-only drift remains informational and does not fail a strict plan or final verification. With --activate, activation differences require changes and are aligned only after verified writes and pruning. Plan and deploy must use the same activation policy. schedules push --all --yes --activate also aligns verified new, updated and otherwise unchanged Schedules, including disabling them. Failed configuration preflight skips activation; changed definitions or dependencies block activation. Interactive activation asks for a second confirmation. Named migrate retains its separate activation-convergence checks.
Use --project to select a saved project explicitly (--env remains a compatible alias):
revo <command> pull --all --project source
revo <command> plan --all --project target
revo <command> push --all --project target --yesFor example:
revo endpoints pull --all --project source
revo endpoints plan --all --project target --json
revo endpoints push --all --project target --yesDatabase Table and Database View pushes can also select one Stable Key:
revo database-tables push 'Sales/Orders#1234abcd' --project target --yes
revo database-views push 'Sales/Revenue#1234abcd' --project target --yesEndpoint route uniqueness applies to live resources, matching the API. Soft-deleted endpoints with other Stable Keys do not reserve routes. Exact-key restore also checks the endpoint's old route during planning and again immediately before restoration, even when the desired path changes. The backend remains the final authority for concurrent route changes.
Database View plans show a line diff for changed RAW_SQL queries, including unchanged context and red/green removed and added lines in color terminals.
Preview failures use validation-failed, preserve the definition diff, and show the backend message, HTTP status, definition mode, and timeout information. RAW_SQL failures also suggest checking whether the instance enables RAW SQL Database Views. They block deployment of the affected view even with --force; database-views push continues with independent views but exits unsuccessfully if validation failed. An unchanged view skips planning preview only when its target hash and source-table dependency fingerprint match the environment lock; old locks still require preview. Pull and verified push update this fingerprint, while plan remains fileless. Creates and updates always revalidate immediately before writing.
RAW_SQL validation sends take: 0 without an output overlay, using the backend's LIMIT 0 path. STRUCTURED validation sends take: 1 and output: { take: 1, count: false } so the current backend limits returned rows. Queries that aggregate or sort large source sets can still be expensive; true schema-only validation requires a backend change. Each preview has a 30-second request timeout and is not retried.
Database Table identity, DDL, and target-local policy
Table Stable Keys match resources across instances; a table name is the physical SQL name. Dedicated database-tables plan/push leaves table renames and column removals for manual handling, even with --force. Repository plan/deploy and named migrate use a broader source-wins policy: they can rename tables and apply supported column changes, while column removal requires --force. Table kind, partition parent or bounds, unsupported array changes, and ambiguous identities remain blocked. Every accepted write needs portable readback verification before its lock baseline advances.
Root-table columns can carry unique, non-empty Stable Keys in definition[].metadata.<stable-key-name>. metadata plan/apply can backfill missing root-column keys; partitions inherit the root definition. Existing column keys are immutable even with --force, and malformed or duplicate keys block backfill. A shared key matches columns before exact-name fallback, and writes attach the live target column ID. Source column IDs and reserved __* metadata never become portable column identity.
Dedicated push allows reviewed key-matched changes to existing columns, including name, type, default, nullability, array, primary-key, unique, and index fields. These operations can scan, lock, rewrite, or validate live data. Exact-name matching remains a fallback for safe presentation changes and an ordinary nullable column addition; it never guesses a rename. A required, defaulted, indexed, unique, or primary-key addition needs a unique column Stable Key. Structural changes to an existing unkeyed column and unkeyed operational additions remain manual. Active Database Views block dependent column renames, removals, type changes, and array changes at plan time and again before the write.
Database Table manifests own the complete non-reserved user metadata object. Push replaces target user metadata with the manifest's object, including deliberate key removals, while platform-reserved __* fields remain server-owned. Audit history and the source audit setting are target-local: updates preserve the target audit setting and new tables start with audit disabled.
Access control is also target-local. Pull records a root table's source restricted flag as sourceRestricted in the shared lock, outside its portable manifest and hash. New roots use that flag but copy no source ACL user or group IDs; existing target access is preserved on update, and partitions inherit the target parent's policy. Assign target principals separately. Dedicated push blocks creation when an older lock lacks the restriction snapshot; pull again to capture it. Orchestrated deployment permits that create and defaults an unknown restriction to false, so preserve the lock alongside manifests when source restriction matters.
Non-empty foreignReferences remain visible in manifests. Dedicated push classifies a create or change carrying them as a manual schema migration, even with --force; an identical target can remain clean. Repository deploy and named migrate can copy the declarations with a warning, but the backend does not create physical SQL foreign-key constraints. Verify and apply physical constraints separately when required.
Database Table partitions and deletion
Review database-tables plan --all before a destructive push. database-tables plan --all --skip-partitions is a root-only report: it excludes local and remote partitions even under --strict. Pull and push still include managed partitions, so use a full plan before partition work. A changed range, value set, or hash bound creates a replacement partition with a new Stable Key. Restoring the same physical partition keeps its key. Replacement takes two verified pushes: remove the old key, then create the new one. The CLI does not attempt an overlapping delete and create in one push or reuse a key still owned by a recoverable deleted target.
database-tables push --all --prune-partitions --yes --force removes only previously managed partitions whose local manifests were removed. It deletes from leaves to root, refuses to remove a parent with an active child, and records a tombstone in the selected environment's lock after verified deletion. Unknown target-only partitions stay untouched. database-tables push '<stableKey>' --prune-orphans --yes --force can remove a target-only root with that Stable Key without a prior baseline. Before either remote deletion, the CLI rechecks live identity and dependencies; active child partitions or Database Views referencing the table block it. A successful delete is verified before the lock changes.
database-tables pull --prune-orphans --yes --force removes local manifests missing from the source; it never deletes target tables. Dedicated plan reports root orphans without accepting --prune-orphans. Repository deploy --force retains its legacy partition-only removal policy; deploy --prune additionally reconciles all selected keyed table roots and partitions. Named migrate does not prune remote tables or partitions.
Resource plans support --json for automation and generally support --strict as a convergence gate. Human-readable plans show changes by default and hide informational details such as dependency readiness, component hashes, owners, and size statistics; add --hints to show them in individual plans, revo plan, and revo migrate plan. JSON plans still include the complete diagnostic data. Run revo --help for resource-specific safety flags such as --activate, --skip-partitions, and prune controls.
Safety model
plan,diff, andverifydo not write target resources.--yesconfirms a bulk operation; it does not relax consistency checks.--forceapproves only explicitly supported risky or drifted operations; it is not a universal bypass.- Writes resolve the target UUID and version immediately before mutation and verify the portable result afterward.
- Stable Keys cross environments; source UUIDs, versions, members, users, ACLs, and other target-local identities do not.
- New Schedules and Events are created inactive; ordinary updates preserve target activation. Dedicated
schedules push --all --yes --activatealigns manifest activation intent for verified new, updated and otherwise unchanged Schedules, including disabling them. Activation drift is shown by plans and affects repository convergence only with --activate.events push --all --yes --activatealigns verified Events with manifest activation intent, including disabling them; configuration failures skip Event activation. Interactive activation requires an additional confirmation.deploy --activateruns this phase after verified writes and pruning; namedmigrateleaves activation to dedicated commands. - Groups and Role Groups are intentionally late phases because they can affect ownership and access.
- Lock data is stored in
.revoengine/revo.lock.json; API tokens are never persisted in the project.
Other commands
# List API endpoints
revo endpoints
# Search platform content
revo search CODE button
revo search SIMPLE customer
# Make a low-level API request
revo request GET /api/component/list
revo request POST /api/component '{"name":"Customer Card"}'
# Debug a local component in the sandbox
revo component debug <componentId> --read-only=true --stream
# Full execution requires a component execution role:
revo component debug <componentId> --read-only=falseUse low-level request carefully; it bypasses the higher-level migration planner.
HTTP diagnostics
Enable a per-invocation HTTP trace in .revoengine/revo.lock.json:
{
"schemaVersion": 2,
"diagnostics": {
"httpTrace": {
"enabled": true
}
}
}Each enabled invocation writes .revoengine/logs/http-trace/<timestamp>.json and prints its report path to stderr. A trace entry covers one client request and its entire retry loop, including waits; it records the final status and duration. Finalization waits up to five seconds for outstanding requests and marks any that remain incomplete.
Traces retain only known static API route segments, method, status, timing, payload byte counts, and diagnostic stage or resource labels. They omit URL origins and query strings, dynamic IDs and names, Stable Keys, profile names, headers, correlation values, bodies, error messages, and command arguments. An error contributes its class name only. This keeps credentials and instance-specific identifiers out of trace reports.
Each CLI invocation shares one queue of up to six active API requests across resource clients. An available slot starts the next waiting request, with queued POST, PUT, PATCH, and DELETE requests taking priority over reads. Waiting requests can be cancelled and expire after five minutes; the HTTP timeout starts when the first network attempt begins. Retry backoff releases the slot before the next attempt is queued. Endpoint and general resource inventories use order-preserving workers that refill free slots without batch barriers. Database View inventory deduplicates active list IDs, fetches complete details, excludes deleted readbacks, and sorts by ID. Failed detail reads fail the inventory instead of silently producing a partial snapshot. Streaming debug and terminal connections use separate transport paths.
Safe HTTP reads (GET, HEAD, OPTIONS), including plan/deploy inventory reads, default to two retries, for up to three attempts. Transport or response-read errors and HTTP 429, 500, 502, 503 and 504 can be retried; validation, permission and version-conflict responses are not. Mutations default to no transport retries, so an uncertain write is not automatically replayed. Explicit client/request retry budgets, including zero, take precedence; there is no global retry flag. Aborted requests are not retried. Backoff starts at 100 ms with jitter, respects a bounded numeric Retry-After, releases the transport queue slot while waiting, and shares one request timeout across attempts. Existing mutation-specific opt-ins and fresh live validation remain responsible for their own recovery.
Update notifications
Interactive runs check npm for a newer version at most once every 24 hours. Disable the check with --no-update-notifier or REVO_NO_UPDATE_NOTIFIER=1.
Local development
npm install
npm run build
npm run check
npm test
npm pack --dry-runService Accounts in migration
revo metadata plan --resources service-account and revo metadata apply assign Stable Keys explicitly.
revo service-accounts pull --all, plan --all, and push --all --yes manage the portable definition.
The same family is included in revo pull --all, repository diff/plan/deploy, and named migrate plan/run/verify.
Select it alone with --resources service-account. Named profiles (--project/--env), URL/token environment variables,
and the ordinary authenticated runtime work through the existing config resolution. --force retains the existing drift policy.
The portable manifest contains the Stable Key, name, and a single Group, Service Account or built-in System management principal. The manifest field owner remains the portable dependency policy; Platform 1.6.4 writes it as a restricted management access ACL, with target UUIDs and no legacy ownerId/ownerType fields.
Group USER owners and Job Template triggerUser references to Service Accounts are resolved to target userId at write time.
Use owner: { "type": "system" } for a Group or Service Account managed by the physical built-in System account. The CLI reads the target account with UUID 00000000-0000-0000-0000-000000000000 and verifies serviceAccount: true and active state before any dependent write; an unmigrated instance is blocked. System itself is never exported as a managed account, assigned a Stable Key, updated or pruned. An ordinary account named System remains managed through its own Stable Key.
Human principals are reported for manual configuration; an account with a human owner needs a target account with the exact Stable Key before it can be adopted.
A missing or circular owner dependency blocks writes. Groups and accounts are created in dependency order before their consumers.
API keys and secrets cannot be exported. Every newly created Service Account is active and has platform access but has no migrated credentials.
The push result contains credentials: { stableKey, environment, policy: "target-local", status: "configure-manually" }.
Existing target secrets remain local and are never compared, copied, rotated, or exposed in manifests, plans, lock files, or HTTP trace.
Execution memberships, unrestricted or multiple-principal management ACLs, other account metadata, and metadataAdvanced remain target-local. Existing self access is preserved when a portable management principal changes; name/metadata updates omit the ACL. Complex target ACLs cannot be replaced through the single-principal manifest, including with --force.
Runtime execution policies
Endpoint execution still identifies its Component. The separate optional executionPolicy selects runtime authority and, for Endpoints, the account exposed through api.currentUser:
{
"executionPolicy": {
"mode": "SERVICE_ACCOUNT",
"serviceAccount": { "kind": "system" },
"currentUser": "CALLER"
}
}A normal Service Account uses serviceAccount: { "kind": "service-account", "stableKey": "Ops/Robot#abc12345" }. The target account must be active and have platform access. Plans include accounts created earlier in a multi-resource migration; writes resolve fresh target IDs and account state. The manifest and lock contain portable references, never source account UUIDs.
Both Endpoints and Job Templates accept mode: "INHERIT", "CALLER" or "SERVICE_ACCOUNT". Non-Service Account modes omit serviceAccount. Endpoint currentUser independently accepts "INHERIT", "CALLER" or "EXECUTION_ACCOUNT". Job Templates expose the executor through api.currentUser and record the initiator separately; unattended jobs require a Service Account. Set instance defaults in Settings > Endpoints. Instance settings are target-local and are not copied by resource migration.
Job Template manifests retain the legacy executionPrincipal reference for existing definitions, including { "kind": "system" }. Use either this field or executionPolicy, never both. Writing an explicit policy clears the legacy triggerUser alias. Definitions without either field retain target-local execution configuration. An explicit policy pointing to a human or an unkeyed ordinary account cannot be exported as a portable definition.
Selecting System or another instance administrator execution account approves the current instance Component and library code. Later source changes require administrator approval; ordinary editors cannot replace approved code silently. Authority selection preserves the caller in the audit trail.
Agents and Agent Plugins
Agents and registry plugins participate in the same repository pull/plan/deploy and named migration flow as Service Accounts. Enable the families explicitly, or use all:
revo project update --resources component,service-account,group,agent-plugin,agent
revo metadata plan --project source --resources agent-plugin,agent
revo metadata apply --project source --resources agent-plugin,agent --yes
revo pull --project source --resources component,service-account,group,agent-plugin,agent --yes
revo plan --project target --json
revo deploy --project target --yesManifests live under Agents/<name>-<key-suffix>/agent.json and AgentPlugins/<name>-<key-suffix>/agent-plugin.json in the configured workspace. The shared lock stores portable hashes and per-instance baselines. Dedicated revo agents and revo agent-plugins commands support pull, plan, and push with --all, --project, --json, and --force; push requires --yes or interactive confirmation. Dedicated plan also supports --strict.
Plugin definitions cover tools, mcp_server, skill, and package, including routing, governance, schemas, toolsets, Markdown content and content token budgets. Read-only marketplace package metadata is excluded from portable manifests. Component references use componentStableKey; skill/package dependencies use pluginStableKeys. Agent config/policy attachments use pluginStableKeys, eager attachments use eagerPluginStableKeys, and executionPrincipal resolves a Service Account or the built-in System account. Portable Group and Service Account owners are resolved on the target; human ownership remains target-local. Plugins are deployed in dependency order before Agents. Missing/duplicate keys, unmanaged name/slug collisions, cycles and invalid references block writes even with force.
Agent config.memory, workspace files, conversations, runs, terminal selection, automation recipients, rollout state, avatars and MCP secrets/Vault UUIDs are instance-local. Export omits them; updates preserve target settings, including memory checkpoints, while replacing managed configuration. New Agents use the API's ordinary creation defaults and are not ru
