npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@tarout/cli

v1.15.2

Published

Tarout CLI: the Saudi cloud platform for coding agents

Readme

Tarout CLI

npm version License: MIT

The official command-line interface for Tarout - the Saudi cloud platform built for coding agents. Your agent writes code and defines infrastructure; Tarout provisions it instantly.

Installation

curl -fsSL https://tarout.sa/install.sh | sh

Or with other package managers:

# Using npm
npm install -g @tarout/cli

# Using yarn
yarn global add @tarout/cli

# Using pnpm
pnpm add -g @tarout/cli

# Using bun
bun add -g @tarout/cli

Quick Start

# 1. Login via browser (opens authentication page)
tarout login

# 2. Inspect and deploy from your project root
tarout deploy --wait --source upload

# The first deploy prompts to create or link an app, and to create detected resources.

Cloned a repo that already deploys to Tarout? Its .tarout/ folder arrives with only a .gitignore, because your login is not committed. Run tarout login (or straight tarout deploy, which signs you in and asks which app to deploy to). On a machine with no browser, such as a server over SSH, run tarout login --device and approve its one-time code from any browser. In CI, set TAROUT_TOKEN to a key from https://tarout.sa/dashboard/agent/keys. To commit the login instead, see Sharing one login through git.

Call any API (tarout call)

Beyond the curated commands, tarout call reaches every platform procedure directly - the same control surface exposed via REST and MCP:

tarout call --list                 # discover all callable procedures + type
tarout call application.create --input '{"name":"my-app"}' --json
tarout call deployment.all --input '{"applicationId":"app_123"}'

MCP server

tarout-mcp is a local MCP server that gives coding agents (Claude Code, Cursor, Claude Desktop) the CLI's capabilities as first-class tools: deploy from the current directory, sync .env, run SQL against Postgres, schedule cron tasks (job_*), run a command in an app's container (app_exec), explain what a deploy would build (app_explain_build), deploy a ready-made app from a template (template_*), check parked approval requests (approvals_*), map a project in one read (agent_manifest), read the agent activity timeline and credentials (agent_events, agent_sessions), switch org/project/env, upgrade billing, and more, with a call escape hatch covering the entire platform API.

Set up every agent at once: tarout agent setup

tarout agent setup             # shows the plan, asks once
tarout agent setup --dry-run   # only shows the plan and the changes

Finds the coding agents on this machine (Claude Code, Codex, Cursor, VS Code / GitHub Copilot, Devin Desktop / Windsurf, OpenCode, Gemini CLI), installs the tarout-deploy and tarout-domains skills, and registers the hosted MCP server below. It uses OAuth, so no credential is written to disk and no sign-in is needed first; the command then prints each agent's sign-in step (/mcp in Claude Code, codex mcp login tarout, and so on).

  • --local registers the stdio tarout-mcp instead of the hosted URL.
  • --agents claude,cursor, --skills-only and --mcp-only narrow the run.
  • --yes applies without asking (required when stdin is not a TTY); --json returns one result per agent.

Only agents already installed are touched, other MCP servers are left alone, and an existing tarout entry with different settings is replaced only with --yes. Re-running reports unchanged. tarout agent init is the per-project counterpart (CLAUDE.md / AGENTS.md plus the Claude Code permission allowlist).

Map a project in one read: tarout agent manifest

tarout agent manifest                  # compact tree of the active project
tarout agent manifest --env-names      # list env var names instead of a count
tarout agent manifest --project api --json

One call returns the whole project: every app with its status, URL, source, custom domains, linked databases, env var names (never values) and scheduled job count, plus the project's databases, buckets and domains. --json prints the full manifest in the standard envelope. Unlike the rest of tarout agent, it needs you signed in. The MCP server exposes the same read as the agent_manifest tool ({ projectId? }).

Agent credentials and activity: tarout agent sessions / tarout agent events

tarout agent sessions                  # OAuth connections and API keys
tarout agent events                    # the 30 most recent agent actions
tarout agent events --since 1h         # everything in the last hour (up to 500)
tarout agent events --follow           # keep printing new ones (Ctrl+C stops)
tarout agent events --follow --json | jq .procedure

sessions lists this account's agent credentials in the organization, in two groups: OAuth connections (hosted MCP connectors: client, tier, status, created, last used, expires) and API keys (name, prefix, tier, status, last used). Status is active, paused (agent access switched off) or expired. The key itself is never shown. Revoking or pausing is done by a human in the dashboard (Agent > Keys, https://tarout.sa/dashboard/agent); this command deliberately does not.

events is the timeline the dashboard's Agent page shows: every change an agent made through the CLI or MCP (reads are not recorded), with the time, the key or OAuth client that acted, the procedure, the surface (cli or mcp), ok or error, and the error. Rows print oldest first. --limit takes 1 to 500 (default 30, or 500 with --since); --since takes 15m, 2h, 7d or 1h30m. --follow polls every 3 seconds, prints only rows it has not printed before, and exits 0 on Ctrl+C. --json prints one envelope, or with --follow one JSON object per event per line (NDJSON, "type": "agent_event").

Both are organization-level: they need you signed in but no project. When the platform refuses a read (an organization agent-policy rule, for example), they exit 5 with PERMISSION_DENIED and the dashboard link rather than a stack trace. The MCP server exposes the same reads as agent_events ({ limit?, since? }, up to 100 rows) and agent_sessions. agent_sessions exists because user.listApiKeys was already reachable over MCP through the platform surface and call; it adds no new access.

No install: the hosted connector

If you just want an agent talking to Tarout, you do not need this package at all. The platform hosts an MCP server that authenticates with OAuth, so the URL is the whole setup:

https://tarout.sa/api/mcp
  • claude.ai / ChatGPT / Claude Desktop: Settings, Connectors, add a custom connector, paste the URL. Leave client ID and secret empty.
  • Claude Code: claude mcp add --transport http tarout https://tarout.sa/api/mcp
  • Codex: codex mcp add tarout --url https://tarout.sa/api/mcp then codex mcp login tarout
  • Cursor (~/.cursor/mcp.json): "tarout": { "url": "https://tarout.sa/api/mcp" }

That path offers a curated tool set plus a tarout_call escape hatch. Install the CLI below when you want the local toolset, deploys from the current folder, and per-project credentials. Full guide: https://tarout.sa/docs/getting-started/connect-your-ai

Setup (local stdio server)

Claude Code

npm i -g @tarout/cli
claude mcp add tarout -- tarout-mcp

Cursor (~/.cursor/mcp.json):

{
	"mcpServers": {
		"tarout": { "command": "tarout-mcp" }
	}
}

Claude Desktop (claude_desktop_config.json):

{
	"mcpServers": {
		"tarout": { "command": "tarout-mcp" }
	}
}

Auth

The server reuses the credential tarout login wrote: the project's .tarout/auth.json first, then the machine-wide profile. Launch it from the project directory, or pass a path argument on the tools that accept one, so it resolves the right project. If nothing resolves, tool calls return a structured AUTH_ERROR.

Bootstrap URL

Point an agent at https://tarout.sa/agent-setup/prompt.md and it will install the CLI + register the server in one shot.

Commands

Authentication

| Command | Description | |---------|-------------| | tarout login | Authenticate via browser; writes this project's .tarout/auth.json | | tarout login --token <key> | Same, headless, with no browser | | tarout login --device | Sign in from a host with no browser (SSH, a container): approve the printed one-time code in any browser | | tarout login --commit-token | Commit this project's .tarout/auth.json with the repo (details); --no-commit-token undoes it | | tarout register | Create a new account via browser | | tarout token <key> | Alias for login --token | | tarout token:create | Mint a new API key for the current account | | tarout logout | Sign this project out (--global for the machine-wide login) | | tarout whoami | Show current user, organization, project, and credential scope |

Credentials are per project - see Configuration.

# Login with a custom API URL (e.g. staging)
tarout login --api-url https://staging.tarout.sa

# Store the credential machine-wide instead of in this project
tarout login --global

# On a server over SSH or in a container: open the printed URL on any device,
# enter the code, and approve it (only approve a code you started yourself)
tarout login --device

# Ignore this project's credential for a single command
tarout whoami --global-auth

Applications

| Command | Description | |---------|-------------| | tarout apps list | List all applications | | tarout apps create <name> | Create a new application | | tarout apps delete <app> | Delete an application | | tarout apps info <app> | Show application details | | tarout apps open <app> | Open application URL in browser |

# List apps as JSON
tarout apps list --json

# Create an app
tarout apps create my-api

# Delete without confirmation
tarout apps delete my-api --yes

Deployment

| Command | Description | |---------|-------------| | tarout deploy [app] | Deploy an application | | tarout deploy:status <app> | Check deployment status | | tarout deploy:cancel <app> | Cancel running deployment | | tarout deploy:list <app> | List recent deployments | | tarout deploy:retry <app> | Retry a failed deployment without rebuilding | | tarout deploy:rollback <app> | Roll back to a previous deployment |

# Inspect and deploy the current folder
tarout deploy --wait --source upload

# Deploy and wait for completion
tarout deploy my-app --wait

# Check deployment status
tarout deploy:status my-app

Retrying a failed deployment

A deployment can build a good image and then fail after the build - the host could not pull the image, a registry token went stale, the target server hiccuped. Deploying again from scratch re-resolves the commit, re-runs preflight and re-enters the builder just to arrive at the identical image.

tarout deploy:retry my-app --wait

This reuses the image the failed deployment already produced and re-runs only the deploy step. It refuses deployments that failed during the build, because there is no image to reuse - fix the code and run tarout deploy instead.

tarout deploy inspects the current folder for database, file storage, and Git signals before it asks questions. A folder with a github.com remote deploys from that repo, so every push redeploys: the CLI binds it when the organization's GitHub connection can read the repo. When it cannot, a terminal run offers to connect GitHub and waits for it; an agent or script run uploads the folder and its result names the fix, tarout providers github connect --wait --app <app>, which opens Tarout's GitHub setup page, waits until GitHub can read the repo, and binds the app. A folder with no GitHub remote is uploaded, so users without GitHub can still deploy.

Logs

| Command | Description | |---------|-------------| | tarout logs <app> | View application logs |

# Fetch a snapshot of recent logs
tarout logs my-app

# Filter by log level
tarout logs my-app --level error

# Logs from last hour
tarout logs my-app --since 1h

# Last 100 lines
tarout logs my-app --limit 100

# Follow continuously (Ctrl+C to stop)
tarout logs my-app --follow

--follow polls every 3s and prints only new lines - the platform exposes runtime (container) logs as a snapshot query, not a stream. Under --json it emits JSON Lines, one object per line, so agents can consume it incrementally.

Environment Variables

| Command | Description | |---------|-------------| | tarout env <app> list | List environment variables (masked) | | tarout env <app> set <KEY=value> | Set an environment variable | | tarout env <app> unset <KEY> | Remove an environment variable | | tarout env <app> pull | Download variables as .env file | | tarout env <app> push | Upload variables from .env file |

tarout env <app> list shows an AVAILABLE column: build + runtime or runtime only.

This is not cosmetic. Only public-prefixed keys (NEXT_PUBLIC_*, VITE_*, EXPO_PUBLIC_*, PUBLIC_*, GATSBY_*, NUXT_PUBLIC_*, REACT_APP_*) are passed to the build - everything else, including DATABASE_URL and every secret, exists only in the running container, so that secrets can never land in build logs or image history. A build step that reads a runtime-only variable sees nothing, however correctly you set it. If your build genuinely needs one, move that step to a releaseCommand instead.

# Set a variable
tarout env my-app set DATABASE_URL=postgres://...

# Set multiple variables
tarout env my-app set API_KEY=xxx SECRET=yyy

# Download to .env file
tarout env my-app pull

# Upload from .env file
tarout env my-app push

Run a local command with the app's env: tarout run

tarout run -- npm test
tarout run --app api -- npx prisma migrate deploy

Runs the command on your machine with the linked app's environment variables injected (--app picks another app by id, name or slug). Put the command after --: it starts directly, without a shell, and every argument reaches it exactly as typed, spaces and quotes included. Use sh -c "..." when you need pipes or shell syntax.

  • Managed database variables (DATABASE_URL, PGHOST, ...) carry the database's external connection details. A database without external access is left unset, and stderr prints how to enable it (tarout db external-access <db> --enable); the platform's hidden-route placeholder is never injected.
  • Values are never printed.
  • The command's output and exit code pass straight through, unmapped; a command killed by a signal exits 128+n (130 for SIGINT). Ctrl+C and SIGTERM are forwarded to the command. --json does not apply and is rejected.

tarout dev and tarout build resolve their variables the same way.

Run a command in the app's container: tarout exec

tarout exec -- ls -la "/app/my dir"
tarout exec -- "npm run migrate && echo ok"
tarout exec --app api --timeout 300 -- node scripts/backfill.js

Runs one command inside the app's running container (the linked app, or --app) and prints its stdout and stderr. Put the command after --:

  • One argument is sent as-is and runs as a shell line (sh), so quote the whole line locally when it uses pipes, && or $VARS.
  • Several arguments are each quoted for sh (single quotes, with ' written as '"'"'), so the container sees exactly the argv you typed.

It is one request, not a stream: output arrives when the command finishes, and the platform keeps the first 4,000 and last 8,000 characters of each stream. --timeout is 1 to 300 seconds (default 60). The command's exit code is tarout exec's exit code (124 when it timed out). With --json you get one { success, data } envelope carrying exitCode, stdout, stderr, truncated, timedOut, durationMs and ok (exit 0, no timeout), and the exit code is 0 whenever the command ran, whatever its own exit code (the same convention as tarout jobs run); a non-zero exit then means the CLI or platform failed.

Owners and admins only. An operator-tier agent key gets NEEDS_APPROVAL with a tarout approvals wait <id> next step; once a human approves, the command runs but its output is not returned. There is no interactive shell in the CLI: -it prints the link to the dashboard console (Application > Console).

Explain what a deploy would build: tarout build --explain

tarout build --explain
tarout build --explain --app api --wait 300 --json

Asks Tarout what a deploy would build and whether it would get past the pre-build checks, without building, deploying or changing anything. It reads the app's configured source on Tarout (the tracked Git branch, or the last uploaded archive), not your local files, so push (or upload) first to explain local changes. Nothing runs locally. The report shows the source (repo@branch and short commit), detected kind and build type, a summary, providers and toolchain versions, build steps with their commands, the start command, the port, and the names of build-time variables (never values), then warnings and errors.

Exit status: 0 when the plan is ok, 12 (BUILD_FAILED) when a deploy would fail, and 11 (EXPLAIN_PENDING) when Tarout is still inspecting after --wait seconds (default 120, max 600; run it again to pick up the same inspection). --json prints one envelope carrying the platform's full answer. The platform allows 10 explanations a minute per user. The MCP tool app_explain_build does the same.

Templates

Deploy a ready-made app (an image, its port, the environment variables it reads, and a managed PostgreSQL database when it needs one) into the active project with one command.

| Command | Description | |---------|-------------| | tarout template list | List the templates: code, name, category, and whether it creates a Postgres database | | tarout template info <code> | Image, port, database, docs link, and every variable: required, secret, generated or defaulted | | tarout template deploy <code> | Create the app, its database if any, fill the variables, and start the first deployment |

tarout template list
tarout template info <code>
tarout template deploy <code> --env [email protected] --yes --wait

deploy takes --name <name>, a repeatable --env KEY=VALUE, and --wait (follow the first deployment and stream its logs, like tarout deploy --wait). --env keys are checked against the template first: an unknown key exits 2 before anything is created. A required variable you leave out is asked for (masked when secret); with --json or --non-interactive it is a needs_input event naming the --env flag to add (exit 6). A template that creates a managed PostgreSQL database asks before it does, since that can add a charge to your plan; --yes skips the question. Variables the platform generates are listed by name only and never printed: read one with tarout env reveal <app> <KEY>. An operator-tier API key may get NEEDS_APPROVAL (wait with tarout approvals wait <id>), and a plan limit gets the usual upgrade options. The MCP tools template_list, template_info and template_deploy do the same.

Databases

| Command | Description | |---------|-------------| | tarout db list | List all databases | | tarout db create [name] | Create a new PostgreSQL database | | tarout db delete <db> | Delete a database | | tarout db info <db> | Show connection details | | tarout db connect <db> | Open database shell | | tarout db external-access <db> | Turn external access on or off (always TLS) |

# Create a PostgreSQL database (the only engine that can be created)
tarout db create mydb

# Get connection string
tarout db info mydb

# Allow external connections from anywhere (TLS is always required)
tarout db external-access mydb --enable --public

# Connect directly (opens psql)
tarout db connect mydb

Domains

External (customer-owned) domains connect through one flow: add-external → DNS records → verify → link to app.

| Command | Description | |---------|-------------| | tarout domains list | List domains | | tarout domains add-external <hostname> | Register an external hostname (creates the edge hostname) | | tarout domains instructions <hostname> | Show the exact DNS records to create | | tarout domains verify <hostname> | Check DNS configuration | | tarout domains wait-verified <hostname> | Poll until verified (--timeout, --interval) | | tarout domains app link-to-app --domain-id <id> --app-id <id> | Attach the verified domain to an app | | tarout domains unlink <domain> | Unlink a domain from an application |

# 1. Register the hostname, 2. add the records it prints, 3. verify, 4. attach
tarout domains add-external www.example.com
tarout domains instructions www.example.com
tarout domains wait-verified www.example.com --timeout 1800
tarout domains app link-to-app --domain-id <domain-id> --app-id <app-id>

Root domains (example.com) connect only when the domain's DNS is hosted on Cloudflare: the CLI instructs a root CNAME that must be set to Proxied (orange cloud). On other DNS providers, connect www.example.com and redirect the root to it. Hostnames under a Tarout-registered domain use tarout domains app link-registered instead (domains link is retired and always rejected by the platform).

Scheduled Tasks (cron)

Two kinds of task: http fires a signed request at your app's own URL, command runs a shell command inside the app's running container (so the app must already be deployed).

| Command | Description | |---------|-------------| | tarout jobs list | List scheduled tasks (defaults to the linked app) | | tarout jobs info <id> | Show a task, including its signing secret | | tarout jobs create | Create a scheduled task | | tarout jobs update <id> | Update a scheduled task | | tarout jobs delete <id> | Delete a task and its run history | | tarout jobs enable <id> / disable <id> | Pause or resume a task | | tarout jobs run <id> | Run a task immediately | | tarout jobs runs <id> | Show recent runs (exit code, duration, output) |

# Ping /cron/hourly on the linked app every hour
tarout jobs create --name hourly --schedule "0 * * * *" --path /cron/hourly

# Run a command inside the container every night at 02:00 Riyadh time
tarout jobs create --name cleanup --type command \
  --command "bun run cleanup" --schedule "0 2 * * *" --timezone Asia/Riyadh

# Fire it now and wait for the result (command runs are queued in the background)
tarout jobs run <id> --wait

# Inspect the history
tarout jobs runs <id> --limit 5

Command tasks may run for up to 15 minutes, so jobs run only queues them: without --wait it reports queued and you read the outcome from tarout jobs runs <id>. HTTP tasks run inline (60s cap) and print their result immediately. Each HTTP fire carries x-tarout-cron-timestamp and x-tarout-cron-signature headers - verify them with the task's signing secret (tarout jobs info <id>).

Approvals

When an operator-tier API key calls a destructive action (deleting an app, dropping a database), the platform parks it for a human and refuses the call with NEEDS_APPROVAL:<id>. The action has not failed: a human approves or denies it in the dashboard (Agent page, https://tarout.sa/dashboard/agent), and approving runs it. Requests expire after 24 hours.

| Command | Description | |---------|-------------| | tarout approvals list | List requests, pending first (--status, --limit) | | tarout approvals get <id> | Show one request: procedure, requesting key, status, expiry, result or error | | tarout approvals wait <id> | Wait for the decision (--timeout 30m, --interval 5) |

# The id comes from the NEEDS_APPROVAL error (details.approvalId under --json)
tarout approvals wait pa_123abc --timeout 1h --json

wait exits 0 only when the action was executed. A denied request exits 5, an expired one or one that failed when it ran exits 1, and a request still pending when --timeout passes (or on Ctrl+C) exits 11 with APPROVAL_PENDING and a nextCommand to resume. There is no approve or deny command: the platform refuses both to API keys, because an agent must never approve its own request. The MCP server exposes the same reads as approvals_list and approvals_get.

Organizations

| Command | Description | |---------|-------------| | tarout orgs list | List organizations | | tarout orgs switch <org> | Switch active organization (local CLI default) | | tarout orgs activate <org> | Set the active organization server-side |

# Switch organization
tarout orgs switch "Acme Corp"

Tarout has no separate "environment" (production/staging) concept - apps are scoped to an organization and a project. Use tarout projects to separate workloads.

All commands

The sections above cover the everyday flows. The full command surface (run any with --help for its subcommands and flags):

| Namespace | Description | |-----------|-------------| | tarout login / logout / whoami | Authenticate, sign out, show current context | | tarout apps | Manage applications | | tarout deploy / up | Deploy an application (up = inspect + deploy the current folder) | | tarout logs | View application logs | | tarout env | Manage application environment variables | | tarout db | Manage databases | | tarout domains | Manage domains and DNS | | tarout storage | Manage cloud storage buckets | | tarout template | Deploy ready-made apps from templates (list, info, deploy) | | tarout servers | Manage cloud servers (VMs) | | tarout backups | Manage database backup configurations | | tarout destinations | Manage backup storage destinations | | tarout billing | Manage subscription and billing | | tarout wallet | Manage AI Gateway wallet balance | | tarout referral | Your referral code, link and compute-credit earnings (history for per-payment credit) | | tarout ai | Manage AI Gateway models and API keys | | tarout monitor | Manage uptime monitors for applications | | tarout jobs | Manage scheduled tasks (cron) for applications | | tarout approvals | See and wait on approval requests for agent actions | | tarout projects | Manage projects within the active organization | | tarout orgs | Manage and switch the active organization | | tarout providers | Manage Git providers (GitHub, GitLab) | | tarout keys | Manage SSH keys for server access | | tarout firewall | Manage firewall templates for cloud servers | | tarout tickets | Manage support tickets | | tarout notifications | Manage notification preferences | | tarout inbox | Manage in-app notifications | | tarout link | Link the local directory to a Tarout application | | tarout dev | Run local dev server with cloud environment variables | | tarout build | Build locally with cloud environment variables; --explain shows what a deploy would build from the app's configured source | | tarout run | Run any local command with the app's environment variables (tarout run -- <command>) | | tarout exec | Run one command inside the app's running container (tarout exec -- <command>) | | tarout settings | Platform settings and information | | tarout upgrade | Upgrade the CLI to the latest published version | | tarout queues | Background job queues (platform operators only) | | tarout call | Call any platform procedure directly (see above) | | tarout agent | Set up coding agents: setup (skills + MCP, machine-wide), init (per project), connect; manifest maps the project in one read; sessions lists agent credentials; events shows (or --follows) what agents did |

Global Flags

These flags work with all commands:

| Flag | Description | |------|-------------| | --json | Output as JSON (machine-readable) | | --yes, -y | Skip all confirmation prompts | | --quiet, -q | Minimal output (errors only) | | --verbose, -v | Extra debug information | | --no-color | Disable colored output | | --no-update-check | Skip the automatic CLI self-update for this command |

Self-update on every command

Before running any command, the CLI checks npm for a newer @tarout/cli; when one exists it installs it globally and re-runs your exact command on the new version - so the CLI (and any agent driving it) always runs the latest, with no action needed. The network check is throttled to at most once every 3 hours (so ordinary commands stay fast - the throttle window is a single local read); tarout up / tarout deploy force an immediate check so a deploy is never on a stale CLI. It fails open (offline or npm errors just continue on the current version). Opt out per-invocation with --no-update-check or permanently with TAROUT_NO_UPDATE_CHECK=1; tune the throttle with TAROUT_UPDATE_CHECK_INTERVAL_SECONDS (set 0 to check on every command). Under --json the update is announced as a { "type": "event", "event": "cli_update" } line on stderr.

To bypass the throttle and request an upgrade explicitly, run:

tarout upgrade

This command does not require authentication or a linked project. In JSON mode it returns upgraded or up_to_date; registry and installation failures return a structured error and a non-zero exit code.

AI & Automation Usage

The CLI is designed to be 100% AI-friendly and scriptable:

# Get JSON output for parsing
tarout apps list --json

# Non-interactive operations (no prompts)
tarout apps delete my-app --yes

# Quiet mode for scripts
tarout deploy my-app --quiet

# Pipe-friendly
APP_ID=$(tarout apps list --json | jq -r '.[0].id')

Exit Codes

| Code | Meaning | |------|---------| | 0 | Success | | 1 | General error | | 2 | Invalid arguments | | 3 | Authentication error (not logged in) | | 4 | Resource not found | | 5 | Permission denied | | 6 | Needs input - see needs_input event below | | 11 | Still running or pending when the wait ended, not failed: resume with the envelope's resumeCommand (deploy) or nextCommand (approvals, build --explain) | | 12 | Build failed: the local tarout build command failed, or tarout build --explain found errors that would stop a deploy |

JSON Output Format

All --json output follows a consistent structure:

// Success
{ "success": true, "data": { ... } }

// Error
{ "success": false, "error": { "code": "NOT_FOUND", "message": "..." } }

// List operations
{ "success": true, "data": [...], "meta": { "total": 10 } }

Agent Input Relay (needs_input)

When tarout up --json hits a choice point and the value wasn't passed as a flag, it emits a single needs_input line on stdout and exits with code 6 instead of crashing or silently defaulting. The external agent reads that event, asks the human user in its chat UI, then re-invokes tarout up with the same arguments plus the new flag.

{
  "type": "needs_input",
  "field": "name",
  "kind": "input",
  "question": "Application name:",
  "default": "my-project",
  "flag": "--name",
  "sensitive": false,
  "context": { "step": "app_name", "defaultName": "my-project" }
}

Fields:

  • field - stable id (e.g. name, region, token, source).
  • kind - "input" | "select" | "confirm" | "password".
  • question - show verbatim to the user.
  • choices - present when kind: "select".
  • default - pre-fill suggestion.
  • flag - the CLI flag to pass on the next invocation.
  • sensitive - true for tokens / passwords (mask in UI, omit from logs).
  • context - free-form metadata the agent can use to phrase a richer prompt.

Loop pattern for an agent:

1. invoke `tarout up --json [flags]`
2. read stdout line-by-line
3. if line.type === "needs_input":
     - ask the user for `question`
     - re-invoke with the same flags + `${flag} ${answer}`
4. otherwise treat the final JSON envelope as success / error

Flags currently supported by tarout up for skipping the relay: --token, --name, --plan, --source, --repo, --branch, --region, --yes.

Configuration

Credentials are per project. tarout login writes ./.tarout/auth.json, and the CLI finds it by walking up from the working directory - so it works from any subfolder, and connecting a key in one project never re-points another at a different account.

your-project/
  .tarout/
    auth.json      # the credential: mode 0600, in a 0700 directory (git-ignored by default)
    project.json   # which Tarout app this directory deploys to (never committed)
    config.json    # your deploy contract: COMMIT THIS
    .gitignore     # written automatically: ignores everything but itself and config.json,
                   # and tells whoever clones the repo how to sign in

auth.json and project.json are git-ignored on creation and always excluded from deploy archives. You normally never edit those two by hand.

Sharing one login through git

By default every person who clones the repo signs in for themselves with tarout login. If you would rather the whole team (or a private CI runner) share one login that comes with the clone, commit the credential:

# Neither a dashboard key nor a browser-login token expires.
tarout login --token <key> --commit-token   # key from https://tarout.sa/dashboard/agent/keys
git add .tarout/.gitignore .tarout/auth.json

# Already signed in? The flag works on its own, no new sign-in:
tarout login --commit-token

This adds !auth.json to .tarout/.gitignore. Only do it in a private repository: anyone who can read the repo can deploy and manage resources as the key's account, and so can anything that builds from it (a Git-connected app build included). The CLI warns if a .gitignore higher up still hides .tarout/, which would keep the file out of git anyway.

To go back, run tarout login --no-commit-token, then git rm --cached .tarout/auth.json. A key that was ever pushed stays in the git history, so revoke it in the dashboard as well.

.tarout/config.json - the deploy contract

Everything else Tarout needs used to be inferred: which database to provision came from scanning your dependencies, and "working" meant / returned 200. Heuristics are a good default and a bad contract - they cannot be reviewed, and they can differ between the laptop that ran tarout up and the CI that runs it next. This file is the declaration, and it is committed so it travels with the repo.

{
  // Container readiness probe (loopback, inside the container).
  "healthCheck": { "path": "/", "expectedStatus": 200 },

  // Routes fetched on your PUBLIC url after the container is healthy.
  // If one of these does not serve, the deployment is NOT promoted.
  "smokePaths": ["/", "/ar"],

  // Runs once inside the new container, after it starts and before the
  // deployment goes live. Where migrations belong.
  "releaseCommand": "bunx prisma migrate deploy",
  "releaseCommandTimeoutSec": 300,

  // Overrides the framework detector.
  "build": { "buildCommand": "bun run build" },

  // What to provision. Beats dependency scanning in BOTH directions:
  // "postgres": false means don't, even if you depend on `pg`.
  "resources": { "postgres": true, "storage": false },

  // Created once if missing, never rotated.
  "generatedSecrets": ["SESSION_SECRET"]
}

Precedence is manifest > what is already set on the app > detection. Every key is optional; an absent file means absent, not empty.

A malformed manifest fails the command and names the offending field, rather than falling back to detection - a contract that silently doesn't apply would deploy something other than what the file says.

Why smokePaths matters

The container health probe runs on the container's own loopback and asks "is a process listening". That is not the same question as "does the app work": an app whose / returns 200 while every real route 500s passes it. Listing the routes that must actually work turns that into a gate - and until you list one, Tarout only warns, because it cannot know which of your routes are supposed to serve.

Why releaseCommand matters

Without it, prisma migrate deploy has nowhere to run except your start command, where it re-runs on every container restart and every replica, races itself, and cannot fail a deployment. A release command runs exactly once per deployment, and a non-zero exit stops the rollout with the current version still serving.

Resolution order

| # | Layer | Set by | |---|-------|--------| | 1 | .tarout/auth.json at or above the cwd | tarout login (default) | | 2 | Machine-wide profile | tarout login --global | | 3 | TAROUT_TOKEN environment variable | your shell - lowest precedence, and ignored entirely whenever 1 or 2 exists |

tarout whoami --json reports which one is in effect as scope (project / global / env / none) plus the credentialPath. Pass --global-auth on any command to skip layer 1 for that invocation.

Running tarout login somewhere that is not a project (no .tarout, .git, or package manifest above it) falls back to the machine-wide profile and tells you so, rather than scattering a .tarout/ folder.

The machine-wide profile

Stored in the OS-standard config directory (via the conf package), not ~/.tarout/:

  • macOS: ~/Library/Preferences/tarout-nodejs/config.json
  • Linux: ~/.config/tarout-nodejs/config.json (or $XDG_CONFIG_HOME)
  • Windows: %APPDATA%\tarout-nodejs\Config\config.json

Both layers hold the same shape:

{
  "currentProfile": "default",
  "profiles": {
    "default": {
      "token": "cli_xxx...",
      "apiUrl": "https://tarout.sa",
      "organizationId": "...",
      "organizationName": "My Org",
      "projectId": "...",
      "projectName": "My Project",
      "projectSlug": "my-project",
      "userId": "...",
      "userEmail": "[email protected]"
    }
  }
}

Requirements

  • Node.js 18.0.0 or higher
  • A Tarout account (sign up)

Support

License

MIT - see LICENSE for details.