@delta-infra/cli 1.0.75 → 1.0.77
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/README.md +427 -411
- package/checksums.txt +5 -5
- package/package.json +2 -1
- package/scripts/install-wizard.js +596 -596
- package/scripts/install.js +400 -400
- package/scripts/run.js +65 -65
- package/skills/antbo-ldm-guard/SKILL.md +58 -54
- package/skills/antbo-ldm-guard/agents/openai.yaml +4 -4
- package/skills/antbo-service/SKILL.md +79 -74
- package/skills/antbo-service/agents/openai.yaml +4 -4
- package/skills/antbo-service/references/operations.md +45 -45
- package/skills/delta-bo/SKILL.md +36 -31
- package/skills/delta-bo/agents/openai.yaml +4 -4
- package/skills/delta-bo/references/endpoints.md +263 -263
- package/skills/delta-sandbox/SKILL.md +356 -356
- package/skills/delta-sandbox/agents/openai.yaml +4 -4
- package/skills/delta-sandbox/references/commands.md +56 -56
- package/skills/delta-sandbox/references/lifecycle.md +92 -92
- package/skills/delta-sandbox/references/recipes.md +133 -133
- package/skills/delta-science/SKILL.md +171 -166
- package/skills/delta-science/agents/openai.yaml +4 -4
- package/skills/delta-science/references/cli-contract.md +84 -84
- package/skills/delta-science/references/generated-catalog.md +48 -0
- package/skills/delta-science/references/services.md +112 -112
- package/skills/delta-science/references/workflows.md +44 -44
- package/skills/delta-science/scripts/generated_catalog.py +69 -0
- package/skills/delta-science/scripts/invoke.py +263 -329
- package/skills/delta-shared/SKILL.md +95 -95
- package/skills/delta-shared/agents/openai.yaml +4 -4
- package/skills/gsasii/SKILL.md +42 -37
- package/skills/gsasii/agents/openai.yaml +4 -4
- package/skills/gsasii/references/endpoints.md +130 -130
- package/skills/lammps/SKILL.md +39 -34
- package/skills/lammps/agents/openai.yaml +4 -4
- package/skills/lammps/references/endpoints.md +61 -61
- package/skills/ldm-bo/SKILL.md +35 -30
- package/skills/ldm-bo/agents/openai.yaml +4 -4
- package/skills/ldm-bo/references/endpoints.md +127 -127
- package/skills/manifest.json +17 -17
- package/skills/pubchem/SKILL.md +93 -88
- package/skills/pubchem/agents/openai.yaml +4 -4
- package/skills/pubchem/references/endpoints.md +122 -122
- package/skills/pubchem/references/operations.md +41 -41
- package/skills/pymatgen/SKILL.md +54 -49
- package/skills/pymatgen/agents/openai.yaml +4 -4
- package/skills/pymatgen/references/endpoints.md +66 -66
- package/skills/rdkit/SKILL.md +112 -107
- package/skills/rdkit/agents/openai.yaml +4 -4
- package/skills/rdkit/references/endpoints.md +133 -133
- package/skills/rdkit/references/operations.md +64 -64
- package/skills/science-tools.json +127 -0
- package/skills/synbo-service/SKILL.md +36 -31
- package/skills/synbo-service/agents/openai.yaml +4 -4
- package/skills/synbo-service/references/endpoints.md +157 -157
package/scripts/install.js
CHANGED
|
@@ -1,277 +1,277 @@
|
|
|
1
|
-
// Copyright (c) 2026 Delta Infra Authors
|
|
2
|
-
// SPDX-License-Identifier: MIT
|
|
3
|
-
|
|
4
|
-
const fs = require("fs");
|
|
5
|
-
const path = require("path");
|
|
6
|
-
const { execFileSync } = require("child_process");
|
|
7
|
-
const os = require("os");
|
|
8
|
-
const crypto = require("crypto");
|
|
9
|
-
|
|
10
|
-
const PKG = "@delta-infra/cli";
|
|
11
|
-
|
|
12
|
-
// Resolve the version to download:
|
|
13
|
-
// env var > npm registry > local package.json (fallback)
|
|
14
|
-
function resolveVersion() {
|
|
15
|
-
if (process.env.DELTA_CLI_VERSION) return process.env.DELTA_CLI_VERSION.replace(/^v/, "");
|
|
16
|
-
try {
|
|
17
|
-
const out = require("child_process").execFileSync("npm", ["view", PKG, "version"], {
|
|
18
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
19
|
-
timeout: 15000,
|
|
20
|
-
encoding: "utf8",
|
|
21
|
-
});
|
|
22
|
-
const ver = out.trim();
|
|
23
|
-
if (/^\d+\.\d+\.\d+/.test(ver)) return ver;
|
|
24
|
-
} catch {}
|
|
25
|
-
return require("../package.json").version.replace(/-.*$/, "");
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const VERSION = resolveVersion();
|
|
29
|
-
const NAME = "delta-cli";
|
|
30
|
-
const REPO = "yzailab/delta-infra-cli";
|
|
31
|
-
const DEFAULT_ALLOWED_HOSTS = new Set([
|
|
32
|
-
"github.com",
|
|
33
|
-
"gh-proxy.com",
|
|
34
|
-
"ghproxy.net",
|
|
35
|
-
"gh.ddlc.top",
|
|
36
|
-
"objects.githubusercontent.com",
|
|
37
|
-
"release-assets.githubusercontent.com",
|
|
38
|
-
"github-releases.githubusercontent.com",
|
|
39
|
-
"raw.githubusercontent.com",
|
|
40
|
-
"registry.npmmirror.com",
|
|
41
|
-
]);
|
|
42
|
-
const DEFAULT_ALLOWED_HOST_PREFIXES = [
|
|
43
|
-
"github-production-release-asset-",
|
|
44
|
-
];
|
|
45
|
-
|
|
46
|
-
const PLATFORM_MAP = {
|
|
47
|
-
darwin: "darwin",
|
|
48
|
-
linux: "linux",
|
|
49
|
-
win32: "windows",
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
const ARCH_MAP = {
|
|
53
|
-
x64: "amd64",
|
|
54
|
-
arm64: "arm64",
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
const platform = PLATFORM_MAP[process.platform];
|
|
58
|
-
const arch = ARCH_MAP[process.arch];
|
|
59
|
-
|
|
60
|
-
const isWindows = process.platform === "win32";
|
|
61
|
-
const ext = isWindows ? ".zip" : ".tar.gz";
|
|
62
|
-
const archiveName = `${NAME}-${platform}-${arch}${ext}`;
|
|
63
|
-
const GITHUB_URL = `https://github.com/${REPO}/releases/download/v${VERSION}/${archiveName}`;
|
|
64
|
-
|
|
65
|
-
const binDir = path.join(__dirname, "..", "bin");
|
|
66
|
-
const dest = path.join(binDir, NAME + (isWindows ? ".exe" : ""));
|
|
67
|
-
|
|
68
|
-
// ── Mirror URL resolution ──────────────────────────────────────────────────
|
|
69
|
-
|
|
70
|
-
var _curlTimeoutMs = null;
|
|
71
|
-
|
|
72
|
-
function _getCurlTimeout() {
|
|
73
|
-
if (_curlTimeoutMs !== null) return _curlTimeoutMs;
|
|
74
|
-
var env = process.env.DELTA_CLI_DOWNLOAD_TIMEOUT;
|
|
75
|
-
var t = env ? parseInt(env, 10) : 0;
|
|
76
|
-
_curlTimeoutMs = t > 0 ? t : 60000;
|
|
77
|
-
return _curlTimeoutMs;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// GitHub release download mirrors.
|
|
81
|
-
// Format: [hostname, urlPrefix] — urlPrefix is prepended to the GitHub URL.
|
|
82
|
-
var _MIRROR_LIST = [
|
|
83
|
-
["gh.ddlc.top", "https://gh.ddlc.top"],
|
|
84
|
-
["ghproxy.net", "https://ghproxy.net"],
|
|
85
|
-
["gh-proxy.com", "https://gh-proxy.com"],
|
|
86
|
-
];
|
|
87
|
-
|
|
88
|
-
// Quick connectivity check: fetch a small file (checksums.txt) from each
|
|
89
|
-
// mirror and return only the ones that respond within the probe timeout.
|
|
90
|
-
function probeMirrors(version, cb) {
|
|
91
|
-
console.error("Probing " + _MIRROR_LIST.length + " download mirrors...");
|
|
92
|
-
var probeUrl = "/https://github.com/" + REPO + "/releases/download/v" + version + "/checksums.txt";
|
|
93
|
-
var results = [];
|
|
94
|
-
var pending = _MIRROR_LIST.length;
|
|
95
|
-
var done = false;
|
|
96
|
-
|
|
97
|
-
function finish() {
|
|
98
|
-
if (done) return;
|
|
99
|
-
done = true;
|
|
100
|
-
if (results.length > 0) {
|
|
101
|
-
console.error("Found " + results.length + " available mirror(s)");
|
|
102
|
-
} else {
|
|
103
|
-
console.error("No mirror responded, falling back to GitHub source");
|
|
104
|
-
}
|
|
105
|
-
cb(results);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
if (pending === 0) return finish();
|
|
109
|
-
|
|
110
|
-
for (var i = 0; i < _MIRROR_LIST.length; i++) {
|
|
111
|
-
(function(idx) {
|
|
112
|
-
var entry = _MIRROR_LIST[idx];
|
|
113
|
-
var host = entry[0];
|
|
114
|
-
var prefix = entry[1];
|
|
115
|
-
var testUrl = prefix + probeUrl;
|
|
116
|
-
var args = [
|
|
117
|
-
"--fail", "--location", "--silent", "--show-error",
|
|
118
|
-
"--connect-timeout", "3", "--max-time", "5",
|
|
119
|
-
"--output", "/dev/null", testUrl,
|
|
120
|
-
];
|
|
121
|
-
var child;
|
|
122
|
-
try {
|
|
123
|
-
child = require("child_process").spawn("curl", args, { stdio: ["ignore", "pipe", "pipe"], timeout: 5000 });
|
|
124
|
-
var stdout = "";
|
|
125
|
-
var stderr = "";
|
|
126
|
-
child.stdout.on("data", function(d) { stdout += d; });
|
|
127
|
-
child.stderr.on("data", function(d) { stderr += d; });
|
|
128
|
-
child.on("close", function(code) {
|
|
129
|
-
if (code === 0) {
|
|
130
|
-
results.push(prefix + "/https://github.com/" + REPO + "/releases/download/v" + version + "/" + archiveName);
|
|
131
|
-
}
|
|
132
|
-
pending--;
|
|
133
|
-
if (pending === 0) finish();
|
|
134
|
-
});
|
|
135
|
-
child.on("error", function() {
|
|
136
|
-
pending--;
|
|
137
|
-
if (pending === 0) finish();
|
|
138
|
-
});
|
|
139
|
-
} catch (e) {
|
|
140
|
-
pending--;
|
|
141
|
-
if (pending === 0) finish();
|
|
142
|
-
}
|
|
143
|
-
})(i);
|
|
144
|
-
}
|
|
145
|
-
// Safety timeout: if probes hang, continue without mirrors after 10s
|
|
146
|
-
setTimeout(finish, 5000);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
function releaseAssetUrls(version, assetName, cb) {
|
|
150
|
-
var urls = [];
|
|
151
|
-
var mirror = process.env.DELTA_CLI_MIRROR || "";
|
|
152
|
-
if (mirror) {
|
|
153
|
-
urls.push("" + mirror.replace(/\/$/, "") + "/yzailab/delta-infra-cli/releases/download/v" + version + "/" + assetName);
|
|
154
|
-
}
|
|
155
|
-
// Probe mirrors in parallel first, then fallback
|
|
156
|
-
probeMirrors(version, function(mirrorUrls) {
|
|
157
|
-
for (var i = 0; i < mirrorUrls.length; i++) {
|
|
158
|
-
urls.push(mirrorUrls[i]);
|
|
159
|
-
}
|
|
160
|
-
// GitHub source (last resort)
|
|
161
|
-
urls.push(GITHUB_URL);
|
|
162
|
-
cb(urls);
|
|
163
|
-
});
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
function assertAllowedHost(url) {
|
|
167
|
-
const { hostname } = new URL(url);
|
|
168
|
-
const allowlist = new Set(DEFAULT_ALLOWED_HOSTS);
|
|
169
|
-
(process.env.DELTA_CLI_MIRROR_ALLOWLIST || "")
|
|
170
|
-
.split(",")
|
|
171
|
-
.map((h) => h.trim().toLowerCase())
|
|
172
|
-
.filter(Boolean)
|
|
173
|
-
.forEach((h) => allowlist.add(h));
|
|
174
|
-
for (const allowed of allowlist) {
|
|
175
|
-
if (hostname === allowed || hostname.endsWith("." + allowed)) return;
|
|
176
|
-
}
|
|
177
|
-
for (const prefix of DEFAULT_ALLOWED_HOST_PREFIXES) {
|
|
178
|
-
if (hostname.startsWith(prefix)) return;
|
|
179
|
-
}
|
|
180
|
-
throw new Error(`Download host not allowed: ${hostname}`);
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
// ── curl version detection (for --ssl-revoke-best-effort) ──────────────────
|
|
184
|
-
|
|
185
|
-
function isCurlVersionSupported(versionOutput) {
|
|
186
|
-
const match = String(versionOutput).match(/^\s*curl\s+(\d+)\.(\d+)\.(\d+)/i);
|
|
187
|
-
if (!match) return false;
|
|
188
|
-
const major = parseInt(match[1], 10);
|
|
189
|
-
const minor = parseInt(match[2], 10);
|
|
190
|
-
return major > 7 || (major === 7 && minor >= 70);
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
let _curlSupportsSslRevokeBestEffort;
|
|
194
|
-
|
|
195
|
-
function curlSupportsSslRevokeBestEffort() {
|
|
196
|
-
if (_curlSupportsSslRevokeBestEffort !== undefined) {
|
|
197
|
-
return _curlSupportsSslRevokeBestEffort;
|
|
198
|
-
}
|
|
199
|
-
try {
|
|
200
|
-
const output = execFileSync("curl", ["--version"], {
|
|
201
|
-
stdio: ["ignore", "pipe", "ignore"],
|
|
202
|
-
encoding: "utf8",
|
|
203
|
-
timeout: 5000,
|
|
204
|
-
});
|
|
205
|
-
_curlSupportsSslRevokeBestEffort = isCurlVersionSupported(output);
|
|
206
|
-
} catch {
|
|
207
|
-
_curlSupportsSslRevokeBestEffort = false;
|
|
208
|
-
}
|
|
209
|
-
return _curlSupportsSslRevokeBestEffort;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
// ── Download ───────────────────────────────────────────────────────────────
|
|
213
|
-
|
|
214
|
-
function download(url, destPath) {
|
|
215
|
-
assertAllowedHost(url);
|
|
216
|
-
const args = [
|
|
217
|
-
"--fail", "--location", "--silent", "--show-error",
|
|
218
|
-
"--connect-timeout", "10",
|
|
219
|
-
"--max-time", "45",
|
|
220
|
-
"--speed-limit", "50000", "--speed-time", "10",
|
|
221
|
-
"--max-redirs", "3",
|
|
222
|
-
"--output", destPath,
|
|
223
|
-
];
|
|
224
|
-
if (isWindows && curlSupportsSslRevokeBestEffort()) {
|
|
225
|
-
args.unshift("--ssl-revoke-best-effort");
|
|
226
|
-
}
|
|
227
|
-
args.push(url);
|
|
228
|
-
execFileSync("curl", args, { stdio: ["ignore", "ignore", "pipe"] });
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
// ── Extraction ─────────────────────────────────────────────────────────────
|
|
232
|
-
|
|
233
|
-
function extractZipWindows(archivePath, destDir) {
|
|
234
|
-
const psOpts = ["-NoProfile", "-ExecutionPolicy", "Bypass", "-Command"];
|
|
235
|
-
const psEnv = {
|
|
236
|
-
DELTA_CLI_ARCHIVE: archivePath,
|
|
237
|
-
DELTA_CLI_DEST: destDir,
|
|
238
|
-
};
|
|
239
|
-
try {
|
|
240
|
-
const dotnet =
|
|
241
|
-
"$ErrorActionPreference='Stop';" +
|
|
242
|
-
"Add-Type -AssemblyName System.IO.Compression.FileSystem;" +
|
|
243
|
-
"[System.IO.Compression.ZipFile]::ExtractToDirectory($env:DELTA_CLI_ARCHIVE,$env:DELTA_CLI_DEST)";
|
|
244
|
-
execFileSync("powershell.exe", [...psOpts, dotnet], { stdio: ["ignore", "inherit", "inherit"], env: psEnv });
|
|
245
|
-
} catch (primaryErr) {
|
|
246
|
-
try {
|
|
247
|
-
const cmdlet =
|
|
248
|
-
"$ErrorActionPreference='Stop';" +
|
|
249
|
-
"Expand-Archive -LiteralPath $env:DELTA_CLI_ARCHIVE -DestinationPath $env:DELTA_CLI_DEST -Force";
|
|
250
|
-
execFileSync("powershell.exe", [...psOpts, cmdlet], { stdio: ["ignore", "inherit", "inherit"], env: psEnv });
|
|
251
|
-
} catch (secondErr) {
|
|
252
|
-
try {
|
|
253
|
-
execFileSync("tar", ["-xf", archivePath, "-C", destDir], { stdio: "ignore" });
|
|
254
|
-
} catch (fallbackErr) {
|
|
255
|
-
throw new Error(
|
|
256
|
-
`Failed to extract ${archivePath}. ` +
|
|
257
|
-
`.NET ZipFile: ${primaryErr.message}. ` +
|
|
258
|
-
`Expand-Archive: ${secondErr.message}. ` +
|
|
259
|
-
`tar: ${fallbackErr.message}`
|
|
260
|
-
);
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
// ── Checksum ───────────────────────────────────────────────────────────────
|
|
267
|
-
|
|
268
|
-
function getExpectedChecksum(archiveName, checksumsDir) {
|
|
269
|
-
const dir = checksumsDir || path.join(__dirname, "..");
|
|
270
|
-
const checksumsPath = path.join(dir, "checksums.txt");
|
|
271
|
-
if (!fs.existsSync(checksumsPath)) {
|
|
272
|
-
console.error("[WARN] checksums.txt not found, skipping checksum verification");
|
|
273
|
-
return null;
|
|
274
|
-
}
|
|
1
|
+
// Copyright (c) 2026 Delta Infra Authors
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
|
|
4
|
+
const fs = require("fs");
|
|
5
|
+
const path = require("path");
|
|
6
|
+
const { execFileSync } = require("child_process");
|
|
7
|
+
const os = require("os");
|
|
8
|
+
const crypto = require("crypto");
|
|
9
|
+
|
|
10
|
+
const PKG = "@delta-infra/cli";
|
|
11
|
+
|
|
12
|
+
// Resolve the version to download:
|
|
13
|
+
// env var > npm registry > local package.json (fallback)
|
|
14
|
+
function resolveVersion() {
|
|
15
|
+
if (process.env.DELTA_CLI_VERSION) return process.env.DELTA_CLI_VERSION.replace(/^v/, "");
|
|
16
|
+
try {
|
|
17
|
+
const out = require("child_process").execFileSync("npm", ["view", PKG, "version"], {
|
|
18
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
19
|
+
timeout: 15000,
|
|
20
|
+
encoding: "utf8",
|
|
21
|
+
});
|
|
22
|
+
const ver = out.trim();
|
|
23
|
+
if (/^\d+\.\d+\.\d+/.test(ver)) return ver;
|
|
24
|
+
} catch {}
|
|
25
|
+
return require("../package.json").version.replace(/-.*$/, "");
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const VERSION = resolveVersion();
|
|
29
|
+
const NAME = "delta-cli";
|
|
30
|
+
const REPO = "yzailab/delta-infra-cli";
|
|
31
|
+
const DEFAULT_ALLOWED_HOSTS = new Set([
|
|
32
|
+
"github.com",
|
|
33
|
+
"gh-proxy.com",
|
|
34
|
+
"ghproxy.net",
|
|
35
|
+
"gh.ddlc.top",
|
|
36
|
+
"objects.githubusercontent.com",
|
|
37
|
+
"release-assets.githubusercontent.com",
|
|
38
|
+
"github-releases.githubusercontent.com",
|
|
39
|
+
"raw.githubusercontent.com",
|
|
40
|
+
"registry.npmmirror.com",
|
|
41
|
+
]);
|
|
42
|
+
const DEFAULT_ALLOWED_HOST_PREFIXES = [
|
|
43
|
+
"github-production-release-asset-",
|
|
44
|
+
];
|
|
45
|
+
|
|
46
|
+
const PLATFORM_MAP = {
|
|
47
|
+
darwin: "darwin",
|
|
48
|
+
linux: "linux",
|
|
49
|
+
win32: "windows",
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const ARCH_MAP = {
|
|
53
|
+
x64: "amd64",
|
|
54
|
+
arm64: "arm64",
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const platform = PLATFORM_MAP[process.platform];
|
|
58
|
+
const arch = ARCH_MAP[process.arch];
|
|
59
|
+
|
|
60
|
+
const isWindows = process.platform === "win32";
|
|
61
|
+
const ext = isWindows ? ".zip" : ".tar.gz";
|
|
62
|
+
const archiveName = `${NAME}-${platform}-${arch}${ext}`;
|
|
63
|
+
const GITHUB_URL = `https://github.com/${REPO}/releases/download/v${VERSION}/${archiveName}`;
|
|
64
|
+
|
|
65
|
+
const binDir = path.join(__dirname, "..", "bin");
|
|
66
|
+
const dest = path.join(binDir, NAME + (isWindows ? ".exe" : ""));
|
|
67
|
+
|
|
68
|
+
// ── Mirror URL resolution ──────────────────────────────────────────────────
|
|
69
|
+
|
|
70
|
+
var _curlTimeoutMs = null;
|
|
71
|
+
|
|
72
|
+
function _getCurlTimeout() {
|
|
73
|
+
if (_curlTimeoutMs !== null) return _curlTimeoutMs;
|
|
74
|
+
var env = process.env.DELTA_CLI_DOWNLOAD_TIMEOUT;
|
|
75
|
+
var t = env ? parseInt(env, 10) : 0;
|
|
76
|
+
_curlTimeoutMs = t > 0 ? t : 60000;
|
|
77
|
+
return _curlTimeoutMs;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// GitHub release download mirrors.
|
|
81
|
+
// Format: [hostname, urlPrefix] — urlPrefix is prepended to the GitHub URL.
|
|
82
|
+
var _MIRROR_LIST = [
|
|
83
|
+
["gh.ddlc.top", "https://gh.ddlc.top"],
|
|
84
|
+
["ghproxy.net", "https://ghproxy.net"],
|
|
85
|
+
["gh-proxy.com", "https://gh-proxy.com"],
|
|
86
|
+
];
|
|
87
|
+
|
|
88
|
+
// Quick connectivity check: fetch a small file (checksums.txt) from each
|
|
89
|
+
// mirror and return only the ones that respond within the probe timeout.
|
|
90
|
+
function probeMirrors(version, cb) {
|
|
91
|
+
console.error("Probing " + _MIRROR_LIST.length + " download mirrors...");
|
|
92
|
+
var probeUrl = "/https://github.com/" + REPO + "/releases/download/v" + version + "/checksums.txt";
|
|
93
|
+
var results = [];
|
|
94
|
+
var pending = _MIRROR_LIST.length;
|
|
95
|
+
var done = false;
|
|
96
|
+
|
|
97
|
+
function finish() {
|
|
98
|
+
if (done) return;
|
|
99
|
+
done = true;
|
|
100
|
+
if (results.length > 0) {
|
|
101
|
+
console.error("Found " + results.length + " available mirror(s)");
|
|
102
|
+
} else {
|
|
103
|
+
console.error("No mirror responded, falling back to GitHub source");
|
|
104
|
+
}
|
|
105
|
+
cb(results);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (pending === 0) return finish();
|
|
109
|
+
|
|
110
|
+
for (var i = 0; i < _MIRROR_LIST.length; i++) {
|
|
111
|
+
(function(idx) {
|
|
112
|
+
var entry = _MIRROR_LIST[idx];
|
|
113
|
+
var host = entry[0];
|
|
114
|
+
var prefix = entry[1];
|
|
115
|
+
var testUrl = prefix + probeUrl;
|
|
116
|
+
var args = [
|
|
117
|
+
"--fail", "--location", "--silent", "--show-error",
|
|
118
|
+
"--connect-timeout", "3", "--max-time", "5",
|
|
119
|
+
"--output", "/dev/null", testUrl,
|
|
120
|
+
];
|
|
121
|
+
var child;
|
|
122
|
+
try {
|
|
123
|
+
child = require("child_process").spawn("curl", args, { stdio: ["ignore", "pipe", "pipe"], timeout: 5000 });
|
|
124
|
+
var stdout = "";
|
|
125
|
+
var stderr = "";
|
|
126
|
+
child.stdout.on("data", function(d) { stdout += d; });
|
|
127
|
+
child.stderr.on("data", function(d) { stderr += d; });
|
|
128
|
+
child.on("close", function(code) {
|
|
129
|
+
if (code === 0) {
|
|
130
|
+
results.push(prefix + "/https://github.com/" + REPO + "/releases/download/v" + version + "/" + archiveName);
|
|
131
|
+
}
|
|
132
|
+
pending--;
|
|
133
|
+
if (pending === 0) finish();
|
|
134
|
+
});
|
|
135
|
+
child.on("error", function() {
|
|
136
|
+
pending--;
|
|
137
|
+
if (pending === 0) finish();
|
|
138
|
+
});
|
|
139
|
+
} catch (e) {
|
|
140
|
+
pending--;
|
|
141
|
+
if (pending === 0) finish();
|
|
142
|
+
}
|
|
143
|
+
})(i);
|
|
144
|
+
}
|
|
145
|
+
// Safety timeout: if probes hang, continue without mirrors after 10s
|
|
146
|
+
setTimeout(finish, 5000);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function releaseAssetUrls(version, assetName, cb) {
|
|
150
|
+
var urls = [];
|
|
151
|
+
var mirror = process.env.DELTA_CLI_MIRROR || "";
|
|
152
|
+
if (mirror) {
|
|
153
|
+
urls.push("" + mirror.replace(/\/$/, "") + "/yzailab/delta-infra-cli/releases/download/v" + version + "/" + assetName);
|
|
154
|
+
}
|
|
155
|
+
// Probe mirrors in parallel first, then fallback
|
|
156
|
+
probeMirrors(version, function(mirrorUrls) {
|
|
157
|
+
for (var i = 0; i < mirrorUrls.length; i++) {
|
|
158
|
+
urls.push(mirrorUrls[i]);
|
|
159
|
+
}
|
|
160
|
+
// GitHub source (last resort)
|
|
161
|
+
urls.push(GITHUB_URL);
|
|
162
|
+
cb(urls);
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function assertAllowedHost(url) {
|
|
167
|
+
const { hostname } = new URL(url);
|
|
168
|
+
const allowlist = new Set(DEFAULT_ALLOWED_HOSTS);
|
|
169
|
+
(process.env.DELTA_CLI_MIRROR_ALLOWLIST || "")
|
|
170
|
+
.split(",")
|
|
171
|
+
.map((h) => h.trim().toLowerCase())
|
|
172
|
+
.filter(Boolean)
|
|
173
|
+
.forEach((h) => allowlist.add(h));
|
|
174
|
+
for (const allowed of allowlist) {
|
|
175
|
+
if (hostname === allowed || hostname.endsWith("." + allowed)) return;
|
|
176
|
+
}
|
|
177
|
+
for (const prefix of DEFAULT_ALLOWED_HOST_PREFIXES) {
|
|
178
|
+
if (hostname.startsWith(prefix)) return;
|
|
179
|
+
}
|
|
180
|
+
throw new Error(`Download host not allowed: ${hostname}`);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// ── curl version detection (for --ssl-revoke-best-effort) ──────────────────
|
|
184
|
+
|
|
185
|
+
function isCurlVersionSupported(versionOutput) {
|
|
186
|
+
const match = String(versionOutput).match(/^\s*curl\s+(\d+)\.(\d+)\.(\d+)/i);
|
|
187
|
+
if (!match) return false;
|
|
188
|
+
const major = parseInt(match[1], 10);
|
|
189
|
+
const minor = parseInt(match[2], 10);
|
|
190
|
+
return major > 7 || (major === 7 && minor >= 70);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
let _curlSupportsSslRevokeBestEffort;
|
|
194
|
+
|
|
195
|
+
function curlSupportsSslRevokeBestEffort() {
|
|
196
|
+
if (_curlSupportsSslRevokeBestEffort !== undefined) {
|
|
197
|
+
return _curlSupportsSslRevokeBestEffort;
|
|
198
|
+
}
|
|
199
|
+
try {
|
|
200
|
+
const output = execFileSync("curl", ["--version"], {
|
|
201
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
202
|
+
encoding: "utf8",
|
|
203
|
+
timeout: 5000,
|
|
204
|
+
});
|
|
205
|
+
_curlSupportsSslRevokeBestEffort = isCurlVersionSupported(output);
|
|
206
|
+
} catch {
|
|
207
|
+
_curlSupportsSslRevokeBestEffort = false;
|
|
208
|
+
}
|
|
209
|
+
return _curlSupportsSslRevokeBestEffort;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// ── Download ───────────────────────────────────────────────────────────────
|
|
213
|
+
|
|
214
|
+
function download(url, destPath) {
|
|
215
|
+
assertAllowedHost(url);
|
|
216
|
+
const args = [
|
|
217
|
+
"--fail", "--location", "--silent", "--show-error",
|
|
218
|
+
"--connect-timeout", "10",
|
|
219
|
+
"--max-time", "45",
|
|
220
|
+
"--speed-limit", "50000", "--speed-time", "10",
|
|
221
|
+
"--max-redirs", "3",
|
|
222
|
+
"--output", destPath,
|
|
223
|
+
];
|
|
224
|
+
if (isWindows && curlSupportsSslRevokeBestEffort()) {
|
|
225
|
+
args.unshift("--ssl-revoke-best-effort");
|
|
226
|
+
}
|
|
227
|
+
args.push(url);
|
|
228
|
+
execFileSync("curl", args, { stdio: ["ignore", "ignore", "pipe"] });
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// ── Extraction ─────────────────────────────────────────────────────────────
|
|
232
|
+
|
|
233
|
+
function extractZipWindows(archivePath, destDir) {
|
|
234
|
+
const psOpts = ["-NoProfile", "-ExecutionPolicy", "Bypass", "-Command"];
|
|
235
|
+
const psEnv = {
|
|
236
|
+
DELTA_CLI_ARCHIVE: archivePath,
|
|
237
|
+
DELTA_CLI_DEST: destDir,
|
|
238
|
+
};
|
|
239
|
+
try {
|
|
240
|
+
const dotnet =
|
|
241
|
+
"$ErrorActionPreference='Stop';" +
|
|
242
|
+
"Add-Type -AssemblyName System.IO.Compression.FileSystem;" +
|
|
243
|
+
"[System.IO.Compression.ZipFile]::ExtractToDirectory($env:DELTA_CLI_ARCHIVE,$env:DELTA_CLI_DEST)";
|
|
244
|
+
execFileSync("powershell.exe", [...psOpts, dotnet], { stdio: ["ignore", "inherit", "inherit"], env: psEnv });
|
|
245
|
+
} catch (primaryErr) {
|
|
246
|
+
try {
|
|
247
|
+
const cmdlet =
|
|
248
|
+
"$ErrorActionPreference='Stop';" +
|
|
249
|
+
"Expand-Archive -LiteralPath $env:DELTA_CLI_ARCHIVE -DestinationPath $env:DELTA_CLI_DEST -Force";
|
|
250
|
+
execFileSync("powershell.exe", [...psOpts, cmdlet], { stdio: ["ignore", "inherit", "inherit"], env: psEnv });
|
|
251
|
+
} catch (secondErr) {
|
|
252
|
+
try {
|
|
253
|
+
execFileSync("tar", ["-xf", archivePath, "-C", destDir], { stdio: "ignore" });
|
|
254
|
+
} catch (fallbackErr) {
|
|
255
|
+
throw new Error(
|
|
256
|
+
`Failed to extract ${archivePath}. ` +
|
|
257
|
+
`.NET ZipFile: ${primaryErr.message}. ` +
|
|
258
|
+
`Expand-Archive: ${secondErr.message}. ` +
|
|
259
|
+
`tar: ${fallbackErr.message}`
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// ── Checksum ───────────────────────────────────────────────────────────────
|
|
267
|
+
|
|
268
|
+
function getExpectedChecksum(archiveName, checksumsDir) {
|
|
269
|
+
const dir = checksumsDir || path.join(__dirname, "..");
|
|
270
|
+
const checksumsPath = path.join(dir, "checksums.txt");
|
|
271
|
+
if (!fs.existsSync(checksumsPath)) {
|
|
272
|
+
console.error("[WARN] checksums.txt not found, skipping checksum verification");
|
|
273
|
+
return null;
|
|
274
|
+
}
|
|
275
275
|
const content = fs.readFileSync(checksumsPath, "utf8");
|
|
276
276
|
for (const line of content.split("\n")) {
|
|
277
277
|
const trimmed = line.trim();
|
|
@@ -282,129 +282,129 @@ function getExpectedChecksum(archiveName, checksumsDir) {
|
|
|
282
282
|
const name = match[2];
|
|
283
283
|
if (name === archiveName) return hash;
|
|
284
284
|
}
|
|
285
|
-
throw new Error(`Checksum entry not found for ${archiveName}`);
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
function verifyChecksum(archivePath, expectedHash) {
|
|
289
|
-
if (expectedHash === null) return;
|
|
290
|
-
const hash = crypto.createHash("sha256");
|
|
291
|
-
const fd = fs.openSync(archivePath, "r");
|
|
292
|
-
try {
|
|
293
|
-
const buf = Buffer.alloc(64 * 1024);
|
|
294
|
-
let bytesRead;
|
|
295
|
-
while ((bytesRead = fs.readSync(fd, buf, 0, buf.length, null)) > 0) {
|
|
296
|
-
hash.update(buf.subarray(0, bytesRead));
|
|
297
|
-
}
|
|
298
|
-
} finally {
|
|
299
|
-
fs.closeSync(fd);
|
|
300
|
-
}
|
|
301
|
-
const actual = hash.digest("hex");
|
|
302
|
-
if (actual.toLowerCase() !== expectedHash.toLowerCase()) {
|
|
303
|
-
throw new Error(
|
|
304
|
-
`[SECURITY] Checksum mismatch for ${path.basename(archivePath)}: expected ${expectedHash} but got ${actual}`
|
|
305
|
-
);
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
// ── Main install ───────────────────────────────────────────────────────────
|
|
310
|
-
|
|
311
|
-
function install() {
|
|
312
|
-
fs.mkdirSync(binDir, { recursive: true });
|
|
313
|
-
var tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "delta-cli-"));
|
|
314
|
-
var archivePath = path.join(tmpDir, archiveName);
|
|
315
|
-
|
|
316
|
-
releaseAssetUrls(VERSION, archiveName, function(downloadUrls) {
|
|
317
|
-
try {
|
|
318
|
-
var lastErr;
|
|
319
|
-
var downloaded = false;
|
|
320
|
-
for (var i = 0; i < downloadUrls.length; i++) {
|
|
321
|
-
try {
|
|
322
|
-
console.error("Downloading from " + downloadUrls[i]);
|
|
323
|
-
download(downloadUrls[i], archivePath);
|
|
324
|
-
console.error("Download complete, verifying checksum...");
|
|
325
|
-
const expectedHash = getExpectedChecksum(archiveName);
|
|
326
|
-
verifyChecksum(archivePath, expectedHash);
|
|
327
|
-
console.error("Checksum verified");
|
|
328
|
-
downloaded = true;
|
|
329
|
-
break;
|
|
330
|
-
} catch (e) {
|
|
331
|
-
console.error("Failed: " + e.message + " (" + downloadUrls[i] + ")");
|
|
332
|
-
lastErr = e;
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
if (!downloaded) throw lastErr;
|
|
336
|
-
|
|
337
|
-
if (isWindows) {
|
|
338
|
-
console.error("Extracting archive...");
|
|
339
|
-
extractZipWindows(archivePath, tmpDir);
|
|
340
|
-
} else {
|
|
341
|
-
console.error("Extracting archive...");
|
|
342
|
-
execFileSync("tar", ["-xzf", archivePath, "-C", tmpDir], { stdio: "ignore" });
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
const binaryName = NAME + (isWindows ? ".exe" : "");
|
|
346
|
-
const extractedBinary = path.join(tmpDir, NAME + "-" + platform + "-" + arch);
|
|
347
|
-
if (fs.existsSync(extractedBinary)) {
|
|
348
|
-
fs.copyFileSync(extractedBinary, dest);
|
|
349
|
-
} else {
|
|
350
|
-
// Fallback: try the plain name (older release format)
|
|
351
|
-
const plainBinary = path.join(tmpDir, binaryName);
|
|
352
|
-
if (fs.existsSync(plainBinary)) {
|
|
353
|
-
fs.copyFileSync(plainBinary, dest);
|
|
354
|
-
} else {
|
|
355
|
-
// Search in tmpDir
|
|
356
|
-
const files = fs.readdirSync(tmpDir).filter(function(f) { return f.indexOf(NAME) !== -1 || f.indexOf(binaryName) !== -1; });
|
|
357
|
-
if (files.length > 0) {
|
|
358
|
-
fs.copyFileSync(path.join(tmpDir, files[0]), dest);
|
|
359
|
-
} else {
|
|
360
|
-
throw new Error("Binary not found in extracted archive. Files: " + fs.readdirSync(tmpDir).join(", "));
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
fs.chmodSync(dest, 0o755);
|
|
365
|
-
console.error("Installed to " + dest);
|
|
366
|
-
} finally {
|
|
367
|
-
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
368
|
-
}
|
|
369
|
-
});
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
// ── Entry ──────────────────────────────────────────────────────────────────
|
|
373
|
-
|
|
374
|
-
if (require.main === module) {
|
|
375
|
-
if (!platform || !arch) {
|
|
376
|
-
console.error(`Unsupported platform: ${process.platform}-${process.arch}`);
|
|
377
|
-
process.exit(1);
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
// npx postinstall guard: skip binary download in ephemeral npx context
|
|
381
|
-
const isNpxPostinstall =
|
|
382
|
-
process.env.npm_command === "exec" && !process.env.DELTA_CLI_RUN;
|
|
383
|
-
|
|
384
|
-
if (isNpxPostinstall) {
|
|
385
|
-
process.exit(0);
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
try {
|
|
389
|
-
install();
|
|
390
|
-
console.log(`${NAME} v${VERSION} installed successfully`);
|
|
391
|
-
} catch (err) {
|
|
392
|
-
console.error(`Failed to install ${NAME}:`, err.message);
|
|
393
|
-
console.error(
|
|
394
|
-
`\nIf you are behind a firewall or in a restricted network, try one of:\n` +
|
|
395
|
-
` # 1. Use a proxy:\n` +
|
|
396
|
-
` export https_proxy=http://your-proxy:port\n` +
|
|
397
|
-
` npm install -g @delta-infra/cli\n\n` +
|
|
398
|
-
` # 2. Point to a corporate npm mirror:\n` +
|
|
399
|
-
` npm install -g @delta-infra/cli --registry=https://your-corp-mirror/\n` +
|
|
400
|
-
`\nOr manually download from:\n` +
|
|
401
|
-
` https://github.com/${REPO}/releases/tag/v${VERSION}`
|
|
402
|
-
);
|
|
403
|
-
process.exit(1);
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
module.exports = {
|
|
408
|
-
getExpectedChecksum, verifyChecksum, assertAllowedHost,
|
|
409
|
-
isCurlVersionSupported, curlSupportsSslRevokeBestEffort,
|
|
410
|
-
};
|
|
285
|
+
throw new Error(`Checksum entry not found for ${archiveName}`);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
function verifyChecksum(archivePath, expectedHash) {
|
|
289
|
+
if (expectedHash === null) return;
|
|
290
|
+
const hash = crypto.createHash("sha256");
|
|
291
|
+
const fd = fs.openSync(archivePath, "r");
|
|
292
|
+
try {
|
|
293
|
+
const buf = Buffer.alloc(64 * 1024);
|
|
294
|
+
let bytesRead;
|
|
295
|
+
while ((bytesRead = fs.readSync(fd, buf, 0, buf.length, null)) > 0) {
|
|
296
|
+
hash.update(buf.subarray(0, bytesRead));
|
|
297
|
+
}
|
|
298
|
+
} finally {
|
|
299
|
+
fs.closeSync(fd);
|
|
300
|
+
}
|
|
301
|
+
const actual = hash.digest("hex");
|
|
302
|
+
if (actual.toLowerCase() !== expectedHash.toLowerCase()) {
|
|
303
|
+
throw new Error(
|
|
304
|
+
`[SECURITY] Checksum mismatch for ${path.basename(archivePath)}: expected ${expectedHash} but got ${actual}`
|
|
305
|
+
);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// ── Main install ───────────────────────────────────────────────────────────
|
|
310
|
+
|
|
311
|
+
function install() {
|
|
312
|
+
fs.mkdirSync(binDir, { recursive: true });
|
|
313
|
+
var tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "delta-cli-"));
|
|
314
|
+
var archivePath = path.join(tmpDir, archiveName);
|
|
315
|
+
|
|
316
|
+
releaseAssetUrls(VERSION, archiveName, function(downloadUrls) {
|
|
317
|
+
try {
|
|
318
|
+
var lastErr;
|
|
319
|
+
var downloaded = false;
|
|
320
|
+
for (var i = 0; i < downloadUrls.length; i++) {
|
|
321
|
+
try {
|
|
322
|
+
console.error("Downloading from " + downloadUrls[i]);
|
|
323
|
+
download(downloadUrls[i], archivePath);
|
|
324
|
+
console.error("Download complete, verifying checksum...");
|
|
325
|
+
const expectedHash = getExpectedChecksum(archiveName);
|
|
326
|
+
verifyChecksum(archivePath, expectedHash);
|
|
327
|
+
console.error("Checksum verified");
|
|
328
|
+
downloaded = true;
|
|
329
|
+
break;
|
|
330
|
+
} catch (e) {
|
|
331
|
+
console.error("Failed: " + e.message + " (" + downloadUrls[i] + ")");
|
|
332
|
+
lastErr = e;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
if (!downloaded) throw lastErr;
|
|
336
|
+
|
|
337
|
+
if (isWindows) {
|
|
338
|
+
console.error("Extracting archive...");
|
|
339
|
+
extractZipWindows(archivePath, tmpDir);
|
|
340
|
+
} else {
|
|
341
|
+
console.error("Extracting archive...");
|
|
342
|
+
execFileSync("tar", ["-xzf", archivePath, "-C", tmpDir], { stdio: "ignore" });
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
const binaryName = NAME + (isWindows ? ".exe" : "");
|
|
346
|
+
const extractedBinary = path.join(tmpDir, NAME + "-" + platform + "-" + arch);
|
|
347
|
+
if (fs.existsSync(extractedBinary)) {
|
|
348
|
+
fs.copyFileSync(extractedBinary, dest);
|
|
349
|
+
} else {
|
|
350
|
+
// Fallback: try the plain name (older release format)
|
|
351
|
+
const plainBinary = path.join(tmpDir, binaryName);
|
|
352
|
+
if (fs.existsSync(plainBinary)) {
|
|
353
|
+
fs.copyFileSync(plainBinary, dest);
|
|
354
|
+
} else {
|
|
355
|
+
// Search in tmpDir
|
|
356
|
+
const files = fs.readdirSync(tmpDir).filter(function(f) { return f.indexOf(NAME) !== -1 || f.indexOf(binaryName) !== -1; });
|
|
357
|
+
if (files.length > 0) {
|
|
358
|
+
fs.copyFileSync(path.join(tmpDir, files[0]), dest);
|
|
359
|
+
} else {
|
|
360
|
+
throw new Error("Binary not found in extracted archive. Files: " + fs.readdirSync(tmpDir).join(", "));
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
fs.chmodSync(dest, 0o755);
|
|
365
|
+
console.error("Installed to " + dest);
|
|
366
|
+
} finally {
|
|
367
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
368
|
+
}
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// ── Entry ──────────────────────────────────────────────────────────────────
|
|
373
|
+
|
|
374
|
+
if (require.main === module) {
|
|
375
|
+
if (!platform || !arch) {
|
|
376
|
+
console.error(`Unsupported platform: ${process.platform}-${process.arch}`);
|
|
377
|
+
process.exit(1);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// npx postinstall guard: skip binary download in ephemeral npx context
|
|
381
|
+
const isNpxPostinstall =
|
|
382
|
+
process.env.npm_command === "exec" && !process.env.DELTA_CLI_RUN;
|
|
383
|
+
|
|
384
|
+
if (isNpxPostinstall) {
|
|
385
|
+
process.exit(0);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
try {
|
|
389
|
+
install();
|
|
390
|
+
console.log(`${NAME} v${VERSION} installed successfully`);
|
|
391
|
+
} catch (err) {
|
|
392
|
+
console.error(`Failed to install ${NAME}:`, err.message);
|
|
393
|
+
console.error(
|
|
394
|
+
`\nIf you are behind a firewall or in a restricted network, try one of:\n` +
|
|
395
|
+
` # 1. Use a proxy:\n` +
|
|
396
|
+
` export https_proxy=http://your-proxy:port\n` +
|
|
397
|
+
` npm install -g @delta-infra/cli\n\n` +
|
|
398
|
+
` # 2. Point to a corporate npm mirror:\n` +
|
|
399
|
+
` npm install -g @delta-infra/cli --registry=https://your-corp-mirror/\n` +
|
|
400
|
+
`\nOr manually download from:\n` +
|
|
401
|
+
` https://github.com/${REPO}/releases/tag/v${VERSION}`
|
|
402
|
+
);
|
|
403
|
+
process.exit(1);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
module.exports = {
|
|
408
|
+
getExpectedChecksum, verifyChecksum, assertAllowedHost,
|
|
409
|
+
isCurlVersionSupported, curlSupportsSslRevokeBestEffort,
|
|
410
|
+
};
|