diff --git a/plugins/webflow-skills/skills/webflow-cloud-command/SKILL.md b/plugins/webflow-skills/skills/webflow-cloud-command/SKILL.md index 89da890..3b8c8f8 100644 --- a/plugins/webflow-skills/skills/webflow-cloud-command/SKILL.md +++ b/plugins/webflow-skills/skills/webflow-cloud-command/SKILL.md @@ -1,11 +1,24 @@ --- name: webflow-cli:cloud -description: Initialize, build, and deploy full-stack Webflow applications to Webflow Cloud hosting. Supports site-attached deploys (linked to an existing Webflow site) and project app deploys (independent project, no existing site required). Use when creating new projects, deploying existing ones, or setting up CI/CD pipelines for Webflow Cloud. +description: Manage full-stack Webflow Cloud apps from the CLI. Initialize, build, and deploy apps (site-attached or project apps), and manage existing apps — list/get apps, view domains and live URLs, inspect environments and deployments, read build and runtime logs, and manage environment variables. Use when creating, deploying, inspecting, or operating Webflow Cloud apps, listing apps, checking deployment status, reading logs, managing env vars, or setting up CI/CD pipelines. --- # Webflow Cloud -Initialize new projects from templates and deploy to Webflow Cloud. Supports two modes: **site-attached** (deploy to an existing Webflow site) and **project app** (deploy as an independent project, no existing site required). +Initialize new projects from templates, deploy to Webflow Cloud, and manage existing apps (list, inspect, logs, deployments, environment variables). Deploys support two modes: **site-attached** (deploy to an existing Webflow site) and **project app** (deploy as an independent app, no existing site required). + +## Namespaces: `apps` (canonical) vs `cloud` (deprecated) + +`webflow apps ` is the **canonical** namespace for all Webflow Cloud work. The legacy `webflow cloud ` forms still work but are **deprecated aliases** and may print a one-time deprecation notice on stderr (suppressed with `--json`). + +| Canonical | Deprecated alias | Notes | +|---|---|---| +| `webflow apps init` | `webflow cloud init` | Same options + handler. | +| `webflow apps deploy` | `webflow cloud deploy` | Same options + handler. | +| — | `webflow cloud create ` | Deprecated; prefer `apps init`. | +| `webflow cloud list` | — | Lists **scaffold templates** (not apps). Stays under `cloud`; there is **no** `apps` equivalent. Do not confuse with `apps list`, which lists workspace **apps**. | + +Prefer the `apps` forms in all new work. This skill shows `apps` commands throughout; every `apps init` / `apps deploy` invocation can be run as `cloud init` / `cloud deploy` on older setups, with identical flags. ## Instructions @@ -37,9 +50,9 @@ cat webflow.json git remote get-url origin 2>/dev/null ``` -**Quick reference:** +**Quick reference** (the CLI now writes `cloud.app_id`; older manifests may carry the legacy `cloud.project_id`, which is still read as a fallback): -| `cloud.project_id` in `webflow.json` | git remote | → Path | +| `cloud.app_id` in `webflow.json` (or legacy `cloud.project_id`) | git remote | → Path | |---|---|---| | No | — | **A** — new project | | Yes | No | **B** — existing project, no git | @@ -47,26 +60,28 @@ git remote get-url origin 2>/dev/null --- -> **You are running without a TTY.** The CLI's interactive prompts only fire when `process.stdin.isTTY` is true. As an agent invoking the CLI through a subprocess, you do not have a TTY — every prompt is silently skipped, and any required value that wasn't passed as a flag triggers a hard error like `--project-name cannot be empty`. +> **You are running without a TTY.** The CLI's interactive prompts only fire when `process.stdin.isTTY` is true. As an agent invoking the CLI through a subprocess, you do not have a TTY — every prompt is silently skipped, and any required value that wasn't passed as a flag triggers a hard error like `--app-name cannot be empty`. > > **Rule for every command in this skill:** pass all required flags explicitly. Never rely on prompts. Pass `--no-input` when the CLI accepts it to make this contract explicit. The required flag set per command: > > | Command | Always pass | > |---|---| -> | `cloud init` (site-attached) | `--no-input --project-name <3–39 chars> --framework --mount --site-id ` | -> | `cloud init --new` (app) | `--no-input --project-name <3–39 chars> --framework --workspace-id ` | -> | `cloud deploy` (site-attached) | `--no-input --mount --environment --site-id ` plus `--project-name` on first deploy | -> | `cloud deploy` (project app, first deploy) | `--no-input --mount --environment --workspace-id --project-name ` | +> | `apps init` (site-attached) | `--no-input --app-name <3–39 chars> --framework --mount --site-id ` | +> | `apps init --new` (app) | `--no-input --app-name <3–39 chars> --framework --workspace-id ` | +> | `apps deploy` (site-attached) | `--no-input --mount --environment --site-id ` plus `--app-name` on first deploy | +> | `apps deploy` (project app, first deploy) | `--no-input --mount --environment --workspace-id --app-name ` | > -> `--site-id`, `--project-id`, `--framework`, and `--workspace-id` on `cloud deploy` let agents override what's in `webflow.json` at deploy time. +> `--site-id`, `--app-id`, `--framework`, and `--workspace-id` on `apps deploy` let agents override what's in `webflow.json` at deploy time. > -> **Multi-workspace tokens used to be an agent-fatal hang** because workspace selection had no non-TTY path. Now pass `--workspace-id` to skip the picker. **The workspace ID is not surfaced anywhere in the Webflow dashboard UI** — users can't look it up by hand. If the agent doesn't have it, ask the user to run `webflow cloud deploy` interactively once from inside their project. The preflight prompts for workspace selection and writes `cloud.workspace_id` to `webflow.json`; from that point the agent can read it from the manifest and pass `--workspace-id` on subsequent runs. Do **not** suggest `cloud init --new` for ID discovery — on an existing project it creates a discarded scratch directory. **Exception:** in Path A2 (empty directory) it *is* safe to try `cloud init --new` without `--workspace-id` to auto-resolve a single-workspace token — see [Path A2](#path-a2-empty-directory-scaffold-from-scratch). +> **Multi-workspace tokens used to be an agent-fatal hang** because workspace selection had no non-TTY path. Now pass `--workspace-id` to skip the picker. **The workspace ID is not surfaced anywhere in the Webflow dashboard UI** — users can't look it up by hand. If the agent doesn't have it, ask the user to run `webflow apps deploy` interactively once from inside their project. The preflight prompts for workspace selection and writes `cloud.workspace_id` to `webflow.json`; from that point the agent can read it from the manifest and pass `--workspace-id` on subsequent runs. Do **not** suggest `apps init --new` for ID discovery — on an existing project it creates a discarded scratch directory. **Exception:** in Path A2 (empty directory) it *is* safe to try `apps init --new` without `--workspace-id` to auto-resolve a single-workspace token — see [Path A2](#path-a2-empty-directory-scaffold-from-scratch). > > **Site IDs are visible in the dashboard.** When `--site-id` is needed but unknown, do not ask the user for a raw `site_XXXX` value — use [`webflow sites list`](#picking-a-site-id-from-a-list) to fetch their sites and present a picker keyed by display name. Users can still check their dashboard to fetch it. +> +> **Read/manage commands don't need `--workspace-id`.** The `apps list` / `get` / `domains` / `environments` / `deployments` / `logs` / `env-vars` commands derive the workspace server-side from the OAuth token — they have **no** `--workspace-id` flag and never prompt for a workspace. See [Managing apps](#managing-apps). --- -### Path A: No `project_id` — new project +### Path A: No `app_id` — new project The project has not been deployed yet. **Before doing anything else, ask the user one question:** @@ -76,15 +91,15 @@ That answer chooses the branch — and they're meaningfully different: | User has... | Branch | Init step | |---|---|---| -| **Existing code** (their own Next.js / Astro project) | **Path A1** | **Skip `cloud init`.** It would create a `.//` subfolder with a hello-world scaffold inside their repo, which they don't want. | -| **Empty directory** or wants a Webflow starter | **Path A2** | Run `cloud init` to scaffold from `Webflow-Examples/hello-world-*`. | +| **Existing code** (their own Next.js / Astro project) | **Path A1** | **Skip `apps init`.** It would create a `.//` subfolder with a hello-world scaffold inside their repo, which they don't want. | +| **Empty directory** or wants a Webflow starter | **Path A2** | Run `apps init` to scaffold from `Webflow-Examples/hello-world-*`. | After the branch decision, also ask **site-attached vs app** (only relevant before the first deploy): | User says... | Mode | Outcome | |---|---|---| -| "deploy to my Webflow site ``", "site-attached", references an existing site | **Site-attached** | Project is bound to an existing Webflow site; site URL hosts the app at the chosen mount path. Requires `--site-id`. | -| "project app", "standalone", "just an app", "no site", or no existing site mentioned | **Project app** | First deploy provisions a brand-new Webflow site (`-.webflow.io`). | +| "deploy to my Webflow site ``", "site-attached", references an existing site | **Site-attached** | App is bound to an existing Webflow site; site URL hosts the app at the chosen mount path. Requires `--site-id`. | +| "project app", "just an app", "no site", or no existing site mentioned | **Project app** | First deploy provisions a brand-new Webflow site (`-.webflow.io`). | If the user is ambiguous on either question, **ask**. Do not default. @@ -92,7 +107,7 @@ If the user is ambiguous on either question, **ask**. Do not default. #### Path A1: existing codebase, no Webflow Cloud config yet -The user has working source. `cloud deploy` handles everything — framework detection runs against `package.json`, and the preflight phase resolves identity from flags or prompts the user. No `cloud init` needed, no `webflow.json` to hand-write up front. +The user has working source. `apps deploy` handles everything — framework detection runs against `package.json`, and the preflight phase resolves identity from flags or prompts the user. No `apps init` needed, no `webflow.json` to hand-write up front. **Step 1: One-time auth (human-only).** Tell the user to run this locally; agents cannot drive the browser flow: @@ -105,9 +120,9 @@ webflow auth login **A1-a — Site-attached, `--site-id` is known:** ```bash -webflow cloud deploy --no-input \ +webflow apps deploy --no-input \ --site-id site_abc123 \ - --project-name my-app \ + --app-name my-app \ --framework nextjs \ --mount /app \ --environment main \ @@ -122,9 +137,9 @@ If the agent doesn't know the user's `--site-id`, do **not** ask for a raw `site **A1-b — Project app, `--workspace-id` is known:** ```bash -webflow cloud deploy --no-input \ +webflow apps deploy --no-input \ --workspace-id ws_abc123 \ - --project-name my-app \ + --app-name my-app \ --framework nextjs \ --mount / \ --environment main \ @@ -139,12 +154,12 @@ webflow cloud deploy --no-input \ **Ask the user to run one interactive deploy locally.** From inside their project directory: ```bash -webflow cloud deploy +webflow apps deploy ``` With no `--no-input` and no identity flags, the preflight prompts: *"This project isn't initialized for Webflow Cloud. How would you like to deploy?"* → user picks "Create a new app" → workspace picker → done. After this one human-driven deploy, `cloud.workspace_id` and `siteId` are written to `webflow.json` and `WEBFLOW_SITE_ID` to `.env`. The agent can then run all subsequent deploys with `--site-id` (the newly provisioned site). -> Do **not** ask the user to run `cloud init --new` to "discover" their workspace ID. On an existing project that creates a discarded `.//` scratch directory with a hello-world scaffold inside the user's repo. Use the interactive `cloud deploy` path above — it discovers the workspace ID *and* completes the first deploy in the same step. +> Do **not** ask the user to run `apps init --new` to "discover" their workspace ID. On an existing project that creates a discarded `.//` scratch directory with a hello-world scaffold inside the user's repo. Use the interactive `apps deploy` path above — it discovers the workspace ID *and* completes the first deploy in the same step. **Step 3: Set up git** (if not already) — same as Path A2 step 3 below. @@ -156,38 +171,38 @@ With no `--no-input` and no identity flags, the preflight prompts: *"This projec ```bash # Project app (no site attachment). --workspace-id avoids the multi-workspace hang. - webflow cloud init --new --no-input \ - --project-name my-app \ + webflow apps init --new --no-input \ + --app-name my-app \ --framework astro \ --workspace-id ws_abc123 ``` ```bash # Site-attached (connect to an existing Webflow site). Requires --site-id. - webflow cloud init --no-input \ - --project-name my-app \ + webflow apps init --no-input \ + --app-name my-app \ --framework astro \ --mount /app \ --site-id site_abc123 ``` - See [`cloud init`](#webflow-cloud-init) for all flags. + See [`apps init`](#webflow-apps-init) for all flags. - **Workspace ID discovery for project apps in Path A2 only:** because Path A2 starts from an empty directory, `cloud init --new` creates a fresh scaffold either way — there's nothing to pollute. So if the agent doesn't have `--workspace-id`, it's safe to **try `cloud init --new` without it first**: + **Workspace ID discovery for project apps in Path A2 only:** because Path A2 starts from an empty directory, `apps init --new` creates a fresh scaffold either way — there's nothing to pollute. So if the agent doesn't have `--workspace-id`, it's safe to **try `apps init --new` without it first**: ```bash # Try this first if --workspace-id is unknown (Path A2 only — empty dir) - webflow cloud init --new --no-input \ - --project-name my-app \ + webflow apps init --new --no-input \ + --app-name my-app \ --framework astro ``` - - **Single-workspace tokens:** the CLI auto-selects the only workspace, writes `cloud.workspace_id` to `webflow.json`, and exits 0. Read it back from the manifest and pass it as `--workspace-id` to `cloud deploy` in step 2. + - **Single-workspace tokens:** the CLI auto-selects the only workspace, writes `cloud.workspace_id` to `webflow.json`, and exits 0. Read it back from the manifest and pass it as `--workspace-id` to `apps deploy` in step 2. - **Multi-workspace tokens:** the workspace picker fires and the command hangs (no TTY). **Set a 30-second timeout on the Bash call** (or wrap the command in `timeout 30s ...`) — a successful single-workspace init completes in 10–20 seconds (OAuth check + `GET /v2/workspaces` + scaffold download from GitHub), so anything past 30s with no output is the picker hanging. Once the timeout fires, ask the user for the workspace ID directly and re-run with `--workspace-id`. For **site-attached** in Path A2, there is no equivalent auto-discovery — `--site-id` is always required up front. Use the [site picker](#picking-a-site-id-from-a-list) pattern below to help the user pick. - **Path A1 (existing codebase) does not get this trick.** Running `cloud init --new` in an existing project creates a discarded scratch subdirectory. The Path A1 workspace-ID discovery path stays as documented in Path A1-c. + **Path A1 (existing codebase) does not get this trick.** Running `apps init --new` in an existing project creates a discarded scratch subdirectory. The Path A1 workspace-ID discovery path stays as documented in Path A1-c. #### Picking a `--site-id` from a list @@ -218,10 +233,10 @@ If `webflow sites list` errors (auth missing / expired), surface the error and a 2. **Deploy:** pick the form matching the init form above. Pass `--site-id` (or `--workspace-id` for project-app first deploy) so the deploy can't misread the manifest if something is half-written. ```bash - # Project-app first deploy — provisions the Cloud site/project/env - webflow cloud deploy \ + # Project-app first deploy — provisions the Cloud site/app/env + webflow apps deploy \ --no-input \ - --project-name my-app \ + --app-name my-app \ --workspace-id ws_abc123 \ --mount / \ --environment main \ @@ -231,9 +246,9 @@ If `webflow sites list` errors (auth missing / expired), surface the error and a ```bash # Site-attached first deploy — uses the existing Webflow site - webflow cloud deploy \ + webflow apps deploy \ --no-input \ - --project-name my-app \ + --app-name my-app \ --site-id site_abc123 \ --mount /app \ --environment main \ @@ -241,7 +256,7 @@ If `webflow sites list` errors (auth missing / expired), surface the error and a --skip-update-check ``` - This creates the project on Webflow Cloud and sets `cloud.project_id` in `webflow.json`. Commit the updated `webflow.json`. + This creates the app on Webflow Cloud and sets `cloud.app_id` in `webflow.json`. Commit the updated `webflow.json`. 3. **Set up git** (if not already): ```bash @@ -252,25 +267,25 @@ If `webflow sites list` errors (auth missing / expired), surface the error and a 4. **(Optional) Enable push-to-deploy via the Webflow dashboard.** Pushing to GitHub alone does **not** trigger deploys — that wiring lives in the Webflow dashboard, not in the CLI or the repo. Tell the user: - 1. Open the Webflow dashboard → their Cloud project → **Settings** → **Git** + 1. Open the Webflow dashboard → their Cloud app → **Settings** → **Git** 2. Connect their GitHub account, then select the repository and branch 3. Confirm — the dashboard runs one initial deploy automatically to verify the connection 4. From that moment on, every push to the connected branch triggers a deploy - The CLI cannot perform any of these steps. If the user skips this, every deploy must be a manual `webflow cloud deploy` invocation (Path B–style) or a CI/CD pipeline. + The CLI cannot perform any of these steps. If the user skips this, every deploy must be a manual `webflow apps deploy` invocation (Path B–style) or a CI/CD pipeline. > If a deploy auth error occurs in step 2: run `webflow auth login`, complete the browser flow, then retry. --- -### Path B: `project_id` exists, no git remote — existing project, no git +### Path B: `app_id` exists, no git remote — existing project, no git The project is already on Webflow Cloud but has no git repo. Deploy directly and nudge toward git setup. 1. **Deploy:** read `webflow.json` first. If `siteId` is set, pass `--site-id` matching it. If only `cloud.workspace_id` is set, pass `--workspace-id` matching it. ```bash - webflow cloud deploy \ + webflow apps deploy \ --no-input \ --site-id site_abc123 \ --mount / \ @@ -279,17 +294,17 @@ The project is already on Webflow Cloud but has no git repo. Deploy directly and --skip-update-check ``` -2. **Nudge toward push-to-deploy:** suggest the user initialize a git repo, push to GitHub, **and then connect the repo in the Webflow dashboard** (project → Settings → Git). The dashboard step is what activates push-to-deploy — the CLI can't do this. See Path A, steps 3–4. +2. **Nudge toward push-to-deploy:** suggest the user initialize a git repo, push to GitHub, **and then connect the repo in the Webflow dashboard** (app → Settings → Git). The dashboard step is what activates push-to-deploy — the CLI can't do this. See Path A, steps 3–4. > If a deploy auth error occurs: run `webflow auth login`, complete the browser flow, then retry step 1. --- -### Path C: `project_id` exists + git remote — possibly ideal +### Path C: `app_id` exists + git remote — possibly ideal The project is deployed and has a git remote, **but the existence of a remote is not proof that push-to-deploy is wired up.** That wiring is a dashboard-side connection that the CLI can't introspect. Confirm before suggesting anything. -> **Always ask the user:** *"Is this repo connected to your Webflow Cloud project in the dashboard (project → Settings → Git, with a branch selected)?"* The answer changes the recommendation: +> **Always ask the user:** *"Is this repo connected to your Webflow Cloud app in the dashboard (app → Settings → Git, with a branch selected)?"* The answer changes the recommendation: > > - **Yes, connected** — push-to-deploy is active. The only action needed is `git push`. Do not suggest re-linking or re-deploying. > - **No, not connected** — `git push` does nothing on the Webflow side. Either run a manual deploy now, or have the user connect the repo in the dashboard first to activate push-to-deploy for future commits. @@ -305,10 +320,10 @@ The project is deployed and has a git remote, **but the existence of a remote is 2. **If not connected** — two routes: - - **Activate push-to-deploy for future commits** (recommended). Tell the user to open the Webflow dashboard → their Cloud project → **Settings** → **Git**, connect the repo, select the branch. The dashboard runs an initial deploy automatically to verify the connection. From then on, every `git push` to that branch deploys. + - **Activate push-to-deploy for future commits** (recommended). Tell the user to open the Webflow dashboard → their Cloud app → **Settings** → **Git**, connect the repo, select the branch. The dashboard runs an initial deploy automatically to verify the connection. From then on, every `git push` to that branch deploys. - **One-off manual deploy now**, without enabling push-to-deploy. Pass `--site-id` matching the `siteId` in `webflow.json`: ```bash - webflow cloud deploy \ + webflow apps deploy \ --no-input \ --site-id site_abc123 \ --mount / \ @@ -322,7 +337,7 @@ The project is deployed and has a git remote, **but the existence of a remote is ### Tool usage -- Use the **Bash tool** for all `webflow cloud` commands +- Use the **Bash tool** for all `webflow apps` / `webflow cloud` commands - Use the **Read tool** to examine `webflow.json`, `package.json` — never modify these directly - Use the **Glob tool** to discover project files - **Do not** use Webflow MCP tools for CLI workflows @@ -343,7 +358,7 @@ The CLI writes the same token env var for **both** modes. There is no per-mode s | Variable | Always written? | Description | |---|---|---| | `WEBFLOW_API_TOKEN` | Yes (both modes) | OAuth access token. The canonical token env var. Set by `webflow auth login`. | -| `WEBFLOW_SITE_ID` | Site-attached only (or after first project-app deploy) | Site ID. Written by `cloud init` for site-attached projects, or by `cloud deploy` for project apps after the first deploy provisions a site. | +| `WEBFLOW_SITE_ID` | Site-attached only (or after first project-app deploy) | Site ID. Written by `apps init` for site-attached projects, or by `apps deploy` for project apps after the first deploy provisions a site. | After the **first project-app deploy**, the CLI provisions a site on the backend and writes `WEBFLOW_SITE_ID` to `.env`. From that point on, the project behaves like a site-attached project — but the token env var is still `WEBFLOW_API_TOKEN`. @@ -356,7 +371,7 @@ Other env vars (any mode): | `DO_NOT_TRACK` | Set to `1` to opt out of telemetry. | | `WEBFLOW_SKIP_UPDATE_CHECKS` | Set to `true` to skip the @webflow package update check. | -> **`WEBFLOW_SITE_ID` env var is read-only.** Used at runtime when no flag or manifest value is set, but never written back to `webflow.json`. Setting `WEBFLOW_SITE_ID=X` in `.env` will not update the manifest — only `cloud init`, `cloud deploy`, and the manifest itself drive that. +> **`WEBFLOW_SITE_ID` env var is read-only.** Used at runtime when no flag or manifest value is set, but never written back to `webflow.json`. Setting `WEBFLOW_SITE_ID=X` in `.env` will not update the manifest — only `apps init`, `apps deploy`, and the manifest itself drive that. > **GitHub Secrets:** use `WEBFLOW_API_TOKEN` for the token in every mode. Also set `WEBFLOW_SITE_ID` for site-attached projects and project apps that have already had their first deploy. Never commit `.env` files. If existing CI uses `WEBFLOW_SITE_API_TOKEN`, rename it — the deploy will still succeed but every run prints a deprecation warning until you switch. @@ -366,7 +381,7 @@ Other env vars (any mode): { "siteId": "site_abc123", "cloud": { - "project_id": "proj_xyz", + "app_id": "app_xyz", "environment_id": "env_xyz", "workspace_id": "ws_xyz", "framework": "nextjs", @@ -375,26 +390,44 @@ Other env vars (any mode): } ``` -All `cloud.*` keys are **snake_case** (`project_id`, not `projectId`). +All `cloud.*` keys are **snake_case** (`app_id`, not `appId`). | Key | When set | Notes | |---|---|---| -| `siteId` | Site-attached: at `cloud init`. Project app: after first deploy (CLI provisions a site). | Absent on project apps that have not been deployed yet. | -| `cloud.framework` | At `cloud init`. | Required for deploy resolution — see below. | -| `cloud.project_id` | After first deploy. | Auto-written. | +| `siteId` | Site-attached: at `apps init`. Project app: after first deploy (CLI provisions a site). | Absent on project apps that have not been deployed yet. | +| `cloud.framework` | At `apps init`. | Required for deploy resolution — see below. | +| `cloud.app_id` | After first deploy. | **Canonical.** Auto-written. Replaces the legacy `cloud.project_id`. | +| `cloud.project_id` | Legacy only. | **Deprecated.** Read as a fallback when `cloud.app_id` is absent (manifests created before the Project → App rename). New deploys never write it; will be removed in a future major release. | | `cloud.environment_id` | After first **project-app** deploy. | Auto-written by `createCloudApp`. | -| `cloud.workspace_id` | At project-app `cloud init` (`--new`). | Used by the first deploy to provision the site. | +| `cloud.workspace_id` | At project-app `apps init` (`--new`). | Used by the first deploy to provision the site. | | `cloud.skipMountPathCheck` | User-managed. | Equivalent to `--skip-mount-path-check`. | -The CLI also writes `cloud.deployment_type` (`"ssr" | "ssg" | "spa"`) and `cloud.entrypoint_path` into the **bundled** `webflow.json` at build time (these power the cosmic deployer's wrangler config). They're build-time outputs — do not strip them from the source `webflow.json` if you find them there; missing values silently break Next.js server-side deploys. +The CLI also writes `cloud.deployment_type` (`"ssr" | "ssg" | "spa"`), `cloud.entrypoint_path`, and `cloud.framework_version` into the **bundled** `webflow.json` at build time (these power the cosmic deployer's wrangler config and telemetry). They're build-time outputs — do not strip them from the source `webflow.json` if you find them there; missing values silently break Next.js server-side deploys. + +#### ID resolution and env vars (CLD-1960) + +A shared resolver resolves `siteId`, `appId`, `environmentId`, and `workspaceId` in a **strict priority order**: + +``` +explicit flag / positional arg > env var > webflow.json manifest > interactive prompt > error +``` + +| Resource | Flag / arg | Env var (read-only) | `webflow.json` | +|---|---|---|---| +| App ID | `--app-id` / `[appId]` arg | `WEBFLOW_APP_ID` | `cloud.app_id` (falls back to legacy `cloud.project_id`) | +| Environment ID | `--environment-id` / `[envId]` arg | `WEBFLOW_APP_ENVIRONMENT_ID` | `cloud.environment_id` | +| Site ID | `--site-id` | `WEBFLOW_SITE_ID` | `siteId` | +| Workspace ID | `--workspace-id` | `WEBFLOW_WORKSPACE_ID` | `cloud.workspace_id` | + +Env vars are **read-only** — they are never persisted back to the manifest. When a value comes from the manifest, the CLI logs a `Using from webflow.json` line (suppressed with `--json`). When nothing resolves and the command can't prompt (`--no-input`, `CI=true`, or no TTY), it exits with a machine-readable `missingFlag` on the error — see the `--no-input` contract in [Managing apps](#managing-apps). **`cloud.framework` resolution at deploy time:** 1. **`webflow.json` exists with `cloud.framework`** — used as-is. Invalid value exits with code 1. -2. **`webflow.json` exists but `cloud.framework` is absent** — throws: _"webflow.json exists but doesn't contain valid framework information under the 'cloud' key"_. Add `"cloud": { "framework": "nextjs" }` manually. +2. **`webflow.json` exists but `cloud.framework` is absent** — falls back to detecting from `package.json`. 3. **No `webflow.json`** — auto-detected from `package.json`. CLI **writes a new `webflow.json`** on success. -Projects created via `cloud init` always land in case 1. +Projects created via `apps init` always land in case 1. ### Commands @@ -404,38 +437,39 @@ Projects created via `cloud init` always land in case 1. webflow cloud list ``` -Lists available scaffold templates. Check this before `cloud init --framework` to confirm valid scaffold IDs. +Lists available **scaffold templates** (used by `init`). Check this before `apps init --framework` to confirm valid scaffold IDs. This is distinct from `apps list` (which lists your deployed apps) and lives only under the `cloud` namespace. -#### webflow cloud init +#### webflow apps init -Bootstrap a new project locally. Two modes: **site-attached** and **app**. +Bootstrap a new app locally. Two modes: **site-attached** and **app**. (`webflow cloud init` is a deprecated alias with identical flags.) **Site-attached** (connects to an existing Webflow site): ```bash # Agent / non-TTY — always pass every flag -webflow cloud init \ +webflow apps init \ --no-input \ - --project-name my-app \ + --app-name my-app \ --framework nextjs \ --mount /app \ --site-id site_abc123 # Human at a real terminal — interactive prompts will fill in any missing flag -webflow cloud init +webflow apps init ``` Flags: | Flag | Short | Description | |---|---|---| -| `--project-name ` | `-n` | Project name. **Must be 3–39 characters** — the CLI rejects anything outside this range at init and at the first project-app deploy. | +| `--app-name ` | `-n` | App name. **Must be 3–39 characters** — the CLI rejects anything outside this range at init and at the first project-app deploy. | +| `--project-name ` | — | **Deprecated alias** of `--app-name`. Still accepted. | | `--framework ` | `-f` | Must match a scaffold ID from `cloud list`. Currently: `nextjs`, `astro`. | -| `--mount ` | `-m` | Mount path (default `/app` for site-attached, `/` for app). Substituted into config files at scaffold time. Not stored in `webflow.json`. | +| `--mount ` | `-m` | Mount path (default `/` for new domain, `/app` for existing site). Substituted into config files at scaffold time. Not stored in `webflow.json`. | | `--site-id ` | `-s` | Required in non-interactive site-attached mode. Mutually exclusive with `--workspace-id`. | | `--workspace-id ` | `-w` | Skips the workspace picker for `--new` (app mode). Mutually exclusive with `--site-id`. | | `--new` | — | Project-app mode (no site). | -| `--no-input` | — | CI mode. Requires `--project-name` and `--framework`. Without `--new`, defaults to app behavior. | +| `--no-input` | — | CI mode. Requires `--app-name` and `--framework`. Without `--new`, defaults to app behavior. | Credential resolution for `--no-input` site-attached: `--site-id` flag → `siteId` in `webflow.json` → `WEBFLOW_SITE_ID` env var → error. @@ -445,16 +479,16 @@ After scaffolding a site-attached project, the CLI automatically runs a **DevLin ```bash # Agent / non-TTY — always pass --workspace-id to skip the workspace picker -webflow cloud init --new --no-input \ - --project-name my-app \ +webflow apps init --new --no-input \ + --app-name my-app \ --framework nextjs \ --workspace-id ws_abc123 # Human at a real terminal — prompts for workspace if not passed -webflow cloud init --new +webflow apps init --new ``` -> If the token sees multiple workspaces and the agent doesn't have a workspace ID, ask the user to run `webflow cloud deploy` interactively from inside their project — the preflight prompt picks a workspace and writes `cloud.workspace_id` to `webflow.json` for subsequent agent-driven runs. The workspace ID is not exposed in the Webflow dashboard UI, so the interactive CLI run is the only practical way to discover it. +> If the token sees multiple workspaces and the agent doesn't have a workspace ID, ask the user to run `webflow apps deploy` interactively from inside their project — the preflight prompt picks a workspace and writes `cloud.workspace_id` to `webflow.json` for subsequent agent-driven runs. The workspace ID is not exposed in the Webflow dashboard UI, so the interactive CLI run is the only practical way to discover it. | | Site-attached | Project app (`--new`) | |---|---|---| @@ -468,15 +502,17 @@ webflow cloud init --new **Workspace selection (project-app mode only):** if `--workspace-id` is **not** passed, the CLI calls `GET /v2/workspaces` to enumerate workspaces the token has access to. Single workspace is auto-selected; multiple workspaces trigger an interactive picker. With `--workspace-id` the API roundtrip is skipped — the CLI trusts the flag and surfaces a 404 later via `createCloudApp` if it's invalid. The chosen ID is persisted as `cloud.workspace_id` in `webflow.json`. -**Agent caveat:** if the user's token sees more than one workspace and the agent can't pass `--workspace-id`, the picker fires and hangs in non-TTY contexts. The workspace ID is not visible in the Webflow dashboard UI, so the recovery is: **ask the user to run `webflow cloud deploy` interactively once from inside their project.** The preflight prompt picks the workspace, completes a first deploy, and writes `cloud.workspace_id` (plus `siteId`, `project_id`, `environment_id`) to `webflow.json`. The agent can then read the workspace ID from the manifest and pass `--workspace-id` (or `--site-id`, now that the site exists) on subsequent runs. To target a different workspace later, delete `cloud.workspace_id` and have the user repeat the interactive deploy. +**Agent caveat:** if the user's token sees more than one workspace and the agent can't pass `--workspace-id`, the picker fires and hangs in non-TTY contexts. The workspace ID is not visible in the Webflow dashboard UI, so the recovery is: **ask the user to run `webflow apps deploy` interactively once from inside their project.** The preflight prompt picks the workspace, completes a first deploy, and writes `cloud.workspace_id` (plus `siteId`, `app_id`, `environment_id`) to `webflow.json`. The agent can then read the workspace ID from the manifest and pass `--workspace-id` (or `--site-id`, now that the site exists) on subsequent runs. To target a different workspace later, delete `cloud.workspace_id` and have the user repeat the interactive deploy. #### webflow cloud create (deprecated) -`webflow cloud create ` still works but **emits a deprecation warning** and will be removed in a future major release. It's hardcoded to `/app` mount in site-attached mode and offers a strict subset of `cloud init`. Always prefer `cloud init` (or `cloud init --new` for app mode). +`webflow cloud create ` still works but **emits a deprecation warning** and will be removed in a future major release. It's hardcoded to `/app` mount in site-attached mode and offers a strict subset of `apps init`. Always prefer `apps init` (or `apps init --new` for app mode). -#### webflow cloud deploy +#### webflow apps deploy -**Preflight phase: identity resolution.** Before any backend call, `cloud deploy` runs a preflight step that resolves whether this is a site-attached deploy or a project-app first deploy. Resolution order (first match wins): +`webflow cloud deploy` is a deprecated alias of `apps deploy` with identical flags and handler. + +**Preflight phase: identity resolution.** Before any backend call, `apps deploy` runs a preflight step that resolves whether this is a site-attached deploy or a project-app first deploy. Resolution order (first match wins): | # | Source | Result | |---|---|---| @@ -490,11 +526,11 @@ webflow cloud init --new This preflight phase exists to prevent the project-app deploy path from running and provisioning a Cloud app before identity is locked in — earlier versions could orphan a new Webflow site if any later step failed. -> **Pass `--site-id` or `--workspace-id` explicitly whenever you can.** It defends against half-written manifests and removes the dependence on whatever state `cloud init` happened to leave behind. If the user wants site-attached, pass `--site-id`. For project-app first deploy, pass `--workspace-id`. +> **Pass `--site-id` or `--workspace-id` explicitly whenever you can.** It defends against half-written manifests and removes the dependence on whatever state `apps init` happened to leave behind. If the user wants site-attached, pass `--site-id`. For project-app first deploy, pass `--workspace-id`. -**Project-app first deploy** (triggered by `--workspace-id` flag or `manifest.cloud.workspace_id`) calls `POST /cosmic/workspaces/:workspace_id/cloudApps` to atomically create a site, project, and environment. On success it writes `siteId`, `cloud.project_id`, and `cloud.environment_id` into `webflow.json`, writes `WEBFLOW_SITE_ID` into `.env`, and forces `--skip-mount-path-check` for that one deploy. Subsequent deploys behave like normal site-attached deploys. +**Project-app first deploy** (triggered by `--workspace-id` flag or `manifest.cloud.workspace_id`) calls `POST /cosmic/workspaces/:workspace_id/cloudApps` to atomically create a site, app, and environment. On success it writes `siteId`, `cloud.app_id`, and `cloud.environment_id` into `webflow.json`, writes `WEBFLOW_SITE_ID` into `.env`, and forces `--skip-mount-path-check` for that one deploy. Subsequent deploys behave like normal site-attached deploys. -If `--project-name` is omitted on the first project-app deploy, the CLI uses the **cwd folder name** (when 3–39 chars) and falls back to `"Cloud App"`. Provide `--project-name` explicitly in CI to avoid surprises. +If `--app-name` is omitted on the first project-app deploy, the CLI uses the **cwd folder name** (when 3–39 chars) and falls back to `"Cloud App"`. Provide `--app-name` explicitly in CI to avoid surprises. **Uninitialized projects** (no `siteId`, no `workspace_id`, no flag): the CLI prompts the user to create a new project app, attach to an existing site, or cancel. With `--no-input` it hard-errors listing the required flags. Agents running with `--no-input` must always supply `--site-id` or `--workspace-id` on the first deploy of an uninitialized project. @@ -505,7 +541,7 @@ There are two deployment approaches. **GitHub-linked deployment is recommended** Once a one-time dashboard setup is done, every push to the connected branch triggers a deploy — no CLI commands, no workflow file. **The setup is dashboard-only — the CLI cannot reach this state on its own.** Pushing a repo to GitHub does not, by itself, enable push-to-deploy. 1. Push the project to GitHub (the user needs at least one commit pushed) -2. **Tell the user to open the Webflow dashboard** → their Cloud project → **Settings** → **Git** and connect their GitHub account if not already connected +2. **Tell the user to open the Webflow dashboard** → their Cloud app → **Settings** → **Git** and connect their GitHub account if not already connected 3. Select the repository, then the branch to deploy from (e.g. `main`) 4. Confirm — the dashboard runs an initial deploy automatically to verify the connection 5. From that point on: `git push` to the connected branch = deploy @@ -523,7 +559,7 @@ Use when you need custom build steps, environment-specific secrets, or deploy ga For development and one-off deploys: ```bash -webflow cloud deploy \ +webflow apps deploy \ --no-input \ --mount / \ --environment main \ @@ -531,25 +567,153 @@ webflow cloud deploy \ --skip-update-check ``` -All `cloud deploy` flags: +All `apps deploy` flags: | Flag | Short | Description | |---|---|---| | `--no-input` | — | CI mode. Disables most prompts but **not** the project-select prompt — see callout below. | | `--mount ` | `-m` | Mount path. **Always required with `--no-input`.** Not auto-read from `webflow.json`. | | `--environment ` | `-e` | Environment name. Creates if it does not exist. Must be passed with `--mount`. | -| `--project-name ` | `-n` | Required on first deploy with `--no-input` when no `cloud.project_id` in `webflow.json`. **Must be 3–39 characters** for project-app first deploy. | -| `--site-id ` | `-s` | **New.** Webflow site ID for site-attached deploys. Overrides `siteId` in `webflow.json`. Mutually exclusive with `--workspace-id`. Use this to recover from a half-written manifest without editing JSON. | +| `--app-name ` | `-n` | Required on first deploy with `--no-input` when no `cloud.app_id` in `webflow.json`. **Must be 3–39 characters** for project-app first deploy. | +| `--project-name ` | — | **Deprecated alias** of `--app-name`. Still accepted. | +| `--site-id ` | `-s` | Webflow site ID for site-attached deploys. Overrides `siteId` in `webflow.json`. Mutually exclusive with `--workspace-id`. Use this to recover from a half-written manifest without editing JSON. | | `--workspace-id ` | `-w` | Workspace ID for project-app first deploys. Overrides `cloud.workspace_id` in `webflow.json`. Mutually exclusive with `--site-id`. | -| `--project-id ` | `-p` | **New.** Cloud project ID. Skips the app picker. Overrides `cloud.project_id` in `webflow.json`. | -| `--framework ` | `-f` | **New.** Override framework detection. Must be `nextjs` or `astro`. Writes the value back into `webflow.json`. Use when auto-detection from `package.json` is unreliable (monorepos, missing dependencies, etc.). | -| `--directory ` | `-d` | Project directory (default: cwd). Use for monorepos. | -| `--description ` | — | Project description for the first deploy. | +| `--app-id ` | `-a` | Cloud app ID. Skips the app picker. Overrides `cloud.app_id` in `webflow.json`. | +| `--project-id ` | — | **Deprecated alias** of `--app-id`. Still accepted. | +| `--framework ` | `-f` | Override framework detection. Must be `nextjs` or `astro`. Writes the value back into `webflow.json`. Use when auto-detection from `package.json` is unreliable (monorepos, missing dependencies, etc.). | +| `--directory ` | `-d` | App directory (default: cwd). Use for monorepos. | +| `--description ` | — | App description for the first deploy. | | `--skip-mount-path-check` | — | Skip domain manifest validation. Required in CI. Can also be set in `webflow.json` as `cloud.skipMountPathCheck: true`. | | `--auto-publish` | — | Publish the Webflow **site** to sync mount path routing. Does not affect app deployment. | | `--skip-update-check` | — | Skip @webflow package update check. | -> **Agents: pass `--mount` AND `--environment` together, every time.** The deploy prompts (select existing project, name a new project, pick an environment) are gated on whether `--mount` and `--environment` are *both* set — not on `--no-input`. Pass `--no-input` without both and the project-select prompt still fires and hangs in non-TTY contexts. The minimum agent-safe deploy flag set is `--no-input --mount --environment --site-id ` (or `--workspace-id ` for project-app first deploy), plus `--project-name` whenever `cloud.project_id` is absent from `webflow.json`. +> **Agents: pass `--mount` AND `--environment` together, every time.** The deploy prompts (select existing app, name a new app, pick an environment) are gated on whether `--mount` and `--environment` are *both* set — not on `--no-input`. Pass `--no-input` without both and the app-select prompt still fires and hangs in non-TTY contexts. The minimum agent-safe deploy flag set is `--no-input --mount --environment --site-id ` (or `--workspace-id ` for project-app first deploy), plus `--app-name` whenever `cloud.app_id` is absent from `webflow.json`. + +### Managing apps + +Commands for inspecting and operating **existing** Cloud apps. They complement `init` / `deploy`: + +- **Workspace is server-side.** Every read command derives the workspace from the OAuth token — there is **no `--workspace-id` flag** on any of them, and they never prompt for a workspace. +- **ID resolution.** `appId` and `envId` resolve via the shared resolver: **explicit arg/flag → env var (`WEBFLOW_APP_ID` / `WEBFLOW_APP_ENVIRONMENT_ID`) → `webflow.json` (`cloud.app_id` / `cloud.environment_id`, legacy `cloud.project_id` fallback) → interactive TTY prompt**. Running inside the app directory (so `webflow.json` resolves the IDs) is the common case, not a requirement. +- **Auto-select vs. `--no-input` contract.** When the ID isn't given, the picker auto-selects a workspace's single app / an app's single environment, so single-app/single-env setups work non-interactively (including CI). When several exist and the command can't prompt (`--no-input`, `CI=true`, no TTY, or `--json`), it exits non-zero with a **machine-readable `missingFlag`** on the error (`appId`, `environmentId`, or `yes` for `delete`) so an agent can recover by re-running with the flag. The human-readable form is e.g. `Missing required appId. Pass --app-id, set WEBFLOW_APP_ID, or add it to webflow.json.` +- **`--json`.** Every command supports `--json`, which prints the raw API response and **ignores `--fields`**. Use it for programmatic parsing. `--json` also suppresses the `Using … from webflow.json` info lines. +- **`--fields `.** Read commands render a table; `--fields` projects which columns show (invalid field names fail fast before the network call). Each command's default and full field set are listed below. + +#### apps list + +Lists all apps in your workspace. `--fields` default `id,name,siteId,createdAt` (all: `id,name,description,siteId,appPath,createdAt,siteName,shortName`). + +```bash +webflow apps list --json +``` + +#### apps get [appId] + +Details for one app. `appId` defaults to `webflow.json` / `WEBFLOW_APP_ID`. `--fields` default `id,name,description,siteId,appPath,createdAt`. + +```bash +webflow apps get app_abc123 --json +``` + +#### apps domains [appId] + +Domains and live URLs for an app. `--fields` default `id,url,lastPublished` (all + `fullSiteCompiledAt`). + +```bash +webflow apps domains app_abc123 --json +``` + +#### apps environments list [appId] + +Lists an app's environments. `--fields` default `id,branch,deployUrl,latestDeploymentStatus,createdAt`. + +```bash +webflow apps environments list app_abc123 --json +``` + +#### apps deployments list + +Lists the most recent deployments of an environment, **newest first**. Takes **no `appId` argument** — it resolves the app + environment from `WEBFLOW_APP_ID` / `WEBFLOW_APP_ENVIRONMENT_ID`, `webflow.json`, or a picker. `--limit` default `20`. `--fields` default `id,status,sourceType,commitHash,buildStartedAt`. + +```bash +webflow apps deployments list --limit 20 --json +``` + +**Deployment status enum:** `starting | building | deploying | success | failed | canceled | unstaged`. Note that **build-done / deploy-running maps to `deploying`** (there is no separate "deployed" state until it reaches `success`). The endpoint is **pollable** — re-run to refresh status — but there is **no `--watch`** yet, so poll on an interval if you need to track progress to completion. + +#### apps deployments get \ + +Details for one deployment (required `` positional). Resolves the app + environment the same way as `deployments list`. + +```bash +webflow apps deployments get dep_abc123 --json +``` + +#### apps logs build [depId] + +Build logs for a deployment. `[depId]` **defaults to the latest deployment** of the resolved environment. `--fields` default `timestamp,message` (all + `phase`). + +Filters: `--since ` (only logs at/after an ISO datetime), `--q ` (substring match on the message), `--limit ` (default `100`), `--cursor ` (pagination cursor from a previous `nextCursor`). There is **no `--level`** filter. + +```bash +webflow apps logs build --since 2026-07-14T00:00:00Z --q error --limit 100 --json +``` + +#### apps logs runtime [envId] + +Runtime logs for an environment. `[envId]` **defaults to the app's single environment**, and **errors if the app has multiple** — pass the env explicitly (arg, `WEBFLOW_APP_ENVIRONMENT_ID`, or `cloud.environment_id`) in that case. Same filters as `logs build` (`--since`, `--q`, `--limit`, `--cursor`; no `--level`). + +```bash +webflow apps logs runtime env_abc123 --q timeout --json +``` + +Pagination for both log commands: the response carries a `nextCursor` (base64url; `null` when exhausted). Pass it back verbatim via `--cursor` to fetch the next page. + +#### apps env-vars + +Manage a Cloud app environment's variables non-interactively. All four subcommands accept `--app-id` and `--environment-id` (defaulting from `webflow.json` via the shared resolver) and `--json`. **Secret values are never printed** to output, logs, or telemetry — the server returns secrets masked on every read, and the CLI never unmasks them. + +**`apps env-vars list`** — `--fields` default `key,isSecret` (all: `key,isSecret,value,id,environmentId`). `value` is excluded by default; even with `--fields value`, secret values arrive masked. + +```bash +webflow apps env-vars list --json +``` + +**`apps env-vars set [value]`** — create or update one variable. `[value]` is optional: omit it to read from piped stdin or be prompted (recommended for secrets, keeps them out of shell history). `--secret` encrypts the value (omitting it preserves an existing key's secrecy). `--dry-run` previews without writing. + +```bash +webflow apps env-vars set API_URL https://api.example.com --json +# secret — value read from stdin/prompt, never echoed: +printf '%s' "$TOKEN" | webflow apps env-vars set API_KEY --secret --json +``` + +**`apps env-vars delete `** — delete one variable. `--dry-run` previews. + +```bash +webflow apps env-vars delete API_KEY --json +``` + +**`apps env-vars import `** — bulk upsert from a `.env` (`KEY=value`) file. `--secret` marks **every** key in the file as secret. `--dry-run` previews (values shown as `KEY=********`, never plaintext). Per-key failures are reported and cause a non-zero exit. + +```bash +webflow apps env-vars import .env.production --secret --json +``` + +#### apps update [appId] + +Update an app's `--name` and/or `--description` (at least one is required). `--dry-run` previews. `appId` defaults from `webflow.json` / `WEBFLOW_APP_ID`. + +```bash +webflow apps update app_abc123 --name "New Name" --description "Marketing site app" --json +``` + +#### apps delete [appId] + +Delete (or archive — the server decides by site kind) an app. Requires confirmation: pass `--yes` to skip the prompt. Non-interactively (`--no-input` / `--json`) without `--yes`, it refuses with `missingFlag: "yes"` rather than deleting. `--dry-run` previews the impact without deleting. + +```bash +webflow apps delete app_abc123 --yes --json +``` ### Frameworks @@ -558,26 +722,27 @@ All `cloud deploy` flags: | `nextjs` | ✓ | ✓ | `@opennextjs/cloudflare` | | `astro` | ✓ | ✓ | `@astrojs/cloudflare` | -Any other value in `cloud.framework` causes `cloud deploy` to exit with code 1. +Any other value in `cloud.framework` causes `apps deploy` to exit with code 1. -> **Scaffolds are fetched from GitHub at init time.** The CLI downloads scaffold tarballs from `Webflow-Examples/hello-world-{astro,nextjs}*` (pinned to the `v1` branch). `cloud init` therefore requires network access to `github.com`. Old CLI installs keep working because the registry pins a `vN` branch per scaffold-contract version. +> **Scaffolds are fetched from GitHub at init time.** The CLI downloads scaffold tarballs from `Webflow-Examples/hello-world-{astro,nextjs}*`, each pinned to a versioned (`vN`) branch that the installed CLI expects. `apps init` therefore requires network access to `github.com`. Old CLI installs keep working because each release stays pinned to a compatible scaffold branch. ### Global flags | Flag | Description | |---|---| -| `--no-input` | Disable all interactive prompts. Required for CI/automation. | +| `--no-input` | Disable all interactive prompts. Required for CI/automation. Auto-enabled when `CI=true` or no TTY is detected. | | `--manifest ` | Custom path to `webflow.json`. Use for monorepos. | | `--skip-update-check` | Skip @webflow package update check. Alternatively, set `WEBFLOW_SKIP_UPDATE_CHECKS=true`. | +| `--verbose` | Display more information for debugging purposes. | ## Output -After a successful `cloud deploy`, the CLI prints two pieces of output. +After a successful `apps deploy`, the CLI prints two pieces of output. **1. Deployment dashboard URL** — always present on success: ``` -https://webflow.com/dashboard/sites/{siteId}/webflow-cloud/projects/{projectId}/environments/{environmentId}/deployments/{deploymentId} +https://webflow.com/dashboard/sites/{siteId}/webflow-cloud/projects/{appId}/environments/{environmentId}/deployments/{deploymentId} ``` Always show this to the user. From here they can view build logs, deployment status, history, and environment settings. @@ -589,11 +754,11 @@ Always show this to the user. From here they can view build logs, deployment sta https://{your-site}.webflow.io/{mount-path} ``` -If a real URL is printed, show it to the user as the live app link. The domain is their Webflow site's domain and the path is whatever `--mount` value was used at deploy time (e.g. `/`, `/app`, or any other user-chosen path). +If a real URL is printed, show it to the user as the live app link. The domain is their Webflow site's domain and the path is whatever `--mount` value was used at deploy time (e.g. `/`, `/app`, or any other user-chosen path). You can also fetch the live URL programmatically later with `webflow apps domains --json`. If the output instead reads `No domains found with the correct mount path configuration yet.`, do not show a live URL — point the user to the dashboard deployment link above to check status and configure their domain. -**Do not** fetch or curl either URL to verify the deploy — just return what the CLI printed. +**Do not** fetch or curl either URL to verify the deploy — just return what the CLI printed. To confirm status programmatically, use `webflow apps deployments list --json` or `webflow apps logs build --json` instead. ## Examples @@ -603,8 +768,8 @@ The CLI handles steps 1 and 2. **Step 3 must happen in the Webflow dashboard — ```bash # 1. Scaffold locally (CLI) -webflow cloud init --new --no-input \ - --project-name my-app \ +webflow apps init --new --no-input \ + --app-name my-app \ --framework nextjs \ --workspace-id ws_abc123 @@ -616,7 +781,7 @@ git push -u origin main ``` # 3. Connect in the Webflow dashboard (manual, dashboard-only): -# a. Open the Cloud project → Settings → Git +# a. Open the Cloud app → Settings → Git # b. Connect the GitHub account (if not already), pick the repo # c. Pick the branch to deploy from (e.g. main) # d. Confirm — the dashboard runs an initial deploy to verify the wiring @@ -630,16 +795,16 @@ git push -u origin main ```bash # Agent-safe init — assumes the token sees a single workspace. # If the token sees multiple workspaces, ask the user to run this command locally first. -webflow cloud init --new --no-input \ - --project-name my-app \ +webflow apps init --new --no-input \ + --app-name my-app \ --framework astro -# First deploy creates site + project + environment on the backend, -# writes siteId / project_id / environment_id back to webflow.json, +# First deploy creates site + app + environment on the backend, +# writes siteId / app_id / environment_id back to webflow.json, # and writes WEBFLOW_SITE_ID to .env. Subsequent deploys are normal. cd my-app -webflow cloud deploy --no-input \ - --project-name my-app \ +webflow apps deploy --no-input \ + --app-name my-app \ --mount / \ --environment main \ --skip-update-check @@ -648,14 +813,54 @@ webflow cloud deploy --no-input \ ### Scaffold a site-attached Astro app locally ```bash -webflow cloud init \ +webflow apps init \ --no-input \ - --project-name my-site-app \ + --app-name my-site-app \ --framework astro \ --mount /app \ --site-id site_abc123 ``` +### Inspect an existing app and its latest deployment + +```bash +# Which apps can this token see? +webflow apps list --json + +# Details + live URLs for one app (or omit the ID inside the app dir) +webflow apps get app_abc123 --json +webflow apps domains app_abc123 --json + +# Most recent deployments (newest first), then drill into one +webflow apps deployments list --limit 5 --json +webflow apps deployments get dep_abc123 --json + +# Poll a build to completion (no --watch yet): re-run until status is +# success/failed/canceled. +webflow apps deployments list --limit 1 --fields id,status + +# Read the latest build log, filtered +webflow apps logs build --q error --json +``` + +### Manage environment variables + +```bash +# Non-secret value inline +webflow apps env-vars set PUBLIC_API_URL https://api.example.com + +# Secret value piped from stdin (stays out of shell history / logs) +printf '%s' "$DB_PASSWORD" | webflow apps env-vars set DB_PASSWORD --secret + +# Bulk import a .env file, all keys secret; preview first +webflow apps env-vars import .env.production --secret --dry-run +webflow apps env-vars import .env.production --secret + +# List (secrets shown masked) and delete +webflow apps env-vars list --json +webflow apps env-vars delete OLD_KEY +``` + ### GitHub Actions CI/CD pipeline (when custom steps are needed) ```yaml @@ -680,7 +885,7 @@ jobs: - name: Deploy run: | - webflow cloud deploy \ + webflow apps deploy \ --no-input \ --mount / \ --environment main \ @@ -695,9 +900,9 @@ jobs: ### Manual deploy (local / one-off) ```bash -webflow cloud deploy \ +webflow apps deploy \ --no-input \ - --project-name my-app \ + --app-name my-app \ --mount / \ --environment main \ --skip-mount-path-check \ @@ -707,7 +912,7 @@ webflow cloud deploy \ ### Manual deploy with error handling ```bash -webflow cloud deploy --no-input --mount / --skip-mount-path-check --skip-update-check +webflow apps deploy --no-input --mount / --skip-mount-path-check --skip-update-check if [ $? -ne 0 ]; then echo "Deploy failed. Log file:" webflow log @@ -719,16 +924,20 @@ fi ### Init vs Deploy in CI -- **`cloud init` is for local, one-time project setup — never run it in CI.** Site-attached mode opens a browser window; there is no headless OAuth path. Run `cloud init` once locally, commit the result, then use `cloud deploy` in CI. +- **`apps init` is for local, one-time project setup — never run it in CI.** Site-attached mode opens a browser window; there is no headless OAuth path. Run `apps init` once locally, commit the result, then use `apps deploy` in CI. ### Mount path - `--mount` is **always required** with `--no-input`. The CLI does not read a saved mount path from `webflow.json`. -- **Never assume a default.** Assuming `/` or `/app` will cause `ENVIRONMENT_MOUNT_MISMATCH` if the project uses a different path. Check the Webflow dashboard under the project's environment settings. +- **Never assume a default.** Assuming `/` or `/app` will cause `ENVIRONMENT_MOUNT_MISMATCH` if the app uses a different path. Check the Webflow dashboard under the environment settings, or run `webflow apps environments list --json` and read the `mount` field. ### Do not add confirmation gates -When `--no-input` is set, do not add a human confirmation step before `cloud deploy`. It blocks unattended CI runs and is unnecessary — the CLI has no built-in prompt to bypass. +When `--no-input` is set, do not add a human confirmation step before `apps deploy`. It blocks unattended CI runs and is unnecessary — the deploy path has no built-in prompt to bypass. (`apps delete` is the exception: it *requires* `--yes` non-interactively by design.) + +### Prefer `--json` for machine reads + +For any read/management command (`list`, `get`, `domains`, `environments`, `deployments`, `logs`, `env-vars`), pass `--json` and parse the structured output rather than scraping the table. `--json` ignores `--fields` and returns the full API shape. ### Package manager @@ -736,7 +945,7 @@ The CLI uses **npm only** regardless of lock files present. pnpm and yarn lock f ### Build-time file management -During `cloud deploy`, the CLI temporarily replaces two files and restores them on success or failure: +During `apps deploy`, the CLI temporarily replaces two files and restores them on success or failure: - **Framework config** (`next.config.ts` / `astro.config.mjs`) — renamed to `clouduser.*`, replaced with CLI template, then restored. - **`wrangler.json`** — replaced with CLI template (original saved to `clouduser.wrangler.json`), then restored. Do not modify `wrangler.json` during a deploy. @@ -749,6 +958,7 @@ The CLI merges `wrangler.json` bindings at build time. Limits: **max 5 of each t ### Error handling - The CLI exits with **code 1 on every error**. Check the exit code — do not match on emoji or text patterns in stdout. +- For management commands, a `missingFlag` on the (JSON) error identifies the flag to re-run with (`appId`, `environmentId`, `yes`). - Use `webflow log` after any failure to get the full error trace. ### Deploy versioning @@ -763,45 +973,50 @@ Commit all changes before deploying to production. ### Known limitations -- No `cloud status` / `cloud logs` — use the Webflow dashboard. -- No `cloud env` commands — runtime env vars managed via dashboard only. -- No `--dry-run` — build validation always triggers a real deployment. -- No `--json` / structured output — deploy URL and project ID must be parsed from stdout. -- No `cloud rollback`. +- **Deploy has no `--dry-run`** — a build validation always triggers a real deployment. (`apps update`, `apps delete`, and `apps env-vars` *do* support `--dry-run`.) +- **Deploy has no `--json`** — the deploy URL and app ID must be parsed from stdout. The read/management commands (`list`, `get`, `domains`, `environments`, `deployments`, `logs`, `env-vars`) all support `--json`. +- **No `--watch` on deployments/logs** — the endpoints are pollable, so poll on an interval to track a deploy to completion. +- **No rollback command.** - **100 MB build size limit** — builds exceeding 104,857,600 bytes fail at upload. ## Troubleshooting -### `--project-name cannot be empty` (or any required-flag error) on `cloud init` +### `--app-name cannot be empty` (or any required-flag error) on `apps init` The CLI gates its interactive prompts on `process.stdin.isTTY`. Agents invoke the CLI from a subprocess that does **not** have a TTY, so the prompt block is skipped entirely and the bare validation fires for the first missing required value. -**Fix:** pass every required flag explicitly. For `cloud init`: +**Fix:** pass every required flag explicitly. For `apps init`: ```bash -webflow cloud init --new --no-input --project-name my-app --framework astro +webflow apps init --new --no-input --app-name my-app --framework astro # or, site-attached: -webflow cloud init --no-input --project-name my-app --framework astro --mount /app --site-id site_abc123 +webflow apps init --no-input --app-name my-app --framework astro --mount /app --site-id site_abc123 ``` Passing `--no-input` is not strictly required for the prompts to be skipped — the absent TTY already does that — but it makes the contract explicit and matches the Required-flag matrix at the top of this skill. -### `cloud init --new` hangs forever / never returns +### `apps init --new` hangs forever / never returns Workspace selection in project-app mode prompts unconditionally when the token sees more than one workspace. Pass `--workspace-id` to skip the picker; without it, in a non-TTY context the CLI hangs at the prompt. -**Fix:** pass `--workspace-id ` to `cloud init --new`. The workspace ID is not visible in the Webflow dashboard UI, so when the agent doesn't have one, ask the user to run `webflow cloud deploy` interactively from inside an existing project. The preflight prompt picks a workspace and writes `cloud.workspace_id` to `webflow.json` — the agent can then read it and pass `--workspace-id` on future runs. Single-workspace tokens are not affected — selection is auto-skipped. +**Fix:** pass `--workspace-id ` to `apps init --new`. The workspace ID is not visible in the Webflow dashboard UI, so when the agent doesn't have one, ask the user to run `webflow apps deploy` interactively from inside an existing project. The preflight prompt picks a workspace and writes `cloud.workspace_id` to `webflow.json` — the agent can then read it and pass `--workspace-id` on future runs. Single-workspace tokens are not affected — selection is auto-skipped. + +### A management command hangs or errors with a missing ID + +Read/management commands resolve `appId` / `envId` from the arg/flag, then `WEBFLOW_APP_ID` / `WEBFLOW_APP_ENVIRONMENT_ID`, then `webflow.json`, then a prompt. In a non-TTY context with several apps/environments and nothing passed, the command exits with a machine-readable `missingFlag`. + +**Fix:** run the command from inside the app directory (so `webflow.json` resolves the IDs), or pass the ID explicitly — `apps get `, `apps env-vars list --app-id --environment-id `, `apps logs runtime `. Single-app / single-env setups auto-select and need nothing extra. ### Deploy provisioned a new site when I expected site-attached -**Symptom:** user wanted to deploy to an existing Webflow site, but `cloud deploy` printed `Creating Cloud app...` / `Cloud app created: ` and the live URL came out as `-.webflow.io` (a freshly minted site) instead of the user's intended site. +**Symptom:** user wanted to deploy to an existing Webflow site, but `apps deploy` printed `Creating Cloud app...` / `Cloud app created: ` and the live URL came out as `-.webflow.io` (a freshly minted site) instead of the user's intended site. -**Cause:** `webflow.json` was in the project-app init state — `cloud.workspace_id` set, `siteId` absent — typically because the project was previously scaffolded with `cloud init --new`. The preflight phase prefers explicit flags but still falls through to the manifest when none are passed. +**Cause:** `webflow.json` was in the project-app init state — `cloud.workspace_id` set, `siteId` absent — typically because the project was previously scaffolded with `apps init --new`. The preflight phase prefers explicit flags but still falls through to the manifest when none are passed. -**Prevention (primary fix):** pass `--site-id ` to `cloud deploy`. The preflight phase resolves identity from flags first, so this overrides whatever's in `webflow.json` and routes the deploy to the intended Webflow site. The skill should always pass `--site-id` when site-attached intent is known. +**Prevention (primary fix):** pass `--site-id ` to `apps deploy`. The preflight phase resolves identity from flags first, so this overrides whatever's in `webflow.json` and routes the deploy to the intended Webflow site. The skill should always pass `--site-id` when site-attached intent is known. ```bash -webflow cloud deploy --no-input \ +webflow apps deploy --no-input \ --site-id site_abc123 \ --mount /app --environment main \ --skip-mount-path-check --skip-update-check @@ -811,8 +1026,8 @@ webflow cloud deploy --no-input \ - **Keep the new project-app site** the deploy just created — do nothing; subsequent deploys will go to the same site (or pass `--site-id` of the new site if there's any ambiguity). - **Re-target an existing Webflow site instead.** The auto-provisioned site cannot be re-bound to an existing site after creation. Options: - 1. Delete the project app (and its auto-provisioned site) from the Webflow dashboard. - 2. Either edit `webflow.json` (remove `cloud.workspace_id`, `cloud.project_id`, `cloud.environment_id`, `siteId`) and re-run `cloud init` with `--site-id `, or skip the manifest edit and run `cloud deploy --site-id ` directly — the preflight will treat this as a fresh site-attached deploy. + 1. Delete the project app (and its auto-provisioned site) from the Webflow dashboard or with `webflow apps delete --yes`. + 2. Either edit `webflow.json` (remove `cloud.workspace_id`, `cloud.app_id`, `cloud.environment_id`, `siteId`) and re-run `apps init` with `--site-id `, or skip the manifest edit and run `apps deploy --site-id ` directly — the preflight will treat this as a fresh site-attached deploy. ### Auth error on deploy @@ -822,7 +1037,7 @@ In CI, browser auth is not possible — an auth error means `WEBFLOW_API_TOKEN` ### Deploying to a different workspace -For **project apps (`--new`)**: pass `--workspace-id ` to `cloud init` or `cloud deploy` to override `cloud.workspace_id` in `webflow.json`. Alternatively, delete `cloud.workspace_id` from `webflow.json` and re-run init (or interactive deploy on an existing project) to re-seed it. +For **project apps (`--new`)**: pass `--workspace-id ` to `apps init` or `apps deploy` to override `cloud.workspace_id` in `webflow.json`. Alternatively, delete `cloud.workspace_id` from `webflow.json` and re-run init (or interactive deploy on an existing project) to re-seed it. For **site-attached projects**, workspace context is implicit in the auth token. Re-run `webflow auth login` and select the target workspace in the browser; the new token replaces the old one in `.env`. @@ -832,27 +1047,27 @@ The token saved to `.env` doesn't include the scopes needed to create a Cloud ap ### First project-app deploy fails: "your workspace has reached its app limit" -The selected workspace (`cloud.workspace_id`) is at its app cap. Either upgrade the workspace plan or delete unused apps in the Webflow dashboard, then retry. +The selected workspace (`cloud.workspace_id`) is at its app cap. Either upgrade the workspace plan or delete unused apps in the Webflow dashboard (or with `webflow apps delete --yes`), then retry. ### First project-app deploy fails with workspace-not-found / 404 The workspace ID (from `--workspace-id` flag, or `cloud.workspace_id` in `webflow.json`) no longer resolves — workspace deleted, or token has no access. The CLI doesn't validate the flag up front: it trusts the value and surfaces the 404 from `createCloudApp`. Fixes: -- Pass `--workspace-id ` to `cloud init` or `cloud deploy`. -- Or delete `cloud.workspace_id` from `webflow.json` and either re-run `cloud init --new --workspace-id ` (empty directory) or run `webflow cloud deploy` interactively (existing project) so the preflight prompt re-seeds the workspace ID. +- Pass `--workspace-id ` to `apps init` or `apps deploy`. +- Or delete `cloud.workspace_id` from `webflow.json` and either re-run `apps init --new --workspace-id ` (empty directory) or run `webflow apps deploy` interactively (existing project) so the preflight prompt re-seeds the workspace ID. ### `ENVIRONMENT_MOUNT_MISMATCH` -The `--mount` value does not match the path registered for that environment. Check the Webflow dashboard under the project's environment settings for the correct mount path and pass it explicitly. +The `--mount` value does not match the path registered for that environment. Check the correct mount path via `webflow apps environments list --json` (read the `mount` field) or the Webflow dashboard, and pass it explicitly. ### Framework cannot be detected / explicit framework required A `webflow.json` that has a `cloud` block but no `framework` key does not throw — the CLI falls back to detecting from `package.json`. The legacy error _"webflow.json exists but doesn't contain valid framework information"_ only fires when `cloud.framework` is explicitly set to an unsupported value. -If framework detection still fails (monorepo, missing framework dependency, ambiguous setup), fix it with the new `--framework` flag on `cloud deploy`: +If framework detection still fails (monorepo, missing framework dependency, ambiguous setup), fix it with the `--framework` flag on `apps deploy`: ```bash -webflow cloud deploy --no-input --framework nextjs --mount /app --environment main ... +webflow apps deploy --no-input --framework nextjs --mount /app --environment main ... ``` This writes `cloud.framework` back into `webflow.json` so subsequent deploys don't need the flag. Or just edit the manifest manually: