Brendan Allan vor 3 Monaten
Ursprung
Commit
36d8973925

+ 1 - 0
bun.lock

@@ -265,6 +265,7 @@
       "name": "@opencode-ai/desktop-electron",
       "name": "@opencode-ai/desktop-electron",
       "version": "1.14.34",
       "version": "1.14.34",
       "dependencies": {
       "dependencies": {
+        "@effect/platform-node": "catalog:",
         "drizzle-orm": "catalog:",
         "drizzle-orm": "catalog:",
         "effect": "catalog:",
         "effect": "catalog:",
         "electron-context-menu": "4.1.2",
         "electron-context-menu": "4.1.2",

+ 1 - 0
packages/desktop-electron/package.json

@@ -25,6 +25,7 @@
   "main": "./out/main/index.js",
   "main": "./out/main/index.js",
   "dependencies": {
   "dependencies": {
     "effect": "catalog:",
     "effect": "catalog:",
+    "@effect/platform-node": "catalog:",
     "electron-context-menu": "4.1.2",
     "electron-context-menu": "4.1.2",
     "electron-log": "^5",
     "electron-log": "^5",
     "electron-store": "^10",
     "electron-store": "^10",

+ 7 - 5
packages/desktop-electron/src/main/index.ts

@@ -4,6 +4,8 @@ import { createServer } from "node:net"
 import { homedir } from "node:os"
 import { homedir } from "node:os"
 import { join } from "node:path"
 import { join } from "node:path"
 import { app, BrowserWindow, dialog } from "electron"
 import { app, BrowserWindow, dialog } from "electron"
+import * as NodeRuntime from "@effect/platform-node/NodeRuntime"
+import * as NodeHttpClient from "@effect/platform-node/NodeHttpClient"
 import pkg from "electron-updater"
 import pkg from "electron-updater"
 import { Data, Deferred, Effect, Fiber, Option, PubSub, Queue, Ref, Stream, SubscriptionRef } from "effect"
 import { Data, Deferred, Effect, Fiber, Option, PubSub, Queue, Ref, Stream, SubscriptionRef } from "effect"
 
 
@@ -312,7 +314,7 @@ const main = Effect.gen(function* () {
   ),
   ),
 )
 )
 
 
-void Effect.runPromise(main)
+main.pipe(Effect.provide(NodeHttpClient.layerFetch), NodeRuntime.runMain())
 
 
 const wireMenu = (win: BrowserWindow) => {
 const wireMenu = (win: BrowserWindow) => {
   createMenu({
   createMenu({
@@ -364,10 +366,10 @@ const registerIpcHandlersImpl = () =>
           Effect.tap(() => Deferred.succeed(deepLinksConsumed, undefined)),
           Effect.tap(() => Deferred.succeed(deepLinksConsumed, undefined)),
         ),
         ),
       ),
       ),
-    getDefaultServerUrl: () => Effect.runPromise(getDefaultServerUrl),
-    setDefaultServerUrl: (url) => Effect.runPromise(setDefaultServerUrl(url)),
-    getWslConfig: () => Effect.runPromise(getWslConfig),
-    setWslConfig: (config: WslConfig) => Effect.runPromise(setWslConfig(config)),
+    getDefaultServerUrl: () => getDefaultServerUrl(),
+    setDefaultServerUrl: (url) => setDefaultServerUrl(url),
+    getWslConfig: () => Promise.resolve(getWslConfig()),
+    setWslConfig: (config: WslConfig) => setWslConfig(config),
     getDisplayBackend: () => Promise.resolve(null),
     getDisplayBackend: () => Promise.resolve(null),
     setDisplayBackend: () => Promise.resolve(undefined),
     setDisplayBackend: () => Promise.resolve(undefined),
     parseMarkdown: (markdown) => Promise.resolve(parseMarkdown(markdown)),
     parseMarkdown: (markdown) => Promise.resolve(parseMarkdown(markdown)),

+ 67 - 69
packages/desktop-electron/src/main/server.ts

@@ -1,81 +1,83 @@
 import { app } from "electron"
 import { app } from "electron"
-import { Effect } from "effect"
+import { Effect, Option } from "effect"
 import { DEFAULT_SERVER_URL_KEY, WSL_ENABLED_KEY } from "./constants"
 import { DEFAULT_SERVER_URL_KEY, WSL_ENABLED_KEY } from "./constants"
 import { getUserShell, loadShellEnv } from "./shell-env"
 import { getUserShell, loadShellEnv } from "./shell-env"
 import { getStore } from "./store"
 import { getStore } from "./store"
+import { HttpClient, HttpClientResponse } from "effect/unstable/http"
 
 
 export type WslConfig = { enabled: boolean }
 export type WslConfig = { enabled: boolean }
 
 
-export const getDefaultServerUrl = Effect.sync((): string | null => {
+export const getDefaultServerUrl = (): string | null => {
   const value = getStore().get(DEFAULT_SERVER_URL_KEY)
   const value = getStore().get(DEFAULT_SERVER_URL_KEY)
   return typeof value === "string" ? value : null
   return typeof value === "string" ? value : null
-})
+}
 
 
-export const setDefaultServerUrl = (url: string | null) =>
-  Effect.sync(() => {
-    if (url) {
-      getStore().set(DEFAULT_SERVER_URL_KEY, url)
-      return
-    }
-    getStore().delete(DEFAULT_SERVER_URL_KEY)
-  })
+export const setDefaultServerUrl = (url: string | null) => {
+  if (url) {
+    getStore().set(DEFAULT_SERVER_URL_KEY, url)
+    return
+  }
+  getStore().delete(DEFAULT_SERVER_URL_KEY)
+}
 
 
-export const getWslConfig = Effect.sync((): WslConfig => {
+export const getWslConfig = (): WslConfig => {
   const value = getStore().get(WSL_ENABLED_KEY)
   const value = getStore().get(WSL_ENABLED_KEY)
   return { enabled: typeof value === "boolean" ? value : false }
   return { enabled: typeof value === "boolean" ? value : false }
-})
-
-export const setWslConfig = (config: WslConfig) =>
-  Effect.sync(() => getStore().set(WSL_ENABLED_KEY, config.enabled))
-
-export const spawnLocalServerEffect = Effect.fn("Server.spawnLocalServer")(
-  function* (hostname: string, port: number, password: string) {
-    yield* prepareServerEnv(password)
-    const { Log, Server } = yield* Effect.promise(() =>
-      import("virtual:opencode-server") as Promise<typeof import("virtual:opencode-server")>,
-    )
-    yield* Effect.promise(() => Log.init({ level: "WARN" }))
-    const listener = yield* Effect.promise(() =>
-      Server.listen({
-        port,
-        hostname,
-        username: "opencode",
-        password,
-        cors: ["oc://renderer"],
-      }),
-    )
+}
 
 
-    const healthCheck = Effect.gen(function* () {
-      const url = `http://${hostname}:${port}`
-      while (true) {
-        const healthy = yield* checkHealthEffect(url, password)
-        if (healthy) return
-        yield* Effect.sleep("100 millis")
-      }
-    })
+export const setWslConfig = (config: WslConfig) => getStore().set(WSL_ENABLED_KEY, config.enabled)
 
 
-    return { listener, health: healthCheck }
-  },
-)
+export const spawnLocalServerEffect = Effect.fn("Server.spawnLocalServer")(function* (
+  hostname: string,
+  port: number,
+  password: string,
+) {
+  prepareServerEnv(password)
+  const { Log, Server } = yield* Effect.promise(
+    () => import("virtual:opencode-server") as Promise<typeof import("virtual:opencode-server")>,
+  )
+  yield* Effect.promise(() => Log.init({ level: "WARN" }))
+  const listener = yield* Effect.promise(() =>
+    Server.listen({
+      port,
+      hostname,
+      username: "opencode",
+      password,
+      cors: ["oc://renderer"],
+    }),
+  )
 
 
-const prepareServerEnv = (password: string) =>
-  Effect.sync(() => {
-    const shell = process.platform === "win32" ? null : getUserShell()
-    const shellEnv = shell ? (loadShellEnv(shell) ?? {}) : {}
-    const env = {
-      ...process.env,
-      ...shellEnv,
-      OPENCODE_EXPERIMENTAL_ICON_DISCOVERY: "true",
-      OPENCODE_EXPERIMENTAL_FILEWATCHER: "true",
-      OPENCODE_CLIENT: "desktop",
-      OPENCODE_SERVER_USERNAME: "opencode",
-      OPENCODE_SERVER_PASSWORD: password,
-      XDG_STATE_HOME: app.getPath("userData"),
+  const healthCheck = Effect.gen(function* () {
+    const url = `http://${hostname}:${port}`
+    while (true) {
+      const healthy = yield* checkHealthEffect(url, password)
+      if (healthy) return
+      yield* Effect.sleep("100 millis")
     }
     }
-    Object.assign(process.env, env)
   })
   })
 
 
+  return { listener, health: healthCheck }
+})
+
+const prepareServerEnv = (password: string) => () => {
+  const shell = process.platform === "win32" ? null : getUserShell()
+  const shellEnv = shell ? (loadShellEnv(shell) ?? {}) : {}
+  const env = {
+    ...process.env,
+    ...shellEnv,
+    OPENCODE_EXPERIMENTAL_ICON_DISCOVERY: "true",
+    OPENCODE_EXPERIMENTAL_FILEWATCHER: "true",
+    OPENCODE_CLIENT: "desktop",
+    OPENCODE_SERVER_USERNAME: "opencode",
+    OPENCODE_SERVER_PASSWORD: password,
+    XDG_STATE_HOME: app.getPath("userData"),
+  }
+  Object.assign(process.env, env)
+}
+
 export const checkHealthEffect = Effect.fn("Server.checkHealth")(function* (url: string, password?: string | null) {
 export const checkHealthEffect = Effect.fn("Server.checkHealth")(function* (url: string, password?: string | null) {
+  const httpClient = yield* HttpClient.HttpClient
+
   let healthUrl: URL
   let healthUrl: URL
   try {
   try {
     healthUrl = new URL("/global/health", url)
     healthUrl = new URL("/global/health", url)
@@ -89,16 +91,12 @@ export const checkHealthEffect = Effect.fn("Server.checkHealth")(function* (url:
     headers.set("authorization", `Basic ${auth}`)
     headers.set("authorization", `Basic ${auth}`)
   }
   }
 
 
-  try {
-    const res = yield* Effect.promise(() =>
-      fetch(healthUrl, {
-        method: "GET",
-        headers,
-        signal: AbortSignal.timeout(3000),
-      }),
+  return yield* httpClient
+    .get(healthUrl, { headers })
+    .pipe(
+      Effect.timeout("3 seconds"),
+      Effect.flatMap(HttpClientResponse.filterStatusOk),
+      Effect.option,
+      Effect.map(Option.isSome),
     )
     )
-    return res.ok
-  } catch {
-    return false
-  }
 })
 })