run402 1.34.0 → 1.34.1
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/lib/deploy.mjs +71 -1
- package/package.json +2 -1
package/lib/deploy.mjs
CHANGED
|
@@ -1,8 +1,72 @@
|
|
|
1
1
|
import { readFileSync } from "fs";
|
|
2
2
|
import { dirname, resolve } from "path";
|
|
3
|
+
import { Agent } from "undici";
|
|
3
4
|
import { API, allowanceAuthHeaders, findProject } from "./config.mjs";
|
|
4
5
|
import { resolveFilePathsInManifest, resolveMigrationsFile } from "./manifest.mjs";
|
|
5
6
|
|
|
7
|
+
// Custom undici dispatcher with longer timeouts for large-batch deploys.
|
|
8
|
+
// Default Node undici headersTimeout is ~5 min; large image uploads can exceed it.
|
|
9
|
+
const deployDispatcher = new Agent({
|
|
10
|
+
headersTimeout: 600_000, // 10 min
|
|
11
|
+
bodyTimeout: 600_000,
|
|
12
|
+
connectTimeout: 30_000,
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
// Retry policy for transient network errors and 5xx gateway errors.
|
|
16
|
+
// We retry on these because they tend to be load-shedding/blip-related; we do
|
|
17
|
+
// NOT retry on other 4xx/5xx (402, 400, etc.) — those are deterministic.
|
|
18
|
+
const RETRY_CAUSE_CODES = new Set([
|
|
19
|
+
"UND_ERR_HEADERS_TIMEOUT",
|
|
20
|
+
"UND_ERR_BODY_TIMEOUT",
|
|
21
|
+
"UND_ERR_SOCKET",
|
|
22
|
+
"ECONNRESET",
|
|
23
|
+
"ECONNREFUSED",
|
|
24
|
+
"ETIMEDOUT",
|
|
25
|
+
]);
|
|
26
|
+
const RETRY_HTTP_STATUSES = new Set([502, 503, 504]);
|
|
27
|
+
|
|
28
|
+
function isRetriableError(err) {
|
|
29
|
+
if (!err) return false;
|
|
30
|
+
const code = err.code || (err.cause && err.cause.code);
|
|
31
|
+
return typeof code === "string" && RETRY_CAUSE_CODES.has(code);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function sleep(ms) {
|
|
35
|
+
return new Promise((r) => setTimeout(r, ms));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Fetch with bounded retries on transient errors. Exported for testability.
|
|
40
|
+
* - 2 retries (3 total attempts)
|
|
41
|
+
* - Backoff ~1s then ~4s with small jitter
|
|
42
|
+
* - Retries on RETRY_CAUSE_CODES network errors and RETRY_HTTP_STATUSES
|
|
43
|
+
* - Silent: no stdout noise on retry (CLI is agent-first)
|
|
44
|
+
*/
|
|
45
|
+
export async function fetchWithRetry(url, init, { attempts = 3 } = {}) {
|
|
46
|
+
for (let attempt = 1; attempt <= attempts; attempt++) {
|
|
47
|
+
try {
|
|
48
|
+
const res = await fetch(url, init);
|
|
49
|
+
if (attempt < attempts && RETRY_HTTP_STATUSES.has(res.status)) {
|
|
50
|
+
// Drain body so the connection can be reused, then retry.
|
|
51
|
+
try { await res.arrayBuffer(); } catch { /* noop */ }
|
|
52
|
+
const delay = (attempt === 1 ? 1000 : 4000) + Math.floor(Math.random() * 250);
|
|
53
|
+
await sleep(delay);
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
return res;
|
|
57
|
+
} catch (err) {
|
|
58
|
+
if (attempt < attempts && isRetriableError(err)) {
|
|
59
|
+
const delay = (attempt === 1 ? 1000 : 4000) + Math.floor(Math.random() * 250);
|
|
60
|
+
await sleep(delay);
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
throw err;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
// Unreachable: the loop above either returns a response or throws.
|
|
67
|
+
throw new Error("fetchWithRetry: exhausted attempts without returning");
|
|
68
|
+
}
|
|
69
|
+
|
|
6
70
|
const HELP = `run402 deploy — Deploy to an existing project on Run402
|
|
7
71
|
|
|
8
72
|
Usage:
|
|
@@ -113,7 +177,13 @@ export async function run(args) {
|
|
|
113
177
|
delete manifest.name;
|
|
114
178
|
|
|
115
179
|
const authHeaders = allowanceAuthHeaders("/deploy/v1");
|
|
116
|
-
const
|
|
180
|
+
const body = JSON.stringify(manifest);
|
|
181
|
+
const res = await fetchWithRetry(`${API}/deploy/v1`, {
|
|
182
|
+
method: "POST",
|
|
183
|
+
headers: { "Content-Type": "application/json", ...authHeaders },
|
|
184
|
+
body,
|
|
185
|
+
dispatcher: deployDispatcher,
|
|
186
|
+
});
|
|
117
187
|
|
|
118
188
|
// Content-type aware parsing: gateways (ALB, CloudFront, etc.) return HTML on
|
|
119
189
|
// 504/413/etc., which would otherwise crash res.json() with SyntaxError.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "run402",
|
|
3
|
-
"version": "1.34.
|
|
3
|
+
"version": "1.34.1",
|
|
4
4
|
"description": "CLI for Run402 — provision Postgres databases, deploy static sites, generate images, and manage wallets via x402 and MPP micropayments.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
"@x402/evm": "^2.6.0",
|
|
21
21
|
"@x402/fetch": "^2.6.0",
|
|
22
22
|
"mppx": "^0.4.7",
|
|
23
|
+
"undici": "^8.0.2",
|
|
23
24
|
"viem": "^2.47.1"
|
|
24
25
|
},
|
|
25
26
|
"engines": {
|