@amd-gaia/agent-ui 0.21.0 → 0.21.2
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/assets/{browser-p1BgTzJo.js → browser-B1LxEoD3.js} +1 -1
- package/dist/assets/index-BaBSHxtq.js +72 -0
- package/dist/assets/index-CGT9a3El.css +1 -0
- package/dist/index.html +3 -3
- package/package.json +1 -1
- package/preload.cjs +22 -1
- package/services/auto-updater.cjs +322 -22
- package/services/backend-installer.cjs +295 -55
- package/dist/assets/index-DRnhDSoO.css +0 -1
- package/dist/assets/index-DmVbNLtH.js +0 -72
|
@@ -36,6 +36,8 @@ const fs = require("fs");
|
|
|
36
36
|
const path = require("path");
|
|
37
37
|
const os = require("os");
|
|
38
38
|
const https = require("https");
|
|
39
|
+
const http = require("http");
|
|
40
|
+
const tls = require("tls");
|
|
39
41
|
|
|
40
42
|
// ── Constants ────────────────────────────────────────────────────────────────
|
|
41
43
|
|
|
@@ -510,6 +512,27 @@ function isTransientNetworkError(output) {
|
|
|
510
512
|
);
|
|
511
513
|
}
|
|
512
514
|
|
|
515
|
+
/**
|
|
516
|
+
* Detect a TLS trust failure in `uv pip install` output. Behind a corporate
|
|
517
|
+
* MITM proxy, PyPI is presented with a certificate signed by a custom root CA
|
|
518
|
+
* that uv's bundled webpki roots don't include, so uv reports
|
|
519
|
+
* "invalid peer certificate: UnknownIssuer" (issue #1693). It surfaces wrapped
|
|
520
|
+
* in "Failed to fetch" / "error sending request", so isTransientNetworkError
|
|
521
|
+
* also matches it — the install loop MUST check this first and retry with
|
|
522
|
+
* --native-tls (the OS trust store, where IT installs the corporate CA) rather
|
|
523
|
+
* than burning the transient retries on the same bundled roots.
|
|
524
|
+
*/
|
|
525
|
+
function isTlsCertError(output) {
|
|
526
|
+
if (!output) return false;
|
|
527
|
+
return (
|
|
528
|
+
/invalid peer certificate/i.test(output) ||
|
|
529
|
+
/unknownissuer/i.test(output) ||
|
|
530
|
+
/certificate verify failed/i.test(output) ||
|
|
531
|
+
/unable to get local issuer certificate/i.test(output) ||
|
|
532
|
+
/self[- ]signed certificate/i.test(output)
|
|
533
|
+
);
|
|
534
|
+
}
|
|
535
|
+
|
|
513
536
|
function sleep(ms) {
|
|
514
537
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
515
538
|
}
|
|
@@ -590,69 +613,234 @@ function checkDiskSpace() {
|
|
|
590
613
|
}
|
|
591
614
|
|
|
592
615
|
/**
|
|
593
|
-
*
|
|
594
|
-
*
|
|
616
|
+
* Read the OS trust store. Node 22+ exposes `tls.getCACertificates("system")`;
|
|
617
|
+
* older Node lacks it, so this returns `[]` there (callers fall back to
|
|
618
|
+
* NODE_EXTRA_CA_CERTS / the bundled Mozilla set).
|
|
619
|
+
*
|
|
620
|
+
* Why this exists: behind a corporate TLS-inspection proxy, the proxy's root
|
|
621
|
+
* CA is installed in the OS trust store but NOT in Node's bundled Mozilla set,
|
|
622
|
+
* so every HTTPS handshake fails with UNABLE_TO_GET_ISSUER_CERT_LOCALLY and the
|
|
623
|
+
* machine looks "offline" when it is online (issue #1572).
|
|
624
|
+
*/
|
|
625
|
+
function _systemCaCertificates() {
|
|
626
|
+
try {
|
|
627
|
+
if (typeof tls.getCACertificates === "function") {
|
|
628
|
+
return tls.getCACertificates("system") || [];
|
|
629
|
+
}
|
|
630
|
+
} catch (err) {
|
|
631
|
+
log(`Could not read system CA store: ${err.message}`);
|
|
632
|
+
}
|
|
633
|
+
return [];
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
/** Read a pinned corporate root CA from NODE_EXTRA_CA_CERTS (any Node version). */
|
|
637
|
+
function _extraCaCertificates() {
|
|
638
|
+
const file = process.env.NODE_EXTRA_CA_CERTS;
|
|
639
|
+
if (!file) return [];
|
|
640
|
+
try {
|
|
641
|
+
return [fs.readFileSync(file, "utf8")];
|
|
642
|
+
} catch (err) {
|
|
643
|
+
log(`Could not read NODE_EXTRA_CA_CERTS (${file}): ${err.message}`);
|
|
644
|
+
return [];
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
/**
|
|
649
|
+
* Build the CA trust bundle for the network probe. Passing `ca` to
|
|
650
|
+
* `https.request` REPLACES Node's default trust store, so when we add system /
|
|
651
|
+
* extra certs we must re-include the bundled Mozilla set. Returns `undefined`
|
|
652
|
+
* when there is nothing extra to add (probe uses Node's default trust store —
|
|
653
|
+
* behaviour unchanged).
|
|
654
|
+
*/
|
|
655
|
+
function buildCaBundle() {
|
|
656
|
+
const extra = [..._systemCaCertificates(), ..._extraCaCertificates()];
|
|
657
|
+
if (extra.length === 0) return undefined;
|
|
658
|
+
return [...new Set([...tls.rootCertificates, ...extra])];
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
/** First non-empty proxy URL from the standard env vars, or null. */
|
|
662
|
+
function proxyForHttps() {
|
|
663
|
+
return (
|
|
664
|
+
process.env.HTTPS_PROXY ||
|
|
665
|
+
process.env.https_proxy ||
|
|
666
|
+
process.env.HTTP_PROXY ||
|
|
667
|
+
process.env.http_proxy ||
|
|
668
|
+
null
|
|
669
|
+
);
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
/**
|
|
673
|
+
* Classify a network error so the caller can tell a trust-store gap (NOT
|
|
674
|
+
* offline) apart from genuine connectivity loss.
|
|
675
|
+
* - "tls" → certificate/handshake failure (machine is online)
|
|
676
|
+
* - "timeout" → request timed out
|
|
677
|
+
* - "connectivity" → DNS/connect failure (likely offline)
|
|
678
|
+
*/
|
|
679
|
+
function classifyNetworkError(err) {
|
|
680
|
+
const code = (err && err.code) || "";
|
|
681
|
+
const message = (err && err.message) || "";
|
|
682
|
+
if (/CERT|SELF_SIGNED|UNABLE_TO_|_SIGNATURE|ALTNAME/.test(code)) {
|
|
683
|
+
return "tls";
|
|
684
|
+
}
|
|
685
|
+
if (code === "ETIMEDOUT" || /timed out/i.test(message)) {
|
|
686
|
+
return "timeout";
|
|
687
|
+
}
|
|
688
|
+
return "connectivity";
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
/** HEAD-probe `target` (a URL) directly, trusting `ca`. */
|
|
692
|
+
function _probeDirect(target, ca, finish, setSock) {
|
|
693
|
+
const opts = {
|
|
694
|
+
host: target.hostname,
|
|
695
|
+
port: target.port || 443,
|
|
696
|
+
path: target.pathname || "/",
|
|
697
|
+
method: "HEAD",
|
|
698
|
+
servername: target.hostname,
|
|
699
|
+
headers: { "User-Agent": "gaia-backend-installer/1.0" },
|
|
700
|
+
};
|
|
701
|
+
if (ca) opts.ca = ca;
|
|
702
|
+
try {
|
|
703
|
+
const req = https.request(opts, (res) => {
|
|
704
|
+
res.resume();
|
|
705
|
+
finish({ ok: true, status: res.statusCode });
|
|
706
|
+
});
|
|
707
|
+
setSock(req);
|
|
708
|
+
req.on("error", (err) =>
|
|
709
|
+
finish({ ok: false, kind: classifyNetworkError(err), message: `${target.href}: ${err.message}` })
|
|
710
|
+
);
|
|
711
|
+
req.end();
|
|
712
|
+
} catch (err) {
|
|
713
|
+
finish({ ok: false, kind: classifyNetworkError(err), message: `${target.href}: ${err.message}` });
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
/** HEAD-probe `target` through an HTTP CONNECT proxy, trusting `ca`. */
|
|
718
|
+
function _probeViaProxy(target, proxy, ca, finish, setSock) {
|
|
719
|
+
let proxyUrl;
|
|
720
|
+
try {
|
|
721
|
+
proxyUrl = new URL(proxy);
|
|
722
|
+
} catch {
|
|
723
|
+
finish({ ok: false, kind: "connectivity", message: `${target.href}: invalid proxy URL "${proxy}"` });
|
|
724
|
+
return;
|
|
725
|
+
}
|
|
726
|
+
const headers = {};
|
|
727
|
+
if (proxyUrl.username) {
|
|
728
|
+
const cred = `${decodeURIComponent(proxyUrl.username)}:${decodeURIComponent(proxyUrl.password)}`;
|
|
729
|
+
headers["Proxy-Authorization"] = "Basic " + Buffer.from(cred).toString("base64");
|
|
730
|
+
}
|
|
731
|
+
const connectReq = http.request({
|
|
732
|
+
host: proxyUrl.hostname,
|
|
733
|
+
port: proxyUrl.port || 80,
|
|
734
|
+
method: "CONNECT",
|
|
735
|
+
path: `${target.hostname}:${target.port || 443}`,
|
|
736
|
+
headers,
|
|
737
|
+
});
|
|
738
|
+
setSock(connectReq);
|
|
739
|
+
connectReq.on("connect", (res, socket) => {
|
|
740
|
+
if (res.statusCode !== 200) {
|
|
741
|
+
try { socket.destroy(); } catch { /* ignore */ }
|
|
742
|
+
finish({ ok: false, kind: "connectivity", message: `${target.href}: proxy CONNECT returned ${res.statusCode}` });
|
|
743
|
+
return;
|
|
744
|
+
}
|
|
745
|
+
setSock(socket);
|
|
746
|
+
const opts = {
|
|
747
|
+
host: target.hostname,
|
|
748
|
+
port: target.port || 443,
|
|
749
|
+
path: target.pathname || "/",
|
|
750
|
+
method: "HEAD",
|
|
751
|
+
socket,
|
|
752
|
+
agent: false,
|
|
753
|
+
servername: target.hostname,
|
|
754
|
+
headers: { "User-Agent": "gaia-backend-installer/1.0" },
|
|
755
|
+
};
|
|
756
|
+
if (ca) opts.ca = ca;
|
|
757
|
+
try {
|
|
758
|
+
const req = https.request(opts, (r) => {
|
|
759
|
+
r.resume();
|
|
760
|
+
finish({ ok: true, status: r.statusCode });
|
|
761
|
+
});
|
|
762
|
+
req.on("error", (err) =>
|
|
763
|
+
finish({ ok: false, kind: classifyNetworkError(err), message: `${target.href}: ${err.message}` })
|
|
764
|
+
);
|
|
765
|
+
req.end();
|
|
766
|
+
} catch (err) {
|
|
767
|
+
finish({ ok: false, kind: classifyNetworkError(err), message: `${target.href}: ${err.message}` });
|
|
768
|
+
}
|
|
769
|
+
});
|
|
770
|
+
connectReq.on("error", (err) =>
|
|
771
|
+
finish({ ok: false, kind: classifyNetworkError(err), message: `${target.href}: ${err.message}` })
|
|
772
|
+
);
|
|
773
|
+
connectReq.end();
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
/**
|
|
777
|
+
* Best-effort reachability probe for a single host. A HEAD that gets any
|
|
778
|
+
* response (even 3xx/4xx) proves connectivity. Trusts the system / pinned CA
|
|
779
|
+
* store (issue #1572) and honors HTTPS_PROXY/HTTP_PROXY. Resolves
|
|
780
|
+
* { ok, status? } or { ok:false, kind, message }.
|
|
595
781
|
*/
|
|
596
782
|
function _checkOneHost(url) {
|
|
597
783
|
return new Promise((resolve) => {
|
|
598
784
|
let settled = false;
|
|
785
|
+
let sock = null;
|
|
786
|
+
let timer = null;
|
|
599
787
|
const finish = (result) => {
|
|
600
788
|
if (settled) return;
|
|
601
789
|
settled = true;
|
|
790
|
+
if (timer) clearTimeout(timer);
|
|
791
|
+
if (sock) {
|
|
792
|
+
try { sock.destroy(); } catch { /* ignore */ }
|
|
793
|
+
}
|
|
602
794
|
resolve(result);
|
|
603
795
|
};
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
{
|
|
609
|
-
method: "HEAD",
|
|
610
|
-
timeout: NETWORK_CHECK_TIMEOUT_MS,
|
|
611
|
-
headers: { "User-Agent": "gaia-backend-installer/1.0" },
|
|
612
|
-
},
|
|
613
|
-
(res) => {
|
|
614
|
-
// Any response (even 3xx/4xx) means we have basic connectivity.
|
|
615
|
-
res.resume();
|
|
616
|
-
finish({ ok: true, status: res.statusCode });
|
|
617
|
-
}
|
|
618
|
-
);
|
|
619
|
-
req.on("timeout", () => {
|
|
620
|
-
req.destroy();
|
|
621
|
-
finish({
|
|
622
|
-
ok: false,
|
|
623
|
-
message: `${url}: timed out after ${NETWORK_CHECK_TIMEOUT_MS / 1000}s`,
|
|
624
|
-
});
|
|
625
|
-
});
|
|
626
|
-
req.on("error", (err) => {
|
|
627
|
-
finish({
|
|
628
|
-
ok: false,
|
|
629
|
-
message: `${url}: ${err.message}`,
|
|
630
|
-
});
|
|
631
|
-
});
|
|
632
|
-
req.end();
|
|
633
|
-
} catch (err) {
|
|
796
|
+
const setSock = (s) => {
|
|
797
|
+
sock = s;
|
|
798
|
+
};
|
|
799
|
+
timer = setTimeout(() => {
|
|
634
800
|
finish({
|
|
635
801
|
ok: false,
|
|
636
|
-
|
|
802
|
+
kind: "timeout",
|
|
803
|
+
message: `${url}: timed out after ${NETWORK_CHECK_TIMEOUT_MS / 1000}s`,
|
|
637
804
|
});
|
|
805
|
+
}, NETWORK_CHECK_TIMEOUT_MS);
|
|
806
|
+
|
|
807
|
+
let target;
|
|
808
|
+
try {
|
|
809
|
+
target = new URL(url);
|
|
810
|
+
} catch (err) {
|
|
811
|
+
finish({ ok: false, kind: "connectivity", message: `${url}: ${err.message}` });
|
|
812
|
+
return;
|
|
813
|
+
}
|
|
814
|
+
const ca = buildCaBundle();
|
|
815
|
+
const proxy = proxyForHttps();
|
|
816
|
+
if (proxy) {
|
|
817
|
+
_probeViaProxy(target, proxy, ca, finish, setSock);
|
|
818
|
+
} else {
|
|
819
|
+
_probeDirect(target, ca, finish, setSock);
|
|
638
820
|
}
|
|
639
821
|
});
|
|
640
822
|
}
|
|
641
823
|
|
|
642
824
|
/**
|
|
643
|
-
* Probe each host in ``NETWORK_CHECK_HOSTS`` sequentially. Succeed as
|
|
644
|
-
*
|
|
645
|
-
*
|
|
825
|
+
* Probe each host in ``NETWORK_CHECK_HOSTS`` sequentially. Succeed as soon as
|
|
826
|
+
* ANY host responds (even 3xx/4xx counts — it proves connectivity). On total
|
|
827
|
+
* failure, report the dominant failure `kind` so the caller can distinguish a
|
|
828
|
+
* trust-store gap ("tls" — online but Node doesn't trust the proxy CA) from a
|
|
829
|
+
* genuine outage ("connectivity").
|
|
646
830
|
*/
|
|
647
831
|
async function checkNetwork() {
|
|
648
832
|
const errors = [];
|
|
833
|
+
const kinds = new Set();
|
|
649
834
|
for (const url of NETWORK_CHECK_HOSTS) {
|
|
650
835
|
const result = await _checkOneHost(url);
|
|
651
|
-
if (result.ok) return result;
|
|
836
|
+
if (result.ok) return { ok: true, status: result.status };
|
|
652
837
|
errors.push(result.message);
|
|
838
|
+
kinds.add(result.kind || "connectivity");
|
|
653
839
|
}
|
|
840
|
+
const allTls = kinds.size > 0 && [...kinds].every((k) => k === "tls");
|
|
654
841
|
return {
|
|
655
842
|
ok: false,
|
|
843
|
+
kind: allTls ? "tls" : kinds.has("connectivity") ? "connectivity" : [...kinds][0] || "connectivity",
|
|
656
844
|
message: `Network check failed for all hosts: ${errors.join("; ")}`,
|
|
657
845
|
};
|
|
658
846
|
}
|
|
@@ -1198,10 +1386,38 @@ async function installBackend(opts = {}) {
|
|
|
1198
1386
|
// would otherwise fail the whole bootstrap (and block a release). File-lock
|
|
1199
1387
|
// failures (Windows os-error-32) are NOT retried — they need user action.
|
|
1200
1388
|
let installResult;
|
|
1201
|
-
|
|
1202
|
-
|
|
1389
|
+
let useNativeTls = false;
|
|
1390
|
+
let nativeTlsAttempted = false;
|
|
1391
|
+
// The TLS branch flips useNativeTls and `continue`s, consuming an iteration.
|
|
1392
|
+
// If the signature only surfaces on the final attempt, extend the loop once
|
|
1393
|
+
// so the promised --native-tls retry actually runs (issue #1693 review).
|
|
1394
|
+
for (
|
|
1395
|
+
let attempt = 1;
|
|
1396
|
+
attempt <= INSTALL_MAX_ATTEMPTS || (useNativeTls && !nativeTlsAttempted);
|
|
1397
|
+
attempt++
|
|
1398
|
+
) {
|
|
1399
|
+
const attemptArgs = useNativeTls ? [...pipArgs, "--native-tls"] : pipArgs;
|
|
1400
|
+
if (useNativeTls) nativeTlsAttempted = true;
|
|
1401
|
+
installResult = await runCommand("uv", attemptArgs, { stageLabel: "pip" });
|
|
1203
1402
|
if (installResult.code === 0) break;
|
|
1204
1403
|
const attemptOutput = `${installResult.stdout || ""}\n${installResult.stderr || ""}`;
|
|
1404
|
+
// A corporate proxy's custom root CA isn't in uv's bundled webpki roots, so
|
|
1405
|
+
// the fetch fails with UnknownIssuer. This LOOKS transient ("Failed to
|
|
1406
|
+
// fetch") but retrying the bundled roots can't fix it — retry once with
|
|
1407
|
+
// --native-tls so uv trusts the OS certificate store instead (issue #1693).
|
|
1408
|
+
if (!useNativeTls && isTlsCertError(attemptOutput)) {
|
|
1409
|
+
useNativeTls = true;
|
|
1410
|
+
log(
|
|
1411
|
+
"TLS trust failure (corporate proxy CA not in uv's bundled roots) — " +
|
|
1412
|
+
"retrying install with --native-tls to use the OS certificate store"
|
|
1413
|
+
);
|
|
1414
|
+
report(
|
|
1415
|
+
STAGES.INSTALL_PACKAGE,
|
|
1416
|
+
0,
|
|
1417
|
+
"Certificate trust issue — retrying with the system certificate store"
|
|
1418
|
+
);
|
|
1419
|
+
continue;
|
|
1420
|
+
}
|
|
1205
1421
|
const canRetry =
|
|
1206
1422
|
attempt < INSTALL_MAX_ATTEMPTS &&
|
|
1207
1423
|
isTransientNetworkError(attemptOutput) &&
|
|
@@ -1225,11 +1441,23 @@ async function installBackend(opts = {}) {
|
|
|
1225
1441
|
// main.cjs should have killed it first; if the lock persists, point the
|
|
1226
1442
|
// user at the live process rather than the generic manual-install hint.
|
|
1227
1443
|
const output = `${installResult.stdout || ""}\n${installResult.stderr || ""}`;
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1444
|
+
let suggestion;
|
|
1445
|
+
if (isFileLockedError(output)) {
|
|
1446
|
+
suggestion =
|
|
1447
|
+
"A running GAIA process is locking the install. Close GAIA completely (including any leftover gaia.exe in Task Manager), then relaunch.";
|
|
1448
|
+
} else if (isTlsCertError(output)) {
|
|
1449
|
+
suggestion =
|
|
1450
|
+
"PyPI's certificate isn't trusted on this network — usually a corporate proxy presenting its own root CA. " +
|
|
1451
|
+
(nativeTlsAttempted
|
|
1452
|
+
? "GAIA retried with the OS certificate store and it still failed. "
|
|
1453
|
+
: "") +
|
|
1454
|
+
"Ask IT to install the proxy's root CA in your system's trusted certificate store, then relaunch GAIA. " +
|
|
1455
|
+
"See https://amd-gaia.ai/quickstart#cli-install";
|
|
1456
|
+
} else {
|
|
1457
|
+
suggestion = `Try installing manually:\n uv pip install ${pipPackage} --python ${
|
|
1458
|
+
IS_WINDOWS ? `${GAIA_VENV_DISPLAY}/Scripts/python.exe` : `${GAIA_VENV_DISPLAY}/bin/python`
|
|
1459
|
+
}\nThen restart GAIA. See https://amd-gaia.ai/quickstart#cli-install`;
|
|
1460
|
+
}
|
|
1233
1461
|
throw new InstallError(
|
|
1234
1462
|
`Failed to install ${pipPackage} (pip exit ${installResult.code}).`,
|
|
1235
1463
|
{
|
|
@@ -1426,18 +1654,25 @@ async function ensureBackend(opts = {}) {
|
|
|
1426
1654
|
throw err;
|
|
1427
1655
|
}
|
|
1428
1656
|
|
|
1429
|
-
// Network check
|
|
1657
|
+
// Network check is ADVISORY, never fatal. A failed HEAD probe is most often
|
|
1658
|
+
// a corporate TLS-inspection proxy whose root CA isn't in Node's bundled
|
|
1659
|
+
// trust store (#1572) — the machine is online and `uv`/`pip`, which honor
|
|
1660
|
+
// HTTPS_PROXY and the system trust store, install fine. If the host really
|
|
1661
|
+
// is unreachable, the install step below fails loudly with an accurate
|
|
1662
|
+
// uv/pip error, which is more actionable than a generic "offline".
|
|
1430
1663
|
if (!preChecks.network.ok) {
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1664
|
+
if (preChecks.network.kind === "tls") {
|
|
1665
|
+
log(
|
|
1666
|
+
`Network pre-check hit a TLS/certificate error (not offline): ${preChecks.network.message}. ` +
|
|
1667
|
+
"Typically a corporate proxy root CA missing from Node's trust store — " +
|
|
1668
|
+
"set NODE_EXTRA_CA_CERTS to it (or relaunch with --use-system-ca) if the install fails. Proceeding."
|
|
1669
|
+
);
|
|
1670
|
+
} else {
|
|
1671
|
+
log(
|
|
1672
|
+
`Network pre-check could not reach any host: ${preChecks.network.message}. ` +
|
|
1673
|
+
"Proceeding anyway; the install step will surface an accurate error if the network is truly unreachable."
|
|
1674
|
+
);
|
|
1675
|
+
}
|
|
1441
1676
|
}
|
|
1442
1677
|
|
|
1443
1678
|
// Fast-path: already installed at the expected version.
|
|
@@ -1521,6 +1756,11 @@ module.exports = {
|
|
|
1521
1756
|
checkNetwork,
|
|
1522
1757
|
isFileLockedError,
|
|
1523
1758
|
isTransientNetworkError,
|
|
1759
|
+
isTlsCertError,
|
|
1760
|
+
buildCaBundle,
|
|
1761
|
+
proxyForHttps,
|
|
1762
|
+
classifyNetworkError,
|
|
1763
|
+
_checkOneHost,
|
|
1524
1764
|
|
|
1525
1765
|
// State machine
|
|
1526
1766
|
getState,
|