maactl 0.1.0 → 0.1.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/README.md CHANGED
@@ -1,62 +1,64 @@
1
- # maactl (npm)
2
-
3
- 通过 `npx` 运行 MaaCtl 的命令行客户端。这个包只是一个很薄的转发器:它负责找到
4
- `maactl.exe`(自带、缓存或按需下载),把命令行参数原样传给它,并把退出码回传给 shell。
5
-
6
- ```powershell
7
- # 直接运行,无需全局安装
8
- npx maactl --version
9
- npx maactl adb devices
10
- npx maactl interface --tasks -f D:\01_Projects\github\MaaMio
11
-
12
- # 若某些 npx 版本吞掉了参数,用 -- 显式分隔
13
- npx maactl -- run task "自动挂机卖蛋" -f D:\01_Projects\github\MaaMio --stop-after 10s
14
- ```
15
-
16
- `npx maactl ...` 之后的所有参数都会原样传给 `maactl.exe`,包括 `-f/--interface`、
17
- `--adb-address`、`--json` 等;工作目录也不会被改变,因此默认的 `./interface.json`
18
- 仍然相对于你执行命令的目录解析。
19
-
20
- ## 平台
21
-
22
- 只提供 Windows amd64 的 `maactl.exe`(自带 MaaFramework 运行库)。包内声明了
23
- `"os": ["win32"]`,在其他系统上 npm 会以 `EBADPLATFORM` 拒绝安装。
24
-
25
- ## maactl.exe 的来源
26
-
27
- 按以下顺序查找,第一个命中的会被使用:
28
-
29
- 1. 环境变量 `MAACTL_BINARY` 指向的文件;
30
- 2. 包内的 `vendor/maactl.exe`(正式发布的 tarball 会携带);
31
- 3. 缓存 `%LOCALAPPDATA%\maactl\npm\<version>\maactl.exe`;
32
- 4. 都找不到时,从对应版本的 GitHub Release 下载并校验后缓存(下次直接复用)。
33
-
34
- ## 环境变量
35
-
36
- | 变量 | 作用 |
37
- | --- | --- |
38
- | `MAACTL_BINARY` | 指定要执行的 `maactl.exe`,跳过查找与下载。 |
39
- | `MAACTL_HOME` | 覆盖缓存根目录,默认 `%LOCALAPPDATA%\maactl`。 |
40
- | `MAACTL_VERSION` | 覆盖下载时使用的版本,默认取包的 `version`。 |
41
- | `MAACTL_BINARY_URL` | 直接指定下载地址,完全跳过 GitHub 拼接。 |
42
- | `MAACTL_MIRROR` | 镜像前缀,最终地址为 `<mirror>/https://github.com/<repo>/releases/download/<tag>/maactl.exe`。 |
43
- | `MAACTL_REPO` / `MAACTL_ASSET` | 覆盖仓库(默认 `TanyaShue/MaaCtl`)与资产名(默认 `maactl.exe`)。 |
44
- | `MAACTL_SKIP_DOWNLOAD=1` | 安装阶段不预下载,改为首次运行时下载。 |
45
- | `MAACTL_STRICT_INSTALL=1` | 预下载失败时让 `npm install` 直接失败。 |
46
- | `MAACTL_QUIET=1` | 静默模式,不输出下载进度。 |
47
- | `MAACTL_VERBOSE=1` | 输出实际使用的 `maactl.exe` 路径。 |
48
- | `MAACTL_LANG` | 包装器的提示语言(`zh_CN`/`en`),与 CLI 自身一致。 |
49
-
50
- ## 从源码使用
51
-
52
- ```powershell
53
- # 直接用 shim,无需安装
54
- node npm/bin/maactl.js --help
55
-
56
- # 或在本地安装为全局命令
57
- cd npm
58
- npm install -g .
59
- maactl --version
60
- ```
61
-
62
- 完整的命令说明见 [MaaCtl 主仓库](https://github.com/TanyaShue/MaaCtl#readme)。
1
+ # maactl (npm)
2
+
3
+ 通过 `npx` 运行 MaaCtl 的命令行客户端。这个包只是一个很薄的转发器:它负责找到
4
+ `maactl.exe`(自带、缓存或按需下载),把命令行参数原样传给它,并把退出码回传给 shell。
5
+
6
+ ```powershell
7
+ # 直接运行,无需全局安装
8
+ npx maactl --version
9
+ npx maactl adb devices
10
+ npx maactl interface --tasks -f D:\01_Projects\github\MaaMio
11
+
12
+ # 若某些 npx 版本吞掉了参数,用 -- 显式分隔
13
+ npx maactl -- run task "自动挂机卖蛋" -f D:\01_Projects\github\MaaMio --stop-after 10s
14
+ ```
15
+
16
+ `npx maactl ...` 之后的所有参数都会原样传给 `maactl.exe`,包括 `-f/--interface`、
17
+ `--adb-address`、`--json` 等;工作目录也不会被改变,因此默认的 `./interface.json`
18
+ 仍然相对于你执行命令的目录解析。
19
+
20
+ ## 平台
21
+
22
+ 只提供 Windows amd64 的 `maactl.exe`(自带 MaaFramework 运行库)。包内声明了
23
+ `"os": ["win32"]`,在其他系统上 npm 会以 `EBADPLATFORM` 拒绝安装;同时声明了
24
+ `"engines": { "node": ">=22" }`,因为包装器只用 Node 22 及以上提供的内置能力。
25
+
26
+ ## maactl.exe 的来源
27
+
28
+ 按以下顺序查找,第一个命中的会被使用:
29
+
30
+ 1. 环境变量 `MAACTL_BINARY` 指向的文件;
31
+ 2. 包内的 `vendor/maactl.exe`(正式发布的 tarball 会携带);
32
+ 3. 缓存 `%LOCALAPPDATA%\maactl\npm\<version>\maactl.exe`;
33
+ 4. 都找不到时,从对应版本的 GitHub Release 下载并校验后缓存(下次直接复用)。
34
+
35
+ ## 环境变量
36
+
37
+ | 变量 | 作用 |
38
+ | --- | --- |
39
+ | `MAACTL_BINARY` | 指定要执行的 `maactl.exe`,跳过查找与下载。 |
40
+ | `MAACTL_HOME` | 覆盖缓存根目录,默认 `%LOCALAPPDATA%\maactl`。 |
41
+ | `MAACTL_VERSION` | 覆盖下载时使用的版本,默认取包的 `version`。 |
42
+ | `MAACTL_BINARY_URL` | 直接指定下载地址,完全跳过 GitHub 拼接。 |
43
+ | `MAACTL_MIRROR` | 镜像前缀,最终地址为 `<mirror>/https://github.com/<repo>/releases/download/<tag>/maactl.exe`。 |
44
+ | `MAACTL_REPO` / `MAACTL_ASSET` | 覆盖仓库(默认 `TanyaShue/MaaCtl`)与资产名(默认 `maactl.exe`)。 |
45
+ | `MAACTL_SKIP_DOWNLOAD=1` | 安装阶段不预下载,改为首次运行时下载。 |
46
+ | `MAACTL_STRICT_INSTALL=1` | 预下载失败时让 `npm install` 直接失败。 |
47
+ | `MAACTL_QUIET=1` | 静默模式,不输出下载进度。 |
48
+ | `MAACTL_VERBOSE=1` | 输出实际使用的 `maactl.exe` 路径。 |
49
+ | `MAACTL_LANG` | 包装器的提示语言(`zh_CN`/`en`),与 CLI 自身一致。 |
50
+
51
+ ## 从源码使用
52
+
53
+ ```powershell
54
+ # 直接用 shim,无需安装
55
+ node npm/bin/maactl.js --help
56
+
57
+ # 或在本地安装为全局命令
58
+ cd npm
59
+ npm install -g .
60
+ maactl --version
61
+ ```
62
+
63
+ 完整的命令说明见 [MaaCtl 命令行参考](https://github.com/TanyaShue/MaaCtl/blob/main/docs/cli.md),
64
+ 安装方式见 [主仓库 README](https://github.com/TanyaShue/MaaCtl#readme)。
package/bin/maactl.js CHANGED
@@ -1,19 +1,19 @@
1
- #!/usr/bin/env node
2
- 'use strict';
3
-
4
- // npm installs this file as the `maactl` command on Windows
5
- // (maactl.cmd / maactl.ps1 wrappers), so `npx maactl <args>` ends up here and
6
- // the arguments are forwarded to maactl.exe verbatim.
7
-
8
- const { main } = require('../lib/cli');
9
- const { MaactlError } = require('../lib/errors');
10
-
11
- main()
12
- .then((code) => {
13
- process.exitCode = code;
14
- })
15
- .catch((error) => {
16
- const message = error instanceof MaactlError ? error.message : (error && error.stack) || String(error);
17
- process.stderr.write(`${message}\n`);
18
- process.exitCode = error instanceof MaactlError ? error.exitCode : 1;
19
- });
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ // npm installs this file as the `maactl` command on Windows
5
+ // (maactl.cmd / maactl.ps1 wrappers), so `npx maactl <args>` ends up here and
6
+ // the arguments are forwarded to maactl.exe verbatim.
7
+
8
+ const { main } = require('../lib/cli');
9
+ const { MaactlError } = require('../lib/errors');
10
+
11
+ main()
12
+ .then((code) => {
13
+ process.exitCode = code;
14
+ })
15
+ .catch((error) => {
16
+ const message = error instanceof MaactlError ? error.message : (error && error.stack) || String(error);
17
+ process.stderr.write(`${message}\n`);
18
+ process.exitCode = error instanceof MaactlError ? error.exitCode : 1;
19
+ });
package/lib/binary.js CHANGED
@@ -1,221 +1,221 @@
1
- 'use strict';
2
-
3
- // Locating (and if necessary fetching) the maactl.exe the wrapper hands over to.
4
- //
5
- // Resolution order:
6
- // 1. MAACTL_BINARY – an explicit path supplied by the user;
7
- // 2. <package>/vendor/maactl.exe – the executable published inside the tarball;
8
- // 3. <cache>/npm/<version>/maactl.exe – a previous download, reused across runs.
9
- //
10
- // When none of them exists the exe is downloaded from the GitHub release that
11
- // matches the package version, verified, and cached for the next run.
12
-
13
- const fs = require('node:fs');
14
- const os = require('node:os');
15
- const path = require('node:path');
16
- const { spawnSync } = require('node:child_process');
17
-
18
- const { downloadWithRetry, formatBytes, progressReporter } = require('./download');
19
- const env = require('./env');
20
- const { MaactlError } = require('./errors');
21
- const messages = require('./messages');
22
-
23
- const PACKAGE_ROOT = path.resolve(__dirname, '..');
24
- const PACKAGE = require(path.join(PACKAGE_ROOT, 'package.json'));
25
-
26
- // Every asset is a Windows PE binary; anything smaller than this floor is a
27
- // truncated download, an HTML error page or a git-lfs pointer.
28
- const MIN_BINARY_BYTES = 1 << 20;
29
-
30
- const EXE_NAME = 'maactl.exe';
31
- const DEFAULT_REPO = 'TanyaShue/MaaCtl';
32
- const DEFAULT_ASSET = 'maactl.exe';
33
-
34
- function version() {
35
- return (env.value('MAACTL_VERSION') || PACKAGE.version).replace(/^v/, '');
36
- }
37
-
38
- function tag() {
39
- return `v${version()}`;
40
- }
41
-
42
- function repo() {
43
- return env.value('MAACTL_REPO') || DEFAULT_REPO;
44
- }
45
-
46
- function assetName() {
47
- return env.value('MAACTL_ASSET') || DEFAULT_ASSET;
48
- }
49
-
50
- /** Cache root mirroring the CLI's own layout: %LOCALAPPDATA%\maactl on Windows. */
51
- function home() {
52
- const explicit = env.value('MAACTL_HOME');
53
- if (explicit) {
54
- return path.resolve(explicit);
55
- }
56
- if (process.platform === 'win32') {
57
- const localAppData = env.value('LOCALAPPDATA') || path.join(os.homedir(), 'AppData', 'Local');
58
- return path.join(localAppData, 'maactl');
59
- }
60
- const cacheHome = env.value('XDG_CACHE_HOME') || path.join(os.homedir(), '.cache');
61
- return path.join(cacheHome, 'maactl');
62
- }
63
-
64
- function cacheExePath(versionOverride) {
65
- return path.join(home(), 'npm', versionOverride || version(), EXE_NAME);
66
- }
67
-
68
- function bundledExePath() {
69
- return path.join(PACKAGE_ROOT, 'vendor', EXE_NAME);
70
- }
71
-
72
- function binaryUrl() {
73
- const direct = env.value('MAACTL_BINARY_URL');
74
- if (direct) {
75
- return direct;
76
- }
77
- const github = `https://github.com/${repo()}/releases/download/${tag()}/${assetName()}`;
78
- const mirror = env.value('MAACTL_MIRROR').replace(/\/+$/, '');
79
- return mirror ? `${mirror}/${github}` : github;
80
- }
81
-
82
- function isUsable(file) {
83
- try {
84
- return fs.statSync(file).size > 0;
85
- } catch {
86
- return false;
87
- }
88
- }
89
-
90
- /**
91
- * Return the path of an existing maactl.exe without touching the network, or
92
- * `null` when nothing is available locally.
93
- */
94
- function resolveBinary() {
95
- const explicit = env.value('MAACTL_BINARY');
96
- if (explicit) {
97
- const resolved = path.resolve(explicit);
98
- if (!fs.existsSync(resolved)) {
99
- throw new MaactlError(messages.text('explicitMissing', resolved));
100
- }
101
- return resolved;
102
- }
103
-
104
- return [bundledExePath(), cacheExePath()].find(isUsable) || null;
105
- }
106
-
107
- /** Sanity-check a freshly downloaded executable by asking it for its version. */
108
- function verifyBinary(file, { spawn = spawnSync } = {}) {
109
- const header = Buffer.alloc(2);
110
- const fd = fs.openSync(file, 'r');
111
- try {
112
- fs.readSync(fd, header, 0, 2, 0);
113
- } finally {
114
- fs.closeSync(fd);
115
- }
116
- if (header.toString('latin1') !== 'MZ') {
117
- throw new MaactlError(messages.text('verifyFailed', file, 'not a Windows PE executable'));
118
- }
119
- if (fs.statSync(file).size < MIN_BINARY_BYTES) {
120
- throw new MaactlError(messages.text('verifyFailed', file, 'file is truncated'));
121
- }
122
-
123
- const result = spawn(file, ['--version'], {
124
- encoding: 'utf8',
125
- timeout: 60_000,
126
- windowsHide: true,
127
- });
128
- const output = `${result.stdout || ''}${result.stderr || ''}`.trim();
129
- if (result.error) {
130
- throw new MaactlError(messages.text('verifyFailed', file, result.error.message), { cause: result.error });
131
- }
132
- if (result.status !== 0 || !/maactl/i.test(output)) {
133
- throw new MaactlError(
134
- messages.text('verifyFailed', file, `unexpected output: ${output || '(empty)'}`),
135
- );
136
- }
137
- return output;
138
- }
139
-
140
- /**
141
- * Download maactl.exe into the cache unless a local copy already exists.
142
- * `write(text)` receives human readable progress lines; pass `quiet` to
143
- * suppress them.
144
- */
145
- async function ensureBinary({ write = () => {}, quiet = false, verify = verifyBinary } = {}) {
146
- const existing = resolveBinary();
147
- if (existing) {
148
- return existing;
149
- }
150
-
151
- const dest = cacheExePath();
152
- try {
153
- fs.mkdirSync(path.dirname(dest), { recursive: true });
154
- } catch (error) {
155
- throw new MaactlError(`${messages.text('downloadFailed', binaryUrl(), error.message)}\n cache: ${dest}`, {
156
- cause: error,
157
- });
158
- }
159
-
160
- const url = binaryUrl();
161
- const showProgress = !quiet && Boolean(process.stderr.isTTY);
162
- if (!quiet) {
163
- write(messages.text('downloading', url));
164
- }
165
-
166
- const onProgress = showProgress ? progressReporter() : undefined;
167
- try {
168
- await downloadWithRetry(url, dest, {
169
- onProgress,
170
- onRetry: (attempt, attempts, error) => {
171
- if (!quiet) {
172
- write(`maactl: retrying download (${attempt}/${attempts - 1}) after: ${error.message}`);
173
- }
174
- },
175
- });
176
- if (onProgress) {
177
- process.stderr.write('\n');
178
- }
179
- try {
180
- verify(dest);
181
- } catch (error) {
182
- fs.rmSync(dest, { force: true });
183
- throw error;
184
- }
185
- } catch (error) {
186
- if (error instanceof MaactlError) {
187
- throw error;
188
- }
189
- throw new MaactlError(messages.text('downloadFailed', url, error.message), { cause: error });
190
- }
191
-
192
- const size = formatBytes(fs.statSync(dest).size);
193
- if (!quiet) {
194
- write(messages.text('downloaded', size, dest));
195
- }
196
- return dest;
197
- }
198
-
199
- /** Print the exe the wrapper will hand over to, when MAACTL_VERBOSE is set. */
200
- function verbose() {
201
- return env.flag('MAACTL_VERBOSE');
202
- }
203
-
204
- module.exports = {
205
- PACKAGE,
206
- PACKAGE_ROOT,
207
- MIN_BINARY_BYTES,
208
- EXE_NAME,
209
- version,
210
- tag,
211
- repo,
212
- assetName,
213
- home,
214
- cacheExePath,
215
- bundledExePath,
216
- binaryUrl,
217
- resolveBinary,
218
- ensureBinary,
219
- verifyBinary,
220
- verbose,
221
- };
1
+ 'use strict';
2
+
3
+ // Locating (and if necessary fetching) the maactl.exe the wrapper hands over to.
4
+ //
5
+ // Resolution order:
6
+ // 1. MAACTL_BINARY – an explicit path supplied by the user;
7
+ // 2. <package>/vendor/maactl.exe – the executable published inside the tarball;
8
+ // 3. <cache>/npm/<version>/maactl.exe – a previous download, reused across runs.
9
+ //
10
+ // When none of them exists the exe is downloaded from the GitHub release that
11
+ // matches the package version, verified, and cached for the next run.
12
+
13
+ const fs = require('node:fs');
14
+ const os = require('node:os');
15
+ const path = require('node:path');
16
+ const { spawnSync } = require('node:child_process');
17
+
18
+ const { downloadWithRetry, formatBytes, progressReporter } = require('./download');
19
+ const env = require('./env');
20
+ const { MaactlError } = require('./errors');
21
+ const messages = require('./messages');
22
+
23
+ const PACKAGE_ROOT = path.resolve(__dirname, '..');
24
+ const PACKAGE = require(path.join(PACKAGE_ROOT, 'package.json'));
25
+
26
+ // Every asset is a Windows PE binary; anything smaller than this floor is a
27
+ // truncated download, an HTML error page or a git-lfs pointer.
28
+ const MIN_BINARY_BYTES = 1 << 20;
29
+
30
+ const EXE_NAME = 'maactl.exe';
31
+ const DEFAULT_REPO = 'TanyaShue/MaaCtl';
32
+ const DEFAULT_ASSET = 'maactl.exe';
33
+
34
+ function version() {
35
+ return (env.value('MAACTL_VERSION') || PACKAGE.version).replace(/^v/, '');
36
+ }
37
+
38
+ function tag() {
39
+ return `v${version()}`;
40
+ }
41
+
42
+ function repo() {
43
+ return env.value('MAACTL_REPO') || DEFAULT_REPO;
44
+ }
45
+
46
+ function assetName() {
47
+ return env.value('MAACTL_ASSET') || DEFAULT_ASSET;
48
+ }
49
+
50
+ /** Cache root mirroring the CLI's own layout: %LOCALAPPDATA%\maactl on Windows. */
51
+ function home() {
52
+ const explicit = env.value('MAACTL_HOME');
53
+ if (explicit) {
54
+ return path.resolve(explicit);
55
+ }
56
+ if (process.platform === 'win32') {
57
+ const localAppData = env.value('LOCALAPPDATA') || path.join(os.homedir(), 'AppData', 'Local');
58
+ return path.join(localAppData, 'maactl');
59
+ }
60
+ const cacheHome = env.value('XDG_CACHE_HOME') || path.join(os.homedir(), '.cache');
61
+ return path.join(cacheHome, 'maactl');
62
+ }
63
+
64
+ function cacheExePath(versionOverride) {
65
+ return path.join(home(), 'npm', versionOverride || version(), EXE_NAME);
66
+ }
67
+
68
+ function bundledExePath() {
69
+ return path.join(PACKAGE_ROOT, 'vendor', EXE_NAME);
70
+ }
71
+
72
+ function binaryUrl() {
73
+ const direct = env.value('MAACTL_BINARY_URL');
74
+ if (direct) {
75
+ return direct;
76
+ }
77
+ const github = `https://github.com/${repo()}/releases/download/${tag()}/${assetName()}`;
78
+ const mirror = env.value('MAACTL_MIRROR').replace(/\/+$/, '');
79
+ return mirror ? `${mirror}/${github}` : github;
80
+ }
81
+
82
+ function isUsable(file) {
83
+ try {
84
+ return fs.statSync(file).size > 0;
85
+ } catch {
86
+ return false;
87
+ }
88
+ }
89
+
90
+ /**
91
+ * Return the path of an existing maactl.exe without touching the network, or
92
+ * `null` when nothing is available locally.
93
+ */
94
+ function resolveBinary() {
95
+ const explicit = env.value('MAACTL_BINARY');
96
+ if (explicit) {
97
+ const resolved = path.resolve(explicit);
98
+ if (!fs.existsSync(resolved)) {
99
+ throw new MaactlError(messages.text('explicitMissing', resolved));
100
+ }
101
+ return resolved;
102
+ }
103
+
104
+ return [bundledExePath(), cacheExePath()].find(isUsable) || null;
105
+ }
106
+
107
+ /** Sanity-check a freshly downloaded executable by asking it for its version. */
108
+ function verifyBinary(file, { spawn = spawnSync } = {}) {
109
+ const header = Buffer.alloc(2);
110
+ const fd = fs.openSync(file, 'r');
111
+ try {
112
+ fs.readSync(fd, header, 0, 2, 0);
113
+ } finally {
114
+ fs.closeSync(fd);
115
+ }
116
+ if (header.toString('latin1') !== 'MZ') {
117
+ throw new MaactlError(messages.text('verifyFailed', file, 'not a Windows PE executable'));
118
+ }
119
+ if (fs.statSync(file).size < MIN_BINARY_BYTES) {
120
+ throw new MaactlError(messages.text('verifyFailed', file, 'file is truncated'));
121
+ }
122
+
123
+ const result = spawn(file, ['--version'], {
124
+ encoding: 'utf8',
125
+ timeout: 60_000,
126
+ windowsHide: true,
127
+ });
128
+ const output = `${result.stdout || ''}${result.stderr || ''}`.trim();
129
+ if (result.error) {
130
+ throw new MaactlError(messages.text('verifyFailed', file, result.error.message), { cause: result.error });
131
+ }
132
+ if (result.status !== 0 || !/maactl/i.test(output)) {
133
+ throw new MaactlError(
134
+ messages.text('verifyFailed', file, `unexpected output: ${output || '(empty)'}`),
135
+ );
136
+ }
137
+ return output;
138
+ }
139
+
140
+ /**
141
+ * Download maactl.exe into the cache unless a local copy already exists.
142
+ * `write(text)` receives human readable progress lines; pass `quiet` to
143
+ * suppress them.
144
+ */
145
+ async function ensureBinary({ write = () => {}, quiet = false, verify = verifyBinary } = {}) {
146
+ const existing = resolveBinary();
147
+ if (existing) {
148
+ return existing;
149
+ }
150
+
151
+ const dest = cacheExePath();
152
+ try {
153
+ fs.mkdirSync(path.dirname(dest), { recursive: true });
154
+ } catch (error) {
155
+ throw new MaactlError(`${messages.text('downloadFailed', binaryUrl(), error.message)}\n cache: ${dest}`, {
156
+ cause: error,
157
+ });
158
+ }
159
+
160
+ const url = binaryUrl();
161
+ const showProgress = !quiet && Boolean(process.stderr.isTTY);
162
+ if (!quiet) {
163
+ write(messages.text('downloading', url));
164
+ }
165
+
166
+ const onProgress = showProgress ? progressReporter() : undefined;
167
+ try {
168
+ await downloadWithRetry(url, dest, {
169
+ onProgress,
170
+ onRetry: (attempt, attempts, error) => {
171
+ if (!quiet) {
172
+ write(`maactl: retrying download (${attempt}/${attempts - 1}) after: ${error.message}`);
173
+ }
174
+ },
175
+ });
176
+ if (onProgress) {
177
+ process.stderr.write('\n');
178
+ }
179
+ try {
180
+ verify(dest);
181
+ } catch (error) {
182
+ fs.rmSync(dest, { force: true });
183
+ throw error;
184
+ }
185
+ } catch (error) {
186
+ if (error instanceof MaactlError) {
187
+ throw error;
188
+ }
189
+ throw new MaactlError(messages.text('downloadFailed', url, error.message), { cause: error });
190
+ }
191
+
192
+ const size = formatBytes(fs.statSync(dest).size);
193
+ if (!quiet) {
194
+ write(messages.text('downloaded', size, dest));
195
+ }
196
+ return dest;
197
+ }
198
+
199
+ /** Print the exe the wrapper will hand over to, when MAACTL_VERBOSE is set. */
200
+ function verbose() {
201
+ return env.flag('MAACTL_VERBOSE');
202
+ }
203
+
204
+ module.exports = {
205
+ PACKAGE,
206
+ PACKAGE_ROOT,
207
+ MIN_BINARY_BYTES,
208
+ EXE_NAME,
209
+ version,
210
+ tag,
211
+ repo,
212
+ assetName,
213
+ home,
214
+ cacheExePath,
215
+ bundledExePath,
216
+ binaryUrl,
217
+ resolveBinary,
218
+ ensureBinary,
219
+ verifyBinary,
220
+ verbose,
221
+ };