@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.
Files changed (2) hide show
  1. package/dist/index.js +77 -17
  2. 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
- async function fetchWithRetry(url, options, retries = HTTP_COLLECT_MAX_RETRIES) {
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(HTTP_COLLECT_TIMEOUT_MS)
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 = HTTP_COLLECT_BACKOFF_MS[attempt] ?? 3e3;
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 fetch(url, {
1329
- method: "PUT",
1330
- headers,
1331
- body: JSON.stringify(sel.seedData),
1332
- signal: AbortSignal.timeout(HTTP_PUSH_TIMEOUT_MS)
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 fetch(`${authBaseUrl}/auth/cli/token`, {
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 fetch(`${authBaseUrl}/auth/cli/refresh`, {
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
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@archal/cli",
3
- "version": "0.7.5",
3
+ "version": "0.7.6",
4
4
  "description": "Pre-deployment testing for AI agents",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",