History

fix desktop

Rhys Sullivan committed 6 months ago

Commit 8921e791 parent 15719265 files · +68 −1Browse files

Unified · Split

5 files
  1. .changeset/desktop-sidecar-quickjs-wasm.md +10 −0
  2. apps/desktop/package.json +3 −0
  3. apps/desktop/scripts/build-sidecar.ts +22 −1
  4. apps/desktop/src/sidecar/server.ts +30 −0
  5. bun.lock +3 −0
.changeset/desktop-sidecar-quickjs-wasm.md+10−0
@@ -0,0 +1,10 @@
1---
2"@executor-js/desktop": patch
3"executor": patch
4---
5
6Fix: desktop sidecar now stages the QuickJS WASM file next to the compiled
7binary and preloads it at boot. Without this, MCP `execute` calls (and any
8other code execution) hit `quickjs-emscripten`'s loader inside bunfs and
9crashed with `ENOENT: emscripten-module.wasm`. Mirrors the standalone CLI's
10preload pattern.
apps/desktop/package.json+3−0
@@ -28,6 +28,9 @@ · 27 unmodified lines
28 "devDependencies": {28 "devDependencies": {
29 "@executor-js/app": "workspace:*",29 "@executor-js/app": "workspace:*",
30 "@executor-js/local": "workspace:*",30 "@executor-js/local": "workspace:*",
31 "@executor-js/runtime-quickjs": "workspace:*",
32 "@jitl/quickjs-wasmfile-release-sync": "catalog:",
33 "quickjs-emscripten": "catalog:",
31 "@executor-js/plugin-desktop-settings": "workspace:*",34 "@executor-js/plugin-desktop-settings": "workspace:*",
32 "@executor-js/plugin-file-secrets": "workspace:*",35 "@executor-js/plugin-file-secrets": "workspace:*",
33 "@executor-js/plugin-google-discovery": "workspace:*",36 "@executor-js/plugin-google-discovery": "workspace:*",
apps/desktop/scripts/build-sidecar.ts+22−1
@@ -17,7 +17,8 @@ · 16 unmodified lines
17 */17 */
18import { mkdir, rm, cp, writeFile } from "node:fs/promises";18import { mkdir, rm, cp, writeFile } from "node:fs/promises";
19import { existsSync } from "node:fs";19import { existsSync } from "node:fs";
20import { join, resolve } from "node:path";20import { createRequire } from "node:module";
21import { dirname, join, resolve } from "node:path";
21import { $ } from "bun";22import { $ } from "bun";
22 23
23const ROOT = resolve(import.meta.dir, "..");24const ROOT = resolve(import.meta.dir, "..");
@@ -42,6 +43,23 @@ const targetIsWindows = BUN_TARGET.includes("windows") || process.platform === " · 18 unmodified lines
42const binaryName = targetIsWindows ? "executor-sidecar.exe" : "executor-sidecar";43const binaryName = targetIsWindows ? "executor-sidecar.exe" : "executor-sidecar";
43const sidecarBinary = resolve(SIDECAR_OUT_DIR, binaryName);44const sidecarBinary = resolve(SIDECAR_OUT_DIR, binaryName);
44 45
46// QuickJS ships its WASM as a side asset; `bun build --compile` can't pull
47// it into bunfs, so we stage it next to the binary and the sidecar entry
48// preloads it via `setQuickJSModule` before any server import.
49const resolveQuickJsWasmPath = (): string => {
50 const req = createRequire(join(REPO_ROOT, "packages/kernel/runtime-quickjs/package.json"));
51 const quickJsPkg = req.resolve("quickjs-emscripten/package.json");
52 const wasmPath = resolve(
53 dirname(quickJsPkg),
54 "../@jitl/quickjs-wasmfile-release-sync/dist/emscripten-module.wasm",
55 );
56 if (!existsSync(wasmPath)) {
57 // oxlint-disable-next-line executor/no-try-catch-or-throw, executor/no-error-constructor -- boundary: build-time fatal
58 throw new Error(`QuickJS WASM not found at ${wasmPath}`);
59 }
60 return wasmPath;
61};
62
45const createEmbeddedMigrationsSource = async (): Promise<string> => {63const createEmbeddedMigrationsSource = async (): Promise<string> => {
46 const migrationsDir = resolve(APPS_LOCAL, "drizzle");64 const migrationsDir = resolve(APPS_LOCAL, "drizzle");
47 const files = (await Array.fromAsync(new Bun.Glob("**/*").scan({ cwd: migrationsDir })))65 const files = (await Array.fromAsync(new Bun.Glob("**/*").scan({ cwd: migrationsDir })))
@@ -92,6 +110,9 @@ try { · 44 unmodified lines
92 await writeFile(EMBEDDED_MIGRATIONS_PATH, EMBEDDED_MIGRATIONS_STUB);110 await writeFile(EMBEDDED_MIGRATIONS_PATH, EMBEDDED_MIGRATIONS_STUB);
93}111}
94 112
113console.log(`[build-sidecar] staging QuickJS WASM → ${SIDECAR_OUT_DIR}`);
114await cp(resolveQuickJsWasmPath(), join(SIDECAR_OUT_DIR, "emscripten-module.wasm"));
115
95console.log(`[build-sidecar] staging web UI → ${WEB_UI_OUT_DIR}`);116console.log(`[build-sidecar] staging web UI → ${WEB_UI_OUT_DIR}`);
96await cp(APPS_LOCAL_DIST, WEB_UI_OUT_DIR, { recursive: true });117await cp(APPS_LOCAL_DIST, WEB_UI_OUT_DIR, { recursive: true });
97 118
apps/desktop/src/sidecar/server.ts+30−0
@@ -7,6 +7,36 @@ · 6 unmodified lines
7 * announces readiness with the resolved port on stdout so the Electron7 * announces readiness with the resolved port on stdout so the Electron
8 * main process can attach a BrowserWindow to it.8 * main process can attach a BrowserWindow to it.
9 */9 */
10import { dirname, join } from "node:path";
11
12// Pre-load QuickJS WASM for compiled binaries. `bun build --compile` can't
13// embed the side-asset WASM that `quickjs-emscripten` ships with, so
14// build-sidecar.ts stages it next to this binary and we feed the bytes in
15// via `setQuickJSModule` before any server import touches QuickJS. Mirrors
16// the CLI's preload in apps/cli/src/main.ts.
17const wasmOnDisk = join(dirname(process.execPath), "emscripten-module.wasm");
18if (typeof Bun !== "undefined" && (await Bun.file(wasmOnDisk).exists())) {
19 const { setQuickJSModule } = await import("@executor-js/runtime-quickjs");
20 const { newQuickJSWASMModule } = await import("quickjs-emscripten");
21 type QuickJSSyncVariant = import("quickjs-emscripten").QuickJSSyncVariant;
22 const wasmBinary = await Bun.file(wasmOnDisk).arrayBuffer();
23 const importFFI: QuickJSSyncVariant["importFFI"] = () =>
24 import("@jitl/quickjs-wasmfile-release-sync/ffi").then((m) => m.QuickJSFFI);
25 const importModuleLoader: QuickJSSyncVariant["importModuleLoader"] = async () => {
26 const { default: original } = await import(
27 "@jitl/quickjs-wasmfile-release-sync/emscripten-module"
28 );
29 return (moduleArg = {}) => original({ ...moduleArg, wasmBinary });
30 };
31 const variant: QuickJSSyncVariant = {
32 type: "sync" as const,
33 importFFI,
34 importModuleLoader,
35 };
36 const mod = await newQuickJSWASMModule(variant);
37 setQuickJSModule(mod);
38}
39
10import { startServer } from "@executor-js/local";40import { startServer } from "@executor-js/local";
11 41
12const requestedPort = parseInt(process.env.EXECUTOR_PORT ?? "0", 10);42const requestedPort = parseInt(process.env.EXECUTOR_PORT ?? "0", 10);
bun.lock+3−0
@@ -136,11 +136,14 @@ · 135 unmodified lines
136 "@executor-js/plugin-mcp": "workspace:*",136 "@executor-js/plugin-mcp": "workspace:*",
137 "@executor-js/plugin-onepassword": "workspace:*",137 "@executor-js/plugin-onepassword": "workspace:*",
138 "@executor-js/plugin-openapi": "workspace:*",138 "@executor-js/plugin-openapi": "workspace:*",
139 "@executor-js/runtime-quickjs": "workspace:*",
140 "@jitl/quickjs-wasmfile-release-sync": "catalog:",
139 "@types/node": "catalog:",141 "@types/node": "catalog:",
140 "bun-types": "catalog:",142 "bun-types": "catalog:",
141 "electron": "41.2.1",143 "electron": "41.2.1",
142 "electron-builder": "^26",144 "electron-builder": "^26",
143 "electron-vite": "^5",145 "electron-vite": "^5",
146 "quickjs-emscripten": "catalog:",
144 "typescript": "catalog:",147 "typescript": "catalog:",
145 "vite": "catalog:",148 "vite": "catalog:",
146 },149 },