@eve-horizon/cli 0.2.44 → 0.2.45

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 +63 -12
  2. package/package.json +8 -8
package/dist/index.js CHANGED
@@ -80594,17 +80594,27 @@ async function downloadBytes(url) {
80594
80594
  return Buffer.from(arrayBuffer);
80595
80595
  }
80596
80596
  async function fetchText(url, headers) {
80597
- const response = await fetch(url, {
80598
- headers: {
80599
- "User-Agent": "eve-cli-local-stack",
80600
- Accept: "application/json,text/plain,*/*",
80601
- ...headers ?? {}
80597
+ const maxRetries = 3;
80598
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
80599
+ const response = await fetch(url, {
80600
+ headers: {
80601
+ "User-Agent": "eve-cli-local-stack",
80602
+ Accept: "application/json,text/plain,*/*",
80603
+ ...headers ?? {}
80604
+ }
80605
+ });
80606
+ if (response.status === 429 && attempt < maxRetries) {
80607
+ const retryAfter = parseInt(response.headers.get("retry-after") ?? "", 10);
80608
+ const delay = (retryAfter > 0 ? retryAfter : 2 ** attempt) * 1e3;
80609
+ await new Promise((r) => setTimeout(r, delay));
80610
+ continue;
80602
80611
  }
80603
- });
80604
- if (!response.ok) {
80605
- throw new Error(`Request failed (${response.status}) for ${url}`);
80612
+ if (!response.ok) {
80613
+ throw new Error(`Request failed (${response.status}) for ${url}`);
80614
+ }
80615
+ return response.text();
80606
80616
  }
80607
- return response.text();
80617
+ throw new Error(`Request failed after ${maxRetries} retries for ${url}`);
80608
80618
  }
80609
80619
  function writeExecutable(destination, bytes) {
80610
80620
  const tempPath = `${destination}.tmp-${Date.now()}-${process.pid}`;
@@ -80709,11 +80719,52 @@ async function fetchRegistryTags(imageRef) {
80709
80719
  }
80710
80720
  const registry2 = imageRef.slice(0, slashIndex);
80711
80721
  const repository = imageRef.slice(slashIndex + 1);
80712
- const tagsPayload = await fetchText(
80713
- `https://${registry2}/v2/${repository}/tags/list?n=200`
80714
- );
80722
+ const url = `https://${registry2}/v2/${repository}/tags/list?n=200`;
80723
+ const token = await fetchRegistryBearerToken(registry2, repository);
80724
+ const headers = token ? { Authorization: `Bearer ${token}` } : void 0;
80725
+ const tagsPayload = await fetchText(url, headers);
80715
80726
  return JSON.parse(tagsPayload).tags ?? [];
80716
80727
  }
80728
+ async function fetchRegistryBearerToken(registry2, repository) {
80729
+ const probeUrl = `https://${registry2}/v2/`;
80730
+ const probeResponse = await fetch(probeUrl, {
80731
+ headers: { "User-Agent": "eve-cli-local-stack" }
80732
+ });
80733
+ if (probeResponse.ok) {
80734
+ return void 0;
80735
+ }
80736
+ if (probeResponse.status !== 401) {
80737
+ throw new Error(`Registry probe failed (${probeResponse.status}) for ${probeUrl}`);
80738
+ }
80739
+ const wwwAuth = probeResponse.headers.get("www-authenticate") ?? "";
80740
+ const challenge = parseWwwAuthenticate(wwwAuth);
80741
+ if (!challenge.realm) {
80742
+ throw new Error(`Registry returned 401 without a Bearer realm in WWW-Authenticate header: ${wwwAuth}`);
80743
+ }
80744
+ const tokenUrl = new URL(challenge.realm);
80745
+ if (challenge.service) {
80746
+ tokenUrl.searchParams.set("service", challenge.service);
80747
+ }
80748
+ tokenUrl.searchParams.set("scope", `repository:${repository}:pull`);
80749
+ const tokenResponse = await fetch(tokenUrl.toString(), {
80750
+ headers: { "User-Agent": "eve-cli-local-stack" }
80751
+ });
80752
+ if (!tokenResponse.ok) {
80753
+ throw new Error(`Token exchange failed (${tokenResponse.status}) at ${tokenUrl.toString()}`);
80754
+ }
80755
+ const tokenData = await tokenResponse.json();
80756
+ return tokenData.token ?? tokenData.access_token;
80757
+ }
80758
+ function parseWwwAuthenticate(header) {
80759
+ const result = {};
80760
+ const body = header.replace(/^Bearer\s+/i, "");
80761
+ const pattern = /(\w+)="([^"]*)"/g;
80762
+ let match;
80763
+ while ((match = pattern.exec(body)) !== null) {
80764
+ result[match[1]] = match[2];
80765
+ }
80766
+ return result;
80767
+ }
80717
80768
  function normalizeVersion(value) {
80718
80769
  const trimmed = value.trim();
80719
80770
  if (!trimmed) return null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eve-horizon/cli",
3
- "version": "0.2.44",
3
+ "version": "0.2.45",
4
4
  "description": "Eve Horizon CLI",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -18,9 +18,6 @@
18
18
  "assets",
19
19
  "README.md"
20
20
  ],
21
- "scripts": {
22
- "build": "rm -rf dist && esbuild src/index.ts --bundle --platform=node --target=node22 --outfile=dist/index.js --format=cjs --external:yaml"
23
- },
24
21
  "publishConfig": {
25
22
  "access": "public"
26
23
  },
@@ -31,11 +28,14 @@
31
28
  "yaml": "^2.5.1"
32
29
  },
33
30
  "devDependencies": {
34
- "@eve/migrate": "workspace:*",
35
- "@eve/shared": "workspace:*",
36
31
  "@types/node": "^22.0.0",
37
32
  "esbuild": "^0.27.3",
38
33
  "postgres": "^3.4.0",
39
- "typescript": "^5.7.0"
34
+ "typescript": "^5.7.0",
35
+ "@eve/migrate": "0.0.1",
36
+ "@eve/shared": "0.0.1"
37
+ },
38
+ "scripts": {
39
+ "build": "rm -rf dist && esbuild src/index.ts --bundle --platform=node --target=node22 --outfile=dist/index.js --format=cjs --external:yaml"
40
40
  }
41
- }
41
+ }