diff --git a/apps/server/src/preview/Manager.test.ts b/apps/server/src/preview/Manager.test.ts index 1c16992123206..82f62ff9061e6 100644 --- a/apps/server/src/preview/Manager.test.ts +++ b/apps/server/src/preview/Manager.test.ts @@ -85,6 +85,8 @@ it.layer(layer)("PreviewManager", (it) => { const manager = yield* PreviewManager.PreviewManager; const collector = yield* collectEvents; const opened = yield* manager.open({ threadId, runtime: "server", reveal: false }); + // The test server has no desktop browser channel. + expect(opened.headless).toBe(true); yield* manager.requestReveal({ threadId, tabId: opened.tabId, force: true }); const first = (yield* manager.list({ threadId })).sessions[0]; expect(first?.reveal).toBe(true); diff --git a/apps/server/src/preview/Manager.ts b/apps/server/src/preview/Manager.ts index ff8442306f2ff..f38d8d01538e8 100644 --- a/apps/server/src/preview/Manager.ts +++ b/apps/server/src/preview/Manager.ts @@ -29,6 +29,8 @@ import * as Stream from "effect/Stream"; import * as SynchronizedRef from "effect/SynchronizedRef"; import { PreviewControlRequiredError } from "@t3tools/contracts"; +import * as ServerConfig from "../config.ts"; + export class PreviewManager extends Context.Service< PreviewManager, { @@ -124,6 +126,10 @@ const currentIsoTimestamp = DateTime.now.pipe(Effect.map(DateTime.formatIso)); /** @public */ export const make = Effect.gen(function* PreviewManagerMake() { const random = yield* SecureRandom.SecureRandom; + const config = yield* ServerConfig.ServerConfig; + // Without a desktop browser channel no desktop can draw server tabs, so publish them headless. + const serverTabsHeadless = + config.desktopBrowserFd === undefined || config.desktopBrowserControlFd === undefined; const serverEpoch = yield* random.uuidV4(); const stateRef = yield* SynchronizedRef.make(initialState); const eventsPubSub = yield* PubSub.unbounded(); @@ -197,6 +203,7 @@ export const make = Effect.gen(function* PreviewManagerMake() { ? { automationOwner: input.automationOwner } : {}), ...(input.reveal === undefined ? {} : { reveal: input.reveal }), + ...(runtime === "server" && serverTabsHeadless ? { headless: true } : {}), updatedAt, }; yield* SynchronizedRef.modifyEffect(stateRef, (state) => diff --git a/apps/server/src/preview/ServerBrowser.test.ts b/apps/server/src/preview/ServerBrowser.test.ts index c32947b30b995..a97f3583705f0 100644 --- a/apps/server/src/preview/ServerBrowser.test.ts +++ b/apps/server/src/preview/ServerBrowser.test.ts @@ -420,7 +420,17 @@ const dependencies = Layer.mergeAll( }), PreviewAgentActivity.layer, ).pipe( - Layer.provideMerge(ServerConfig.layerTest(process.cwd(), { prefix: "t3-server-browser-" })), + Layer.provideMerge( + // The mock channel stands in for a desktop that passed its browser fds. + Layer.effect( + ServerConfig.ServerConfig, + Effect.map(ServerConfig.ServerConfig, (config) => ({ + ...config, + desktopBrowserFd: 3, + desktopBrowserControlFd: 4, + })), + ).pipe(Layer.provide(ServerConfig.layerTest(process.cwd(), { prefix: "t3-server-browser-" }))), + ), Layer.provideMerge(Layer.mergeAll(NodeServices.layer, NodeCrypto.layer)), ); const layer = ServerBrowser.layer.pipe(Layer.provideMerge(dependencies));