howcode 0.1.0 → 0.1.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/README.md +10 -0
- package/lib/howcode.js +127 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -13,6 +13,16 @@ howcode
|
|
|
13
13
|
|
|
14
14
|
On first run, the launcher downloads the matching desktop build from GitHub Releases and caches it locally.
|
|
15
15
|
|
|
16
|
+
## Linux note
|
|
17
|
+
|
|
18
|
+
If the Linux build hits a WebKit/GBM white-screen issue, the launcher retries with `WEBKIT_DISABLE_DMABUF_RENDERER=1` automatically.
|
|
19
|
+
|
|
20
|
+
If you are launching a downloaded Linux release asset manually, use:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
WEBKIT_DISABLE_DMABUF_RENDERER=1 ./howcode/bin/launcher
|
|
24
|
+
```
|
|
25
|
+
|
|
16
26
|
## Cache location
|
|
17
27
|
|
|
18
28
|
- macOS: `~/Library/Caches/howcode`
|
package/lib/howcode.js
CHANGED
|
@@ -44,6 +44,8 @@ const TARGETS = {
|
|
|
44
44
|
},
|
|
45
45
|
};
|
|
46
46
|
|
|
47
|
+
const LINUX_DMABUF_FAILURE_PATTERNS = [/Failed to create GBM buffer/i, /GLXBadWindow/i, /dmabuf/i];
|
|
48
|
+
|
|
47
49
|
function readJsonIfPresent(filePath) {
|
|
48
50
|
try {
|
|
49
51
|
return JSON.parse(fs.readFileSync(filePath, "utf8"));
|
|
@@ -203,15 +205,135 @@ async function pruneOldVersions(cacheRoot, keepDir) {
|
|
|
203
205
|
);
|
|
204
206
|
}
|
|
205
207
|
|
|
206
|
-
function
|
|
207
|
-
|
|
208
|
+
function spawnLauncherProcess(executablePath, options = {}) {
|
|
209
|
+
return spawn(executablePath, [], {
|
|
208
210
|
detached: true,
|
|
209
|
-
stdio: "ignore",
|
|
211
|
+
stdio: options.stdio || "ignore",
|
|
210
212
|
windowsHide: true,
|
|
211
213
|
cwd: path.dirname(executablePath),
|
|
214
|
+
env: {
|
|
215
|
+
...process.env,
|
|
216
|
+
...(options.env || {}),
|
|
217
|
+
},
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function killDetachedProcess(child) {
|
|
222
|
+
if (!child.pid) {
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
try {
|
|
227
|
+
if (process.platform !== "win32") {
|
|
228
|
+
process.kill(-child.pid, "SIGTERM");
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
} catch {}
|
|
232
|
+
|
|
233
|
+
try {
|
|
234
|
+
child.kill("SIGTERM");
|
|
235
|
+
} catch {}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function hasLinuxDmabufFailure(output) {
|
|
239
|
+
return LINUX_DMABUF_FAILURE_PATTERNS.some((pattern) => pattern.test(output));
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function trimLauncherLog(output) {
|
|
243
|
+
return output.trim().split(/\r?\n/).slice(-10).join("\n");
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
async function launch(executablePath) {
|
|
247
|
+
if (process.platform !== "linux" || process.env.WEBKIT_DISABLE_DMABUF_RENDERER === "1") {
|
|
248
|
+
const child = spawnLauncherProcess(executablePath);
|
|
249
|
+
child.unref();
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const child = spawnLauncherProcess(executablePath, {
|
|
254
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
212
255
|
});
|
|
213
256
|
|
|
257
|
+
let output = "";
|
|
258
|
+
const appendOutput = (chunk) => {
|
|
259
|
+
output += chunk.toString();
|
|
260
|
+
if (output.length > 32_000) {
|
|
261
|
+
output = output.slice(-32_000);
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
child.stdout?.on("data", appendOutput);
|
|
266
|
+
child.stderr?.on("data", appendOutput);
|
|
267
|
+
|
|
268
|
+
const outcome = await new Promise((resolve) => {
|
|
269
|
+
let settled = false;
|
|
270
|
+
|
|
271
|
+
const finish = (value) => {
|
|
272
|
+
if (settled) {
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
settled = true;
|
|
277
|
+
clearTimeout(timer);
|
|
278
|
+
resolve(value);
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
const checkForFallback = () => {
|
|
282
|
+
if (hasLinuxDmabufFailure(output)) {
|
|
283
|
+
finish({ type: "fallback" });
|
|
284
|
+
}
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
const timer = setTimeout(() => finish({ type: "ok" }), 4_000);
|
|
288
|
+
|
|
289
|
+
child.stdout?.on("data", checkForFallback);
|
|
290
|
+
child.stderr?.on("data", checkForFallback);
|
|
291
|
+
child.once("error", (error) => finish({ type: "error", error }));
|
|
292
|
+
child.once("exit", (code, signal) => {
|
|
293
|
+
if (hasLinuxDmabufFailure(output)) {
|
|
294
|
+
finish({ type: "fallback" });
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
finish({ type: "exit", code, signal });
|
|
299
|
+
});
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
child.stdout?.destroy();
|
|
303
|
+
child.stderr?.destroy();
|
|
214
304
|
child.unref();
|
|
305
|
+
|
|
306
|
+
if (outcome.type === "fallback") {
|
|
307
|
+
killDetachedProcess(child);
|
|
308
|
+
|
|
309
|
+
const fallbackChild = spawnLauncherProcess(executablePath, {
|
|
310
|
+
env: {
|
|
311
|
+
WEBKIT_DISABLE_DMABUF_RENDERER: "1",
|
|
312
|
+
},
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
fallbackChild.unref();
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
if (outcome.type === "ok") {
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
if (outcome.type === "error") {
|
|
324
|
+
throw outcome.error;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
if (outcome.code === 0) {
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
const logTail = trimLauncherLog(output);
|
|
332
|
+
throw new Error(
|
|
333
|
+
logTail
|
|
334
|
+
? `Desktop launcher exited early.\n${logTail}`
|
|
335
|
+
: `Desktop launcher exited early with code ${outcome.code ?? "unknown"}.`,
|
|
336
|
+
);
|
|
215
337
|
}
|
|
216
338
|
|
|
217
339
|
async function main() {
|
|
@@ -226,7 +348,7 @@ async function main() {
|
|
|
226
348
|
releaseInfo = await resolveLatestRelease(target);
|
|
227
349
|
} catch (error) {
|
|
228
350
|
if (current?.executablePath && fs.existsSync(current.executablePath)) {
|
|
229
|
-
launch(current.executablePath);
|
|
351
|
+
await launch(current.executablePath);
|
|
230
352
|
return;
|
|
231
353
|
}
|
|
232
354
|
|
|
@@ -239,7 +361,7 @@ async function main() {
|
|
|
239
361
|
}
|
|
240
362
|
|
|
241
363
|
await pruneOldVersions(cacheRoot, paths.installDir);
|
|
242
|
-
launch(paths.executablePath);
|
|
364
|
+
await launch(paths.executablePath);
|
|
243
365
|
}
|
|
244
366
|
|
|
245
367
|
module.exports = {
|