| author | |
| committer | |
| log | 060c576f8922703725f0dbdd01443ed88ecc9baa |
| tree | 3997f9cf71a5f1c28cb29c70fc161c11aa0d83d4 |
| parent | 77d9ffc822ac4c1f3600dc6c97e92fc94e9e12a3 |
| signature | Signed by PGP key B5690EEEBB952194 |
19 files changed, 23 insertions(+), 67 deletions(-)
apps/server/src/provider/ClaudeModelCatalog.ts+2-2| ... | @@ -118,7 +118,7 @@ export function scopeClaudeModelCatalog( | ... | @@ -118,7 +118,7 @@ export function scopeClaudeModelCatalog( |
| 118 | return { models: [...builtInModels, ...customCatalogModels] }; | 118 | return { models: [...builtInModels, ...customCatalogModels] }; |
| 119 | } | 119 | } |
| 120 | 120 | ||
| 121 | export function resolveClaudeCatalogModel( | 121 | function resolveClaudeCatalogModel( |
| 122 | catalog: ClaudeModelCatalog, | 122 | catalog: ClaudeModelCatalog, |
| 123 | slugOrAlias: string | null | undefined, | 123 | slugOrAlias: string | null | undefined, |
| 124 | ): ClaudeCatalogModel | undefined { | 124 | ): ClaudeCatalogModel | undefined { |
| ... | @@ -215,7 +215,7 @@ export function isClaudeCatalogUltracodeEffort(effort: string | null | undefined | ... | @@ -215,7 +215,7 @@ export function isClaudeCatalogUltracodeEffort(effort: string | null | undefined |
| 215 | return effort === "ultracode"; | 215 | return effort === "ultracode"; |
| 216 | } | 216 | } |
| 217 | 217 | ||
| 218 | export function resolveClaudeCatalogContextWindow( | 218 | function resolveClaudeCatalogContextWindow( |
| 219 | catalog: ClaudeModelCatalog, | 219 | catalog: ClaudeModelCatalog, |
| 220 | modelSelection: ModelSelection | undefined, | 220 | modelSelection: ModelSelection | undefined, |
| 221 | ): string | undefined { | 221 | ): string | undefined { |
apps/server/src/provider/Drivers/ClaudeSkills.ts+1-1| ... | @@ -104,7 +104,7 @@ function parseSkillFrontmatter(contents: string): SkillFrontmatter { | ... | @@ -104,7 +104,7 @@ function parseSkillFrontmatter(contents: string): SkillFrontmatter { |
| 104 | * user and project one. Absent on almost every machine, which is why a missing | 104 | * user and project one. Absent on almost every machine, which is why a missing |
| 105 | * file is the normal case rather than an error. | 105 | * file is the normal case rather than an error. |
| 106 | */ | 106 | */ |
| 107 | export function claudeManagedSettingsPath( | 107 | function claudeManagedSettingsPath( |
| 108 | path: Path.Path, | 108 | path: Path.Path, |
| 109 | platform: NodeJS.Platform, | 109 | platform: NodeJS.Platform, |
| 110 | environment: NodeJS.ProcessEnv, | 110 | environment: NodeJS.ProcessEnv, |
apps/server/src/provider/Layers/ProviderAdapterRegistry.ts-7| ... | @@ -80,13 +80,6 @@ export const ProviderAdapterRegistryLive = Layer.effect( | ... | @@ -80,13 +80,6 @@ export const ProviderAdapterRegistryLive = Layer.effect( |
| 80 | makeProviderAdapterRegistry(), | 80 | makeProviderAdapterRegistry(), |
| 81 | ); | 81 | ); |
| 82 | 82 | ||
| 83 | // Exposed for tests that want to build a facade over a pre-assembled | ||
| 84 | // `ProviderInstanceRegistry` without pulling in the whole boot graph. | ||
| 85 | export { makeProviderAdapterRegistry }; | ||
| 86 | |||
| 87 | // Re-export for consumers that need the accessor shape. The service tag | ||
| 88 | // itself lives in `Services/ProviderAdapterRegistry.ts`. | ||
| 89 | export { ProviderAdapterRegistry } from "../Services/ProviderAdapterRegistry.ts"; | ||
| 90 | // Re-export for consumers (including tests) that construct a | 83 | // Re-export for consumers (including tests) that construct a |
| 91 | // `ProviderInstanceId` before calling `getByInstance`. | 84 | // `ProviderInstanceId` before calling `getByInstance`. |
| 92 | export { ProviderInstanceId }; | 85 | export { ProviderInstanceId }; |
apps/server/src/provider/Layers/ProviderEventLoggers.ts+2| ... | @@ -61,6 +61,8 @@ export const NoOpProviderEventLoggers: ProviderEventLoggers["Service"] = { | ... | @@ -61,6 +61,8 @@ export const NoOpProviderEventLoggers: ProviderEventLoggers["Service"] = { |
| 61 | /** | 61 | /** |
| 62 | * Builds both stream views over one shared store. Setup failures are logged | 62 | * Builds both stream views over one shared store. Setup failures are logged |
| 63 | * and downgraded to the no-op service so diagnostics never block startup. | 63 | * and downgraded to the no-op service so diagnostics never block startup. |
| 64 | * | ||
| 65 | * @public Service construction is part of the canonical Effect module API. | ||
| 64 | */ | 66 | */ |
| 65 | export const make = Effect.gen(function* () { | 67 | export const make = Effect.gen(function* () { |
| 66 | const { providerEventLogPath } = yield* ServerConfig; | 68 | const { providerEventLogPath } = yield* ServerConfig; |
apps/server/src/provider/Layers/ProviderInstanceRegistryLive.ts-3| ... | @@ -33,7 +33,6 @@ | ... | @@ -33,7 +33,6 @@ |
| 33 | * @module provider/Layers/ProviderInstanceRegistryLive | 33 | * @module provider/Layers/ProviderInstanceRegistryLive |
| 34 | */ | 34 | */ |
| 35 | import { | 35 | import { |
| 36 | defaultInstanceIdForDriver, | ||
| 37 | providerInstanceConfigEnabledFlag, | 36 | providerInstanceConfigEnabledFlag, |
| 38 | ProviderInstanceId, | 37 | ProviderInstanceId, |
| 39 | type ProviderInstanceConfig, | 38 | type ProviderInstanceConfig, |
| ... | @@ -430,5 +429,3 @@ export const ProviderInstanceRegistryMutableLayer = <R>(input: { | ... | @@ -430,5 +429,3 @@ export const ProviderInstanceRegistryMutableLayer = <R>(input: { |
| 430 | ), | 429 | ), |
| 431 | ), | 430 | ), |
| 432 | ) as Layer.Layer<ProviderInstanceRegistry | ProviderInstanceRegistryMutator, never, R>; | 431 | ) as Layer.Layer<ProviderInstanceRegistry | ProviderInstanceRegistryMutator, never, R>; |
| 433 | |||
| 434 | export { defaultInstanceIdForDriver }; |
apps/server/src/provider/Layers/codexLaunchArgs.ts+2-3| ... | @@ -1,14 +1,13 @@ | ... | @@ -1,14 +1,13 @@ |
| 1 | import { tokenizeCliArgs } from "@t3tools/shared/cliArgs"; | 1 | import { tokenizeCliArgs } from "@t3tools/shared/cliArgs"; |
| 2 | 2 | ||
| 3 | export const T3CODE_CODEX_LAUNCH_ARGS_ENV = "T3CODE_CODEX_LAUNCH_ARGS"; | 3 | const T3CODE_CODEX_LAUNCH_ARGS_ENV = "T3CODE_CODEX_LAUNCH_ARGS"; |
| 4 | 4 | ||
| 5 | export const resolveCodexLaunchArgs = ( | 5 | export const resolveCodexLaunchArgs = ( |
| 6 | launchArgs?: string, | 6 | launchArgs?: string, |
| 7 | environment: NodeJS.ProcessEnv = process.env, | 7 | environment: NodeJS.ProcessEnv = process.env, |
| 8 | ) => environment[T3CODE_CODEX_LAUNCH_ARGS_ENV]?.trim() || launchArgs?.trim() || ""; | 8 | ) => environment[T3CODE_CODEX_LAUNCH_ARGS_ENV]?.trim() || launchArgs?.trim() || ""; |
| 9 | 9 | ||
| 10 | export const codexLaunchArgv = (launchArgs?: string): ReadonlyArray<string> => | 10 | const codexLaunchArgv = (launchArgs?: string): ReadonlyArray<string> => tokenizeCliArgs(launchArgs); |
| 11 | tokenizeCliArgs(launchArgs); | ||
| 12 | 11 | ||
| 13 | export const codexAppServerArgs = (launchArgs?: string) => [ | 12 | export const codexAppServerArgs = (launchArgs?: string) => [ |
| 14 | "app-server", | 13 | "app-server", |
apps/server/src/provider/Layers/codexResetCredit.ts+1| ... | @@ -45,6 +45,7 @@ export class CodexResetCreditCoordinator extends Context.Service< | ... | @@ -45,6 +45,7 @@ export class CodexResetCreditCoordinator extends Context.Service< |
| 45 | } | 45 | } |
| 46 | >()("t3/provider/Layers/codexResetCredit/CodexResetCreditCoordinator") {} | 46 | >()("t3/provider/Layers/codexResetCredit/CodexResetCreditCoordinator") {} |
| 47 | 47 | ||
| 48 | /** @public Service construction is part of the canonical Effect module API. */ | ||
| 48 | export const make = Effect.gen(function* () { | 49 | export const make = Effect.gen(function* () { |
| 49 | const crypto = yield* Crypto.Crypto; | 50 | const crypto = yield* Crypto.Crypto; |
| 50 | const statesRef = yield* Ref.make<ReadonlyMap<string, AccountRedemptionState>>(new Map()); | 51 | const statesRef = yield* Ref.make<ReadonlyMap<string, AccountRedemptionState>>(new Map()); |
apps/server/src/provider/Layers/codexUsageLimits.ts+1-1| ... | @@ -67,7 +67,7 @@ function labelForKind(kind: ServerProviderUsageWindow["kind"]): string { | ... | @@ -67,7 +67,7 @@ function labelForKind(kind: ServerProviderUsageWindow["kind"]): string { |
| 67 | * `windowDurationMins`; when it does not, paid plans expose the 5-hour and | 67 | * `windowDurationMins`; when it does not, paid plans expose the 5-hour and |
| 68 | * weekly pair and Free/Go expose one monthly allowance. | 68 | * weekly pair and Free/Go expose one monthly allowance. |
| 69 | */ | 69 | */ |
| 70 | export function codexRateLimitsToWindows( | 70 | function codexRateLimitsToWindows( |
| 71 | snapshot: CodexRateLimitSnapshot, | 71 | snapshot: CodexRateLimitSnapshot, |
| 72 | ): ReadonlyArray<ServerProviderUsageWindow> { | 72 | ): ReadonlyArray<ServerProviderUsageWindow> { |
| 73 | // Show the main allowance only. Model-specific notifications (such as Spark) | 73 | // Show the main allowance only. Model-specific notifications (such as Spark) |
apps/server/src/provider/ModelManifest.ts+3-3| ... | @@ -207,7 +207,7 @@ export const encodeManifestCache = Schema.encodeEffect( | ... | @@ -207,7 +207,7 @@ export const encodeManifestCache = Schema.encodeEffect( |
| 207 | ); | 207 | ); |
| 208 | 208 | ||
| 209 | /** True when the manifest classifies `slug` as legacy for `driverKind`. */ | 209 | /** True when the manifest classifies `slug` as legacy for `driverKind`. */ |
| 210 | export function isLegacyModel( | 210 | function isLegacyModel( |
| 211 | manifest: ModelManifestData, | 211 | manifest: ModelManifestData, |
| 212 | driverKind: ProviderDriverKind, | 212 | driverKind: ProviderDriverKind, |
| 213 | slug: string, | 213 | slug: string, |
| ... | @@ -310,8 +310,8 @@ export class ModelManifest extends Context.Service< | ... | @@ -310,8 +310,8 @@ export class ModelManifest extends Context.Service< |
| 310 | } | 310 | } |
| 311 | >()("t3/provider/ModelManifest") {} | 311 | >()("t3/provider/ModelManifest") {} |
| 312 | 312 | ||
| 313 | /** Constant service for tests and callers that only need the bundled data. */ | 313 | /** Constant service backing the bundled-data test layer. */ |
| 314 | export const BundledOnlyModelManifest: ModelManifest["Service"] = { | 314 | const BundledOnlyModelManifest: ModelManifest["Service"] = { |
| 315 | current: Effect.succeed(BUNDLED_MODEL_MANIFEST), | 315 | current: Effect.succeed(BUNDLED_MODEL_MANIFEST), |
| 316 | refresh: Effect.succeed(BUNDLED_MODEL_MANIFEST), | 316 | refresh: Effect.succeed(BUNDLED_MODEL_MANIFEST), |
| 317 | refreshInBackground: Effect.void, | 317 | refreshInBackground: Effect.void, |
apps/server/src/provider/OpenCodeServerOwner.ts+2-1| ... | @@ -8,7 +8,7 @@ import * as Semaphore from "effect/Semaphore"; | ... | @@ -8,7 +8,7 @@ import * as Semaphore from "effect/Semaphore"; |
| 8 | 8 | ||
| 9 | import * as OpenCodeRuntime from "./opencodeRuntime.ts"; | 9 | import * as OpenCodeRuntime from "./opencodeRuntime.ts"; |
| 10 | 10 | ||
| 11 | export const OPENCODE_SERVER_IDLE_TTL = "30 seconds"; | 11 | const OPENCODE_SERVER_IDLE_TTL = "30 seconds"; |
| 12 | 12 | ||
| 13 | interface OpenCodeServerOwnerState { | 13 | interface OpenCodeServerOwnerState { |
| 14 | server: OpenCodeRuntime.OpenCodeServerProcess | null; | 14 | server: OpenCodeRuntime.OpenCodeServerProcess | null; |
| ... | @@ -176,6 +176,7 @@ export const make = Effect.fn("OpenCodeServerOwner.make")(function* (input: { | ... | @@ -176,6 +176,7 @@ export const make = Effect.fn("OpenCodeServerOwner.make")(function* (input: { |
| 176 | }); | 176 | }); |
| 177 | }); | 177 | }); |
| 178 | 178 | ||
| 179 | /** @public Service construction is part of the canonical Effect module API. */ | ||
| 179 | export const layer = (input: { | 180 | export const layer = (input: { |
| 180 | readonly binaryPath: string; | 181 | readonly binaryPath: string; |
| 181 | readonly directory: string; | 182 | readonly directory: string; |
apps/server/src/provider/acp/AntigravityAcpSupport.ts+1-1| ... | @@ -181,7 +181,7 @@ const AUDIO_MIME_TYPES = new Set([ | ... | @@ -181,7 +181,7 @@ const AUDIO_MIME_TYPES = new Set([ |
| 181 | "audio/x-wav", | 181 | "audio/x-wav", |
| 182 | "audio/webm", | 182 | "audio/webm", |
| 183 | ]); | 183 | ]); |
| 184 | export const ANTIGRAVITY_MAX_AUDIO_ATTACHMENT_BYTES = 20 * 1024 * 1024; | 184 | const ANTIGRAVITY_MAX_AUDIO_ATTACHMENT_BYTES = 20 * 1024 * 1024; |
| 185 | const TEXT_MIME_TYPES = new Set([ | 185 | const TEXT_MIME_TYPES = new Set([ |
| 186 | "application/json", | 186 | "application/json", |
| 187 | "application/ld+json", | 187 | "application/ld+json", |
apps/server/src/provider/acp/AntigravityProtocol.ts+1-3| ... | @@ -72,9 +72,7 @@ const decodeSecurityWarning = Schema.decodeUnknownOption( | ... | @@ -72,9 +72,7 @@ const decodeSecurityWarning = Schema.decodeUnknownOption( |
| 72 | * The agent marks "Allow Always" on shell and web tools with a prompt injection | 72 | * The agent marks "Allow Always" on shell and web tools with a prompt injection |
| 73 | * warning in `_meta`. Surface it as option text so both clients can show it. | 73 | * warning in `_meta`. Surface it as option text so both clients can show it. |
| 74 | */ | 74 | */ |
| 75 | export function antigravitySecurityWarning( | 75 | function antigravitySecurityWarning(option: EffectAcpSchema.PermissionOption): string | undefined { |
| 76 | option: EffectAcpSchema.PermissionOption, | ||
| 77 | ): string | undefined { | ||
| 78 | const meta = option._meta; | 76 | const meta = option._meta; |
| 79 | if (!Predicate.isObject(meta)) return undefined; | 77 | if (!Predicate.isObject(meta)) return undefined; |
| 80 | const warning = Option.getOrUndefined(decodeSecurityWarning(meta[SECURITY_WARNING_META_KEY])); | 78 | const warning = Option.getOrUndefined(decodeSecurityWarning(meta[SECURITY_WARNING_META_KEY])); |
apps/server/src/provider/antigravityRelease.ts+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | export const ANTIGRAVITY_RELEASE_VERSION = "agy_acp_server_1.1.1"; | 1 | const ANTIGRAVITY_RELEASE_VERSION = "agy_acp_server_1.1.1"; |
| 2 | 2 | ||
| 3 | export interface AntigravityReleaseAsset { | 3 | export interface AntigravityReleaseAsset { |
| 4 | readonly version: string; | 4 | readonly version: string; |
apps/server/src/provider/opencodeRuntime.ts+2-2| ... | @@ -438,9 +438,9 @@ export function openCodeQuestionId( | ... | @@ -438,9 +438,9 @@ export function openCodeQuestionId( |
| 438 | * puts in the prompt. | 438 | * puts in the prompt. |
| 439 | */ | 439 | */ |
| 440 | const OPENCODE_NATIVE_IMAGE_MIMES = new Set(["image/png", "image/jpeg", "image/gif", "image/webp"]); | 440 | const OPENCODE_NATIVE_IMAGE_MIMES = new Set(["image/png", "image/jpeg", "image/gif", "image/webp"]); |
| 441 | export const OPENCODE_NATIVE_FILE_PART_MAX_BYTES = 20 * 1024 * 1024; | 441 | const OPENCODE_NATIVE_FILE_PART_MAX_BYTES = 20 * 1024 * 1024; |
| 442 | 442 | ||
| 443 | export function isOpenCodeNativeFilePart(input: { | 443 | function isOpenCodeNativeFilePart(input: { |
| 444 | readonly mimeType: string; | 444 | readonly mimeType: string; |
| 445 | readonly sizeBytes: number; | 445 | readonly sizeBytes: number; |
| 446 | }): boolean { | 446 | }): boolean { |
apps/server/src/provider/providerMaintenance.ts+1-1| ... | @@ -33,7 +33,7 @@ const PROVIDER_UPDATE_ACTION_TOAST_MESSAGE = "Install the update now or review p | ... | @@ -33,7 +33,7 @@ const PROVIDER_UPDATE_ACTION_TOAST_MESSAGE = "Install the update now or review p |
| 33 | * move on their own, so this mostly bounds how stale a Homebrew "latest" can | 33 | * move on their own, so this mostly bounds how stale a Homebrew "latest" can |
| 34 | * get; the npm registry check keeps its own cache. | 34 | * get; the npm registry check keeps its own cache. |
| 35 | */ | 35 | */ |
| 36 | export const MAINTENANCE_CAPABILITIES_CACHE_TTL = Duration.hours(1); | 36 | const MAINTENANCE_CAPABILITIES_CACHE_TTL = Duration.hours(1); |
| 37 | 37 | ||
| 38 | const compactEnv = (input: Record<string, Option.Option<string>>): NodeJS.ProcessEnv => | 38 | const compactEnv = (input: Record<string, Option.Option<string>>): NodeJS.ProcessEnv => |
| 39 | Object.fromEntries( | 39 | Object.fromEntries( |
apps/server/src/provider/providerMaintenanceRunner.ts+1| ... | @@ -211,6 +211,7 @@ function makeUpdateState(input: { | ... | @@ -211,6 +211,7 @@ function makeUpdateState(input: { |
| 211 | }; | 211 | }; |
| 212 | } | 212 | } |
| 213 | 213 | ||
| 214 | /** @public Service construction is part of the canonical Effect module API. */ | ||
| 214 | export const make = Effect.fn("ProviderMaintenanceRunner.make")(function* () { | 215 | export const make = Effect.fn("ProviderMaintenanceRunner.make")(function* () { |
| 215 | const providerRegistry = yield* ProviderRegistry; | 216 | const providerRegistry = yield* ProviderRegistry; |
| 216 | const spawner = yield* ChildProcessSpawner.ChildProcessSpawner; | 217 | const spawner = yield* ChildProcessSpawner.ChildProcessSpawner; |
apps/server/src/provider/providerSnapshot.ts-36| ... | @@ -105,42 +105,6 @@ export const spawnAndCollect = (binaryPath: string, command: ChildProcess.Comman | ... | @@ -105,42 +105,6 @@ export const spawnAndCollect = (binaryPath: string, command: ChildProcess.Comman |
| 105 | return result; | 105 | return result; |
| 106 | }).pipe(Effect.scoped); | 106 | }).pipe(Effect.scoped); |
| 107 | 107 | ||
| 108 | export function detailFromResult( | ||
| 109 | result: CommandResult & { readonly timedOut?: boolean }, | ||
| 110 | ): string | undefined { | ||
| 111 | if (result.timedOut) return "Timed out while running command."; | ||
| 112 | const stderr = nonEmptyTrimmed(result.stderr); | ||
| 113 | if (stderr) return stderr; | ||
| 114 | const stdout = nonEmptyTrimmed(result.stdout); | ||
| 115 | if (stdout) return stdout; | ||
| 116 | if (result.code !== 0) { | ||
| 117 | return `Command exited with code ${result.code}.`; | ||
| 118 | } | ||
| 119 | return undefined; | ||
| 120 | } | ||
| 121 | |||
| 122 | export function extractAuthBoolean(value: unknown): boolean | undefined { | ||
| 123 | if (globalThis.Array.isArray(value)) { | ||
| 124 | for (const entry of value) { | ||
| 125 | const nested = extractAuthBoolean(entry); | ||
| 126 | if (nested !== undefined) return nested; | ||
| 127 | } | ||
| 128 | return undefined; | ||
| 129 | } | ||
| 130 | |||
| 131 | if (!value || typeof value !== "object") return undefined; | ||
| 132 | |||
| 133 | const record = value as Record<string, unknown>; | ||
| 134 | for (const key of ["authenticated", "isAuthenticated", "loggedIn", "isLoggedIn"] as const) { | ||
| 135 | if (typeof record[key] === "boolean") return record[key]; | ||
| 136 | } | ||
| 137 | for (const key of ["auth", "status", "session", "account"] as const) { | ||
| 138 | const nested = extractAuthBoolean(record[key]); | ||
| 139 | if (nested !== undefined) return nested; | ||
| 140 | } | ||
| 141 | return undefined; | ||
| 142 | } | ||
| 143 | |||
| 144 | export function parseGenericCliVersion(output: string): string | null { | 108 | export function parseGenericCliVersion(output: string): string | null { |
| 145 | const match = output.match(/\b(\d+\.\d+\.\d+)\b/); | 109 | const match = output.match(/\b(\d+\.\d+\.\d+)\b/); |
| 146 | return match?.[1] ?? null; | 110 | return match?.[1] ?? null; |
apps/server/src/provider/providerUpdateSettings.ts+1-1| ... | @@ -10,7 +10,7 @@ export interface ProviderSnapshotSettings<Settings> { | ... | @@ -10,7 +10,7 @@ export interface ProviderSnapshotSettings<Settings> { |
| 10 | readonly enableProviderUpdateChecks: boolean; | 10 | readonly enableProviderUpdateChecks: boolean; |
| 11 | } | 11 | } |
| 12 | 12 | ||
| 13 | export function makeProviderSnapshotSettings<Settings>( | 13 | function makeProviderSnapshotSettings<Settings>( |
| 14 | provider: Settings, | 14 | provider: Settings, |
| 15 | settings: ServerSettings, | 15 | settings: ServerSettings, |
| 16 | ): ProviderSnapshotSettings<Settings> { | 16 | ): ProviderSnapshotSettings<Settings> { |
apps/server/src/provider/testUtils/providerRegistryMock.ts+1-1| ... | @@ -5,7 +5,7 @@ import * as Layer from "effect/Layer"; | ... | @@ -5,7 +5,7 @@ import * as Layer from "effect/Layer"; |
| 5 | import * as Stream from "effect/Stream"; | 5 | import * as Stream from "effect/Stream"; |
| 6 | import { makeManualOnlyProviderMaintenanceCapabilities } from "../providerMaintenance.ts"; | 6 | import { makeManualOnlyProviderMaintenanceCapabilities } from "../providerMaintenance.ts"; |
| 7 | 7 | ||
| 8 | export const makeProviderRegistryMock = ( | 8 | const makeProviderRegistryMock = ( |
| 9 | providers: ReadonlyArray<ServerProvider> = [], | 9 | providers: ReadonlyArray<ServerProvider> = [], |
| 10 | ): ProviderRegistryShape => ({ | 10 | ): ProviderRegistryShape => ({ |
| 11 | getProviders: Effect.succeed(providers), | 11 | getProviders: Effect.succeed(providers), |