bunosh 0.4.0 → 0.4.6
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 +236 -60
- package/bunosh.js +207 -65
- package/package.json +7 -5
- package/src/completion.js +15 -2
- package/src/formatters/console.js +10 -5
- package/src/formatters/factory.js +30 -0
- package/src/io.js +5 -0
- package/src/mcp-server.js +575 -0
- package/src/printer.js +1 -1
- package/src/program.js +564 -486
- package/src/task.js +72 -9
- package/src/tasks/ai.js +10 -2
- package/src/tasks/exec.js +31 -9
- package/src/tasks/fetch.js +11 -3
- package/src/tasks/shell.js +18 -4
- package/src/upgrade.js +279 -199
package/src/upgrade.js
CHANGED
|
@@ -1,255 +1,335 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
1
|
+
import { exec, execSync } from 'child_process';
|
|
2
|
+
import { readFileSync, writeFileSync, existsSync } from 'fs';
|
|
3
|
+
import { platform } from 'os';
|
|
4
|
+
import { homedir } from 'os';
|
|
5
|
+
import { join } from 'path';
|
|
6
|
+
import { mkdir, chmod } from 'fs/promises';
|
|
7
|
+
import color from 'chalk';
|
|
5
8
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
export function isExecutable() {
|
|
9
|
+
export async function upgradeCommand(options = {}) {
|
|
10
|
+
const { force = false, check = false } = options;
|
|
11
|
+
|
|
10
12
|
try {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
const execPath = process.execPath;
|
|
14
|
-
const isCompiledBinary = !execPath.includes('node_modules') &&
|
|
15
|
-
!execPath.includes('.bun') &&
|
|
16
|
-
(execPath.includes('bunosh') || path.basename(execPath).startsWith('bunosh'));
|
|
13
|
+
const installMethod = detectInstallMethod();
|
|
14
|
+
console.log(`Bunosh is installed via: ${color.bold(installMethod)}`);
|
|
17
15
|
|
|
18
|
-
|
|
16
|
+
if (installMethod === 'bun') {
|
|
17
|
+
if (!check) {
|
|
18
|
+
await upgradeWithBun();
|
|
19
|
+
} else {
|
|
20
|
+
console.log('Will upgrade with: ' + color.bold('bun add -g bunosh'));
|
|
21
|
+
}
|
|
22
|
+
} else if (installMethod === 'npm') {
|
|
23
|
+
if (!check) {
|
|
24
|
+
await upgradeWithNpm();
|
|
25
|
+
} else {
|
|
26
|
+
console.log('Will upgrade with: ' + color.bold('npm update -g bunosh'));
|
|
27
|
+
}
|
|
28
|
+
} else if (installMethod === 'executable') {
|
|
29
|
+
await upgradeExecutable({ force, check });
|
|
30
|
+
} else {
|
|
31
|
+
console.error('Unknown installation method');
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
19
34
|
} catch (error) {
|
|
20
|
-
|
|
35
|
+
console.error(`Upgrade failed: ${error.message}`);
|
|
36
|
+
process.exit(1);
|
|
21
37
|
}
|
|
22
38
|
}
|
|
23
39
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
const arch = process.arch;
|
|
40
|
-
|
|
41
|
-
// Map to GitHub release asset names
|
|
42
|
-
switch (platform) {
|
|
43
|
-
case 'linux':
|
|
44
|
-
if (arch === 'x64' || arch === 'x86_64') {
|
|
45
|
-
return { platform: 'linux', arch: 'x64', asset: 'bunosh-linux-x64.tar.gz' };
|
|
46
|
-
}
|
|
47
|
-
break;
|
|
48
|
-
case 'darwin':
|
|
49
|
-
if (arch === 'arm64') {
|
|
50
|
-
return { platform: 'darwin', arch: 'arm64', asset: 'bunosh-darwin-arm64.tar.gz' };
|
|
51
|
-
}
|
|
52
|
-
if (arch === 'x64' || arch === 'x86_64') {
|
|
53
|
-
return { platform: 'darwin', arch: 'x64', asset: 'bunosh-darwin-x64.tar.gz' };
|
|
40
|
+
function detectInstallMethod() {
|
|
41
|
+
if (process.env.BUNOSH_EXECUTABLE === 'true') {
|
|
42
|
+
return 'executable';
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const executablePath = process.argv[0];
|
|
46
|
+
|
|
47
|
+
if (executablePath.endsWith('bunosh')) {
|
|
48
|
+
try {
|
|
49
|
+
const buffer = readFileSync(executablePath);
|
|
50
|
+
const header = buffer.subarray(0, 4).toString('hex');
|
|
51
|
+
const binarySignatures = ['7f454c46', 'feedface', 'feedfacf', '4d5a9000'];
|
|
52
|
+
|
|
53
|
+
if (binarySignatures.includes(header)) {
|
|
54
|
+
return 'executable';
|
|
54
55
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
if (
|
|
58
|
-
return
|
|
56
|
+
|
|
57
|
+
const textContent = buffer.toString('utf8');
|
|
58
|
+
if (!textContent.startsWith('#!') && !textContent.includes('node_modules')) {
|
|
59
|
+
return 'executable';
|
|
59
60
|
}
|
|
60
|
-
|
|
61
|
+
} catch (e) {
|
|
62
|
+
return 'executable';
|
|
63
|
+
}
|
|
61
64
|
}
|
|
62
65
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
66
|
+
const invokedVia = process.argv[0];
|
|
67
|
+
|
|
68
|
+
if (invokedVia.endsWith('node') || invokedVia.includes('node.exe')) {
|
|
69
|
+
return 'npm';
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (invokedVia.endsWith('bun') || invokedVia.includes('bun.exe')) {
|
|
73
|
+
try {
|
|
74
|
+
const bunGlobalPath = execSync('bun pm -g bin', { encoding: 'utf8' }).trim();
|
|
75
|
+
const bunoshPath = join(bunGlobalPath, 'bunosh');
|
|
76
|
+
|
|
77
|
+
if (existsSync(bunoshPath)) {
|
|
78
|
+
return 'bun';
|
|
79
|
+
}
|
|
80
|
+
} catch (e) {
|
|
74
81
|
}
|
|
75
|
-
return await response.json();
|
|
76
|
-
} catch (error) {
|
|
77
|
-
throw new Error(`Failed to fetch release info: ${error.message}`);
|
|
78
82
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* Gets current version from package.json or executable
|
|
83
|
-
*/
|
|
84
|
-
export function getCurrentVersion() {
|
|
83
|
+
|
|
85
84
|
try {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
const pkg = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
|
|
90
|
-
if (pkg.name === 'bunosh') {
|
|
91
|
-
return pkg.version;
|
|
92
|
-
}
|
|
85
|
+
const npmListOutput = execSync('npm list -g bunosh --depth=0 2>/dev/null || echo "not found"', { encoding: 'utf8' });
|
|
86
|
+
if (npmListOutput.includes('bunosh@') && !npmListOutput.includes('not found')) {
|
|
87
|
+
return 'npm';
|
|
93
88
|
}
|
|
94
89
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
return version;
|
|
99
|
-
} catch (error) {
|
|
100
|
-
// Fallback to unknown
|
|
101
|
-
return 'unknown';
|
|
90
|
+
const bunListOutput = execSync('bun pm -g ls 2>/dev/null | grep bunosh || echo "not found"', { encoding: 'utf8' });
|
|
91
|
+
if (bunListOutput.includes('bunosh') && !bunListOutput.includes('not found')) {
|
|
92
|
+
return 'bun';
|
|
102
93
|
}
|
|
103
|
-
} catch (
|
|
104
|
-
return 'unknown';
|
|
94
|
+
} catch (e) {
|
|
105
95
|
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Compares version strings (simple semantic version comparison)
|
|
110
|
-
*/
|
|
111
|
-
export function isNewerVersion(latest, current) {
|
|
112
|
-
if (current === 'unknown') return true;
|
|
113
|
-
|
|
114
|
-
// Remove 'v' prefix if present
|
|
115
|
-
const latestClean = latest.replace(/^v/, '');
|
|
116
|
-
const currentClean = current.replace(/^v/, '');
|
|
117
|
-
|
|
118
|
-
const latestParts = latestClean.split('.').map(n => parseInt(n) || 0);
|
|
119
|
-
const currentParts = currentClean.split('.').map(n => parseInt(n) || 0);
|
|
120
|
-
|
|
121
|
-
// Pad arrays to same length
|
|
122
|
-
const maxLength = Math.max(latestParts.length, currentParts.length);
|
|
123
|
-
while (latestParts.length < maxLength) latestParts.push(0);
|
|
124
|
-
while (currentParts.length < maxLength) currentParts.push(0);
|
|
125
96
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
if (latestParts[i] > currentParts[i]) return true;
|
|
129
|
-
if (latestParts[i] < currentParts[i]) return false;
|
|
97
|
+
if (process.argv[0].endsWith('bunosh')) {
|
|
98
|
+
return 'executable';
|
|
130
99
|
}
|
|
131
100
|
|
|
132
|
-
return
|
|
101
|
+
return 'unknown';
|
|
133
102
|
}
|
|
134
103
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
*/
|
|
138
|
-
export async function downloadAndInstall(release, platformInfo, executablePath, onProgress) {
|
|
139
|
-
const asset = release.assets.find(a => a.name === platformInfo.asset);
|
|
140
|
-
if (!asset) {
|
|
141
|
-
throw new Error(`No asset found for platform: ${platformInfo.asset}`);
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'bunosh-upgrade-'));
|
|
145
|
-
const downloadPath = path.join(tempDir, asset.name);
|
|
104
|
+
async function upgradeWithBun() {
|
|
105
|
+
console.log('Upgrading with Bun...');
|
|
146
106
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
107
|
+
return new Promise((resolve, reject) => {
|
|
108
|
+
exec('bun add -g bunosh', (error, stdout, stderr) => {
|
|
109
|
+
if (error) {
|
|
110
|
+
reject(new Error(`Bun upgrade failed: ${stderr || error.message}`));
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
console.log(stdout);
|
|
114
|
+
console.log(color.green('Upgrade successful!'));
|
|
115
|
+
resolve();
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
}
|
|
158
119
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
120
|
+
async function upgradeWithNpm() {
|
|
121
|
+
console.log('Upgrading with npm...');
|
|
122
|
+
|
|
123
|
+
return new Promise((resolve, reject) => {
|
|
124
|
+
exec('npm update -g bunosh', (error, stdout, stderr) => {
|
|
125
|
+
if (error) {
|
|
126
|
+
reject(new Error(`npm upgrade failed: ${stderr || error.message}`));
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
console.log(stdout);
|
|
130
|
+
console.log(color.green('Upgrade successful!'));
|
|
131
|
+
resolve();
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
}
|
|
165
135
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
136
|
+
export async function upgradeExecutable(options = {}) {
|
|
137
|
+
const { force = false, check = false } = options;
|
|
138
|
+
|
|
139
|
+
const currentVersion = getCurrentVersion();
|
|
140
|
+
console.log(`Current version: ${color.bold(currentVersion)}`);
|
|
141
|
+
|
|
142
|
+
if (check) {
|
|
143
|
+
console.log('Checking for updates...');
|
|
144
|
+
try {
|
|
145
|
+
const release = await getLatestRelease();
|
|
146
|
+
const latestVersion = release.tag_name;
|
|
177
147
|
|
|
178
|
-
|
|
179
|
-
const extractedFiles = fs.readdirSync(extractDir);
|
|
180
|
-
const executableName = extractedFiles.find(f => f.startsWith('bunosh-'));
|
|
148
|
+
console.log(`Latest version: ${color.bold(latestVersion)}`);
|
|
181
149
|
|
|
182
|
-
if (
|
|
183
|
-
|
|
150
|
+
if (isNewerVersion(latestVersion, currentVersion)) {
|
|
151
|
+
console.log(`${color.green('Update available!')} ${currentVersion} → ${latestVersion}`);
|
|
152
|
+
console.log('Run ' + color.bold('bunosh upgrade') + ' to update.');
|
|
153
|
+
} else {
|
|
154
|
+
console.log(`${color.green('You are on the latest version!')}`);
|
|
184
155
|
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
fs.chmodSync(executablePath, 0o755);
|
|
156
|
+
} catch (error) {
|
|
157
|
+
console.error(`Failed to check for updates: ${error.message}`);
|
|
158
|
+
process.exit(1);
|
|
189
159
|
}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
fs.copyFileSync(backupPath, executablePath);
|
|
204
|
-
fs.unlinkSync(backupPath);
|
|
205
|
-
} catch (restoreError) {
|
|
206
|
-
console.error('Failed to restore backup:', restoreError.message);
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
console.log('Starting upgrade process...');
|
|
164
|
+
console.log();
|
|
165
|
+
|
|
166
|
+
let lastMessage = '';
|
|
167
|
+
const result = await performUpgradeExecutable({
|
|
168
|
+
force,
|
|
169
|
+
onProgress: (message) => {
|
|
170
|
+
if (message !== lastMessage) {
|
|
171
|
+
console.log(` ${message}`);
|
|
172
|
+
lastMessage = message;
|
|
207
173
|
}
|
|
208
174
|
}
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
console.log();
|
|
178
|
+
if (result.updated) {
|
|
179
|
+
console.log(`${color.green('Upgrade successful!')}`);
|
|
180
|
+
console.log(` ${result.currentVersion} → ${color.bold(result.latestVersion)}`);
|
|
181
|
+
console.log();
|
|
182
|
+
console.log(`Run ${color.bold('bunosh --version')} to verify the new version.`);
|
|
183
|
+
} else {
|
|
184
|
+
console.log(`${color.green(result.message)}`);
|
|
185
|
+
if (!force) {
|
|
186
|
+
console.log(` Use ${color.bold('--force')} to reinstall the current version.`);
|
|
213
187
|
}
|
|
214
|
-
|
|
215
|
-
throw error;
|
|
216
188
|
}
|
|
217
189
|
}
|
|
218
190
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
191
|
+
function getCurrentVersion() {
|
|
192
|
+
try {
|
|
193
|
+
const pkgPath = new URL('../package.json', import.meta.url);
|
|
194
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
|
|
195
|
+
return pkg.version;
|
|
196
|
+
} catch (e) {
|
|
197
|
+
return 'unknown';
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
async function getLatestRelease() {
|
|
202
|
+
const response = await fetch('https://api.github.com/repos/davertmik/bunosh/releases/latest', {
|
|
203
|
+
headers: {
|
|
204
|
+
'User-Agent': 'bunosh'
|
|
205
|
+
}
|
|
206
|
+
});
|
|
224
207
|
|
|
225
|
-
if (!
|
|
226
|
-
throw new Error(
|
|
208
|
+
if (!response.ok) {
|
|
209
|
+
throw new Error(`GitHub API error: ${response.status}`);
|
|
227
210
|
}
|
|
211
|
+
|
|
212
|
+
return await response.json();
|
|
213
|
+
}
|
|
228
214
|
|
|
229
|
-
|
|
215
|
+
function isNewerVersion(latest, current) {
|
|
216
|
+
const latestParts = latest.replace(/^v/, '').split('.').map(Number);
|
|
217
|
+
const currentParts = current.replace(/^v/, '').split('.').map(Number);
|
|
218
|
+
|
|
219
|
+
for (let i = 0; i < Math.max(latestParts.length, currentParts.length); i++) {
|
|
220
|
+
const latestPart = latestParts[i] || 0;
|
|
221
|
+
const currentPart = currentParts[i] || 0;
|
|
222
|
+
|
|
223
|
+
if (latestPart > currentPart) return true;
|
|
224
|
+
if (latestPart < currentPart) return false;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return false;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
async function performUpgradeExecutable(options = {}) {
|
|
231
|
+
const { force = false, onProgress = () => {} } = options;
|
|
230
232
|
|
|
231
|
-
const currentVersion = getCurrentVersion();
|
|
232
233
|
const release = await getLatestRelease();
|
|
233
234
|
const latestVersion = release.tag_name;
|
|
235
|
+
const currentVersion = getCurrentVersion();
|
|
234
236
|
|
|
235
237
|
if (!force && !isNewerVersion(latestVersion, currentVersion)) {
|
|
236
238
|
return {
|
|
237
239
|
updated: false,
|
|
240
|
+
message: 'Already on latest version',
|
|
238
241
|
currentVersion,
|
|
239
|
-
latestVersion
|
|
240
|
-
message: `Already on latest version: ${currentVersion}`
|
|
242
|
+
latestVersion
|
|
241
243
|
};
|
|
242
244
|
}
|
|
243
|
-
|
|
244
|
-
const platformInfo = getPlatformInfo();
|
|
245
|
-
const executablePath = getExecutablePath();
|
|
246
245
|
|
|
247
|
-
|
|
246
|
+
const platformName = getPlatformName();
|
|
247
|
+
const asset = release.assets.find(a => a.name.includes(platformName));
|
|
248
|
+
|
|
249
|
+
if (!asset) {
|
|
250
|
+
throw new Error(`Unsupported platform: ${platformName}`);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
onProgress('Downloading update...');
|
|
254
|
+
|
|
255
|
+
const downloadDir = join(homedir(), '.bunosh', 'updates');
|
|
256
|
+
await mkdir(downloadDir, { recursive: true });
|
|
257
|
+
|
|
258
|
+
const downloadPath = join(downloadDir, asset.name);
|
|
259
|
+
|
|
260
|
+
await downloadFile(asset.browser_download_url, downloadPath, onProgress);
|
|
261
|
+
|
|
262
|
+
onProgress('Installing update...');
|
|
263
|
+
|
|
264
|
+
const currentPath = process.argv[0];
|
|
265
|
+
const backupPath = `${currentPath}.backup`;
|
|
266
|
+
if (existsSync(currentPath)) {
|
|
267
|
+
writeFileSync(backupPath, readFileSync(currentPath));
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
writeFileSync(currentPath, readFileSync(downloadPath));
|
|
271
|
+
await chmod(currentPath, '755');
|
|
248
272
|
|
|
249
273
|
return {
|
|
250
274
|
updated: true,
|
|
251
275
|
currentVersion,
|
|
252
|
-
latestVersion
|
|
253
|
-
message: `Successfully upgraded from ${currentVersion} to ${latestVersion}`
|
|
276
|
+
latestVersion
|
|
254
277
|
};
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
function getPlatformName() {
|
|
281
|
+
const arch = platform().arch();
|
|
282
|
+
const sysPlatform = platform().toLowerCase();
|
|
283
|
+
|
|
284
|
+
if (sysPlatform === 'darwin' && arch === 'arm64') {
|
|
285
|
+
return 'darwin-arm64';
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
if (sysPlatform === 'darwin' && arch === 'x64') {
|
|
289
|
+
return 'darwin-x64';
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
if (sysPlatform === 'linux' && arch === 'x64') {
|
|
293
|
+
return 'linux-x64';
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
if (sysPlatform === 'win32' && arch === 'x64') {
|
|
297
|
+
return 'windows-x64.exe';
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
throw new Error(`Unsupported platform: ${sysPlatform} ${arch}`);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
async function downloadFile(url, filePath, onProgress = () => {}) {
|
|
304
|
+
const response = await fetch(url);
|
|
305
|
+
|
|
306
|
+
if (!response.ok) {
|
|
307
|
+
throw new Error(`Download failed: ${response.status}`);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
const reader = response.body.getReader();
|
|
311
|
+
const contentLength = +response.headers.get('Content-Length');
|
|
312
|
+
let receivedLength = 0;
|
|
313
|
+
|
|
314
|
+
const chunks = [];
|
|
315
|
+
while (true) {
|
|
316
|
+
const { done, value } = await reader.read();
|
|
317
|
+
|
|
318
|
+
if (done) break;
|
|
319
|
+
|
|
320
|
+
chunks.push(value);
|
|
321
|
+
receivedLength += value.length;
|
|
322
|
+
|
|
323
|
+
const progress = Math.round((receivedLength / contentLength) * 100);
|
|
324
|
+
onProgress(`Downloading... ${progress}%`);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
const chunksAll = new Uint8Array(receivedLength);
|
|
328
|
+
let position = 0;
|
|
329
|
+
for (let chunk of chunks) {
|
|
330
|
+
chunksAll.set(chunk, position);
|
|
331
|
+
position += chunk.length;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
writeFileSync(filePath, chunksAll);
|
|
255
335
|
}
|