@yetech/estra7ah 3.3.1 → 3.3.3
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/dist/estra7ah.js +88 -13
- package/package.json +1 -1
package/dist/estra7ah.js
CHANGED
|
@@ -10,7 +10,9 @@ const RUNNERS = {
|
|
|
10
10
|
win64: "runner-win-x64.exe",
|
|
11
11
|
linux64: "runner-linux-x64",
|
|
12
12
|
linuxarm64: "runner-linux-arm64",
|
|
13
|
-
|
|
13
|
+
// pkg has no node18 armv7 target, so 32-bit ARM (Raspberry Pi OS) gets the
|
|
14
|
+
// plain webpack bundle and runs it under the system Node instead.
|
|
15
|
+
linuxarmv7: "runner.js",
|
|
14
16
|
};
|
|
15
17
|
const DIR_NAME = __dirname;
|
|
16
18
|
|
|
@@ -51,17 +53,46 @@ function downloadRange(url, fd, start, end, progressBar, attempt = 0) {
|
|
|
51
53
|
.then(
|
|
52
54
|
(response) =>
|
|
53
55
|
new Promise((resolve, reject) => {
|
|
56
|
+
// A 200 means the server (or a proxy on this plain-HTTP route) ignored
|
|
57
|
+
// the Range header and is sending the whole file: writing that at six
|
|
58
|
+
// different offsets corrupts the binary. Bail out to single-stream.
|
|
59
|
+
if (response.status !== 206) {
|
|
60
|
+
response.data.destroy();
|
|
61
|
+
return reject({ noRangeSupport: true });
|
|
62
|
+
}
|
|
54
63
|
let position = start;
|
|
64
|
+
// With timeout: 0 a dead connection would hang the update forever
|
|
65
|
+
// (progress stuck at 0%). Kill the stream if no data arrives for 30s
|
|
66
|
+
// so the retry/fallback path can take over.
|
|
67
|
+
let watchdog;
|
|
68
|
+
const resetWatchdog = () => {
|
|
69
|
+
clearTimeout(watchdog);
|
|
70
|
+
watchdog = setTimeout(() => {
|
|
71
|
+
response.data.destroy(new Error("download stalled: no data for 30s"));
|
|
72
|
+
}, 30000);
|
|
73
|
+
};
|
|
74
|
+
resetWatchdog();
|
|
75
|
+
response.data.on("close", () => clearTimeout(watchdog));
|
|
55
76
|
response.data.on("data", (chunk) => {
|
|
77
|
+
resetWatchdog();
|
|
56
78
|
fs.writeSync(fd, chunk, 0, chunk.length, position);
|
|
57
79
|
position += chunk.length;
|
|
58
80
|
progressBar.tick(chunk.length);
|
|
59
81
|
});
|
|
60
|
-
response.data.on("end", () =>
|
|
82
|
+
response.data.on("end", () => {
|
|
83
|
+
// "end" also fires on some premature disconnects; an incomplete
|
|
84
|
+
// chunk must be retried, never treated as success.
|
|
85
|
+
if (position !== end + 1) {
|
|
86
|
+
return reject({ err: new Error(`chunk ended at ${position}, expected ${end + 1}`), position });
|
|
87
|
+
}
|
|
88
|
+
resolve(position);
|
|
89
|
+
});
|
|
90
|
+
response.data.on("aborted", () => reject({ err: new Error("connection aborted"), position }));
|
|
61
91
|
response.data.on("error", (err) => reject({ err, position }));
|
|
62
92
|
})
|
|
63
93
|
)
|
|
64
94
|
.catch((failure) => {
|
|
95
|
+
if (failure && failure.noRangeSupport) throw failure;
|
|
65
96
|
const resumeAt = failure.position || start;
|
|
66
97
|
if (attempt < 2) return downloadRange(url, fd, resumeAt, end, progressBar, attempt + 1);
|
|
67
98
|
throw failure.err || failure;
|
|
@@ -103,10 +134,17 @@ async function downloadFileFast(url, destPath) {
|
|
|
103
134
|
await Promise.all(jobs);
|
|
104
135
|
} catch (error) {
|
|
105
136
|
fs.closeSync(fd);
|
|
106
|
-
|
|
137
|
+
try {
|
|
138
|
+
fs.unlinkSync(tmpPath);
|
|
139
|
+
} catch (e) {}
|
|
140
|
+
if (error && error.noRangeSupport) return downloadFileStream(url, destPath);
|
|
107
141
|
throw error;
|
|
108
142
|
}
|
|
109
143
|
fs.closeSync(fd);
|
|
144
|
+
if (fs.statSync(tmpPath).size !== totalLength) {
|
|
145
|
+
fs.unlinkSync(tmpPath);
|
|
146
|
+
throw new Error("downloaded size does not match content-length");
|
|
147
|
+
}
|
|
110
148
|
fs.renameSync(tmpPath, destPath);
|
|
111
149
|
}
|
|
112
150
|
|
|
@@ -121,12 +159,40 @@ function downloadFileStream(url, destPath) {
|
|
|
121
159
|
total: totalLength,
|
|
122
160
|
})
|
|
123
161
|
: null;
|
|
124
|
-
const
|
|
162
|
+
const tmpPath = `${destPath}.download`;
|
|
163
|
+
const writer = fs.createWriteStream(tmpPath);
|
|
125
164
|
return new Promise((resolve, reject) => {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
165
|
+
let watchdog;
|
|
166
|
+
const fail = (err) => {
|
|
167
|
+
clearTimeout(watchdog);
|
|
168
|
+
writer.destroy();
|
|
169
|
+
try {
|
|
170
|
+
fs.unlinkSync(tmpPath);
|
|
171
|
+
} catch (e) {}
|
|
172
|
+
reject(err);
|
|
173
|
+
};
|
|
174
|
+
const resetWatchdog = () => {
|
|
175
|
+
clearTimeout(watchdog);
|
|
176
|
+
watchdog = setTimeout(() => {
|
|
177
|
+
response.data.destroy(new Error("download stalled: no data for 30s"));
|
|
178
|
+
}, 30000);
|
|
179
|
+
};
|
|
180
|
+
resetWatchdog();
|
|
181
|
+
response.data.on("data", (chunk) => {
|
|
182
|
+
resetWatchdog();
|
|
183
|
+
if (progressBar) progressBar.tick(chunk.length);
|
|
184
|
+
});
|
|
185
|
+
writer.on("error", fail);
|
|
186
|
+
writer.on("finish", () => {
|
|
187
|
+
clearTimeout(watchdog);
|
|
188
|
+
if (totalLength && fs.statSync(tmpPath).size !== totalLength) {
|
|
189
|
+
return fail(new Error("downloaded size does not match content-length"));
|
|
190
|
+
}
|
|
191
|
+
fs.renameSync(tmpPath, destPath);
|
|
192
|
+
resolve();
|
|
193
|
+
});
|
|
194
|
+
response.data.on("aborted", () => fail(new Error("connection aborted")));
|
|
195
|
+
response.data.on("error", fail);
|
|
130
196
|
response.data.pipe(writer);
|
|
131
197
|
});
|
|
132
198
|
});
|
|
@@ -150,13 +216,18 @@ async function downloadRunner() {
|
|
|
150
216
|
localVersion = fs.readFileSync(versionFilePath, "utf-8");
|
|
151
217
|
}
|
|
152
218
|
|
|
153
|
-
if (localVersion !== version) {
|
|
219
|
+
if (localVersion !== version || !fs.existsSync(`${DIR_NAME}/${getRunner()}`)) {
|
|
154
220
|
console.log("** DOWNLOADING NEW RUNNER VERSION **");
|
|
155
221
|
const serverFile = `${YETECH_URL}/runner/${getRunner()}`;
|
|
156
222
|
const runnerPath = `${DIR_NAME}/${getRunner()}`;
|
|
157
223
|
|
|
158
224
|
try {
|
|
159
|
-
|
|
225
|
+
try {
|
|
226
|
+
await downloadFileFast(serverFile, runnerPath);
|
|
227
|
+
} catch (error) {
|
|
228
|
+
console.error("Parallel download failed, retrying with a single stream:", error.message);
|
|
229
|
+
await downloadFileStream(serverFile, runnerPath);
|
|
230
|
+
}
|
|
160
231
|
fs.writeFileSync(versionFilePath, version);
|
|
161
232
|
console.log("** Finished DOWNLOADING NEW RUNNER VERSION **");
|
|
162
233
|
} catch (error) {
|
|
@@ -167,20 +238,24 @@ async function downloadRunner() {
|
|
|
167
238
|
|
|
168
239
|
// Start the runner executable
|
|
169
240
|
function startRunner() {
|
|
170
|
-
const
|
|
241
|
+
const runnerFile = getRunner();
|
|
242
|
+
const runnerPath = `"${DIR_NAME}/${runnerFile}"`;
|
|
171
243
|
const args = process.argv.slice(2);
|
|
172
244
|
const options = { stdio: "inherit", shell: true };
|
|
173
245
|
|
|
174
246
|
setTimeout(() => {
|
|
175
247
|
if (process.platform !== "win32") {
|
|
176
248
|
try {
|
|
177
|
-
fs.chmodSync(`${DIR_NAME}/${
|
|
249
|
+
fs.chmodSync(`${DIR_NAME}/${runnerFile}`, "755");
|
|
178
250
|
} catch (error) {
|
|
179
251
|
console.error("Error setting permissions:", error.message);
|
|
180
252
|
}
|
|
181
253
|
}
|
|
182
254
|
|
|
183
|
-
|
|
255
|
+
// .js runner (armv7) runs under the same Node that launched us
|
|
256
|
+
const command = runnerFile.endsWith(".js") ? `"${process.execPath}"` : runnerPath;
|
|
257
|
+
const commandArgs = runnerFile.endsWith(".js") ? [runnerPath, ...args] : args;
|
|
258
|
+
const runnerProcess = child_process.spawn(command, commandArgs, options);
|
|
184
259
|
runnerProcess.on("exit", (code) => {
|
|
185
260
|
console.log(`Runner exited with code ${code}`);
|
|
186
261
|
});
|