From 31b04e2ee929660d80d5931ed53fd17bf1d33ce6 Mon Sep 17 00:00:00 2001 From: Julius Marminge Date: Fri, 9 Oct 2026 09:42:40 -0700 Subject: [PATCH] refactor(provider): bring opencode, muse, pi, core and testing in line with Effect conventions (#17542) 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 da8b17fec5672b4f33943b8e013ad92161329ab6..49b667fecec12f794ce61cefd9d944bf7c780e8f 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 b9344451e64f4c0d044f43f268f6d843d9c32eca..c9fbc064168c3865ddf3db5541088be52b08177a 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 697da040bef6d4fef238ac0c8c5524198073e91d..a8c9677eeb0e36660effe879bc89f7b124472892 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 17ff9b76ff445a0562fe9a91ce8d28e422c11f51..e7327f034db7904546ce751a1611541f52f519f3 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 c3015252900649af6b88d0369e8da345837501cc..93254166f183ab5ab15df16a5c3f48e09bd5782d 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 ca19c1f86c508b96095fa63acc65726b8462e299..825d0d81508ce94f767e0707ca2bdb8aa7b3e821 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 9f1e34f8d1a0b73472dfedc43c528c4ff8ca2bf8..c7b1fb0d581900522ba1dd95b81c407d01793baf 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 8b885320cf17550e0fb6589be83ad864feadd624..dbdbab32a74da485d1594705a55767ff2eba0d06 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 0355b7bc69176275c6ee16ed67b9a0c4ebcde11c..a194edd4a3ce0d43c1871959d5fb1d1bc5913d4d 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 dee5d16aac13b3363aba4d26334853e43821a528..86a6df752f6933a5f4afa9a8c6db761447e11464 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 467ad620d534ea542d492ca9ade634b72550b0ac..443019cea47283128d0d0e56eb1349a9b5aac2dc 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 b695b39fa8d0249af05b3447c9f94616c6df62b9..dbe4f7068ebbeb405dba4213333e87f4d191008a 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 2b009799cc5343cc5d8a45a4acd44fcb1aafe802..7eb2959974838ed3c6abe649dfb4c25d52ddc26d 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 5c982997a92f07b69fec0c6c2fb2972507e3bfd6..424bae4f25d9d7595b4f490f5028464a6041f014 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 f0538385e8106d491db15b7646c923156326b711..ae166ed76bca52a1213c1850f018830d4e3df422 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 ac3b299b18bf003dc2f285dba45bdb9683e05578..913a1983a107a2eaad1ed32ba4d9afee84e8cc78 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 e27a278f34c350a9cbba73024762af71d5c70730..660c0dad965a35db2192b72005dd2fbe77d5d157 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 5f5120ee28bc680db368ba92dccd46d53ccd0a9b..0e2c50e6670ea9f0d9e249ce086a0c704ae11502 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 115ad692ff5e7a0a45a6b46b0814bf40caa7b677..ac0924cf7e9eaa8faf9a8490f55fa119d1f6cf15 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 180d19ccee3551106d666be1e7c3f817f2c500be..4a4d1fec6434a517023c9d6193a951285b8f5094 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 424764cd8f377ab8befecfd3e41270d3fdaacf1e..31556dbdb8aa218b54ea41e51ec78600b668d5b8 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 1a39c754b16525232860df82e49ae5cd20a1e8ba..beac8d18a094c9a21877a9412f1a417b8cdd41b2 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 82dedf3b159a7dd1582523759b2ae84d571a68e9..000a422bcdc838e4a6df4ed9f2d5a8ceb035a6b4 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 2496a593fd2d62ff6b058c10ce38874e6654a62e..ee7154577458c32cef69fcb74c5020c6e7305e3c 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 35cf0f199b9da79b593f107b03d890467a21baa5..960602c8cc947f7a05bb1715c67fd062b71e5bb6 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 df215d3197429059b5a4040472d57fb3ea979d02..18ed1aec1277beb5ce04444642aae738068d045d 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 6d0dd9cae93fa314825a00aaf79b310798bcf551..23f0707a4a51719c3bca35fc5e6d1f2d937c1d6f 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 ddb26fc916737e7b8db2dfa819b3ab255b501812..fb985bd2011af7668e1a08a89052c0fec5f2c622 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 dab08b246ce3ce1545b6dd4360be5437f420ce6b..239508cec41c61b5216c8de5309dce4059b589c9 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 946f9ad86217dc66507922869f3ad1e3951bf8c1..b74788a38bada01f0257812a537109fc9e9d7e29 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 ec3f03faa5b8d9b3f6e5baf3661192c304e1a2a9..bacdaece0b1c58a8d76dffc8c833ddb0cdb106b6 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 a6f2a985fd5786077159a1d08178b97f5899bc76..7832e944410f0185e394dcb8e3dd3d958c97b645 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 d15e8744cc0c7a538055683f731d3b954db72ccc..8ee272cee394b0e731eec4e20296d2b8c366c191 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 1c440a6629e3eaa40eb12a25763b5ee70a89194a..f61881ba08063cc92d84bd3cdf6920f40836c267 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 ab03ec8ffbac35b6f8c632e277c3d533fdfde352..bbd813400da9d1934aa2bc6e17adadc225b7040a 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 803324d9f97993cc31b87c67c88c96e948d96f1e..10e5a6b8d06f5a8be91c89b3dc37e3ac4e5a85dc 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 cd46bfa464c9bf5094869b8119d7e4d347b571cf..ef24403a4b2ce7944315fe514e1ac26ca6fde59f 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 fc1beba6399009973750a60c01b41d664239c615..9c27b9a2b09232f9552ed15a3e8d64218e92ca03 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 3bb26b7933e5cf5a41a0671149713a5031feff55..ae9ca1d351b2724e38634e595c0b9fd0604fb97d 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 6e0f06deddb83002f8438e8da473a9278f6ce5b4..1d2922e542703418a832e97653f2b0666aa233f9 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 244d99a89c701a0085b7a185e3a2f7cd10bd79f1..869aa6dca2fec44879dadd03f4a4279b76eac488 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 107fa77bb606c2afe5bf585c883e8ccc9a2e4f6f..776e9b0f3c239ca80632671d7bbdf15018a8dcdf 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 2cb4e5360ff2572ffe97755c5b39f326c6af2a91..a0a51b732e0d5f8cb2dbfcd2a26ff72605da025f 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 fdd1ae3319bd934b790ca50ea2727a9dbf682903..6476c7a7c3cea1423288ce05a072d9c295a2df5b 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 91e1a6e11d5e1f2b4021fe78735cc32eda645111..4637ec831b219dc73971a0f0539d15487663fd2c 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 a805572e8aac4d01daa03c74c0d2632e7bdde981..90b0c55c157f4e4ff6f49804c6f67b94d348d4cb 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 0c721cf6ffd52c531f4d93f6fa7c68266cf2fab7..43774e24523242f4e41898637bd0b0084cd47a69 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 f1f9881fdc293d81c410a2a7f015524eb3cb4646..f9df8c5a1de03548c05f5a3acf6a01d57abd0fc2 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 53b0c54fad44ad86a9646f892136983eaed10b86..c2ec42bd16b5ff59ac5d2416e247e4eb0e0f49b4 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 ac94e3ecd297387733a11108c99845463b1948fc..96d535070294da25b2cc15acb7e2d8ddb3dab065 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 0e82fed63f889ae9a7c616afaf7d1b674171717b..2e4225f8d374f4fded3a588b7f96f84406655c53 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 53a5f05424f3e285afe6b3650211b40ada9bfe23..3834ec73534bcea43d9950bf394f7447c892ddca 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 0eff526acd4f5d0b393845f76b8f32f499b2e60b..3dbf954b2f2075b8ada4b8df64c72838d6d420e6 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 f062330ad733e05e71068ef7d3d12340bbfde2f2..c84dbdf3f89412e6b9f48c86abe04f8fc59cb224 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 db93d27d274740905fc41d4f7ede24e3b8391f4b..48f85e60b6cbf7dcd3221282527c6cc191b0fd07 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 2192e75a7b3d854e6b82601b1e336e0af866d9e3..3c21536b8798c33965099e7d998cd709d0aa4b59 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 21c01be67185a5e14f40ea4c5b82397b4c54e52e..a1d83d3d8eecee995671387844effa22660d34c9 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 5bfbd6ab901ce5a3128be3536e2bbeef66d62aac..dbc56a04c7eaf05e33b704fb93a575d9a1f324fa 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 3b9141e6e7183cdd11e8ef9c34ae79fdc8bc2675..fcfb87e412605bbd95ec3e57ab39bce7d02d7b5e 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("/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("/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 81a38f9ae22b24a14443eaa80052b4da8b3c3d46..40e6e5568893106002e70155344e05ac78336f2f 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 43e31ece86fac86a2421ff31738a3e69ce73d197..340e03f8d7d5880fd6bbc600c61ad3ff01956eb5 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 af7939e2f800a198c4bdfb555b9e5aa38ad51469..6301e3848634c9cbe32fc671ee41c1465eb240b7 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 f0296025d3a87b3a248f89aceae6b09e1993a718..6bf6763e9941d18e6e6f225b2d75547809134481 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 c1ac522f34247cde90fa50f9980733c798a2f8ff..519144944a4f848062fdf72d6f28a802fd558809 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 9c93ba12e9d718e5db903c687903f9462f28b8c6..8508fb064837a16670e618d9d16ff5e18f1b7482 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 2aa7d1bb8951ac21ca163d287624502fafb3e1de..eecba9c93aad2a582cfc5914adf93f3000bee58a 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 c2237b7dd642a9062e2d10f5b8457fb2686146cd..d0a108bc5cc13ea2a4ed439b628716b90a56ee83 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 9d6962c4cd47297c650cd5e48193dbbc5c54ba8f..38bf7df846625d1c653a699964401880fc721f81 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 e7bf65c5932e3914cc341dc9b483b0b1a489d5ab..628204ee9f8db203e5e04fc2143d5036effe1987 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 133a505612b1090f9d04f382ff3d9502bd36bed7..2cb16288844a5e9bcd51d3de04c913db292ba5e7 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 9969a244b5add9e345892e6ddaa5b48f8334b72a..0ff054c4f63cd5c9e9cac8cd0e3855ef8de8a565 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 fb56753785a8093bf31a3743f71a74b479c417de..d7b5630d6388f6e4dae99dc9c408701b5aff2a1e 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 cf334556fdf92a9d7c981d3ed775ba51d8350de2..e8ac19a7e13ceb2a1b6ab42c2e9a28ed52a9f546 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 39c6c1f644aa29f192ccdc99016011b5585084d1..90cd5b4a7f98fdd5092e0602bb9550d17576a530 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 3316f1a9df2c92153b7e1bf97c632d86f1a70a96..4f38ddf02cba5c1aaba56a660694a35cea98b863 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 1239d5bfbd9e2443a51c8f172b04c27c113b2188..a9fdcdee91545db214db6532e1c10005335d0d6a 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", -- 2.54.0