diff --git a/apps/desktop/src/main.ts b/apps/desktop/src/main.ts index 460684929..4d41cf892 100644 --- a/apps/desktop/src/main.ts +++ b/apps/desktop/src/main.ts @@ -47,6 +47,7 @@ import { isArm64HostRunningIntelBuild, resolveDesktopRuntimeInfo } from "./runti fixPath(); const PICK_FOLDER_CHANNEL = "desktop:pick-folder"; +const GET_WS_URL_CHANNEL = "desktop:get-ws-url"; const CONFIRM_CHANNEL = "desktop:confirm"; const SET_THEME_CHANNEL = "desktop:set-theme"; const CONTEXT_MENU_CHANNEL = "desktop:context-menu"; @@ -1086,6 +1087,11 @@ function registerIpcHandlers(): void { return result.filePaths[0] ?? null; }); + ipcMain.removeAllListeners(GET_WS_URL_CHANNEL); + ipcMain.on(GET_WS_URL_CHANNEL, (event) => { + event.returnValue = backendWsUrl || null; + }); + ipcMain.removeHandler(CONFIRM_CHANNEL); ipcMain.handle(CONFIRM_CHANNEL, async (_event, message: unknown) => { if (typeof message !== "string") { diff --git a/apps/desktop/src/preload.ts b/apps/desktop/src/preload.ts index 1e1bb3bd8..4759ad51f 100644 --- a/apps/desktop/src/preload.ts +++ b/apps/desktop/src/preload.ts @@ -2,6 +2,7 @@ import { contextBridge, ipcRenderer } from "electron"; import type { DesktopBridge } from "@t3tools/contracts"; const PICK_FOLDER_CHANNEL = "desktop:pick-folder"; +const GET_WS_URL_CHANNEL = "desktop:get-ws-url"; const CONFIRM_CHANNEL = "desktop:confirm"; const SET_THEME_CHANNEL = "desktop:set-theme"; const CONTEXT_MENU_CHANNEL = "desktop:context-menu"; @@ -11,7 +12,15 @@ const UPDATE_STATE_CHANNEL = "desktop:update-state"; const UPDATE_GET_STATE_CHANNEL = "desktop:update-get-state"; const UPDATE_DOWNLOAD_CHANNEL = "desktop:update-download"; const UPDATE_INSTALL_CHANNEL = "desktop:update-install"; -const wsUrl = process.env.T3CODE_DESKTOP_WS_URL ?? null; +let wsUrl: string | null = null; +try { + const value = ipcRenderer.sendSync(GET_WS_URL_CHANNEL); + if (typeof value === "string" && value.length > 0) { + wsUrl = value; + } +} catch { + wsUrl = null; +} contextBridge.exposeInMainWorld("desktopBridge", { getWsUrl: () => wsUrl, diff --git a/apps/server/src/persistence/Layers/OrchestrationEventStore.ts b/apps/server/src/persistence/Layers/OrchestrationEventStore.ts index 4d81cf5e8..8122d8b32 100644 --- a/apps/server/src/persistence/Layers/OrchestrationEventStore.ts +++ b/apps/server/src/persistence/Layers/OrchestrationEventStore.ts @@ -13,7 +13,7 @@ import { } from "@t3tools/contracts"; import * as SqlClient from "effect/unstable/sql/SqlClient"; import * as SqlSchema from "effect/unstable/sql/SqlSchema"; -import { Effect, Layer, Schema, Stream } from "effect"; +import { Effect, Layer, Option, Schema, Stream } from "effect"; import { toPersistenceDecodeError, @@ -231,26 +231,31 @@ const makeEventStore = Effect.gen(function* () { Effect.flatMap((rows) => Effect.forEach(rows, (row) => decodeEvent(row).pipe( - Effect.mapError( - toPersistenceDecodeError("OrchestrationEventStore.readFromSequence:rowToEvent"), + Effect.map(Option.some), + Effect.catch((cause) => + Effect.logWarning( + `OrchestrationEventStore.readFromSequence: dropping event sequence=${row.sequence} due to decode error: ${String(cause)}`, + ).pipe(Effect.as(Option.none())), ), ), + ).pipe( + Effect.map((events) => ({ + events: events.flatMap((event) => (Option.isSome(event) ? [event.value] : [])), + lastSequence: rows.length > 0 ? rows[rows.length - 1]!.sequence : null, + })), ), ), ), ).pipe( - Stream.flatMap((events) => { - if (events.length === 0) { + Stream.flatMap(({ events, lastSequence }) => { + if (lastSequence === null) { return Stream.empty; } const nextRemaining = remaining - events.length; if (nextRemaining <= 0) { return Stream.fromIterable(events); } - return Stream.concat( - Stream.fromIterable(events), - readPage(events[events.length - 1]!.sequence, nextRemaining), - ); + return Stream.concat(Stream.fromIterable(events), readPage(lastSequence, nextRemaining)); }), ); diff --git a/apps/server/src/provider/Layers/ProviderSessionDirectory.ts b/apps/server/src/provider/Layers/ProviderSessionDirectory.ts index 38e097e1c..6d0eda835 100644 --- a/apps/server/src/provider/Layers/ProviderSessionDirectory.ts +++ b/apps/server/src/provider/Layers/ProviderSessionDirectory.ts @@ -72,6 +72,18 @@ const makeProviderSessionDirectory = Effect.gen(function* () { runtimePayload: value.runtimePayload, }), ), + Effect.catch(() => + // Unknown providers should not poison startup; drop the binding and move on. + repository + .deleteByThreadId({ threadId: value.threadId }) + .pipe( + Effect.mapError( + toPersistenceError("ProviderSessionDirectory.getBinding:cleanup"), + ), + Effect.ignore, + Effect.as(Option.none()), + ), + ), ), }), ),