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 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;
@@ -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
- if (wantRuntime && project.project_type !== wantRuntime) {
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, wantRuntime);
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 ${wantRuntime}: ${err.body.slice(0, 200)}\n` +
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, wantRuntime, true);
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: wantRuntime });
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.1",
4
- "description": "Layero CLI publish a local site with one command. No git, no GitHub, agent-friendly (Cursor, Claude Code).",
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.5",
55
+ "layero-detection": "^0.1.11",
56
56
  "open": "^10.1.0",
57
57
  "tar": "^7.4.3"
58
58
  },