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 {
1313 ServerProviderStatus,
1414 ServerProviderStatusState,
1515} from "@t3tools/contracts";
16import { Effect, Layer, Option, Result, Stream } from "effect";
16import { Array, Effect, Fiber, Layer, Option, Result, Stream } from "effect";
1717import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process";
1818
1919import {
......@@ -312,56 +312,13 @@ export const checkCodexProviderStatus: Effect.Effect<
312312export const ProviderHealthLive = Layer.effect(
313313 ProviderHealth,
314314 Effect.gen(function* () {
315 const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;
316 let cachedStatuses: ReadonlyArray<ServerProviderStatus> = [
317 {
318 provider: CODEX_PROVIDER,
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 });
315 const codexStatusFiber = yield* checkCodexProviderStatus.pipe(
316 Effect.map(Array.of),
317 Effect.forkScoped,
318 );
355319
356320 return {
357 getStatuses: Effect.sync(() => cachedStatuses),
358 onReady: (cb) => {
359 if (resolved) {
360 cb(cachedStatuses);
361 } else {
362 readyListeners.push(cb);
363 }
364 },
321 getStatuses: Fiber.join(codexStatusFiber),
365322 } satisfies ProviderHealthShape;
366323 }),
367324);
apps/server/src/wsServer.ts+20-17
......@@ -26,6 +26,7 @@ import {
2626 WebSocketRequest,
2727 WsPush,
2828 WsResponse,
29 ServerProviderStatus,
2930} from "@t3tools/contracts";
3031import * as NodeHttpServer from "@effect/platform-node/NodeHttpServer";
3132import {
......@@ -268,9 +269,6 @@ export const createServer = Effect.fn(function* (): Effect.fn.Return<
268269 ),
269270 );
270271
271 // Read provider statuses lazily so background health checks are reflected.
272 const getProviderStatuses = () => Effect.runSync(providerHealth.getStatuses);
273
274272 const clients = yield* Ref.make(new Set<WebSocket>());
275273 const logger = createLogger("ws");
276274
......@@ -618,6 +616,23 @@ export const createServer = Effect.fn(function* (): Effect.fn.Return<
618616 const subscriptionsScope = yield* Scope.make("sequential");
619617 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
621636 yield* Stream.runForEach(orchestrationEngine.streamDomainEvents, (event) =>
622637 broadcastPush({
623638 type: "push",
......@@ -632,23 +647,11 @@ export const createServer = Effect.fn(function* (): Effect.fn.Return<
632647 channel: WS_CHANNELS.serverConfigUpdated,
633648 data: {
634649 issues: event.issues,
635 providers: getProviderStatuses(),
650 providers,
636651 },
637652 }),
638653 ).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
652655 yield* Scope.provide(orchestrationReactor.start, subscriptionsScope);
653656
654657 let welcomeBootstrapProjectId: ProjectId | undefined;
......@@ -896,7 +899,7 @@ export const createServer = Effect.fn(function* (): Effect.fn.Return<
896899 keybindingsConfigPath,
897900 keybindings: keybindingsConfig.keybindings,
898901 issues: keybindingsConfig.issues,
899 providers: getProviderStatuses(),
902 providers,
900903 availableEditors,
901904 };
902905