layero 0.9.4 → 0.10.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/dist/config.js CHANGED
@@ -26,6 +26,7 @@ export async function loadConfig() {
26
26
  // окружения: файл мог остаться от другого аккаунта, и подпись в
27
27
  // выводе врала бы. Кто мы — узнаем у API.
28
28
  user: ENV_TOKEN ? undefined : parsed.user,
29
+ claim_tokens: parsed.claim_tokens,
29
30
  };
30
31
  }
31
32
  catch (err) {
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Коды выхода CLI по классам ошибок (AX-аудит 17.09.2026, этап 6).
3
+ *
4
+ * До этого любой отказ заканчивался `exit 1`, и скрипт, вызвавший `layero`,
5
+ * не мог отличить «нет токена» от «сборка упала»: приходилось разбирать
6
+ * JSON-событие `error`, а в человеческом режиме — текст. Классы взяты
7
+ * из аудита и совпадают с тем, как их делят Vercel и `gh`:
8
+ *
9
+ * 0 — успех;
10
+ * 1 — прочий отказ (лимит тарифа, нет прав, нужно подтверждение,
11
+ * повторяющаяся ошибка сборки — всё, что не попало в классы ниже);
12
+ * 2 — нужен вход: нет токена, токен протух, вход не подтверждён;
13
+ * 3 — не найдено: проект, организация, база, домен, хук, окружение;
14
+ * 4 — неверный ввод: флаг, аргумент, формат, неподдерживаемая операция;
15
+ * 5 — удалённая ошибка: сборка упала или отменена, 5xx платформы,
16
+ * непредвиденное исключение CLI.
17
+ *
18
+ * Отнесение ПО КОДУ ОШИБКИ, а не по месту выброса: код — контракт (см.
19
+ * `check-error-codes.py`), и класс выхода становится его частью. Новый код,
20
+ * не попавший ни в один список, даёт 1 — честно «прочее», а не наугад.
21
+ */
22
+ const AUTH = new Set(["auth_required", "auth_expired", "auth_timeout"]);
23
+ const NOT_FOUND = new Set([
24
+ "project_unknown",
25
+ "project_not_found",
26
+ "org_unknown",
27
+ "database_unknown",
28
+ "env_not_found",
29
+ "domain_not_found",
30
+ "hook_not_found",
31
+ "connection_not_found",
32
+ "account_not_found",
33
+ "repo_not_found",
34
+ "claim_unknown",
35
+ "branch_without_env",
36
+ "no_deploy",
37
+ "no_deploys",
38
+ "no_runs",
39
+ "data_key_unknown",
40
+ ]);
41
+ const INVALID_INPUT = new Set([
42
+ "invalid_type",
43
+ "invalid_choice",
44
+ "prebuilt_no_dir",
45
+ "prebuilt_no_index",
46
+ "bad_format",
47
+ "nothing_to_set",
48
+ "sql_missing",
49
+ "branch_unsupported",
50
+ "provider_unknown",
51
+ "repo_format",
52
+ "token_missing",
53
+ "username_rejected",
54
+ "gb_not_supported",
55
+ "dedicated_needs_panel",
56
+ "data_key_kind",
57
+ "data_key_expiry",
58
+ "data_key_ambiguous",
59
+ "data_levels_missing",
60
+ "data_level_unknown",
61
+ "data_probe_method",
62
+ "data_probe_path",
63
+ "data_probe_query",
64
+ "data_probe_body",
65
+ "data_probe_as",
66
+ "data_probe_user_required",
67
+ "data_probe_user_invalid",
68
+ "data_probe_schema",
69
+ "data_probe_expect",
70
+ ]);
71
+ const REMOTE = new Set([
72
+ "deploy_failed",
73
+ "deploy_cancelled",
74
+ "deploy_not_started",
75
+ "internal",
76
+ "oauth_unavailable",
77
+ "claimable_unavailable",
78
+ "data_probe_gateway_failed",
79
+ ]);
80
+ export const EXIT_OK = 0;
81
+ export const EXIT_OTHER = 1;
82
+ export const EXIT_AUTH = 2;
83
+ export const EXIT_NOT_FOUND = 3;
84
+ export const EXIT_INVALID_INPUT = 4;
85
+ export const EXIT_REMOTE = 5;
86
+ export function exitCodeFor(code) {
87
+ if (AUTH.has(code))
88
+ return EXIT_AUTH;
89
+ if (NOT_FOUND.has(code))
90
+ return EXIT_NOT_FOUND;
91
+ if (INVALID_INPUT.has(code))
92
+ return EXIT_INVALID_INPUT;
93
+ if (REMOTE.has(code))
94
+ return EXIT_REMOTE;
95
+ // `deploy_<status>` собирается динамически; `http_5xx` — отказ платформы,
96
+ // который не разобрали в понятный код.
97
+ if (code.startsWith("deploy_"))
98
+ return EXIT_REMOTE;
99
+ if (/^http_5\d\d$/.test(code))
100
+ return EXIT_REMOTE;
101
+ return EXIT_OTHER;
102
+ }
@@ -67,6 +67,8 @@ export async function persistProjectLinking(cwd, linking, fallbackHint) {
67
67
  };
68
68
  if (linking.api_url !== undefined)
69
69
  merged.api_url = linking.api_url;
70
+ if (linking.claim !== undefined)
71
+ merged.claim = linking.claim;
70
72
  // Only seed framework_hint if the user hasn't set one yet — once it's in
71
73
  // the file (manually or from a prior --type), leave it alone.
72
74
  if (fallbackHint != null &&
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Ссылка на проект: идентификатор или слаг, и как одно отличить от другого.
3
+ *
4
+ * 🚨 ОТДЕЛЬНЫЙ МОДУЛЬ РАДИ ОДНОЙ СТРОКИ — намеренно. Форма UUID нужна и
5
+ * клиенту API, и команде деплоя; жила она копией в `commands/deploy.ts`, и
6
+ * второй копией разошлась бы молча. Положить её в `api.ts` не вышло: тесты
7
+ * подменяют этот модуль целиком (`vi.mock`), и константа исчезала бы вместе с
8
+ * ним — то есть общий модуль стал бы способом уронить чужой тест.
9
+ */
10
+ export const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
11
+ /**
12
+ * Что нам дали — идентификатор или слаг.
13
+ *
14
+ * ⚠️ Отличаем по ФОРМЕ, а не по успеху запроса: UUID, ушедший в поиск по
15
+ * слагу, просто не найдётся, и человек получит «нет такого проекта» на
16
+ * совершенно правильный идентификатор.
17
+ */
18
+ export function looksLikeId(ref) {
19
+ return UUID_RE.test(ref);
20
+ }
package/dist/urls.js ADDED
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Адреса панели, выведенные из адреса API.
3
+ *
4
+ * 🚨 ОДНО МЕСТО, А НЕ ПО КОПИИ НА КОМАНДУ. Правило простое — `api.` меняется на
5
+ * `app.`, — и ровно поэтому его тянет переписать заново там, где понадобилось.
6
+ * Вторая копия разошлась бы на первом же стенде: переменную `LAYERO_DASHBOARD_URL`
7
+ * читала бы одна команда, а вторая уводила бы человека на боевую панель.
8
+ */
9
+ export function dashboardOrigin(apiUrl) {
10
+ const override = process.env.LAYERO_DASHBOARD_URL;
11
+ if (override)
12
+ return override.replace(/\/+$/, "");
13
+ try {
14
+ const u = new URL(apiUrl);
15
+ u.hostname = u.hostname.replace(/^api\./, "app.");
16
+ return u.origin;
17
+ }
18
+ catch {
19
+ return "https://app.layero.ru";
20
+ }
21
+ }
package/package.json CHANGED
@@ -1,12 +1,16 @@
1
1
  {
2
2
  "name": "layero",
3
- "version": "0.9.4",
4
- "description": "Layero CLI \u2014 publish a local site with one command. No git, no GitHub, agent-friendly (Cursor, Claude Code).",
3
+ "version": "0.10.0",
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",
7
- "homepage": "https://layero.ru",
7
+ "homepage": "https://docs.layero.ru/cli/",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "https://github.com/LayeroInfra/cli"
11
+ },
8
12
  "bugs": {
9
- "url": "https://docs.layero.ru/contacts/"
13
+ "url": "https://github.com/LayeroInfra/cli/issues"
10
14
  },
11
15
  "keywords": [
12
16
  "layero",
@@ -39,7 +43,8 @@
39
43
  "files": [
40
44
  "dist",
41
45
  "scripts/postinstall.cjs",
42
- "README.md"
46
+ "README.md",
47
+ "LICENSE"
43
48
  ],
44
49
  "scripts": {
45
50
  "build": "tsc -p tsconfig.json",