# Tray Sync CLI

CLI tool to clone Tray projects and related assets to a local directory, and promote them between Tray environments

CLI tool to clone tray projects and related assets on local machine which then can be used with version control system or promoted to different tray environment

## Requirements

* Node.js >= 22

## Install

```bash
npm install -g @trayai/tray-sync-cli
```

## CLI Commands

| Command                                                  | Description                                                                                              |
| -------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
| `tray init -r <region> -w <workspace-id>`                | Scaffold a new tray-sync-cli repository                                                                  |
| `tray pull`                                              | Export tracked projects from Tray and materialise them on disk                                           |
| `tray promote <env>`                                     | Reconstruct local projects and import them into a target Tray environment                                |
| `tray status`                                            | Local checksum-based drift detection (zero API calls)                                                    |
| `tray project remove <project-id>`                       | Untrack a project and delete its local directory (project scope only)                                    |
| `tray project list`                                      | List managed projects with directory names and last-pulled timestamps                                    |
| `tray env add <name>`                                    | Add a target environment                                                                                 |
| `tray env list`                                          | List source and target environments with connectivity status                                             |
| `tray env remove <name>`                                 | Remove a target environment and its per-project mappings                                                 |
| `tray env discover [env]`                                | List an env's projects (id + name, default) and/or matching authentications — `env` defaults to `source` |
| `tray env resolve <env-name>`                            | Report unresolved auth requirements for a target env, with candidate matches                             |
| `tray auth set -w <workspace-id> -r <region> -t <token>` | Set (or overwrite) a workspace's token                                                                   |
| `tray auth remove -w <workspace-id>`                     | Remove a workspace's stored token                                                                        |
| `tray auth list`                                         | List workspaces with stored credentials                                                                  |
| `tray --version`                                         | Print the installed CLI version                                                                          |
| `tray --help`                                            | Show help for the CLI or a specific command                                                              |

## Getting started: a first promotion, end to end

This is a concrete, step-by-step walkthrough of the most common workflow: pull a project from a source workspace, and promote it to a target workspace. It uses a two-org example (`DEV_ORG` → `PROD_ORG`) since that's the realistic setup for most customer pipelines, but everything here works identically for two workspaces within the same org.

### 1. Pick a source and a target environment

Decide which workspace you're pulling *from* (the source) and which one you're promoting *to* (the target). For this guide:

* Source: `DEV_ORG`
* Target: `PROD_ORG`

You'll need each workspace's ID and region, and a Tray API token with access to it.

### 2. Create a local directory for this project

```bash
mkdir -p ~/tcli-projects/my-first-tcli-project
cd ~/tcli-projects/my-first-tcli-project
```

Everything the CLI manages — `tray.yaml`, `environments.json`, and every pulled project — lives under this directory.

### 3. Initialize the repository against your source workspace

```bash
tray init -r <region> -w <your-SOURCE-workspace-id> -t <your-token> -p <project-id-to-pull>
```

Example:

```bash
tray init -r us1 -w 11111111-1111-1111-1111-111111111111 -t <token> -p <project_id>
```

Don't know the project ID yet? Run `init` without `-p` (an empty scaffold is fine), then:

```bash
tray env discover
```

lists every project in your source workspace — id + name — so you don't have to dig it out of Tray's UI.

A few notes on the flags:

* **`-p <project-id>`** pulls that project immediately after scaffolding. Omit it and `init` just scaffolds an empty repository — you can pull specific projects later with `tray pull -p <project_id>`, which tracks and pulls it in one step.
* **`-s workspace`** (i.e. `tray init -s workspace -r <region> -w <id> -t <token>`, omitting `-p`) switches to workspace scope: every project in the workspace is pulled automatically, and `tray pull` keeps that mirror in sync going forward (including removing projects deleted from Tray). `-p` is ignored in this scope.
* **`-t <token>`** is optional at `init` time. If you skip it (or need to replace it later — tokens expire), set or refresh it any time with:
  ```bash
  tray auth set -w <workspace-id> -r <region> -t <token>
  ```
  `tray auth set` always overwrites whatever token was stored for that workspace — no separate "force" flag needed.

At this point you have a local mirror of your source project(s), configured against `DEV_ORG`.

### 4. Add your target environment

Now register where you want to promote *to*:

```bash
tray env add <env-name> -r <region> -w <target-workspace-id> -t <target-token>
```

Example:

```bash
tray env add PROD_ORG -r us1 -w 22222222-2222-2222-2222-222222222222 -t <prod-token>
```

You can add more than one target environment this way (e.g. `STAGING_ORG`, `PROD_ORG`) — each gets its own entry in `environments.json`.

Same as `init`, providing `-t` here stores the token in `~/.tray/credentials.json` immediately. You can always set or refresh it independently with `tray auth set -w <target-workspace-id> -r <region> -t <token>`.

### 5. Promote

You're ready to import your local projects into the target environment. A few common invocations:

```bash
# Recommended first: see what would happen without changing anything.
tray promote --to PROD_ORG --dry-run

# Promote a single project.
tray promote --to PROD_ORG -p <project-id>

# Promote multiple specific projects.
tray promote --to PROD_ORG -p <project-id-1> <project-id-2>

# Promote every project tracked in this directory (see them with `tray project list`).
tray promote --to PROD_ORG
```

### Important: every project needs a `target_project_id` per environment

The first time you promote a given project to a given environment — or whenever the target project changes — you need to tell the CLI which project in the target workspace this should import into.

Each pulled project has its own `mappings.json`, at:

```
projects/<project-id>--<project-name>/mappings.json
```

You don't have to create this file by hand — `tray pull` / `tray init` already scaffold it for you (with a placeholder entry showing the full shape) the first time a project is pulled. What you do need to do is edit the target environment's entry and fill in the real `target_project_id`:

```json
{
  "version": 1,
  "environments": {
    "PROD_ORG": {
      "target_project_id": "<project-id>",
      "authentications": {},
      "config": {},
      "connectors": [],
      "services": []
    }
  }
}
```

`target_project_id` here is the ID of the project **in `PROD_ORG`** that this local project should be imported into.

#### Authentications

If any workflow in the project you're promoting uses an authentication, you'll also need to map it. Tray tracks this per **authentication group** — a logical "auth slot" a workflow step references — not the authentication ID itself. When `tray promote` finds one it doesn't recognize, it tells you and scaffolds a placeholder into `mappings.json` for you:

```json
{
  "version": 1,
  "environments": {
    "PROD_ORG": {
      "target_project_id": "<project-id>",
      "authentications": {
        "<source_authentication_id>": "MISSING_TARGET_AUTHENTICATION"
      },
      "config": {},
      "connectors": [],
      "services": []
    }
  }
}
```

The key (`<source_authentication_id>`) is the source-side auth group ID — leave it as-is. Replace the placeholder value with the ID of the actual authentication in your **target** environment (`PROD_ORG`) that should serve that slot — you can find this in Tray's app, under that workspace's authentications. For example:

```json
"authentications": {
  "<source_authentication_id>": "<resolved_target_authentication_id>"
}
```

#### If you get it wrong

If a required field is missing or still holds a placeholder value, `tray promote` refuses the attempt and tells you exactly what's outstanding and where to fix it — it won't silently promote a project into the wrong target, or with an unresolved authentication.
