mirror of
https://github.com/anomalyco/opencode.git
synced 2026-07-22 18:25:32 -04:00
refactor: centralize client identity (#38148)
This commit is contained in:
@@ -1,10 +1,11 @@
|
||||
#!/usr/bin/env bun
|
||||
|
||||
import { NodeRuntime, NodeServices } from "@effect/platform-node"
|
||||
import { Effect } from "effect"
|
||||
import { Effect, Layer } from "effect"
|
||||
import { Commands } from "./commands/commands"
|
||||
import { Runtime } from "./framework/runtime"
|
||||
import { Observability } from "@opencode-ai/util/observability"
|
||||
import { Client } from "@opencode-ai/util/client"
|
||||
import { Updater } from "./services/updater"
|
||||
import { InstallationChannel, InstallationVersion, InstallationLocal } from "@opencode-ai/util/installation/version"
|
||||
import { LayerNode } from "@opencode-ai/util/effect/layer-node"
|
||||
@@ -73,8 +74,7 @@ Effect.logInfo("cli starting", {
|
||||
Observability.layer({
|
||||
endpoint: process.env.OTEL_EXPORTER_OTLP_ENDPOINT,
|
||||
headers: process.env.OTEL_EXPORTER_OTLP_HEADERS,
|
||||
client: process.env.OPENCODE_CLIENT ?? "cli",
|
||||
}),
|
||||
}).pipe(Layer.provide(Client.layer(process.env.OPENCODE_CLIENT))),
|
||||
),
|
||||
Effect.provide(NodeServices.layer),
|
||||
Effect.scoped,
|
||||
|
||||
@@ -3,6 +3,7 @@ import { Context, Duration, Effect, Layer, Option, Schedule, Schema } from "effe
|
||||
import { HttpClient, HttpClientRequest } from "effect/unstable/http"
|
||||
import { ModelsDev } from "@opencode-ai/schema/models-dev"
|
||||
import { Money } from "@opencode-ai/schema/money"
|
||||
import { Client } from "@opencode-ai/util/client"
|
||||
import { Global } from "@opencode-ai/util/global"
|
||||
import { Flock } from "@opencode-ai/util/flock"
|
||||
import { Hash } from "@opencode-ai/util/hash"
|
||||
@@ -531,17 +532,18 @@ export const Options = Schema.Struct({
|
||||
url: Schema.optional(Schema.String),
|
||||
file: Schema.optional(Schema.String),
|
||||
fetch: Schema.optional(Schema.Boolean),
|
||||
client: Schema.optional(Schema.String),
|
||||
})
|
||||
export type Options = typeof Options.Type
|
||||
|
||||
export class Service extends Context.Service<Service, Interface>()("@opencode/ModelsDev") {}
|
||||
|
||||
export const layer = (options?: Options) => Layer.effect(
|
||||
export const layer = (options?: Options) =>
|
||||
Layer.effect(
|
||||
Service,
|
||||
Effect.gen(function* () {
|
||||
const fs = yield* FSUtil.Service
|
||||
const events = yield* EventV2.Service
|
||||
const client = yield* Client.Name
|
||||
const http = HttpClient.filterStatusOk(
|
||||
(yield* HttpClient.HttpClient).pipe(
|
||||
HttpClient.retryTransient({
|
||||
@@ -554,7 +556,7 @@ export const layer = (options?: Options) => Layer.effect(
|
||||
|
||||
const source = options?.url || "https://models.dev"
|
||||
const fetch = options?.fetch ?? true
|
||||
const userAgent = `opencode/${InstallationChannel}/${InstallationVersion}/${options?.client ?? "cli"}`
|
||||
const userAgent = `opencode/${InstallationChannel}/${InstallationVersion}/${client}`
|
||||
const filepath = path.join(
|
||||
Global.Path.cache,
|
||||
source === "https://models.dev" ? "models.json" : `models-${Hash.fast(source)}.json`,
|
||||
@@ -581,11 +583,7 @@ export const layer = (options?: Options) => Layer.effect(
|
||||
const loadFromDisk = fs.readJson(options?.file ?? filepath).pipe(
|
||||
Effect.map((input) => input as Record<string, SourceProvider>),
|
||||
Effect.catch((error) => {
|
||||
if (
|
||||
options?.file === undefined &&
|
||||
error._tag === "FileSystemError" &&
|
||||
error.method === "readJson"
|
||||
) {
|
||||
if (options?.file === undefined && error._tag === "FileSystemError" && error.method === "readJson") {
|
||||
return fs.remove(filepath, { force: true }).pipe(Effect.ignore, Effect.as(undefined))
|
||||
}
|
||||
return Effect.succeed(undefined)
|
||||
@@ -659,7 +657,11 @@ export const layer = (options?: Options) => Layer.effect(
|
||||
)
|
||||
|
||||
export function configured(options?: Options) {
|
||||
return makeGlobalNode({ service: Service, layer: layer(options), deps: [FSUtil.node, EventV2.node, httpClient] })
|
||||
return makeGlobalNode({
|
||||
service: Service,
|
||||
layer: layer(options),
|
||||
deps: [FSUtil.node, EventV2.node, Client.node, httpClient],
|
||||
})
|
||||
}
|
||||
|
||||
export const node = configured()
|
||||
|
||||
@@ -10,6 +10,7 @@ import { llmClient } from "../effect/app-node-platform"
|
||||
import { SessionEvent } from "./event"
|
||||
import type { SessionMessage } from "./message"
|
||||
import { SessionModelHeaders } from "./model-headers"
|
||||
import { Client } from "@opencode-ai/util/client"
|
||||
import { SessionRunnerModel } from "./runner/model"
|
||||
import { SessionSchema } from "./schema"
|
||||
import { toSessionError } from "./to-session-error"
|
||||
@@ -60,7 +61,7 @@ type Settings = {
|
||||
}
|
||||
|
||||
type Dependencies = {
|
||||
readonly headers?: SessionModelHeaders.Options
|
||||
readonly client: string
|
||||
readonly events: EventV2.Interface
|
||||
readonly llm: {
|
||||
readonly stream: (request: LLMRequest) => Stream.Stream<LLMEvent, LLMError>
|
||||
@@ -259,7 +260,7 @@ const make = (dependencies: Dependencies) => {
|
||||
.stream(
|
||||
LLM.request({
|
||||
model: plan.model,
|
||||
http: { headers: SessionModelHeaders.make(plan.session, dependencies.headers) },
|
||||
http: { headers: SessionModelHeaders.make(plan.session, dependencies.client) },
|
||||
messages: [Message.user(plan.prompt)],
|
||||
tools: [],
|
||||
}),
|
||||
@@ -391,23 +392,20 @@ const make = (dependencies: Dependencies) => {
|
||||
})
|
||||
}
|
||||
|
||||
export const layer = (options?: SessionModelHeaders.Options) => Layer.effect(
|
||||
export const layer = Layer.effect(
|
||||
Service,
|
||||
Effect.gen(function* () {
|
||||
const events = yield* EventV2.Service
|
||||
const llm = yield* LLMClient.Service
|
||||
const config = yield* Config.Service
|
||||
const models = yield* SessionRunnerModel.Service
|
||||
return make({ events, llm, models, config: settings(yield* config.entries()), headers: options })
|
||||
const client = yield* Client.Name
|
||||
return make({ events, llm, models, config: settings(yield* config.entries()), client })
|
||||
}),
|
||||
)
|
||||
|
||||
export function configured(options?: SessionModelHeaders.Options) {
|
||||
return makeLocationNode({
|
||||
service: Service,
|
||||
layer: layer(options),
|
||||
deps: [EventV2.node, llmClient, Config.node, SessionRunnerModel.node],
|
||||
})
|
||||
}
|
||||
|
||||
export const node = configured()
|
||||
export const node = makeLocationNode({
|
||||
service: Service,
|
||||
layer,
|
||||
deps: [EventV2.node, llmClient, Config.node, SessionRunnerModel.node, Client.node],
|
||||
})
|
||||
|
||||
@@ -4,6 +4,7 @@ import { LLM, LLMClient, Message, SystemPart } from "@opencode-ai/ai"
|
||||
import { Effect, Layer } from "effect"
|
||||
import { Database } from "../database/database"
|
||||
import { makeLocationNode } from "@opencode-ai/util/effect/app-node"
|
||||
import { Client } from "@opencode-ai/util/client"
|
||||
import { llmClient } from "../effect/app-node-platform"
|
||||
import { PluginHooks } from "../plugin/hooks"
|
||||
import { SessionContext } from "./context"
|
||||
@@ -14,7 +15,7 @@ import { SessionRunnerModel } from "./runner/model"
|
||||
import PROMPT_DEFAULT from "./runner/prompt/base.txt"
|
||||
import { toLLMMessages } from "./runner/to-llm-message"
|
||||
|
||||
export const layer = (options?: SessionModelHeaders.Options) => Layer.effect(
|
||||
export const layer = Layer.effect(
|
||||
SessionGenerate.Service,
|
||||
Effect.gen(function* () {
|
||||
const context = yield* SessionContext.Service
|
||||
@@ -22,6 +23,7 @@ export const layer = (options?: SessionModelHeaders.Options) => Layer.effect(
|
||||
const hooks = yield* PluginHooks.Service
|
||||
const llm = yield* LLMClient.Service
|
||||
const models = yield* SessionRunnerModel.Service
|
||||
const client = yield* Client.Name
|
||||
|
||||
return SessionGenerate.Service.of({
|
||||
generate: Effect.fn("SessionGenerate.generate")(function* (input) {
|
||||
@@ -49,7 +51,7 @@ export const layer = (options?: SessionModelHeaders.Options) => Layer.effect(
|
||||
return (yield* llm.generate(
|
||||
LLM.request({
|
||||
model: model.model,
|
||||
http: { headers: SessionModelHeaders.make(selection.session, options) },
|
||||
http: { headers: SessionModelHeaders.make(selection.session, client) },
|
||||
providerOptions: { openai: { promptCacheKey } },
|
||||
system: contextEvent.system,
|
||||
messages: contextEvent.messages,
|
||||
@@ -62,12 +64,8 @@ export const layer = (options?: SessionModelHeaders.Options) => Layer.effect(
|
||||
}),
|
||||
)
|
||||
|
||||
export function configured(options?: SessionModelHeaders.Options) {
|
||||
return makeLocationNode({
|
||||
service: SessionGenerate.Service,
|
||||
layer: layer(options),
|
||||
deps: [SessionContext.node, Database.node, PluginHooks.node, SessionRunnerModel.node, llmClient],
|
||||
})
|
||||
}
|
||||
|
||||
export const node = configured()
|
||||
export const node = makeLocationNode({
|
||||
service: SessionGenerate.Service,
|
||||
layer,
|
||||
deps: [SessionContext.node, Database.node, PluginHooks.node, SessionRunnerModel.node, Client.node, llmClient],
|
||||
})
|
||||
|
||||
@@ -2,22 +2,13 @@ export * as SessionModelHeaders from "./model-headers"
|
||||
|
||||
import { InstallationVersion } from "@opencode-ai/util/installation/version"
|
||||
import { SessionSchema } from "./schema"
|
||||
import { Schema } from "effect"
|
||||
|
||||
export const Options = Schema.Struct({
|
||||
client: Schema.optional(Schema.String),
|
||||
})
|
||||
export type Options = typeof Options.Type
|
||||
|
||||
export const make = (
|
||||
session: Pick<SessionSchema.Info, "id" | "parentID" | "projectID">,
|
||||
options?: Options,
|
||||
) => ({
|
||||
export const make = (session: Pick<SessionSchema.Info, "id" | "parentID" | "projectID">, client: string) => ({
|
||||
"x-session-affinity": session.id,
|
||||
"X-Session-Id": session.id,
|
||||
...(session.parentID ? { "x-parent-session-id": session.parentID } : {}),
|
||||
"User-Agent": `opencode/${InstallationVersion}`,
|
||||
"x-opencode-project": session.projectID,
|
||||
"x-opencode-session": session.id,
|
||||
"x-opencode-client": options?.client ?? "cli",
|
||||
"x-opencode-client": client,
|
||||
})
|
||||
|
||||
@@ -4,6 +4,7 @@ import { LLM, Message, SystemPart, type LLMRequest } from "@opencode-ai/ai"
|
||||
import { SessionError } from "@opencode-ai/schema/session-error"
|
||||
import { Context, Effect, Layer } from "effect"
|
||||
import { makeLocationNode } from "@opencode-ai/util/effect/app-node"
|
||||
import { Client } from "@opencode-ai/util/client"
|
||||
import { PluginHooks } from "../plugin/hooks"
|
||||
import { ToolRegistry } from "../tool/registry"
|
||||
import { SessionContext } from "./context"
|
||||
@@ -39,11 +40,12 @@ export interface Interface {
|
||||
/** Location-scoped outbound model-request preparation. */
|
||||
export class Service extends Context.Service<Service, Interface>()("@opencode/v2/SessionModelRequest") {}
|
||||
|
||||
export const layer = (options?: SessionModelHeaders.Options) => Layer.effect(
|
||||
export const layer = Layer.effect(
|
||||
Service,
|
||||
Effect.gen(function* () {
|
||||
const hooks = yield* PluginHooks.Service
|
||||
const registry = yield* ToolRegistry.Service
|
||||
const client = yield* Client.Name
|
||||
|
||||
const prepare = Effect.fn("SessionModelRequest.prepare")(function* (input: PrepareInput) {
|
||||
const session = input.context.session
|
||||
@@ -81,7 +83,7 @@ export const layer = (options?: SessionModelHeaders.Options) => Layer.effect(
|
||||
const request = LLM.request({
|
||||
model,
|
||||
http: {
|
||||
headers: SessionModelHeaders.make(session, options),
|
||||
headers: SessionModelHeaders.make(session, client),
|
||||
},
|
||||
providerOptions: { openai: { promptCacheKey } },
|
||||
system: contextEvent.system,
|
||||
@@ -112,8 +114,8 @@ export const layer = (options?: SessionModelHeaders.Options) => Layer.effect(
|
||||
}),
|
||||
)
|
||||
|
||||
export function configured(options?: SessionModelHeaders.Options) {
|
||||
return makeLocationNode({ service: Service, layer: layer(options), deps: [PluginHooks.node, ToolRegistry.node] })
|
||||
}
|
||||
|
||||
export const node = configured()
|
||||
export const node = makeLocationNode({
|
||||
service: Service,
|
||||
layer,
|
||||
deps: [PluginHooks.node, ToolRegistry.node, Client.node],
|
||||
})
|
||||
|
||||
@@ -6,6 +6,7 @@ import { AgentV2 } from "../agent"
|
||||
import { Database } from "../database/database"
|
||||
import { EventV2 } from "../event"
|
||||
import { makeLocationNode } from "@opencode-ai/util/effect/app-node"
|
||||
import { Client } from "@opencode-ai/util/client"
|
||||
import { llmClient } from "../effect/app-node-platform"
|
||||
import { SessionEvent } from "./event"
|
||||
import { SessionHistory } from "./history"
|
||||
@@ -17,7 +18,7 @@ import { SessionUsage } from "./usage"
|
||||
const MAX_LENGTH = 100
|
||||
|
||||
type Dependencies = {
|
||||
readonly headers?: SessionModelHeaders.Options
|
||||
readonly client: string
|
||||
readonly events: EventV2.Interface
|
||||
readonly llm: {
|
||||
readonly stream: (request: LLMRequest) => Stream.Stream<LLMEvent, LLMError>
|
||||
@@ -67,7 +68,7 @@ const make = (dependencies: Dependencies) => {
|
||||
.stream(
|
||||
LLM.request({
|
||||
model: resolved.model,
|
||||
http: { headers: SessionModelHeaders.make(session, dependencies.headers) },
|
||||
http: { headers: SessionModelHeaders.make(session, dependencies.client) },
|
||||
system: agent.system,
|
||||
messages: [Message.user(firstUser.text)],
|
||||
tools: [],
|
||||
@@ -103,7 +104,7 @@ const make = (dependencies: Dependencies) => {
|
||||
return { generateForFirstPrompt }
|
||||
}
|
||||
|
||||
export const layer = (options?: SessionModelHeaders.Options) => Layer.effect(
|
||||
export const layer = Layer.effect(
|
||||
Service,
|
||||
Effect.gen(function* () {
|
||||
const events = yield* EventV2.Service
|
||||
@@ -111,19 +112,16 @@ export const layer = (options?: SessionModelHeaders.Options) => Layer.effect(
|
||||
const agents = yield* AgentV2.Service
|
||||
const models = yield* SessionRunnerModel.Service
|
||||
const database = yield* Database.Service
|
||||
const title = make({ events, llm, agents, models, headers: options })
|
||||
const client = yield* Client.Name
|
||||
const title = make({ events, llm, agents, models, client })
|
||||
return Service.of({
|
||||
generateForFirstPrompt: (session) => title.generateForFirstPrompt(database.db, session),
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
||||
export function configured(options?: SessionModelHeaders.Options) {
|
||||
return makeLocationNode({
|
||||
service: Service,
|
||||
layer: layer(options),
|
||||
deps: [EventV2.node, llmClient, AgentV2.node, SessionRunnerModel.node, Database.node],
|
||||
})
|
||||
}
|
||||
|
||||
export const node = configured()
|
||||
export const node = makeLocationNode({
|
||||
service: Service,
|
||||
layer,
|
||||
deps: [EventV2.node, llmClient, AgentV2.node, SessionRunnerModel.node, Database.node, Client.node],
|
||||
})
|
||||
|
||||
@@ -56,7 +56,7 @@ const locations = Layer.effect(
|
||||
() =>
|
||||
// The test only needs the compaction location service used by SessionV2.compact.
|
||||
// oxlint-disable-next-line typescript-eslint/no-unsafe-type-assertion
|
||||
SessionCompaction.layer().pipe(
|
||||
SessionCompaction.layer.pipe(
|
||||
Layer.provide(client),
|
||||
Layer.provide(config),
|
||||
Layer.provide(models),
|
||||
|
||||
@@ -9,9 +9,11 @@ export const create = Effect.fn("OpenCode.create")(function* (options: ServerOpt
|
||||
const runtime = yield* Effect.acquireRelease(
|
||||
Effect.sync(() =>
|
||||
ManagedRuntime.make(
|
||||
createEmbeddedRoutes({ ...options, database: { path: ":memory:", ...options.database } }).pipe(
|
||||
Layer.provide(HttpServer.layerServices),
|
||||
),
|
||||
createEmbeddedRoutes({
|
||||
...options,
|
||||
client: options.client ?? "sdk",
|
||||
database: { path: ":memory:", ...options.database },
|
||||
}).pipe(Layer.provide(HttpServer.layerServices)),
|
||||
),
|
||||
),
|
||||
(runtime) => runtime.disposeEffect,
|
||||
|
||||
@@ -6,6 +6,7 @@ import { EventV2 } from "@opencode-ai/core/event"
|
||||
import { EventLogger } from "@opencode-ai/core/event-logger"
|
||||
import { FileSystemSearch } from "@opencode-ai/core/filesystem/search"
|
||||
import { Observability } from "@opencode-ai/util/observability"
|
||||
import { Client } from "@opencode-ai/util/client"
|
||||
import { Credential } from "@opencode-ai/core/credential"
|
||||
import { Config } from "@opencode-ai/core/config"
|
||||
import { CommandV2 } from "@opencode-ai/core/command"
|
||||
@@ -87,7 +88,8 @@ function makeRoutes<AuthError, AuthServices>(
|
||||
const pluginRuntimeCell = PluginRuntime.makeCell()
|
||||
const replacements: LayerNode.Replacements = [
|
||||
[Database.node, Database.configured(options.database)],
|
||||
[ModelsDev.node, ModelsDev.configured({ ...options.models, client: options.client })],
|
||||
[Client.node, Client.configured(options.client)],
|
||||
[ModelsDev.node, ModelsDev.configured(options.models)],
|
||||
[Watcher.node, Watcher.configured({ enabled: options.fs?.filewatcher })],
|
||||
[FileSystemSearch.node, FileSystemSearch.configured({ fff: options.fs?.fff })],
|
||||
[Global.node, Global.layerWith(options.config?.directory ? { config: options.config.directory } : {})],
|
||||
@@ -103,10 +105,6 @@ function makeRoutes<AuthError, AuthServices>(
|
||||
[CommandV2.node, CommandV2.configured({ gitbash: options.windows?.gitbash })],
|
||||
[Pty.node, Pty.configured({ gitbash: options.windows?.gitbash })],
|
||||
[Shell.node, Shell.configured({ gitbash: options.windows?.gitbash })],
|
||||
[SessionCompaction.node, SessionCompaction.configured({ client: options.client })],
|
||||
[SessionGenerateNode.node, SessionGenerateNode.configured({ client: options.client })],
|
||||
[SessionModelRequest.node, SessionModelRequest.configured({ client: options.client })],
|
||||
[SessionTitle.node, SessionTitle.configured({ client: options.client })],
|
||||
[PluginRuntime.node, PluginRuntime.layerWithCell(pluginRuntimeCell)],
|
||||
[PluginRuntime.providerNode, PluginRuntime.providerNodeWithCell(pluginRuntimeCell)],
|
||||
]
|
||||
@@ -135,7 +133,7 @@ function makeRoutes<AuthError, AuthServices>(
|
||||
Layer.provide(authorizationLayer),
|
||||
Layer.provide(schemaErrorLayer),
|
||||
Layer.provide(auth),
|
||||
Layer.provide(Observability.layer({ ...options.observability, client: options.client })),
|
||||
Layer.provide(Observability.layer(options.observability).pipe(Layer.provide(Client.layer(options.client)))),
|
||||
HttpRouter.provideRequest(requestServices),
|
||||
Layer.provideMerge(services),
|
||||
Layer.provideMerge(HttpRouter.layer),
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
export * as Client from "./client.js"
|
||||
|
||||
import { Context, Layer } from "effect"
|
||||
import { makeGlobalNode } from "./effect/app-node.js"
|
||||
|
||||
export const Name = Context.Reference<string>("@opencode/Client/Name", {
|
||||
defaultValue: () => "cli",
|
||||
})
|
||||
|
||||
export const layer = (name = "cli") => Layer.succeed(Name, name)
|
||||
|
||||
export const configured = (name?: string) => makeGlobalNode({ service: Name, layer: layer(name), deps: [] })
|
||||
|
||||
export const node = configured()
|
||||
@@ -7,11 +7,11 @@ import { FetchHttpClient } from "effect/unstable/http"
|
||||
import { OtlpSerialization } from "effect/unstable/observability"
|
||||
import { Logging } from "./observability/logging.js"
|
||||
import { Otlp } from "./observability/otlp.js"
|
||||
import { Client } from "./client.js"
|
||||
|
||||
export const Options = Schema.Struct({
|
||||
endpoint: Schema.optional(Schema.String),
|
||||
headers: Schema.optional(Schema.String),
|
||||
client: Schema.optional(Schema.String),
|
||||
})
|
||||
export type Options = typeof Options.Type
|
||||
|
||||
@@ -19,7 +19,6 @@ export function layer(
|
||||
options: Options = {
|
||||
endpoint: process.env.OTEL_EXPORTER_OTLP_ENDPOINT,
|
||||
headers: process.env.OTEL_EXPORTER_OTLP_HEADERS,
|
||||
client: process.env.OPENCODE_CLIENT ?? "cli",
|
||||
},
|
||||
) {
|
||||
const local = Logger.layer(Logging.loggers(), { mergeWithExisting: false }).pipe(
|
||||
@@ -29,14 +28,17 @@ export function layer(
|
||||
)
|
||||
return Layer.unwrap(
|
||||
Effect.gen(function* () {
|
||||
const logs = Logger.layer([...Logging.loggers(), ...Otlp.loggers(options)], { mergeWithExisting: false }).pipe(
|
||||
const client = yield* Client.Name
|
||||
const logs = Logger.layer([...Logging.loggers(), ...Otlp.loggers(options, client)], {
|
||||
mergeWithExisting: false,
|
||||
}).pipe(
|
||||
Layer.provide(NodeFileSystem.layer),
|
||||
Layer.provide(OtlpSerialization.layerJson),
|
||||
Layer.provide(FetchHttpClient.layer),
|
||||
Layer.orDie,
|
||||
Layer.merge(Layer.succeed(References.MinimumLogLevel, Logging.minimumLogLevel())),
|
||||
)
|
||||
return Layer.merge(logs, yield* Effect.promise(() => Otlp.tracingLayer(options)))
|
||||
return Layer.merge(logs, yield* Effect.promise(() => Otlp.tracingLayer(options, client)))
|
||||
}),
|
||||
).pipe(Layer.catchCause(() => local))
|
||||
}
|
||||
|
||||
@@ -6,19 +6,18 @@ import { runID } from "./shared.js"
|
||||
export interface Options {
|
||||
readonly endpoint?: string
|
||||
readonly headers?: string
|
||||
readonly client?: string
|
||||
}
|
||||
|
||||
function parseHeaders(value?: string) {
|
||||
return value
|
||||
? value.split(",").reduce(
|
||||
(acc, entry) => {
|
||||
const [key, ...value] = entry.split("=")
|
||||
acc[key] = value.join("=")
|
||||
return acc
|
||||
},
|
||||
{} as Record<string, string>,
|
||||
)
|
||||
(acc, entry) => {
|
||||
const [key, ...value] = entry.split("=")
|
||||
acc[key] = value.join("=")
|
||||
return acc
|
||||
},
|
||||
{} as Record<string, string>,
|
||||
)
|
||||
: undefined
|
||||
}
|
||||
|
||||
@@ -38,7 +37,11 @@ function resourceAttributes() {
|
||||
}
|
||||
}
|
||||
|
||||
export function resource(client = "cli"): { serviceName: string; serviceVersion: string; attributes: Record<string, string> } {
|
||||
export function resource(client = "cli"): {
|
||||
serviceName: string
|
||||
serviceVersion: string
|
||||
attributes: Record<string, string>
|
||||
} {
|
||||
return {
|
||||
serviceName: "opencode",
|
||||
serviceVersion: InstallationVersion,
|
||||
@@ -52,18 +55,18 @@ export function resource(client = "cli"): { serviceName: string; serviceVersion:
|
||||
}
|
||||
}
|
||||
|
||||
export function loggers(options?: Options) {
|
||||
export function loggers(options: Options | undefined, client: string) {
|
||||
if (!options?.endpoint) return []
|
||||
return [
|
||||
OtlpLogger.make({
|
||||
url: `${options.endpoint}/v1/logs`,
|
||||
resource: resource(options.client),
|
||||
resource: resource(client),
|
||||
headers: parseHeaders(options.headers),
|
||||
}),
|
||||
]
|
||||
}
|
||||
|
||||
export async function tracingLayer(options?: Options) {
|
||||
export async function tracingLayer(options: Options | undefined, client: string) {
|
||||
if (!options?.endpoint) return Layer.empty
|
||||
const NodeSdk = await import("@effect/opentelemetry/NodeSdk")
|
||||
const OTLP = await import("@opentelemetry/exporter-trace-otlp-http")
|
||||
@@ -77,7 +80,7 @@ export async function tracingLayer(options?: Options) {
|
||||
context.setGlobalContextManager(manager)
|
||||
|
||||
return NodeSdk.layer(() => ({
|
||||
resource: resource(options.client),
|
||||
resource: resource(client),
|
||||
spanProcessor: new SdkBase.BatchSpanProcessor(
|
||||
new OTLP.OTLPTraceExporter({
|
||||
url: `${options.endpoint}/v1/traces`,
|
||||
|
||||
Reference in New Issue
Block a user