@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.
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
|
|
80598
|
-
|
|
80599
|
-
|
|
80600
|
-
|
|
80601
|
-
|
|
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
|
-
|
|
80605
|
-
|
|
80612
|
+
if (!response.ok) {
|
|
80613
|
+
throw new Error(`Request failed (${response.status}) for ${url}`);
|
|
80614
|
+
}
|
|
80615
|
+
return response.text();
|
|
80606
80616
|
}
|
|
80607
|
-
|
|
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
|
|
80713
|
-
|
|
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
|
-
|
|
80749
|
-
|
|
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...");
|