Skip to main content

Arrival CLI

Last Updated:25.08.2026Version1.0

Overview

The Arrival CLI manages Arrival.Space spaces as local workspaces: pull a space into files, edit its room configuration, entities, plugins, and assets with your own tools or an AI agent, validate the changes on the server, and push them back to the live space. A workspace is plain files, so it can be committed to Git and reviewed like any other project.

The CLI uses the same materialize, validate, and sync-back workflow as the in-app space agent, without AI and without live hot reload. The source code is available in the Arrival plugins repository.

Working with an AI agent

Give it this prompt — it reads this page and sets itself up:

Please set yourself up to use the arrival CLI: https://codex.arrival.space/api/cli

Requirements

  • Node.js 18 or newer
  • An Arrival.Space account with permission to edit the target space
  • A browser for the OAuth login flow

Installation

Clone the Arrival plugins repository, then install the CLI dependencies:

git clone https://github.com/arrival-space/arrival-plugins.git
cd arrival-plugins/tools/arrival-cli
npm install

If you already have the repository locally, skip the clone and cd tools/arrival-cli.

To make the arrival command available on your PATH, create a local npm link:

npm link

You can also run the CLI directly from the project directory with node index.js.

Authentication

Run:

arrival login

The CLI opens a browser and uses OAuth 2.0 Authorization Code with PKCE. After successful authentication, the token is stored locally in:

~/.arrival/config.json

The file contains the selected server, the OAuth client ID, and the access token. The file is written with restricted permissions where supported. The default server is:

https://api-live.arrival.space

arrival logout removes the locally stored token. It does not revoke the API key on the server. Manage or revoke API keys from the Arrival.Space account settings.

Command reference

arrival spaces

Lists the spaces available to the authenticated user. Use --search to filter by title or description:

arrival spaces
arrival spaces --search gallery

arrival create <title>

Creates a new space and prints its id. Use --pull to check the new space out into a local workspace in the same step:

arrival create "Photo wall"
arrival create "Photo wall" --pull

Options: --description <text>, --privacy Open|Closed (default Closed), --type infinite|hub (default infinite), --pull, and --dir <path> to choose where --pull writes the workspace.

arrival pull <spaceId>

Downloads a space into a local workspace. By default, the workspace is created in ./<spaceId>:

arrival pull 45637586_1234

Use --dir to select another target directory. The target must be empty unless --force is supplied:

arrival pull 45637586_1234 --dir ./spaces/gallery
arrival pull 45637586_1234 --force

The pull also includes the generated workspace map and, when available, the plugin documentation and examples under reference/.

arrival status

Shows local changes since the last pull or successful push. The command does not access the network:

arrival status
arrival status --dir ./spaces/gallery

Changes are reported as modified, added, or deleted files. The command compares the working tree with the baseline in .arrival/base/space.

arrival diff

Shows line-level changes for text files since the last pull or successful push:

arrival diff

Binary files are reported without a line diff. Use Git for full history, binary comparisons, and multi-file review workflows.

arrival validate

Performs a server-side dry run without applying changes:

arrival validate
arrival validate --dir ./spaces/gallery

Validation errors include the affected file and the server-provided message. A workspace must contain .arrival/manifest.json, so validation is intended to run inside a directory created by arrival pull.

arrival push

Sends the changed files to the live space:

arrival push

Only added and modified files, together with explicit deletions, are sent. Use --dir for a workspace outside the current directory:

arrival push --dir ./spaces/gallery

Use --dry-run to validate the pending changes without applying them:

arrival push --dry-run

If files were deleted locally, the CLI requires an explicit confirmation:

arrival push --force

The command reports applied and failed server operations. If the server returns a partial result, the command exits with an error status so it can be handled by scripts or CI workflows.

caution

Pushing changes updates the live space for the next load. It does not automatically refresh a browser that already has the space open. Reload the space to see the changes.

arrival upload <file>

Uploads a large asset directly to the CDN using the presigned upload flow:

arrival upload ./assets/museum.glb

The command supports models, splats, images, audio, and video files. It prints the resulting URL or resource key. Reference the result from an entity, then run arrival push.

The upload bytes go directly from the CLI to storage. They do not pass through the user server. If the server needs to process the file, the CLI polls the associated job until it completes or fails.

Workspace structure

A pulled workspace has the following structure:

<spaceId>/
space/
room.json # RoomInfo and space-level configuration
entities/*.json # One JSON file per entity
plugins/<name>.mjs # Single-file ArrivalScript plugins
plugins/<name>/ # Multi-file plugins
assets/<name> # Local binary assets referenced by the space
README.md # Generated overview of the space
.arrival/
manifest.json # Pull baseline and space metadata
base/space/ # Local baseline used by status and diff
AGENTS.md # Workspace guidance and plugin references
CLAUDE.md # Optional assistant guidance
reference/ # Optional plugin API documentation and examples

Only files under space/ are editable space content and are considered for a push. AGENTS.md, CLAUDE.md, and reference/ are generated guidance and should be treated as read-only. Do not edit .arrival/manifest.json or .arrival/base/space. The baseline is maintained by the CLI and is updated after a successful push.

tip

Read space/README.md before editing a workspace. It is a generated map of the current space and lists entities, plugins, positions, and shared plugin usage.

Entities

Entity files use the general shape:

{
"id": "entity-id",
"type": "UserModelEntity",
"data": {
"position": { "x": 0, "y": 1.5, "z": -3 },
"rotation": { "x": 0, "y": 90, "z": 0 },
"scale": 1,
"glbUrl": "assets/museum.glb"
}
}

The exact fields depend on the entity type. Space-level settings belong in room.json. Settings for a specific model, plugin, gate, spawn point, or other entity belong in that entity's JSON file.

Edit the data object. The state object contains runtime or multiplayer state and is not the authoring source for a workspace. A normal validation pass expects every entity JSON file to contain an id, a type, and an object-valued data field.

Plugins

Plugins are stored as .mjs files under space/plugins/. A plugin normally exports one class extending ArrivalScript and defines a unique static scriptName. Multi-file plugins use a directory under space/plugins/, normally with an index.mjs entry point. The pulled reference/ directory contains the ArrivalScript API, examples, and authoring guidance when the server provides them.

Plugin host entities reference a plugin through data.glbUrl using a workspace-relative value such as plugins/my-plugin.mjs. When a plugin is referenced by multiple entities, editing the shared plugin file changes all of those entities after the next push. To change only one host, copy the plugin to a new filename and update that entity's reference.

Plugin files with identical content can be merged into one shared plugin during apply. If two copies are intended to remain separate, they must contain different content or use separate authoring logic.

Assets

Asset files used by a plugin or entity can be stored under space/assets/ and referenced with the literal assets/<name> token. Only real asset files belong in this directory. Documentation files, placeholders, and .gitkeep files can fail validation when they are treated as assets.

For large models, splats, images, or other binary files, use arrival upload and set the returned URL or resource key on the appropriate entity. The generated README.md and the reference documents show which files are used by the current space.

Validation rules for references

Before a push, arrival validate checks the workspace content on the server. The validation includes:

  • JavaScript module parsing for .mjs files
  • Required entity shape with id, type, and object-valued data
  • Plugin references under plugins/
  • Asset references under assets/
  • Missing or dangling file references

Fix validation errors before pushing. A broken reference can prevent the complete changeset from being accepted.

Cutscenes and .path files

Animations, object movement, camera fly-throughs, and other keyframe sequences can be authored as .path files. A .path file is a JSON sequence asset and does not require a plugin or the in-app Sequence Editor.

The CLI workflow is:

  1. Create a valid .path file using the sequence format in the bundled reference/docs/cutscenes-via-mcp.md documentation.
  2. Upload the file with arrival upload animation.path.
  3. Create or edit an entity that references the uploaded .path resource.
  4. Push the entity changes with arrival validate followed by arrival push.

Use a plugin when the behavior needs custom logic, input, UI, physics, or runtime interaction. Use a .path cutscene when the requirement is primarily animation or a camera sequence without custom interaction. Playback settings such as loop and autoplay belong to the cutscene entity configuration, not inside the .path file.

caution

The onInstall() plugin hook runs for an in-app install. It does not run for CLI or MCP deployments. Set plugin parameters directly in the entity data when deploying through the CLI.

For the full runtime API, multiplayer behavior, properties, sequences, and examples, use the documentation bundled in reference/ or the Arrival plugins repository.

Change and push behavior

The CLI normalizes line endings to LF for supported text files before comparing and sending them. This prevents Windows CRLF changes from appearing as unrelated content changes. Binary files are transferred without text normalization.

The current changeset flow sends only changed files. Text files are sent as text content, while binary files are sent as base64 content. Deletions are sent explicitly and require --force during a normal push.

The server validates the complete changeset before applying it. Applying the individual operations is best effort, so a push can report a partial result if one or more writes fail. The server also takes a snapshot before applying a push, which makes a bad push undoable in the Arrival.Space application.

Limits and current behavior

  • arrival dev and live hot reload are not available yet.
  • The CLI currently uses a last-writer-wins workflow. Changes made in the live space after a pull can be overwritten by a later push.
  • A push does not merge concurrent edits automatically.
  • Deleting a local file can delete the matching live entity after arrival push --force. Review arrival status first.
  • Large assets should be uploaded with arrival upload instead of being committed as ordinary workspace files.
  • The CLI is intended for owners and users with edit permission. Read-only users cannot apply changes.

Troubleshooting

Not logged in

Run arrival login. If the space belongs to another server environment, log in again with the matching --server value.

The target directory is not empty

Choose another directory or confirm that it can be overwritten with arrival pull <spaceId> --force.

Validation fails

Run arrival validate and inspect each reported file and message. Common causes include invalid JSON, unsupported entity data, missing plugin files, or invalid references to assets.

A push would delete files

Review the output of arrival status. Restore files that were deleted accidentally, or run arrival push --force only when the deletions are intentional.

Changes are not visible

The push applies changes for the next load. Reload the open space in the browser.