@peerbit/shared-fs-cli 0.0.1
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/LICENSE +202 -0
- package/README.md +84 -0
- package/lib/esm/bin.d.ts +2 -0
- package/lib/esm/bin.js +7 -0
- package/lib/esm/bin.js.map +1 -0
- package/lib/esm/cross-os-interop.d.ts +2 -0
- package/lib/esm/cross-os-interop.js +99 -0
- package/lib/esm/cross-os-interop.js.map +1 -0
- package/lib/esm/index.d.ts +2 -0
- package/lib/esm/index.js +576 -0
- package/lib/esm/index.js.map +1 -0
- package/lib/esm/install-native-adapter.d.ts +2 -0
- package/lib/esm/install-native-adapter.js +38 -0
- package/lib/esm/install-native-adapter.js.map +1 -0
- package/lib/esm/native-adapter.d.ts +55 -0
- package/lib/esm/native-adapter.js +268 -0
- package/lib/esm/native-adapter.js.map +1 -0
- package/package.json +53 -0
- package/scripts/postinstall.mjs +38 -0
- package/src/bin.ts +8 -0
- package/src/cross-os-interop.ts +133 -0
- package/src/index.ts +788 -0
- package/src/install-native-adapter.ts +48 -0
- package/src/native-adapter.ts +434 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import chalk from "chalk";
|
|
4
|
+
import { installNativeAdapter } from "./native-adapter.js";
|
|
5
|
+
|
|
6
|
+
const takeValue = (args: string[], name: string) => {
|
|
7
|
+
const index = args.indexOf(name);
|
|
8
|
+
if (index === -1) {
|
|
9
|
+
return undefined;
|
|
10
|
+
}
|
|
11
|
+
return args[index + 1];
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const hasFlag = (args: string[], name: string) => args.includes(name);
|
|
15
|
+
|
|
16
|
+
const args = process.argv.slice(2);
|
|
17
|
+
|
|
18
|
+
installNativeAdapter({
|
|
19
|
+
installDir: takeValue(args, "--prefix"),
|
|
20
|
+
version: takeValue(args, "--version"),
|
|
21
|
+
baseUrl: takeValue(args, "--base-url"),
|
|
22
|
+
force: hasFlag(args, "--force"),
|
|
23
|
+
ifNeeded: hasFlag(args, "--if-needed"),
|
|
24
|
+
})
|
|
25
|
+
.then((result) => {
|
|
26
|
+
if (hasFlag(args, "--print-path")) {
|
|
27
|
+
console.log(result.binaryPath);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
if (hasFlag(args, "--quiet")) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
if (result.installed) {
|
|
34
|
+
console.log(
|
|
35
|
+
chalk.green(`Installed native adapter at ${result.binaryPath}`)
|
|
36
|
+
);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
console.log(
|
|
40
|
+
chalk.gray(
|
|
41
|
+
`Native adapter already installed at ${result.binaryPath}`
|
|
42
|
+
)
|
|
43
|
+
);
|
|
44
|
+
})
|
|
45
|
+
.catch((error) => {
|
|
46
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
47
|
+
process.exitCode = 1;
|
|
48
|
+
});
|
|
@@ -0,0 +1,434 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import fsp from "node:fs/promises";
|
|
4
|
+
import http from "node:http";
|
|
5
|
+
import https from "node:https";
|
|
6
|
+
import os from "node:os";
|
|
7
|
+
import path from "node:path";
|
|
8
|
+
import { pipeline } from "node:stream/promises";
|
|
9
|
+
import { fileURLToPath } from "node:url";
|
|
10
|
+
|
|
11
|
+
const CLI_PACKAGE_NAME = "@peerbit/shared-fs-cli";
|
|
12
|
+
const DEFAULT_RELEASE_REPOSITORY = "dao-xyz/peerbit-examples";
|
|
13
|
+
const DEFAULT_PATH_COMMAND = "peerbit-shared-fs-native";
|
|
14
|
+
|
|
15
|
+
export type NativeAdapterTarget = {
|
|
16
|
+
id: string;
|
|
17
|
+
platform: NodeJS.Platform;
|
|
18
|
+
arch: NodeJS.Architecture;
|
|
19
|
+
archiveExtension: "tar.gz" | "zip";
|
|
20
|
+
binaryName: string;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export type ResolveNativeAdapterOptions = {
|
|
24
|
+
env?: NodeJS.ProcessEnv;
|
|
25
|
+
installDir?: string;
|
|
26
|
+
platform?: NodeJS.Platform;
|
|
27
|
+
commandExists?: (command: string) => Promise<boolean>;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export type InstallNativeAdapterOptions = {
|
|
31
|
+
installDir?: string;
|
|
32
|
+
platform?: NodeJS.Platform;
|
|
33
|
+
arch?: NodeJS.Architecture;
|
|
34
|
+
version?: string;
|
|
35
|
+
baseUrl?: string;
|
|
36
|
+
force?: boolean;
|
|
37
|
+
ifNeeded?: boolean;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export type InstallNativeAdapterResult = {
|
|
41
|
+
binaryPath: string;
|
|
42
|
+
installed: boolean;
|
|
43
|
+
skippedReason?: "already-installed";
|
|
44
|
+
target: NativeAdapterTarget;
|
|
45
|
+
assetName: string;
|
|
46
|
+
url: string;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export class NativeAdapterInstallError extends Error {
|
|
50
|
+
constructor(message: string) {
|
|
51
|
+
super(message);
|
|
52
|
+
this.name = "NativeAdapterInstallError";
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export const nativeAdapterBinaryName = (
|
|
57
|
+
platform: NodeJS.Platform = process.platform
|
|
58
|
+
) =>
|
|
59
|
+
platform === "win32" ? `${DEFAULT_PATH_COMMAND}.exe` : DEFAULT_PATH_COMMAND;
|
|
60
|
+
|
|
61
|
+
export const getNativeAdapterTarget = (
|
|
62
|
+
platform: NodeJS.Platform = process.platform,
|
|
63
|
+
arch: NodeJS.Architecture = process.arch
|
|
64
|
+
): NativeAdapterTarget => {
|
|
65
|
+
if (platform !== "linux" && platform !== "darwin" && platform !== "win32") {
|
|
66
|
+
throw new NativeAdapterInstallError(
|
|
67
|
+
`No prebuilt native adapter target for platform ${platform}.`
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
if (arch !== "x64" && arch !== "arm64") {
|
|
71
|
+
throw new NativeAdapterInstallError(
|
|
72
|
+
`No prebuilt native adapter target for architecture ${arch}.`
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return {
|
|
77
|
+
id: `${platform}-${arch}`,
|
|
78
|
+
platform,
|
|
79
|
+
arch,
|
|
80
|
+
archiveExtension: platform === "win32" ? "zip" : "tar.gz",
|
|
81
|
+
binaryName: nativeAdapterBinaryName(platform),
|
|
82
|
+
};
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
export const nativeAdapterAssetName = (target: NativeAdapterTarget) =>
|
|
86
|
+
`peerbit-shared-fs-native-${target.id}.${target.archiveExtension}`;
|
|
87
|
+
|
|
88
|
+
export const nativeAdapterReleaseTag = (version: string) =>
|
|
89
|
+
version.startsWith("shared-fs-native-v")
|
|
90
|
+
? version
|
|
91
|
+
: `shared-fs-native-v${version.replace(/^v/, "")}`;
|
|
92
|
+
|
|
93
|
+
export const nativeAdapterDownloadBaseUrl = (tag: string) =>
|
|
94
|
+
`https://github.com/${DEFAULT_RELEASE_REPOSITORY}/releases/download/${tag}`;
|
|
95
|
+
|
|
96
|
+
export const nativeAdapterDownloadUrl = (options: {
|
|
97
|
+
assetName: string;
|
|
98
|
+
baseUrl?: string;
|
|
99
|
+
tag: string;
|
|
100
|
+
}) => {
|
|
101
|
+
const baseUrl =
|
|
102
|
+
options.baseUrl ?? nativeAdapterDownloadBaseUrl(options.tag);
|
|
103
|
+
return `${baseUrl.replace(/\/$/, "")}/${options.assetName}`;
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
export const defaultNativeAdapterInstallDir = (
|
|
107
|
+
env: NodeJS.ProcessEnv = process.env
|
|
108
|
+
) =>
|
|
109
|
+
env.PEERBIT_SHARED_FS_NATIVE_INSTALL_DIR ||
|
|
110
|
+
path.join(os.homedir(), ".peerbit", "shared-fs", "bin");
|
|
111
|
+
|
|
112
|
+
export const defaultNativeAdapterPath = (
|
|
113
|
+
options: {
|
|
114
|
+
env?: NodeJS.ProcessEnv;
|
|
115
|
+
installDir?: string;
|
|
116
|
+
platform?: NodeJS.Platform;
|
|
117
|
+
} = {}
|
|
118
|
+
) =>
|
|
119
|
+
path.join(
|
|
120
|
+
options.installDir ??
|
|
121
|
+
defaultNativeAdapterInstallDir(options.env ?? process.env),
|
|
122
|
+
nativeAdapterBinaryName(options.platform ?? process.platform)
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
const pathExists = async (candidate: string) => {
|
|
126
|
+
try {
|
|
127
|
+
await fsp.access(candidate, fs.constants.F_OK);
|
|
128
|
+
return true;
|
|
129
|
+
} catch {
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
const executablePathExists = async (candidate: string) => {
|
|
135
|
+
try {
|
|
136
|
+
await fsp.access(candidate, fs.constants.X_OK);
|
|
137
|
+
return true;
|
|
138
|
+
} catch {
|
|
139
|
+
return process.platform === "win32" && (await pathExists(candidate));
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
const isPathLikeCommand = (command: string) =>
|
|
144
|
+
path.isAbsolute(command) || command.includes("/") || command.includes("\\");
|
|
145
|
+
|
|
146
|
+
export const commandExistsOnPath = async (
|
|
147
|
+
command: string,
|
|
148
|
+
options: {
|
|
149
|
+
env?: NodeJS.ProcessEnv;
|
|
150
|
+
platform?: NodeJS.Platform;
|
|
151
|
+
} = {}
|
|
152
|
+
) => {
|
|
153
|
+
const env = options.env ?? process.env;
|
|
154
|
+
const platform = options.platform ?? process.platform;
|
|
155
|
+
if (isPathLikeCommand(command)) {
|
|
156
|
+
return executablePathExists(command);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const pathValue = env.PATH;
|
|
160
|
+
if (!pathValue) {
|
|
161
|
+
return false;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const extensions =
|
|
165
|
+
platform === "win32"
|
|
166
|
+
? (env.PATHEXT ?? ".EXE;.CMD;.BAT;.COM").split(";").filter(Boolean)
|
|
167
|
+
: [""];
|
|
168
|
+
const commandNames =
|
|
169
|
+
platform === "win32" && path.extname(command) === ""
|
|
170
|
+
? [
|
|
171
|
+
command,
|
|
172
|
+
...extensions.map((extension) => `${command}${extension}`),
|
|
173
|
+
]
|
|
174
|
+
: [command];
|
|
175
|
+
|
|
176
|
+
for (const directory of pathValue.split(path.delimiter)) {
|
|
177
|
+
for (const commandName of commandNames) {
|
|
178
|
+
if (await executablePathExists(path.join(directory, commandName))) {
|
|
179
|
+
return true;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return false;
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
export const resolveExternalNativeAdapter = async (
|
|
187
|
+
explicitCommand?: string,
|
|
188
|
+
options: ResolveNativeAdapterOptions = {}
|
|
189
|
+
) => {
|
|
190
|
+
const env = options.env ?? process.env;
|
|
191
|
+
const platform = options.platform ?? process.platform;
|
|
192
|
+
const commandExists =
|
|
193
|
+
options.commandExists ??
|
|
194
|
+
((command: string) => commandExistsOnPath(command, { env, platform }));
|
|
195
|
+
|
|
196
|
+
if (explicitCommand?.trim()) {
|
|
197
|
+
return explicitCommand;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (env.PEERBIT_SHARED_FS_NATIVE_ADAPTER?.trim()) {
|
|
201
|
+
return env.PEERBIT_SHARED_FS_NATIVE_ADAPTER;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const managedPath = defaultNativeAdapterPath({
|
|
205
|
+
env,
|
|
206
|
+
installDir: options.installDir,
|
|
207
|
+
platform,
|
|
208
|
+
});
|
|
209
|
+
if (await pathExists(managedPath)) {
|
|
210
|
+
return managedPath;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (await commandExists(DEFAULT_PATH_COMMAND)) {
|
|
214
|
+
return DEFAULT_PATH_COMMAND;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
return undefined;
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
const readCliPackageVersion = async () => {
|
|
221
|
+
let directory = path.dirname(fileURLToPath(import.meta.url));
|
|
222
|
+
while (true) {
|
|
223
|
+
const packagePath = path.join(directory, "package.json");
|
|
224
|
+
try {
|
|
225
|
+
const parsed = JSON.parse(
|
|
226
|
+
await fsp.readFile(packagePath, "utf8")
|
|
227
|
+
) as {
|
|
228
|
+
name?: string;
|
|
229
|
+
version?: string;
|
|
230
|
+
};
|
|
231
|
+
if (parsed.name === CLI_PACKAGE_NAME && parsed.version) {
|
|
232
|
+
return parsed.version;
|
|
233
|
+
}
|
|
234
|
+
} catch {}
|
|
235
|
+
|
|
236
|
+
const parent = path.dirname(directory);
|
|
237
|
+
if (parent === directory) {
|
|
238
|
+
throw new NativeAdapterInstallError(
|
|
239
|
+
`Unable to find ${CLI_PACKAGE_NAME} package version.`
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
directory = parent;
|
|
243
|
+
}
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
const downloadFile = async (
|
|
247
|
+
url: string,
|
|
248
|
+
destination: string,
|
|
249
|
+
redirectBudget = 5
|
|
250
|
+
): Promise<void> => {
|
|
251
|
+
const parsedUrl = new URL(url);
|
|
252
|
+
const client = parsedUrl.protocol === "http:" ? http : https;
|
|
253
|
+
await new Promise<void>((resolve, reject) => {
|
|
254
|
+
const request = client.get(
|
|
255
|
+
parsedUrl,
|
|
256
|
+
{
|
|
257
|
+
headers: {
|
|
258
|
+
"user-agent": `${CLI_PACKAGE_NAME} native-adapter-installer`,
|
|
259
|
+
},
|
|
260
|
+
},
|
|
261
|
+
(response) => {
|
|
262
|
+
const statusCode = response.statusCode ?? 0;
|
|
263
|
+
const location = response.headers.location;
|
|
264
|
+
if (
|
|
265
|
+
statusCode >= 300 &&
|
|
266
|
+
statusCode < 400 &&
|
|
267
|
+
location &&
|
|
268
|
+
redirectBudget > 0
|
|
269
|
+
) {
|
|
270
|
+
response.resume();
|
|
271
|
+
downloadFile(
|
|
272
|
+
new URL(location, parsedUrl).toString(),
|
|
273
|
+
destination,
|
|
274
|
+
redirectBudget - 1
|
|
275
|
+
).then(resolve, reject);
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
if (statusCode !== 200) {
|
|
280
|
+
response.resume();
|
|
281
|
+
reject(
|
|
282
|
+
new NativeAdapterInstallError(
|
|
283
|
+
`Download failed with HTTP ${statusCode}: ${url}`
|
|
284
|
+
)
|
|
285
|
+
);
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
pipeline(response, fs.createWriteStream(destination)).then(
|
|
290
|
+
resolve,
|
|
291
|
+
reject
|
|
292
|
+
);
|
|
293
|
+
}
|
|
294
|
+
);
|
|
295
|
+
request.on("error", reject);
|
|
296
|
+
});
|
|
297
|
+
};
|
|
298
|
+
|
|
299
|
+
const runProcess = async (command: string, args: string[]) => {
|
|
300
|
+
await new Promise<void>((resolve, reject) => {
|
|
301
|
+
const child = spawn(command, args, {
|
|
302
|
+
stdio: ["ignore", "ignore", "pipe"],
|
|
303
|
+
});
|
|
304
|
+
let stderr = "";
|
|
305
|
+
child.stderr.on("data", (chunk) => {
|
|
306
|
+
stderr += chunk.toString("utf8");
|
|
307
|
+
});
|
|
308
|
+
child.once("error", reject);
|
|
309
|
+
child.once("exit", (code, signal) => {
|
|
310
|
+
if (code === 0) {
|
|
311
|
+
resolve();
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
reject(
|
|
315
|
+
new NativeAdapterInstallError(
|
|
316
|
+
`${command} ${args.join(" ")} failed with code=${code} signal=${signal}: ${stderr.trim()}`
|
|
317
|
+
)
|
|
318
|
+
);
|
|
319
|
+
});
|
|
320
|
+
});
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
const extractArchive = async (
|
|
324
|
+
archivePath: string,
|
|
325
|
+
outputDirectory: string,
|
|
326
|
+
target: NativeAdapterTarget
|
|
327
|
+
) => {
|
|
328
|
+
if (target.archiveExtension === "zip") {
|
|
329
|
+
const quotePowerShell = (value: string) =>
|
|
330
|
+
`'${value.replace(/'/g, "''")}'`;
|
|
331
|
+
await runProcess("powershell.exe", [
|
|
332
|
+
"-NoProfile",
|
|
333
|
+
"-ExecutionPolicy",
|
|
334
|
+
"Bypass",
|
|
335
|
+
"-Command",
|
|
336
|
+
`Expand-Archive -LiteralPath ${quotePowerShell(
|
|
337
|
+
archivePath
|
|
338
|
+
)} -DestinationPath ${quotePowerShell(outputDirectory)} -Force`,
|
|
339
|
+
]);
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
await runProcess("tar", ["-xzf", archivePath, "-C", outputDirectory]);
|
|
343
|
+
};
|
|
344
|
+
|
|
345
|
+
const findExtractedBinary = async (
|
|
346
|
+
directory: string,
|
|
347
|
+
binaryName: string
|
|
348
|
+
): Promise<string | undefined> => {
|
|
349
|
+
const entries = await fsp.readdir(directory, { withFileTypes: true });
|
|
350
|
+
for (const entry of entries) {
|
|
351
|
+
const entryPath = path.join(directory, entry.name);
|
|
352
|
+
if (
|
|
353
|
+
entry.isFile() &&
|
|
354
|
+
entry.name.toLowerCase() === binaryName.toLowerCase()
|
|
355
|
+
) {
|
|
356
|
+
return entryPath;
|
|
357
|
+
}
|
|
358
|
+
if (entry.isDirectory()) {
|
|
359
|
+
const found = await findExtractedBinary(entryPath, binaryName);
|
|
360
|
+
if (found) {
|
|
361
|
+
return found;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
return undefined;
|
|
366
|
+
};
|
|
367
|
+
|
|
368
|
+
export const installNativeAdapter = async (
|
|
369
|
+
options: InstallNativeAdapterOptions = {}
|
|
370
|
+
): Promise<InstallNativeAdapterResult> => {
|
|
371
|
+
const target = getNativeAdapterTarget(options.platform, options.arch);
|
|
372
|
+
const installDir =
|
|
373
|
+
options.installDir ?? defaultNativeAdapterInstallDir(process.env);
|
|
374
|
+
const binaryPath = path.join(installDir, target.binaryName);
|
|
375
|
+
const existing = await pathExists(binaryPath);
|
|
376
|
+
const version =
|
|
377
|
+
options.version ??
|
|
378
|
+
process.env.PEERBIT_SHARED_FS_NATIVE_VERSION ??
|
|
379
|
+
(await readCliPackageVersion());
|
|
380
|
+
const tag = nativeAdapterReleaseTag(version);
|
|
381
|
+
const assetName = nativeAdapterAssetName(target);
|
|
382
|
+
const url = nativeAdapterDownloadUrl({
|
|
383
|
+
assetName,
|
|
384
|
+
baseUrl:
|
|
385
|
+
options.baseUrl ??
|
|
386
|
+
process.env.PEERBIT_SHARED_FS_NATIVE_RELEASE_BASE_URL,
|
|
387
|
+
tag,
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
if (existing && (options.ifNeeded || !options.force)) {
|
|
391
|
+
return {
|
|
392
|
+
binaryPath,
|
|
393
|
+
installed: false,
|
|
394
|
+
skippedReason: "already-installed",
|
|
395
|
+
target,
|
|
396
|
+
assetName,
|
|
397
|
+
url,
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
const tempDirectory = await fsp.mkdtemp(
|
|
402
|
+
path.join(os.tmpdir(), "peerbit-shared-fs-native-")
|
|
403
|
+
);
|
|
404
|
+
try {
|
|
405
|
+
const archivePath = path.join(tempDirectory, assetName);
|
|
406
|
+
await downloadFile(url, archivePath);
|
|
407
|
+
await extractArchive(archivePath, tempDirectory, target);
|
|
408
|
+
const extractedBinary = await findExtractedBinary(
|
|
409
|
+
tempDirectory,
|
|
410
|
+
target.binaryName
|
|
411
|
+
);
|
|
412
|
+
if (!extractedBinary) {
|
|
413
|
+
throw new NativeAdapterInstallError(
|
|
414
|
+
`Archive ${assetName} did not contain ${target.binaryName}.`
|
|
415
|
+
);
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
await fsp.mkdir(installDir, { recursive: true });
|
|
419
|
+
await fsp.copyFile(extractedBinary, binaryPath);
|
|
420
|
+
if (target.platform !== "win32") {
|
|
421
|
+
await fsp.chmod(binaryPath, 0o755);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
return {
|
|
425
|
+
binaryPath,
|
|
426
|
+
installed: true,
|
|
427
|
+
target,
|
|
428
|
+
assetName,
|
|
429
|
+
url,
|
|
430
|
+
};
|
|
431
|
+
} finally {
|
|
432
|
+
await fsp.rm(tempDirectory, { recursive: true, force: true });
|
|
433
|
+
}
|
|
434
|
+
};
|