| author | |
| committer | |
| log | 9c4095aaaa31c5628d40026cbddf094c0f85146d |
| tree | fb5dcc2d84b2da9f247308cbdc804ecbe954aedb |
| parent | 961df8597c4c8149febbc95d0ab52f895ca7ff94 |
- Use generic type parameter for stream errors instead of unknown
- Type ServerShape errors as ServerLifecycleError instead of unknown
- Add error mapping for autoBootstrapProject and httpServerListen
- Type makeServerProviderLayer with ProviderUnsupportedError
- Use Effect.try with decodeUnknownSync in projector to avoid unknown requirements
- Fix test layer types from any/unknown to never
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>6 files changed, 41 insertions(+), 29 deletions(-)
apps/server/src/git/Layers/CodexTextGeneration.ts+2-2| ... | ... | @@ -129,9 +129,9 @@ const makeCodexTextGeneration = Effect.gen(function* () { |
| 129 | 129 | const path = yield* Path.Path; |
| 130 | 130 | const commandSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; |
| 131 | 131 | |
| 132 | const readStreamAsString = ( | |
| 132 | const readStreamAsString = <E>( | |
| 133 | 133 | operation: string, |
| 134 | stream: Stream.Stream<Uint8Array, unknown>, | |
| 134 | stream: Stream.Stream<Uint8Array, E>, | |
| 135 | 135 | ): Effect.Effect<string, TextGenerationError> => |
| 136 | 136 | Effect.gen(function* () { |
| 137 | 137 | let text = ""; |
apps/server/src/git/Layers/GitService.ts+2-2| ... | ... | @@ -39,9 +39,9 @@ function toGitCommandError( |
| 39 | 39 | }); |
| 40 | 40 | } |
| 41 | 41 | |
| 42 | const collectOutput = Effect.fn(function* ( | |
| 42 | const collectOutput = Effect.fn(function* <E>( | |
| 43 | 43 | input: Pick<ExecuteGitInput, "operation" | "cwd" | "args">, |
| 44 | stream: Stream.Stream<Uint8Array, unknown>, | |
| 44 | stream: Stream.Stream<Uint8Array, E>, | |
| 45 | 45 | maxOutputBytes: number, |
| 46 | 46 | ): Effect.fn.Return<string, GitCommandError> { |
| 47 | 47 | const decoder = new TextDecoder(); |
apps/server/src/orchestration/projector.ts+6-3| ... | ... | @@ -40,9 +40,12 @@ function decodeForEvent<A>( |
| 40 | 40 | eventType: OrchestrationEvent["type"], |
| 41 | 41 | field: string, |
| 42 | 42 | ): Effect.Effect<A, OrchestrationProjectorDecodeError> { |
| 43 | return Schema.decodeUnknownEffect(schema)(value).pipe( | |
| 44 | Effect.mapError(toProjectorDecodeError(`${eventType}:${field}`)), | |
| 45 | ) as Effect.Effect<A, OrchestrationProjectorDecodeError>; | |
| 43 | return Effect.try({ | |
| 44 | // eslint-disable-next-line @typescript-eslint/no-explicit-any | |
| 45 | try: () => Schema.decodeUnknownSync(schema as any)(value) as A, | |
| 46 | catch: (error) => | |
| 47 | toProjectorDecodeError(`${eventType}:${field}`)(error as Schema.SchemaError), | |
| 48 | }); | |
| 46 | 49 | } |
| 47 | 50 | |
| 48 | 51 | function retainThreadMessagesAfterRevert( |
apps/server/src/serverLayers.ts+2-1| ... | ... | @@ -17,6 +17,7 @@ import { ProviderCommandReactorLive } from "./orchestration/Layers/ProviderComma |
| 17 | 17 | import { OrchestrationProjectionPipelineLive } from "./orchestration/Layers/ProjectionPipeline"; |
| 18 | 18 | import { OrchestrationProjectionSnapshotQueryLive } from "./orchestration/Layers/ProjectionSnapshotQuery"; |
| 19 | 19 | import { ProviderRuntimeIngestionLive } from "./orchestration/Layers/ProviderRuntimeIngestion"; |
| 20 | import { ProviderUnsupportedError } from "./provider/Errors"; | |
| 20 | 21 | import { makeCodexAdapterLive } from "./provider/Layers/CodexAdapter"; |
| 21 | 22 | import { ProviderAdapterRegistryLive } from "./provider/Layers/ProviderAdapterRegistry"; |
| 22 | 23 | import { makeProviderServiceLive } from "./provider/Layers/ProviderService"; |
| ... | ... | @@ -35,7 +36,7 @@ import { NodePtyAdapterLive } from "./terminal/Layers/NodePTY"; |
| 35 | 36 | |
| 36 | 37 | export function makeServerProviderLayer(): Layer.Layer< |
| 37 | 38 | ProviderService, |
| 38 | unknown, | |
| 39 | ProviderUnsupportedError, | |
| 39 | 40 | SqlClient.SqlClient | ServerConfig |
| 40 | 41 | > { |
| 41 | 42 | return Effect.gen(function* () { |
apps/server/src/wsServer.test.ts+2-2| ... | ... | @@ -323,14 +323,14 @@ describe("WebSocket Server", () => { |
| 323 | 323 | |
| 324 | 324 | async function createTestServer( |
| 325 | 325 | options: { |
| 326 | persistenceLayer?: Layer.Layer<SqlClient.SqlClient, any>; | |
| 326 | persistenceLayer?: Layer.Layer<SqlClient.SqlClient, never>; | |
| 327 | 327 | cwd?: string; |
| 328 | 328 | autoBootstrapProjectFromCwd?: boolean; |
| 329 | 329 | logWebSocketEvents?: boolean; |
| 330 | 330 | devUrl?: string; |
| 331 | 331 | authToken?: string; |
| 332 | 332 | stateDir?: string; |
| 333 | providerLayer?: Layer.Layer<ProviderService, unknown>; | |
| 333 | providerLayer?: Layer.Layer<ProviderService, never>; | |
| 334 | 334 | open?: OpenShape; |
| 335 | 335 | gitManager?: GitManagerShape; |
| 336 | 336 | gitCore?: Pick<GitCoreShape, "listBranches" | "initRepo" | "pullCurrentBranch">; |
apps/server/src/wsServer.ts+27-19| ... | ... | @@ -66,14 +66,14 @@ export interface ServerShape { |
| 66 | 66 | */ |
| 67 | 67 | readonly start: Effect.Effect< |
| 68 | 68 | http.Server, |
| 69 | unknown, | |
| 69 | ServerLifecycleError, | |
| 70 | 70 | Scope.Scope | ServerRuntimeServices | ServerConfig |
| 71 | 71 | >; |
| 72 | 72 | |
| 73 | 73 | /** |
| 74 | 74 | * Wait for process shutdown signals. |
| 75 | 75 | */ |
| 76 | readonly stopSignal: Effect.Effect<unknown, never>; | |
| 76 | readonly stopSignal: Effect.Effect<void, never>; | |
| 77 | 77 | } |
| 78 | 78 | |
| 79 | 79 | /** |
| ... | ... | @@ -165,7 +165,7 @@ class RouteRequestError extends Schema.TaggedErrorClass<RouteRequestError>()("Ro |
| 165 | 165 | |
| 166 | 166 | export const createServer = Effect.fn(function* (): Effect.fn.Return< |
| 167 | 167 | http.Server, |
| 168 | unknown, // TODO: This should not be unknown | |
| 168 | ServerLifecycleError, | |
| 169 | 169 | Scope.Scope | ServerRuntimeServices | ServerConfig |
| 170 | 170 | > { |
| 171 | 171 | const serverConfig = yield* ServerConfig; |
| ... | ... | @@ -338,21 +338,27 @@ export const createServer = Effect.fn(function* (): Effect.fn.Return< |
| 338 | 338 | yield* Scope.provide(orchestrationReactor.start, subscriptionsScope); |
| 339 | 339 | |
| 340 | 340 | if (autoBootstrapProjectFromCwd) { |
| 341 | const snapshot = yield* projectionReadModelQuery.getSnapshot(); | |
| 342 | const existing = snapshot.projects.find((project) => project.workspaceRoot === cwd); | |
| 343 | if (!existing) { | |
| 344 | const createdAt = new Date().toISOString(); | |
| 345 | const projectName = path.basename(cwd) || "project"; | |
| 346 | yield* orchestrationEngine.dispatch({ | |
| 347 | type: "project.create", | |
| 348 | commandId: CommandId.makeUnsafe(crypto.randomUUID()), | |
| 349 | projectId: ProjectId.makeUnsafe(crypto.randomUUID()), | |
| 350 | title: projectName, | |
| 351 | workspaceRoot: cwd, | |
| 352 | defaultModel: "gpt-5-codex", | |
| 353 | createdAt, | |
| 354 | }); | |
| 355 | } | |
| 341 | yield* Effect.gen(function* () { | |
| 342 | const snapshot = yield* projectionReadModelQuery.getSnapshot(); | |
| 343 | const existing = snapshot.projects.find((project) => project.workspaceRoot === cwd); | |
| 344 | if (!existing) { | |
| 345 | const createdAt = new Date().toISOString(); | |
| 346 | const projectName = path.basename(cwd) || "project"; | |
| 347 | yield* orchestrationEngine.dispatch({ | |
| 348 | type: "project.create", | |
| 349 | commandId: CommandId.makeUnsafe(crypto.randomUUID()), | |
| 350 | projectId: ProjectId.makeUnsafe(crypto.randomUUID()), | |
| 351 | title: projectName, | |
| 352 | workspaceRoot: cwd, | |
| 353 | defaultModel: "gpt-5-codex", | |
| 354 | createdAt, | |
| 355 | }); | |
| 356 | } | |
| 357 | }).pipe( | |
| 358 | Effect.mapError( | |
| 359 | (cause) => new ServerLifecycleError({ operation: "autoBootstrapProject", cause }), | |
| 360 | ), | |
| 361 | ); | |
| 356 | 362 | } |
| 357 | 363 | |
| 358 | 364 | const routeRequest = Effect.fnUntraced(function* (request: WebSocketRequest) { |
| ... | ... | @@ -618,7 +624,9 @@ export const createServer = Effect.fn(function* (): Effect.fn.Return< |
| 618 | 624 | ); |
| 619 | 625 | yield* Effect.addFinalizer(() => Effect.sync(() => unsubscribeTerminalEvents())); |
| 620 | 626 | |
| 621 | yield* NodeHttpServer.make(() => httpServer, listenOptions); | |
| 627 | yield* NodeHttpServer.make(() => httpServer, listenOptions).pipe( | |
| 628 | Effect.mapError((cause) => new ServerLifecycleError({ operation: "httpServerListen", cause })), | |
| 629 | ); | |
| 622 | 630 | |
| 623 | 631 | yield* Effect.addFinalizer(() => |
| 624 | 632 | Effect.all([ |