@lmctl-ai/lmctl 0.2.413 → 0.2.415

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.
@@ -364,8 +364,9 @@ async function responseError(response, what, url) {
364
364
  try { detail = String(JSON.parse(detail).message ?? detail); } catch { /* not JSON */ }
365
365
  detail = safe(detail.replace(/\\s+/g, " ")).slice(0, 240);
366
366
  const host = new URL(url).host;
367
- if (response.status === 403 || response.status === 429) {
368
- const reset = response.headers.get("x-ratelimit-reset"), remaining = response.headers.get("x-ratelimit-remaining"), retry = response.headers.get("retry-after");
367
+ const reset = response.headers.get("x-ratelimit-reset"), remaining = response.headers.get("x-ratelimit-remaining"), retry = response.headers.get("retry-after");
368
+ const rateLimited = response.status === 429 || response.status === 403 && (remaining === "0" || !!retry || /rate.?limit/i.test(detail));
369
+ if (rateLimited) {
369
370
  const at = reset && /^\\d+$/.test(reset) ? new Date(Number(reset) * 1000) : null;
370
371
  const minutes = at ? Math.max(0, Math.ceil((at.getTime() - Date.now()) / 60000)) : 0;
371
372
  throw new Error("GitHub rate limit reached: " + what + " got HTTP " + response.status + " from " + host + (detail ? " (" + detail + ")" : "") +
@@ -464,7 +465,9 @@ try {
464
465
  if (found && typeof found.url === "string") downloaded = await request(found.url, "GET", {Accept: "application/octet-stream", ...(token ? {Authorization: "Bearer " + token} : {})});
465
466
  }
466
467
  if (!downloaded.response.ok) await responseError(downloaded.response, "Release asset " + asset + " download", downloaded.url);
467
- const bytes = Buffer.from(await downloaded.response.arrayBuffer());
468
+ let bytes;
469
+ try { bytes = Buffer.from(await downloaded.response.arrayBuffer()); }
470
+ catch (error) { throw new Error("Release asset " + asset + " download was incomplete (truncated or interrupted): " + safe(error && error.message || String(error)) + "; retry the install."); }
468
471
  const sha256 = createHash("sha256").update(bytes).digest("hex");
469
472
  if (expected && sha256 !== expected) throw new Error("release asset " + asset + " SHA-256 mismatch: expected " + expected + ", downloaded " + sha256 + "; refusing to install it (the expected digest is from " + source + ")");
470
473
  writeFileSync(out, bytes, {mode: 0o600});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lmctl-ai/lmctl",
3
- "version": "0.2.413",
3
+ "version": "0.2.415",
4
4
  "description": "A provider-agnostic control plane for teams of AI coding agents — across providers, with independent review and durable memory.",
5
5
  "type": "module",
6
6
  "homepage": "https://lmctl.com",