layero 0.11.2 → 0.11.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 +1 -1
- package/dist/api.js +10 -2
- package/dist/exit-codes.js +1 -0
- package/dist/logs.js +41 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -351,7 +351,7 @@ Full reference: <https://docs.layero.ru/cli/json-events>
|
|
|
351
351
|
| 2 | sign-in needed | `auth_required`, `auth_expired`, `auth_timeout` |
|
|
352
352
|
| 3 | not found | `project_unknown`, `project_not_found`, `org_unknown`, `hook_not_found`, `connection_not_found`, `claim_unknown`, … |
|
|
353
353
|
| 4 | invalid input | `invalid_type`, `prebuilt_no_dir`, `prebuilt_no_index`, `branch_unsupported`, `repo_format`, `token_missing`, `bad_format`, … |
|
|
354
|
-
| 5 | remote failure | `deploy_failed`, `deploy_cancelled`, `deploy_not_started`, `internal`, 5xx from the platform |
|
|
354
|
+
| 5 | remote failure | `deploy_failed`, `deploy_cancelled`, `deploy_not_started`, `deploy_watch_lost`, `internal`, 5xx from the platform |
|
|
355
355
|
|
|
356
356
|
## Ignore rules
|
|
357
357
|
|
package/dist/api.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { LayeroError } from "./agent.js";
|
|
2
2
|
import { CLI_VERSION, userAgent } from "./version.js";
|
|
3
3
|
import { looksLikeId } from "./project-ref.js";
|
|
4
|
+
// Один опрос логов отвечает за доли секунды. 30 с — заведомо «запрос завис»,
|
|
5
|
+
// а не «сборка идёт долго»: длительность сборки на ответ этой ручки не влияет.
|
|
6
|
+
export const POLL_TIMEOUT_MS = 30_000;
|
|
4
7
|
export class ApiError extends Error {
|
|
5
8
|
status;
|
|
6
9
|
body;
|
|
@@ -28,10 +31,15 @@ export class ApiClient {
|
|
|
28
31
|
h["X-Layero-Cli-Version"] = CLI_VERSION;
|
|
29
32
|
return h;
|
|
30
33
|
}
|
|
31
|
-
async request(method, path, body
|
|
34
|
+
async request(method, path, body,
|
|
35
|
+
// Потолок ожидания ответа. По умолчанию его нет: загрузка архива и
|
|
36
|
+
// длинные ручки живут дольше любого разумного числа. Задаётся там, где
|
|
37
|
+
// запрос короткий и повторяемый (опрос логов сборки).
|
|
38
|
+
timeoutMs) {
|
|
32
39
|
const url = `${this.cfg.apiUrl.replace(/\/+$/, "")}${path}`;
|
|
33
40
|
const init = {
|
|
34
41
|
method,
|
|
42
|
+
...(timeoutMs ? { signal: AbortSignal.timeout(timeoutMs) } : {}),
|
|
35
43
|
headers: this.headers(body !== undefined ? { "Content-Type": "application/json" } : undefined),
|
|
36
44
|
};
|
|
37
45
|
if (body !== undefined) {
|
|
@@ -312,7 +320,7 @@ export class ApiClient {
|
|
|
312
320
|
return this.request("DELETE", `/projects/${projectId}/domains/${domainId}`);
|
|
313
321
|
}
|
|
314
322
|
pollLogs(deployId, afterId) {
|
|
315
|
-
return this.request("GET", `/deploys/${deployId}/logs?after_id=${afterId}
|
|
323
|
+
return this.request("GET", `/deploys/${deployId}/logs?after_id=${afterId}`, undefined, POLL_TIMEOUT_MS);
|
|
316
324
|
}
|
|
317
325
|
listProjectDeploys(projectId, branch) {
|
|
318
326
|
const qs = branch ? `?branch=${encodeURIComponent(branch)}` : "";
|
package/dist/exit-codes.js
CHANGED
package/dist/logs.js
CHANGED
|
@@ -1,6 +1,29 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
|
-
import {
|
|
2
|
+
import { ApiError } from "./api.js";
|
|
3
|
+
import { detectMode, emit, LayeroError } from "./agent.js";
|
|
3
4
|
const POLL_INTERVAL_MS = 1500;
|
|
5
|
+
// 🚨 ОДИН СБОЙ ОПРОСА — НЕ КОНЕЦ НАБЛЮДЕНИЯ. До 0.11.3 `fetch` шёл без
|
|
6
|
+
// таймаута, а цикл не переживал ни одной сетевой ошибки: 18.09.2026 деплой с
|
|
7
|
+
// токеном дважды подряд молчал 3 и 15 минут и падал с `internal: fetch failed`,
|
|
8
|
+
// хотя обе сборки на платформе дошли до ready (T-20260918-16). Для агента это
|
|
9
|
+
// худший исход: он считает деплой проваленным и запускает его заново.
|
|
10
|
+
// Теперь: таймаут на запрос (api.ts), до MAX_POLL_FAILURES сбоев ПОДРЯД с
|
|
11
|
+
// растущей паузой, и свой код ошибки с подсказкой, где смотреть итог.
|
|
12
|
+
const MAX_POLL_FAILURES = 8;
|
|
13
|
+
const RETRY_BASE_MS = 1500;
|
|
14
|
+
const RETRY_CAP_MS = 10_000;
|
|
15
|
+
/** Сбой, который повторять бессмысленно: сервер ответил отказом по существу. */
|
|
16
|
+
/** Тестам не нужно ждать настоящие паузы. */
|
|
17
|
+
let delayScale = 1;
|
|
18
|
+
export function setRetryDelayScaleForTests(scale) {
|
|
19
|
+
delayScale = scale;
|
|
20
|
+
}
|
|
21
|
+
function retryDelayScale() {
|
|
22
|
+
return delayScale;
|
|
23
|
+
}
|
|
24
|
+
function isFatal(err) {
|
|
25
|
+
return err instanceof ApiError && err.status >= 400 && err.status < 500 && err.status !== 408 && err.status !== 429;
|
|
26
|
+
}
|
|
4
27
|
function colorForStream(stream) {
|
|
5
28
|
switch (stream) {
|
|
6
29
|
case "meta":
|
|
@@ -29,6 +52,8 @@ export async function streamDeployLogs(api, deployId) {
|
|
|
29
52
|
let afterId = 0;
|
|
30
53
|
let lastStage = null;
|
|
31
54
|
let hiddenNoted = false;
|
|
55
|
+
let failures = 0;
|
|
56
|
+
const sleep = (ms) => new Promise((res) => setTimeout(res, retryDelayScale() * ms));
|
|
32
57
|
const announce = (name) => {
|
|
33
58
|
if (name === lastStage)
|
|
34
59
|
return;
|
|
@@ -43,7 +68,21 @@ export async function streamDeployLogs(api, deployId) {
|
|
|
43
68
|
// Loop until terminal — the server's per-stage timeouts bound duration.
|
|
44
69
|
// eslint-disable-next-line no-constant-condition
|
|
45
70
|
while (true) {
|
|
46
|
-
|
|
71
|
+
let poll;
|
|
72
|
+
try {
|
|
73
|
+
poll = await api.pollLogs(deployId, afterId);
|
|
74
|
+
failures = 0;
|
|
75
|
+
}
|
|
76
|
+
catch (err) {
|
|
77
|
+
if (isFatal(err))
|
|
78
|
+
throw err;
|
|
79
|
+
failures += 1;
|
|
80
|
+
if (failures >= MAX_POLL_FAILURES) {
|
|
81
|
+
throw new LayeroError("deploy_watch_lost", `lost connection to the build log after ${failures} attempts (${err.message}); the build itself keeps running on the platform`, `do NOT deploy again — check the result: \`npx layero@latest deploys list --json\`, then \`npx layero@latest logs --deploy ${deployId}\``);
|
|
82
|
+
}
|
|
83
|
+
await sleep(Math.min(RETRY_BASE_MS * 2 ** (failures - 1), RETRY_CAP_MS));
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
47
86
|
// 🚨 Этап объявляем по СТРОКЕ, а не по текущему этапу деплоя. Опрос
|
|
48
87
|
// приносит пачку строк, где хвост прошлого этапа идёт вперемешку с
|
|
49
88
|
// началом нового, а `current_stage` — это уже новый: объявленный до
|
package/package.json
CHANGED