layero 0.10.5 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +41 -15
- package/dist/agent.js +36 -13
- package/dist/bin/layero.js +23 -13
- package/dist/commands/deploy.js +239 -69
- package/dist/commands/diagnose.js +23 -8
- package/dist/commands/init.js +38 -19
- package/dist/commands/projects.js +1 -1
- package/dist/detect.js +440 -20
- package/dist/logs.js +40 -9
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -53,7 +53,8 @@ bare `npx layero` call then runs the globally installed copy for years.
|
|
|
53
53
|
```bash
|
|
54
54
|
layero login # device flow: prints a URL + code, sign in once (email code or Yandex ID)
|
|
55
55
|
cd my-site
|
|
56
|
-
layero deploy
|
|
56
|
+
layero deploy --dry-run # how the platform will build this folder; uploads nothing, no login needed
|
|
57
|
+
layero deploy # packs, uploads, the platform builds and ships
|
|
57
58
|
```
|
|
58
59
|
|
|
59
60
|
The first `layero deploy` in a directory creates a project and links it via
|
|
@@ -70,9 +71,9 @@ The first `layero deploy` in a directory creates a project and links it via
|
|
|
70
71
|
|
|
71
72
|
| Command | Description |
|
|
72
73
|
|---|---|
|
|
73
|
-
| `layero init` |
|
|
74
|
+
| `layero init` | Write a Layero block into `AGENTS.md` / `CLAUDE.md` / `.cursorrules` (compact index; full skill in `layero-agents`) and scaffold `.layero/project.json`. Optional: `deploy` links the folder by itself. It records no guessed settings. |
|
|
74
75
|
| `layero login` / `logout` / `whoami` | Browser device-flow sign-in, sign-out, current account. |
|
|
75
|
-
| `layero deploy` | Pack the current directory, build on the platform, publish. `--claim` — without an account. |
|
|
76
|
+
| `layero deploy` | Pack the current directory, build on the platform, publish. `--dry-run` — show the build plan only. `--claim` — without an account. |
|
|
76
77
|
| `layero projects list` | Projects on your account with addresses. |
|
|
77
78
|
| `layero projects create --repo <provider>:<owner/repo>` | Create a project from a repository of a connected provider, apply the detected settings and start the first build (what the dashboard's "Start deploy" button does); `--no-deploy` leaves it in the wizard. Events: `project_created`, `source_connected`, `webhook_installed` \| `webhook_unavailable`, then `setup_applied` + `deploy_started` \| `setup_pending` \| `setup_failed`. |
|
|
78
79
|
| `layero projects delete <slug> --yes` | Delete a project. Irreversible; needs a token with scope `admin`. |
|
|
@@ -93,11 +94,21 @@ Run `layero <cmd> --help` for full options.
|
|
|
93
94
|
|
|
94
95
|
## `layero deploy` flags
|
|
95
96
|
|
|
97
|
+
- `--dry-run` — print how the platform will build this folder and exit:
|
|
98
|
+
framework, build command, output folder, where each value comes from
|
|
99
|
+
(`layero.json`, project settings, `package.json`, a framework default), and
|
|
100
|
+
a `hint` / `next_action` when the folder is a monorepo, a frontend +
|
|
101
|
+
backend pair, a custom build script or a server. Uploads nothing, needs no
|
|
102
|
+
login (with a login it also reads the settings of a linked project).
|
|
96
103
|
- `--type <preset>` — framework override: `vite`, `vitepress`, `next`,
|
|
97
104
|
`astro`, `cra`, `sveltekit`, `nuxt`, `gatsby`, `docusaurus`, `eleventy`
|
|
98
|
-
(alias `11ty`), `hugo`, `static`; runtime kinds `node_web`,
|
|
99
|
-
`flask`, `streamlit`, `gradio`, `ssr_next` (aliases
|
|
100
|
-
`django`, …). **Optional** —
|
|
105
|
+
(alias `11ty`), `hugo`, `static`, `generic`; runtime kinds `node_web`,
|
|
106
|
+
`python_web`, `flask`, `streamlit`, `gradio`, `ssr_next` (aliases
|
|
107
|
+
`express`, `fastapi`, `django`, …). **Optional** — detected by the
|
|
108
|
+
platform when omitted. `static` serves the files as they are and never runs
|
|
109
|
+
a build; `generic` runs your own build command (`layero.json`
|
|
110
|
+
`buildCommand` or the `build` script) and serves the folder with
|
|
111
|
+
`index.html`.
|
|
101
112
|
- `--prebuilt [dir]` — ship an already-built artifact instead of building
|
|
102
113
|
remotely. Without an argument, picks the first existing of `dist/`,
|
|
103
114
|
`build/`, `public/`, `out/`, `_site/`, `.output/public/`,
|
|
@@ -106,8 +117,10 @@ Run `layero <cmd> --help` for full options.
|
|
|
106
117
|
- `--name <name>` — project name (only on first deploy).
|
|
107
118
|
- `--project <id_or_slug>` — deploy into an existing project, ignoring
|
|
108
119
|
`./.layero/project.json` (use this in CI, not `--name`).
|
|
109
|
-
- `--prod` —
|
|
110
|
-
|
|
120
|
+
- `--prod` — only for a project **with a connected repository**: publish this
|
|
121
|
+
upload at the live address (without it the upload lands in the project's
|
|
122
|
+
separate `cli` environment). A project without a repository is always
|
|
123
|
+
published live.
|
|
111
124
|
- `--branch <name>` — **refused** (`branch_unsupported`, exit 4), see above.
|
|
112
125
|
- `--claim` — deploy without an account: a temporary project for 72 hours
|
|
113
126
|
plus a `claim_url` for a human to take it over. Turns on by itself when
|
|
@@ -143,7 +156,15 @@ the page in a browser (in agent mode it prints the link).
|
|
|
143
156
|
|
|
144
157
|
## Framework auto-detection
|
|
145
158
|
|
|
146
|
-
`layero deploy` (and `layero init
|
|
159
|
+
`layero deploy --dry-run` (and `layero init`, and the `detected` event of every
|
|
160
|
+
deploy) read your project on disk with the same rules the platform uses. This
|
|
161
|
+
is **advice, not a decision**: the platform detects again on the uploaded
|
|
162
|
+
files, and nothing the CLI guesses is saved to the project — only what you name
|
|
163
|
+
(`--type`, `--root`, fields you write into `.layero/project.json` or
|
|
164
|
+
`layero.json`). `confident: false` means the folder was not recognised; read
|
|
165
|
+
`hint` and `next_action` (an app in a subfolder → `--root <dir>`, frontend +
|
|
166
|
+
backend → `layero.json` with both halves, a custom build script →
|
|
167
|
+
`"framework": "generic"`).
|
|
147
168
|
|
|
148
169
|
| Signal | Framework | `build_cmd` | `output_dir` |
|
|
149
170
|
|---|---|---|---|
|
|
@@ -159,7 +180,8 @@ the page in a browser (in agent mode it prints the link).
|
|
|
159
180
|
| `react-scripts` dep | cra | `npm run build` | `build` |
|
|
160
181
|
| `@11ty/eleventy` dep / `.eleventy.js` / `eleventy.config.*` | eleventy | `npm run build` (or `npx @11ty/eleventy`) | `_site` |
|
|
161
182
|
| `hugo.{toml,yaml,json}` or `config.*` with Hugo markers (`baseURL`, `[markup]`, …) | hugo | `hugo --gc --minify` (no install needed) | `public` |
|
|
162
|
-
|
|
|
183
|
+
| `index.html` at the root, no framework | static | none — served as is | `.` |
|
|
184
|
+
| `package.json` with a `build` script, no known framework | generic | `npm run build` | the folder with `index.html` after the build |
|
|
163
185
|
|
|
164
186
|
## `layero.json` — pin the settings in the repository
|
|
165
187
|
|
|
@@ -287,7 +309,8 @@ Every command emits events — one JSON object per line on stdout:
|
|
|
287
309
|
{"event":"sources","org":"…","providers":[…],"connections":[…]}
|
|
288
310
|
{"event":"source_repos","org":"…","connection_id":"…","repos":[…]}
|
|
289
311
|
{"event":"environments","project":"…","environments":[{"branch":"main","url":"https://…","production":true,…}]}
|
|
290
|
-
{"event":"detected","framework":"…","build_cmd":"…","output_dir":"…","confident":true}
|
|
312
|
+
{"event":"detected","framework":"…","build_cmd":"…"|null,"output_dir":"…"|null,"confident":true,"sources":{…},"hint":"…","next_action":"…"}
|
|
313
|
+
{"event":"plan","framework":"…","build_cmd":"…"|null,"output_dir":"…"|null,"sources":{…},"creates_project":true,"replaces_live_site":true,…} (--dry-run)
|
|
291
314
|
{"event":"packing","files":N,"bytes":N,"sha256":"…"}
|
|
292
315
|
{"event":"uploading"} {"event":"uploaded","archive_key":"…"}
|
|
293
316
|
{"event":"deploy_started","deploy_id":"…"}
|
|
@@ -304,9 +327,11 @@ Every command emits events — one JSON object per line on stdout:
|
|
|
304
327
|
```
|
|
305
328
|
|
|
306
329
|
On `ready`, `url` is the **live public site** — show it as-is and never
|
|
307
|
-
rebuild the hostname from a template. `
|
|
308
|
-
|
|
309
|
-
|
|
330
|
+
rebuild the hostname from a template. `ready` comes once the address answers
|
|
331
|
+
with the site itself (`edge_ready: true`); `edge_ready: false` with `screen`
|
|
332
|
+
means the platform's own page still answered after the wait — the app did not
|
|
333
|
+
come up, read `layero logs --runtime`. `dashboard_url` is the management page,
|
|
334
|
+
not the site. `preview_url` and `edge_eta_seconds` are legacy fields.
|
|
310
335
|
|
|
311
336
|
Errors carry a stable `code` (`auth_required`, `auth_expired`, `auth_timeout`,
|
|
312
337
|
`project_unknown`, `project_not_found`, `cli_deploys_disabled`, `invalid_type`,
|
|
@@ -341,7 +366,8 @@ the rule files themselves. Maximum archive size is 200 MB.
|
|
|
341
366
|
- Per-project link: `./.layero/project.json` — `project_id`, `slug`,
|
|
342
367
|
`organization_slug`, `apex_hostname`, `claim` are managed by the CLI;
|
|
343
368
|
`framework_hint`, `build_cmd`, `output_dir`, `analytics_enabled`,
|
|
344
|
-
`env_vars` are
|
|
369
|
+
`env_vars` are yours: the CLI never writes guesses there, and what you put
|
|
370
|
+
there is applied to a new project as your choice.
|
|
345
371
|
|
|
346
372
|
## Contributing
|
|
347
373
|
|
package/dist/agent.js
CHANGED
|
@@ -127,10 +127,39 @@ function renderHuman(event) {
|
|
|
127
127
|
}
|
|
128
128
|
break;
|
|
129
129
|
case "detected":
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
130
|
+
if (event.runtime_kind) {
|
|
131
|
+
process.stdout.write(`→ Detected ${event.runtime_kind} runtime app — will deploy as a scale-to-zero container\n`);
|
|
132
|
+
}
|
|
133
|
+
else if (event.confident) {
|
|
134
|
+
process.stdout.write(`→ Detected ${event.framework} (build: ${event.build_cmd ?? "none"}, output: ${event.output_dir ?? "found after the build"})\n`);
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
process.stdout.write(`! Не удалось уверенно определить приложение в этой папке\n`);
|
|
138
|
+
}
|
|
139
|
+
if (event.hint)
|
|
140
|
+
process.stdout.write(` ${event.hint}\n`);
|
|
141
|
+
if (event.next_action)
|
|
142
|
+
process.stdout.write(` → ${event.next_action}\n`);
|
|
143
|
+
break;
|
|
144
|
+
case "plan": {
|
|
145
|
+
const src = event.sources;
|
|
146
|
+
process.stdout.write(`План сборки (ничего не выгружено):\n`);
|
|
147
|
+
process.stdout.write(` фреймворк: ${event.framework} (${src.framework})\n`);
|
|
148
|
+
if (event.runtime_kind)
|
|
149
|
+
process.stdout.write(` запуск: контейнер ${event.runtime_kind}\n`);
|
|
150
|
+
process.stdout.write(` сборка: ${event.build_cmd ?? "не запускается"} (${src.build_cmd})\n`);
|
|
151
|
+
process.stdout.write(` результат: ${event.output_dir ?? "определится после сборки"} (${src.output_dir})\n`);
|
|
152
|
+
if (event.root)
|
|
153
|
+
process.stdout.write(` папка: ${event.root}\n`);
|
|
154
|
+
process.stdout.write(event.replaces_live_site
|
|
155
|
+
? ` выкатка заменит живой сайт\n`
|
|
156
|
+
: ` выкатка ляжет в окружение «cli», живой сайт не изменится\n`);
|
|
157
|
+
if (event.hint)
|
|
158
|
+
process.stdout.write(` ${event.hint}\n`);
|
|
159
|
+
if (event.next_action)
|
|
160
|
+
process.stdout.write(` → ${event.next_action}\n`);
|
|
133
161
|
break;
|
|
162
|
+
}
|
|
134
163
|
case "prebuilt":
|
|
135
164
|
process.stdout.write(`→ Prebuilt mode: shipping ${event.dir}\n`);
|
|
136
165
|
break;
|
|
@@ -186,16 +215,10 @@ function renderHuman(event) {
|
|
|
186
215
|
break;
|
|
187
216
|
case "ready":
|
|
188
217
|
if (event.edge_ready === false) {
|
|
189
|
-
//
|
|
190
|
-
//
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
? ` (edge propagating, ~${event.edge_eta_seconds}s)`
|
|
194
|
-
: " (edge propagating)";
|
|
195
|
-
process.stdout.write(`✓ Built. Live at ${event.url}${eta}\n`);
|
|
196
|
-
if (event.preview_url) {
|
|
197
|
-
process.stdout.write(` Reachable now: ${event.preview_url}\n`);
|
|
198
|
-
}
|
|
218
|
+
// Сборка готова, но по адресу всё ещё отвечает экран платформы, а не
|
|
219
|
+
// приложение: оно не поднялось за время ожидания.
|
|
220
|
+
process.stdout.write(`✓ Built. ${event.url} still shows the platform screen` +
|
|
221
|
+
`${event.screen ? ` (${event.screen})` : ""} — check \`layero logs --runtime\`\n`);
|
|
199
222
|
}
|
|
200
223
|
else {
|
|
201
224
|
process.stdout.write(`✓ Live at ${event.url}\n`);
|
package/dist/bin/layero.js
CHANGED
|
@@ -424,17 +424,26 @@ async function main() {
|
|
|
424
424
|
.action(tokenSetCmd);
|
|
425
425
|
program
|
|
426
426
|
.command("deploy")
|
|
427
|
-
.description("Pack the current directory and deploy it
|
|
427
|
+
.description("Pack the current directory and deploy it: the platform builds it and publishes it. " +
|
|
428
|
+
"A project without a connected repository is published live — every deploy replaces the site at ready.url. " +
|
|
429
|
+
"Framework, build command and output directory are detected by the platform; --dry-run shows the plan first.")
|
|
430
|
+
.option("--dry-run", "show how the platform will build this folder (framework, build command, output folder, where each came from, " +
|
|
431
|
+
"hints for monorepos / frontend+backend / custom build scripts) and exit; uploads nothing, needs no login")
|
|
428
432
|
.option("-t, --type <preset>", "type override — static preset (vite | vitepress | next | astro | cra | sveltekit | nuxt | gatsby | docusaurus | storybook | eleventy | hugo | static) " +
|
|
429
|
-
"or runtime kind for apps the platform RUNS (node_web | python_web | flask | streamlit | gradio | ssr_next; aliases: express, fastapi, django, node, python)"
|
|
433
|
+
"or runtime kind for apps the platform RUNS (node_web | python_web | flask | streamlit | gradio | ssr_next; aliases: express, fastapi, django, node, python). " +
|
|
434
|
+
"`static` serves the files as they are and never runs a build; `generic` runs your own build command " +
|
|
435
|
+
"(layero.json buildCommand or the package.json build script) and serves the folder with index.html")
|
|
430
436
|
.option("--name <name>", "project name (only used on first deploy)")
|
|
431
437
|
.option("--project <id_or_slug>", "deploy into an existing project, ignoring local config")
|
|
432
|
-
.option("-y, --yes", "non-interactive: accept defaults and skip --prod confirmation")
|
|
438
|
+
.option("-y, --yes", "non-interactive: accept defaults and skip the --prod confirmation")
|
|
433
439
|
.option("--config", "(legacy alias of the default behaviour — auto-detect + .layero/project.json values)")
|
|
434
440
|
.option("--prebuilt [dir]", "ship an already-built artifact directory (default auto-pick: dist/build/public/out/_site/...)")
|
|
435
|
-
.option("--root <dir>", "monorepo:
|
|
436
|
-
|
|
437
|
-
.option("--
|
|
441
|
+
.option("--root <dir>", "monorepo: the app's subfolder (e.g. apps/web) that the builder treats as the app root; the whole folder is still uploaded. " +
|
|
442
|
+
"Saved on the project, so later deploys and git pushes use it too")
|
|
443
|
+
.option("--prod", "only for a project WITH a connected repository: publish this upload at the live address. Without it such an upload " +
|
|
444
|
+
"lands in the project's separate `cli` environment. A project without a repository is always published live")
|
|
445
|
+
.option("--promote", "after a successful build, point the live address at this deploy (done by the CLI; same result as --prod). " +
|
|
446
|
+
"Not needed for a project without a repository — it is published live anyway")
|
|
438
447
|
.option("--branch <name>", "REFUSED (branch_unsupported, exit 4): archive uploads always land in the " +
|
|
439
448
|
"reserved `cli` environment, so this flag cannot give you a preview. " +
|
|
440
449
|
"Branch previews come from pushing to a connected repository.")
|
|
@@ -449,14 +458,15 @@ async function main() {
|
|
|
449
458
|
// стоит по умолчанию и снял бы стоп, ничего не остановив.
|
|
450
459
|
.option("--confirm-repeated-failure", "proceed even though recent deploys keep failing with the SAME error (the platform stops repeat deploys until you confirm you know what changed)")
|
|
451
460
|
.addHelpText("after", "\nExamples:\n" +
|
|
452
|
-
" $ layero deploy
|
|
453
|
-
" $ layero deploy
|
|
454
|
-
" $ layero deploy --
|
|
455
|
-
" $ layero deploy --
|
|
456
|
-
" $ layero deploy --claim # no account: temporary site + claim link\n" +
|
|
457
|
-
" $ layero deploy --type vite # force a framework preset\n" +
|
|
461
|
+
" $ layero deploy --dry-run # how the platform will build this folder; nothing is uploaded\n" +
|
|
462
|
+
" $ layero deploy # build and publish (no repository: replaces the live site)\n" +
|
|
463
|
+
" $ layero deploy --root apps/web # monorepo: the app lives in a subfolder\n" +
|
|
464
|
+
" $ layero deploy --type generic # own build script, no known framework: run it, serve the result\n" +
|
|
458
465
|
" $ layero deploy --type express # Node backend: platform RUNS it, not serves files\n" +
|
|
459
|
-
" $ layero deploy --
|
|
466
|
+
" $ layero deploy --claim # no account: temporary site + claim link\n" +
|
|
467
|
+
" $ layero deploy --prod --yes # project with a connected repository: publish this upload live (CI)\n" +
|
|
468
|
+
" $ layero deploy --json # machine-readable output for agents\n" +
|
|
469
|
+
"\nIsolated previews come only from pushing a branch of a connected repository (layero projects create --repo).")
|
|
460
470
|
.action(async (opts) => {
|
|
461
471
|
await deployCmd(opts);
|
|
462
472
|
});
|