fix(desktop): stabilize bundled dev and process exit (#46523)

This commit is contained in:
Luke Parker 2026-09-02 18:26:57 +10:00 committed by GitHub
parent 1bd1f72bcf
commit 48c8a308b9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 98 additions and 10 deletions

View file

@ -418,7 +418,7 @@
"electron-window-state": "^5.0.3",
},
"devDependencies": {
"@brendonovich/vite-plugin-opencode": "0.1.1",
"@brendonovich/vite-plugin-opencode": "0.1.3",
"@effect/platform-node": "catalog:",
"@lydell/node-pty": "catalog:",
"@opencode-ai/app": "workspace:*",
@ -1050,6 +1050,7 @@
"@npmcli/agent@4.0.2": "patches/@npmcli%2Fagent@4.0.2.patch",
"@silvia-odwyer/photon-node@0.3.4": "patches/@silvia-odwyer%2Fphoton-node@0.3.4.patch",
"solid-js@1.9.15": "patches/solid-js@1.9.15.patch",
"vite@8.2.2": "patches/vite@8.2.2.patch",
"@ff-labs/fff-bun@0.10.5": "patches/@ff-labs%2Ffff-bun@0.10.5.patch",
"@ai-sdk/google@3.0.73": "patches/@ai-sdk%2Fgoogle@3.0.73.patch",
"@dnd-kit/dom@0.5.0": "patches/@dnd-kit%2Fdom@0.5.0.patch",
@ -1596,7 +1597,7 @@
"@braintree/sanitize-url": ["@braintree/sanitize-url@7.1.2", "", {}, "sha512-jigsZK+sMF/cuiB7sERuo9V7N9jx+dhmHHnQyDSVdpZwVutaBu7WvNYqMDLSgFgfB30n452TP3vjDAvFC973mA=="],
"@brendonovich/vite-plugin-opencode": ["@brendonovich/vite-plugin-opencode@0.1.1", "", { "dependencies": { "@babel/core": "^7.29.0", "@opencode-ai/client": "0.0.0-beta-18050" }, "peerDependencies": { "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-aPG0ct8ctxAqndbNOx7NW0GhU6QY6sOUfi/DaKqH9c5WdxICSsUop6uSkJwPDHP9WpN9eg0dd2D2qwYpG6UdHw=="],
"@brendonovich/vite-plugin-opencode": ["@brendonovich/vite-plugin-opencode@0.1.3", "", { "dependencies": { "@babel/core": "^7.29.0", "@opencode-ai/client": "0.0.0-beta-18050" }, "peerDependencies": { "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-iiIwlNoycOMUiaUzL1ZirLapCG3WY8cg9hSj4KgE/JYIWgqauoHikIlfUt7SdMqqGIkb4iZu6I1MxgDDJaRfBA=="],
"@bruits/satteri-darwin-arm64": ["@bruits/satteri-darwin-arm64@0.9.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-iw4nZgx9v30lWo/MTngQqi1pI78KI0DnkSm+lVJGYdmPLgAyDNJigVhpG42/Iq55A6c1Ll8q66ljyyRiQUxwow=="],

View file

@ -178,6 +178,7 @@
"@pierre/trees@1.0.0-beta.4": "patches/@pierre%2Ftrees@1.0.0-beta.4.patch",
"@modelcontextprotocol/sdk@1.29.0": "patches/@modelcontextprotocol%2Fsdk@1.29.0.patch",
"@tanstack/virtual-core@3.17.8": "patches/@tanstack%2Fvirtual-core@3.17.8.patch",
"@ff-labs/fff-bun@0.10.5": "patches/@ff-labs%2Ffff-bun@0.10.5.patch"
"@ff-labs/fff-bun@0.10.5": "patches/@ff-labs%2Ffff-bun@0.10.5.patch",
"vite@8.2.2": "patches/vite@8.2.2.patch"
}
}

View file

@ -91,7 +91,7 @@ const require = __cjs_mod__.createRequire(import.meta.url);
"import.meta.env.OPENCODE_VERSION": JSON.stringify(process.env.OPENCODE_VERSION),
"import.meta.env.VITE_OPENCODE_CHANNEL": JSON.stringify(channel),
},
plugins: [{ ...picker, transformIndexHtml: undefined }, appPlugin, sentry],
plugins: [picker, appPlugin, sentry],
publicDir: "../../../app/public",
root: "src/renderer",
build: {

View file

@ -31,7 +31,7 @@
"electron-window-state": "^5.0.3"
},
"devDependencies": {
"@brendonovich/vite-plugin-opencode": "0.1.1",
"@brendonovich/vite-plugin-opencode": "0.1.3",
"@effect/platform-node": "catalog:",
"@lydell/node-pty": "catalog:",
"@opencode-ai/app": "workspace:*",

View file

@ -1,5 +1,6 @@
import { $ } from "bun"
import { join } from "node:path"
import { fileURLToPath } from "node:url"
import { downloadCliToResources, windowsify } from "./utils"
type ServerSource = { type: "build" } | { type: "download"; version: string }
@ -43,13 +44,18 @@ async function prepareServer(source: ServerSource) {
if (source.type === "download")
return downloadCliToResources(source.version, windowsify("resources/opencode-cli-dev"))
process.env.OPENCODE_DESKTOP_CLI_DEV = join(import.meta.dirname, "../../cli")
await $`bun run --cwd ${process.env.OPENCODE_DESKTOP_CLI_DEV} --define=OPENCODE_VERSION=${JSON.stringify(process.env.OPENCODE_VERSION)} src/index.ts --version`
if (process.platform !== "win32") return
process.env.OPENCODE_DESKTOP_WSL_CLI_BUILD = join(import.meta.dirname, "../../cli/script/build.ts")
process.env.OPENCODE_DESKTOP_WSL_CLI_OUTPUT = join(import.meta.dirname, "../resources/opencode-cli-wsl")
}
async function startDesktop(args: string[]) {
await $`electron-vite dev ${args}`
// Bun's implicit spawn environment omits values set during preparation.
process.exitCode = await Bun.spawn(
["node", fileURLToPath(new URL("../bin/electron-vite.js", import.meta.resolve("electron-vite"))), "dev", ...args],
{ env: process.env, stdio: ["inherit", "inherit", "inherit"] },
).exited
}
await main()

View file

@ -1,5 +1,3 @@
declare module "virtual:vite-opencode-picker/client"
interface ImportMetaEnv {
readonly OPENCODE_CHANNEL: string
readonly OPENCODE_VERSION?: string

View file

@ -20,5 +20,3 @@ startDesktopMenu(api)
startDeepLinks(api)
render(() => <DesktopApp api={api} updater={updater} version={version} />, root)
if (import.meta.env.DEV) void import("virtual:vite-opencode-picker/client")

84
patches/vite@8.2.2.patch Normal file
View file

@ -0,0 +1,84 @@
diff --git a/dist/client/bundledDevClient.mjs b/dist/client/bundledDevClient.mjs
index 4c4a3f6f884795001ffbae43cd3c3d6bf84169dc..b5e80b68140824e7460439b125eec8ec3fa14e8a 100644
--- a/dist/client/bundledDevClient.mjs
+++ b/dist/client/bundledDevClient.mjs
@@ -1796,6 +1796,13 @@ function injectQuery(url, queryToInject) {
if (typeof DevRuntime !== "undefined") {
var _ref;
class ViteDevRuntime extends DevRuntime {
+ payloadDelivered(filename) {
+ transport.send({
+ type: "custom",
+ event: "vite:bundled-dev:payload-delivered",
+ data: { filename }
+ });
+ }
createModuleHotContext(moduleId) {
const ctx = new BundledDevHMRContext(bundledDevHmrClient, moduleId);
ctx._internal = {
diff --git a/dist/node/chunks/node.js b/dist/node/chunks/node.js
index 87cfa480c3d4132f9dd1b74c6c1bbe2b90385d3f..83da417e7b9c380d06b7679d3fbfaeed998f5277 100644
--- a/dist/node/chunks/node.js
+++ b/dist/node/chunks/node.js
@@ -26309,7 +26309,6 @@ function memoryFilesMiddleware(server) {
const mime = lookup(filePath);
if (mime) res.setHeader("Content-Type", mime);
for (const name in headers) res.setHeader(name, headers[name]);
- res.on("finish", () => bundledDev.markPayloadDelivered(filePath));
return res.end(file.source);
}
next();
@@ -26339,7 +26338,6 @@ function triggerLazyBundlingMiddleware(server) {
}
if (result == null) return next();
res.setHeader("Content-Type", "application/javascript");
- res.on("finish", () => bundledDev.markPayloadDelivered(result.filename));
return res.end(result.code);
};
}
@@ -35155,6 +35153,9 @@ var BundledDev = class {
this.clients.setupIfNeeded(client, payload.clientId);
this.devEngine.registerClient(payload.clientId);
});
+ this.environment.hot.on("vite:bundled-dev:payload-delivered", (payload) => {
+ this.markPayloadDelivered(payload.filename);
+ });
this.environment.hot.on("vite:client:connect", (_payload, client) => {
if (this.lastBuildError) {
debug$1?.("REPLAY: replaying last build error to newly connected client");
@@ -35287,12 +35288,11 @@ var BundledDev = class {
debug$1?.(`TRIGGER-LAZY: trigger lazy bundling for module ${moduleId} for client ${clientId}`);
const result = await this.devEngine.compileEntry(moduleId, clientId);
this.pendingPayloadFilenames.add(result.filename);
- return result;
+ return { filename: result.filename, code: result.code + payloadDeliveredAck(result.filename) };
}
/**
- * Called by the serving middlewares when the response for a payload completed.
- * Only delivered payloads are recorded on the server's per-client ship map, so
- * later chunks may omit a module only if the payload carrying it was delivered.
+ * Called after the client evaluates a payload and registers its factories.
+ * HTTP response completion alone does not mean the factories are available.
*
* Note: the payload filename is unique across all clients.
*/
@@ -35364,7 +35364,7 @@ var BundledDev = class {
code: typeof hmrOutput.code === "string" ? "[code]" : hmrOutput.code
});
this.pendingPayloadFilenames.add(hmrOutput.filename);
- this.memoryFiles.set(hmrOutput.filename, { source: hmrOutput.code + "\n; export {}" });
+ this.memoryFiles.set(hmrOutput.filename, { source: hmrOutput.code + payloadDeliveredAck(hmrOutput.filename) + "\n; export {}" });
if (hmrOutput.sourcemapFilename && hmrOutput.sourcemap) this.memoryFiles.set(hmrOutput.sourcemapFilename, { source: hmrOutput.sourcemap });
client.send({
type: "bundled-dev-update",
@@ -35407,6 +35407,10 @@ var Clients = class {
}
}
};
+// Backport https://github.com/vitejs/vite/pull/23373.
+function payloadDeliveredAck(filename) {
+ return `\n;__rolldown_runtime__.payloadDelivered(${JSON.stringify(filename)});`;
+}
function debounce(time, cb) {
let timer;
return () => {