layero 0.9.1 → 0.9.3
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 +28 -0
- package/dist/agent.js +11 -0
- package/dist/commands/deploy.js +27 -5
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -116,6 +116,34 @@ Run `layero <cmd> --help` for full options.
|
|
|
116
116
|
| `hugo.{toml,yaml,json}` or `config.*` with Hugo markers (`baseURL`, `[markup]`, …) | hugo | `hugo --gc --minify` (no install needed) | `public` |
|
|
117
117
|
| any `.html` at root, no `package.json` | static | `true` (no-op) | `.` |
|
|
118
118
|
|
|
119
|
+
## `layero.json` — pin the settings in the repository
|
|
120
|
+
|
|
121
|
+
Auto-detection above is a default, not a decision. Drop a `layero.json` at the
|
|
122
|
+
root of the repository and Layero uses what you set there instead — for the
|
|
123
|
+
CLI, the dashboard and pushes alike. It travels with your code, so it can
|
|
124
|
+
differ per branch, and it beats any dashboard setting.
|
|
125
|
+
|
|
126
|
+
```json title="layero.json"
|
|
127
|
+
{
|
|
128
|
+
"$schema": "https://layero.ru/schema/layero-v2.json",
|
|
129
|
+
"framework": "vite",
|
|
130
|
+
"installCommand": "npm ci",
|
|
131
|
+
"buildCommand": "npm run build",
|
|
132
|
+
"outputDirectory": "dist",
|
|
133
|
+
"nodeVersion": "22"
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Every field is optional; `{}` is valid and means "decide everything yourself".
|
|
138
|
+
Short names (`install`, `build`, `output`, `node`, `start`) work too and are not
|
|
139
|
+
deprecated. A field declared here is shown in the dashboard with a badge instead
|
|
140
|
+
of an edit button — an edit there would be undone by the next build.
|
|
141
|
+
|
|
142
|
+
An error in the file never fails a build: unreadable values become warnings in
|
|
143
|
+
the build log.
|
|
144
|
+
|
|
145
|
+
Full reference: https://docs.layero.ru/deploys/layero-json
|
|
146
|
+
|
|
119
147
|
## Deploy hooks — webhook URLs that trigger builds
|
|
120
148
|
|
|
121
149
|
When something *other than you* should kick a build — a headless CMS
|
package/dist/agent.js
CHANGED
|
@@ -141,6 +141,17 @@ function renderHuman(event) {
|
|
|
141
141
|
case "setup_applied":
|
|
142
142
|
process.stdout.write(`✓ Setup applied\n`);
|
|
143
143
|
break;
|
|
144
|
+
case "preview_evicted": {
|
|
145
|
+
// Предупреждение, а не отчёт: перестал отвечать адрес, который кому-то
|
|
146
|
+
// уже отдали ссылкой. Пишем имя ветки — по нему её и возвращают.
|
|
147
|
+
const names = event.evicted
|
|
148
|
+
.map((e) => e.branch ?? e.hostname ?? "")
|
|
149
|
+
.filter(Boolean)
|
|
150
|
+
.join(", ");
|
|
151
|
+
process.stdout.write(`! Превью ${names} приостановлено — этот деплой занял его место\n`);
|
|
152
|
+
process.stdout.write(` Вернуть: задеплойте ту ветку снова или откройте панель\n`);
|
|
153
|
+
break;
|
|
154
|
+
}
|
|
144
155
|
case "runtime_type_applied":
|
|
145
156
|
process.stdout.write(`✓ Project type set to ${event.project_type}\n`);
|
|
146
157
|
break;
|
package/dist/commands/deploy.js
CHANGED
|
@@ -520,25 +520,35 @@ export async function deployCmd(opts) {
|
|
|
520
520
|
// Момент выбран не случайно: сессия создана (значит, id проекта известен),
|
|
521
521
|
// но архив ещё не упакован и сборка не запущена — флип успевает повлиять на
|
|
522
522
|
// ту же выкатку.
|
|
523
|
+
// 🚨 И ОБРАТНАЯ дорога. Статический пресет (`--type next`) менял только
|
|
524
|
+
// подсказку фреймворка, а проект оставался приложением: Next с
|
|
525
|
+
// `output: "export"` собирал статику, а платформа искала, что запускать, и
|
|
526
|
+
// сборка падала. Выйти из типа «приложение» было нечем — в панели
|
|
527
|
+
// переключателя нет, у CLI была только дорога В рантайм. Живой клиент 30.08
|
|
528
|
+
// прошёл этот тупик восемь раз (`T-20260830-1`).
|
|
523
529
|
const wantRuntime = runtimeTypeOf(opts.type);
|
|
524
|
-
|
|
530
|
+
const wantStatic = !wantRuntime && !!opts.type && project.project_type && project.project_type !== "spa"
|
|
531
|
+
? "spa"
|
|
532
|
+
: null;
|
|
533
|
+
const wantType = wantRuntime ?? wantStatic;
|
|
534
|
+
if (wantType && project.project_type !== wantType) {
|
|
525
535
|
try {
|
|
526
|
-
await api.setRuntimeType(project.id,
|
|
536
|
+
await api.setRuntimeType(project.id, wantType);
|
|
527
537
|
}
|
|
528
538
|
catch (err) {
|
|
529
539
|
// 409 — платформа возражает: репозиторий не похож на этот тип. Возражение
|
|
530
540
|
// обязано быть заметным, но не запирающим: человек написал флаг явно, и
|
|
531
541
|
// спорить с ним мы перестали намеренно (та же логика, что у панели).
|
|
532
542
|
if (err instanceof ApiError && err.status === 409) {
|
|
533
|
-
process.stderr.write(chalk.yellow(`! platform disagrees with --type ${
|
|
543
|
+
process.stderr.write(chalk.yellow(`! platform disagrees with --type ${wantType}: ${err.body.slice(0, 200)}\n` +
|
|
534
544
|
` applying anyway because you asked explicitly\n`));
|
|
535
|
-
await api.setRuntimeType(project.id,
|
|
545
|
+
await api.setRuntimeType(project.id, wantType, true);
|
|
536
546
|
}
|
|
537
547
|
else {
|
|
538
548
|
throw err;
|
|
539
549
|
}
|
|
540
550
|
}
|
|
541
|
-
emit({ event: "runtime_type_applied", project_type:
|
|
551
|
+
emit({ event: "runtime_type_applied", project_type: wantType });
|
|
542
552
|
}
|
|
543
553
|
await persistProjectLinking(cwd, {
|
|
544
554
|
project_id: project.id,
|
|
@@ -628,6 +638,18 @@ export async function deployCmd(opts) {
|
|
|
628
638
|
const liveUrl = promoted || !opts.branch
|
|
629
639
|
? (probe?.canonical_url ?? apexUrl)
|
|
630
640
|
: (probe?.preview_url ?? probe?.canonical_url ?? apexUrl);
|
|
641
|
+
// V263: деплой мог занять слот превью и снять с раздачи чужую ветку.
|
|
642
|
+
// Говорим об этом ДО `ready`: агент, увидевший итог, дальше не читает, а
|
|
643
|
+
// здесь изменился чужой работающий адрес — тот, кому ссылку уже отдали,
|
|
644
|
+
// узнает об этом иначе только открыв её.
|
|
645
|
+
//
|
|
646
|
+
// Пустой список — обычный случай и молчит. Поля может не быть вовсе, если
|
|
647
|
+
// платформа старше него: `?? []` и никаких предупреждений о том, чего не
|
|
648
|
+
// знаем.
|
|
649
|
+
const evicted = deployRow.preview_evicted ?? [];
|
|
650
|
+
if (evicted.length > 0) {
|
|
651
|
+
emit({ event: "preview_evicted", evicted });
|
|
652
|
+
}
|
|
631
653
|
emit({
|
|
632
654
|
event: "ready",
|
|
633
655
|
url: liveUrl,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "layero",
|
|
3
|
-
"version": "0.9.
|
|
4
|
-
"description": "Layero CLI
|
|
3
|
+
"version": "0.9.3",
|
|
4
|
+
"description": "Layero CLI \u2014 publish a local site with one command. No git, no GitHub, agent-friendly (Cursor, Claude Code).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"homepage": "https://layero.ru",
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"chalk": "^5.3.0",
|
|
53
53
|
"commander": "^12.1.0",
|
|
54
54
|
"ignore": "^5.3.2",
|
|
55
|
-
"layero-detection": "^0.1.
|
|
55
|
+
"layero-detection": "^0.1.11",
|
|
56
56
|
"open": "^10.1.0",
|
|
57
57
|
"tar": "^7.4.3"
|
|
58
58
|
},
|