From 633454f4c974855546933a4357bf48ffa7a5323a Mon Sep 17 00:00:00 2001 From: Brendan Allan Date: Tue, 21 Apr 2026 13:31:45 +0800 Subject: [PATCH 1/2] feat(desktop-electron): add CORS headers to main window webRequest --- packages/desktop-electron/src/main/windows.ts | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/packages/desktop-electron/src/main/windows.ts b/packages/desktop-electron/src/main/windows.ts index df55e8da2f88..337e1ca0bcc4 100644 --- a/packages/desktop-electron/src/main/windows.ts +++ b/packages/desktop-electron/src/main/windows.ts @@ -100,6 +100,19 @@ export function createMainWindow() { }, }) + win.webContents.session.webRequest.onBeforeSendHeaders((details, callback) => { + const { requestHeaders } = details + upsertKeyValue(requestHeaders, "Access-Control-Allow-Origin", ["*"]) + callback({ requestHeaders }) + }) + + win.webContents.session.webRequest.onHeadersReceived((details, callback) => { + const { responseHeaders = {} } = details + upsertKeyValue(responseHeaders, "Access-Control-Allow-Origin", ["*"]) + upsertKeyValue(responseHeaders, "Access-Control-Allow-Headers", ["*"]) + callback({ responseHeaders }) + }) + state.manage(win) loadWindow(win, "index.html") wireZoom(win) @@ -177,3 +190,17 @@ function wireZoom(win: BrowserWindow) { win.webContents.setZoomFactor(1) }) } + +function upsertKeyValue(obj: Record, keyToChange: string, value: any) { + const keyToChangeLower = keyToChange.toLowerCase() + for (const key of Object.keys(obj)) { + if (key.toLowerCase() === keyToChangeLower) { + // Reassign old key + obj[key] = value + // Done + return + } + } + // Insert at end instead + obj[keyToChange] = value +} From 3925181348edf8a8e788a1a4264115436f1ec2b6 Mon Sep 17 00:00:00 2001 From: Brendan Allan Date: Tue, 21 Apr 2026 14:09:03 +0800 Subject: [PATCH 2/2] fix(app): properly wrap produce calls in setProjects The setProjects function was expecting a function that modifies a draft in-place, but the callers were passing functions that return void. This fix ensures that produce() is called at the call sites and the result is passed to setProjects. --- packages/app/src/context/global-sync.tsx | 11 +++-------- .../src/context/global-sync/event-reducer.ts | 18 +++++++++++------- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/packages/app/src/context/global-sync.tsx b/packages/app/src/context/global-sync.tsx index 4ced2b939d5c..313ff2965954 100644 --- a/packages/app/src/context/global-sync.tsx +++ b/packages/app/src/context/global-sync.tsx @@ -10,7 +10,7 @@ import type { import { showToast } from "@opencode-ai/ui/toast" import { getFilename } from "@opencode-ai/shared/util/path" import { batch, createContext, getOwner, onCleanup, onMount, type ParentProps, untrack, useContext } from "solid-js" -import { createStore, produce, reconcile } from "solid-js/store" +import { createStore, produce, reconcile, unwrap } from "solid-js/store" import { useLanguage } from "@/context/language" import { Persist, persisted } from "@/utils/persist" import type { InitError } from "../pages/error" @@ -95,13 +95,8 @@ function createGlobalSync() { ) } - const setProjects = (next: Project[] | ((draft: Project[]) => void)) => { + const setProjects = (next: Project[] | ((draft: Project[]) => Project[])) => { projectWritten = true - if (typeof next === "function") { - setGlobalStore("project", produce(next)) - cacheProjects() - return - } setGlobalStore("project", next) cacheProjects() } @@ -116,7 +111,7 @@ function createGlobalSync() { const set = ((...input: unknown[]) => { if (input[0] === "project" && (Array.isArray(input[1]) || typeof input[1] === "function")) { - setProjects(input[1] as Project[] | ((draft: Project[]) => void)) + setProjects(input[1] as Project[] | ((draft: Project[]) => Project[])) return input[1] } return (setGlobalStore as (...args: unknown[]) => unknown)(...input) diff --git a/packages/app/src/context/global-sync/event-reducer.ts b/packages/app/src/context/global-sync/event-reducer.ts index 11a0cf83fd1d..82408fdfe9e5 100644 --- a/packages/app/src/context/global-sync/event-reducer.ts +++ b/packages/app/src/context/global-sync/event-reducer.ts @@ -21,7 +21,7 @@ const SKIP_PARTS = new Set(["patch", "step-start", "step-finish"]) export function applyGlobalEvent(input: { event: { type: string; properties?: unknown } project: Project[] - setGlobalProject: (next: Project[] | ((draft: Project[]) => void)) => void + setGlobalProject: (next: Project[] | ((draft: Project[]) => Project[])) => void refresh: () => void }) { if (input.event.type === "global.disposed" || input.event.type === "server.connected") { @@ -33,14 +33,18 @@ export function applyGlobalEvent(input: { const properties = input.event.properties as Project const result = Binary.search(input.project, properties.id, (s) => s.id) if (result.found) { - input.setGlobalProject((draft) => { - draft[result.index] = { ...draft[result.index], ...properties } - }) + input.setGlobalProject( + produce((draft) => { + draft[result.index] = { ...draft[result.index], ...properties } + }), + ) return } - input.setGlobalProject((draft) => { - draft.splice(result.index, 0, properties) - }) + input.setGlobalProject( + produce((draft) => { + draft.splice(result.index, 0, properties) + }), + ) } function cleanupSessionCaches(