authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-10-09 09:15:21-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-10-09 09:15:21-07:00
log68ab16f3084236871685bed5032cb05c7c436dbd
tree050b9f440c3622cdaba255ce726bccf26ac1f9a9
parentb66dd402e86899c3fb4341d46c32d0d69df1eefc
signature Signed by PGP key B5690EEEBB952194

refactor(provider-core): latest-version lookups go through a ProviderLatestVersions service (#17434)

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";
2929import * as ServerEnvironment from "../environment/ServerEnvironment.ts";
3030import * as ModelManifest from "../provider/ModelManifest.ts";
3131import * as ProviderInstanceRegistryHydration from "../provider/ProviderInstanceRegistryHydration.ts";
32import * as ProviderLatestVersions from "@t3tools/provider-core/server/ProviderLatestVersions";
3233import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers";
3334import * as OpenCodeRuntime from "@t3tools/provider-opencode/server/OpenCodeRuntime";
3435import * as OpenCodeServerLedger from "@t3tools/provider-opencode/server/OpenCodeServerLedger";
......@@ -112,6 +113,7 @@ const layerProviderInstanceRegistry = ProviderInstanceRegistryHydration.layer.pi
112113 ProviderEventLoggers.ProviderEventLoggers,
113114 ProviderEventLoggers.NoOpProviderEventLoggers,
114115 ),
116 ProviderLatestVersions.layer,
115117 ModelManifest.layerTest,
116118 AntigravityInstallation.AntigravityInstallation.layer.pipe(
117119 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";
2929import * as ServerEnvironment from "../environment/ServerEnvironment.ts";
3030import * as ModelManifest from "../provider/ModelManifest.ts";
3131import * as ProviderInstanceRegistryHydration from "../provider/ProviderInstanceRegistryHydration.ts";
32import * as ProviderLatestVersions from "@t3tools/provider-core/server/ProviderLatestVersions";
3233import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers";
3334import * as OpenCodeRuntime from "@t3tools/provider-opencode/server/OpenCodeRuntime";
3435import * as OpenCodeServerLedger from "@t3tools/provider-opencode/server/OpenCodeServerLedger";
......@@ -92,6 +93,7 @@ const layerProviderInstanceRegistry = ProviderInstanceRegistryHydration.layer.pi
9293 ProviderEventLoggers.ProviderEventLoggers,
9394 ProviderEventLoggers.NoOpProviderEventLoggers,
9495 ),
96 ProviderLatestVersions.layer,
9597 ModelManifest.layerTest,
9698 AntigravityInstallation.AntigravityInstallation.layer.pipe(
9799 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";
2929import * as ServerEnvironment from "../environment/ServerEnvironment.ts";
3030import * as ModelManifest from "../provider/ModelManifest.ts";
3131import * as ProviderInstanceRegistryHydration from "../provider/ProviderInstanceRegistryHydration.ts";
32import * as ProviderLatestVersions from "@t3tools/provider-core/server/ProviderLatestVersions";
3233import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers";
3334import * as OpenCodeRuntime from "@t3tools/provider-opencode/server/OpenCodeRuntime";
3435import * as OpenCodeServerLedger from "@t3tools/provider-opencode/server/OpenCodeServerLedger";
......@@ -91,6 +92,7 @@ const layerProviderInstanceRegistry = ProviderInstanceRegistryHydration.layer.pi
9192 ProviderEventLoggers.ProviderEventLoggers,
9293 ProviderEventLoggers.NoOpProviderEventLoggers,
9394 ),
95 ProviderLatestVersions.layer,
9496 ModelManifest.layerTest,
9597 AntigravityInstallation.AntigravityInstallation.layer.pipe(
9698 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";
5454import * as ServerEnvironment from "../environment/ServerEnvironment.ts";
5555import * as ModelManifest from "../provider/ModelManifest.ts";
5656import * as ProviderInstanceRegistryHydration from "../provider/ProviderInstanceRegistryHydration.ts";
57import * as ProviderLatestVersions from "@t3tools/provider-core/server/ProviderLatestVersions";
5758import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers";
5859import * as OpenCode2Client from "@t3tools/provider-opencode/server/v2/OpenCode2Client";
5960import * as OpenCodeRuntime from "@t3tools/provider-opencode/server/OpenCodeRuntime";
......@@ -185,6 +186,7 @@ const layerProviderInstanceRegistry = ProviderInstanceRegistryHydration.layer.pi
185186 ProviderEventLoggers.ProviderEventLoggers,
186187 ProviderEventLoggers.NoOpProviderEventLoggers,
187188 ),
189 ProviderLatestVersions.layer,
188190 ModelManifest.layerTest,
189191 AntigravityInstallation.AntigravityInstallation.layer.pipe(
190192 Layer.provide(layerServerConfig.pipe(Layer.provide(layerPlatformTest))),
apps/server/src/provider/Drivers/ClaudeDriver.ts+4
......@@ -24,6 +24,7 @@ import { HttpClient } from "effect/http";
2424import { ChildProcessSpawner } from "effect/process";
2525
2626import { makeClaudeTextGeneration } from "../../textGeneration/ClaudeTextGeneration.ts";
27import * as ProviderLatestVersions from "@t3tools/provider-core/server/ProviderLatestVersions";
2728import * as ProviderHost from "@t3tools/provider-core/server/ProviderHost";
2829import * as ServerConfig from "../../config.ts";
2930import { expandHomePath } from "@t3tools/provider-core/server/pathExpansion";
......@@ -100,6 +101,7 @@ export type ClaudeDriverEnv =
100101 | Crypto.Crypto
101102 | FileSystem.FileSystem
102103 | HttpClient.HttpClient
104 | ProviderLatestVersions.ProviderLatestVersions
103105 | ModelCatalog.ModelCatalog
104106 | Path.Path
105107 | ProviderEventLoggers.ProviderEventLoggers
......@@ -120,6 +122,7 @@ export const ClaudeDriver: ProviderDriver<ClaudeSettings, ClaudeDriverEnv> = {
120122 const path = yield* Path.Path;
121123 const { cwd } = yield* ServerConfig.ServerConfig;
122124 const httpClient = yield* HttpClient.HttpClient;
125 const latestVersions = yield* ProviderLatestVersions.ProviderLatestVersions;
123126 const resetCreditCoordinator = yield* ResetCreditCoordinator.ResetCreditCoordinator;
124127 const catalogService = yield* ModelCatalog.ModelCatalog;
125128 const modelCatalog = catalogService
......@@ -256,6 +259,7 @@ export const ClaudeDriver: ProviderDriver<ClaudeSettings, ClaudeDriverEnv> = {
256259 }),
257260 ),
258261 Effect.provideService(HttpClient.HttpClient, httpClient),
262 Effect.provideService(ProviderLatestVersions.ProviderLatestVersions, latestVersions),
259263 Effect.flatMap((enrichedSnapshot) => publishSnapshot(enrichedSnapshot)),
260264 ),
261265 }).pipe(
apps/server/src/provider/Drivers/CodexDriver.test.ts+5-6
......@@ -26,11 +26,11 @@ import * as BackgroundPolicy from "../../background/BackgroundPolicy.ts";
2626import * as ServerConfig from "../../config.ts";
2727import * as ServerSettings from "../../serverSettings.ts";
2828import * as ResetCreditCoordinator from "../resetCreditCoordinator.ts";
29import * as ProviderLatestVersions from "@t3tools/provider-core/server/ProviderLatestVersions";
2930import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers";
3031import * as ModelManifest from "../ModelManifest.ts";
3132import {
3233 createProviderVersionAdvisory,
33 ProviderVersionCache,
3434 resolveLatestProviderVersion,
3535} from "@t3tools/provider-core/server/maintenanceResolver";
3636import { CodexDriver } from "./CodexDriver.ts";
......@@ -75,6 +75,7 @@ const layerDeps = ServerConfig.layerTest(process.cwd(), {
7575 ProviderEventLoggers.NoOpProviderEventLoggers,
7676 ),
7777 ),
78 Layer.provideMerge(ProviderLatestVersions.layer),
7879 Layer.provideMerge(
7980 Layer.succeed(
8081 HttpClient.HttpClient,
......@@ -557,11 +558,9 @@ it.layer(layerTest)("CodexDriver", (it) => {
557558 }).pipe(Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, metadataSpawner));
558559 const capabilities = yield* instance.snapshot.resolveMaintenance();
559560 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"]]),
565564 ),
566565 );
567566 expect(probes).toEqual([]);
apps/server/src/provider/Drivers/CodexDriver.ts+4
......@@ -31,6 +31,7 @@ import { HttpClient } from "effect/http";
3131import { ChildProcessSpawner } from "effect/process";
3232
3333import { makeCodexTextGeneration } from "../../textGeneration/CodexTextGeneration.ts";
34import * as ProviderLatestVersions from "@t3tools/provider-core/server/ProviderLatestVersions";
3435import * as ProviderHost from "@t3tools/provider-core/server/ProviderHost";
3536import * as ServerConfig from "../../config.ts";
3637import { expandHomePath } from "@t3tools/provider-core/server/pathExpansion";
......@@ -115,6 +116,7 @@ export type CodexDriverEnv =
115116 | Crypto.Crypto
116117 | FileSystem.FileSystem
117118 | HttpClient.HttpClient
119 | ProviderLatestVersions.ProviderLatestVersions
118120 | ModelCatalog.ModelCatalog
119121 | Path.Path
120122 | ProviderEventLoggers.ProviderEventLoggers
......@@ -147,6 +149,7 @@ export const CodexDriver: ProviderDriver<CodexSettings, CodexDriverEnv> = {
147149 const fileSystem = yield* FileSystem.FileSystem;
148150 const pathService = yield* Path.Path;
149151 const httpClient = yield* HttpClient.HttpClient;
152 const latestVersions = yield* ProviderLatestVersions.ProviderLatestVersions;
150153 const modelCatalog = yield* ModelCatalog.ModelCatalog;
151154 const currentCatalog = modelCatalog.current(DRIVER_KIND);
152155 const processEnv = mergeProviderInstanceEnvironment(environment);
......@@ -251,6 +254,7 @@ export const CodexDriver: ProviderDriver<CodexSettings, CodexDriverEnv> = {
251254 }),
252255 ),
253256 Effect.provideService(HttpClient.HttpClient, httpClient),
257 Effect.provideService(ProviderLatestVersions.ProviderLatestVersions, latestVersions),
254258 Effect.flatMap((enrichedSnapshot) => publishSnapshot(enrichedSnapshot)),
255259 ),
256260 }).pipe(
apps/server/src/provider/ProviderInstanceRegistry.test.ts+3
......@@ -35,6 +35,7 @@ import {
3535 type ProviderInstanceConfigMap,
3636 ProviderInstanceId,
3737} from "@t3tools/contracts";
38import * as ProviderLatestVersions from "@t3tools/provider-core/server/ProviderLatestVersions";
3839import type { GrokSettings } from "@t3tools/provider-grok/settings";
3940import type { CursorSettings } from "@t3tools/provider-cursor/settings";
4041import { HostProcessPlatform, isHostWindows } from "@t3tools/shared/hostProcess";
......@@ -228,6 +229,7 @@ describe("ProviderInstanceRegistry — multi-instance codex slice", () => {
228229 ProviderEventLoggers.NoOpProviderEventLoggers,
229230 ),
230231 ),
232 Layer.provideMerge(ProviderLatestVersions.layer),
231233 Layer.provideMerge(ModelManifest.layerTest),
232234 Layer.provideMerge(ResetCreditCoordinator.layerTest),
233235 );
......@@ -609,6 +611,7 @@ describe("ProviderInstanceRegistry — all drivers slice", () => {
609611 ProviderEventLoggers.NoOpProviderEventLoggers,
610612 ),
611613 ),
614 Layer.provideMerge(ProviderLatestVersions.layer),
612615 Layer.provideMerge(ModelManifest.layerTest),
613616 Layer.provideMerge(ResetCreditCoordinator.layerTest),
614617 );
apps/server/src/provider/ProviderRegistry.test.ts+5
......@@ -45,6 +45,7 @@ import * as AntigravityInstallation from "./AntigravityInstallation.ts";
4545import * as ModelManifest from "./ModelManifest.ts";
4646import { applyProviderCompatibility } from "./providerCompatibility.ts";
4747import * as ResetCreditCoordinator from "./resetCreditCoordinator.ts";
48import * as ProviderLatestVersions from "@t3tools/provider-core/server/ProviderLatestVersions";
4849import * as OpenCodeRuntime from "@t3tools/provider-opencode/server/OpenCodeRuntime";
4950import * as OpenCodeServerLedger from "@t3tools/provider-opencode/server/OpenCodeServerLedger";
5051import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers";
......@@ -2759,6 +2760,7 @@ it.layer(
27592760 ProviderEventLoggers.NoOpProviderEventLoggers,
27602761 ),
27612762 ),
2763 Layer.provideMerge(ProviderLatestVersions.layer),
27622764 Layer.provideMerge(ModelManifest.layerTest),
27632765 Layer.provideMerge(ResetCreditCoordinator.layerTest),
27642766 Layer.provideMerge(
......@@ -2862,6 +2864,7 @@ it.layer(
28622864 ProviderEventLoggers.NoOpProviderEventLoggers,
28632865 ),
28642866 ),
2867 Layer.provideMerge(ProviderLatestVersions.layer),
28652868 Layer.provideMerge(ModelManifest.layerTest),
28662869 Layer.provideMerge(ResetCreditCoordinator.layerTest),
28672870 Layer.provideMerge(
......@@ -2980,6 +2983,7 @@ it.layer(
29802983 ProviderEventLoggers.NoOpProviderEventLoggers,
29812984 ),
29822985 ),
2986 Layer.provideMerge(ProviderLatestVersions.layer),
29832987 Layer.provideMerge(ModelManifest.layerTest),
29842988 Layer.provideMerge(ResetCreditCoordinator.layerTest),
29852989 Layer.provideMerge(
......@@ -3038,6 +3042,7 @@ it.layer(
30383042 ProviderEventLoggers.NoOpProviderEventLoggers,
30393043 ),
30403044 ),
3045 Layer.provideMerge(ProviderLatestVersions.layer),
30413046 Layer.provideMerge(ModelManifest.layerTest),
30423047 Layer.provideMerge(ResetCreditCoordinator.layerTest),
30433048 Layer.provideMerge(
apps/server/src/provider/providerMaintenanceRunner.test.ts+2-2
......@@ -26,9 +26,9 @@ import { SpawnExecutableResolution } from "@t3tools/shared/shell";
2626import * as ProviderRegistry from "./ProviderRegistry.ts";
2727import * as ModelManifest from "./ModelManifest.ts";
2828import * as ProviderMaintenanceRunner from "./providerMaintenanceRunner.ts";
29import * as ProviderLatestVersions from "@t3tools/provider-core/server/ProviderLatestVersions";
2930import {
3031 makeProviderMaintenanceCapabilities,
31 ProviderVersionCache,
3232 type ProviderMaintenanceCapabilities,
3333} from "@t3tools/provider-core/server/maintenanceResolver";
3434const isServerProviderUpdateError = Schema.is(ServerProviderUpdateError);
......@@ -239,7 +239,7 @@ const makeTestRunner = (
239239 refreshInBackground: Effect.void,
240240 }),
241241 // Fresh per runner so a version cached by one test cannot leak into another.
242 Layer.sync(ProviderVersionCache, () => new Map()),
242 ProviderLatestVersions.layer,
243243 ),
244244 ),
245245 ),
apps/server/src/provider/providerMaintenanceRunner.ts+7-4
......@@ -33,8 +33,8 @@ import {
3333 makeTargetedProviderUpdateAction,
3434 resolveLatestProviderVersion,
3535 type ProviderMaintenanceCommandAction,
36 ProviderVersionCache,
3736} from "@t3tools/provider-core/server/maintenanceResolver";
37import * as ProviderLatestVersions from "@t3tools/provider-core/server/ProviderLatestVersions";
3838import type { ProviderMaintenanceCapabilities } from "@t3tools/provider-core/server/maintenanceResolver";
3939import { collectUint8StreamText } from "@t3tools/provider-core/server/collectStreamText";
4040const isServerProviderUpdateError = Schema.is(ServerProviderUpdateError);
......@@ -291,7 +291,7 @@ export const make = Effect.fn("ProviderMaintenanceRunner.make")(function* () {
291291 const manifestService = yield* ModelManifest.ModelManifest;
292292 const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;
293293 const httpClient = yield* HttpClient.HttpClient;
294 const versionCache = yield* ProviderVersionCache;
294 const latestVersions = yield* ProviderLatestVersions.ProviderLatestVersions;
295295 const runMaintenanceCommand = (
296296 update: ProviderMaintenanceCommandAction,
297297 onProgress: (line: string) => Effect.Effect<void>,
......@@ -356,7 +356,7 @@ export const make = Effect.fn("ProviderMaintenanceRunner.make")(function* () {
356356 maintenanceCapabilities,
357357 ).pipe(
358358 Effect.provideService(HttpClient.HttpClient, httpClient),
359 Effect.provideService(ProviderVersionCache, versionCache),
359 Effect.provideService(ProviderLatestVersions.ProviderLatestVersions, latestVersions),
360360 ),
361361 {
362362 concurrency: "unbounded",
......@@ -463,7 +463,10 @@ export const make = Effect.fn("ProviderMaintenanceRunner.make")(function* () {
463463 targetVersion ??
464464 (yield* resolveLatestProviderVersion(fresh).pipe(
465465 Effect.provideService(HttpClient.HttpClient, httpClient),
466 Effect.provideService(ProviderVersionCache, versionCache),
466 Effect.provideService(
467 ProviderLatestVersions.ProviderLatestVersions,
468 latestVersions,
469 ),
467470 ));
468471 const advisory =
469472 resolveProviderCompatibility(manifest.compatibility, provider, candidateVersion) ??
apps/server/src/server.ts+2
......@@ -44,6 +44,7 @@ import * as ProviderEventIngestor from "./orchestration-v2/ProviderEventIngestor
4444import * as ModelManifest from "./provider/ModelManifest.ts";
4545import * as ResetCreditCoordinator from "./provider/resetCreditCoordinator.ts";
4646import * as EventNdjsonLogger from "./provider/EventNdjsonLogger.ts";
47import * as ProviderLatestVersions from "@t3tools/provider-core/server/ProviderLatestVersions";
4748import * as OpenCodeRuntime from "@t3tools/provider-opencode/server/OpenCodeRuntime";
4849import * as OpenCodeServerLedger from "@t3tools/provider-opencode/server/OpenCodeServerLedger";
4950import * as ProviderHostLive from "./provider/ProviderHostLive.ts";
......@@ -634,6 +635,7 @@ const layerRuntimeCoreDependencies = layerRuntimeCoreDependenciesBase.pipe(
634635 layerProviderEventLoggers,
635636 ModelManifest.layerModelCatalog.pipe(Layer.provideMerge(ModelManifest.layer)),
636637 ResetCreditCoordinator.layer,
638 ProviderLatestVersions.layer,
637639 ),
638640 ),
639641 // `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
168168import * as AcpRegistrySupport from "@t3tools/provider-acp-registry/server/AcpRegistrySupport";
169169import * as AcpRegistryRuntimeCoordinator from "@t3tools/provider-acp-registry/server/AcpRegistryRuntimeCoordinator";
170170import * as ModelManifest from "./provider/ModelManifest.ts";
171import * as ProviderMaintenance from "@t3tools/provider-core/server/maintenanceResolver";
171import * as ProviderLatestVersions from "@t3tools/provider-core/server/ProviderLatestVersions";
172172import * as ProviderMaintenanceRunner from "./provider/providerMaintenanceRunner.ts";
173173import * as ProviderAuthService from "./provider/ProviderAuthService.ts";
174174import { makeProviderInstallation } from "./provider/providerInstallation.ts";
......@@ -1271,7 +1271,7 @@ const layerWsRpc = (
12711271 const portDiscovery = yield* PortScanner.PortDiscovery;
12721272 const providerRegistry = yield* ProviderRegistry.ProviderRegistry;
12731273 const modelManifest = yield* ModelManifest.ModelManifest;
1274 const providerVersionCache = yield* ProviderMaintenance.ProviderVersionCache;
1274 const providerLatestVersions = yield* ProviderLatestVersions.ProviderLatestVersions;
12751275 const providerInstances = yield* ProviderInstanceRegistry.ProviderInstanceRegistry;
12761276 const acpRegistryCatalog = yield* AcpRegistrySupport.AcpRegistryCatalog;
12771277 const acpRegistryRuntimeCoordinator =
......@@ -2193,7 +2193,7 @@ const layerWsRpc = (
21932193 fresh: true,
21942194 });
21952195 if (maintenance.packageName)
2196 providerVersionCache.delete(maintenance.packageName);
2196 yield* providerLatestVersions.invalidate(maintenance.packageName);
21972197 }),
21982198 { concurrency: "unbounded", discard: true },
21992199 );
packages/provider-core/package.json+4
......@@ -115,6 +115,10 @@
115115 "types": "./src/server/ProviderHost.ts",
116116 "import": "./src/server/ProviderHost.ts"
117117 },
118 "./server/ProviderLatestVersions": {
119 "types": "./src/server/ProviderLatestVersions.ts",
120 "import": "./src/server/ProviderLatestVersions.ts"
121 },
118122 "./server/randomUuid": {
119123 "types": "./src/server/randomUuid.ts",
120124 "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 */
9import * as Context from "effect/Context";
10import * as DateTime from "effect/DateTime";
11import * as Duration from "effect/Duration";
12import * as Effect from "effect/Effect";
13import * as Layer from "effect/Layer";
14import * as Ref from "effect/Ref";
15
16/** A found version stays fresh this long. */
17const FOUND_TTL = Duration.hours(1);
18/** A failed lookup is retried sooner, so a flaky network does not hide an update for an hour. */
19const MISSING_TTL = Duration.minutes(1);
20
21interface Entry {
22 readonly expiresAt: number;
23 readonly version: string | null;
24}
25
26export 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. */
43export 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
75export 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";
1515import * as Stream from "effect/Stream";
1616import { HttpClient } from "effect/http";
1717import { ChildProcessSpawner } from "effect/process";
18import * as ProviderLatestVersions from "./ProviderLatestVersions.ts";
1819import {
1920 createProviderVersionAdvisory,
2021 makeTargetedProviderUpdateAction,
......@@ -26,7 +27,6 @@ import {
2627 normalizeCommandPath,
2728 npmGlobalPrefixFromCommandPath,
2829 parseHomebrewLatestVersion,
29 ProviderVersionCache,
3030 resolveLatestProviderVersion,
3131 resolvePackageManagedProviderMaintenance,
3232 resolveProviderMaintenanceCapabilitiesEffect,
......@@ -124,19 +124,11 @@ function stdoutSpawner(onSpawn: (command: string, args: ReadonlyArray<string>) =
124124}
125125
126126it.layer(NodeServices.layer)("providerMaintenance", (it) => {
127 it.effect("reads cached versions through the injectable cache reference", () =>
127 it.effect("reads cached versions through ProviderLatestVersions", () =>
128128 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"]]),
140132 ),
141133 Effect.provideService(
142134 HttpClient.HttpClient,
......@@ -152,7 +144,7 @@ it.layer(NodeServices.layer)("providerMaintenance", (it) => {
152144
153145 it.effect("prefers the installer's own latest version over the npm registry", () =>
154146 resolveLatestProviderVersion({ ...manualPackageTool, latestVersion: "1.2.0" }).pipe(
155 Effect.provideService(ProviderVersionCache, new Map()),
147 Effect.provide(ProviderLatestVersions.layer),
156148 Effect.provideService(
157149 HttpClient.HttpClient,
158150 HttpClient.make(() =>
......@@ -169,7 +161,7 @@ it.layer(NodeServices.layer)("providerMaintenance", (it) => {
169161 enrichProviderSnapshotWithVersionAdvisory(installedPackageToolProvider, manualPackageTool, {
170162 enableProviderUpdateChecks: false,
171163 }).pipe(
172 Effect.provideService(ProviderVersionCache, new Map()),
164 Effect.provide(ProviderLatestVersions.layer),
173165 Effect.provideService(
174166 HttpClient.HttpClient,
175167 HttpClient.make(() =>
packages/provider-core/src/server/maintenanceResolver.ts+3-26
......@@ -17,7 +17,6 @@ import { causeErrorTag } from "@t3tools/shared/observability";
1717import { resolveCommandPath } from "@t3tools/shared/shell";
1818import * as Cache from "effect/Cache";
1919import * as Config from "effect/Config";
20import * as Context from "effect/Context";
2120import * as DateTime from "effect/DateTime";
2221import * as Duration from "effect/Duration";
2322import * as Effect from "effect/Effect";
......@@ -30,8 +29,8 @@ import { HttpClient, HttpClientRequest } from "effect/http";
3029import { ChildProcess, ChildProcessSpawner } from "effect/process";
3130
3231import { collectUint8StreamText } from "./collectStreamText.ts";
32import * as ProviderLatestVersions from "./ProviderLatestVersions.ts";
3333
34const LATEST_VERSION_CACHE_TTL_MS = 60 * 60 * 1_000;
3534const LATEST_VERSION_TIMEOUT_MS = 4_000;
3635const HOMEBREW_INFO_TIMEOUT_MS = 10_000;
3736const HOMEBREW_INFO_MAX_BYTES = 256 * 1_024;
......@@ -102,17 +101,6 @@ export interface PackageManagedProviderMaintenanceDefinition {
102101 } | null;
103102}
104103
105export interface ProviderVersionCacheEntry {
106 readonly expiresAt: number;
107 readonly version: string | null;
108}
109
110export const ProviderVersionCache = Context.Reference<Map<string, ProviderVersionCacheEntry>>(
111 "@t3tools/server/providerMaintenance/ProviderVersionCache",
112 {
113 defaultValue: () => new Map(),
114 },
115);
116104const NpmLatestVersionResponse = Schema.Struct({
117105 version: Schema.optional(Schema.String),
118106});
......@@ -784,19 +772,8 @@ export const resolveLatestProviderVersion = Effect.fn("resolveLatestProviderVers
784772 return null;
785773 }
786774
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));
800777});
801778
802779export const enrichProviderSnapshotWithVersionAdvisory = Effect.fn(
packages/provider-grok/src/server/driver.test.ts+2
......@@ -9,6 +9,7 @@ import * as Path from "effect/Path";
99import { HttpClient } from "effect/http";
1010import * as ChildProcessSpawner from "effect/process/ChildProcessSpawner";
1111
12import * as ProviderLatestVersions from "@t3tools/provider-core/server/ProviderLatestVersions";
1213import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers";
1314import { layerTestProviderHost } from "@t3tools/provider-testing/host";
1415import { GrokDriver } from "./driver.ts";
......@@ -18,6 +19,7 @@ import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";
1819const layerTest = layerTestProviderHost({ runBackgroundWork: false }).pipe(
1920 Layer.provideMerge(NodeServices.layer),
2021 Layer.provideMerge(IdAllocator.layer),
22 Layer.provideMerge(ProviderLatestVersions.layer),
2123 Layer.provideMerge(
2224 Layer.succeed(
2325 ProviderEventLoggers.ProviderEventLoggers,
packages/provider-grok/src/server/driver.ts+5-1
......@@ -8,6 +8,7 @@ import * as Schema from "effect/Schema";
88import { HttpClient } from "effect/http";
99import { ChildProcessSpawner } from "effect/process";
1010
11import * as ProviderLatestVersions from "@t3tools/provider-core/server/ProviderLatestVersions";
1112import * as ProviderHost from "@t3tools/provider-core/server/ProviderHost";
1213import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers";
1314import { makeGrokTextGeneration } from "./textGeneration.ts";
......@@ -77,6 +78,7 @@ export type GrokDriverEnv =
7778 | Crypto.Crypto
7879 | FileSystem.FileSystem
7980 | HttpClient.HttpClient
81 | ProviderLatestVersions.ProviderLatestVersions
8082 | Path.Path
8183 | ProviderEventLoggers.ProviderEventLoggers;
8284
......@@ -93,6 +95,7 @@ export const GrokDriver: ProviderDriver<GrokSettings, GrokDriverEnv> = {
9395 const crypto = yield* Crypto.Crypto;
9496 const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;
9597 const httpClient = yield* HttpClient.HttpClient;
98 const latestVersions = yield* ProviderLatestVersions.ProviderLatestVersions;
9699 const fileSystem = yield* FileSystem.FileSystem;
97100 const path = yield* Path.Path;
98101 const host = yield* ProviderHost.ProviderHost;
......@@ -177,9 +180,10 @@ export const GrokDriver: ProviderDriver<GrokSettings, GrokDriverEnv> = {
177180 maintenanceCapabilities,
178181 enableProviderUpdateChecks: settings.enableProviderUpdateChecks,
179182 publishSnapshot,
180 httpClient,
181183 }),
182184 ),
185 Effect.provideService(HttpClient.HttpClient, httpClient),
186 Effect.provideService(ProviderLatestVersions.ProviderLatestVersions, latestVersions),
183187 ),
184188 }).pipe(
185189 Effect.mapError(
packages/provider-grok/src/server/status.ts+6-3
......@@ -21,6 +21,7 @@ import { ChildProcess, ChildProcessSpawner } from "effect/process";
2121import { createModelCapabilities } from "@t3tools/shared/model";
2222import { resolveSpawnCommand } from "@t3tools/shared/shell";
2323
24import * as ProviderLatestVersions from "@t3tools/provider-core/server/ProviderLatestVersions";
2425import {
2526 AUTH_PROBE_TIMEOUT_MS,
2627 buildServerProvider,
......@@ -567,14 +568,16 @@ export const enrichGrokSnapshot = (input: {
567568 readonly maintenanceCapabilities: ProviderMaintenanceCapabilities;
568569 readonly enableProviderUpdateChecks?: boolean;
569570 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> => {
572576 const { snapshot, publishSnapshot } = input;
573577
574578 return enrichProviderSnapshotWithVersionAdvisory(snapshot, input.maintenanceCapabilities, {
575579 enableProviderUpdateChecks: input.enableProviderUpdateChecks,
576580 }).pipe(
577 Effect.provideService(HttpClient.HttpClient, input.httpClient),
578581 Effect.flatMap((enrichedSnapshot) => publishSnapshot(enrichedSnapshot)),
579582 Effect.catchCause((cause) =>
580583 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";
88import { HttpClient } from "effect/http";
99import { ChildProcessSpawner } from "effect/process";
1010
11import * as ProviderLatestVersions from "@t3tools/provider-core/server/ProviderLatestVersions";
1112import * as ProviderHost from "@t3tools/provider-core/server/ProviderHost";
1213import { expandHomePath } from "@t3tools/provider-core/server/pathExpansion";
1314import { makeMuseTextGeneration } from "./textGeneration.ts";
......@@ -46,6 +47,7 @@ export type MuseDriverEnv =
4647 | ChildProcessSpawner.ChildProcessSpawner
4748 | FileSystem.FileSystem
4849 | HttpClient.HttpClient
50 | ProviderLatestVersions.ProviderLatestVersions
4951 | Path.Path
5052 | ProviderEventLoggers.ProviderEventLoggers;
5153
......@@ -60,6 +62,7 @@ export const MuseDriver: ProviderDriver<MuseSettings, MuseDriverEnv> = {
6062 const fileSystem = yield* FileSystem.FileSystem;
6163 const path = yield* Path.Path;
6264 const httpClient = yield* HttpClient.HttpClient;
65 const latestVersions = yield* ProviderLatestVersions.ProviderLatestVersions;
6366 const host = yield* ProviderHost.ProviderHost;
6467 const eventLoggers = yield* ProviderEventLoggers.ProviderEventLoggers;
6568 const { cwd } = host.paths;
......@@ -105,6 +108,10 @@ export const MuseDriver: ProviderDriver<MuseSettings, MuseDriverEnv> = {
105108 const latestVersion = options?.fresh
106109 ? yield* latestMuseVersion(processEnvironment, { fresh: true }).pipe(
107110 Effect.provideService(HttpClient.HttpClient, httpClient),
111 Effect.provideService(
112 ProviderLatestVersions.ProviderLatestVersions,
113 latestVersions,
114 ),
108115 )
109116 : undefined;
110117 return latestVersion !== undefined ? { ...capabilities, latestVersion } : capabilities;
......@@ -133,6 +140,7 @@ export const MuseDriver: ProviderDriver<MuseSettings, MuseDriverEnv> = {
133140 }),
134141 ),
135142 Effect.provideService(HttpClient.HttpClient, httpClient),
143 Effect.provideService(ProviderLatestVersions.ProviderLatestVersions, latestVersions),
136144 Effect.flatMap(publishSnapshot),
137145 ),
138146 }).pipe(
packages/provider-muse/src/server/maintenance.test.ts+7-8
......@@ -12,10 +12,8 @@ import {
1212 latestMuseVersion,
1313 parseMuseVersion,
1414} from "./maintenance.ts";
15import {
16 ProviderVersionCache,
17 makeManualOnlyProviderMaintenanceCapabilities,
18} from "@t3tools/provider-core/server/maintenanceResolver";
15import * as ProviderLatestVersions from "@t3tools/provider-core/server/ProviderLatestVersions";
16import { makeManualOnlyProviderMaintenanceCapabilities } from "@t3tools/provider-core/server/maintenanceResolver";
1917
2018const maintenanceCapabilities = {
2119 ...makeManualOnlyProviderMaintenanceCapabilities({
......@@ -50,7 +48,7 @@ it.layer(NodeServices.layer)("Muse maintenance", (it) => {
5048 expect(yield* latestMuseVersion({}, { fresh: true })).toBe("1.1.1-R10.1");
5149 });
5250 yield* check.pipe(
53 Effect.provideService(ProviderVersionCache, new Map()),
51 Effect.provide(ProviderLatestVersions.layer),
5452 Effect.provideService(
5553 HttpClient.HttpClient,
5654 HttpClient.make((request) => {
......@@ -101,7 +99,7 @@ it.layer(NodeServices.layer)("Muse maintenance", (it) => {
10199 });
102100 const [first, second] = yield* Effect.all([check, check], { concurrency: 1 }).pipe(
103101 Effect.provideService(HttpClient.HttpClient, httpClient),
104 Effect.provideService(ProviderVersionCache, new Map()),
102 Effect.provide(ProviderLatestVersions.layer),
105103 );
106104 expect(first.versionAdvisory).toMatchObject({
107105 status: "behind_latest",
......@@ -134,7 +132,7 @@ it.layer(NodeServices.layer)("Muse maintenance", (it) => {
134132 ),
135133 ),
136134 ),
137 Effect.provideService(ProviderVersionCache, new Map()),
135 Effect.provide(ProviderLatestVersions.layer),
138136 );
139137 expect(result.versionAdvisory?.status).toBe(status);
140138 }
......@@ -153,6 +151,7 @@ it.layer(NodeServices.layer)("Muse maintenance", (it) => {
153151 maintenanceCapabilities,
154152 environment: {},
155153 }).pipe(
154 Effect.provide(ProviderLatestVersions.layer),
156155 Effect.provideService(
157156 HttpClient.HttpClient,
158157 HttpClient.make(() => Effect.die("Unexpected release request")),
......@@ -182,7 +181,7 @@ it.layer(NodeServices.layer)("Muse maintenance", (it) => {
182181 Effect.succeed(HttpClientResponse.fromWeb(request, response)),
183182 ),
184183 ),
185 Effect.provideService(ProviderVersionCache, new Map()),
184 Effect.provide(ProviderLatestVersions.layer),
186185 );
187186 expect(result.status).toBe("ready");
188187 expect(result.versionAdvisory?.status).toBe("unknown");
packages/provider-muse/src/server/maintenance.ts+16-19
......@@ -1,6 +1,5 @@
11import { ProviderDriverKind, type ServerProvider } from "@t3tools/contracts";
22import { compareSemverVersions } from "@t3tools/shared/semver";
3import * as DateTime from "effect/DateTime";
43import * as Effect from "effect/Effect";
54import * as FileSystem from "effect/FileSystem";
65import * as Schema from "effect/Schema";
......@@ -10,10 +9,10 @@ import {
109 enrichProviderSnapshotWithVersionAdvisory,
1110 makeManualOnlyProviderMaintenanceCapabilities,
1211 makeProviderMaintenanceCapabilities,
13 ProviderVersionCache,
1412 type ProviderMaintenanceCapabilities,
1513 type ProviderMaintenanceCapabilitiesResolver,
1614} from "@t3tools/provider-core/server/maintenanceResolver";
15import * as ProviderLatestVersions from "@t3tools/provider-core/server/ProviderLatestVersions";
1716import { parseGenericCliVersion } from "@t3tools/provider-core/server/snapshotProbe";
1817
1918const DRIVER = ProviderDriverKind.make("muse");
......@@ -97,26 +96,24 @@ export const latestMuseVersion = Effect.fn("latestMuseVersion")(function* (
9796 const channel = environment.MUSE_CHANNEL || "muse-stable";
9897 if (!MUSE_CHANNELS.has(channel)) return null;
9998 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;
10599 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),
114114 ),
115 Effect.timeout(4_000),
116 Effect.orElseSucceed(() => null),
115 options,
117116 );
118 cache.set(key, { version, expiresAt: now + (version === null ? 60_000 : 60 * 60 * 1_000) });
119 return version;
120117});
121118
122119/** 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";
1515import { HttpClient } from "effect/http";
1616import { ChildProcessSpawner } from "effect/process";
1717
18import * as ProviderLatestVersions from "@t3tools/provider-core/server/ProviderLatestVersions";
1819import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";
1920import * as ProviderEventLoggers from "@t3tools/provider-core/server/ProviderEventLoggers";
2021import * as ProviderMaintenance from "@t3tools/provider-core/server/maintenanceResolver";
......@@ -48,6 +49,7 @@ const openCode2Runtime = {
4849
4950const layer = Layer.mergeAll(
5051 IdAllocator.layer,
52 ProviderLatestVersions.layer,
5153 layerTestProviderHost(),
5254 Layer.succeed(
5355 ProviderEventLoggers.ProviderEventLoggers,
......@@ -210,6 +212,7 @@ const changingRuntime = {
210212} as unknown as OpenCodeRuntime.OpenCodeRuntimeShape;
211213const layerUpdate = Layer.mergeAll(
212214 IdAllocator.layer,
215 ProviderLatestVersions.layer,
213216 layerTestProviderHost(),
214217 Layer.succeed(
215218 ProviderEventLoggers.ProviderEventLoggers,
packages/provider-opencode/src/server/driver.ts+4
......@@ -27,6 +27,7 @@ import { ChildProcessSpawner } from "effect/process";
2727
2828import * as OpenCode2TextGeneration from "./v2/textGeneration.ts";
2929import { makeOpenCodeTextGeneration } from "./textGeneration.ts";
30import * as ProviderLatestVersions from "@t3tools/provider-core/server/ProviderLatestVersions";
3031import * as ProviderHost from "@t3tools/provider-core/server/ProviderHost";
3132import * as OpenCodeAdapterV2 from "./adapter.ts";
3233import * as OpenCode2AdapterV2 from "./v2/adapter.ts";
......@@ -177,6 +178,7 @@ export type OpenCodeDriverEnv =
177178 | Crypto.Crypto
178179 | FileSystem.FileSystem
179180 | HttpClient.HttpClient
181 | ProviderLatestVersions.ProviderLatestVersions
180182 | OpenCodeRuntime.OpenCodeRuntime
181183 | Path.Path;
182184
......@@ -195,6 +197,7 @@ export const OpenCodeDriver: ProviderDriver<OpenCodeSettings, OpenCodeDriverEnv>
195197 const pathService = yield* Path.Path;
196198 const openCodeRuntime = yield* OpenCodeRuntime.OpenCodeRuntime;
197199 const httpClient = yield* HttpClient.HttpClient;
200 const latestVersions = yield* ProviderLatestVersions.ProviderLatestVersions;
198201 const crypto = yield* Crypto.Crypto;
199202 const host = yield* ProviderHost.ProviderHost;
200203 const processEnv = mergeProviderInstanceEnvironment(environment);
......@@ -465,6 +468,7 @@ export const OpenCodeDriver: ProviderDriver<OpenCodeSettings, OpenCodeDriverEnv>
465468 }),
466469 ),
467470 Effect.provideService(HttpClient.HttpClient, httpClient),
471 Effect.provideService(ProviderLatestVersions.ProviderLatestVersions, latestVersions),
468472 Effect.flatMap((enrichedSnapshot) => publishSnapshot(enrichedSnapshot)),
469473 ),
470474 },
packages/provider-pi/src/server/driver.test.ts+2
......@@ -13,6 +13,7 @@ import * as TestClock from "effect/testing/TestClock";
1313import { HttpClient } from "effect/http";
1414import { ChildProcess, ChildProcessSpawner } from "effect/process";
1515
16import * as ProviderLatestVersions from "@t3tools/provider-core/server/ProviderLatestVersions";
1617import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator";
1718import { layerTestProviderHost } from "@t3tools/provider-testing/host";
1819import type { PiSettings } from "../settings.ts";
......@@ -25,6 +26,7 @@ const layerTest = Layer.mergeAll(
2526 runBackgroundWork: false,
2627 }),
2728 IdAllocator.layer,
29 ProviderLatestVersions.layer,
2830 Layer.succeed(
2931 HttpClient.HttpClient,
3032 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";
1515import { HttpClient } from "effect/http";
1616import { ChildProcessSpawner } from "effect/process";
1717
18import * as ProviderLatestVersions from "@t3tools/provider-core/server/ProviderLatestVersions";
1819import * as ProviderHost from "@t3tools/provider-core/server/ProviderHost";
1920import { makePiTextGeneration } from "./textGeneration.ts";
2021import { PiAdapterV2Driver, type PiAdapterV2DriverEnv } from "./adapter.ts";
......@@ -60,6 +61,7 @@ export type PiDriverEnv =
6061 | ChildProcessSpawner.ChildProcessSpawner
6162 | FileSystem.FileSystem
6263 | HttpClient.HttpClient
64 | ProviderLatestVersions.ProviderLatestVersions
6365 | Path.Path;
6466
6567const withInstanceIdentity =
......@@ -92,6 +94,7 @@ export const PiDriver: ProviderDriver<PiSettings, PiDriverEnv> = {
9294 const fileSystem = yield* FileSystem.FileSystem;
9395 const pathService = yield* Path.Path;
9496 const httpClient = yield* HttpClient.HttpClient;
97 const latestVersions = yield* ProviderLatestVersions.ProviderLatestVersions;
9598 const host = yield* ProviderHost.ProviderHost;
9699 const { cwd } = host.paths;
97100 const processEnv = mergeProviderInstanceEnvironment(environment);
......@@ -159,9 +162,10 @@ export const PiDriver: ProviderDriver<PiSettings, PiDriverEnv> = {
159162 maintenanceCapabilities,
160163 enableProviderUpdateChecks: settings.enableProviderUpdateChecks,
161164 publishSnapshot,
162 httpClient,
163165 }),
164166 ),
167 Effect.provideService(HttpClient.HttpClient, httpClient),
168 Effect.provideService(ProviderLatestVersions.ProviderLatestVersions, latestVersions),
165169 ),
166170 }).pipe(
167171 Effect.mapError(
packages/provider-pi/src/server/status.ts+6-3
......@@ -34,6 +34,7 @@ import {
3434 piRecordField as recordField,
3535 piRecordString as recordString,
3636} from "./rpc.ts";
37import * as ProviderLatestVersions from "@t3tools/provider-core/server/ProviderLatestVersions";
3738import {
3839 buildServerProvider,
3940 isCommandMissingCause,
......@@ -461,13 +462,15 @@ export const enrichPiSnapshot = (input: {
461462 readonly maintenanceCapabilities: ProviderMaintenanceCapabilities;
462463 readonly enableProviderUpdateChecks?: boolean;
463464 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> => {
466470 const { snapshot, publishSnapshot } = input;
467471 return enrichProviderSnapshotWithVersionAdvisory(snapshot, input.maintenanceCapabilities, {
468472 enableProviderUpdateChecks: input.enableProviderUpdateChecks,
469473 }).pipe(
470 Effect.provideService(HttpClient.HttpClient, input.httpClient),
471474 Effect.flatMap((enrichedSnapshot) => publishSnapshot(enrichedSnapshot)),
472475 Effect.catchCause((cause) =>
473476 Effect.logWarning("Pi version advisory enrichment failed", {