diff --git a/packages/core/src/account/sql.ts b/packages/core/src/account/sql.ts index 4f45651d78e..f6442d67dd6 100644 --- a/packages/core/src/account/sql.ts +++ b/packages/core/src/account/sql.ts @@ -1,7 +1,7 @@ import { sqliteTable, text, integer, primaryKey } from "drizzle-orm/sqlite-core" import { AccountV2 } from "../account" -import { Timestamps } from "../database/schema.sql" +import { Timestamps } from "../database/schema.sql.js" export const AccountTable = sqliteTable("account", { id: text().$type().primaryKey(), diff --git a/packages/core/src/credential/sql.ts b/packages/core/src/credential/sql.ts index a849092ea05..c1fa19af579 100644 --- a/packages/core/src/credential/sql.ts +++ b/packages/core/src/credential/sql.ts @@ -1,5 +1,5 @@ import { integer, sqliteTable, text } from "drizzle-orm/sqlite-core" -import { Timestamps } from "../database/schema.sql" +import { Timestamps } from "../database/schema.sql.js" import type { IntegrationSchema } from "../integration/schema" import type { Credential } from "../credential" diff --git a/packages/core/src/npm-config.ts b/packages/core/src/npm-config.ts index 896bb84872c..79dd4f2e7b4 100644 --- a/packages/core/src/npm-config.ts +++ b/packages/core/src/npm-config.ts @@ -7,11 +7,10 @@ import Config from "@npmcli/config" import { definitions, flatten, nerfDarts, shorthands } from "@npmcli/config/lib/definitions/index.js" import { Effect } from "effect" -const npmPath = fileURLToPath(new URL("..", import.meta.url)) - export const load = (dir: string) => Effect.tryPromise({ try: async () => { + const npmPath = fileURLToPath(new URL("..", import.meta.url)) const config = new Config({ npmPath, cwd: dir, diff --git a/packages/core/src/observability.ts b/packages/core/src/observability.ts index faffb273333..3157ffa2923 100644 --- a/packages/core/src/observability.ts +++ b/packages/core/src/observability.ts @@ -9,13 +9,14 @@ import { Otlp } from "./observability/otlp" export const layer = Layer.unwrap( Effect.gen(function* () { - const logs = Logger.layer([...Logging.loggers(), ...Otlp.loggers()], { mergeWithExisting: false }).pipe( + const runID = crypto.randomUUID().slice(0, 8) + const logs = Logger.layer([...Logging.loggers(runID), ...Otlp.loggers(runID)], { 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)) + return Layer.merge(logs, yield* Effect.promise(() => Otlp.tracingLayer(runID))) }), ) diff --git a/packages/core/src/observability/logging.ts b/packages/core/src/observability/logging.ts index 0047d8d5e3f..d83596a4072 100644 --- a/packages/core/src/observability/logging.ts +++ b/packages/core/src/observability/logging.ts @@ -1,9 +1,8 @@ import { Formatter, Logger, type LogLevel } from "effect" import path from "path" import { Global } from "../global" -import { runID } from "./shared" -function formatter(id: string = runID) { +function formatter(id: string) { return Logger.map(Logger.formatStructured, (output) => { const messages = Array.isArray(output.message) ? output.message : [output.message] return [ @@ -46,12 +45,13 @@ function format(input: unknown) { return /^[^\s="\\]+$/.test(value) ? value : JSON.stringify(value) } -export function fileLogger(file = path.join(Global.Path.log, "opencode.log"), id: string = runID) { +export function fileLogger(id: string, file = path.join(Global.Path.log, "opencode.log")) { // Do not set batchWindow to 0; it causes high idle CPU usage. return Logger.toFile(formatter(id), file, { flag: "a" }) } -const stderrLogger = Logger.make((options) => process.stderr.write(formatter().log(options) + "\n")) +const stderrLogger = (id: string) => + Logger.make((options) => process.stderr.write(formatter(id).log(options) + "\n")) export function minimumLogLevel() { const value = process.env.OPENCODE_LOG_LEVEL?.toUpperCase() @@ -64,8 +64,10 @@ export function minimumLogLevel() { return value && value in levels ? levels[value as keyof typeof levels] : levels.INFO } -export function loggers() { - return process.env.OPENCODE_PRINT_LOGS === "1" ? [fileLogger(), stderrLogger] : [fileLogger()] +export function loggers(runID: string) { + return process.env.OPENCODE_PRINT_LOGS === "1" + ? [fileLogger(runID), stderrLogger(runID)] + : [fileLogger(runID)] } export * as Logging from "./logging" diff --git a/packages/core/src/observability/otlp.ts b/packages/core/src/observability/otlp.ts index dd99ebc1436..0cb5b4c032b 100644 --- a/packages/core/src/observability/otlp.ts +++ b/packages/core/src/observability/otlp.ts @@ -2,7 +2,6 @@ import { Layer } from "effect" import { OtlpLogger } from "effect/unstable/observability" import { Flag } from "../flag/flag" import { InstallationChannel, InstallationVersion } from "../installation/version" -import { runID } from "./shared" const endpoint = Flag.OTEL_EXPORTER_OTLP_ENDPOINT @@ -33,7 +32,11 @@ function resourceAttributes() { } } -export function resource(): { serviceName: string; serviceVersion: string; attributes: Record } { +export function resource(runID: string): { + serviceName: string + serviceVersion: string + attributes: Record +} { return { serviceName: "opencode", serviceVersion: InstallationVersion, @@ -47,12 +50,12 @@ export function resource(): { serviceName: string; serviceVersion: string; attri } } -export function loggers() { +export function loggers(runID: string) { if (!endpoint) return [] - return [OtlpLogger.make({ url: `${endpoint}/v1/logs`, resource: resource(), headers })] + return [OtlpLogger.make({ url: `${endpoint}/v1/logs`, resource: resource(runID), headers })] } -export async function tracingLayer() { +export async function tracingLayer(runID: string) { if (!endpoint) return Layer.empty const NodeSdk = await import("@effect/opentelemetry/NodeSdk") const OTLP = await import("@opentelemetry/exporter-trace-otlp-http") @@ -66,7 +69,7 @@ export async function tracingLayer() { context.setGlobalContextManager(manager) return NodeSdk.layer(() => ({ - resource: resource(), + resource: resource(runID), spanProcessor: new SdkBase.BatchSpanProcessor( new OTLP.OTLPTraceExporter({ url: `${endpoint}/v1/traces`, diff --git a/packages/core/src/observability/shared.ts b/packages/core/src/observability/shared.ts deleted file mode 100644 index 76393aacce5..00000000000 --- a/packages/core/src/observability/shared.ts +++ /dev/null @@ -1 +0,0 @@ -export const runID = crypto.randomUUID().slice(0, 8) diff --git a/packages/core/src/permission/sql.ts b/packages/core/src/permission/sql.ts index c395555d795..8ae812f0e72 100644 --- a/packages/core/src/permission/sql.ts +++ b/packages/core/src/permission/sql.ts @@ -1,5 +1,5 @@ import { sqliteTable, text, uniqueIndex } from "drizzle-orm/sqlite-core" -import { Timestamps } from "../database/schema.sql" +import { Timestamps } from "../database/schema.sql.js" import { ProjectV2 } from "../project" import { ProjectTable } from "../project/sql" import type { PermissionSaved } from "./saved" diff --git a/packages/core/src/plugin/boot.ts b/packages/core/src/plugin/boot.ts index dac25a2489c..ad3ef43107c 100644 --- a/packages/core/src/plugin/boot.ts +++ b/packages/core/src/plugin/boot.ts @@ -20,7 +20,7 @@ import { Npm } from "../npm" import { PluginV2 } from "../plugin" import { AgentPlugin } from "./agent" import { CommandPlugin } from "./command" -import { SkillPlugin } from "./skill" +// import { SkillPlugin } from "./skill" import { ConfigProviderPlugin } from "../config/plugin/provider" import { EnvPlugin } from "./env" import { ModelsDevPlugin } from "./models-dev" @@ -102,7 +102,7 @@ export const layer = Layer.effect( yield* add(EnvPlugin) yield* add(AgentPlugin.Plugin) yield* add(CommandPlugin.Plugin) - yield* add(SkillPlugin.Plugin) + // yield* add(SkillPlugin.Plugin) for (const item of ProviderPlugins) { yield* add(item) } diff --git a/packages/core/src/plugin/provider/amazon-bedrock.ts b/packages/core/src/plugin/provider/amazon-bedrock.ts index 9c7fd65665a..1c7a33e40f9 100644 --- a/packages/core/src/plugin/provider/amazon-bedrock.ts +++ b/packages/core/src/plugin/provider/amazon-bedrock.ts @@ -95,8 +95,8 @@ export const AmazonBedrockPlugin = PluginV2.define({ if (!bearerToken && options.credentialProvider === undefined) { // Do not gate SDK creation on explicit AWS env vars. The default chain // also handles ~/.aws/credentials, SSO, process creds, and instance roles. - const { fromNodeProviderChain } = yield* Effect.promise(() => import("@aws-sdk/credential-providers")) - options.credentialProvider = fromNodeProviderChain(profile ? { profile } : {}) + // const { fromNodeProviderChain } = yield* Effect.promise(() => import("@aws-sdk/credential-providers")) + // options.credentialProvider = fromNodeProviderChain(profile ? { profile } : {}) } if (evt.package === "@ai-sdk/amazon-bedrock/mantle") { diff --git a/packages/core/src/project/copy.ts b/packages/core/src/project/copy.ts index de2beda9816..8b4ddb495aa 100644 --- a/packages/core/src/project/copy.ts +++ b/packages/core/src/project/copy.ts @@ -271,9 +271,9 @@ export const layer = Layer.effect( ) export const defaultLayer = layer.pipe( - Layer.provide(Database.defaultLayer), Layer.provide(FSUtil.defaultLayer), Layer.provide(Git.defaultLayer), Layer.provide(EventV2.defaultLayer), + Layer.provide(Database.defaultLayer), ) export const node = LayerNode.make(layer, [FSUtil.node, Git.node, EventV2.node, Database.node]) diff --git a/packages/core/src/project/sql.ts b/packages/core/src/project/sql.ts index c3954b771ea..290aa1908cd 100644 --- a/packages/core/src/project/sql.ts +++ b/packages/core/src/project/sql.ts @@ -1,6 +1,6 @@ import { sqliteTable, text, integer, primaryKey } from "drizzle-orm/sqlite-core" import * as DatabasePath from "../database/path" -import { Timestamps } from "../database/schema.sql" +import { Timestamps } from "../database/schema.sql.js" import { ProjectV2 } from "../project" export const ProjectTable = sqliteTable("project", { diff --git a/packages/core/src/session/projector.ts b/packages/core/src/session/projector.ts index caf63de78ac..8063ea2dbff 100644 --- a/packages/core/src/session/projector.ts +++ b/packages/core/src/session/projector.ts @@ -7,7 +7,7 @@ import { EventV2 } from "../event" import { LayerNode } from "../effect/layer-node" import { SessionEvent } from "./event" import { SessionV1 } from "../v1/session" -import { WorkspaceTable } from "../control-plane/workspace.sql" +import { WorkspaceTable } from "../control-plane/workspace.sql.js" import { SessionMessage } from "./message" import { SessionMessageUpdater } from "./message-updater" import { SessionInput } from "./input" diff --git a/packages/core/src/session/sql.ts b/packages/core/src/session/sql.ts index ca3d8e1b530..df746f5998e 100644 --- a/packages/core/src/session/sql.ts +++ b/packages/core/src/session/sql.ts @@ -10,7 +10,7 @@ import { ProjectV2 } from "../project" import type { SessionSchema } from "./schema" import type { MessageID, PartID, SessionV1 } from "../v1/session" import { WorkspaceV2 } from "../workspace" -import { Timestamps } from "../database/schema.sql" +import { Timestamps } from "../database/schema.sql.js" import type { SystemContext } from "../system-context/index" import { AgentV2 } from "../agent" diff --git a/packages/core/src/share/sql.ts b/packages/core/src/share/sql.ts index a7a08d0c025..6cd150725db 100644 --- a/packages/core/src/share/sql.ts +++ b/packages/core/src/share/sql.ts @@ -1,6 +1,6 @@ import { sqliteTable, text } from "drizzle-orm/sqlite-core" import { SessionTable } from "../session/sql" -import { Timestamps } from "../database/schema.sql" +import { Timestamps } from "../database/schema.sql.js" export const SessionShareTable = sqliteTable("session_share", { session_id: text() diff --git a/packages/core/test/effect/observability.test.ts b/packages/core/test/effect/observability.test.ts index 4758563f287..56d6971ec1b 100644 --- a/packages/core/test/effect/observability.test.ts +++ b/packages/core/test/effect/observability.test.ts @@ -19,11 +19,13 @@ afterEach(() => { }) describe("resource", () => { + const runID = "1234abcd" + test("parses and decodes OTEL resource attributes", () => { process.env.OTEL_RESOURCE_ATTRIBUTES = "service.namespace=anomalyco,team=platform%2Cobservability,label=hello%3Dworld,key%2Fname=value%20here" - expect(resource().attributes).toMatchObject({ + expect(resource(runID).attributes).toMatchObject({ "service.namespace": "anomalyco", team: "platform,observability", label: "hello=world", @@ -34,8 +36,8 @@ describe("resource", () => { test("drops OTEL resource attributes when any entry is invalid", () => { process.env.OTEL_RESOURCE_ATTRIBUTES = "service.namespace=anomalyco,broken" - expect(resource().attributes["service.namespace"]).toBeUndefined() - expect(resource().attributes["opencode.client"]).toBeDefined() + expect(resource(runID).attributes["service.namespace"]).toBeUndefined() + expect(resource(runID).attributes["opencode.client"]).toBeDefined() }) test("keeps built-in attributes when env values conflict", () => { @@ -43,12 +45,12 @@ describe("resource", () => { process.env.OTEL_RESOURCE_ATTRIBUTES = "opencode.client=web,service.instance.id=override,service.namespace=anomalyco" - expect(resource().attributes).toMatchObject({ + expect(resource(runID).attributes).toMatchObject({ "opencode.client": "cli", "service.namespace": "anomalyco", }) - expect(resource().attributes["service.instance.id"]).not.toBe("override") - expect(resource().attributes["opencode.run"]).toMatch(/^[0-9a-f]{8}$/) + expect(resource(runID).attributes["service.instance.id"]).toBe(runID) + expect(resource(runID).attributes["opencode.run"]).toBe(runID) }) }) @@ -65,7 +67,7 @@ test("file logger appends concurrent runs with a run on every line", async () => Array.from({ length: 50 }, (_, index) => index), (index) => Effect.logInfo(`entry-${index}`), ).pipe( - Effect.provide(Logger.layer([fileLogger(file, runID)]).pipe(Layer.provide(NodeFileSystem.layer), Layer.orDie)), + Effect.provide(Logger.layer([fileLogger(runID, file)]).pipe(Layer.provide(NodeFileSystem.layer), Layer.orDie)), Effect.scoped, ) @@ -94,7 +96,7 @@ test("file logger flattens nested objects", async () => { tags: ["api", "test"], }).pipe( Effect.annotateLogs({ session: { id: "session-1" } }), - Effect.provide(Logger.layer([fileLogger(file, "run-a")]).pipe(Layer.provide(NodeFileSystem.layer), Layer.orDie)), + Effect.provide(Logger.layer([fileLogger("run-a", file)]).pipe(Layer.provide(NodeFileSystem.layer), Layer.orDie)), Effect.scoped, Effect.runPromise, )