@dhruv2mars/offdex 0.0.3 → 0.0.5
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/bin/install-lib.js +61 -12
- package/bin/install.js +16 -1
- package/bin/offdex.js +27 -0
- package/package.json +1 -1
package/bin/install-lib.js
CHANGED
|
@@ -5,6 +5,7 @@ import http from "node:http";
|
|
|
5
5
|
import https from "node:https";
|
|
6
6
|
import { homedir } from "node:os";
|
|
7
7
|
import { join } from "node:path";
|
|
8
|
+
import { gunzipSync } from "node:zlib";
|
|
8
9
|
|
|
9
10
|
const REPO = "Dhruv2mars/offdex";
|
|
10
11
|
const SUPPORTED_TARGETS = new Map([
|
|
@@ -58,6 +59,10 @@ export function checksumsAssetNameFor(platform = process.platform, arch = proces
|
|
|
58
59
|
return `checksums-${platform}-${arch}.txt`;
|
|
59
60
|
}
|
|
60
61
|
|
|
62
|
+
export function compressedAssetNameFor(platform = process.platform, arch = process.arch) {
|
|
63
|
+
return `${assetNameFor(platform, arch)}.gz`;
|
|
64
|
+
}
|
|
65
|
+
|
|
61
66
|
export function resolveInstallRoot(env = process.env, home = homedir()) {
|
|
62
67
|
return env.OFFDEX_INSTALL_ROOT || join(home, ".offdex");
|
|
63
68
|
}
|
|
@@ -136,6 +141,18 @@ export function parseChecksumForAsset(text, asset) {
|
|
|
136
141
|
return null;
|
|
137
142
|
}
|
|
138
143
|
|
|
144
|
+
export function shouldReportProgress({ receivedBytes, totalBytes, lastPercent }) {
|
|
145
|
+
if (!totalBytes || totalBytes <= 0) {
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const percent = Math.floor((receivedBytes / totalBytes) * 100);
|
|
150
|
+
if (lastPercent < 0) {
|
|
151
|
+
return true;
|
|
152
|
+
}
|
|
153
|
+
return percent === 100 || percent >= lastPercent + 10;
|
|
154
|
+
}
|
|
155
|
+
|
|
139
156
|
function requestProtocolFor(url) {
|
|
140
157
|
return new URL(url).protocol;
|
|
141
158
|
}
|
|
@@ -183,7 +200,7 @@ export function requestText(url, redirects = 0) {
|
|
|
183
200
|
});
|
|
184
201
|
}
|
|
185
202
|
|
|
186
|
-
export function download(url, outputPath, redirects = 0) {
|
|
203
|
+
export function download(url, outputPath, redirects = 0, options = {}) {
|
|
187
204
|
if (redirects > 5) {
|
|
188
205
|
throw new Error("too_many_redirects");
|
|
189
206
|
}
|
|
@@ -207,7 +224,7 @@ export function download(url, outputPath, redirects = 0) {
|
|
|
207
224
|
response.headers.location
|
|
208
225
|
) {
|
|
209
226
|
response.resume();
|
|
210
|
-
download(response.headers.location, outputPath, redirects + 1).then(resolve, reject);
|
|
227
|
+
download(response.headers.location, outputPath, redirects + 1, options).then(resolve, reject);
|
|
211
228
|
return;
|
|
212
229
|
}
|
|
213
230
|
if (response.statusCode !== 200) {
|
|
@@ -215,6 +232,12 @@ export function download(url, outputPath, redirects = 0) {
|
|
|
215
232
|
reject(new Error(`http ${response.statusCode}`));
|
|
216
233
|
return;
|
|
217
234
|
}
|
|
235
|
+
const totalBytes = Number(response.headers["content-length"] || 0);
|
|
236
|
+
let receivedBytes = 0;
|
|
237
|
+
response.on("data", (chunk) => {
|
|
238
|
+
receivedBytes += chunk.length;
|
|
239
|
+
options.onProgress?.({ receivedBytes, totalBytes });
|
|
240
|
+
});
|
|
218
241
|
const file = createWriteStream(partPath);
|
|
219
242
|
file.on("error", async (error) => {
|
|
220
243
|
await rm(partPath, { force: true });
|
|
@@ -238,6 +261,9 @@ export function download(url, outputPath, redirects = 0) {
|
|
|
238
261
|
});
|
|
239
262
|
}
|
|
240
263
|
);
|
|
264
|
+
request.setTimeout(options.timeoutMs ?? 30_000, () => {
|
|
265
|
+
request.destroy(new Error("download_timeout"));
|
|
266
|
+
});
|
|
241
267
|
request.on("error", async (error) => {
|
|
242
268
|
await rm(partPath, { force: true });
|
|
243
269
|
reject(error);
|
|
@@ -252,13 +278,15 @@ export async function installRuntime({
|
|
|
252
278
|
arch = process.arch,
|
|
253
279
|
home = homedir(),
|
|
254
280
|
downloadFn = download,
|
|
255
|
-
requestTextFn = requestText
|
|
281
|
+
requestTextFn = requestText,
|
|
282
|
+
onProgress
|
|
256
283
|
}) {
|
|
257
284
|
assertSupportedPlatform(platform, arch);
|
|
258
285
|
const installRoot = resolveInstallRoot(env, home);
|
|
259
286
|
const installBin = resolveInstalledBin(env, platform, home);
|
|
260
287
|
const installMeta = resolveInstallMetaPath(env, home);
|
|
261
288
|
const asset = assetNameFor(platform, arch);
|
|
289
|
+
const compressedAsset = compressedAssetNameFor(platform, arch);
|
|
262
290
|
const checksumsAsset = checksumsAssetNameFor(platform, arch);
|
|
263
291
|
const baseUrl = env.OFFDEX_RELEASE_BASE_URL
|
|
264
292
|
|| `https://github.com/${REPO}/releases/download/v${version}`;
|
|
@@ -271,21 +299,42 @@ export async function installRuntime({
|
|
|
271
299
|
} catch {
|
|
272
300
|
throw new Error(`failed_download:${checksumsAsset}`);
|
|
273
301
|
}
|
|
274
|
-
const
|
|
302
|
+
const expectedCompressedChecksum = parseChecksumForAsset(checksumsText, compressedAsset);
|
|
303
|
+
const expectedChecksum = expectedCompressedChecksum || parseChecksumForAsset(checksumsText, asset);
|
|
275
304
|
if (!expectedChecksum) {
|
|
276
305
|
throw new Error(`missing_checksum:${asset}`);
|
|
277
306
|
}
|
|
278
307
|
|
|
279
308
|
const tempPath = `${installBin}.download`;
|
|
280
309
|
try {
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
310
|
+
if (expectedCompressedChecksum) {
|
|
311
|
+
const compressedPath = `${tempPath}.gz`;
|
|
312
|
+
try {
|
|
313
|
+
await downloadFn(`${baseUrl}/${compressedAsset}`, compressedPath, 0, { onProgress });
|
|
314
|
+
} catch {
|
|
315
|
+
throw new Error(`failed_download:${compressedAsset}`);
|
|
316
|
+
}
|
|
317
|
+
try {
|
|
318
|
+
const actualCompressedChecksum = createHash("sha256")
|
|
319
|
+
.update(readFileSync(compressedPath))
|
|
320
|
+
.digest("hex");
|
|
321
|
+
if (actualCompressedChecksum !== expectedCompressedChecksum) {
|
|
322
|
+
throw new Error(`checksum_mismatch:${compressedAsset}`);
|
|
323
|
+
}
|
|
324
|
+
await writeFile(tempPath, gunzipSync(readFileSync(compressedPath)));
|
|
325
|
+
} finally {
|
|
326
|
+
await rm(compressedPath, { force: true });
|
|
327
|
+
}
|
|
328
|
+
} else {
|
|
329
|
+
try {
|
|
330
|
+
await downloadFn(`${baseUrl}/${asset}`, tempPath, 0, { onProgress });
|
|
331
|
+
} catch {
|
|
332
|
+
throw new Error(`failed_download:${asset}`);
|
|
333
|
+
}
|
|
334
|
+
const actualChecksum = createHash("sha256").update(readFileSync(tempPath)).digest("hex");
|
|
335
|
+
if (actualChecksum !== expectedChecksum) {
|
|
336
|
+
throw new Error(`checksum_mismatch:${asset}`);
|
|
337
|
+
}
|
|
289
338
|
}
|
|
290
339
|
if (platform !== "win32") {
|
|
291
340
|
await chmod(tempPath, 0o755);
|
package/bin/install.js
CHANGED
|
@@ -4,6 +4,7 @@ import { dirname, join } from "node:path";
|
|
|
4
4
|
import {
|
|
5
5
|
installRuntime,
|
|
6
6
|
resolvePackageVersion,
|
|
7
|
+
shouldReportProgress,
|
|
7
8
|
shouldSkipPackageInstall,
|
|
8
9
|
supportedPlatformList
|
|
9
10
|
} from "./install-lib.js";
|
|
@@ -17,7 +18,21 @@ if (shouldSkipPackageInstall({ env: process.env, packageRoot })) {
|
|
|
17
18
|
process.exit(0);
|
|
18
19
|
}
|
|
19
20
|
|
|
20
|
-
|
|
21
|
+
let lastPercent = -10;
|
|
22
|
+
|
|
23
|
+
installRuntime({
|
|
24
|
+
version: packageVersion,
|
|
25
|
+
onProgress({ receivedBytes, totalBytes }) {
|
|
26
|
+
if (!shouldReportProgress({ receivedBytes, totalBytes, lastPercent })) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
lastPercent = Math.floor((receivedBytes / totalBytes) * 100);
|
|
31
|
+
console.error(
|
|
32
|
+
`offdex: downloading native runtime ${lastPercent}% (${Math.round(receivedBytes / 1024 / 1024)}MB/${Math.round(totalBytes / 1024 / 1024)}MB)`
|
|
33
|
+
);
|
|
34
|
+
},
|
|
35
|
+
})
|
|
21
36
|
.then(({ installBin }) => {
|
|
22
37
|
console.log(`offdex: installed ${installBin}`);
|
|
23
38
|
})
|
package/bin/offdex.js
CHANGED
|
@@ -13,11 +13,38 @@ import {
|
|
|
13
13
|
} from "./offdex-lib.js";
|
|
14
14
|
|
|
15
15
|
const args = process.argv.slice(2);
|
|
16
|
+
const HELP_TEXT = `Offdex CLI
|
|
17
|
+
|
|
18
|
+
Usage:
|
|
19
|
+
offdex bridge [options]
|
|
20
|
+
offdex help
|
|
21
|
+
|
|
22
|
+
Options:
|
|
23
|
+
--host <host> Bridge host. Default: 0.0.0.0
|
|
24
|
+
--port <port> Bridge port. Default: 42420
|
|
25
|
+
--mode <codex|demo> Bridge runtime mode. Default: codex
|
|
26
|
+
--control-plane-url <url> Managed remote control plane URL
|
|
27
|
+
-h, --help Show help
|
|
28
|
+
|
|
29
|
+
Environment fallbacks:
|
|
30
|
+
OFFDEX_BRIDGE_HOST
|
|
31
|
+
OFFDEX_BRIDGE_PORT
|
|
32
|
+
OFFDEX_BRIDGE_MODE
|
|
33
|
+
OFFDEX_CONTROL_PLANE_URL
|
|
34
|
+
`;
|
|
16
35
|
const installedBin = resolveInstalledBin(process.env, process.platform);
|
|
17
36
|
const packageVersion = readPackageVersion();
|
|
18
37
|
const currentInstalledVersion = installedVersion(process.env);
|
|
19
38
|
const workspaceBridgeCli = resolveWorkspaceBridgeCli();
|
|
20
39
|
|
|
40
|
+
if (
|
|
41
|
+
!existsSync(installedBin) &&
|
|
42
|
+
(args.length === 0 || args[0] === "help" || args[0] === "--help" || args[0] === "-h")
|
|
43
|
+
) {
|
|
44
|
+
console.log(HELP_TEXT);
|
|
45
|
+
process.exit(0);
|
|
46
|
+
}
|
|
47
|
+
|
|
21
48
|
if (workspaceBridgeCli && !existsSync(installedBin)) {
|
|
22
49
|
const result = spawnSync("bun", [workspaceBridgeCli, ...args], {
|
|
23
50
|
stdio: "inherit",
|