@archal/cli 0.7.5 → 0.7.6
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/index.js +77 -17
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1257,19 +1257,50 @@ ${stderrPreview}`);
|
|
|
1257
1257
|
var HTTP_COLLECT_TIMEOUT_MS = 1e4;
|
|
1258
1258
|
var HTTP_COLLECT_MAX_RETRIES = 2;
|
|
1259
1259
|
var HTTP_COLLECT_BACKOFF_MS = [1e3, 3e3];
|
|
1260
|
-
|
|
1260
|
+
var HTTP_RETRYABLE_STATUS_CODES = /* @__PURE__ */ new Set([408, 425, 429, 500, 502, 503, 504]);
|
|
1261
|
+
var HTTP_PUSH_TIMEOUT_MS = 2e4;
|
|
1262
|
+
var HTTP_PUSH_MAX_RETRIES = 6;
|
|
1263
|
+
var HTTP_PUSH_BACKOFF_MS = [1e3, 2e3, 3e3, 5e3, 5e3, 5e3];
|
|
1264
|
+
function resolveRetryDelay(backoffMs, attempt, fallbackMs) {
|
|
1265
|
+
const indexed = backoffMs[attempt];
|
|
1266
|
+
if (typeof indexed === "number" && Number.isFinite(indexed) && indexed >= 0) {
|
|
1267
|
+
return indexed;
|
|
1268
|
+
}
|
|
1269
|
+
const last = backoffMs.length > 0 ? backoffMs[backoffMs.length - 1] : void 0;
|
|
1270
|
+
if (typeof last === "number" && Number.isFinite(last) && last >= 0) {
|
|
1271
|
+
return last;
|
|
1272
|
+
}
|
|
1273
|
+
return fallbackMs;
|
|
1274
|
+
}
|
|
1275
|
+
async function fetchWithRetry(url, options, retryOptions) {
|
|
1276
|
+
const retries = retryOptions?.retries ?? HTTP_COLLECT_MAX_RETRIES;
|
|
1277
|
+
const timeoutMs = retryOptions?.timeoutMs ?? HTTP_COLLECT_TIMEOUT_MS;
|
|
1278
|
+
const backoffMs = retryOptions?.backoffMs ?? HTTP_COLLECT_BACKOFF_MS;
|
|
1261
1279
|
let lastError;
|
|
1262
1280
|
for (let attempt = 0; attempt <= retries; attempt++) {
|
|
1263
1281
|
try {
|
|
1264
1282
|
const response = await fetch(url, {
|
|
1265
1283
|
...options,
|
|
1266
|
-
signal: AbortSignal.timeout(
|
|
1284
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
1267
1285
|
});
|
|
1286
|
+
if (!response.ok && HTTP_RETRYABLE_STATUS_CODES.has(response.status) && attempt < retries) {
|
|
1287
|
+
const delay = resolveRetryDelay(backoffMs, attempt, 3e3);
|
|
1288
|
+
let bodyPreview = "";
|
|
1289
|
+
try {
|
|
1290
|
+
bodyPreview = (await response.clone().text()).slice(0, 180);
|
|
1291
|
+
} catch {
|
|
1292
|
+
}
|
|
1293
|
+
debug(
|
|
1294
|
+
`HTTP fetch got ${response.status} (attempt ${attempt + 1}/${retries + 1}), retrying in ${delay}ms${bodyPreview ? `: ${bodyPreview}` : ""}`
|
|
1295
|
+
);
|
|
1296
|
+
await new Promise((resolve13) => setTimeout(resolve13, delay));
|
|
1297
|
+
continue;
|
|
1298
|
+
}
|
|
1268
1299
|
return response;
|
|
1269
1300
|
} catch (err) {
|
|
1270
1301
|
lastError = err;
|
|
1271
1302
|
if (attempt < retries) {
|
|
1272
|
-
const delay =
|
|
1303
|
+
const delay = resolveRetryDelay(backoffMs, attempt, 3e3);
|
|
1273
1304
|
debug(`HTTP fetch failed (attempt ${attempt + 1}/${retries + 1}), retrying in ${delay}ms: ${err instanceof Error ? err.message : String(err)}`);
|
|
1274
1305
|
await new Promise((resolve13) => setTimeout(resolve13, delay));
|
|
1275
1306
|
}
|
|
@@ -1309,7 +1340,6 @@ Cannot proceed \u2014 evaluator would receive empty state and produce unreliable
|
|
|
1309
1340
|
}
|
|
1310
1341
|
return state;
|
|
1311
1342
|
}
|
|
1312
|
-
var HTTP_PUSH_TIMEOUT_MS = 2e4;
|
|
1313
1343
|
async function pushStateToCloud(twinUrls, seedSelections, bearerToken, adminAuth) {
|
|
1314
1344
|
const headers = adminAuth ? {
|
|
1315
1345
|
"x-archal-admin-token": adminAuth.token,
|
|
@@ -1325,12 +1355,19 @@ async function pushStateToCloud(twinUrls, seedSelections, bearerToken, adminAuth
|
|
|
1325
1355
|
}
|
|
1326
1356
|
const url = `${twinBasePath(baseUrl)}/state`;
|
|
1327
1357
|
debug(`Pushing dynamic seed to ${sel.twinName}`, { url });
|
|
1328
|
-
const response = await
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1358
|
+
const response = await fetchWithRetry(
|
|
1359
|
+
url,
|
|
1360
|
+
{
|
|
1361
|
+
method: "PUT",
|
|
1362
|
+
headers,
|
|
1363
|
+
body: JSON.stringify(sel.seedData)
|
|
1364
|
+
},
|
|
1365
|
+
{
|
|
1366
|
+
retries: HTTP_PUSH_MAX_RETRIES,
|
|
1367
|
+
timeoutMs: HTTP_PUSH_TIMEOUT_MS,
|
|
1368
|
+
backoffMs: HTTP_PUSH_BACKOFF_MS
|
|
1369
|
+
}
|
|
1370
|
+
);
|
|
1334
1371
|
if (!response.ok) {
|
|
1335
1372
|
const text = await response.text().catch(() => "");
|
|
1336
1373
|
throw new Error(
|
|
@@ -2291,6 +2328,30 @@ function getConfiguredApiBaseUrl() {
|
|
|
2291
2328
|
return explicit ?? getConfiguredAuthBaseUrl();
|
|
2292
2329
|
}
|
|
2293
2330
|
var REQUEST_TIMEOUT_MS = 8e3;
|
|
2331
|
+
var AUTH_MAX_RETRIES = 2;
|
|
2332
|
+
var AUTH_RETRY_BACKOFF_MS = [500, 1500];
|
|
2333
|
+
var AUTH_RETRYABLE_CODES = /* @__PURE__ */ new Set([502, 503, 504, 429]);
|
|
2334
|
+
async function fetchAuthWithRetry(url, options) {
|
|
2335
|
+
let lastError;
|
|
2336
|
+
for (let attempt = 0; attempt <= AUTH_MAX_RETRIES; attempt++) {
|
|
2337
|
+
try {
|
|
2338
|
+
const response = await fetch(url, {
|
|
2339
|
+
...options,
|
|
2340
|
+
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
|
|
2341
|
+
});
|
|
2342
|
+
if (response.ok || !AUTH_RETRYABLE_CODES.has(response.status) || attempt >= AUTH_MAX_RETRIES) {
|
|
2343
|
+
return response;
|
|
2344
|
+
}
|
|
2345
|
+
lastError = new Error(`HTTP ${response.status}`);
|
|
2346
|
+
} catch (err) {
|
|
2347
|
+
lastError = err;
|
|
2348
|
+
if (attempt >= AUTH_MAX_RETRIES) break;
|
|
2349
|
+
}
|
|
2350
|
+
const delay = AUTH_RETRY_BACKOFF_MS[attempt] ?? 1500;
|
|
2351
|
+
await new Promise((resolve13) => setTimeout(resolve13, delay));
|
|
2352
|
+
}
|
|
2353
|
+
throw lastError;
|
|
2354
|
+
}
|
|
2294
2355
|
var ENV_TOKEN_FALLBACK_TTL_SECONDS = 10 * 365 * 24 * 60 * 60;
|
|
2295
2356
|
function getCredentialsPath() {
|
|
2296
2357
|
return join4(ensureArchalDir(), CREDENTIALS_FILE);
|
|
@@ -2636,15 +2697,14 @@ async function exchangeCliAuthCode(input) {
|
|
|
2636
2697
|
"ARCHAL_AUTH_URL is required for browser login when ARCHAL_STRICT_ENDPOINTS=1. Set ARCHAL_AUTH_URL and run `archal login` again."
|
|
2637
2698
|
);
|
|
2638
2699
|
}
|
|
2639
|
-
const response = await
|
|
2700
|
+
const response = await fetchAuthWithRetry(`${authBaseUrl}/auth/cli/token`, {
|
|
2640
2701
|
method: "POST",
|
|
2641
2702
|
headers: {
|
|
2642
2703
|
"content-type": "application/json",
|
|
2643
2704
|
"user-agent": CLI_USER_AGENT,
|
|
2644
2705
|
"x-archal-cli-version": CLI_VERSION
|
|
2645
2706
|
},
|
|
2646
|
-
body: JSON.stringify(input)
|
|
2647
|
-
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
|
|
2707
|
+
body: JSON.stringify(input)
|
|
2648
2708
|
});
|
|
2649
2709
|
if (!response.ok) {
|
|
2650
2710
|
throw new Error(`Login failed during code exchange (${response.status})`);
|
|
@@ -2672,15 +2732,14 @@ async function refreshCliSession(creds) {
|
|
|
2672
2732
|
if (!authBaseUrl) {
|
|
2673
2733
|
return null;
|
|
2674
2734
|
}
|
|
2675
|
-
const response = await
|
|
2735
|
+
const response = await fetchAuthWithRetry(`${authBaseUrl}/auth/cli/refresh`, {
|
|
2676
2736
|
method: "POST",
|
|
2677
2737
|
headers: {
|
|
2678
2738
|
"content-type": "application/json",
|
|
2679
2739
|
"user-agent": CLI_USER_AGENT,
|
|
2680
2740
|
"x-archal-cli-version": CLI_VERSION
|
|
2681
2741
|
},
|
|
2682
|
-
body: JSON.stringify({ refreshToken: creds.refreshToken })
|
|
2683
|
-
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
|
|
2742
|
+
body: JSON.stringify({ refreshToken: creds.refreshToken })
|
|
2684
2743
|
});
|
|
2685
2744
|
if (!response.ok) {
|
|
2686
2745
|
return null;
|
|
@@ -12471,6 +12530,7 @@ async function callTool(baseUrl: string, name: string, args: Record<string, unkn
|
|
|
12471
12530
|
method: 'POST',
|
|
12472
12531
|
headers: getAuthHeaders(),
|
|
12473
12532
|
body: JSON.stringify({ name, arguments: args }),
|
|
12533
|
+
signal: AbortSignal.timeout(30_000),
|
|
12474
12534
|
});
|
|
12475
12535
|
const text = await res.text();
|
|
12476
12536
|
if (!res.ok) throw new Error(\`\${name} failed (HTTP \${res.status}): \${text}\`);
|
|
@@ -12481,7 +12541,7 @@ async function main(): Promise<void> {
|
|
|
12481
12541
|
const baseUrl = getTwinUrl();
|
|
12482
12542
|
|
|
12483
12543
|
// 1. Discover available tools
|
|
12484
|
-
const toolsRes = await fetch(\`\${baseUrl}/tools\`, { headers: getAuthHeaders() });
|
|
12544
|
+
const toolsRes = await fetch(\`\${baseUrl}/tools\`, { headers: getAuthHeaders(), signal: AbortSignal.timeout(10_000) });
|
|
12485
12545
|
const tools: Tool[] = await toolsRes.json();
|
|
12486
12546
|
console.error(\`Connected: \${tools.length} tools available\`);
|
|
12487
12547
|
|