@eve-horizon/cli 0.2.44 → 0.2.46

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.
@@ -53,6 +53,8 @@ spec:
53
53
  value: "4749"
54
54
  - name: EVE_DEFAULT_DOMAIN
55
55
  value: lvh.me
56
+ - name: EVE_ORG_FS_ROOT
57
+ value: /org
56
58
  ports:
57
59
  - name: http
58
60
  containerPort: 4749
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;
@@ -80740,14 +80791,17 @@ async function importPlatformImages(version2, runtimeOptions) {
80740
80791
  const docker = requireToolPath("docker", "Install Docker Desktop.");
80741
80792
  const k3d = requireToolPath("k3d", "Run 'eve local up' again to auto-install managed tools.");
80742
80793
  const stdio = runtimeOptions.verbose && !runtimeOptions.quiet ? "inherit" : "pipe";
80794
+ ensureDockerRegistryAuth(docker, runtimeOptions);
80743
80795
  for (const image of PLATFORM_IMAGES) {
80744
80796
  const remoteTag = `${image.remote}:${version2}`;
80745
80797
  printProgress(runtimeOptions, `Pulling image ${remoteTag}...`);
80746
80798
  const pull = pullImageWithRetry(docker, remoteTag, stdio, runtimeOptions);
80747
80799
  if (pull.status !== 0) {
80748
- throw new Error(
80749
- `Failed to pull ${remoteTag}. Ensure image availability/access at ${image.remote} and the version exists. Try: eve local up --version <x.y.z>`
80750
- );
80800
+ const pullOutput = `${pull.stderr}
80801
+ ${pull.stdout}`.toLowerCase();
80802
+ const isAuthError = pullOutput.includes("403") || pullOutput.includes("401") || pullOutput.includes("unauthorized");
80803
+ const hint = isAuthError ? "This is likely an ECR authentication issue. Run: aws ecr-public get-login-password --region us-east-1 | docker login --username AWS --password-stdin public.ecr.aws" : `Ensure image availability/access at ${image.remote} and the version exists. Try: eve local up --version <x.y.z>`;
80804
+ throw new Error(`Failed to pull ${remoteTag}. ${hint}`);
80751
80805
  }
80752
80806
  run(docker, ["tag", remoteTag, image.local], { stdio });
80753
80807
  printProgress(runtimeOptions, `Importing image ${image.component} into k3d...`);
@@ -80778,7 +80832,7 @@ function pullImageWithRetry(docker, remoteTag, stdio, runtimeOptions) {
80778
80832
  }
80779
80833
  const combined = `${pull.stderr}
80780
80834
  ${pull.stdout}`.toLowerCase();
80781
- const retryable = combined.includes("unexpected eof") || combined.includes("short read") || combined.includes("i/o timeout") || combined.includes("timed out");
80835
+ const retryable = combined.includes("unexpected eof") || combined.includes("short read") || combined.includes("i/o timeout") || combined.includes("timed out") || combined.includes("403 forbidden") || combined.includes("toomanyrequests");
80782
80836
  if (!retryable || attempt === maxAttempts) {
80783
80837
  return pull;
80784
80838
  }
@@ -80786,6 +80840,42 @@ ${pull.stdout}`.toLowerCase();
80786
80840
  }
80787
80841
  return { status: 1, stdout: "", stderr: "pull retry exhausted" };
80788
80842
  }
80843
+ function ensureDockerRegistryAuth(docker, runtimeOptions) {
80844
+ const registry2 = PLATFORM_IMAGE_REGISTRY.split("/")[0];
80845
+ if (!registry2 || !registry2.includes("ecr.aws")) {
80846
+ return;
80847
+ }
80848
+ printProgress(runtimeOptions, "Authenticating Docker to ECR public registry...");
80849
+ if (dockerLoginViaAwsCli(docker, registry2)) {
80850
+ printProgress(runtimeOptions, "Docker registry auth succeeded.");
80851
+ return;
80852
+ }
80853
+ printProgress(
80854
+ runtimeOptions,
80855
+ "Warning: could not authenticate to ECR. Pulls may hit rate limits.\n Install AWS CLI and configure credentials to avoid this: https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html"
80856
+ );
80857
+ }
80858
+ function dockerLoginViaAwsCli(docker, registry2) {
80859
+ const aws = findExecutable("aws");
80860
+ if (!aws) {
80861
+ return false;
80862
+ }
80863
+ const token = run(aws, ["ecr-public", "get-login-password", "--region", "us-east-1"], {
80864
+ stdio: "pipe",
80865
+ allowFailure: true,
80866
+ timeoutMs: 15e3
80867
+ });
80868
+ if (token.status !== 0 || !token.stdout.trim()) {
80869
+ return false;
80870
+ }
80871
+ const login = (0, import_node_child_process10.spawnSync)(docker, ["login", "--username", "AWS", "--password-stdin", registry2], {
80872
+ input: token.stdout.trim(),
80873
+ encoding: "utf8",
80874
+ stdio: ["pipe", "pipe", "pipe"],
80875
+ timeout: 15e3
80876
+ });
80877
+ return login.status === 0;
80878
+ }
80789
80879
  function applyLocalManifests(runtimeOptions) {
80790
80880
  const kubectl = requireToolPath("kubectl", "Run 'eve local up' again to auto-install managed tools.");
80791
80881
  printProgress(runtimeOptions, "Applying local Kubernetes manifests...");
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.46",
4
4
  "description": "Eve Horizon CLI",
5
5
  "license": "MIT",
6
6
  "repository": {