From 6bc94e1b1cd6e88e881d860044d1a0b470c49c79 Mon Sep 17 00:00:00 2001 From: Julius Marminge <51714798+juliusmarminge@users.noreply.github.com> Date: Fri, 9 Oct 2026 09:35:21 -0700 Subject: [PATCH] refactor(provider): bring opencode, muse, pi, core and testing in line with Effect conventions - Inline OpenCodeRuntimeShape into the OpenCodeRuntime service; consumers use OpenCodeRuntime.OpenCodeRuntime["Service"]. - Move OpenCode message-id clock state from module-level lets into a Ref owned by makeOpenCodeAdapterV2. - OpenCode2Client.readEvents and OpenCode 2 text generation yield their services instead of taking them as parameters. - Rename provider-core ServerProviderShape to ManagedServerProvider. - Drop expandHomePathWith; expandHomePath covers every caller. - Rename provider-testing/host to TestProviderHost (layer + TestProviderHostSettings) with a matching service key. - Import effect/http and effect/process modules as subpath namespaces. Co-Authored-By: Claude Opus 5.5 --- .../scripts/record-grok-acp-replay-fixture.ts | 4 +- apps/server/src/assets/AssetAccess.ts | 4 +- .../Adapters/AcpAdapterV2.test.ts | 4 +- .../Adapters/AcpRegistryAdapterV2.test.ts | 4 +- .../Adapters/AcpRegistryAdapterV2.testkit.ts | 4 +- .../Adapters/AntigravityAdapterV2.test.ts | 4 +- .../Adapters/ClaudeAdapterV2.ts | 4 +- .../Adapters/CodexAdapterV2.ts | 4 +- .../Adapters/CursorAdapterV2.testkit.ts | 4 +- .../Adapters/GrokAdapterV2.test.ts | 4 +- .../Adapters/GrokAdapterV2.testkit.ts | 4 +- .../Adapters/MuseAdapterV2.testkit.ts | 4 +- .../Adapters/OpenCode2AdapterV2.testkit.ts | 4 +- .../Adapters/OpenCodeAdapterV2.testkit.ts | 6 +- .../Adapters/PiAdapterV2.testkit.ts | 4 +- apps/server/src/provider/CodexProvider.ts | 2 +- .../src/provider/Drivers/CodexDriver.ts | 2 +- apps/server/src/provider/ProviderRegistry.ts | 4 +- .../src/provider/builtInProviderCatalog.ts | 6 +- .../SourceControlRepositoryService.ts | 4 +- .../OpenCode2TextGeneration.test.ts | 4 +- apps/server/src/workspace/WorkspaceEntries.ts | 6 +- apps/server/src/workspace/WorkspacePaths.ts | 4 +- apps/server/src/worktreesDirectory.ts | 4 +- .../src/server/AcpRegistrySupport.test.ts | 10 +- packages/provider-core/src/server/driver.ts | 4 +- .../src/server/maintenanceResolver.test.ts | 4 +- .../src/server/maintenanceResolver.ts | 6 +- .../src/server/managedProvider.test.ts | 2 +- .../src/server/managedProvider.ts | 10 +- .../provider-core/src/server/pathExpansion.ts | 18 -- packages/provider-core/src/server/snapshot.ts | 2 +- .../src/server/snapshotProbe.test.ts | 3 +- .../provider-core/src/server/snapshotProbe.ts | 3 +- .../src/server/adapter.test.ts | 8 +- .../src/server/credentialStore.test.ts | 4 +- .../provider-cursor/src/server/driver.test.ts | 4 +- .../provider-grok/src/server/driver.test.ts | 4 +- .../provider-muse/src/server/adapter.test.ts | 4 +- packages/provider-muse/src/server/driver.ts | 4 +- .../src/server/maintenance.test.ts | 3 +- .../provider-muse/src/server/maintenance.ts | 3 +- packages/provider-muse/src/server/status.ts | 2 +- .../OpenCodeRuntime.environment.test.ts | 3 +- .../src/server/OpenCodeRuntime.ts | 156 +++++++++--------- .../src/server/OpenCodeServerLedger.test.ts | 2 +- .../src/server/OpenCodeServerLedger.ts | 3 +- .../src/server/OpenCodeServerOwner.test.ts | 8 +- .../src/server/adapter.test.ts | 12 +- .../provider-opencode/src/server/adapter.ts | 27 ++- .../src/server/driver.test.ts | 14 +- .../provider-opencode/src/server/driver.ts | 4 +- .../src/server/probeResponses.fixture.ts | 3 +- .../src/server/status.test.ts | 9 +- .../src/server/textGeneration.test.ts | 12 +- .../src/server/usageLimits.ts | 4 +- .../server/v2/OpenCode2Client.live.test.ts | 5 +- .../src/server/v2/OpenCode2Client.test.ts | 3 +- .../src/server/v2/OpenCode2Client.ts | 46 +++--- .../server/v2/OpenCode2Server.live.test.ts | 5 +- .../src/server/v2/OpenCode2Server.test.ts | 4 +- .../src/server/v2/textGeneration.ts | 7 +- .../src/server/versionProbe.test.ts | 8 +- .../src/server/versionProbe.ts | 3 +- .../provider-pi/src/server/adapter.test.ts | 7 +- packages/provider-pi/src/server/adapter.ts | 2 +- .../provider-pi/src/server/driver.test.ts | 9 +- packages/provider-pi/src/server/driver.ts | 4 +- packages/provider-pi/src/server/rpc.ts | 3 +- .../provider-pi/src/server/status.test.ts | 3 +- packages/provider-pi/src/server/status.ts | 5 +- .../src/server/textGeneration.test.ts | 2 +- .../provider-pi/src/server/textGeneration.ts | 2 +- packages/provider-testing/package.json | 20 +-- .../src/{host.ts => TestProviderHost.ts} | 8 +- .../provider-testing/src/replayWorkspace.ts | 3 +- 76 files changed, 309 insertions(+), 293 deletions(-) rename packages/provider-testing/src/{host.ts => TestProviderHost.ts} (94%) diff --git a/apps/server/scripts/record-grok-acp-replay-fixture.ts b/apps/server/scripts/record-grok-acp-replay-fixture.ts index da8b17fec567..49b667fecec1 100644 --- a/apps/server/scripts/record-grok-acp-replay-fixture.ts +++ b/apps/server/scripts/record-grok-acp-replay-fixture.ts @@ -24,7 +24,7 @@ import * as Schema from "effect/Schema"; import { ChildProcessSpawner } from "effect/process"; import type * as EffectAcpProtocol from "effect-acp/protocol"; -import { layerTestProviderHost } from "@t3tools/provider-testing/host"; +import * as TestProviderHost from "@t3tools/provider-testing/TestProviderHost"; import { GROK_DEFAULT_INSTANCE_ID, GROK_PROVIDER, @@ -490,7 +490,7 @@ const recordScenario = Effect.fn("recordGrokScenario")(function* (fixtureName: s ).pipe( Layer.provide( Layer.mergeAll( - layerTestProviderHost().pipe(Layer.provide(NodeServices.layer)), + TestProviderHost.layer().pipe(Layer.provide(NodeServices.layer)), NodeServices.layer, IdAllocator.layer, ), diff --git a/apps/server/src/assets/AssetAccess.ts b/apps/server/src/assets/AssetAccess.ts index b9344451e64f..c9fbc064168c 100644 --- a/apps/server/src/assets/AssetAccess.ts +++ b/apps/server/src/assets/AssetAccess.ts @@ -53,7 +53,7 @@ import * as ServerSecretStore from "../auth/ServerSecretStore.ts"; import { parseAttachmentFileExtension, resolveAttachmentPathById } from "../attachmentStore.ts"; import * as ServerConfig from "../config.ts"; import * as Orchestrator from "../orchestration-v2/Orchestrator.ts"; -import { expandHomePathWith } from "@t3tools/provider-core/server/pathExpansion"; +import { expandHomePath } from "@t3tools/provider-core/server/pathExpansion"; import * as ProjectFaviconResolver from "../project/ProjectFaviconResolver.ts"; import * as WorkspacePaths from "../workspace/WorkspacePaths.ts"; import * as NativeAppIconResolver from "./NativeAppIconResolver.ts"; @@ -469,7 +469,7 @@ export const issueAssetUrl = Effect.fn("AssetAccess.issueAssetUrl")(function* (i switch (input.resource._tag) { case "media-file": { - let requestedPath = expandHomePathWith(input.resource.path, path); + let requestedPath = expandHomePath(input.resource.path); if (!path.isAbsolute(requestedPath)) { if (!input.workspaceRoot) { return yield* new AssetWorkspaceContextNotFoundError({ resource: input.resource }); diff --git a/apps/server/src/orchestration-v2/Adapters/AcpAdapterV2.test.ts b/apps/server/src/orchestration-v2/Adapters/AcpAdapterV2.test.ts index 697da040bef6..a8c9677eeb0e 100644 --- a/apps/server/src/orchestration-v2/Adapters/AcpAdapterV2.test.ts +++ b/apps/server/src/orchestration-v2/Adapters/AcpAdapterV2.test.ts @@ -53,7 +53,7 @@ import * as EffectAcpErrors from "effect-acp/errors"; import type * as EffectAcpProtocol from "effect-acp/protocol"; import type * as EffectAcpSchema from "effect-acp/compat"; -import { layerTestProviderHost } from "@t3tools/provider-testing/host"; +import * as TestProviderHost from "@t3tools/provider-testing/TestProviderHost"; import * as McpProviderSessions from "@t3tools/provider-core/server/McpProviderSessions"; import * as AcpSessionRuntime from "@t3tools/provider-acp/server/AcpSessionRuntime"; import { @@ -94,7 +94,7 @@ import { const DEFAULT_GROK_SETTINGS = Schema.decodeSync(GrokSettings)({}); -const layerHost = layerTestProviderHost().pipe(Layer.provide(NodeServices.layer)); +const layerHost = TestProviderHost.layer().pipe(Layer.provide(NodeServices.layer)); const layerTest = Layer.mergeAll( NodeServices.layer, diff --git a/apps/server/src/orchestration-v2/Adapters/AcpRegistryAdapterV2.test.ts b/apps/server/src/orchestration-v2/Adapters/AcpRegistryAdapterV2.test.ts index 17ff9b76ff44..e7327f034db7 100644 --- a/apps/server/src/orchestration-v2/Adapters/AcpRegistryAdapterV2.test.ts +++ b/apps/server/src/orchestration-v2/Adapters/AcpRegistryAdapterV2.test.ts @@ -15,7 +15,7 @@ import * as EffectAcpErrors from "effect-acp/errors"; import * as ProviderHost from "@t3tools/provider-core/server/ProviderHost"; import * as McpProviderSessions from "@t3tools/provider-core/server/McpProviderSessions"; -import { layerTestProviderHost } from "@t3tools/provider-testing/host"; +import * as TestProviderHost from "@t3tools/provider-testing/TestProviderHost"; import * as ServerSettings from "../../serverSettings.ts"; import type { AcpRegistryAvailableCommands, @@ -44,7 +44,7 @@ const decodeAcpRegistryAdapterSettings = Schema.decodeUnknownEffect( AcpRegistryAdapterV2Driver.configSchema, ); -const layerHost = layerTestProviderHost().pipe(Layer.provide(NodeServices.layer)); +const layerHost = TestProviderHost.layer().pipe(Layer.provide(NodeServices.layer)); const layerRegistry = Layer.succeed( HttpClient.HttpClient, diff --git a/apps/server/src/orchestration-v2/Adapters/AcpRegistryAdapterV2.testkit.ts b/apps/server/src/orchestration-v2/Adapters/AcpRegistryAdapterV2.testkit.ts index c30152529006..93254166f183 100644 --- a/apps/server/src/orchestration-v2/Adapters/AcpRegistryAdapterV2.testkit.ts +++ b/apps/server/src/orchestration-v2/Adapters/AcpRegistryAdapterV2.testkit.ts @@ -8,7 +8,7 @@ import * as Path from "effect/Path"; import * as Schema from "effect/Schema"; import { ChildProcessSpawner } from "effect/process"; -import { layerTestProviderHost } from "@t3tools/provider-testing/host"; +import * as TestProviderHost from "@t3tools/provider-testing/TestProviderHost"; import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; import * as ProviderAdapterRegistry from "../ProviderAdapterRegistry.ts"; import type { ProviderReplayGate } from "@t3tools/provider-testing/replayGate"; @@ -35,7 +35,7 @@ function layerAcpRegistryProviderAdapterRegistryReplay( transcript: AcpReplayTranscript, options: { readonly replayGate?: ProviderReplayGate } = {}, ) { - const layerHost = layerTestProviderHost().pipe(Layer.provide(NodeServices.layer)); + const layerHost = TestProviderHost.layer().pipe(Layer.provide(NodeServices.layer)); return ProviderAdapterRegistry.layerFromAdaptersEffect( Effect.gen(function* () { diff --git a/apps/server/src/orchestration-v2/Adapters/AntigravityAdapterV2.test.ts b/apps/server/src/orchestration-v2/Adapters/AntigravityAdapterV2.test.ts index ca19c1f86c50..825d0d81508c 100644 --- a/apps/server/src/orchestration-v2/Adapters/AntigravityAdapterV2.test.ts +++ b/apps/server/src/orchestration-v2/Adapters/AntigravityAdapterV2.test.ts @@ -24,7 +24,7 @@ import type * as EffectAcpSchema from "effect-acp/compat"; import * as ProviderHost from "@t3tools/provider-core/server/ProviderHost"; import * as McpProviderSessions from "@t3tools/provider-core/server/McpProviderSessions"; -import { layerTestProviderHost } from "@t3tools/provider-testing/host"; +import * as TestProviderHost from "@t3tools/provider-testing/TestProviderHost"; import type * as AcpSessionRuntime from "@t3tools/provider-acp/server/AcpSessionRuntime"; import { makeAntigravityAcpRuntime } from "../../provider/acp/AntigravityAcpSupport.ts"; import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; @@ -131,7 +131,7 @@ const layerSession = Layer.mergeAll( NodeServices.layer, IdAllocator.layer, McpProviderSessions.layer, - layerTestProviderHost().pipe(Layer.provide(NodeServices.layer)), + TestProviderHost.layer().pipe(Layer.provide(NodeServices.layer)), ); describe("AntigravityAdapterV2 client file system", () => { diff --git a/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts b/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts index 9f1e34f8d1a0..c7b1fb0d5819 100644 --- a/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts +++ b/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts @@ -108,7 +108,7 @@ import { claudeRateLimitEventToUpdate, type ClaudeScopedLimitNames, } from "../../provider/claudeUsageLimits.ts"; -import type { ServerProviderShape } from "@t3tools/provider-core/server/snapshot"; +import type { ManagedServerProvider } from "@t3tools/provider-core/server/snapshot"; import { mergeProviderInstanceEnvironment } from "@t3tools/provider-core/server/instanceEnvironment"; import { T3_CODE_ORCHESTRATION_INSTRUCTIONS } from "@t3tools/provider-core/server/orchestrationInstructions"; import { buildRuntimeInstructions } from "@t3tools/provider-core/server/runtimeInstructions"; @@ -3018,7 +3018,7 @@ export interface ClaudeAdapterV2Options { readonly idAllocator: IdAllocator.IdAllocatorV2["Service"]; readonly queryRunner: ClaudeAgentSdkQueryRunnerShape; readonly scopedLimitNames?: Ref.Ref; - readonly onUsageLimits?: ServerProviderShape["applyUsageLimits"]; + readonly onUsageLimits?: ManagedServerProvider["applyUsageLimits"]; /** Sink for wake-turn continuation requests; defaults to dropping them. */ readonly continuationRequests?: { readonly offer: ( diff --git a/apps/server/src/orchestration-v2/Adapters/CodexAdapterV2.ts b/apps/server/src/orchestration-v2/Adapters/CodexAdapterV2.ts index 8b885320cf17..dbdbab32a74d 100644 --- a/apps/server/src/orchestration-v2/Adapters/CodexAdapterV2.ts +++ b/apps/server/src/orchestration-v2/Adapters/CodexAdapterV2.ts @@ -12,7 +12,7 @@ import { completeCodexTurnTokenUsage, type CodexTurnTokenUsageState, } from "../../provider/CodexTurnTokenUsage.ts"; -import type { ServerProviderShape } from "@t3tools/provider-core/server/snapshot"; +import type { ManagedServerProvider } from "@t3tools/provider-core/server/snapshot"; import type { CodexEffectiveRuntime } from "../../provider/CodexManagedRuntime.ts"; import { buildCodexInitializeParams } from "../../provider/CodexProvider.ts"; import { @@ -1646,7 +1646,7 @@ export interface CodexAdapterV2Options { readonly settings: CodexSettings; readonly environment: NodeJS.ProcessEnv; readonly clientFactory: CodexAppServerClientFactoryShape; - readonly onUsageLimits?: ServerProviderShape["applyUsageLimits"]; + readonly onUsageLimits?: ManagedServerProvider["applyUsageLimits"]; /** * Resolves launch settings when each session opens, replacing `settings` and * `environment`. Managed ChatGPT sign-in uses it to launch the T3-installed diff --git a/apps/server/src/orchestration-v2/Adapters/CursorAdapterV2.testkit.ts b/apps/server/src/orchestration-v2/Adapters/CursorAdapterV2.testkit.ts index 0355b7bc6917..a194edd4a3ce 100644 --- a/apps/server/src/orchestration-v2/Adapters/CursorAdapterV2.testkit.ts +++ b/apps/server/src/orchestration-v2/Adapters/CursorAdapterV2.testkit.ts @@ -15,7 +15,7 @@ import * as FileSystem from "effect/FileSystem"; import * as Layer from "effect/Layer"; import * as Schema from "effect/Schema"; -import { layerTestProviderHost } from "@t3tools/provider-testing/host"; +import * as TestProviderHost from "@t3tools/provider-testing/TestProviderHost"; import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; import { ProviderAdapterDriverCreateError } from "@t3tools/provider-core/server/adapterDriver"; import * as ProviderAdapterRegistry from "../ProviderAdapterRegistry.ts"; @@ -537,7 +537,7 @@ export function layer( Layer.provide( Layer.mergeAll( layerCursorAgentSdkReplay(transcript, options), - layerTestProviderHost().pipe(Layer.provide(NodeServices.layer)), + TestProviderHost.layer().pipe(Layer.provide(NodeServices.layer)), layerHostEnvironment, NodeServices.layer, IdAllocator.layer, diff --git a/apps/server/src/orchestration-v2/Adapters/GrokAdapterV2.test.ts b/apps/server/src/orchestration-v2/Adapters/GrokAdapterV2.test.ts index dee5d16aac13..86a6df752f69 100644 --- a/apps/server/src/orchestration-v2/Adapters/GrokAdapterV2.test.ts +++ b/apps/server/src/orchestration-v2/Adapters/GrokAdapterV2.test.ts @@ -20,7 +20,7 @@ import * as Schema from "effect/Schema"; import { ChildProcessSpawner } from "effect/process"; import type * as EffectAcpSchema from "effect-acp/compat"; -import { layerTestProviderHost } from "@t3tools/provider-testing/host"; +import * as TestProviderHost from "@t3tools/provider-testing/TestProviderHost"; import * as ProjectStore from "../ProjectStore.ts"; import { buildInitialGrokProviderSnapshot } from "@t3tools/provider-grok/testing"; import type { ProviderInstance } from "@t3tools/provider-core/server/driver"; @@ -309,7 +309,7 @@ describe("Grok permission prompts", () => { }); describe("Grok launch permission mode", () => { - const layerHost = layerTestProviderHost().pipe(Layer.provide(NodeServices.layer)); + const layerHost = TestProviderHost.layer().pipe(Layer.provide(NodeServices.layer)); const layerTest = Layer.mergeAll( NodeServices.layer, IdAllocator.layer, diff --git a/apps/server/src/orchestration-v2/Adapters/GrokAdapterV2.testkit.ts b/apps/server/src/orchestration-v2/Adapters/GrokAdapterV2.testkit.ts index 467ad620d534..443019cea472 100644 --- a/apps/server/src/orchestration-v2/Adapters/GrokAdapterV2.testkit.ts +++ b/apps/server/src/orchestration-v2/Adapters/GrokAdapterV2.testkit.ts @@ -9,7 +9,7 @@ import { ChildProcessSpawner } from "effect/process"; import { HostProcessPlatform } from "@t3tools/shared/hostProcess"; import { resolveSelfInvocation } from "@t3tools/shared/nodeRuntime"; -import { layerTestProviderHost } from "@t3tools/provider-testing/host"; +import * as TestProviderHost from "@t3tools/provider-testing/TestProviderHost"; import { GROK_ACP_CANCEL_META, GROK_ACP_INITIALIZE_META } from "@t3tools/provider-grok/testing"; import { makeXAiPromptCompletionRuntime } from "@t3tools/provider-grok/testing"; import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; @@ -36,7 +36,7 @@ function layerGrokProviderAdapterRegistryReplay( transcript: AcpReplayTranscript, options: { readonly replayGate?: ProviderReplayGate } = {}, ) { - const layerHost = layerTestProviderHost().pipe(Layer.provide(NodeServices.layer)); + const layerHost = TestProviderHost.layer().pipe(Layer.provide(NodeServices.layer)); return ProviderAdapterRegistry.layerFromAdaptersEffect( Effect.gen(function* () { diff --git a/apps/server/src/orchestration-v2/Adapters/MuseAdapterV2.testkit.ts b/apps/server/src/orchestration-v2/Adapters/MuseAdapterV2.testkit.ts index b695b39fa8d0..dbe4f7068ebb 100644 --- a/apps/server/src/orchestration-v2/Adapters/MuseAdapterV2.testkit.ts +++ b/apps/server/src/orchestration-v2/Adapters/MuseAdapterV2.testkit.ts @@ -27,7 +27,7 @@ import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; import * as Schema from "effect/Schema"; -import { layerTestProviderHost } from "@t3tools/provider-testing/host"; +import * as TestProviderHost from "@t3tools/provider-testing/TestProviderHost"; import { museInitializeParams, museServeArgs, @@ -460,7 +460,7 @@ export function layer(input: { ).pipe( Layer.provide( Layer.mergeAll( - layerTestProviderHost().pipe(Layer.provide(NodeServices.layer)), + TestProviderHost.layer().pipe(Layer.provide(NodeServices.layer)), NodeServices.layer, IdAllocator.layer, ), diff --git a/apps/server/src/orchestration-v2/Adapters/OpenCode2AdapterV2.testkit.ts b/apps/server/src/orchestration-v2/Adapters/OpenCode2AdapterV2.testkit.ts index 2b009799cc53..7eb295997483 100644 --- a/apps/server/src/orchestration-v2/Adapters/OpenCode2AdapterV2.testkit.ts +++ b/apps/server/src/orchestration-v2/Adapters/OpenCode2AdapterV2.testkit.ts @@ -26,7 +26,7 @@ import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; import * as ProviderAdapterRegistry from "../ProviderAdapterRegistry.ts"; import type { ProviderReplayGate } from "@t3tools/provider-testing/replayGate"; import type { OrchestratorV2ProviderReplayHarness } from "../testkit/ProviderReplayHarness.ts"; -import { layerTestProviderHost } from "@t3tools/provider-testing/host"; +import * as TestProviderHost from "@t3tools/provider-testing/TestProviderHost"; import { OPENCODE_PROVIDER } from "@t3tools/provider-opencode/testing"; import { OpenCodeReplayController, @@ -267,7 +267,7 @@ const makeReplayAdapter = ( ); }); -const layerReplayHost = layerTestProviderHost().pipe(Layer.provide(NodeServices.layer)); +const layerReplayHost = TestProviderHost.layer().pipe(Layer.provide(NodeServices.layer)); function layerRegistry( transcript: OpenCode2ReplayTranscript, diff --git a/apps/server/src/orchestration-v2/Adapters/OpenCodeAdapterV2.testkit.ts b/apps/server/src/orchestration-v2/Adapters/OpenCodeAdapterV2.testkit.ts index 5c982997a92f..424bae4f25d9 100644 --- a/apps/server/src/orchestration-v2/Adapters/OpenCodeAdapterV2.testkit.ts +++ b/apps/server/src/orchestration-v2/Adapters/OpenCodeAdapterV2.testkit.ts @@ -8,7 +8,7 @@ import * as Schema from "effect/Schema"; import { OpenCodeAdapterV2Driver } from "@t3tools/provider-opencode/server"; import * as OpenCodeRuntime from "@t3tools/provider-opencode/server/OpenCodeRuntime"; -import { layerTestProviderHost } from "@t3tools/provider-testing/host"; +import * as TestProviderHost from "@t3tools/provider-testing/TestProviderHost"; import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers"; import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; import { ProviderAdapterDriverCreateError } from "@t3tools/provider-core/server/adapterDriver"; @@ -448,7 +448,7 @@ function layerOpenCodeReplayRuntime(transcript: OpenCodeSdkReplayTranscript) { detail: "OpenCode replay does not load skills.", }), ), - } satisfies OpenCodeRuntime.OpenCodeRuntimeShape); + } satisfies OpenCodeRuntime.OpenCodeRuntime["Service"]); }), ); } @@ -466,7 +466,7 @@ function layerOpenCodeProviderAdapterRegistryReplay(transcript: OpenCodeSdkRepla Layer.provide( Layer.mergeAll( layerOpenCodeReplayRuntime(transcript), - layerTestProviderHost().pipe(Layer.provide(NodeServices.layer)), + TestProviderHost.layer().pipe(Layer.provide(NodeServices.layer)), NodeServices.layer, IdAllocator.layer, Layer.succeed( diff --git a/apps/server/src/orchestration-v2/Adapters/PiAdapterV2.testkit.ts b/apps/server/src/orchestration-v2/Adapters/PiAdapterV2.testkit.ts index f0538385e810..ae166ed76bca 100644 --- a/apps/server/src/orchestration-v2/Adapters/PiAdapterV2.testkit.ts +++ b/apps/server/src/orchestration-v2/Adapters/PiAdapterV2.testkit.ts @@ -30,7 +30,7 @@ import * as Sink from "effect/Sink"; import * as Stream from "effect/Stream"; import { ChildProcess, ChildProcessSpawner } from "effect/process"; -import { layerTestProviderHost } from "@t3tools/provider-testing/host"; +import * as TestProviderHost from "@t3tools/provider-testing/TestProviderHost"; import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; import { ProviderAdapterDriverCreateError } from "@t3tools/provider-core/server/adapterDriver"; import * as ProviderAdapterRegistry from "../ProviderAdapterRegistry.ts"; @@ -425,7 +425,7 @@ export function layer(input: { Layer.provide(input.spawner), Layer.provide( Layer.mergeAll( - layerTestProviderHost().pipe(Layer.provide(NodeServices.layer)), + TestProviderHost.layer().pipe(Layer.provide(NodeServices.layer)), NodeServices.layer, IdAllocator.layer, ), diff --git a/apps/server/src/provider/CodexProvider.ts b/apps/server/src/provider/CodexProvider.ts index ac3b299b18bf..913a1983a107 100644 --- a/apps/server/src/provider/CodexProvider.ts +++ b/apps/server/src/provider/CodexProvider.ts @@ -716,7 +716,7 @@ export const checkCodexProviderStatus = Effect.fn("checkCodexProviderStatus")(fu // NOTE: the singleton `CodexProviderLive` Layer has been removed as part of // the per-instance-driver refactor. `CodexDriver.create()` builds a managed // snapshot per instance (each with its own `CodexSettings`) and hands the -// resulting `ServerProviderShape` back as `ProviderInstance.snapshot`. +// resulting `ManagedServerProvider` back as `ProviderInstance.snapshot`. // // The `makePendingCodexProvider` and `checkCodexProviderStatus` helpers are // re-exported for use by `CodexDriver`. diff --git a/apps/server/src/provider/Drivers/CodexDriver.ts b/apps/server/src/provider/Drivers/CodexDriver.ts index e27a278f34c3..660c0dad965a 100644 --- a/apps/server/src/provider/Drivers/CodexDriver.ts +++ b/apps/server/src/provider/Drivers/CodexDriver.ts @@ -3,7 +3,7 @@ * * A driver is a plain value (not a Context.Service) whose `create()` returns * one `ProviderInstance` bundling: - * - `snapshot` — the live `ServerProviderShape` for this instance; + * - `snapshot` — the live `ManagedServerProvider` for this instance; * - `adapter` — the Codex session/turn/approval runtime; * - `textGeneration` — commit/PR/branch/title generation via `codex exec`. * diff --git a/apps/server/src/provider/ProviderRegistry.ts b/apps/server/src/provider/ProviderRegistry.ts index 5f5120ee28bc..0e2c50e6670e 100644 --- a/apps/server/src/provider/ProviderRegistry.ts +++ b/apps/server/src/provider/ProviderRegistry.ts @@ -9,8 +9,8 @@ * * Historically this Layer composed four per-kind Live Layers * (`CodexProviderLive`, `ClaudeProviderLive`, …) that each exposed a - * `ServerProviderShape`. Those Lives were deleted during the driver / - * instance refactor — every driver now carries its `snapshot: ServerProviderShape` + * `ManagedServerProvider`. Those Lives were deleted during the driver / + * instance refactor — every driver now carries its `snapshot: ManagedServerProvider` * bundled onto the `ProviderInstance` the registry produces. * * Each configured instance (including multi-instance setups like diff --git a/apps/server/src/provider/builtInProviderCatalog.ts b/apps/server/src/provider/builtInProviderCatalog.ts index 115ad692ff5e..ac0924cf7e9e 100644 --- a/apps/server/src/provider/builtInProviderCatalog.ts +++ b/apps/server/src/provider/builtInProviderCatalog.ts @@ -1,6 +1,6 @@ import type { ProviderDriverKind, ProviderInstanceId, ServerProvider } from "@t3tools/contracts"; import type * as Stream from "effect/Stream"; -import type { ServerProviderShape } from "@t3tools/provider-core/server/snapshot"; +import type { ManagedServerProvider } from "@t3tools/provider-core/server/snapshot"; export type ProviderSnapshotSource = { /** @@ -11,7 +11,7 @@ export type ProviderSnapshotSource = { readonly instanceId: ProviderInstanceId; /** Driver implementation kind. */ readonly driverKind: ProviderDriverKind; - readonly getSnapshot: ServerProviderShape["getSnapshot"]; - readonly refresh: ServerProviderShape["refresh"]; + readonly getSnapshot: ManagedServerProvider["getSnapshot"]; + readonly refresh: ManagedServerProvider["refresh"]; readonly streamChanges: Stream.Stream; }; diff --git a/apps/server/src/sourceControl/SourceControlRepositoryService.ts b/apps/server/src/sourceControl/SourceControlRepositoryService.ts index 180d19ccee35..4a4d1fec6434 100644 --- a/apps/server/src/sourceControl/SourceControlRepositoryService.ts +++ b/apps/server/src/sourceControl/SourceControlRepositoryService.ts @@ -21,7 +21,7 @@ import { } from "@t3tools/contracts"; import * as ServerConfig from "../config.ts"; -import { expandHomePathWith } from "@t3tools/provider-core/server/pathExpansion"; +import { expandHomePath } from "@t3tools/provider-core/server/pathExpansion"; import { parseGitCloneProgressLine, type GitCloneProgressLine, @@ -213,7 +213,7 @@ export const make = Effect.gen(function* () { }); } - return path.resolve(expandHomePathWith(trimmed, path)); + return path.resolve(expandHomePath(trimmed)); }, ); diff --git a/apps/server/src/textGeneration/OpenCode2TextGeneration.test.ts b/apps/server/src/textGeneration/OpenCode2TextGeneration.test.ts index 424764cd8f37..31556dbdb8aa 100644 --- a/apps/server/src/textGeneration/OpenCode2TextGeneration.test.ts +++ b/apps/server/src/textGeneration/OpenCode2TextGeneration.test.ts @@ -4,13 +4,13 @@ import { ProviderInstanceId } from "@t3tools/contracts"; import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; -import { layerTestProviderHost } from "@t3tools/provider-testing/host"; +import * as TestProviderHost from "@t3tools/provider-testing/TestProviderHost"; import * as OpenCode2AdapterV2Testkit from "../orchestration-v2/Adapters/OpenCode2AdapterV2.testkit.ts"; import * as OpenCode2Server from "@t3tools/provider-opencode/server/v2/OpenCode2Server"; import * as OpenCode2TextGeneration from "@t3tools/provider-opencode/server/v2/textGeneration"; import { OPENCODE2_TITLE_GENERATION } from "./OpenCode2TextGeneration.fixture.ts"; -const layer = layerTestProviderHost().pipe(Layer.provideMerge(NodeServices.layer)); +const layer = TestProviderHost.layer().pipe(Layer.provideMerge(NodeServices.layer)); it.layer(layer)("OpenCode2TextGeneration", (it) => { it.effect("generates a title in a temporary session on the free tier and removes it", () => diff --git a/apps/server/src/workspace/WorkspaceEntries.ts b/apps/server/src/workspace/WorkspaceEntries.ts index 1a39c754b165..beac8d18a094 100644 --- a/apps/server/src/workspace/WorkspaceEntries.ts +++ b/apps/server/src/workspace/WorkspaceEntries.ts @@ -23,7 +23,7 @@ import { HostProcessPlatform } from "@t3tools/shared/hostProcess"; import { isExplicitRelativePath, isWindowsAbsolutePath } from "@t3tools/shared/path"; import { normalizeSearchQuery } from "@t3tools/shared/searchRanking"; -import { expandHomePathWith } from "@t3tools/provider-core/server/pathExpansion"; +import { expandHomePath } from "@t3tools/provider-core/server/pathExpansion"; import * as VcsProcess from "../vcs/VcsProcess.ts"; import * as WorkspacePaths from "./WorkspacePaths.ts"; import * as WorkspaceSearchIndex from "./WorkspaceSearchIndex.ts"; @@ -120,7 +120,7 @@ const resolveBrowseTarget = Effect.fn("WorkspaceEntries.resolveBrowseTarget")(fu } if (!isExplicitRelativePath(input.partialPath)) { - return path.resolve(expandHomePathWith(input.partialPath, path)); + return path.resolve(expandHomePath(input.partialPath)); } if (!input.cwd) { @@ -128,7 +128,7 @@ const resolveBrowseTarget = Effect.fn("WorkspaceEntries.resolveBrowseTarget")(fu partialPath: input.partialPath, }); } - return path.resolve(expandHomePathWith(input.cwd, path), input.partialPath); + return path.resolve(expandHomePath(input.cwd), input.partialPath); }); /** @public Service construction is part of the canonical Effect module API. */ diff --git a/apps/server/src/workspace/WorkspacePaths.ts b/apps/server/src/workspace/WorkspacePaths.ts index 82dedf3b159a..000a422bcdc8 100644 --- a/apps/server/src/workspace/WorkspacePaths.ts +++ b/apps/server/src/workspace/WorkspacePaths.ts @@ -14,7 +14,7 @@ import * as Layer from "effect/Layer"; import * as Path from "effect/Path"; import * as Schema from "effect/Schema"; -import { expandHomePathWith } from "@t3tools/provider-core/server/pathExpansion"; +import { expandHomePath } from "@t3tools/provider-core/server/pathExpansion"; export class WorkspaceRootNotExistsError extends Schema.TaggedError()( "WorkspaceRootNotExistsError", @@ -152,7 +152,7 @@ export const make = Effect.gen(function* () { const normalizeWorkspaceRoot: WorkspacePaths["Service"]["normalizeWorkspaceRoot"] = Effect.fn( "WorkspacePaths.normalizeWorkspaceRoot", )(function* (workspaceRoot, options) { - const normalizedWorkspaceRoot = path.resolve(expandHomePathWith(workspaceRoot.trim(), path)); + const normalizedWorkspaceRoot = path.resolve(expandHomePath(workspaceRoot.trim())); let workspaceStat = yield* statWorkspaceRoot( workspaceRoot, normalizedWorkspaceRoot, diff --git a/apps/server/src/worktreesDirectory.ts b/apps/server/src/worktreesDirectory.ts index 2496a593fd2d..ee7154577458 100644 --- a/apps/server/src/worktreesDirectory.ts +++ b/apps/server/src/worktreesDirectory.ts @@ -1,6 +1,6 @@ import type * as Path from "effect/Path"; -import { expandHomePathWith } from "@t3tools/provider-core/server/pathExpansion"; +import { expandHomePath } from "@t3tools/provider-core/server/pathExpansion"; /** * Directory new worktrees are created under: the `worktreesDirectory` @@ -15,7 +15,7 @@ export function resolveWorktreesDirectory( path: Path.Path, ): string | null { if (setting === "") return defaultDir; - const expanded = expandHomePathWith(setting, path); + const expanded = expandHomePath(setting); if (!path.isAbsolute(expanded)) return null; const resolved = path.resolve(expanded); return isFilesystemRoot(resolved, path) ? null : resolved; diff --git a/packages/provider-acp-registry/src/server/AcpRegistrySupport.test.ts b/packages/provider-acp-registry/src/server/AcpRegistrySupport.test.ts index 35cf0f199b9d..960602c8cc94 100644 --- a/packages/provider-acp-registry/src/server/AcpRegistrySupport.test.ts +++ b/packages/provider-acp-registry/src/server/AcpRegistrySupport.test.ts @@ -24,7 +24,7 @@ import * as TestClock from "effect/testing/TestClock"; import { ChildProcess, ChildProcessSpawner } from "effect/process"; import * as AcpRegistrySupport from "./AcpRegistrySupport.ts"; -import { layerTestProviderHost, TestProviderHostSettings } from "@t3tools/provider-testing/host"; +import * as TestProviderHost from "@t3tools/provider-testing/TestProviderHost"; const registryUrl = "https://registry.test/registry.json"; const archiveUrl = "https://registry.test/example-agent.bin"; @@ -60,7 +60,7 @@ function layerResolver( ) { return Layer.mergeAll( NodeServices.layer, - layerTestProviderHost().pipe(Layer.provide(NodeServices.layer)), + TestProviderHost.layer().pipe(Layer.provide(NodeServices.layer)), Layer.succeed(HostProcessPlatform, "linux"), Layer.succeed(HostProcessArchitecture, "x64"), Layer.succeed(HostProcessEnvironment, environment), @@ -776,7 +776,7 @@ describe("AcpRegistrySupport", () => { Effect.provide( Layer.mergeAll( NodeServices.layer, - layerTestProviderHost().pipe(Layer.provide(NodeServices.layer)), + TestProviderHost.layer().pipe(Layer.provide(NodeServices.layer)), ), ), Effect.provideService(HostProcessPlatform, "linux"), @@ -1374,7 +1374,7 @@ describe("AcpRegistrySupport", () => { toolsDir: `${cacheDir}/tools`, registryUrl, }); - const hostSettings = yield* TestProviderHostSettings; + const hostSettings = yield* TestProviderHost.TestProviderHostSettings; const instanceId = ProviderInstanceId.make("uninstall-reference"); yield* hostSettings.set({ ...DEFAULT_SERVER_SETTINGS, @@ -1492,7 +1492,7 @@ describe("AcpRegistrySupport", () => { const agentRoot = `${cacheDir}/tools/${agent.id}`; expect(yield* fileSystem.exists(agentRoot)).toBe(true); - const hostSettings = yield* TestProviderHostSettings; + const hostSettings = yield* TestProviderHost.TestProviderHostSettings; const instanceId = ProviderInstanceId.make("registry-reference"); yield* hostSettings.set({ ...DEFAULT_SERVER_SETTINGS, diff --git a/packages/provider-core/src/server/driver.ts b/packages/provider-core/src/server/driver.ts index df215d319742..18ed1aec1277 100644 --- a/packages/provider-core/src/server/driver.ts +++ b/packages/provider-core/src/server/driver.ts @@ -40,7 +40,7 @@ import type * as Scope from "effect/Scope"; import type * as ProviderAdapter from "./ProviderAdapter.ts"; import type { ProviderAuthController } from "./auth.ts"; import type { ProviderDriverError } from "./errors.ts"; -import type { ServerProviderShape } from "./snapshot.ts"; +import type { ManagedServerProvider } from "./snapshot.ts"; import type { ProviderTextGeneration } from "./textGeneration.ts"; /** @@ -85,7 +85,7 @@ export interface ProviderInstance { readonly displayName: string | undefined; readonly accentColor?: string | undefined; readonly enabled: boolean; - readonly snapshot: ServerProviderShape; + readonly snapshot: ManagedServerProvider; readonly snapshotForCwd?: ( cwd: string, ) => Effect.Effect; diff --git a/packages/provider-core/src/server/maintenanceResolver.test.ts b/packages/provider-core/src/server/maintenanceResolver.test.ts index 6d0dd9cae93f..23f0707a4a51 100644 --- a/packages/provider-core/src/server/maintenanceResolver.test.ts +++ b/packages/provider-core/src/server/maintenanceResolver.test.ts @@ -13,8 +13,8 @@ import * as Effect from "effect/Effect"; import * as Fiber from "effect/Fiber"; import * as Sink from "effect/Sink"; import * as Stream from "effect/Stream"; -import { HttpClient } from "effect/http"; -import { ChildProcessSpawner } from "effect/process"; +import * as HttpClient from "effect/http/HttpClient"; +import * as ChildProcessSpawner from "effect/process/ChildProcessSpawner"; import * as ProviderLatestVersions from "./ProviderLatestVersions.ts"; import { createProviderVersionAdvisory, diff --git a/packages/provider-core/src/server/maintenanceResolver.ts b/packages/provider-core/src/server/maintenanceResolver.ts index ddb26fc91673..fb985bd2011a 100644 --- a/packages/provider-core/src/server/maintenanceResolver.ts +++ b/packages/provider-core/src/server/maintenanceResolver.ts @@ -25,8 +25,10 @@ import * as Option from "effect/Option"; import * as Path from "effect/Path"; import * as Schema from "effect/Schema"; import * as Stream from "effect/Stream"; -import { HttpClient, HttpClientRequest } from "effect/http"; -import { ChildProcess, ChildProcessSpawner } from "effect/process"; +import * as HttpClient from "effect/http/HttpClient"; +import * as HttpClientRequest from "effect/http/HttpClientRequest"; +import * as ChildProcess from "effect/process/ChildProcess"; +import * as ChildProcessSpawner from "effect/process/ChildProcessSpawner"; import { collectUint8StreamText } from "./collectStreamText.ts"; import * as ProviderLatestVersions from "./ProviderLatestVersions.ts"; diff --git a/packages/provider-core/src/server/managedProvider.test.ts b/packages/provider-core/src/server/managedProvider.test.ts index dab08b246ce3..239508cec41c 100644 --- a/packages/provider-core/src/server/managedProvider.test.ts +++ b/packages/provider-core/src/server/managedProvider.test.ts @@ -14,7 +14,7 @@ import * as Layer from "effect/Layer"; import * as PubSub from "effect/PubSub"; import * as Ref from "effect/Ref"; import * as Stream from "effect/Stream"; -import { TestClock } from "effect/testing"; +import * as TestClock from "effect/testing/TestClock"; import * as ProviderHost from "./ProviderHost.ts"; import { makeManagedServerProvider } from "./managedProvider.ts"; diff --git a/packages/provider-core/src/server/managedProvider.ts b/packages/provider-core/src/server/managedProvider.ts index 946f9ad86217..b74788a38bad 100644 --- a/packages/provider-core/src/server/managedProvider.ts +++ b/packages/provider-core/src/server/managedProvider.ts @@ -17,7 +17,7 @@ import * as Semaphore from "effect/Semaphore"; import * as ProviderHost from "./ProviderHost.ts"; import { applyUsageLimitsUpdate, resolveUsageLimitsAfterProbe } from "./usageLimits.ts"; -import type { ServerProviderShape } from "./snapshot.ts"; +import type { ManagedServerProvider } from "./snapshot.ts"; interface ProviderSnapshotState { readonly snapshot: ServerProvider; @@ -38,7 +38,7 @@ function withUsageLimits( export const makeManagedServerProvider = Effect.fn("makeManagedServerProvider")(function* < Settings, >(input: { - readonly resolveMaintenance: ServerProviderShape["resolveMaintenance"]; + readonly resolveMaintenance: ManagedServerProvider["resolveMaintenance"]; readonly getSettings: Effect.Effect; readonly streamSettings: Stream.Stream; readonly haveSettingsChanged: (previous: Settings, next: Settings) => boolean; @@ -54,7 +54,7 @@ export const makeManagedServerProvider = Effect.fn("makeManagedServerProvider")( readonly refreshOnInterval?: boolean; readonly checkProviderOnSettingsChange?: (previous: Settings, next: Settings) => boolean; }): Effect.fn.Return< - ServerProviderShape, + ManagedServerProvider, ServerSettingsError, Scope.Scope | ProviderHost.ProviderHost > { @@ -181,7 +181,7 @@ export const makeManagedServerProvider = Effect.fn("makeManagedServerProvider")( * `usageLimits` on whatever snapshot is published and leave the enrichment * generation alone, so an in-flight enrichment still lands. */ - const applyUsageLimits: ServerProviderShape["applyUsageLimits"] = (update) => + const applyUsageLimits: ManagedServerProvider["applyUsageLimits"] = (update) => Effect.gen(function* () { const snapshotToPublish = yield* Ref.modify(snapshotStateRef, (state) => { const usageLimits = applyUsageLimitsUpdate({ @@ -289,5 +289,5 @@ export const makeManagedServerProvider = Effect.fn("makeManagedServerProvider")( get streamChanges() { return Stream.fromPubSub(changesPubSub); }, - } satisfies ServerProviderShape; + } satisfies ManagedServerProvider; }); diff --git a/packages/provider-core/src/server/pathExpansion.ts b/packages/provider-core/src/server/pathExpansion.ts index ec3f03faa5b8..bacdaece0b1c 100644 --- a/packages/provider-core/src/server/pathExpansion.ts +++ b/packages/provider-core/src/server/pathExpansion.ts @@ -2,8 +2,6 @@ import * as NodeOS from "node:os"; import * as NodePath from "node:path"; -import type * as Path from "effect/Path"; - /** * Expand a leading `~` (or `~/…`, `~\…`) in a user-supplied path to the * current user's home directory. Spawned processes don't get shell @@ -24,19 +22,3 @@ export function expandHomePath(value: string): string { } return value; } - -/** - * Same expansion as `expandHomePath`, but joins with a caller-supplied - * `Path.Path` service instead of `node:path`. Use this inside Effect code that - * already has `Path.Path` in context so the platform layer stays in control of - * separator handling. - */ -export function expandHomePathWith(value: string, path: Path.Path): string { - if (value === "~") { - return NodeOS.homedir(); - } - if (value.startsWith("~/") || value.startsWith("~\\")) { - return path.join(NodeOS.homedir(), value.slice(2)); - } - return value; -} diff --git a/packages/provider-core/src/server/snapshot.ts b/packages/provider-core/src/server/snapshot.ts index a6f2a985fd57..7832e944410f 100644 --- a/packages/provider-core/src/server/snapshot.ts +++ b/packages/provider-core/src/server/snapshot.ts @@ -3,7 +3,7 @@ import type * as Effect from "effect/Effect"; import type * as Stream from "effect/Stream"; import type { ProviderMaintenanceCapabilities } from "./maintenance.ts"; -export interface ServerProviderShape { +export interface ManagedServerProvider { /** * Ownership-derived update capabilities. Cached between reads; pass * `{ fresh: true }` before executing an update so it never trusts a diff --git a/packages/provider-core/src/server/snapshotProbe.test.ts b/packages/provider-core/src/server/snapshotProbe.test.ts index d15e8744cc0c..8ee272cee394 100644 --- a/packages/provider-core/src/server/snapshotProbe.test.ts +++ b/packages/provider-core/src/server/snapshotProbe.test.ts @@ -7,7 +7,8 @@ import * as Layer from "effect/Layer"; import * as PlatformError from "effect/PlatformError"; import * as Sink from "effect/Sink"; import * as Stream from "effect/Stream"; -import { ChildProcess, ChildProcessSpawner } from "effect/process"; +import * as ChildProcess from "effect/process/ChildProcess"; +import * as ChildProcessSpawner from "effect/process/ChildProcessSpawner"; import { isCommandMissingCause, diff --git a/packages/provider-core/src/server/snapshotProbe.ts b/packages/provider-core/src/server/snapshotProbe.ts index 1c440a6629e3..f61881ba0806 100644 --- a/packages/provider-core/src/server/snapshotProbe.ts +++ b/packages/provider-core/src/server/snapshotProbe.ts @@ -16,7 +16,8 @@ import * as Effect from "effect/Effect"; import * as PlatformError from "effect/PlatformError"; import * as Schema from "effect/Schema"; import * as Stream from "effect/Stream"; -import { ChildProcess, ChildProcessSpawner } from "effect/process"; +import * as ChildProcess from "effect/process/ChildProcess"; +import * as ChildProcessSpawner from "effect/process/ChildProcessSpawner"; import { readCustomModelEntries } from "@t3tools/shared/model"; import { HostProcessPlatform } from "@t3tools/shared/hostProcess"; import { createProviderVersionAdvisory } from "./maintenanceResolver.ts"; diff --git a/packages/provider-cursor/src/server/adapter.test.ts b/packages/provider-cursor/src/server/adapter.test.ts index ab03ec8ffbac..bbd813400da9 100644 --- a/packages/provider-cursor/src/server/adapter.test.ts +++ b/packages/provider-cursor/src/server/adapter.test.ts @@ -22,7 +22,7 @@ import * as Path from "effect/Path"; import * as Schema from "effect/Schema"; import * as Stream from "effect/Stream"; -import { layerTestProviderHost } from "@t3tools/provider-testing/host"; +import * as TestProviderHost from "@t3tools/provider-testing/TestProviderHost"; import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; import * as McpProviderSessions from "@t3tools/provider-core/server/McpProviderSessions"; import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter"; @@ -125,7 +125,7 @@ describe("CursorAdapterV2", () => { }), }), }), - Effect.provide(layerTestProviderHost({ cwd: workspace })), + Effect.provide(TestProviderHost.layer({ cwd: workspace })), ); const runtime = yield* adapter.openSession({ threadId, @@ -258,7 +258,7 @@ describe("CursorAdapterV2", () => { }), }), }), - Effect.provide(layerTestProviderHost({ cwd: workspace })), + Effect.provide(TestProviderHost.layer({ cwd: workspace })), ); const runtime = yield* adapter.openSession({ threadId, @@ -597,7 +597,7 @@ describe("CursorAdapterV2", () => { }), }), }), - Effect.provide(layerTestProviderHost({ cwd: workspace })), + Effect.provide(TestProviderHost.layer({ cwd: workspace })), ); const runtime = yield* adapter.openSession({ threadId, diff --git a/packages/provider-cursor/src/server/credentialStore.test.ts b/packages/provider-cursor/src/server/credentialStore.test.ts index 803324d9f979..10e5a6b8d06f 100644 --- a/packages/provider-cursor/src/server/credentialStore.test.ts +++ b/packages/provider-cursor/src/server/credentialStore.test.ts @@ -6,11 +6,11 @@ import * as FileSystem from "effect/FileSystem"; import * as Path from "effect/Path"; import * as ProviderHost from "@t3tools/provider-core/server/ProviderHost"; -import { layerTestProviderHost } from "@t3tools/provider-testing/host"; +import * as TestProviderHost from "@t3tools/provider-testing/TestProviderHost"; import * as Layer from "effect/Layer"; import { makeCursorCredentialStore } from "./credentialStore.ts"; -const layerHost = Layer.provideMerge(layerTestProviderHost(), NodeServices.layer); +const layerHost = Layer.provideMerge(TestProviderHost.layer(), NodeServices.layer); const legacyCredentials = { version: 1 as const, diff --git a/packages/provider-cursor/src/server/driver.test.ts b/packages/provider-cursor/src/server/driver.test.ts index cd46bfa464c9..ef24403a4b2c 100644 --- a/packages/provider-cursor/src/server/driver.test.ts +++ b/packages/provider-cursor/src/server/driver.test.ts @@ -11,7 +11,7 @@ import { HttpClient } from "effect/http"; import * as ChildProcessSpawner from "effect/process/ChildProcessSpawner"; import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers"; -import { layerTestProviderHost } from "@t3tools/provider-testing/host"; +import * as TestProviderHost from "@t3tools/provider-testing/TestProviderHost"; import { CursorDriver } from "./driver.ts"; import * as CursorAgentSdk from "./CursorAgentSdk.ts"; import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; @@ -19,7 +19,7 @@ import * as McpProviderSessions from "@t3tools/provider-core/server/McpProviderS import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter"; import { Cursor } from "./sdk.ts"; -const layerTest = layerTestProviderHost({ runBackgroundWork: false }).pipe( +const layerTest = TestProviderHost.layer({ runBackgroundWork: false }).pipe( Layer.provideMerge(NodeServices.layer), Layer.provideMerge(IdAllocator.layer), Layer.provideMerge(McpProviderSessions.layer), diff --git a/packages/provider-grok/src/server/driver.test.ts b/packages/provider-grok/src/server/driver.test.ts index fc1beba63990..9c27b9a2b092 100644 --- a/packages/provider-grok/src/server/driver.test.ts +++ b/packages/provider-grok/src/server/driver.test.ts @@ -11,13 +11,13 @@ import * as ChildProcessSpawner from "effect/process/ChildProcessSpawner"; import * as ProviderLatestVersions from "@t3tools/provider-core/server/ProviderLatestVersions"; import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers"; -import { layerTestProviderHost } from "@t3tools/provider-testing/host"; +import * as TestProviderHost from "@t3tools/provider-testing/TestProviderHost"; import { GrokDriver } from "./driver.ts"; import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; import * as McpProviderSessions from "@t3tools/provider-core/server/McpProviderSessions"; -const layerTest = layerTestProviderHost({ runBackgroundWork: false }).pipe( +const layerTest = TestProviderHost.layer({ runBackgroundWork: false }).pipe( Layer.provideMerge(NodeServices.layer), Layer.provideMerge(IdAllocator.layer), Layer.provideMerge(McpProviderSessions.layer), diff --git a/packages/provider-muse/src/server/adapter.test.ts b/packages/provider-muse/src/server/adapter.test.ts index 3bb26b7933e5..ae9ca1d351b2 100644 --- a/packages/provider-muse/src/server/adapter.test.ts +++ b/packages/provider-muse/src/server/adapter.test.ts @@ -30,7 +30,7 @@ import * as Queue from "effect/Queue"; import * as Schema from "effect/Schema"; import * as Stream from "effect/Stream"; -import { layerTestProviderHost } from "@t3tools/provider-testing/host"; +import * as TestProviderHost from "@t3tools/provider-testing/TestProviderHost"; import * as McpProviderSessions from "@t3tools/provider-core/server/McpProviderSessions"; import type { MuseItem } from "./protocol.ts"; import type { MuseSdkHost } from "./sdk.ts"; @@ -43,7 +43,7 @@ const testLayer = Layer.mergeAll( NodeServices.layer, IdAllocator.layer, McpProviderSessions.layer, - layerTestProviderHost().pipe(Layer.provide(NodeServices.layer)), + TestProviderHost.layer().pipe(Layer.provide(NodeServices.layer)), ); const MUSE_PROVIDER = ProviderDriverKind.make("muse"); const INSTANCE_ID = ProviderInstanceId.make("muse_work"); diff --git a/packages/provider-muse/src/server/driver.ts b/packages/provider-muse/src/server/driver.ts index 6e0f06deddb8..1d2922e54270 100644 --- a/packages/provider-muse/src/server/driver.ts +++ b/packages/provider-muse/src/server/driver.ts @@ -5,8 +5,8 @@ import * as Effect from "effect/Effect"; import * as FileSystem from "effect/FileSystem"; import * as Path from "effect/Path"; import * as Schema from "effect/Schema"; -import { HttpClient } from "effect/http"; -import { ChildProcessSpawner } from "effect/process"; +import * as HttpClient from "effect/http/HttpClient"; +import * as ChildProcessSpawner from "effect/process/ChildProcessSpawner"; import * as ProviderLatestVersions from "@t3tools/provider-core/server/ProviderLatestVersions"; import * as ProviderHost from "@t3tools/provider-core/server/ProviderHost"; diff --git a/packages/provider-muse/src/server/maintenance.test.ts b/packages/provider-muse/src/server/maintenance.test.ts index 244d99a89c70..869aa6dca2fe 100644 --- a/packages/provider-muse/src/server/maintenance.test.ts +++ b/packages/provider-muse/src/server/maintenance.test.ts @@ -3,7 +3,8 @@ import { expect, it } from "@effect/vitest"; import { ProviderDriverKind, ProviderInstanceId, type ServerProvider } from "@t3tools/contracts"; import * as Effect from "effect/Effect"; import * as FileSystem from "effect/FileSystem"; -import { HttpClient, HttpClientResponse } from "effect/http"; +import * as HttpClient from "effect/http/HttpClient"; +import * as HttpClientResponse from "effect/http/HttpClientResponse"; import { compareMuseVersions, diff --git a/packages/provider-muse/src/server/maintenance.ts b/packages/provider-muse/src/server/maintenance.ts index 107fa77bb606..776e9b0f3c23 100644 --- a/packages/provider-muse/src/server/maintenance.ts +++ b/packages/provider-muse/src/server/maintenance.ts @@ -3,7 +3,8 @@ import { compareSemverVersions } from "@t3tools/shared/semver"; import * as Effect from "effect/Effect"; import * as FileSystem from "effect/FileSystem"; import * as Schema from "effect/Schema"; -import { HttpClient, HttpClientRequest } from "effect/http"; +import * as HttpClient from "effect/http/HttpClient"; +import * as HttpClientRequest from "effect/http/HttpClientRequest"; import { enrichProviderSnapshotWithVersionAdvisory, diff --git a/packages/provider-muse/src/server/status.ts b/packages/provider-muse/src/server/status.ts index 2cb4e5360ff2..a0a51b732e0d 100644 --- a/packages/provider-muse/src/server/status.ts +++ b/packages/provider-muse/src/server/status.ts @@ -5,7 +5,7 @@ import * as DateTime from "effect/DateTime"; import * as Effect from "effect/Effect"; import * as Option from "effect/Option"; import * as Result from "effect/Result"; -import { ChildProcess } from "effect/process"; +import * as ChildProcess from "effect/process/ChildProcess"; import { createMuseSdkHost, makeMuseEnvironment } from "./sdk.ts"; import { parseMuseVersion } from "./maintenance.ts"; diff --git a/packages/provider-opencode/src/server/OpenCodeRuntime.environment.test.ts b/packages/provider-opencode/src/server/OpenCodeRuntime.environment.test.ts index fdd1ae3319bd..6476c7a7c3ce 100644 --- a/packages/provider-opencode/src/server/OpenCodeRuntime.environment.test.ts +++ b/packages/provider-opencode/src/server/OpenCodeRuntime.environment.test.ts @@ -12,7 +12,8 @@ import * as FileSystem from "effect/FileSystem"; import * as Layer from "effect/Layer"; import * as Path from "effect/Path"; import * as TestClock from "effect/testing/TestClock"; -import { FetchHttpClient, HttpClient } from "effect/http"; +import * as FetchHttpClient from "effect/http/FetchHttpClient"; +import * as HttpClient from "effect/http/HttpClient"; import { describe, expect, it } from "vite-plus/test"; import * as OpenCodeRuntime from "./OpenCodeRuntime.ts"; diff --git a/packages/provider-opencode/src/server/OpenCodeRuntime.ts b/packages/provider-opencode/src/server/OpenCodeRuntime.ts index 91e1a6e11d5e..4637ec831b21 100644 --- a/packages/provider-opencode/src/server/OpenCodeRuntime.ts +++ b/packages/provider-opencode/src/server/OpenCodeRuntime.ts @@ -28,7 +28,8 @@ import * as Result from "effect/Result"; import * as Scope from "effect/Scope"; import * as Schema from "effect/Schema"; import * as Stream from "effect/Stream"; -import { ChildProcess, ChildProcessSpawner } from "effect/process"; +import * as ChildProcess from "effect/process/ChildProcess"; +import * as ChildProcessSpawner from "effect/process/ChildProcessSpawner"; import { signalProcessGroup } from "@t3tools/provider-core/server/processGroup"; import { isWindowsCommandNotFound } from "@t3tools/provider-core/server/snapshotProbe"; @@ -227,68 +228,71 @@ const decodeOpenCodeSkillsCliOutputExit = Schema.decodeUnknownExit( Schema.fromJsonString(Schema.Array(OpenCodeSkillSchema)), ); -export interface OpenCodeRuntimeShape { - /** - * Spawns a local OpenCode server process. Its lifetime is bound to the caller's - * `Scope.Scope` — the child is killed automatically when that scope closes. - * Consumers that want a long-lived server must create and hold a scope explicitly - * (see {@link Scope.make}) and close it when done. - */ - readonly startOpenCodeServerProcess: (input: { - readonly binaryPath: string; - readonly directory: string; - readonly serverPassword?: string; - readonly environment?: NodeJS.ProcessEnv; - readonly port?: number; - readonly hostname?: string; - readonly timeoutMs?: number; - /** Checks the listening server and returns its version. Defaults to the 1.x health check. */ - readonly verify?: (url: string) => Effect.Effect; - }) => Effect.Effect; - /** - * Returns a handle to either an externally-managed OpenCode server (when - * `serverUrl` is provided — no lifetime is attached to the caller's scope) or a - * freshly spawned local server whose lifetime is bound to the caller's scope. - */ - readonly connectToOpenCodeServer: (input: { - readonly binaryPath: string; - readonly directory: string; - readonly serverUrl?: string | null; - readonly serverPassword?: string; - readonly environment?: NodeJS.ProcessEnv; - readonly port?: number; - readonly hostname?: string; - readonly timeoutMs?: number; - }) => Effect.Effect; - readonly runOpenCodeCommand: (input: { - readonly binaryPath: string; - readonly args: ReadonlyArray; - readonly environment?: NodeJS.ProcessEnv; - readonly cwd?: string; - readonly maxOutputBytes?: number; - }) => Effect.Effect; - readonly createOpenCodeSdkClient: (input: { - readonly baseUrl: string; - readonly directory: string; - readonly serverPassword?: string; - }) => OpencodeClient; - readonly loadOpenCodeInventory: ( - client: OpencodeClient, - ) => Effect.Effect; - readonly loadOpenCodeSkills: ( - client: OpencodeClient, - ) => Effect.Effect, OpenCodeRuntimeError>; - readonly loadInventoryFromCli: (input: { - readonly binaryPath: string; - readonly cwd: string; - readonly environment?: NodeJS.ProcessEnv; - }) => Effect.Effect; - readonly loadSkillsFromCli: (input: { - readonly binaryPath: string; - readonly cwd: string; - readonly environment?: NodeJS.ProcessEnv; - }) => Effect.Effect, OpenCodeRuntimeError>; -} +export class OpenCodeRuntime extends Context.Service< + OpenCodeRuntime, + { + /** + * Spawns a local OpenCode server process. Its lifetime is bound to the caller's + * `Scope.Scope` — the child is killed automatically when that scope closes. + * Consumers that want a long-lived server must create and hold a scope explicitly + * (see {@link Scope.make}) and close it when done. + */ + readonly startOpenCodeServerProcess: (input: { + readonly binaryPath: string; + readonly directory: string; + readonly serverPassword?: string; + readonly environment?: NodeJS.ProcessEnv; + readonly port?: number; + readonly hostname?: string; + readonly timeoutMs?: number; + /** Checks the listening server and returns its version. Defaults to the 1.x health check. */ + readonly verify?: (url: string) => Effect.Effect; + }) => Effect.Effect; + /** + * Returns a handle to either an externally-managed OpenCode server (when + * `serverUrl` is provided — no lifetime is attached to the caller's scope) or a + * freshly spawned local server whose lifetime is bound to the caller's scope. + */ + readonly connectToOpenCodeServer: (input: { + readonly binaryPath: string; + readonly directory: string; + readonly serverUrl?: string | null; + readonly serverPassword?: string; + readonly environment?: NodeJS.ProcessEnv; + readonly port?: number; + readonly hostname?: string; + readonly timeoutMs?: number; + }) => Effect.Effect; + readonly runOpenCodeCommand: (input: { + readonly binaryPath: string; + readonly args: ReadonlyArray; + readonly environment?: NodeJS.ProcessEnv; + readonly cwd?: string; + readonly maxOutputBytes?: number; + }) => Effect.Effect; + readonly createOpenCodeSdkClient: (input: { + readonly baseUrl: string; + readonly directory: string; + readonly serverPassword?: string; + }) => OpencodeClient; + readonly loadOpenCodeInventory: ( + client: OpencodeClient, + ) => Effect.Effect; + readonly loadOpenCodeSkills: ( + client: OpencodeClient, + ) => Effect.Effect, OpenCodeRuntimeError>; + readonly loadInventoryFromCli: (input: { + readonly binaryPath: string; + readonly cwd: string; + readonly environment?: NodeJS.ProcessEnv; + }) => Effect.Effect; + readonly loadSkillsFromCli: (input: { + readonly binaryPath: string; + readonly cwd: string; + readonly environment?: NodeJS.ProcessEnv; + }) => Effect.Effect, OpenCodeRuntimeError>; + } +>()("@t3tools/provider-opencode/server/OpenCodeRuntime") {} function parseServerUrlFromOutput(output: string): string | null { for (const line of output.split("\n")) { @@ -596,7 +600,7 @@ const makeOpenCodeRuntime = Effect.gen(function* () { const resolveCommand = (command: string, args: ReadonlyArray, env?: NodeJS.ProcessEnv) => resolveSpawnCommand(command, args, env ? { env } : {}); - const runOpenCodeCommand: OpenCodeRuntimeShape["runOpenCodeCommand"] = (input) => + const runOpenCodeCommand: OpenCodeRuntime["Service"]["runOpenCodeCommand"] = (input) => Effect.gen(function* () { const spawnCommand = yield* resolveCommand(input.binaryPath, input.args, input.environment); const child = yield* spawner.spawn( @@ -651,7 +655,7 @@ const makeOpenCodeRuntime = Effect.gen(function* () { ), ); - const createOpenCodeSdkClient: OpenCodeRuntimeShape["createOpenCodeSdkClient"] = (input) => + const createOpenCodeSdkClient: OpenCodeRuntime["Service"]["createOpenCodeSdkClient"] = (input) => createOpencodeClient({ baseUrl: input.baseUrl, directory: input.directory, @@ -665,7 +669,9 @@ const makeOpenCodeRuntime = Effect.gen(function* () { throwOnError: true, }); - const startOpenCodeServerProcess: OpenCodeRuntimeShape["startOpenCodeServerProcess"] = (input) => + const startOpenCodeServerProcess: OpenCodeRuntime["Service"]["startOpenCodeServerProcess"] = ( + input, + ) => Effect.gen(function* () { // Bind this server's lifetime to the caller's scope. When the caller's // scope closes, the spawned child is killed and all associated fibers @@ -872,7 +878,9 @@ const makeOpenCodeRuntime = Effect.gen(function* () { } satisfies OpenCodeServerProcess; }); - const connectToOpenCodeServer: OpenCodeRuntimeShape["connectToOpenCodeServer"] = (input) => { + const connectToOpenCodeServer: OpenCodeRuntime["Service"]["connectToOpenCodeServer"] = ( + input, + ) => { const serverUrl = input.serverUrl?.trim(); if (serverUrl) { const serverPassword = resolveOpenCodeServerPassword({ @@ -937,7 +945,7 @@ const makeOpenCodeRuntime = Effect.gen(function* () { Effect.orElseSucceed((): ReadonlyArray => []), ); - const loadOpenCodeSkills: OpenCodeRuntimeShape["loadOpenCodeSkills"] = (client) => + const loadOpenCodeSkills: OpenCodeRuntime["Service"]["loadOpenCodeSkills"] = (client) => runOpenCodeSdk("app.skills", (signal) => client.app.skills(undefined, { signal })).pipe( Effect.map((result) => (result.data ?? []).map((skill) => ({ @@ -950,7 +958,7 @@ const makeOpenCodeRuntime = Effect.gen(function* () { const loadSkills = (client: OpencodeClient) => loadOpenCodeSkills(client).pipe(Effect.orElseSucceed((): ReadonlyArray => [])); - const loadOpenCodeInventory: OpenCodeRuntimeShape["loadOpenCodeInventory"] = (client) => + const loadOpenCodeInventory: OpenCodeRuntime["Service"]["loadOpenCodeInventory"] = (client) => Effect.all( [ loadProviders(client), @@ -970,7 +978,7 @@ const makeOpenCodeRuntime = Effect.gen(function* () { })), ); - const loadInventoryFromCli: OpenCodeRuntimeShape["loadInventoryFromCli"] = (input) => + const loadInventoryFromCli: OpenCodeRuntime["Service"]["loadInventoryFromCli"] = (input) => Effect.gen(function* () { const env = input.environment !== undefined ? { environment: input.environment } : ({} as {}); const commandContext = { cwd: input.cwd, ...env }; @@ -1070,7 +1078,7 @@ const makeOpenCodeRuntime = Effect.gen(function* () { }; }); - const loadSkillsFromCli: OpenCodeRuntimeShape["loadSkillsFromCli"] = (input) => + const loadSkillsFromCli: OpenCodeRuntime["Service"]["loadSkillsFromCli"] = (input) => runOpenCodeCommand({ binaryPath: input.binaryPath, args: ["debug", "skill"], @@ -1099,13 +1107,9 @@ const makeOpenCodeRuntime = Effect.gen(function* () { loadOpenCodeSkills, loadInventoryFromCli, loadSkillsFromCli, - } satisfies OpenCodeRuntimeShape; + } satisfies OpenCodeRuntime["Service"]; }); -export class OpenCodeRuntime extends Context.Service()( - "@t3tools/provider-opencode/server/OpenCodeRuntime", -) {} - export const layer = Layer.effect(OpenCodeRuntime, makeOpenCodeRuntime).pipe( Layer.provide(NetService.layer), ); diff --git a/packages/provider-opencode/src/server/OpenCodeServerLedger.test.ts b/packages/provider-opencode/src/server/OpenCodeServerLedger.test.ts index a805572e8aac..90b0c55c157f 100644 --- a/packages/provider-opencode/src/server/OpenCodeServerLedger.test.ts +++ b/packages/provider-opencode/src/server/OpenCodeServerLedger.test.ts @@ -12,7 +12,7 @@ import * as FileSystem from "effect/FileSystem"; import * as Layer from "effect/Layer"; import * as Path from "effect/Path"; import * as Scope from "effect/Scope"; -import { ChildProcessSpawner } from "effect/process"; +import * as ChildProcessSpawner from "effect/process/ChildProcessSpawner"; import * as OpenCodeRuntime from "./OpenCodeRuntime.ts"; import * as OpenCodeServerLedger from "./OpenCodeServerLedger.ts"; diff --git a/packages/provider-opencode/src/server/OpenCodeServerLedger.ts b/packages/provider-opencode/src/server/OpenCodeServerLedger.ts index 0c721cf6ffd5..43774e245232 100644 --- a/packages/provider-opencode/src/server/OpenCodeServerLedger.ts +++ b/packages/provider-opencode/src/server/OpenCodeServerLedger.ts @@ -6,7 +6,8 @@ import * as Layer from "effect/Layer"; import * as Option from "effect/Option"; import * as Path from "effect/Path"; import * as Schema from "effect/Schema"; -import { ChildProcess, ChildProcessSpawner } from "effect/process"; +import * as ChildProcess from "effect/process/ChildProcess"; +import * as ChildProcessSpawner from "effect/process/ChildProcessSpawner"; import { writeFileStringAtomically } from "@t3tools/shared/atomicWrite"; import { signalProcessGroup } from "@t3tools/provider-core/server/processGroup"; diff --git a/packages/provider-opencode/src/server/OpenCodeServerOwner.test.ts b/packages/provider-opencode/src/server/OpenCodeServerOwner.test.ts index f1f9881fdc29..f9df8c5a1de0 100644 --- a/packages/provider-opencode/src/server/OpenCodeServerOwner.test.ts +++ b/packages/provider-opencode/src/server/OpenCodeServerOwner.test.ts @@ -24,7 +24,7 @@ const makeRuntime = Effect.gen(function* () { const failNextStart = yield* Ref.make(false); const started = yield* Deferred.make(); const closed = yield* Deferred.make(); - const runtime: OpenCodeRuntime.OpenCodeRuntimeShape = { + const runtime: OpenCodeRuntime.OpenCodeRuntime["Service"] = { startOpenCodeServerProcess: () => Effect.gen(function* () { if (yield* Ref.getAndSet(failNextStart, false)) { @@ -115,7 +115,7 @@ it.effect("invalidates an exited process so the next borrower starts a new one", const starts = yield* Ref.make(0); const processExits: Array> = []; const processClosed = yield* Deferred.make(); - const runtime: OpenCodeRuntime.OpenCodeRuntimeShape = { + const runtime: OpenCodeRuntime.OpenCodeRuntime["Service"] = { startOpenCodeServerProcess: () => Effect.gen(function* () { const index = yield* Ref.updateAndGet(starts, (count) => count + 1); @@ -165,7 +165,7 @@ it.effect("replaces a dead cached process before its exit watcher runs", () => const starts = yield* Ref.make(0); const closes = yield* Ref.make(0); const processRunning: Array> = []; - const runtime: OpenCodeRuntime.OpenCodeRuntimeShape = { + const runtime: OpenCodeRuntime.OpenCodeRuntime["Service"] = { startOpenCodeServerProcess: () => Effect.gen(function* () { const index = yield* Ref.updateAndGet(starts, (count) => count + 1); @@ -214,7 +214,7 @@ it.effect("cleans up an interrupted startup and allows a retry", () => const starts = yield* Ref.make(0); const firstStartEntered = yield* Deferred.make(); const firstStartClosed = yield* Deferred.make(); - const runtime: OpenCodeRuntime.OpenCodeRuntimeShape = { + const runtime: OpenCodeRuntime.OpenCodeRuntime["Service"] = { startOpenCodeServerProcess: () => Effect.gen(function* () { const index = yield* Ref.updateAndGet(starts, (count) => count + 1); diff --git a/packages/provider-opencode/src/server/adapter.test.ts b/packages/provider-opencode/src/server/adapter.test.ts index 53b0c54fad44..c2ec42bd16b5 100644 --- a/packages/provider-opencode/src/server/adapter.test.ts +++ b/packages/provider-opencode/src/server/adapter.test.ts @@ -32,7 +32,7 @@ import * as Stream from "effect/Stream"; import * as TestClock from "effect/testing/TestClock"; import * as ProviderHost from "@t3tools/provider-core/server/ProviderHost"; -import { layerTestProviderHost } from "@t3tools/provider-testing/host"; +import * as TestProviderHost from "@t3tools/provider-testing/TestProviderHost"; import type * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers"; import * as OpenCodeRuntime from "./OpenCodeRuntime.ts"; import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; @@ -165,7 +165,7 @@ const makeOpenCodeRuntimeHarness = Effect.fn("makeOpenCodeRuntimeHarness")(funct Effect.provideService(OpenCodeRuntime.OpenCodeRuntime, { connectToOpenCodeServer: () => Effect.succeed({ url: "http://test.invalid", external: true }), createOpenCodeSdkClient: () => client, - } as unknown as OpenCodeRuntime.OpenCodeRuntimeShape), + } as unknown as OpenCodeRuntime.OpenCodeRuntime["Service"]), Effect.provideService(ProviderHost.ProviderHost, { paths: { cwd: "/workspace" }, resolveAttachmentPath: () => null, @@ -1538,7 +1538,7 @@ describe("OpenCodeAdapterV2", () => { connectToOpenCodeServer: () => Effect.succeed({ url: "http://test.invalid", external: true }), createOpenCodeSdkClient: () => client, - } as unknown as OpenCodeRuntime.OpenCodeRuntimeShape), + } as unknown as OpenCodeRuntime.OpenCodeRuntime["Service"]), Effect.provideService(ProviderHost.ProviderHost, { paths: { cwd: "/workspace" }, resolveAttachmentPath: () => null, @@ -1835,7 +1835,7 @@ describe("OpenCodeAdapterV2", () => { connectToOpenCodeServer: () => Effect.succeed({ url: "http://test.invalid", external: true }), createOpenCodeSdkClient: () => client, - } as unknown as OpenCodeRuntime.OpenCodeRuntimeShape), + } as unknown as OpenCodeRuntime.OpenCodeRuntime["Service"]), Effect.provideService(ProviderHost.ProviderHost, { paths: { cwd: "/workspace" }, resolveAttachmentPath: () => null, @@ -2507,7 +2507,7 @@ describe("OpenCodeAdapterV2", () => { }, } as unknown as OpencodeClient; const unused = (operation: string) => () => Effect.die(`${operation} is not used`); - const runtime: OpenCodeRuntime.OpenCodeRuntimeShape = { + const runtime: OpenCodeRuntime.OpenCodeRuntime["Service"] = { startOpenCodeServerProcess: unused("startOpenCodeServerProcess"), connectToOpenCodeServer: () => Effect.succeed({ @@ -2585,7 +2585,7 @@ describe("OpenCodeAdapterV2", () => { Layer.mergeAll( IdAllocator.layer, McpProviderSessions.layer, - layerTestProviderHost().pipe(Layer.provide(NodeServices.layer)), + TestProviderHost.layer().pipe(Layer.provide(NodeServices.layer)), ), ), ), diff --git a/packages/provider-opencode/src/server/adapter.ts b/packages/provider-opencode/src/server/adapter.ts index ac94e3ecd297..96d535070294 100644 --- a/packages/provider-opencode/src/server/adapter.ts +++ b/packages/provider-opencode/src/server/adapter.ts @@ -48,6 +48,7 @@ import * as Layer from "effect/Layer"; import * as Option from "effect/Option"; import * as Queue from "effect/Queue"; import * as Random from "effect/Random"; +import * as Ref from "effect/Ref"; import * as Schema from "effect/Schema"; import * as Semaphore from "effect/Semaphore"; import * as Scope from "effect/Scope"; @@ -88,20 +89,15 @@ export const OPENCODE_DEFAULT_INSTANCE_ID = defaultInstanceIdForDriver(OPENCODE_ export const OPENCODE_SDK_PROTOCOL = "opencode-sdk.sse" as const; const DEFAULT_OPENCODE_SETTINGS = Schema.decodeSync(OpenCodeSettings)({}); -let openCodeMessageIdEpochMillis = -1; -let openCodeMessageIdCounter = 0; - -const makeOpenCodeMessageId = Effect.fnUntraced(function* () { +const makeOpenCodeMessageId = Effect.fnUntraced(function* ( + clock: Ref.Ref<{ readonly epochMillis: number; readonly counter: number }>, +) { const epochMillis = DateTime.toEpochMillis(yield* DateTime.now); - if (epochMillis !== openCodeMessageIdEpochMillis) { - openCodeMessageIdEpochMillis = epochMillis; - openCodeMessageIdCounter = 0; - } - openCodeMessageIdCounter += 1; - const encodedTime = BigInt.asUintN( - 48, - BigInt(epochMillis) * 0x1000n + BigInt(openCodeMessageIdCounter), - ) + const counter = yield* Ref.modify(clock, (previous) => { + const next = previous.epochMillis === epochMillis ? previous.counter + 1 : 1; + return [next, { epochMillis, counter: next }] as const; + }); + const encodedTime = BigInt.asUintN(48, BigInt(epochMillis) * 0x1000n + BigInt(counter)) .toString(16) .padStart(12, "0"); const alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; @@ -948,6 +944,7 @@ export const makeOpenCodeAdapterV2 = Effect.fn("makeOpenCodeAdapterV2")(function const idAllocator = yield* IdAllocator.IdAllocatorV2; const host = yield* ProviderHost.ProviderHost; const mcpSessions = yield* McpProviderSessions.McpProviderSessions; + const messageIdClock = yield* Ref.make({ epochMillis: -1, counter: 0 }); return ProviderAdapter.ProviderAdapterV2.of({ instanceId: options.instanceId, @@ -3170,7 +3167,7 @@ export const makeOpenCodeAdapterV2 = Effect.fn("makeOpenCodeAdapterV2")(function startedAt, completedAt: null, }; - const admissionMessageId = yield* makeOpenCodeMessageId(); + const admissionMessageId = yield* makeOpenCodeMessageId(messageIdClock); // No Effect may be yielded between this check and installing the // turn. If the event stream ended while IDs were being prepared, // registering afterward would leave a running turn that the EOF @@ -3369,7 +3366,7 @@ export const makeOpenCodeAdapterV2 = Effect.fn("makeOpenCodeAdapterV2")(function ...files, ]; turn.admissionGeneration = state.nextAdmissionGeneration++; - turn.admissionMessageId = yield* makeOpenCodeMessageId(); + turn.admissionMessageId = yield* makeOpenCodeMessageId(messageIdClock); turn.admissionPending = true; turn.admissionAccepted = false; turn.admissionMessageObserved = false; diff --git a/packages/provider-opencode/src/server/driver.test.ts b/packages/provider-opencode/src/server/driver.test.ts index 0e82fed63f88..2e4225f8d374 100644 --- a/packages/provider-opencode/src/server/driver.test.ts +++ b/packages/provider-opencode/src/server/driver.test.ts @@ -12,8 +12,8 @@ import * as Effect from "effect/Effect"; import * as Fiber from "effect/Fiber"; import * as Layer from "effect/Layer"; import * as TestClock from "effect/testing/TestClock"; -import { HttpClient } from "effect/http"; -import { ChildProcessSpawner } from "effect/process"; +import * as HttpClient from "effect/http/HttpClient"; +import * as ChildProcessSpawner from "effect/process/ChildProcessSpawner"; import * as ProviderLatestVersions from "@t3tools/provider-core/server/ProviderLatestVersions"; import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; @@ -27,7 +27,7 @@ import { replayOpenCodeServer, } from "./probeResponses.fixture.ts"; import { OpenCodeDriver, openCodeUpdateFor } from "./driver.ts"; -import { layerTestProviderHost } from "@t3tools/provider-testing/host"; +import * as TestProviderHost from "@t3tools/provider-testing/TestProviderHost"; const serverStarts: Array = []; const reachedServer = (operation: string) => @@ -46,13 +46,13 @@ const openCode2Runtime = { runOpenCodeCommand: () => Effect.succeed({ stdout: "opencode v2.0.18\n", stderr: "", code: 0 }), startOpenCodeServerProcess: () => reachedServer("start"), connectToOpenCodeServer: () => reachedServer("connect"), -} as unknown as OpenCodeRuntime.OpenCodeRuntimeShape; +} as unknown as OpenCodeRuntime.OpenCodeRuntime["Service"]; const layer = Layer.mergeAll( IdAllocator.layer, McpProviderSessions.layer, ProviderLatestVersions.layer, - layerTestProviderHost(), + TestProviderHost.layer(), Layer.succeed( ProviderEventLoggers.ProviderEventLoggers, ProviderEventLoggers.NoOpProviderEventLoggers, @@ -211,12 +211,12 @@ const changingRuntime = { ), startOpenCodeServerProcess: () => reachedServer("start"), connectToOpenCodeServer: () => reachedServer("connect"), -} as unknown as OpenCodeRuntime.OpenCodeRuntimeShape; +} as unknown as OpenCodeRuntime.OpenCodeRuntime["Service"]; const layerUpdate = Layer.mergeAll( IdAllocator.layer, McpProviderSessions.layer, ProviderLatestVersions.layer, - layerTestProviderHost(), + TestProviderHost.layer(), Layer.succeed( ProviderEventLoggers.ProviderEventLoggers, ProviderEventLoggers.NoOpProviderEventLoggers, diff --git a/packages/provider-opencode/src/server/driver.ts b/packages/provider-opencode/src/server/driver.ts index 53a5f05424f3..3834ec73534b 100644 --- a/packages/provider-opencode/src/server/driver.ts +++ b/packages/provider-opencode/src/server/driver.ts @@ -22,8 +22,8 @@ import * as FileSystem from "effect/FileSystem"; import * as Path from "effect/Path"; import * as Schema from "effect/Schema"; import * as Stream from "effect/Stream"; -import { HttpClient } from "effect/http"; -import { ChildProcessSpawner } from "effect/process"; +import * as HttpClient from "effect/http/HttpClient"; +import * as ChildProcessSpawner from "effect/process/ChildProcessSpawner"; import * as OpenCode2TextGeneration from "./v2/textGeneration.ts"; import { makeOpenCodeTextGeneration } from "./textGeneration.ts"; diff --git a/packages/provider-opencode/src/server/probeResponses.fixture.ts b/packages/provider-opencode/src/server/probeResponses.fixture.ts index 0eff526acd4f..3dbf954b2f20 100644 --- a/packages/provider-opencode/src/server/probeResponses.fixture.ts +++ b/packages/provider-opencode/src/server/probeResponses.fixture.ts @@ -3,7 +3,8 @@ * `HttpClient` so the version probe runs against real bytes. */ import * as Effect from "effect/Effect"; -import { HttpClient, HttpClientResponse } from "effect/http"; +import * as HttpClient from "effect/http/HttpClient"; +import * as HttpClientResponse from "effect/http/HttpClientResponse"; interface RecordedResponse { readonly status: number; diff --git a/packages/provider-opencode/src/server/status.test.ts b/packages/provider-opencode/src/server/status.test.ts index f062330ad733..c84dbdf3f894 100644 --- a/packages/provider-opencode/src/server/status.test.ts +++ b/packages/provider-opencode/src/server/status.test.ts @@ -9,7 +9,8 @@ import * as FileSystem from "effect/FileSystem"; import * as Layer from "effect/Layer"; import * as Schema from "effect/Schema"; import * as TestClock from "effect/testing/TestClock"; -import { HttpClient, HttpClientResponse } from "effect/http"; +import * as HttpClient from "effect/http/HttpClient"; +import * as HttpClientResponse from "effect/http/HttpClientResponse"; import { beforeEach } from "vite-plus/test"; import { OpenCodeSettings } from "../settings.ts"; @@ -161,7 +162,7 @@ it.effect("keeps Go entitlement absence distinct from failed or malformed usage * are deleted. The snapshot-producing logic they wrapped now lives in the * standalone `checkOpenCodeProviderStatus(settings, cwd)` Effect, which * drivers call directly when building their per-instance snapshot - * `ServerProviderShape`. Tests mirror that shape: build a settings payload, + * `ManagedServerProvider`. Tests mirror that shape: build a settings payload, * invoke the check, assert on the returned snapshot. */ @@ -202,7 +203,7 @@ const runtimeMock = { }, }; -const OpenCodeRuntimeTestDouble: OpenCodeRuntime.OpenCodeRuntimeShape = { +const OpenCodeRuntimeTestDouble: OpenCodeRuntime.OpenCodeRuntime["Service"] = { startOpenCodeServerProcess: ({ serverPassword, environment }) => Effect.gen(function* () { yield* Effect.addFinalizer(() => @@ -264,7 +265,7 @@ const OpenCodeRuntimeTestDouble: OpenCodeRuntime.OpenCodeRuntimeShape = { createOpenCodeSdkClient: (input) => { runtimeMock.state.sdkClientInputs.push(input); return {} as unknown as ReturnType< - OpenCodeRuntime.OpenCodeRuntimeShape["createOpenCodeSdkClient"] + OpenCodeRuntime.OpenCodeRuntime["Service"]["createOpenCodeSdkClient"] >; }, loadOpenCodeInventory: () => diff --git a/packages/provider-opencode/src/server/textGeneration.test.ts b/packages/provider-opencode/src/server/textGeneration.test.ts index db93d27d2747..48f85e60b6cb 100644 --- a/packages/provider-opencode/src/server/textGeneration.test.ts +++ b/packages/provider-opencode/src/server/textGeneration.test.ts @@ -10,7 +10,7 @@ import * as TestClock from "effect/testing/TestClock"; import * as NetService from "@t3tools/shared/Net"; import { beforeEach, expect } from "vite-plus/test"; -import { layerTestProviderHost } from "@t3tools/provider-testing/host"; +import * as TestProviderHost from "@t3tools/provider-testing/TestProviderHost"; import * as OpenCodeRuntime from "./OpenCodeRuntime.ts"; import * as OpenCodeServerOwner from "./OpenCodeServerOwner.ts"; import * as OpenCodeTextGeneration from "./textGeneration.ts"; @@ -47,7 +47,7 @@ const runtimeMock = { }, }; -const OpenCodeRuntimeTestDouble: OpenCodeRuntime.OpenCodeRuntimeShape = { +const OpenCodeRuntimeTestDouble: OpenCodeRuntime.OpenCodeRuntime["Service"] = { startOpenCodeServerProcess: ({ binaryPath, serverPassword, environment }) => Effect.gen(function* () { const index = runtimeMock.state.startCalls.length + 1; @@ -128,7 +128,9 @@ const OpenCodeRuntimeTestDouble: OpenCodeRuntime.OpenCodeRuntimeShape = { ); }, }, - }) as unknown as ReturnType, + }) as unknown as ReturnType< + OpenCodeRuntime.OpenCodeRuntime["Service"]["createOpenCodeSdkClient"] + >, loadOpenCodeInventory: () => Effect.fail( new OpenCodeRuntime.OpenCodeRuntimeError({ @@ -167,7 +169,7 @@ const layerOpenCodeTextGenerationTest = Layer.succeed( OpenCodeRuntime.OpenCodeRuntime, OpenCodeRuntimeTestDouble, ).pipe( - Layer.provideMerge(layerTestProviderHost()), + Layer.provideMerge(TestProviderHost.layer()), Layer.provideMerge(NetService.layer), Layer.provideMerge(NodeServices.layer), ); @@ -176,7 +178,7 @@ const layerOpenCodeTextGenerationExistingServerTest = Layer.succeed( OpenCodeRuntime.OpenCodeRuntime, OpenCodeRuntimeTestDouble, ).pipe( - Layer.provideMerge(layerTestProviderHost()), + Layer.provideMerge(TestProviderHost.layer()), Layer.provideMerge(NetService.layer), Layer.provideMerge(NodeServices.layer), ); diff --git a/packages/provider-opencode/src/server/usageLimits.ts b/packages/provider-opencode/src/server/usageLimits.ts index 2192e75a7b3d..3c21536b8798 100644 --- a/packages/provider-opencode/src/server/usageLimits.ts +++ b/packages/provider-opencode/src/server/usageLimits.ts @@ -9,7 +9,9 @@ import * as Option from "effect/Option"; import * as Path from "effect/Path"; import * as Schema from "effect/Schema"; import * as Hex from "effect/encoding/Hex"; -import { HttpClient, HttpClientRequest, HttpClientResponse } from "effect/http"; +import * as HttpClient from "effect/http/HttpClient"; +import * as HttpClientRequest from "effect/http/HttpClientRequest"; +import * as HttpClientResponse from "effect/http/HttpClientResponse"; import { clampPercent, diff --git a/packages/provider-opencode/src/server/v2/OpenCode2Client.live.test.ts b/packages/provider-opencode/src/server/v2/OpenCode2Client.live.test.ts index 21c01be67185..a1d83d3d8eec 100644 --- a/packages/provider-opencode/src/server/v2/OpenCode2Client.live.test.ts +++ b/packages/provider-opencode/src/server/v2/OpenCode2Client.live.test.ts @@ -20,8 +20,9 @@ import * as Layer from "effect/Layer"; import * as Option from "effect/Option"; import * as Path from "effect/Path"; import * as Stream from "effect/Stream"; -import { FetchHttpClient } from "effect/http"; -import { ChildProcess, ChildProcessSpawner } from "effect/process"; +import * as FetchHttpClient from "effect/http/FetchHttpClient"; +import * as ChildProcess from "effect/process/ChildProcess"; +import * as ChildProcessSpawner from "effect/process/ChildProcessSpawner"; import { describe } from "vite-plus/test"; import * as OpenCode2Client from "./OpenCode2Client.ts"; diff --git a/packages/provider-opencode/src/server/v2/OpenCode2Client.test.ts b/packages/provider-opencode/src/server/v2/OpenCode2Client.test.ts index 5bfbd6ab901c..dbc56a04c7ea 100644 --- a/packages/provider-opencode/src/server/v2/OpenCode2Client.test.ts +++ b/packages/provider-opencode/src/server/v2/OpenCode2Client.test.ts @@ -2,7 +2,8 @@ import { assert, it } from "@effect/vitest"; import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; import * as Redacted from "effect/Redacted"; -import { HttpClient, HttpClientResponse } from "effect/http"; +import * as HttpClient from "effect/http/HttpClient"; +import * as HttpClientResponse from "effect/http/HttpClientResponse"; import { describe } from "vite-plus/test"; import * as OpenCode2Client from "./OpenCode2Client.ts"; diff --git a/packages/provider-opencode/src/server/v2/OpenCode2Client.ts b/packages/provider-opencode/src/server/v2/OpenCode2Client.ts index 3b9141e6e718..fcfb87e41260 100644 --- a/packages/provider-opencode/src/server/v2/OpenCode2Client.ts +++ b/packages/provider-opencode/src/server/v2/OpenCode2Client.ts @@ -139,26 +139,27 @@ const undecodable = (data: string) => }); /** Subscribes to `/api/event`, then streams every frame this build can route. */ -const readEvents = (httpClient: HttpClient.HttpClient) => - httpClient.get("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/api/event", { headers: { accept: "text/event-stream" } }).pipe( - Effect.flatMap(HttpClientResponse.filterStatusOk), - Effect.map((response) => - response.stream.pipe( - Stream.timeoutOrElse({ - duration: SILENT_STREAM_TIMEOUT, - orElse: () => Stream.fail(new OpenCode2SilentStreamError()), - }), - Stream.decodeText, - Stream.pipeThroughChannel(Sse.decode()), - Stream.filterMapEffect((frame) => { - const decoded = decodeEvent(frame.data); - return Result.isSuccess(decoded) - ? Effect.succeed(Result.succeed(decoded.success)) - : undecodable(frame.data); - }), - ), +const readEvents = HttpClient.get("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/api/event", { + headers: { accept: "text/event-stream" }, +}).pipe( + Effect.flatMap(HttpClientResponse.filterStatusOk), + Effect.map((response) => + response.stream.pipe( + Stream.timeoutOrElse({ + duration: SILENT_STREAM_TIMEOUT, + orElse: () => Stream.fail(new OpenCode2SilentStreamError()), + }), + Stream.decodeText, + Stream.pipeThroughChannel(Sse.decode()), + Stream.filterMapEffect((frame) => { + const decoded = decodeEvent(frame.data); + return Result.isSuccess(decoded) + ? Effect.succeed(Result.succeed(decoded.success)) + : undecodable(frame.data); + }), ), - ); + ), +); /** * OpenCode decodes Basic credentials as UTF-8. `HttpClientRequest.basicAuth` @@ -182,8 +183,11 @@ export const make = Effect.gen(function* () { Effect.provideService(HttpClient.HttpClient, authenticated), Effect.map((client) => ({ client, - events: readEvents( - HttpClient.mapRequest(authenticated, HttpClientRequest.prependUrl(baseUrl)), + events: readEvents.pipe( + Effect.provideService( + HttpClient.HttpClient, + HttpClient.mapRequest(authenticated, HttpClientRequest.prependUrl(baseUrl)), + ), ), })), ); diff --git a/packages/provider-opencode/src/server/v2/OpenCode2Server.live.test.ts b/packages/provider-opencode/src/server/v2/OpenCode2Server.live.test.ts index 81a38f9ae22b..40e6e5568893 100644 --- a/packages/provider-opencode/src/server/v2/OpenCode2Server.live.test.ts +++ b/packages/provider-opencode/src/server/v2/OpenCode2Server.live.test.ts @@ -19,8 +19,9 @@ import * as Path from "effect/Path"; import * as Redacted from "effect/Redacted"; import * as Scope from "effect/Scope"; import * as Stream from "effect/Stream"; -import { FetchHttpClient } from "effect/http"; -import { ChildProcess, ChildProcessSpawner } from "effect/process"; +import * as FetchHttpClient from "effect/http/FetchHttpClient"; +import * as ChildProcess from "effect/process/ChildProcess"; +import * as ChildProcessSpawner from "effect/process/ChildProcessSpawner"; import { describe } from "vite-plus/test"; import * as OpenCodeRuntime from "../OpenCodeRuntime.ts"; diff --git a/packages/provider-opencode/src/server/v2/OpenCode2Server.test.ts b/packages/provider-opencode/src/server/v2/OpenCode2Server.test.ts index 43e31ece86fa..340e03f8d7d5 100644 --- a/packages/provider-opencode/src/server/v2/OpenCode2Server.test.ts +++ b/packages/provider-opencode/src/server/v2/OpenCode2Server.test.ts @@ -12,7 +12,9 @@ import * as Layer from "effect/Layer"; import * as Path from "effect/Path"; import * as Redacted from "effect/Redacted"; import * as TestClock from "effect/testing/TestClock"; -import { FetchHttpClient, HttpClient, HttpClientResponse } from "effect/http"; +import * as FetchHttpClient from "effect/http/FetchHttpClient"; +import * as HttpClient from "effect/http/HttpClient"; +import * as HttpClientResponse from "effect/http/HttpClientResponse"; import { describe } from "vite-plus/test"; import * as OpenCodeRuntime from "../OpenCodeRuntime.ts"; diff --git a/packages/provider-opencode/src/server/v2/textGeneration.ts b/packages/provider-opencode/src/server/v2/textGeneration.ts index af7939e2f800..6301e3848634 100644 --- a/packages/provider-opencode/src/server/v2/textGeneration.ts +++ b/packages/provider-opencode/src/server/v2/textGeneration.ts @@ -40,9 +40,9 @@ type Outcome = const runOnServer = ( connection: OpenCode2Server.OpenCode2Connection, input: TextGenerationOperations.Request, - resolveAttachmentPath: ProviderHost.ProviderHost["Service"]["resolveAttachmentPath"], ) => Effect.gen(function* () { + const { resolveAttachmentPath } = yield* ProviderHost.ProviderHost; const { client } = connection; const parsed = OpenCodeRuntime.parseOpenCodeModelSlug(input.modelSelection.model); if (parsed === null) { @@ -184,11 +184,12 @@ const runOnServer = ( /** Text generation for an instance whose server is OpenCode 2. */ export const make = Effect.fn("OpenCode2TextGeneration.make")(function* () { const server = yield* OpenCode2Server.OpenCode2Server; - const { resolveAttachmentPath } = yield* ProviderHost.ProviderHost; + const host = yield* ProviderHost.ProviderHost; const run: TextGenerationOperations.Runner = (input) => server - .withConnection((connection) => runOnServer(connection, input, resolveAttachmentPath)) + .withConnection((connection) => runOnServer(connection, input)) .pipe( + Effect.provideService(ProviderHost.ProviderHost, host), Effect.mapError((cause) => isTextGenerationError(cause) ? cause diff --git a/packages/provider-opencode/src/server/versionProbe.test.ts b/packages/provider-opencode/src/server/versionProbe.test.ts index f0296025d3a8..6bf6763e9941 100644 --- a/packages/provider-opencode/src/server/versionProbe.test.ts +++ b/packages/provider-opencode/src/server/versionProbe.test.ts @@ -1,6 +1,8 @@ import { assert, describe, it } from "@effect/vitest"; import * as Effect from "effect/Effect"; -import { HttpClient, HttpClientError, HttpClientRequest } from "effect/http"; +import * as HttpClient from "effect/http/HttpClient"; +import * as HttpClientError from "effect/http/HttpClientError"; +import * as HttpClientRequest from "effect/http/HttpClientRequest"; import * as OpenCodeRuntime from "./OpenCodeRuntime.ts"; import { @@ -16,7 +18,7 @@ import { const noBinary = { runOpenCodeCommand: () => Effect.die("A configured server must not run the local binary"), -} as unknown as OpenCodeRuntime.OpenCodeRuntimeShape; +} as unknown as OpenCodeRuntime.OpenCodeRuntime["Service"]; const probeServer = (serverUrl: string, serverPassword: string, http: HttpClient.HttpClient) => probeOpenCodeRuntime({ binaryPath: "opencode", serverUrl, serverPassword }).pipe( @@ -172,7 +174,7 @@ describe("OpenCode version probe", () => { new OpenCodeRuntime.OpenCodeRuntimeError({ operation: "spawn", detail: "ENOENT" }), ); }, - } as unknown as OpenCodeRuntime.OpenCodeRuntimeShape; + } as unknown as OpenCodeRuntime.OpenCodeRuntime["Service"]; const probe = yield* makeOpenCodeRuntimeProbe( probeOpenCodeRuntime({ binaryPath: "opencode", serverUrl: "", serverPassword: "" }).pipe( Effect.provideService(OpenCodeRuntime.OpenCodeRuntime, runtime), diff --git a/packages/provider-opencode/src/server/versionProbe.ts b/packages/provider-opencode/src/server/versionProbe.ts index c1ac522f3424..519144944a4f 100644 --- a/packages/provider-opencode/src/server/versionProbe.ts +++ b/packages/provider-opencode/src/server/versionProbe.ts @@ -6,7 +6,8 @@ import * as Effect from "effect/Effect"; import * as Exit from "effect/Exit"; import * as Option from "effect/Option"; import * as Schema from "effect/Schema"; -import { HttpClient, HttpClientRequest } from "effect/http"; +import * as HttpClient from "effect/http/HttpClient"; +import * as HttpClientRequest from "effect/http/HttpClientRequest"; import * as OpenCodeRuntime from "./OpenCodeRuntime.ts"; import { parseGenericCliVersion } from "@t3tools/provider-core/server/snapshotProbe"; diff --git a/packages/provider-pi/src/server/adapter.test.ts b/packages/provider-pi/src/server/adapter.test.ts index 9c93ba12e9d7..8508fb064837 100644 --- a/packages/provider-pi/src/server/adapter.test.ts +++ b/packages/provider-pi/src/server/adapter.test.ts @@ -29,9 +29,10 @@ import * as Schema from "effect/Schema"; import * as Sink from "effect/Sink"; import * as Stream from "effect/Stream"; import * as TestClock from "effect/testing/TestClock"; -import { ChildProcess, ChildProcessSpawner } from "effect/process"; +import * as ChildProcess from "effect/process/ChildProcess"; +import * as ChildProcessSpawner from "effect/process/ChildProcessSpawner"; -import { layerTestProviderHost } from "@t3tools/provider-testing/host"; +import * as TestProviderHost from "@t3tools/provider-testing/TestProviderHost"; import * as ProviderContinuationRequests from "@t3tools/provider-core/server/ProviderContinuationRequests"; import * as McpProviderSessions from "@t3tools/provider-core/server/McpProviderSessions"; import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; @@ -50,7 +51,7 @@ const layerTest = Layer.mergeAll( NodeServices.layer, IdAllocator.layer, McpProviderSessions.layer, - layerTestProviderHost().pipe(Layer.provide(NodeServices.layer)), + TestProviderHost.layer().pipe(Layer.provide(NodeServices.layer)), ); const decodeJsonLine = Schema.decodeSync(Schema.fromJsonString(Schema.Unknown)); diff --git a/packages/provider-pi/src/server/adapter.ts b/packages/provider-pi/src/server/adapter.ts index 2aa7d1bb8951..eecba9c93aad 100644 --- a/packages/provider-pi/src/server/adapter.ts +++ b/packages/provider-pi/src/server/adapter.ts @@ -58,7 +58,7 @@ import * as Queue from "effect/Queue"; import * as Schema from "effect/Schema"; import * as Semaphore from "effect/Semaphore"; import * as Stream from "effect/Stream"; -import { ChildProcessSpawner } from "effect/process"; +import * as ChildProcessSpawner from "effect/process/ChildProcessSpawner"; import * as ProviderHost from "@t3tools/provider-core/server/ProviderHost"; import { mcpToolPresentation } from "@t3tools/provider-core/server/mcpToolPresentation"; diff --git a/packages/provider-pi/src/server/driver.test.ts b/packages/provider-pi/src/server/driver.test.ts index c2237b7dd642..d0a108bc5cc1 100644 --- a/packages/provider-pi/src/server/driver.test.ts +++ b/packages/provider-pi/src/server/driver.test.ts @@ -10,18 +10,19 @@ import * as Schema from "effect/Schema"; import * as Sink from "effect/Sink"; import * as Stream from "effect/Stream"; import * as TestClock from "effect/testing/TestClock"; -import { HttpClient } from "effect/http"; -import { ChildProcess, ChildProcessSpawner } from "effect/process"; +import * as HttpClient from "effect/http/HttpClient"; +import * as ChildProcess from "effect/process/ChildProcess"; +import * as ChildProcessSpawner from "effect/process/ChildProcessSpawner"; import * as ProviderLatestVersions from "@t3tools/provider-core/server/ProviderLatestVersions"; import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; import * as McpProviderSessions from "@t3tools/provider-core/server/McpProviderSessions"; -import { layerTestProviderHost } from "@t3tools/provider-testing/host"; +import * as TestProviderHost from "@t3tools/provider-testing/TestProviderHost"; import type { PiSettings } from "../settings.ts"; import { PiDriver } from "./driver.ts"; const layerTest = Layer.mergeAll( - layerTestProviderHost({ + TestProviderHost.layer({ cwd: "/machine", settings: { ...DEFAULT_SERVER_SETTINGS, enableProviderUpdateChecks: false }, runBackgroundWork: false, diff --git a/packages/provider-pi/src/server/driver.ts b/packages/provider-pi/src/server/driver.ts index 9d6962c4cd47..38bf7df84662 100644 --- a/packages/provider-pi/src/server/driver.ts +++ b/packages/provider-pi/src/server/driver.ts @@ -12,8 +12,8 @@ import * as Effect from "effect/Effect"; import * as FileSystem from "effect/FileSystem"; import * as Path from "effect/Path"; import * as Schema from "effect/Schema"; -import { HttpClient } from "effect/http"; -import { ChildProcessSpawner } from "effect/process"; +import * as HttpClient from "effect/http/HttpClient"; +import * as ChildProcessSpawner from "effect/process/ChildProcessSpawner"; import * as ProviderLatestVersions from "@t3tools/provider-core/server/ProviderLatestVersions"; import * as ProviderHost from "@t3tools/provider-core/server/ProviderHost"; diff --git a/packages/provider-pi/src/server/rpc.ts b/packages/provider-pi/src/server/rpc.ts index e7bf65c5932e..628204ee9f8d 100644 --- a/packages/provider-pi/src/server/rpc.ts +++ b/packages/provider-pi/src/server/rpc.ts @@ -26,7 +26,8 @@ import * as Queue from "effect/Queue"; import * as Scope from "effect/Scope"; import * as Schema from "effect/Schema"; import * as Stream from "effect/Stream"; -import { ChildProcess, ChildProcessSpawner } from "effect/process"; +import * as ChildProcess from "effect/process/ChildProcess"; +import * as ChildProcessSpawner from "effect/process/ChildProcessSpawner"; import { HostProcessPlatform } from "@t3tools/shared/hostProcess"; import { resolveSpawnCommand } from "@t3tools/shared/shell"; diff --git a/packages/provider-pi/src/server/status.test.ts b/packages/provider-pi/src/server/status.test.ts index 133a505612b1..2cb16288844a 100644 --- a/packages/provider-pi/src/server/status.test.ts +++ b/packages/provider-pi/src/server/status.test.ts @@ -3,7 +3,8 @@ import { assert, describe, it } from "@effect/vitest"; import * as Effect from "effect/Effect"; import * as Sink from "effect/Sink"; import * as Stream from "effect/Stream"; -import { ChildProcess, ChildProcessSpawner } from "effect/process"; +import * as ChildProcess from "effect/process/ChildProcess"; +import * as ChildProcessSpawner from "effect/process/ChildProcessSpawner"; import { checkPiProviderStatus, MINIMUM_PI_VERSION } from "./status.ts"; diff --git a/packages/provider-pi/src/server/status.ts b/packages/provider-pi/src/server/status.ts index 9969a244b5ad..0ff054c4f63c 100644 --- a/packages/provider-pi/src/server/status.ts +++ b/packages/provider-pi/src/server/status.ts @@ -23,8 +23,9 @@ import * as Stream from "effect/Stream"; import * as Exit from "effect/Exit"; import * as Option from "effect/Option"; import * as Result from "effect/Result"; -import { HttpClient } from "effect/http"; -import { ChildProcess, ChildProcessSpawner } from "effect/process"; +import * as HttpClient from "effect/http/HttpClient"; +import * as ChildProcess from "effect/process/ChildProcess"; +import * as ChildProcessSpawner from "effect/process/ChildProcessSpawner"; import { buildPiRpcLaunch, resolvePiLaunchArgs } from "./mcpInjection.ts"; import { diff --git a/packages/provider-pi/src/server/textGeneration.test.ts b/packages/provider-pi/src/server/textGeneration.test.ts index fb56753785a8..d7b5630d6388 100644 --- a/packages/provider-pi/src/server/textGeneration.test.ts +++ b/packages/provider-pi/src/server/textGeneration.test.ts @@ -7,7 +7,7 @@ import * as Queue from "effect/Queue"; import * as Schema from "effect/Schema"; import * as Sink from "effect/Sink"; import * as Stream from "effect/Stream"; -import { ChildProcessSpawner } from "effect/process"; +import * as ChildProcessSpawner from "effect/process/ChildProcessSpawner"; import { makePiTextGeneration } from "./textGeneration.ts"; diff --git a/packages/provider-pi/src/server/textGeneration.ts b/packages/provider-pi/src/server/textGeneration.ts index cf334556fdf9..e8ac19a7e13c 100644 --- a/packages/provider-pi/src/server/textGeneration.ts +++ b/packages/provider-pi/src/server/textGeneration.ts @@ -8,7 +8,7 @@ import * as Effect from "effect/Effect"; import * as Option from "effect/Option"; import * as Queue from "effect/Queue"; import * as Schema from "effect/Schema"; -import { ChildProcessSpawner } from "effect/process"; +import * as ChildProcessSpawner from "effect/process/ChildProcessSpawner"; import { TextGenerationError } from "@t3tools/contracts"; import type { PiSettings } from "../settings.ts"; diff --git a/packages/provider-testing/package.json b/packages/provider-testing/package.json index 39c6c1f644aa..90cd5b4a7f98 100644 --- a/packages/provider-testing/package.json +++ b/packages/provider-testing/package.json @@ -3,25 +3,25 @@ "private": true, "type": "module", "exports": { - "./replayTranscript": { - "types": "./src/replayTranscript.ts", - "import": "./src/replayTranscript.ts" + "./fakeCli": { + "types": "./src/fakeCli.ts", + "import": "./src/fakeCli.ts" }, "./replayGate": { "types": "./src/replayGate.ts", "import": "./src/replayGate.ts" }, + "./replayTranscript": { + "types": "./src/replayTranscript.ts", + "import": "./src/replayTranscript.ts" + }, "./replayWorkspace": { "types": "./src/replayWorkspace.ts", "import": "./src/replayWorkspace.ts" }, - "./host": { - "types": "./src/host.ts", - "import": "./src/host.ts" - }, - "./fakeCli": { - "types": "./src/fakeCli.ts", - "import": "./src/fakeCli.ts" + "./TestProviderHost": { + "types": "./src/TestProviderHost.ts", + "import": "./src/TestProviderHost.ts" } }, "scripts": { diff --git a/packages/provider-testing/src/host.ts b/packages/provider-testing/src/TestProviderHost.ts similarity index 94% rename from packages/provider-testing/src/host.ts rename to packages/provider-testing/src/TestProviderHost.ts index 3316f1a9df2c..4f38ddf02cba 100644 --- a/packages/provider-testing/src/host.ts +++ b/packages/provider-testing/src/TestProviderHost.ts @@ -3,7 +3,7 @@ * scoped temp directory, settings are fixed, and background work always runs * unless the test says otherwise. * - * @module provider-testing/host + * @module provider-testing/TestProviderHost */ import { DEFAULT_SERVER_SETTINGS, type ServerSettings } from "@t3tools/contracts"; import * as ProviderHost from "@t3tools/provider-core/server/ProviderHost"; @@ -25,13 +25,13 @@ export interface TestProviderHostOptions { readonly runBackgroundWork?: boolean; } -/** Lets a test change the settings its `layerTestProviderHost` reports. */ +/** Lets a test change the settings its `layer` reports. */ export class TestProviderHostSettings extends Context.Service< TestProviderHostSettings, { readonly set: (settings: ServerSettings) => Effect.Effect } ->()("@t3tools/provider-testing/host/TestProviderHostSettings") {} +>()("@t3tools/provider-testing/TestProviderHost/TestProviderHostSettings") {} -export const layerTestProviderHost = ( +export const layer = ( options: TestProviderHostOptions = {}, ): Layer.Layer< ProviderHost.ProviderHost | TestProviderHostSettings, diff --git a/packages/provider-testing/src/replayWorkspace.ts b/packages/provider-testing/src/replayWorkspace.ts index 1239d5bfbd9e..a9fdcdee9154 100644 --- a/packages/provider-testing/src/replayWorkspace.ts +++ b/packages/provider-testing/src/replayWorkspace.ts @@ -4,7 +4,8 @@ import * as FileSystem from "effect/FileSystem"; import * as Path from "effect/Path"; import * as PlatformError from "effect/PlatformError"; import * as Schema from "effect/Schema"; -import { ChildProcess, ChildProcessSpawner } from "effect/process"; +import * as ChildProcess from "effect/process/ChildProcess"; +import * as ChildProcessSpawner from "effect/process/ChildProcessSpawner"; class ReplayFixtureGitCommandError extends Schema.TaggedError()( "ReplayFixtureGitCommandError",