layero 0.10.4 → 0.10.5

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
@@ -111,8 +111,11 @@ Run `layero <cmd> --help` for full options.
111
111
  - `--branch <name>` — **refused** (`branch_unsupported`, exit 4), see above.
112
112
  - `--claim` — deploy without an account: a temporary project for 72 hours
113
113
  plus a `claim_url` for a human to take it over. Turns on by itself when
114
- there is no token, the run is non-interactive (an agent, not CI) and
115
- `--yes` is passed. In CI a missing `LAYERO_TOKEN` stays an error.
114
+ there is no token, the run is non-interactive (an agent, not CI), `--yes`
115
+ is passed and the project is new: no `--project`, and the folder is not
116
+ linked to an account project. An existing project without a token means
117
+ signing in (`auth_required`). With `--project` it is refused
118
+ (`claim_with_project`, exit 4). In CI a missing `LAYERO_TOKEN` stays an error.
116
119
  - `--org <slug>` — organization for first-time project creation.
117
120
  - `--yes` / `-y` — non-interactive mode.
118
121
  - `--json` — JSON-lines events on stdout (for agents and CI).
@@ -439,7 +439,9 @@ async function main() {
439
439
  "reserved `cli` environment, so this flag cannot give you a preview. " +
440
440
  "Branch previews come from pushing to a connected repository.")
441
441
  .option("--claim", "deploy without an account: a temporary project for 72 hours plus a claim_url for a human to take it over. " +
442
- "Turns on by itself when there is no token, the run is non-interactive (agent, not CI) and --yes is passed.")
442
+ "Turns on by itself when there is no token, the run is non-interactive (agent, not CI), --yes is passed " +
443
+ "and the project is new (no --project, folder not linked to an account project). " +
444
+ "Refused together with --project (claim_with_project, exit 4).")
443
445
  .option("--org <slug>", "Layero organization slug for first-time project creation. Defaults to personal; required when you're a member of multiple orgs and want a non-personal home.")
444
446
  // Осознанное подтверждение выкатки, когда предыдущие сборки подряд падают
445
447
  // с ОДНОЙ И ТОЙ ЖЕ ошибкой. Намеренно НЕ покрывается `--yes`: смысл стопа
@@ -370,15 +370,40 @@ export async function deployCmd(opts) {
370
370
  // Claimable-проект этого запуска (этап 13): событие `claimable` уходит
371
371
  // перед `ready`, когда адрес сайта уже известен.
372
372
  let claimable = null;
373
+ // 🚨 ПЕСОЧНИЦА — ТОЛЬКО ДЛЯ НОВОГО ПРОЕКТА. `--project` или папка,
374
+ // привязанная к проекту аккаунта, значат «выкатить в существующий», а у
375
+ // токена песочницы прав на него нет: до 0.10.5 авто-режим всё равно
376
+ // включался, и платформа отвечала `username_required` про держателя
377
+ // песочницы — человек читал совет выбрать имя аккаунта, которого у него
378
+ // нет. Теперь существующий проект без токена — это вход (`auth_required`).
379
+ if (opts.claim && opts.project) {
380
+ throw new LayeroError("claim_with_project", `--claim вместе с --project ${opts.project}: песочница создаёт новый проект, в существующий она не выкатывает`, "песочница создаёт новый проект; для существующего войдите: `layero login` — и повторите без --claim");
381
+ }
382
+ // Папка привязана к claimable-проекту, и `--project` (если задан) называет
383
+ // его же. Любой другой `--project` — проект аккаунта, и токен песочницы
384
+ // из конфига к нему не подходит.
385
+ const projectIsLinkedOne = !opts.project ||
386
+ (existing?.project_id !== undefined &&
387
+ (opts.project === existing.project_id || opts.project === existing.slug));
388
+ const linkedIsClaimable = Boolean(existing?.project_id && existing.claim);
389
+ const boundToAccountProject = opts.project
390
+ ? !(projectIsLinkedOne && linkedIsClaimable)
391
+ : Boolean(existing?.project_id) && !linkedIsClaimable;
373
392
  if (!cliCfg.token) {
374
393
  // Папка уже привязана к claimable-проекту — деплоим его же токеном,
375
394
  // пока заявка жива. Заявка забрана или истекла — платформа ответит 401,
376
395
  // и это станет `auth_expired`: честнее, чем молча завести ещё один.
377
- const reuse = claimTokenFor(cliCfg, existing?.project_id);
396
+ const reuse = projectIsLinkedOne
397
+ ? claimTokenFor(cliCfg, existing?.project_id)
398
+ : undefined;
378
399
  if (reuse) {
379
400
  cliCfg = { ...cliCfg, token: reuse };
380
401
  }
381
- else if (opts.claim || (!mode.interactive && !isCiEnv() && opts.yes)) {
402
+ else if (
403
+ // Явный `--claim` — всегда новая песочница (с `--project` он отклонён
404
+ // выше). Сам режим включается только для нового проекта.
405
+ opts.claim ||
406
+ (!boundToAccountProject && !mode.interactive && !isCiEnv() && opts.yes)) {
382
407
  // 🚨 CI СЮДА НЕ ПОПАДАЕТ НАМЕРЕННО. Раннер без LAYERO_TOKEN — это
383
408
  // забытый секрет, и правильный ответ ему — отказ, а не сайт на
384
409
  // временном адресе, который через 72 часа исчезнет вместе с
@@ -48,6 +48,7 @@ const INVALID_INPUT = new Set([
48
48
  "rollback_noop",
49
49
  "sql_missing",
50
50
  "branch_unsupported",
51
+ "claim_with_project",
51
52
  "provider_unknown",
52
53
  "repo_format",
53
54
  "token_missing",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "layero",
3
- "version": "0.10.4",
3
+ "version": "0.10.5",
4
4
  "description": "Layero CLI — deploy a directory or a repo to hosting with build servers in Russia. JSON events and exit codes for AI agents and CI.",
5
5
  "license": "MIT",
6
6
  "type": "module",