From 52f9bcbb8204c5bb459ec9227c64dd392a551c41 Mon Sep 17 00:00:00 2001 From: Shoubhit Dash Date: Thu, 14 May 2026 00:10:31 +0530 Subject: [PATCH] refactor(flags): route installation client through runtime flags (#27369) --- packages/opencode/src/installation/index.ts | 7 +++++-- packages/opencode/src/provider/models.ts | 9 +++++++-- packages/opencode/test/provider/models.test.ts | 10 ++++++++-- 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/packages/opencode/src/installation/index.ts b/packages/opencode/src/installation/index.ts index cc0b06e8e4..00fbabe351 100644 --- a/packages/opencode/src/installation/index.ts +++ b/packages/opencode/src/installation/index.ts @@ -5,7 +5,6 @@ import { ChildProcess } from "effect/unstable/process" import { AppProcess } from "@opencode-ai/core/process" import path from "path" import { BusEvent } from "@/bus/bus-event" -import { Flag } from "@opencode-ai/core/flag/flag" import * as Log from "@opencode-ai/core/util/log" import { makeRuntime } from "@opencode-ai/core/effect/runtime" import semver from "semver" @@ -50,7 +49,11 @@ export const Info = Schema.Struct({ }).annotate({ identifier: "InstallationInfo" }) export type Info = Schema.Schema.Type -export const USER_AGENT = `opencode/${InstallationChannel}/${InstallationVersion}/${Flag.OPENCODE_CLIENT}` +export function userAgent(client = "cli") { + return `opencode/${InstallationChannel}/${InstallationVersion}/${client}` +} + +export const USER_AGENT = userAgent() export function isPreview() { return InstallationChannel !== "latest" diff --git a/packages/opencode/src/provider/models.ts b/packages/opencode/src/provider/models.ts index 9f88f9db17..8d88e0fced 100644 --- a/packages/opencode/src/provider/models.ts +++ b/packages/opencode/src/provider/models.ts @@ -9,6 +9,7 @@ import { Hash } from "@opencode-ai/core/util/hash" import { AppFileSystem } from "@opencode-ai/core/filesystem" import { withTransientReadRetry } from "@/util/effect-http-client" import { CatalogModelStatus } from "./model-status" +import { RuntimeFlags } from "@/effect/runtime-flags" const CostTier = Schema.Struct({ input: Schema.Finite, @@ -109,11 +110,14 @@ export interface Interface { export class Service extends Context.Service()("@opencode/ModelsDev") {} -export const layer: Layer.Layer = Layer.effect( +type Requirements = AppFileSystem.Service | HttpClient.HttpClient | RuntimeFlags.Service + +export const layer: Layer.Layer = Layer.effect( Service, Effect.gen(function* () { const fs = yield* AppFileSystem.Service const http = HttpClient.filterStatusOk(withTransientReadRetry(yield* HttpClient.HttpClient)) + const flags = yield* RuntimeFlags.Service const source = Flag.OPENCODE_MODELS_URL || "https://models.dev" const filepath = path.join( @@ -132,7 +136,7 @@ export const layer: Layer.Layer res.text), Effect.timeout("10 seconds"), @@ -208,6 +212,7 @@ export const layer: Layer.Layer = layer.pipe( Layer.provide(FetchHttpClient.layer), Layer.provide(AppFileSystem.defaultLayer), + Layer.provide(RuntimeFlags.defaultLayer), ) export * as ModelsDev from "./models" diff --git a/packages/opencode/test/provider/models.test.ts b/packages/opencode/test/provider/models.test.ts index 7ccf126a9c..c0dbd5caae 100644 --- a/packages/opencode/test/provider/models.test.ts +++ b/packages/opencode/test/provider/models.test.ts @@ -8,6 +8,7 @@ import { ModelsDev } from "../../src/provider/models" import { it } from "../lib/effect" import { rm, writeFile, utimes, mkdir } from "fs/promises" import path from "path" +import { RuntimeFlags } from "@/effect/runtime-flags" // test/preload.ts pins OPENCODE_MODELS_PATH to a fixture so other tests can // resolve providers without network. These tests need to drive the on-disk @@ -70,13 +71,16 @@ const fixture2: Record = { interface MockState { body: string status: number - calls: Array<{ url: string }> + calls: Array<{ url: string; userAgent: string | null }> } const makeMockClient = (state: Ref.Ref) => HttpClient.make((request) => Effect.gen(function* () { - yield* Ref.update(state, (s) => ({ ...s, calls: [...s.calls, { url: request.url }] })) + yield* Ref.update(state, (s) => ({ + ...s, + calls: [...s.calls, { url: request.url, userAgent: request.headers["user-agent"] ?? null }], + })) const s = yield* Ref.get(state) return HttpClientResponse.fromWeb(request, new Response(s.body, { status: s.status })) }), @@ -89,6 +93,7 @@ const buildLayer = (state: Ref.Ref) => Layer.fresh(ModelsDev.layer).pipe( Layer.provide(Layer.succeed(HttpClient.HttpClient, makeMockClient(state))), Layer.provide(AppFileSystem.defaultLayer), + Layer.provide(RuntimeFlags.layer({ client: "test-client" })), ) const writeCache = (data: object, mtimeMs?: number) => @@ -202,6 +207,7 @@ describe("ModelsDev Service", () => { const final = yield* Ref.get(state) expect(final.calls.length).toBe(1) expect(final.calls[0].url).toContain("/api.json") + expect(final.calls[0].userAgent).toContain("/test-client") }), )