authorgravatar for hello@timsmart.coTim Smart <hello@timsmart.co> 2026-03-09 12:18:33-07:00
committergravatar for jmarminge@gmail.comJulius Marminge <jmarminge@gmail.com> 2026-03-09 14:02:29-07:00
log074d3af79c3a19e7fc0d4c3ec2f3a6c6b6e46d55
treec22f1e0770058a68231fc21511398c57146737e3
parent6593645c8092696403f39e7d6eeebeec190385dc

cleanup

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

2 files changed, 26 insertions(+), 66 deletions(-)

apps/server/src/provider/Layers/ProviderHealth.ts+6-49
...@@ -13,7 +13,7 @@ import type {...@@ -13,7 +13,7 @@ import type {
13 ServerProviderStatus,13 ServerProviderStatus,
14 ServerProviderStatusState,14 ServerProviderStatusState,
15} from "@t3tools/contracts";15} from "@t3tools/contracts";
16import { Effect, Layer, Option, Result, Stream } from "effect";16import { Array, Effect, Fiber, Layer, Option, Result, Stream } from "effect";
17import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process";17import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process";
1818
19import {19import {
...@@ -312,56 +312,13 @@ export const checkCodexProviderStatus: Effect.Effect<...@@ -312,56 +312,13 @@ export const checkCodexProviderStatus: Effect.Effect<
312export const ProviderHealthLive = Layer.effect(312export const ProviderHealthLive = Layer.effect(
313 ProviderHealth,313 ProviderHealth,
314 Effect.gen(function* () {314 Effect.gen(function* () {
315 const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;315 const codexStatusFiber = yield* checkCodexProviderStatus.pipe(
316 let cachedStatuses: ReadonlyArray<ServerProviderStatus> = [316 Effect.map(Array.of),
317 {317 Effect.forkScoped,
318 provider: CODEX_PROVIDER,318 );
319 status: "warning",
320 available: false,
321 authStatus: "unknown",
322 checkedAt: new Date().toISOString(),
323 message: "Checking Codex CLI availability...",
324 },
325 ];
326
327 let readyListeners: Array<(statuses: ReadonlyArray<ServerProviderStatus>) => void> = [];
328 let resolved = false;
329
330 const notifyReady = (statuses: ReadonlyArray<ServerProviderStatus>) => {
331 resolved = true;
332 cachedStatuses = statuses;
333 for (const cb of readyListeners) cb(statuses);
334 readyListeners = [];
335 };
336
337 // Run health checks in the background so they don't block server startup.
338 checkCodexProviderStatus.pipe(
339 Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, spawner),
340 Effect.runPromise,
341 ).then((status) => {
342 notifyReady([status]);
343 }).catch(() => {
344 notifyReady([
345 {
346 provider: CODEX_PROVIDER,
347 status: "error",
348 available: false,
349 authStatus: "unknown",
350 checkedAt: new Date().toISOString(),
351 message: "Failed to check Codex CLI status.",
352 },
353 ]);
354 });
355319
356 return {320 return {
357 getStatuses: Effect.sync(() => cachedStatuses),321 getStatuses: Fiber.join(codexStatusFiber),
358 onReady: (cb) => {
359 if (resolved) {
360 cb(cachedStatuses);
361 } else {
362 readyListeners.push(cb);
363 }
364 },
365 } satisfies ProviderHealthShape;322 } satisfies ProviderHealthShape;
366 }),323 }),
367);324);
apps/server/src/wsServer.ts+20-17
...@@ -26,6 +26,7 @@ import {...@@ -26,6 +26,7 @@ import {
26 WebSocketRequest,26 WebSocketRequest,
27 WsPush,27 WsPush,
28 WsResponse,28 WsResponse,
29 ServerProviderStatus,
29} from "@t3tools/contracts";30} from "@t3tools/contracts";
30import * as NodeHttpServer from "@effect/platform-node/NodeHttpServer";31import * as NodeHttpServer from "@effect/platform-node/NodeHttpServer";
31import {32import {
...@@ -268,9 +269,6 @@ export const createServer = Effect.fn(function* (): Effect.fn.Return<...@@ -268,9 +269,6 @@ export const createServer = Effect.fn(function* (): Effect.fn.Return<
268 ),269 ),
269 );270 );
270271
271 // Read provider statuses lazily so background health checks are reflected.
272 const getProviderStatuses = () => Effect.runSync(providerHealth.getStatuses);
273
274 const clients = yield* Ref.make(new Set<WebSocket>());272 const clients = yield* Ref.make(new Set<WebSocket>());
275 const logger = createLogger("ws");273 const logger = createLogger("ws");
276274
...@@ -618,6 +616,23 @@ export const createServer = Effect.fn(function* (): Effect.fn.Return<...@@ -618,6 +616,23 @@ export const createServer = Effect.fn(function* (): Effect.fn.Return<
618 const subscriptionsScope = yield* Scope.make("sequential");616 const subscriptionsScope = yield* Scope.make("sequential");
619 yield* Effect.addFinalizer(() => Scope.close(subscriptionsScope, Exit.void));617 yield* Effect.addFinalizer(() => Scope.close(subscriptionsScope, Exit.void));
620618
619 // Push updated provider statuses to connected clients once background health checks finish.
620 let providers: ReadonlyArray<ServerProviderStatus> = [];
621 yield* providerHealth.getStatuses.pipe(
622 Effect.flatMap((statuses) => {
623 providers = statuses;
624 return broadcastPush({
625 type: "push",
626 channel: WS_CHANNELS.serverConfigUpdated,
627 data: {
628 issues: [],
629 providers: statuses,
630 },
631 });
632 }),
633 Effect.forkIn(subscriptionsScope),
634 );
635
621 yield* Stream.runForEach(orchestrationEngine.streamDomainEvents, (event) =>636 yield* Stream.runForEach(orchestrationEngine.streamDomainEvents, (event) =>
622 broadcastPush({637 broadcastPush({
623 type: "push",638 type: "push",
...@@ -632,23 +647,11 @@ export const createServer = Effect.fn(function* (): Effect.fn.Return<...@@ -632,23 +647,11 @@ export const createServer = Effect.fn(function* (): Effect.fn.Return<
632 channel: WS_CHANNELS.serverConfigUpdated,647 channel: WS_CHANNELS.serverConfigUpdated,
633 data: {648 data: {
634 issues: event.issues,649 issues: event.issues,
635 providers: getProviderStatuses(),650 providers,
636 },651 },
637 }),652 }),
638 ).pipe(Effect.forkIn(subscriptionsScope));653 ).pipe(Effect.forkIn(subscriptionsScope));
639654
640 // Push updated provider statuses to connected clients once background health checks finish.
641 providerHealth.onReady((statuses) => {
642 broadcastPush({
643 type: "push",
644 channel: WS_CHANNELS.serverConfigUpdated,
645 data: {
646 issues: [],
647 providers: statuses,
648 },
649 }).pipe(Effect.runPromise).catch(() => {});
650 });
651
652 yield* Scope.provide(orchestrationReactor.start, subscriptionsScope);655 yield* Scope.provide(orchestrationReactor.start, subscriptionsScope);
653656
654 let welcomeBootstrapProjectId: ProjectId | undefined;657 let welcomeBootstrapProjectId: ProjectId | undefined;
...@@ -896,7 +899,7 @@ export const createServer = Effect.fn(function* (): Effect.fn.Return<...@@ -896,7 +899,7 @@ export const createServer = Effect.fn(function* (): Effect.fn.Return<
896 keybindingsConfigPath,899 keybindingsConfigPath,
897 keybindings: keybindingsConfig.keybindings,900 keybindings: keybindingsConfig.keybindings,
898 issues: keybindingsConfig.issues,901 issues: keybindingsConfig.issues,
899 providers: getProviderStatuses(),902 providers,
900 availableEditors,903 availableEditors,
901 };904 };
902905