| author | |
| committer | |
| log | 68ab16f3084236871685bed5032cb05c7c436dbd |
| tree | 050b9f440c3622cdaba255ce726bccf26ac1f9a9 |
| parent | b66dd402e86899c3fb4341d46c32d0d69df1eefc |
| signature | Signed by PGP key B5690EEEBB952194 |
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>28 files changed, 196 insertions(+), 91 deletions(-)
apps/server/src/orchestration-v2/AcpRegistryOrchestratorV2.live.test.ts+2| ... | ... | @@ -29,6 +29,7 @@ import * as CodexInstallation from "../provider/CodexInstallation.ts"; |
| 29 | 29 | import * as ServerEnvironment from "../environment/ServerEnvironment.ts"; |
| 30 | 30 | import * as ModelManifest from "../provider/ModelManifest.ts"; |
| 31 | 31 | import * as ProviderInstanceRegistryHydration from "../provider/ProviderInstanceRegistryHydration.ts"; |
| 32 | import * as ProviderLatestVersions from "@t3tools/provider-core/server/ProviderLatestVersions"; | |
| 32 | 33 | import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers"; |
| 33 | 34 | import * as OpenCodeRuntime from "@t3tools/provider-opencode/server/OpenCodeRuntime"; |
| 34 | 35 | import * as OpenCodeServerLedger from "@t3tools/provider-opencode/server/OpenCodeServerLedger"; |
| ... | ... | @@ -112,6 +113,7 @@ const layerProviderInstanceRegistry = ProviderInstanceRegistryHydration.layer.pi |
| 112 | 113 | ProviderEventLoggers.ProviderEventLoggers, |
| 113 | 114 | ProviderEventLoggers.NoOpProviderEventLoggers, |
| 114 | 115 | ), |
| 116 | ProviderLatestVersions.layer, | |
| 115 | 117 | ModelManifest.layerTest, |
| 116 | 118 | AntigravityInstallation.AntigravityInstallation.layer.pipe( |
| 117 | 119 | Layer.provide(layerServerConfig.pipe(Layer.provide(layerPlatformTest))), |
apps/server/src/orchestration-v2/CursorOrchestratorV2.live.test.ts+2| ... | ... | @@ -29,6 +29,7 @@ import * as CodexInstallation from "../provider/CodexInstallation.ts"; |
| 29 | 29 | import * as ServerEnvironment from "../environment/ServerEnvironment.ts"; |
| 30 | 30 | import * as ModelManifest from "../provider/ModelManifest.ts"; |
| 31 | 31 | import * as ProviderInstanceRegistryHydration from "../provider/ProviderInstanceRegistryHydration.ts"; |
| 32 | import * as ProviderLatestVersions from "@t3tools/provider-core/server/ProviderLatestVersions"; | |
| 32 | 33 | import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers"; |
| 33 | 34 | import * as OpenCodeRuntime from "@t3tools/provider-opencode/server/OpenCodeRuntime"; |
| 34 | 35 | import * as OpenCodeServerLedger from "@t3tools/provider-opencode/server/OpenCodeServerLedger"; |
| ... | ... | @@ -92,6 +93,7 @@ const layerProviderInstanceRegistry = ProviderInstanceRegistryHydration.layer.pi |
| 92 | 93 | ProviderEventLoggers.ProviderEventLoggers, |
| 93 | 94 | ProviderEventLoggers.NoOpProviderEventLoggers, |
| 94 | 95 | ), |
| 96 | ProviderLatestVersions.layer, | |
| 95 | 97 | ModelManifest.layerTest, |
| 96 | 98 | AntigravityInstallation.AntigravityInstallation.layer.pipe( |
| 97 | 99 | Layer.provide(layerServerConfig.pipe(Layer.provide(layerPlatformTest))), |
apps/server/src/orchestration-v2/GrokOrchestratorV2.live.test.ts+2| ... | ... | @@ -29,6 +29,7 @@ import * as CodexInstallation from "../provider/CodexInstallation.ts"; |
| 29 | 29 | import * as ServerEnvironment from "../environment/ServerEnvironment.ts"; |
| 30 | 30 | import * as ModelManifest from "../provider/ModelManifest.ts"; |
| 31 | 31 | import * as ProviderInstanceRegistryHydration from "../provider/ProviderInstanceRegistryHydration.ts"; |
| 32 | import * as ProviderLatestVersions from "@t3tools/provider-core/server/ProviderLatestVersions"; | |
| 32 | 33 | import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers"; |
| 33 | 34 | import * as OpenCodeRuntime from "@t3tools/provider-opencode/server/OpenCodeRuntime"; |
| 34 | 35 | import * as OpenCodeServerLedger from "@t3tools/provider-opencode/server/OpenCodeServerLedger"; |
| ... | ... | @@ -91,6 +92,7 @@ const layerProviderInstanceRegistry = ProviderInstanceRegistryHydration.layer.pi |
| 91 | 92 | ProviderEventLoggers.ProviderEventLoggers, |
| 92 | 93 | ProviderEventLoggers.NoOpProviderEventLoggers, |
| 93 | 94 | ), |
| 95 | ProviderLatestVersions.layer, | |
| 94 | 96 | ModelManifest.layerTest, |
| 95 | 97 | AntigravityInstallation.AntigravityInstallation.layer.pipe( |
| 96 | 98 | Layer.provide(layerServerConfig.pipe(Layer.provide(layerPlatformTest))), |
apps/server/src/orchestration-v2/OpenCode2OrchestratorV2.live.test.ts+2| ... | ... | @@ -54,6 +54,7 @@ import * as CodexInstallation from "../provider/CodexInstallation.ts"; |
| 54 | 54 | import * as ServerEnvironment from "../environment/ServerEnvironment.ts"; |
| 55 | 55 | import * as ModelManifest from "../provider/ModelManifest.ts"; |
| 56 | 56 | import * as ProviderInstanceRegistryHydration from "../provider/ProviderInstanceRegistryHydration.ts"; |
| 57 | import * as ProviderLatestVersions from "@t3tools/provider-core/server/ProviderLatestVersions"; | |
| 57 | 58 | import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers"; |
| 58 | 59 | import * as OpenCode2Client from "@t3tools/provider-opencode/server/v2/OpenCode2Client"; |
| 59 | 60 | import * as OpenCodeRuntime from "@t3tools/provider-opencode/server/OpenCodeRuntime"; |
| ... | ... | @@ -185,6 +186,7 @@ const layerProviderInstanceRegistry = ProviderInstanceRegistryHydration.layer.pi |
| 185 | 186 | ProviderEventLoggers.ProviderEventLoggers, |
| 186 | 187 | ProviderEventLoggers.NoOpProviderEventLoggers, |
| 187 | 188 | ), |
| 189 | ProviderLatestVersions.layer, | |
| 188 | 190 | ModelManifest.layerTest, |
| 189 | 191 | AntigravityInstallation.AntigravityInstallation.layer.pipe( |
| 190 | 192 | Layer.provide(layerServerConfig.pipe(Layer.provide(layerPlatformTest))), |
apps/server/src/provider/Drivers/ClaudeDriver.ts+4| ... | ... | @@ -24,6 +24,7 @@ import { HttpClient } from "effect/http"; |
| 24 | 24 | import { ChildProcessSpawner } from "effect/process"; |
| 25 | 25 | |
| 26 | 26 | import { makeClaudeTextGeneration } from "../../textGeneration/ClaudeTextGeneration.ts"; |
| 27 | import * as ProviderLatestVersions from "@t3tools/provider-core/server/ProviderLatestVersions"; | |
| 27 | 28 | import * as ProviderHost from "@t3tools/provider-core/server/ProviderHost"; |
| 28 | 29 | import * as ServerConfig from "../../config.ts"; |
| 29 | 30 | import { expandHomePath } from "@t3tools/provider-core/server/pathExpansion"; |
| ... | ... | @@ -100,6 +101,7 @@ export type ClaudeDriverEnv = |
| 100 | 101 | | Crypto.Crypto |
| 101 | 102 | | FileSystem.FileSystem |
| 102 | 103 | | HttpClient.HttpClient |
| 104 | | ProviderLatestVersions.ProviderLatestVersions | |
| 103 | 105 | | ModelCatalog.ModelCatalog |
| 104 | 106 | | Path.Path |
| 105 | 107 | | ProviderEventLoggers.ProviderEventLoggers |
| ... | ... | @@ -120,6 +122,7 @@ export const ClaudeDriver: ProviderDriver<ClaudeSettings, ClaudeDriverEnv> = { |
| 120 | 122 | const path = yield* Path.Path; |
| 121 | 123 | const { cwd } = yield* ServerConfig.ServerConfig; |
| 122 | 124 | const httpClient = yield* HttpClient.HttpClient; |
| 125 | const latestVersions = yield* ProviderLatestVersions.ProviderLatestVersions; | |
| 123 | 126 | const resetCreditCoordinator = yield* ResetCreditCoordinator.ResetCreditCoordinator; |
| 124 | 127 | const catalogService = yield* ModelCatalog.ModelCatalog; |
| 125 | 128 | const modelCatalog = catalogService |
| ... | ... | @@ -256,6 +259,7 @@ export const ClaudeDriver: ProviderDriver<ClaudeSettings, ClaudeDriverEnv> = { |
| 256 | 259 | }), |
| 257 | 260 | ), |
| 258 | 261 | Effect.provideService(HttpClient.HttpClient, httpClient), |
| 262 | Effect.provideService(ProviderLatestVersions.ProviderLatestVersions, latestVersions), | |
| 259 | 263 | Effect.flatMap((enrichedSnapshot) => publishSnapshot(enrichedSnapshot)), |
| 260 | 264 | ), |
| 261 | 265 | }).pipe( |
apps/server/src/provider/Drivers/CodexDriver.test.ts+5-6| ... | ... | @@ -26,11 +26,11 @@ import * as BackgroundPolicy from "../../background/BackgroundPolicy.ts"; |
| 26 | 26 | import * as ServerConfig from "../../config.ts"; |
| 27 | 27 | import * as ServerSettings from "../../serverSettings.ts"; |
| 28 | 28 | import * as ResetCreditCoordinator from "../resetCreditCoordinator.ts"; |
| 29 | import * as ProviderLatestVersions from "@t3tools/provider-core/server/ProviderLatestVersions"; | |
| 29 | 30 | import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers"; |
| 30 | 31 | import * as ModelManifest from "../ModelManifest.ts"; |
| 31 | 32 | import { |
| 32 | 33 | createProviderVersionAdvisory, |
| 33 | ProviderVersionCache, | |
| 34 | 34 | resolveLatestProviderVersion, |
| 35 | 35 | } from "@t3tools/provider-core/server/maintenanceResolver"; |
| 36 | 36 | import { CodexDriver } from "./CodexDriver.ts"; |
| ... | ... | @@ -75,6 +75,7 @@ const layerDeps = ServerConfig.layerTest(process.cwd(), { |
| 75 | 75 | ProviderEventLoggers.NoOpProviderEventLoggers, |
| 76 | 76 | ), |
| 77 | 77 | ), |
| 78 | Layer.provideMerge(ProviderLatestVersions.layer), | |
| 78 | 79 | Layer.provideMerge( |
| 79 | 80 | Layer.succeed( |
| 80 | 81 | HttpClient.HttpClient, |
| ... | ... | @@ -557,11 +558,9 @@ it.layer(layerTest)("CodexDriver", (it) => { |
| 557 | 558 | }).pipe(Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, metadataSpawner)); |
| 558 | 559 | const capabilities = yield* instance.snapshot.resolveMaintenance(); |
| 559 | 560 | const latestVersion = yield* resolveLatestProviderVersion(capabilities).pipe( |
| 560 | Effect.provideService( | |
| 561 | ProviderVersionCache, | |
| 562 | new Map([ | |
| 563 | ["@openai/codex", { expiresAt: Number.MAX_SAFE_INTEGER, version: "0.153.4" }], | |
| 564 | ]), | |
| 561 | Effect.provideServiceEffect( | |
| 562 | ProviderLatestVersions.ProviderLatestVersions, | |
| 563 | ProviderLatestVersions.make([["@openai/codex", "0.153.4"]]), | |
| 565 | 564 | ), |
| 566 | 565 | ); |
| 567 | 566 | expect(probes).toEqual([]); |
apps/server/src/provider/Drivers/CodexDriver.ts+4| ... | ... | @@ -31,6 +31,7 @@ import { HttpClient } from "effect/http"; |
| 31 | 31 | import { ChildProcessSpawner } from "effect/process"; |
| 32 | 32 | |
| 33 | 33 | import { makeCodexTextGeneration } from "../../textGeneration/CodexTextGeneration.ts"; |
| 34 | import * as ProviderLatestVersions from "@t3tools/provider-core/server/ProviderLatestVersions"; | |
| 34 | 35 | import * as ProviderHost from "@t3tools/provider-core/server/ProviderHost"; |
| 35 | 36 | import * as ServerConfig from "../../config.ts"; |
| 36 | 37 | import { expandHomePath } from "@t3tools/provider-core/server/pathExpansion"; |
| ... | ... | @@ -115,6 +116,7 @@ export type CodexDriverEnv = |
| 115 | 116 | | Crypto.Crypto |
| 116 | 117 | | FileSystem.FileSystem |
| 117 | 118 | | HttpClient.HttpClient |
| 119 | | ProviderLatestVersions.ProviderLatestVersions | |
| 118 | 120 | | ModelCatalog.ModelCatalog |
| 119 | 121 | | Path.Path |
| 120 | 122 | | ProviderEventLoggers.ProviderEventLoggers |
| ... | ... | @@ -147,6 +149,7 @@ export const CodexDriver: ProviderDriver<CodexSettings, CodexDriverEnv> = { |
| 147 | 149 | const fileSystem = yield* FileSystem.FileSystem; |
| 148 | 150 | const pathService = yield* Path.Path; |
| 149 | 151 | const httpClient = yield* HttpClient.HttpClient; |
| 152 | const latestVersions = yield* ProviderLatestVersions.ProviderLatestVersions; | |
| 150 | 153 | const modelCatalog = yield* ModelCatalog.ModelCatalog; |
| 151 | 154 | const currentCatalog = modelCatalog.current(DRIVER_KIND); |
| 152 | 155 | const processEnv = mergeProviderInstanceEnvironment(environment); |
| ... | ... | @@ -251,6 +254,7 @@ export const CodexDriver: ProviderDriver<CodexSettings, CodexDriverEnv> = { |
| 251 | 254 | }), |
| 252 | 255 | ), |
| 253 | 256 | Effect.provideService(HttpClient.HttpClient, httpClient), |
| 257 | Effect.provideService(ProviderLatestVersions.ProviderLatestVersions, latestVersions), | |
| 254 | 258 | Effect.flatMap((enrichedSnapshot) => publishSnapshot(enrichedSnapshot)), |
| 255 | 259 | ), |
| 256 | 260 | }).pipe( |
apps/server/src/provider/ProviderInstanceRegistry.test.ts+3| ... | ... | @@ -35,6 +35,7 @@ import { |
| 35 | 35 | type ProviderInstanceConfigMap, |
| 36 | 36 | ProviderInstanceId, |
| 37 | 37 | } from "@t3tools/contracts"; |
| 38 | import * as ProviderLatestVersions from "@t3tools/provider-core/server/ProviderLatestVersions"; | |
| 38 | 39 | import type { GrokSettings } from "@t3tools/provider-grok/settings"; |
| 39 | 40 | import type { CursorSettings } from "@t3tools/provider-cursor/settings"; |
| 40 | 41 | import { HostProcessPlatform, isHostWindows } from "@t3tools/shared/hostProcess"; |
| ... | ... | @@ -228,6 +229,7 @@ describe("ProviderInstanceRegistry — multi-instance codex slice", () => { |
| 228 | 229 | ProviderEventLoggers.NoOpProviderEventLoggers, |
| 229 | 230 | ), |
| 230 | 231 | ), |
| 232 | Layer.provideMerge(ProviderLatestVersions.layer), | |
| 231 | 233 | Layer.provideMerge(ModelManifest.layerTest), |
| 232 | 234 | Layer.provideMerge(ResetCreditCoordinator.layerTest), |
| 233 | 235 | ); |
| ... | ... | @@ -609,6 +611,7 @@ describe("ProviderInstanceRegistry — all drivers slice", () => { |
| 609 | 611 | ProviderEventLoggers.NoOpProviderEventLoggers, |
| 610 | 612 | ), |
| 611 | 613 | ), |
| 614 | Layer.provideMerge(ProviderLatestVersions.layer), | |
| 612 | 615 | Layer.provideMerge(ModelManifest.layerTest), |
| 613 | 616 | Layer.provideMerge(ResetCreditCoordinator.layerTest), |
| 614 | 617 | ); |
apps/server/src/provider/ProviderRegistry.test.ts+5| ... | ... | @@ -45,6 +45,7 @@ import * as AntigravityInstallation from "./AntigravityInstallation.ts"; |
| 45 | 45 | import * as ModelManifest from "./ModelManifest.ts"; |
| 46 | 46 | import { applyProviderCompatibility } from "./providerCompatibility.ts"; |
| 47 | 47 | import * as ResetCreditCoordinator from "./resetCreditCoordinator.ts"; |
| 48 | import * as ProviderLatestVersions from "@t3tools/provider-core/server/ProviderLatestVersions"; | |
| 48 | 49 | import * as OpenCodeRuntime from "@t3tools/provider-opencode/server/OpenCodeRuntime"; |
| 49 | 50 | import * as OpenCodeServerLedger from "@t3tools/provider-opencode/server/OpenCodeServerLedger"; |
| 50 | 51 | import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers"; |
| ... | ... | @@ -2759,6 +2760,7 @@ it.layer( |
| 2759 | 2760 | ProviderEventLoggers.NoOpProviderEventLoggers, |
| 2760 | 2761 | ), |
| 2761 | 2762 | ), |
| 2763 | Layer.provideMerge(ProviderLatestVersions.layer), | |
| 2762 | 2764 | Layer.provideMerge(ModelManifest.layerTest), |
| 2763 | 2765 | Layer.provideMerge(ResetCreditCoordinator.layerTest), |
| 2764 | 2766 | Layer.provideMerge( |
| ... | ... | @@ -2862,6 +2864,7 @@ it.layer( |
| 2862 | 2864 | ProviderEventLoggers.NoOpProviderEventLoggers, |
| 2863 | 2865 | ), |
| 2864 | 2866 | ), |
| 2867 | Layer.provideMerge(ProviderLatestVersions.layer), | |
| 2865 | 2868 | Layer.provideMerge(ModelManifest.layerTest), |
| 2866 | 2869 | Layer.provideMerge(ResetCreditCoordinator.layerTest), |
| 2867 | 2870 | Layer.provideMerge( |
| ... | ... | @@ -2980,6 +2983,7 @@ it.layer( |
| 2980 | 2983 | ProviderEventLoggers.NoOpProviderEventLoggers, |
| 2981 | 2984 | ), |
| 2982 | 2985 | ), |
| 2986 | Layer.provideMerge(ProviderLatestVersions.layer), | |
| 2983 | 2987 | Layer.provideMerge(ModelManifest.layerTest), |
| 2984 | 2988 | Layer.provideMerge(ResetCreditCoordinator.layerTest), |
| 2985 | 2989 | Layer.provideMerge( |
| ... | ... | @@ -3038,6 +3042,7 @@ it.layer( |
| 3038 | 3042 | ProviderEventLoggers.NoOpProviderEventLoggers, |
| 3039 | 3043 | ), |
| 3040 | 3044 | ), |
| 3045 | Layer.provideMerge(ProviderLatestVersions.layer), | |
| 3041 | 3046 | Layer.provideMerge(ModelManifest.layerTest), |
| 3042 | 3047 | Layer.provideMerge(ResetCreditCoordinator.layerTest), |
| 3043 | 3048 | Layer.provideMerge( |
apps/server/src/provider/providerMaintenanceRunner.test.ts+2-2| ... | ... | @@ -26,9 +26,9 @@ import { SpawnExecutableResolution } from "@t3tools/shared/shell"; |
| 26 | 26 | import * as ProviderRegistry from "./ProviderRegistry.ts"; |
| 27 | 27 | import * as ModelManifest from "./ModelManifest.ts"; |
| 28 | 28 | import * as ProviderMaintenanceRunner from "./providerMaintenanceRunner.ts"; |
| 29 | import * as ProviderLatestVersions from "@t3tools/provider-core/server/ProviderLatestVersions"; | |
| 29 | 30 | import { |
| 30 | 31 | makeProviderMaintenanceCapabilities, |
| 31 | ProviderVersionCache, | |
| 32 | 32 | type ProviderMaintenanceCapabilities, |
| 33 | 33 | } from "@t3tools/provider-core/server/maintenanceResolver"; |
| 34 | 34 | const isServerProviderUpdateError = Schema.is(ServerProviderUpdateError); |
| ... | ... | @@ -239,7 +239,7 @@ const makeTestRunner = ( |
| 239 | 239 | refreshInBackground: Effect.void, |
| 240 | 240 | }), |
| 241 | 241 | // Fresh per runner so a version cached by one test cannot leak into another. |
| 242 | Layer.sync(ProviderVersionCache, () => new Map()), | |
| 242 | ProviderLatestVersions.layer, | |
| 243 | 243 | ), |
| 244 | 244 | ), |
| 245 | 245 | ), |
apps/server/src/provider/providerMaintenanceRunner.ts+7-4| ... | ... | @@ -33,8 +33,8 @@ import { |
| 33 | 33 | makeTargetedProviderUpdateAction, |
| 34 | 34 | resolveLatestProviderVersion, |
| 35 | 35 | type ProviderMaintenanceCommandAction, |
| 36 | ProviderVersionCache, | |
| 37 | 36 | } from "@t3tools/provider-core/server/maintenanceResolver"; |
| 37 | import * as ProviderLatestVersions from "@t3tools/provider-core/server/ProviderLatestVersions"; | |
| 38 | 38 | import type { ProviderMaintenanceCapabilities } from "@t3tools/provider-core/server/maintenanceResolver"; |
| 39 | 39 | import { collectUint8StreamText } from "@t3tools/provider-core/server/collectStreamText"; |
| 40 | 40 | const isServerProviderUpdateError = Schema.is(ServerProviderUpdateError); |
| ... | ... | @@ -291,7 +291,7 @@ export const make = Effect.fn("ProviderMaintenanceRunner.make")(function* () { |
| 291 | 291 | const manifestService = yield* ModelManifest.ModelManifest; |
| 292 | 292 | const spawner = yield* ChildProcessSpawner.ChildProcessSpawner; |
| 293 | 293 | const httpClient = yield* HttpClient.HttpClient; |
| 294 | const versionCache = yield* ProviderVersionCache; | |
| 294 | const latestVersions = yield* ProviderLatestVersions.ProviderLatestVersions; | |
| 295 | 295 | const runMaintenanceCommand = ( |
| 296 | 296 | update: ProviderMaintenanceCommandAction, |
| 297 | 297 | onProgress: (line: string) => Effect.Effect<void>, |
| ... | ... | @@ -356,7 +356,7 @@ export const make = Effect.fn("ProviderMaintenanceRunner.make")(function* () { |
| 356 | 356 | maintenanceCapabilities, |
| 357 | 357 | ).pipe( |
| 358 | 358 | Effect.provideService(HttpClient.HttpClient, httpClient), |
| 359 | Effect.provideService(ProviderVersionCache, versionCache), | |
| 359 | Effect.provideService(ProviderLatestVersions.ProviderLatestVersions, latestVersions), | |
| 360 | 360 | ), |
| 361 | 361 | { |
| 362 | 362 | concurrency: "unbounded", |
| ... | ... | @@ -463,7 +463,10 @@ export const make = Effect.fn("ProviderMaintenanceRunner.make")(function* () { |
| 463 | 463 | targetVersion ?? |
| 464 | 464 | (yield* resolveLatestProviderVersion(fresh).pipe( |
| 465 | 465 | Effect.provideService(HttpClient.HttpClient, httpClient), |
| 466 | Effect.provideService(ProviderVersionCache, versionCache), | |
| 466 | Effect.provideService( | |
| 467 | ProviderLatestVersions.ProviderLatestVersions, | |
| 468 | latestVersions, | |
| 469 | ), | |
| 467 | 470 | )); |
| 468 | 471 | const advisory = |
| 469 | 472 | resolveProviderCompatibility(manifest.compatibility, provider, candidateVersion) ?? |
apps/server/src/server.ts+2| ... | ... | @@ -44,6 +44,7 @@ import * as ProviderEventIngestor from "./orchestration-v2/ProviderEventIngestor |
| 44 | 44 | import * as ModelManifest from "./provider/ModelManifest.ts"; |
| 45 | 45 | import * as ResetCreditCoordinator from "./provider/resetCreditCoordinator.ts"; |
| 46 | 46 | import * as EventNdjsonLogger from "./provider/EventNdjsonLogger.ts"; |
| 47 | import * as ProviderLatestVersions from "@t3tools/provider-core/server/ProviderLatestVersions"; | |
| 47 | 48 | import * as OpenCodeRuntime from "@t3tools/provider-opencode/server/OpenCodeRuntime"; |
| 48 | 49 | import * as OpenCodeServerLedger from "@t3tools/provider-opencode/server/OpenCodeServerLedger"; |
| 49 | 50 | import * as ProviderHostLive from "./provider/ProviderHostLive.ts"; |
| ... | ... | @@ -634,6 +635,7 @@ const layerRuntimeCoreDependencies = layerRuntimeCoreDependenciesBase.pipe( |
| 634 | 635 | layerProviderEventLoggers, |
| 635 | 636 | ModelManifest.layerModelCatalog.pipe(Layer.provideMerge(ModelManifest.layer)), |
| 636 | 637 | ResetCreditCoordinator.layer, |
| 638 | ProviderLatestVersions.layer, | |
| 637 | 639 | ), |
| 638 | 640 | ), |
| 639 | 641 | // `OpenCodeDriver.create()` yields `OpenCodeRuntime`; previously the old |
apps/server/src/ws.ts+3-3| ... | ... | @@ -168,7 +168,7 @@ import * as ProviderInstanceRegistry from "./provider/ProviderInstanceRegistry.t |
| 168 | 168 | import * as AcpRegistrySupport from "@t3tools/provider-acp-registry/server/AcpRegistrySupport"; |
| 169 | 169 | import * as AcpRegistryRuntimeCoordinator from "@t3tools/provider-acp-registry/server/AcpRegistryRuntimeCoordinator"; |
| 170 | 170 | import * as ModelManifest from "./provider/ModelManifest.ts"; |
| 171 | import * as ProviderMaintenance from "@t3tools/provider-core/server/maintenanceResolver"; | |
| 171 | import * as ProviderLatestVersions from "@t3tools/provider-core/server/ProviderLatestVersions"; | |
| 172 | 172 | import * as ProviderMaintenanceRunner from "./provider/providerMaintenanceRunner.ts"; |
| 173 | 173 | import * as ProviderAuthService from "./provider/ProviderAuthService.ts"; |
| 174 | 174 | import { makeProviderInstallation } from "./provider/providerInstallation.ts"; |
| ... | ... | @@ -1271,7 +1271,7 @@ const layerWsRpc = ( |
| 1271 | 1271 | const portDiscovery = yield* PortScanner.PortDiscovery; |
| 1272 | 1272 | const providerRegistry = yield* ProviderRegistry.ProviderRegistry; |
| 1273 | 1273 | const modelManifest = yield* ModelManifest.ModelManifest; |
| 1274 | const providerVersionCache = yield* ProviderMaintenance.ProviderVersionCache; | |
| 1274 | const providerLatestVersions = yield* ProviderLatestVersions.ProviderLatestVersions; | |
| 1275 | 1275 | const providerInstances = yield* ProviderInstanceRegistry.ProviderInstanceRegistry; |
| 1276 | 1276 | const acpRegistryCatalog = yield* AcpRegistrySupport.AcpRegistryCatalog; |
| 1277 | 1277 | const acpRegistryRuntimeCoordinator = |
| ... | ... | @@ -2193,7 +2193,7 @@ const layerWsRpc = ( |
| 2193 | 2193 | fresh: true, |
| 2194 | 2194 | }); |
| 2195 | 2195 | if (maintenance.packageName) |
| 2196 | providerVersionCache.delete(maintenance.packageName); | |
| 2196 | yield* providerLatestVersions.invalidate(maintenance.packageName); | |
| 2197 | 2197 | }), |
| 2198 | 2198 | { concurrency: "unbounded", discard: true }, |
| 2199 | 2199 | ); |
packages/provider-core/package.json+4| ... | ... | @@ -115,6 +115,10 @@ |
| 115 | 115 | "types": "./src/server/ProviderHost.ts", |
| 116 | 116 | "import": "./src/server/ProviderHost.ts" |
| 117 | 117 | }, |
| 118 | "./server/ProviderLatestVersions": { | |
| 119 | "types": "./src/server/ProviderLatestVersions.ts", | |
| 120 | "import": "./src/server/ProviderLatestVersions.ts" | |
| 121 | }, | |
| 118 | 122 | "./server/randomUuid": { |
| 119 | 123 | "types": "./src/server/randomUuid.ts", |
| 120 | 124 | "import": "./src/server/randomUuid.ts" |
packages/provider-core/src/server/ProviderLatestVersions.ts created+75| ... | ... | @@ -0,0 +1,75 @@ |
| 1 | /** | |
| 2 | * Memoizes "latest available version" lookups (npm registry, release | |
| 3 | * channels) for the version advisory. One instance is shared by every driver | |
| 4 | * and the maintenance runner, so a provider update can invalidate the entry | |
| 5 | * the next snapshot refresh reads. | |
| 6 | * | |
| 7 | * @module provider-core/server/ProviderLatestVersions | |
| 8 | */ | |
| 9 | import * as Context from "effect/Context"; | |
| 10 | import * as DateTime from "effect/DateTime"; | |
| 11 | import * as Duration from "effect/Duration"; | |
| 12 | import * as Effect from "effect/Effect"; | |
| 13 | import * as Layer from "effect/Layer"; | |
| 14 | import * as Ref from "effect/Ref"; | |
| 15 | ||
| 16 | /** A found version stays fresh this long. */ | |
| 17 | const FOUND_TTL = Duration.hours(1); | |
| 18 | /** A failed lookup is retried sooner, so a flaky network does not hide an update for an hour. */ | |
| 19 | const MISSING_TTL = Duration.minutes(1); | |
| 20 | ||
| 21 | interface Entry { | |
| 22 | readonly expiresAt: number; | |
| 23 | readonly version: string | null; | |
| 24 | } | |
| 25 | ||
| 26 | export class ProviderLatestVersions extends Context.Service< | |
| 27 | ProviderLatestVersions, | |
| 28 | { | |
| 29 | /** | |
| 30 | * Returns the cached version for `key`, running `lookup` when there is no | |
| 31 | * fresh entry or `fresh` is set. `null` means the lookup found nothing. | |
| 32 | */ | |
| 33 | readonly cached: <E, R>( | |
| 34 | key: string, | |
| 35 | lookup: Effect.Effect<string | null, E, R>, | |
| 36 | options?: { readonly fresh?: boolean }, | |
| 37 | ) => Effect.Effect<string | null, E, R>; | |
| 38 | readonly invalidate: (key: string) => Effect.Effect<void>; | |
| 39 | } | |
| 40 | >()("@t3tools/provider-core/server/ProviderLatestVersions") {} | |
| 41 | ||
| 42 | /** Starts from `seed`, for tests that need a cached entry. */ | |
| 43 | export const make = (seed: Iterable<readonly [key: string, version: string | null]> = []) => | |
| 44 | Effect.gen(function* () { | |
| 45 | const entries = yield* Ref.make( | |
| 46 | new Map<string, Entry>( | |
| 47 | Array.from(seed, ([key, version]) => [ | |
| 48 | key, | |
| 49 | { version, expiresAt: Number.MAX_SAFE_INTEGER }, | |
| 50 | ]), | |
| 51 | ), | |
| 52 | ); | |
| 53 | return ProviderLatestVersions.of({ | |
| 54 | cached: (key, lookup, options) => | |
| 55 | Effect.gen(function* () { | |
| 56 | const now = DateTime.toEpochMillis(yield* DateTime.now); | |
| 57 | const existing = (yield* Ref.get(entries)).get(key); | |
| 58 | if (!options?.fresh && existing && existing.expiresAt > now) return existing.version; | |
| 59 | const version = yield* lookup; | |
| 60 | const ttl = version === null ? MISSING_TTL : FOUND_TTL; | |
| 61 | yield* Ref.update(entries, (current) => | |
| 62 | new Map(current).set(key, { version, expiresAt: now + Duration.toMillis(ttl) }), | |
| 63 | ); | |
| 64 | return version; | |
| 65 | }), | |
| 66 | invalidate: (key) => | |
| 67 | Ref.update(entries, (current) => { | |
| 68 | const next = new Map(current); | |
| 69 | next.delete(key); | |
| 70 | return next; | |
| 71 | }), | |
| 72 | }); | |
| 73 | }); | |
| 74 | ||
| 75 | export const layer = Layer.effect(ProviderLatestVersions, make()); |
packages/provider-core/src/server/maintenanceResolver.test.ts+7-15| ... | ... | @@ -15,6 +15,7 @@ import * as Sink from "effect/Sink"; |
| 15 | 15 | import * as Stream from "effect/Stream"; |
| 16 | 16 | import { HttpClient } from "effect/http"; |
| 17 | 17 | import { ChildProcessSpawner } from "effect/process"; |
| 18 | import * as ProviderLatestVersions from "./ProviderLatestVersions.ts"; | |
| 18 | 19 | import { |
| 19 | 20 | createProviderVersionAdvisory, |
| 20 | 21 | makeTargetedProviderUpdateAction, |
| ... | ... | @@ -26,7 +27,6 @@ import { |
| 26 | 27 | normalizeCommandPath, |
| 27 | 28 | npmGlobalPrefixFromCommandPath, |
| 28 | 29 | parseHomebrewLatestVersion, |
| 29 | ProviderVersionCache, | |
| 30 | 30 | resolveLatestProviderVersion, |
| 31 | 31 | resolvePackageManagedProviderMaintenance, |
| 32 | 32 | resolveProviderMaintenanceCapabilitiesEffect, |
| ... | ... | @@ -124,19 +124,11 @@ function stdoutSpawner(onSpawn: (command: string, args: ReadonlyArray<string>) = |
| 124 | 124 | } |
| 125 | 125 | |
| 126 | 126 | it.layer(NodeServices.layer)("providerMaintenance", (it) => { |
| 127 | it.effect("reads cached versions through the injectable cache reference", () => | |
| 127 | it.effect("reads cached versions through ProviderLatestVersions", () => | |
| 128 | 128 | resolveLatestProviderVersion(manualPackageTool).pipe( |
| 129 | Effect.provideService( | |
| 130 | ProviderVersionCache, | |
| 131 | new Map([ | |
| 132 | [ | |
| 133 | "@example/package-tool", | |
| 134 | { | |
| 135 | expiresAt: Number.MAX_SAFE_INTEGER, | |
| 136 | version: "9.9.9", | |
| 137 | }, | |
| 138 | ], | |
| 139 | ]), | |
| 129 | Effect.provideServiceEffect( | |
| 130 | ProviderLatestVersions.ProviderLatestVersions, | |
| 131 | ProviderLatestVersions.make([["@example/package-tool", "9.9.9"]]), | |
| 140 | 132 | ), |
| 141 | 133 | Effect.provideService( |
| 142 | 134 | HttpClient.HttpClient, |
| ... | ... | @@ -152,7 +144,7 @@ it.layer(NodeServices.layer)("providerMaintenance", (it) => { |
| 152 | 144 | |
| 153 | 145 | it.effect("prefers the installer's own latest version over the npm registry", () => |
| 154 | 146 | resolveLatestProviderVersion({ ...manualPackageTool, latestVersion: "1.2.0" }).pipe( |
| 155 | Effect.provideService(ProviderVersionCache, new Map()), | |
| 147 | Effect.provide(ProviderLatestVersions.layer), | |
| 156 | 148 | Effect.provideService( |
| 157 | 149 | HttpClient.HttpClient, |
| 158 | 150 | HttpClient.make(() => |
| ... | ... | @@ -169,7 +161,7 @@ it.layer(NodeServices.layer)("providerMaintenance", (it) => { |
| 169 | 161 | enrichProviderSnapshotWithVersionAdvisory(installedPackageToolProvider, manualPackageTool, { |
| 170 | 162 | enableProviderUpdateChecks: false, |
| 171 | 163 | }).pipe( |
| 172 | Effect.provideService(ProviderVersionCache, new Map()), | |
| 164 | Effect.provide(ProviderLatestVersions.layer), | |
| 173 | 165 | Effect.provideService( |
| 174 | 166 | HttpClient.HttpClient, |
| 175 | 167 | HttpClient.make(() => |
packages/provider-core/src/server/maintenanceResolver.ts+3-26| ... | ... | @@ -17,7 +17,6 @@ import { causeErrorTag } from "@t3tools/shared/observability"; |
| 17 | 17 | import { resolveCommandPath } from "@t3tools/shared/shell"; |
| 18 | 18 | import * as Cache from "effect/Cache"; |
| 19 | 19 | import * as Config from "effect/Config"; |
| 20 | import * as Context from "effect/Context"; | |
| 21 | 20 | import * as DateTime from "effect/DateTime"; |
| 22 | 21 | import * as Duration from "effect/Duration"; |
| 23 | 22 | import * as Effect from "effect/Effect"; |
| ... | ... | @@ -30,8 +29,8 @@ import { HttpClient, HttpClientRequest } from "effect/http"; |
| 30 | 29 | import { ChildProcess, ChildProcessSpawner } from "effect/process"; |
| 31 | 30 | |
| 32 | 31 | import { collectUint8StreamText } from "./collectStreamText.ts"; |
| 32 | import * as ProviderLatestVersions from "./ProviderLatestVersions.ts"; | |
| 33 | 33 | |
| 34 | const LATEST_VERSION_CACHE_TTL_MS = 60 * 60 * 1_000; | |
| 35 | 34 | const LATEST_VERSION_TIMEOUT_MS = 4_000; |
| 36 | 35 | const HOMEBREW_INFO_TIMEOUT_MS = 10_000; |
| 37 | 36 | const HOMEBREW_INFO_MAX_BYTES = 256 * 1_024; |
| ... | ... | @@ -102,17 +101,6 @@ export interface PackageManagedProviderMaintenanceDefinition { |
| 102 | 101 | } | null; |
| 103 | 102 | } |
| 104 | 103 | |
| 105 | export interface ProviderVersionCacheEntry { | |
| 106 | readonly expiresAt: number; | |
| 107 | readonly version: string | null; | |
| 108 | } | |
| 109 | ||
| 110 | export const ProviderVersionCache = Context.Reference<Map<string, ProviderVersionCacheEntry>>( | |
| 111 | "@t3tools/server/providerMaintenance/ProviderVersionCache", | |
| 112 | { | |
| 113 | defaultValue: () => new Map(), | |
| 114 | }, | |
| 115 | ); | |
| 116 | 104 | const NpmLatestVersionResponse = Schema.Struct({ |
| 117 | 105 | version: Schema.optional(Schema.String), |
| 118 | 106 | }); |
| ... | ... | @@ -784,19 +772,8 @@ export const resolveLatestProviderVersion = Effect.fn("resolveLatestProviderVers |
| 784 | 772 | return null; |
| 785 | 773 | } |
| 786 | 774 | |
| 787 | const latestVersionCache = yield* ProviderVersionCache; | |
| 788 | const cached = latestVersionCache.get(packageName); | |
| 789 | const now = DateTime.toEpochMillis(yield* DateTime.now); | |
| 790 | if (cached && cached.expiresAt > now) { | |
| 791 | return cached.version; | |
| 792 | } | |
| 793 | ||
| 794 | const version = yield* fetchNpmLatestVersion(packageName); | |
| 795 | latestVersionCache.set(packageName, { | |
| 796 | expiresAt: now + LATEST_VERSION_CACHE_TTL_MS, | |
| 797 | version, | |
| 798 | }); | |
| 799 | return version; | |
| 775 | const latestVersions = yield* ProviderLatestVersions.ProviderLatestVersions; | |
| 776 | return yield* latestVersions.cached(packageName, fetchNpmLatestVersion(packageName)); | |
| 800 | 777 | }); |
| 801 | 778 | |
| 802 | 779 | export const enrichProviderSnapshotWithVersionAdvisory = Effect.fn( |
packages/provider-grok/src/server/driver.test.ts+2| ... | ... | @@ -9,6 +9,7 @@ import * as Path from "effect/Path"; |
| 9 | 9 | import { HttpClient } from "effect/http"; |
| 10 | 10 | import * as ChildProcessSpawner from "effect/process/ChildProcessSpawner"; |
| 11 | 11 | |
| 12 | import * as ProviderLatestVersions from "@t3tools/provider-core/server/ProviderLatestVersions"; | |
| 12 | 13 | import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers"; |
| 13 | 14 | import { layerTestProviderHost } from "@t3tools/provider-testing/host"; |
| 14 | 15 | import { GrokDriver } from "./driver.ts"; |
| ... | ... | @@ -18,6 +19,7 @@ import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; |
| 18 | 19 | const layerTest = layerTestProviderHost({ runBackgroundWork: false }).pipe( |
| 19 | 20 | Layer.provideMerge(NodeServices.layer), |
| 20 | 21 | Layer.provideMerge(IdAllocator.layer), |
| 22 | Layer.provideMerge(ProviderLatestVersions.layer), | |
| 21 | 23 | Layer.provideMerge( |
| 22 | 24 | Layer.succeed( |
| 23 | 25 | ProviderEventLoggers.ProviderEventLoggers, |
packages/provider-grok/src/server/driver.ts+5-1| ... | ... | @@ -8,6 +8,7 @@ import * as Schema from "effect/Schema"; |
| 8 | 8 | import { HttpClient } from "effect/http"; |
| 9 | 9 | import { ChildProcessSpawner } from "effect/process"; |
| 10 | 10 | |
| 11 | import * as ProviderLatestVersions from "@t3tools/provider-core/server/ProviderLatestVersions"; | |
| 11 | 12 | import * as ProviderHost from "@t3tools/provider-core/server/ProviderHost"; |
| 12 | 13 | import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers"; |
| 13 | 14 | import { makeGrokTextGeneration } from "./textGeneration.ts"; |
| ... | ... | @@ -77,6 +78,7 @@ export type GrokDriverEnv = |
| 77 | 78 | | Crypto.Crypto |
| 78 | 79 | | FileSystem.FileSystem |
| 79 | 80 | | HttpClient.HttpClient |
| 81 | | ProviderLatestVersions.ProviderLatestVersions | |
| 80 | 82 | | Path.Path |
| 81 | 83 | | ProviderEventLoggers.ProviderEventLoggers; |
| 82 | 84 | |
| ... | ... | @@ -93,6 +95,7 @@ export const GrokDriver: ProviderDriver<GrokSettings, GrokDriverEnv> = { |
| 93 | 95 | const crypto = yield* Crypto.Crypto; |
| 94 | 96 | const spawner = yield* ChildProcessSpawner.ChildProcessSpawner; |
| 95 | 97 | const httpClient = yield* HttpClient.HttpClient; |
| 98 | const latestVersions = yield* ProviderLatestVersions.ProviderLatestVersions; | |
| 96 | 99 | const fileSystem = yield* FileSystem.FileSystem; |
| 97 | 100 | const path = yield* Path.Path; |
| 98 | 101 | const host = yield* ProviderHost.ProviderHost; |
| ... | ... | @@ -177,9 +180,10 @@ export const GrokDriver: ProviderDriver<GrokSettings, GrokDriverEnv> = { |
| 177 | 180 | maintenanceCapabilities, |
| 178 | 181 | enableProviderUpdateChecks: settings.enableProviderUpdateChecks, |
| 179 | 182 | publishSnapshot, |
| 180 | httpClient, | |
| 181 | 183 | }), |
| 182 | 184 | ), |
| 185 | Effect.provideService(HttpClient.HttpClient, httpClient), | |
| 186 | Effect.provideService(ProviderLatestVersions.ProviderLatestVersions, latestVersions), | |
| 183 | 187 | ), |
| 184 | 188 | }).pipe( |
| 185 | 189 | Effect.mapError( |
packages/provider-grok/src/server/status.ts+6-3| ... | ... | @@ -21,6 +21,7 @@ import { ChildProcess, ChildProcessSpawner } from "effect/process"; |
| 21 | 21 | import { createModelCapabilities } from "@t3tools/shared/model"; |
| 22 | 22 | import { resolveSpawnCommand } from "@t3tools/shared/shell"; |
| 23 | 23 | |
| 24 | import * as ProviderLatestVersions from "@t3tools/provider-core/server/ProviderLatestVersions"; | |
| 24 | 25 | import { |
| 25 | 26 | AUTH_PROBE_TIMEOUT_MS, |
| 26 | 27 | buildServerProvider, |
| ... | ... | @@ -567,14 +568,16 @@ export const enrichGrokSnapshot = (input: { |
| 567 | 568 | readonly maintenanceCapabilities: ProviderMaintenanceCapabilities; |
| 568 | 569 | readonly enableProviderUpdateChecks?: boolean; |
| 569 | 570 | readonly publishSnapshot: (snapshot: ServerProvider) => Effect.Effect<void>; |
| 570 | readonly httpClient: HttpClient.HttpClient; | |
| 571 | }): Effect.Effect<void> => { | |
| 571 | }): Effect.Effect< | |
| 572 | void, | |
| 573 | never, | |
| 574 | HttpClient.HttpClient | ProviderLatestVersions.ProviderLatestVersions | |
| 575 | > => { | |
| 572 | 576 | const { snapshot, publishSnapshot } = input; |
| 573 | 577 | |
| 574 | 578 | return enrichProviderSnapshotWithVersionAdvisory(snapshot, input.maintenanceCapabilities, { |
| 575 | 579 | enableProviderUpdateChecks: input.enableProviderUpdateChecks, |
| 576 | 580 | }).pipe( |
| 577 | Effect.provideService(HttpClient.HttpClient, input.httpClient), | |
| 578 | 581 | Effect.flatMap((enrichedSnapshot) => publishSnapshot(enrichedSnapshot)), |
| 579 | 582 | Effect.catchCause((cause) => |
| 580 | 583 | Effect.logWarning("Grok version advisory enrichment failed", { |
packages/provider-muse/src/server/driver.ts+8| ... | ... | @@ -8,6 +8,7 @@ import * as Schema from "effect/Schema"; |
| 8 | 8 | import { HttpClient } from "effect/http"; |
| 9 | 9 | import { ChildProcessSpawner } from "effect/process"; |
| 10 | 10 | |
| 11 | import * as ProviderLatestVersions from "@t3tools/provider-core/server/ProviderLatestVersions"; | |
| 11 | 12 | import * as ProviderHost from "@t3tools/provider-core/server/ProviderHost"; |
| 12 | 13 | import { expandHomePath } from "@t3tools/provider-core/server/pathExpansion"; |
| 13 | 14 | import { makeMuseTextGeneration } from "./textGeneration.ts"; |
| ... | ... | @@ -46,6 +47,7 @@ export type MuseDriverEnv = |
| 46 | 47 | | ChildProcessSpawner.ChildProcessSpawner |
| 47 | 48 | | FileSystem.FileSystem |
| 48 | 49 | | HttpClient.HttpClient |
| 50 | | ProviderLatestVersions.ProviderLatestVersions | |
| 49 | 51 | | Path.Path |
| 50 | 52 | | ProviderEventLoggers.ProviderEventLoggers; |
| 51 | 53 | |
| ... | ... | @@ -60,6 +62,7 @@ export const MuseDriver: ProviderDriver<MuseSettings, MuseDriverEnv> = { |
| 60 | 62 | const fileSystem = yield* FileSystem.FileSystem; |
| 61 | 63 | const path = yield* Path.Path; |
| 62 | 64 | const httpClient = yield* HttpClient.HttpClient; |
| 65 | const latestVersions = yield* ProviderLatestVersions.ProviderLatestVersions; | |
| 63 | 66 | const host = yield* ProviderHost.ProviderHost; |
| 64 | 67 | const eventLoggers = yield* ProviderEventLoggers.ProviderEventLoggers; |
| 65 | 68 | const { cwd } = host.paths; |
| ... | ... | @@ -105,6 +108,10 @@ export const MuseDriver: ProviderDriver<MuseSettings, MuseDriverEnv> = { |
| 105 | 108 | const latestVersion = options?.fresh |
| 106 | 109 | ? yield* latestMuseVersion(processEnvironment, { fresh: true }).pipe( |
| 107 | 110 | Effect.provideService(HttpClient.HttpClient, httpClient), |
| 111 | Effect.provideService( | |
| 112 | ProviderLatestVersions.ProviderLatestVersions, | |
| 113 | latestVersions, | |
| 114 | ), | |
| 108 | 115 | ) |
| 109 | 116 | : undefined; |
| 110 | 117 | return latestVersion !== undefined ? { ...capabilities, latestVersion } : capabilities; |
| ... | ... | @@ -133,6 +140,7 @@ export const MuseDriver: ProviderDriver<MuseSettings, MuseDriverEnv> = { |
| 133 | 140 | }), |
| 134 | 141 | ), |
| 135 | 142 | Effect.provideService(HttpClient.HttpClient, httpClient), |
| 143 | Effect.provideService(ProviderLatestVersions.ProviderLatestVersions, latestVersions), | |
| 136 | 144 | Effect.flatMap(publishSnapshot), |
| 137 | 145 | ), |
| 138 | 146 | }).pipe( |
packages/provider-muse/src/server/maintenance.test.ts+7-8| ... | ... | @@ -12,10 +12,8 @@ import { |
| 12 | 12 | latestMuseVersion, |
| 13 | 13 | parseMuseVersion, |
| 14 | 14 | } from "./maintenance.ts"; |
| 15 | import { | |
| 16 | ProviderVersionCache, | |
| 17 | makeManualOnlyProviderMaintenanceCapabilities, | |
| 18 | } from "@t3tools/provider-core/server/maintenanceResolver"; | |
| 15 | import * as ProviderLatestVersions from "@t3tools/provider-core/server/ProviderLatestVersions"; | |
| 16 | import { makeManualOnlyProviderMaintenanceCapabilities } from "@t3tools/provider-core/server/maintenanceResolver"; | |
| 19 | 17 | |
| 20 | 18 | const maintenanceCapabilities = { |
| 21 | 19 | ...makeManualOnlyProviderMaintenanceCapabilities({ |
| ... | ... | @@ -50,7 +48,7 @@ it.layer(NodeServices.layer)("Muse maintenance", (it) => { |
| 50 | 48 | expect(yield* latestMuseVersion({}, { fresh: true })).toBe("1.1.1-R10.1"); |
| 51 | 49 | }); |
| 52 | 50 | yield* check.pipe( |
| 53 | Effect.provideService(ProviderVersionCache, new Map()), | |
| 51 | Effect.provide(ProviderLatestVersions.layer), | |
| 54 | 52 | Effect.provideService( |
| 55 | 53 | HttpClient.HttpClient, |
| 56 | 54 | HttpClient.make((request) => { |
| ... | ... | @@ -101,7 +99,7 @@ it.layer(NodeServices.layer)("Muse maintenance", (it) => { |
| 101 | 99 | }); |
| 102 | 100 | const [first, second] = yield* Effect.all([check, check], { concurrency: 1 }).pipe( |
| 103 | 101 | Effect.provideService(HttpClient.HttpClient, httpClient), |
| 104 | Effect.provideService(ProviderVersionCache, new Map()), | |
| 102 | Effect.provide(ProviderLatestVersions.layer), | |
| 105 | 103 | ); |
| 106 | 104 | expect(first.versionAdvisory).toMatchObject({ |
| 107 | 105 | status: "behind_latest", |
| ... | ... | @@ -134,7 +132,7 @@ it.layer(NodeServices.layer)("Muse maintenance", (it) => { |
| 134 | 132 | ), |
| 135 | 133 | ), |
| 136 | 134 | ), |
| 137 | Effect.provideService(ProviderVersionCache, new Map()), | |
| 135 | Effect.provide(ProviderLatestVersions.layer), | |
| 138 | 136 | ); |
| 139 | 137 | expect(result.versionAdvisory?.status).toBe(status); |
| 140 | 138 | } |
| ... | ... | @@ -153,6 +151,7 @@ it.layer(NodeServices.layer)("Muse maintenance", (it) => { |
| 153 | 151 | maintenanceCapabilities, |
| 154 | 152 | environment: {}, |
| 155 | 153 | }).pipe( |
| 154 | Effect.provide(ProviderLatestVersions.layer), | |
| 156 | 155 | Effect.provideService( |
| 157 | 156 | HttpClient.HttpClient, |
| 158 | 157 | HttpClient.make(() => Effect.die("Unexpected release request")), |
| ... | ... | @@ -182,7 +181,7 @@ it.layer(NodeServices.layer)("Muse maintenance", (it) => { |
| 182 | 181 | Effect.succeed(HttpClientResponse.fromWeb(request, response)), |
| 183 | 182 | ), |
| 184 | 183 | ), |
| 185 | Effect.provideService(ProviderVersionCache, new Map()), | |
| 184 | Effect.provide(ProviderLatestVersions.layer), | |
| 186 | 185 | ); |
| 187 | 186 | expect(result.status).toBe("ready"); |
| 188 | 187 | expect(result.versionAdvisory?.status).toBe("unknown"); |
packages/provider-muse/src/server/maintenance.ts+16-19| ... | ... | @@ -1,6 +1,5 @@ |
| 1 | 1 | import { ProviderDriverKind, type ServerProvider } from "@t3tools/contracts"; |
| 2 | 2 | import { compareSemverVersions } from "@t3tools/shared/semver"; |
| 3 | import * as DateTime from "effect/DateTime"; | |
| 4 | 3 | import * as Effect from "effect/Effect"; |
| 5 | 4 | import * as FileSystem from "effect/FileSystem"; |
| 6 | 5 | import * as Schema from "effect/Schema"; |
| ... | ... | @@ -10,10 +9,10 @@ import { |
| 10 | 9 | enrichProviderSnapshotWithVersionAdvisory, |
| 11 | 10 | makeManualOnlyProviderMaintenanceCapabilities, |
| 12 | 11 | makeProviderMaintenanceCapabilities, |
| 13 | ProviderVersionCache, | |
| 14 | 12 | type ProviderMaintenanceCapabilities, |
| 15 | 13 | type ProviderMaintenanceCapabilitiesResolver, |
| 16 | 14 | } from "@t3tools/provider-core/server/maintenanceResolver"; |
| 15 | import * as ProviderLatestVersions from "@t3tools/provider-core/server/ProviderLatestVersions"; | |
| 17 | 16 | import { parseGenericCliVersion } from "@t3tools/provider-core/server/snapshotProbe"; |
| 18 | 17 | |
| 19 | 18 | const DRIVER = ProviderDriverKind.make("muse"); |
| ... | ... | @@ -97,26 +96,24 @@ export const latestMuseVersion = Effect.fn("latestMuseVersion")(function* ( |
| 97 | 96 | const channel = environment.MUSE_CHANNEL || "muse-stable"; |
| 98 | 97 | if (!MUSE_CHANNELS.has(channel)) return null; |
| 99 | 98 | const channelUrl = `https://api.meta.ai/muse-code/channels/${channel}`; |
| 100 | const cache = yield* ProviderVersionCache; | |
| 101 | const key = `muse:${channelUrl}`; | |
| 102 | const now = DateTime.toEpochMillis(yield* DateTime.now); | |
| 103 | const cached = cache.get(key); | |
| 104 | if (!options?.fresh && cached && cached.expiresAt > now) return cached.version; | |
| 105 | 99 | const client = yield* HttpClient.HttpClient; |
| 106 | const version = yield* client.execute(HttpClientRequest.get(channelUrl)).pipe( | |
| 107 | Effect.flatMap((response) => | |
| 108 | response.status >= 200 && response.status < 300 | |
| 109 | ? response.json.pipe( | |
| 110 | Effect.flatMap(Schema.decodeUnknownEffect(ChannelManifest)), | |
| 111 | Effect.map((manifest) => parseMuseVersion(manifest.version)), | |
| 112 | ) | |
| 113 | : Effect.succeed(null), | |
| 100 | const latestVersions = yield* ProviderLatestVersions.ProviderLatestVersions; | |
| 101 | return yield* latestVersions.cached( | |
| 102 | `muse:${channelUrl}`, | |
| 103 | client.execute(HttpClientRequest.get(channelUrl)).pipe( | |
| 104 | Effect.flatMap((response) => | |
| 105 | response.status >= 200 && response.status < 300 | |
| 106 | ? response.json.pipe( | |
| 107 | Effect.flatMap(Schema.decodeUnknownEffect(ChannelManifest)), | |
| 108 | Effect.map((manifest) => parseMuseVersion(manifest.version)), | |
| 109 | ) | |
| 110 | : Effect.succeed(null), | |
| 111 | ), | |
| 112 | Effect.timeout(4_000), | |
| 113 | Effect.orElseSucceed(() => null), | |
| 114 | 114 | ), |
| 115 | Effect.timeout(4_000), | |
| 116 | Effect.orElseSucceed(() => null), | |
| 115 | options, | |
| 117 | 116 | ); |
| 118 | cache.set(key, { version, expiresAt: now + (version === null ? 60_000 : 60 * 60 * 1_000) }); | |
| 119 | return version; | |
| 120 | 117 | }); |
| 121 | 118 | |
| 122 | 119 | /** Feed Muse's release channel into the same advisory and notification flow as other drivers. */ |
packages/provider-opencode/src/server/driver.test.ts+3| ... | ... | @@ -15,6 +15,7 @@ import * as TestClock from "effect/testing/TestClock"; |
| 15 | 15 | import { HttpClient } from "effect/http"; |
| 16 | 16 | import { ChildProcessSpawner } from "effect/process"; |
| 17 | 17 | |
| 18 | import * as ProviderLatestVersions from "@t3tools/provider-core/server/ProviderLatestVersions"; | |
| 18 | 19 | import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; |
| 19 | 20 | import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers"; |
| 20 | 21 | import * as ProviderMaintenance from "@t3tools/provider-core/server/maintenanceResolver"; |
| ... | ... | @@ -48,6 +49,7 @@ const openCode2Runtime = { |
| 48 | 49 | |
| 49 | 50 | const layer = Layer.mergeAll( |
| 50 | 51 | IdAllocator.layer, |
| 52 | ProviderLatestVersions.layer, | |
| 51 | 53 | layerTestProviderHost(), |
| 52 | 54 | Layer.succeed( |
| 53 | 55 | ProviderEventLoggers.ProviderEventLoggers, |
| ... | ... | @@ -210,6 +212,7 @@ const changingRuntime = { |
| 210 | 212 | } as unknown as OpenCodeRuntime.OpenCodeRuntimeShape; |
| 211 | 213 | const layerUpdate = Layer.mergeAll( |
| 212 | 214 | IdAllocator.layer, |
| 215 | ProviderLatestVersions.layer, | |
| 213 | 216 | layerTestProviderHost(), |
| 214 | 217 | Layer.succeed( |
| 215 | 218 | ProviderEventLoggers.ProviderEventLoggers, |
packages/provider-opencode/src/server/driver.ts+4| ... | ... | @@ -27,6 +27,7 @@ import { ChildProcessSpawner } from "effect/process"; |
| 27 | 27 | |
| 28 | 28 | import * as OpenCode2TextGeneration from "./v2/textGeneration.ts"; |
| 29 | 29 | import { makeOpenCodeTextGeneration } from "./textGeneration.ts"; |
| 30 | import * as ProviderLatestVersions from "@t3tools/provider-core/server/ProviderLatestVersions"; | |
| 30 | 31 | import * as ProviderHost from "@t3tools/provider-core/server/ProviderHost"; |
| 31 | 32 | import * as OpenCodeAdapterV2 from "./adapter.ts"; |
| 32 | 33 | import * as OpenCode2AdapterV2 from "./v2/adapter.ts"; |
| ... | ... | @@ -177,6 +178,7 @@ export type OpenCodeDriverEnv = |
| 177 | 178 | | Crypto.Crypto |
| 178 | 179 | | FileSystem.FileSystem |
| 179 | 180 | | HttpClient.HttpClient |
| 181 | | ProviderLatestVersions.ProviderLatestVersions | |
| 180 | 182 | | OpenCodeRuntime.OpenCodeRuntime |
| 181 | 183 | | Path.Path; |
| 182 | 184 | |
| ... | ... | @@ -195,6 +197,7 @@ export const OpenCodeDriver: ProviderDriver<OpenCodeSettings, OpenCodeDriverEnv> |
| 195 | 197 | const pathService = yield* Path.Path; |
| 196 | 198 | const openCodeRuntime = yield* OpenCodeRuntime.OpenCodeRuntime; |
| 197 | 199 | const httpClient = yield* HttpClient.HttpClient; |
| 200 | const latestVersions = yield* ProviderLatestVersions.ProviderLatestVersions; | |
| 198 | 201 | const crypto = yield* Crypto.Crypto; |
| 199 | 202 | const host = yield* ProviderHost.ProviderHost; |
| 200 | 203 | const processEnv = mergeProviderInstanceEnvironment(environment); |
| ... | ... | @@ -465,6 +468,7 @@ export const OpenCodeDriver: ProviderDriver<OpenCodeSettings, OpenCodeDriverEnv> |
| 465 | 468 | }), |
| 466 | 469 | ), |
| 467 | 470 | Effect.provideService(HttpClient.HttpClient, httpClient), |
| 471 | Effect.provideService(ProviderLatestVersions.ProviderLatestVersions, latestVersions), | |
| 468 | 472 | Effect.flatMap((enrichedSnapshot) => publishSnapshot(enrichedSnapshot)), |
| 469 | 473 | ), |
| 470 | 474 | }, |
packages/provider-pi/src/server/driver.test.ts+2| ... | ... | @@ -13,6 +13,7 @@ import * as TestClock from "effect/testing/TestClock"; |
| 13 | 13 | import { HttpClient } from "effect/http"; |
| 14 | 14 | import { ChildProcess, ChildProcessSpawner } from "effect/process"; |
| 15 | 15 | |
| 16 | import * as ProviderLatestVersions from "@t3tools/provider-core/server/ProviderLatestVersions"; | |
| 16 | 17 | import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; |
| 17 | 18 | import { layerTestProviderHost } from "@t3tools/provider-testing/host"; |
| 18 | 19 | import type { PiSettings } from "../settings.ts"; |
| ... | ... | @@ -25,6 +26,7 @@ const layerTest = Layer.mergeAll( |
| 25 | 26 | runBackgroundWork: false, |
| 26 | 27 | }), |
| 27 | 28 | IdAllocator.layer, |
| 29 | ProviderLatestVersions.layer, | |
| 28 | 30 | Layer.succeed( |
| 29 | 31 | HttpClient.HttpClient, |
| 30 | 32 | HttpClient.make(() => Effect.die("Unexpected HTTP")), |
packages/provider-pi/src/server/driver.ts+5-1| ... | ... | @@ -15,6 +15,7 @@ import * as Schema from "effect/Schema"; |
| 15 | 15 | import { HttpClient } from "effect/http"; |
| 16 | 16 | import { ChildProcessSpawner } from "effect/process"; |
| 17 | 17 | |
| 18 | import * as ProviderLatestVersions from "@t3tools/provider-core/server/ProviderLatestVersions"; | |
| 18 | 19 | import * as ProviderHost from "@t3tools/provider-core/server/ProviderHost"; |
| 19 | 20 | import { makePiTextGeneration } from "./textGeneration.ts"; |
| 20 | 21 | import { PiAdapterV2Driver, type PiAdapterV2DriverEnv } from "./adapter.ts"; |
| ... | ... | @@ -60,6 +61,7 @@ export type PiDriverEnv = |
| 60 | 61 | | ChildProcessSpawner.ChildProcessSpawner |
| 61 | 62 | | FileSystem.FileSystem |
| 62 | 63 | | HttpClient.HttpClient |
| 64 | | ProviderLatestVersions.ProviderLatestVersions | |
| 63 | 65 | | Path.Path; |
| 64 | 66 | |
| 65 | 67 | const withInstanceIdentity = |
| ... | ... | @@ -92,6 +94,7 @@ export const PiDriver: ProviderDriver<PiSettings, PiDriverEnv> = { |
| 92 | 94 | const fileSystem = yield* FileSystem.FileSystem; |
| 93 | 95 | const pathService = yield* Path.Path; |
| 94 | 96 | const httpClient = yield* HttpClient.HttpClient; |
| 97 | const latestVersions = yield* ProviderLatestVersions.ProviderLatestVersions; | |
| 95 | 98 | const host = yield* ProviderHost.ProviderHost; |
| 96 | 99 | const { cwd } = host.paths; |
| 97 | 100 | const processEnv = mergeProviderInstanceEnvironment(environment); |
| ... | ... | @@ -159,9 +162,10 @@ export const PiDriver: ProviderDriver<PiSettings, PiDriverEnv> = { |
| 159 | 162 | maintenanceCapabilities, |
| 160 | 163 | enableProviderUpdateChecks: settings.enableProviderUpdateChecks, |
| 161 | 164 | publishSnapshot, |
| 162 | httpClient, | |
| 163 | 165 | }), |
| 164 | 166 | ), |
| 167 | Effect.provideService(HttpClient.HttpClient, httpClient), | |
| 168 | Effect.provideService(ProviderLatestVersions.ProviderLatestVersions, latestVersions), | |
| 165 | 169 | ), |
| 166 | 170 | }).pipe( |
| 167 | 171 | Effect.mapError( |
packages/provider-pi/src/server/status.ts+6-3| ... | ... | @@ -34,6 +34,7 @@ import { |
| 34 | 34 | piRecordField as recordField, |
| 35 | 35 | piRecordString as recordString, |
| 36 | 36 | } from "./rpc.ts"; |
| 37 | import * as ProviderLatestVersions from "@t3tools/provider-core/server/ProviderLatestVersions"; | |
| 37 | 38 | import { |
| 38 | 39 | buildServerProvider, |
| 39 | 40 | isCommandMissingCause, |
| ... | ... | @@ -461,13 +462,15 @@ export const enrichPiSnapshot = (input: { |
| 461 | 462 | readonly maintenanceCapabilities: ProviderMaintenanceCapabilities; |
| 462 | 463 | readonly enableProviderUpdateChecks?: boolean; |
| 463 | 464 | readonly publishSnapshot: (snapshot: ServerProvider) => Effect.Effect<void>; |
| 464 | readonly httpClient: HttpClient.HttpClient; | |
| 465 | }): Effect.Effect<void> => { | |
| 465 | }): Effect.Effect< | |
| 466 | void, | |
| 467 | never, | |
| 468 | HttpClient.HttpClient | ProviderLatestVersions.ProviderLatestVersions | |
| 469 | > => { | |
| 466 | 470 | const { snapshot, publishSnapshot } = input; |
| 467 | 471 | return enrichProviderSnapshotWithVersionAdvisory(snapshot, input.maintenanceCapabilities, { |
| 468 | 472 | enableProviderUpdateChecks: input.enableProviderUpdateChecks, |
| 469 | 473 | }).pipe( |
| 470 | Effect.provideService(HttpClient.HttpClient, input.httpClient), | |
| 471 | 474 | Effect.flatMap((enrichedSnapshot) => publishSnapshot(enrichedSnapshot)), |
| 472 | 475 | Effect.catchCause((cause) => |
| 473 | 476 | Effect.logWarning("Pi version advisory enrichment failed", { |