diff --git a/packages/core/src/effect/logger.ts b/packages/core/src/effect/logger.ts index 69f9631e06..95c2754c38 100644 --- a/packages/core/src/effect/logger.ts +++ b/packages/core/src/effect/logger.ts @@ -1,36 +1,94 @@ -import { Cause, Effect, Logger, References } from "effect" -import * as Log from "../util/log" +import { appendFileSync } from "fs" +import fs from "fs/promises" +import path from "path" +import { Cause, Effect, Layer, Logger, References, Schema } from "effect" +import * as Global from "../global" +import { ensureProcessMetadata } from "../util/opencode-process" type Fields = Record +type FieldInput = object + +export const Level = Schema.Literals(["DEBUG", "INFO", "WARN", "ERROR"]).annotate({ + identifier: "LogLevel", + description: "Log level", +}) +export type Level = Schema.Schema.Type + +const levelPriority: Record = { + DEBUG: 0, + INFO: 1, + WARN: 2, + ERROR: 3, +} const normalizeKey = (key: string) => (key === "sessionID" ? "session.id" : key) export interface Handle { - readonly debug: (msg?: unknown, extra?: Fields) => Effect.Effect - readonly info: (msg?: unknown, extra?: Fields) => Effect.Effect - readonly warn: (msg?: unknown, extra?: Fields) => Effect.Effect - readonly error: (msg?: unknown, extra?: Fields) => Effect.Effect - readonly with: (extra: Fields) => Handle + readonly debug: (msg?: unknown, extra?: FieldInput) => Effect.Effect + readonly info: (msg?: unknown, extra?: FieldInput) => Effect.Effect + readonly warn: (msg?: unknown, extra?: FieldInput) => Effect.Effect + readonly error: (msg?: unknown, extra?: FieldInput) => Effect.Effect + readonly tag: (key: string, value: string) => Handle + readonly with: (extra: FieldInput) => Handle + readonly clone: () => Handle + readonly time: (message: string, extra?: Fields) => { stop(): void; [Symbol.dispose](): void } } -const clean = (input?: Fields): Fields => +const clean = (input?: FieldInput): Fields => Object.fromEntries( Object.entries(input ?? {}) .filter((entry) => entry[1] !== undefined && entry[1] !== null) .map(([key, value]) => [normalizeKey(key), value]), ) -const text = (input: unknown): string => { - // oxlint-disable-next-line no-base-to-string - if (Array.isArray(input)) return input.map((item) => String(item)).join(" ") - // oxlint-disable-next-line no-base-to-string - return input === undefined ? "" : String(input) +const call = (run: (msg: string) => Effect.Effect, base: FieldInput, msg?: unknown, extra?: FieldInput) => { + const ann = clean({ ...base, ...extra }) + const fx = run(stringifyMessage(msg)) + return Object.keys(ann).length ? Effect.annotateLogs(fx, ann) : fx } -const call = (run: (msg?: unknown) => Effect.Effect, base: Fields, msg?: unknown, extra?: Fields) => { - const ann = clean({ ...base, ...extra }) - const fx = run(msg) - return Object.keys(ann).length ? Effect.annotateLogs(fx, ann) : fx +export function file() { + if (disabled(process.env.OPENCODE_LOG_FILE)) return "" + return process.env.OPENCODE_LOG_FILE || path.join(Global.Path.log, "log.jsonl") +} + +function shouldLog(input: Level): boolean { + return levelPriority[input] >= levelPriority[parseLevel(process.env.OPENCODE_LOG_LEVEL) ?? "INFO"] +} + +function write(input: { json: string; pretty: string }) { + const target = file() + if (target) { + try { + appendFileSync(target, input.json) + } catch {} + } + if (truthy(process.env.OPENCODE_PRINT_LOGS)) process.stderr.write(input.pretty) +} + +function build(inputLevel: Level, ts: Date, message: unknown, fields: Fields): { json: string; pretty: string } { + const metadata = ensureProcessMetadata("main") + const service = typeof fields.service === "string" ? fields.service : undefined + if (service) delete fields.service + const text = stringifyMessage(message) + const record = { + ts: ts.toISOString(), + level: inputLevel, + message: text, + run_id: metadata.runID, + process_role: metadata.processRole, + pid: process.pid, + service, + fields: Object.fromEntries(Object.entries(fields).map(([key, value]) => [key, normalize(value)])), + } + const prefix = Object.entries({ service, ...record.fields }) + .filter((entry) => entry[1] !== undefined && entry[1] !== null) + .map(([key, value]) => `${key}=${typeof value === "object" ? safeStringify(value) : value}`) + .join(" ") + return { + json: safeStringify(record) + "\n", + pretty: [inputLevel.padEnd(5), ts.toISOString().split(".")[0], prefix, text].filter(Boolean).join(" ") + "\n", + } } export const logger = Logger.make((opts) => { @@ -43,31 +101,97 @@ export const logger = Logger.make((opts) => { extra.cause = Cause.pretty(opts.cause) } - const svc = typeof extra.service === "string" ? extra.service : undefined - if (svc) delete extra.service - const log = svc ? Log.create({ service: svc }) : Log.Default - const msg = text(opts.message) - switch (opts.logLevel) { case "Trace": case "Debug": - return log.debug(msg, extra) + if (shouldLog("DEBUG")) write(build("DEBUG", opts.date, opts.message, extra)) + return case "Warn": - return log.warn(msg, extra) + if (shouldLog("WARN")) write(build("WARN", opts.date, opts.message, extra)) + return case "Error": case "Fatal": - return log.error(msg, extra) + if (shouldLog("ERROR")) write(build("ERROR", opts.date, opts.message, extra)) + return default: - return log.info(msg, extra) + if (shouldLog("INFO")) write(build("INFO", opts.date, opts.message, extra)) } }) -export const layer = Logger.layer([logger], { mergeWithExisting: false }) +export const layer = Logger.layer([logger], { mergeWithExisting: false }).pipe( + Layer.tap(() => + Effect.promise(async () => { + const target = file() + if (target) await fs.mkdir(path.dirname(target), { recursive: true }) + }), + ), +) -export const create = (base: Fields = {}): Handle => ({ +export const create = (base: FieldInput = {}): Handle => ({ debug: (msg, extra) => call((item) => Effect.logDebug(item), base, msg, extra), info: (msg, extra) => call((item) => Effect.logInfo(item), base, msg, extra), warn: (msg, extra) => call((item) => Effect.logWarning(item), base, msg, extra), error: (msg, extra) => call((item) => Effect.logError(item), base, msg, extra), + tag: (key, value) => create({ ...base, [key]: value }), with: (extra) => create({ ...base, ...extra }), + clone: () => create({ ...base }), + time: () => ({ + stop() {}, + [Symbol.dispose]() {}, + }), }) + +function truthy(value: string | undefined) { + return value?.toLowerCase() === "1" || value?.toLowerCase() === "true" +} + +function disabled(value: string | undefined) { + const lower = value?.toLowerCase() + return lower === "0" || lower === "false" || lower === "off" +} + +function parseLevel(value: string | undefined): Level | undefined { + if (value === "DEBUG" || value === "INFO" || value === "WARN" || value === "ERROR") return value + return undefined +} + +function formatError(error: Error, depth = 0): string { + const result = error.message + return error.cause instanceof Error && depth < 10 + ? result + " Caused by: " + formatError(error.cause, depth + 1) + : result +} + +function stringifyMessage(message: unknown): string { + if (message instanceof Error) return formatError(message) + if (message === undefined) return "" + if (typeof message === "string") return message + if (Array.isArray(message)) return message.map((item) => stringifyMessage(item)).join(" ") + if (typeof message === "object") return safeStringify(message) + return String(message) +} + +function normalize(value: unknown): unknown { + if (value instanceof Error) { + return { + name: value.name, + message: formatError(value), + stack: value.stack, + } + } + if (typeof value === "bigint") return value.toString() + return value +} + +function safeStringify(value: unknown) { + const seen = new WeakSet() + return JSON.stringify(value, (_, item) => { + if (typeof item === "bigint") return item.toString() + if (item instanceof Error) return normalize(item) + if (typeof item === "object" && item !== null) { + if (seen.has(item)) return "[Circular]" + seen.add(item) + } + return item + }) +} diff --git a/packages/core/src/util/log.ts b/packages/core/src/util/log.ts deleted file mode 100644 index 08dfdcaeac..0000000000 --- a/packages/core/src/util/log.ts +++ /dev/null @@ -1,241 +0,0 @@ -export * as Log from "./log" - -import path from "path" -import fs from "fs/promises" -import { appendFileSync } from "fs" -import * as Global from "../global" -import { Schema } from "effect" -import { ensureProcessMetadata } from "./opencode-process" - -export const Level = Schema.Literals(["DEBUG", "INFO", "WARN", "ERROR"]).annotate({ - identifier: "LogLevel", - description: "Log level", -}) -export type Level = Schema.Schema.Type - -const levelPriority: Record = { - DEBUG: 0, - INFO: 1, - WARN: 2, - ERROR: 3, -} -let level: Level = "INFO" - -function shouldLog(input: Level): boolean { - return levelPriority[input] >= levelPriority[level] -} - -export type Logger = { - debug(message?: any, extra?: Record): void - info(message?: any, extra?: Record): void - error(message?: any, extra?: Record): void - warn(message?: any, extra?: Record): void - tag(key: string, value: string): Logger - clone(): Logger - time( - message: string, - extra?: Record, - ): { - stop(): void - [Symbol.dispose](): void - } -} - -const loggers = new Map() - -export const Default = create({ service: "default" }) - -export interface Options { - print?: boolean - dev?: boolean - level?: Level - file?: string | false -} - -let logpath = "" -export function file() { - return logpath -} -type LogEntry = { - json: string - pretty: string -} -let write = (entry: LogEntry) => { - process.stderr.write(entry.pretty) -} - -export async function init(options: Options) { - level = options.level ?? parseLevel(process.env.OPENCODE_LOG_LEVEL) ?? level - const print = options.print ?? truthy(process.env.OPENCODE_PRINT_LOGS) - const configured = options.file ?? process.env.OPENCODE_LOG_FILE - logpath = configured === false || disabled(configured) ? "" : configured || path.join(Global.Path.log, "log.jsonl") - - if (logpath) await fs.mkdir(path.dirname(logpath), { recursive: true }) - - write = (entry) => { - if (logpath) { - try { - appendFileSync(logpath, entry.json) - } catch {} - } - if (print) process.stderr.write(entry.pretty) - } -} - -function formatError(error: Error, depth = 0): string { - const result = error.message - return error.cause instanceof Error && depth < 10 - ? result + " Caused by: " + formatError(error.cause, depth + 1) - : result -} - -let last = Date.now() -export function create(tags?: Record) { - tags = tags || {} - - const service = tags["service"] - if (service && typeof service === "string") { - const cached = loggers.get(service) - if (cached) { - return cached - } - } - - function build(inputLevel: Level, message: any, extra?: Record): LogEntry { - const ts = new Date() - const metadata = ensureProcessMetadata("main") - const fields = Object.fromEntries( - Object.entries({ - ...tags, - ...extra, - }) - .filter((entry) => entry[1] !== undefined && entry[1] !== null) - .map(([key, value]) => [key, normalize(value)]), - ) - const service = typeof fields.service === "string" ? fields.service : undefined - if (service) delete fields.service - const text = stringifyMessage(message) - const record = { - ts: ts.toISOString(), - level: inputLevel, - message: text, - run_id: metadata.runID, - process_role: metadata.processRole, - pid: process.pid, - service, - fields, - } - const diff = ts.getTime() - last - last = ts.getTime() - const prefix = Object.entries({ service, ...fields }) - .filter((entry) => entry[1] !== undefined && entry[1] !== null) - .map(([key, value]) => `${key}=${typeof value === "object" ? safeStringify(value) : value}`) - .join(" ") - return { - json: safeStringify(record) + "\n", - pretty: - [inputLevel.padEnd(5), ts.toISOString().split(".")[0], "+" + diff + "ms", prefix, text] - .filter(Boolean) - .join(" ") + "\n", - } - } - const result: Logger = { - debug(message?: any, extra?: Record) { - if (shouldLog("DEBUG")) { - write(build("DEBUG", message, extra)) - } - }, - info(message?: any, extra?: Record) { - if (shouldLog("INFO")) { - write(build("INFO", message, extra)) - } - }, - error(message?: any, extra?: Record) { - if (shouldLog("ERROR")) { - write(build("ERROR", message, extra)) - } - }, - warn(message?: any, extra?: Record) { - if (shouldLog("WARN")) { - write(build("WARN", message, extra)) - } - }, - tag(key: string, value: string) { - if (tags) tags[key] = value - return result - }, - clone() { - return create({ ...tags }) - }, - time(message: string, extra?: Record) { - const now = Date.now() - result.info(message, { status: "started", ...extra }) - function stop() { - result.info(message, { - status: "completed", - duration: Date.now() - now, - ...extra, - }) - } - return { - stop, - [Symbol.dispose]() { - stop() - }, - } - }, - } - - if (service && typeof service === "string") { - loggers.set(service, result) - } - - return result -} - -function truthy(value: string | undefined) { - return value?.toLowerCase() === "1" || value?.toLowerCase() === "true" -} - -function disabled(value: string | undefined) { - const lower = value?.toLowerCase() - return lower === "0" || lower === "false" || lower === "off" -} - -function parseLevel(value: string | undefined): Level | undefined { - if (value === "DEBUG" || value === "INFO" || value === "WARN" || value === "ERROR") return value - return undefined -} - -function stringifyMessage(message: any): string { - if (message instanceof Error) return formatError(message) - if (message === undefined) return "" - if (typeof message === "string") return message - if (typeof message === "object") return safeStringify(message) - return String(message) -} - -function normalize(value: any): any { - if (value instanceof Error) { - return { - name: value.name, - message: formatError(value), - stack: value.stack, - } - } - if (typeof value === "bigint") return value.toString() - return value -} - -function safeStringify(value: any) { - const seen = new WeakSet() - return JSON.stringify(value, (_, item) => { - if (typeof item === "bigint") return item.toString() - if (item instanceof Error) return normalize(item) - if (typeof item === "object" && item !== null) { - if (seen.has(item)) return "[Circular]" - seen.add(item) - } - return item - }) -} diff --git a/packages/desktop/src/main/env.d.ts b/packages/desktop/src/main/env.d.ts index 1d03be5055..f34489fd02 100644 --- a/packages/desktop/src/main/env.d.ts +++ b/packages/desktop/src/main/env.d.ts @@ -15,9 +15,6 @@ declare module "virtual:opencode-server" { export const get: typeof import("../../../opencode/dist/types/src/node").Config.get export type Info = import("../../../opencode/dist/types/src/node").Config.Info } - export namespace Log { - export const init: typeof import("../../../opencode/dist/types/src/node").Log.init - } export namespace Database { export const getPath: typeof import("../../../opencode/dist/types/src/node").Database.getPath export const Client: typeof import("../../../opencode/dist/types/src/node").Database.Client diff --git a/packages/desktop/src/main/sidecar.ts b/packages/desktop/src/main/sidecar.ts index e7d652b6e1..e1d2a04b59 100644 --- a/packages/desktop/src/main/sidecar.ts +++ b/packages/desktop/src/main/sidecar.ts @@ -57,8 +57,8 @@ async function start(command: StartCommand) { ensureLoopbackNoProxy() useSystemCertificates() useEnvProxy() - const { Database, JsonMigration, Log, Server } = await import("virtual:opencode-server") - await Log.init({ level: "WARN" }) + process.env.OPENCODE_LOG_LEVEL = "WARN" + const { Database, JsonMigration, Server } = await import("virtual:opencode-server") if (command.needsMigration) { await JsonMigration.run(drizzle({ client: Database.Client().$client }), { diff --git a/packages/opencode/src/acp-next/event.ts b/packages/opencode/src/acp-next/event.ts index 25aa5c08ef..ab42276dbd 100644 --- a/packages/opencode/src/acp-next/event.ts +++ b/packages/opencode/src/acp-next/event.ts @@ -1,5 +1,5 @@ import type { AgentSideConnection } from "@agentclientprotocol/sdk" -import * as Log from "@opencode-ai/core/util/log" +import * as EffectLogger from "@opencode-ai/core/effect/logger" import type { Event, EventMessagePartDelta, @@ -20,7 +20,7 @@ import { completedToolUpdate, } from "./tool" -const log = Log.create({ service: "acp-next-event" }) +const log = EffectLogger.create({ service: "acp-next-event" }) type Connection = Pick type GlobalEventEnvelope = { diff --git a/packages/opencode/src/acp-next/service.ts b/packages/opencode/src/acp-next/service.ts index be368da623..e31700c226 100644 --- a/packages/opencode/src/acp-next/service.ts +++ b/packages/opencode/src/acp-next/service.ts @@ -30,7 +30,7 @@ import { type SetSessionModeResponse, } from "@agentclientprotocol/sdk" import { InstallationVersion } from "@opencode-ai/core/installation/version" -import * as Log from "@opencode-ai/core/util/log" +import * as EffectLogger from "@opencode-ai/core/effect/logger" import type { Message, OpencodeClient, SessionMessageResponse } from "@opencode-ai/sdk/v2" import { Context, Effect, Layer, ManagedRuntime } from "effect" import * as ACPNextError from "./error" @@ -43,7 +43,7 @@ import { Provider } from "@/provider/provider" import type { Command } from "@/command" export const AuthMethodID = "opencode-login" -const log = Log.create({ service: "acp-next-service" }) +const log = EffectLogger.create({ service: "acp-next-service" }) export type Error = ACPNextError.Error diff --git a/packages/opencode/src/acp-next/usage.ts b/packages/opencode/src/acp-next/usage.ts index eabbb02af2..4a3fc5eb3e 100644 --- a/packages/opencode/src/acp-next/usage.ts +++ b/packages/opencode/src/acp-next/usage.ts @@ -1,5 +1,5 @@ import type { AgentSideConnection, Usage } from "@agentclientprotocol/sdk" -import * as Log from "@opencode-ai/core/util/log" +import * as EffectLogger from "@opencode-ai/core/effect/logger" import type { AssistantMessage as OpenCodeAssistantMessage, Message } from "@opencode-ai/sdk/v2" import { InstanceRef } from "@/effect/instance-ref" import { InstanceStore } from "@/project/instance-store" @@ -7,7 +7,7 @@ import { ModelID, ProviderID } from "@/provider/schema" import { Provider } from "@/provider/provider" import { Context, Effect, Layer, SynchronizedRef } from "effect" -const log = Log.create({ service: "acp-next-usage" }) +const log = EffectLogger.create({ service: "acp-next-usage" }) export type AssistantTokenCost = Pick diff --git a/packages/opencode/src/acp/agent.ts b/packages/opencode/src/acp/agent.ts index 8b74b9c9ba..b8290981c9 100644 --- a/packages/opencode/src/acp/agent.ts +++ b/packages/opencode/src/acp/agent.ts @@ -33,7 +33,7 @@ import { type Usage, } from "@agentclientprotocol/sdk" -import * as Log from "@opencode-ai/core/util/log" +import * as EffectLogger from "@opencode-ai/core/effect/logger" import { pathToFileURL } from "url" import { Filesystem } from "@/util/filesystem" import { Hash } from "@opencode-ai/core/util/hash" @@ -58,7 +58,7 @@ const decodeTodos = Schema.decodeUnknownResult(Schema.fromJsonString(Schema.Arra const DEFAULT_VARIANT_VALUE = "default" -const log = Log.create({ service: "acp-agent" }) +const log = EffectLogger.create({ service: "acp-agent" }) async function getContextLimit( sdk: OpencodeClient, diff --git a/packages/opencode/src/acp/session.ts b/packages/opencode/src/acp/session.ts index cc1ed0be30..60df81fcdf 100644 --- a/packages/opencode/src/acp/session.ts +++ b/packages/opencode/src/acp/session.ts @@ -1,9 +1,9 @@ import { RequestError, type McpServer } from "@agentclientprotocol/sdk" import type { ACPSessionState } from "./types" -import * as Log from "@opencode-ai/core/util/log" +import * as EffectLogger from "@opencode-ai/core/effect/logger" import type { OpencodeClient } from "@opencode-ai/sdk/v2" -const log = Log.create({ service: "acp-session-manager" }) +const log = EffectLogger.create({ service: "acp-session-manager" }) export class ACPSessionManager { private sessions = new Map() diff --git a/packages/opencode/src/bus/index.ts b/packages/opencode/src/bus/index.ts index 73ec18d73b..caf2e26876 100644 --- a/packages/opencode/src/bus/index.ts +++ b/packages/opencode/src/bus/index.ts @@ -1,6 +1,6 @@ import { Effect, Exit, Layer, PubSub, Scope, Context, Stream, Schema } from "effect" import { EffectBridge } from "@/effect/bridge" -import * as Log from "@opencode-ai/core/util/log" +import * as EffectLogger from "@opencode-ai/core/effect/logger" import { BusEvent } from "./bus-event" import { GlobalBus } from "./global" import { InstanceState } from "@/effect/instance-state" @@ -10,7 +10,7 @@ import { Identifier } from "@/id/id" import type { InstanceContext } from "@/project/instance-context" import { InstanceRef } from "@/effect/instance-ref" -const log = Log.create({ service: "bus" }) +const log = EffectLogger.create({ service: "bus" }) type BusProperties> = Schema.Schema.Type diff --git a/packages/opencode/src/cli/cmd/acp.ts b/packages/opencode/src/cli/cmd/acp.ts index b113a278f9..558c27e4f0 100644 --- a/packages/opencode/src/cli/cmd/acp.ts +++ b/packages/opencode/src/cli/cmd/acp.ts @@ -1,4 +1,4 @@ -import * as Log from "@opencode-ai/core/util/log" +import * as EffectLogger from "@opencode-ai/core/effect/logger" import { Effect } from "effect" import { effectCmd } from "../effect-cmd" import { AgentSideConnection, ndJsonStream } from "@agentclientprotocol/sdk" @@ -10,7 +10,7 @@ import { createOpencodeClient } from "@opencode-ai/sdk/v2" import { withNetworkOptions, resolveNetworkOptions } from "../network" import { RuntimeFlags } from "@/effect/runtime-flags" -const log = Log.create({ service: "acp-command" }) +const log = EffectLogger.create({ service: "acp-command" }) export const AcpCommand = effectCmd({ command: "acp", diff --git a/packages/opencode/src/cli/cmd/debug/lsp.ts b/packages/opencode/src/cli/cmd/debug/lsp.ts index b40b423181..0c74dce0cd 100644 --- a/packages/opencode/src/cli/cmd/debug/lsp.ts +++ b/packages/opencode/src/cli/cmd/debug/lsp.ts @@ -2,7 +2,6 @@ import { LSP } from "@/lsp/lsp" import { Effect } from "effect" import { effectCmd } from "../../effect-cmd" import { cmd } from "../cmd" -import * as Log from "@opencode-ai/core/util/log" import { EOL } from "os" export const LSPCommand = cmd({ @@ -33,7 +32,6 @@ export const SymbolsCommand = effectCmd({ describe: "search workspace symbols", builder: (yargs) => yargs.positional("query", { type: "string", demandOption: true }), handler: Effect.fn("Cli.debug.lsp.symbols")(function* (args) { - using _ = Log.Default.time("symbols") const results = yield* LSP.Service.use((lsp) => lsp.workspaceSymbol(args.query)) process.stdout.write(JSON.stringify(results, null, 2) + EOL) }), @@ -44,7 +42,6 @@ export const DocumentSymbolsCommand = effectCmd({ describe: "get symbols from a document", builder: (yargs) => yargs.positional("uri", { type: "string", demandOption: true }), handler: Effect.fn("Cli.debug.lsp.documentSymbols")(function* (args) { - using _ = Log.Default.time("document-symbols") const results = yield* LSP.Service.use((lsp) => lsp.documentSymbol(args.uri)) process.stdout.write(JSON.stringify(results, null, 2) + EOL) }), diff --git a/packages/opencode/src/cli/cmd/debug/scrap.ts b/packages/opencode/src/cli/cmd/debug/scrap.ts index 2a127e5dbd..fbf177a6f2 100644 --- a/packages/opencode/src/cli/cmd/debug/scrap.ts +++ b/packages/opencode/src/cli/cmd/debug/scrap.ts @@ -1,6 +1,5 @@ import { EOL } from "os" import { Project } from "@/project/project" -import * as Log from "@opencode-ai/core/util/log" import { cmd } from "../cmd" export const ScrapCommand = cmd({ @@ -8,9 +7,7 @@ export const ScrapCommand = cmd({ describe: "list all known projects", builder: (yargs) => yargs, async handler() { - const timer = Log.Default.time("scrap") const list = await Project.list() process.stdout.write(JSON.stringify(list, null, 2) + EOL) - timer.stop() }, }) diff --git a/packages/opencode/src/cli/cmd/tui/attention.ts b/packages/opencode/src/cli/cmd/tui/attention.ts index f6480961dc..00bd054020 100644 --- a/packages/opencode/src/cli/cmd/tui/attention.ts +++ b/packages/opencode/src/cli/cmd/tui/attention.ts @@ -19,7 +19,7 @@ import permissionSoundPath from "@opencode-ai/ui/audio/staplebops-06.mp3" with { import errorSoundPath from "@opencode-ai/ui/audio/nope-03.mp3" with { type: "file" } import doneSoundPath from "@opencode-ai/ui/audio/bip-bop-01.mp3" with { type: "file" } import subagentDoneSoundPath from "@opencode-ai/ui/audio/yup-01.mp3" with { type: "file" } -import * as Log from "@opencode-ai/core/util/log" +import * as EffectLogger from "@opencode-ai/core/effect/logger" type FocusState = "unknown" | "focused" | "blurred" @@ -38,7 +38,7 @@ type TuiAttentionHost = TuiAttention & { dispose(): void } -const log = Log.create({ service: "tui.attention" }) +const log = EffectLogger.create({ service: "tui.attention" }) const DEFAULT_TITLE = "opencode" const DEFAULT_PACK_ID = "opencode.default" diff --git a/packages/opencode/src/cli/cmd/tui/config/tui-migrate.ts b/packages/opencode/src/cli/cmd/tui/config/tui-migrate.ts index b4dc02d3b8..4303e13901 100644 --- a/packages/opencode/src/cli/cmd/tui/config/tui-migrate.ts +++ b/packages/opencode/src/cli/cmd/tui/config/tui-migrate.ts @@ -6,10 +6,10 @@ import { DiffStyle, ScrollAcceleration, ScrollSpeed } from "./tui-schema" import { Flag } from "@opencode-ai/core/flag/flag" import { Global } from "@opencode-ai/core/global" import { Filesystem } from "@/util/filesystem" -import * as Log from "@opencode-ai/core/util/log" +import * as EffectLogger from "@opencode-ai/core/effect/logger" import * as ConfigPaths from "@/config/paths" -const log = Log.create({ service: "tui.migrate" }) +const log = EffectLogger.create({ service: "tui.migrate" }) const TUI_SCHEMA_URL = "https://opencode.ai/tui.json" diff --git a/packages/opencode/src/cli/cmd/tui/config/tui.ts b/packages/opencode/src/cli/cmd/tui/config/tui.ts index 0d4be41dfc..95826d0d0f 100644 --- a/packages/opencode/src/cli/cmd/tui/config/tui.ts +++ b/packages/opencode/src/cli/cmd/tui/config/tui.ts @@ -18,14 +18,14 @@ import { TuiKeybind } from "./keybind" import { InstallationLocal, InstallationVersion } from "@opencode-ai/core/installation/version" import { makeRuntime } from "@opencode-ai/core/effect/runtime" import { Filesystem } from "@/util/filesystem" -import * as Log from "@opencode-ai/core/util/log" +import * as EffectLogger from "@opencode-ai/core/effect/logger" import { ConfigVariable } from "@/config/variable" import { Npm } from "@opencode-ai/core/npm" import type { DeepMutable } from "@opencode-ai/core/schema" import type { TuiAttentionSoundName } from "@opencode-ai/plugin/tui" import { FormatError, FormatUnknownError } from "@/cli/error" -const log = Log.create({ service: "tui.config" }) +const log = EffectLogger.create({ service: "tui.config" }) export const Info = TuiInfo export type Info = DeepMutable> diff --git a/packages/opencode/src/cli/cmd/tui/context/sync.tsx b/packages/opencode/src/cli/cmd/tui/context/sync.tsx index 9f8a384f77..dc45f57a30 100644 --- a/packages/opencode/src/cli/cmd/tui/context/sync.tsx +++ b/packages/opencode/src/cli/cmd/tui/context/sync.tsx @@ -28,7 +28,6 @@ import type { Snapshot } from "@/snapshot" import { useExit } from "./exit" import { useArgs } from "./args" import { batch, onMount } from "solid-js" -import * as Log from "@opencode-ai/core/util/log" import { emptyConsoleState, type ConsoleState } from "@/config/console-state" import path from "path" import { useKV } from "./kv" @@ -465,11 +464,6 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ }) }) .catch(async (e) => { - Log.Default.error("tui bootstrap failed", { - error: e instanceof Error ? e.message : String(e), - name: e instanceof Error ? e.name : undefined, - stack: e instanceof Error ? e.stack : undefined, - }) if (fatal) { await exit(e) } else { diff --git a/packages/opencode/src/cli/cmd/tui/plugin/runtime.ts b/packages/opencode/src/cli/cmd/tui/plugin/runtime.ts index 515e617563..2ad53b673e 100644 --- a/packages/opencode/src/cli/cmd/tui/plugin/runtime.ts +++ b/packages/opencode/src/cli/cmd/tui/plugin/runtime.ts @@ -14,7 +14,7 @@ import { import path from "path" import { fileURLToPath } from "url" import { TuiConfig } from "@/cli/cmd/tui/config/tui" -import * as Log from "@opencode-ai/core/util/log" +import * as EffectLogger from "@opencode-ai/core/effect/logger" import { errorData, errorMessage } from "@/util/error" import { isRecord } from "@/util/record" import { resolveAttentionSoundPaths } from "../config/tui-schema" @@ -117,7 +117,7 @@ type RuntimeState = { dispose_timeout_ms: number } -const log = Log.create({ service: "tui.plugin" }) +const log = EffectLogger.create({ service: "tui.plugin" }) const DISPOSE_TIMEOUT_MS = 5000 const KV_KEY = "plugin_enabled" const EMPTY_TUI: TuiPluginModule = { diff --git a/packages/opencode/src/cli/cmd/tui/thread.ts b/packages/opencode/src/cli/cmd/tui/thread.ts index 382147d918..fabdaa1567 100644 --- a/packages/opencode/src/cli/cmd/tui/thread.ts +++ b/packages/opencode/src/cli/cmd/tui/thread.ts @@ -4,7 +4,6 @@ import { type rpc } from "./worker" import path from "path" import { fileURLToPath } from "url" import { UI } from "@/cli/ui" -import * as Log from "@opencode-ai/core/util/log" import { errorMessage } from "@/util/error" import { withTimeout } from "@/util/timeout" import { withNetworkOptions, resolveNetworkOptionsNoConfig } from "@/cli/network" @@ -146,26 +145,12 @@ export const TuiThreadCommand = cmd({ const worker = new Worker(file, { env, }) - worker.onerror = (e) => { - Log.Default.error("thread error", { - message: e.message, - filename: e.filename, - lineno: e.lineno, - colno: e.colno, - error: e.error, - }) - } + worker.onerror = () => {} const client = Rpc.client(worker) - const error = (e: unknown) => { - Log.Default.error("process error", { error: errorMessage(e) }) - } + const error = () => {} const reload = () => { - client.call("reload", undefined).catch((err) => { - Log.Default.warn("worker reload failed", { - error: errorMessage(err), - }) - }) + client.call("reload", undefined).catch(() => {}) } process.on("uncaughtException", error) process.on("unhandledRejection", error) @@ -178,11 +163,7 @@ export const TuiThreadCommand = cmd({ process.off("uncaughtException", error) process.off("unhandledRejection", error) process.off("SIGUSR2", reload) - await withTimeout(client.call("shutdown", undefined), 5000).catch((error) => { - Log.Default.warn("worker shutdown failed", { - error: errorMessage(error), - }) - }) + await withTimeout(client.call("shutdown", undefined), 5000).catch(() => {}) worker.terminate() } diff --git a/packages/opencode/src/cli/cmd/tui/util/audio.ts b/packages/opencode/src/cli/cmd/tui/util/audio.ts index 7d7c3d5a42..9b5d7b43f6 100644 --- a/packages/opencode/src/cli/cmd/tui/util/audio.ts +++ b/packages/opencode/src/cli/cmd/tui/util/audio.ts @@ -1,7 +1,7 @@ import { Audio, type AudioErrorContext, type AudioPlayOptions, type AudioSound, type AudioVoice } from "@opentui/core" -import * as Log from "@opencode-ai/core/util/log" +import * as EffectLogger from "@opencode-ai/core/effect/logger" -const log = Log.create({ service: "tui.audio" }) +const log = EffectLogger.create({ service: "tui.audio" }) let audio: Audio | null | undefined const sounds = new Map>() diff --git a/packages/opencode/src/cli/cmd/tui/worker.ts b/packages/opencode/src/cli/cmd/tui/worker.ts index 31ead18cf8..ef03a07d08 100644 --- a/packages/opencode/src/cli/cmd/tui/worker.ts +++ b/packages/opencode/src/cli/cmd/tui/worker.ts @@ -1,6 +1,5 @@ import { Installation } from "@/installation" import { Server } from "@/server/server" -import * as Log from "@opencode-ai/core/util/log" import { InstanceRuntime } from "@/project/instance-runtime" import { Rpc } from "@/util/rpc" import { upgrade } from "@/cli/upgrade" @@ -15,30 +14,10 @@ import { Effect } from "effect" import { disposeAllInstancesAndEmitGlobalDisposed } from "@/server/global-lifecycle" ensureProcessMetadata("worker") - -await Log.init({ - print: process.argv.includes("--print-logs"), - dev: Installation.isLocal(), - level: (() => { - if (Installation.isLocal()) return "DEBUG" - return "INFO" - })(), -}) +if (!process.env.OPENCODE_LOG_LEVEL) process.env.OPENCODE_LOG_LEVEL = Installation.isLocal() ? "DEBUG" : "INFO" Heap.start() -process.on("unhandledRejection", (e) => { - Log.Default.error("rejection", { - e: e instanceof Error ? e.message : e, - }) -}) - -process.on("uncaughtException", (e) => { - Log.Default.error("exception", { - e: e instanceof Error ? e.message : e, - }) -}) - // Subscribe to global events and forward them via RPC GlobalBus.on("event", (event) => { Rpc.emit("global.event", event) @@ -89,8 +68,6 @@ export const rpc = { ) }, async shutdown() { - Log.Default.info("worker shutting down") - await InstanceRuntime.disposeAllInstances() if (server) await server.stop(true) }, diff --git a/packages/opencode/src/cli/heap.ts b/packages/opencode/src/cli/heap.ts index a865c2a4c6..0c31642959 100644 --- a/packages/opencode/src/cli/heap.ts +++ b/packages/opencode/src/cli/heap.ts @@ -2,9 +2,9 @@ import path from "path" import { writeHeapSnapshot } from "node:v8" import { Flag } from "@opencode-ai/core/flag/flag" import { Global } from "@opencode-ai/core/global" -import * as Log from "@opencode-ai/core/util/log" +import * as EffectLogger from "@opencode-ai/core/effect/logger" -const log = Log.create({ service: "heap" }) +const log = EffectLogger.create({ service: "heap" }) const MINUTE = 60_000 const LIMIT = 2 * 1024 * 1024 * 1024 diff --git a/packages/opencode/src/config/agent.ts b/packages/opencode/src/config/agent.ts index 379301cd29..3f6ac8d967 100644 --- a/packages/opencode/src/config/agent.ts +++ b/packages/opencode/src/config/agent.ts @@ -3,7 +3,7 @@ export * as ConfigAgent from "./agent" import path from "path" import { Exit, Schema, SchemaGetter } from "effect" import { PositiveInt } from "@opencode-ai/core/schema" -import * as Log from "@opencode-ai/core/util/log" +import * as EffectLogger from "@opencode-ai/core/effect/logger" import { Glob } from "@opencode-ai/core/util/glob" import { configEntryNameFromPath } from "./entry-name" import * as ConfigMarkdown from "./markdown" @@ -11,7 +11,7 @@ import { ConfigModelID } from "./model-id" import { ConfigParse } from "./parse" import { ConfigPermission } from "./permission" -const log = Log.create({ service: "config" }) +const log = EffectLogger.create({ service: "config" }) const Color = Schema.Union([ Schema.String.check(Schema.isPattern(/^#[0-9a-fA-F]{6}$/)), diff --git a/packages/opencode/src/config/command.ts b/packages/opencode/src/config/command.ts index d5046b6a17..ea801a32ee 100644 --- a/packages/opencode/src/config/command.ts +++ b/packages/opencode/src/config/command.ts @@ -1,7 +1,7 @@ export * as ConfigCommand from "./command" import path from "path" -import * as Log from "@opencode-ai/core/util/log" +import * as EffectLogger from "@opencode-ai/core/effect/logger" import { Cause, Exit, Schema } from "effect" import { Glob } from "@opencode-ai/core/util/glob" import { configEntryNameFromPath } from "./entry-name" @@ -9,7 +9,7 @@ import { InvalidError } from "./error" import * as ConfigMarkdown from "./markdown" import { ConfigModelID } from "./model-id" -const log = Log.create({ service: "config" }) +const log = EffectLogger.create({ service: "config" }) export const Info = Schema.Struct({ template: Schema.String, diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts index 456d6c3ee3..747ff38f80 100644 --- a/packages/opencode/src/config/config.ts +++ b/packages/opencode/src/config/config.ts @@ -1,4 +1,4 @@ -import * as Log from "@opencode-ai/core/util/log" +import * as EffectLogger from "@opencode-ai/core/effect/logger" import { serviceUse } from "@opencode-ai/core/effect/service-use" import path from "path" import { pathToFileURL } from "url" @@ -44,7 +44,7 @@ import { ConfigVariable } from "./variable" import { Npm } from "@opencode-ai/core/npm" import { withTransientReadRetry } from "@/util/effect-http-client" -const log = Log.create({ service: "config" }) +const log = EffectLogger.create({ service: "config" }) // Custom merge function that concatenates array fields instead of replacing them // Keep remeda's deep conditional merge type out of hot config-loading paths; TS profiling showed it dominates here. diff --git a/packages/opencode/src/config/managed.ts b/packages/opencode/src/config/managed.ts index c5348afaf7..059577e85b 100644 --- a/packages/opencode/src/config/managed.ts +++ b/packages/opencode/src/config/managed.ts @@ -3,10 +3,10 @@ export * as ConfigManaged from "./managed" import { existsSync } from "fs" import os from "os" import path from "path" -import * as Log from "@opencode-ai/core/util/log" +import * as EffectLogger from "@opencode-ai/core/effect/logger" import { Process } from "@/util/process" -const log = Log.create({ service: "config" }) +const log = EffectLogger.create({ service: "config" }) const MANAGED_PLIST_DOMAIN = "ai.opencode.managed" diff --git a/packages/opencode/src/control-plane/workspace.ts b/packages/opencode/src/control-plane/workspace.ts index 9f44d22334..6502934cef 100644 --- a/packages/opencode/src/control-plane/workspace.ts +++ b/packages/opencode/src/control-plane/workspace.ts @@ -12,7 +12,7 @@ import { Auth } from "@/auth" import { SyncEvent } from "@/sync" import { EventSequenceTable, EventTable } from "@/sync/event.sql" import { AppFileSystem } from "@opencode-ai/core/filesystem" -import * as Log from "@opencode-ai/core/util/log" +import * as EffectLogger from "@opencode-ai/core/effect/logger" import { RuntimeFlags } from "@/effect/runtime-flags" import { ProjectID } from "@/project/schema" import { Slug } from "@opencode-ai/core/util/slug" @@ -77,7 +77,7 @@ function fromRow(row: typeof WorkspaceTable.$inferSelect): Info { const db = (fn: (d: Parameters[0] extends (trx: infer D) => any ? D : never) => T) => Effect.sync(() => Database.use(fn)) -const log = Log.create({ service: "workspace-sync" }) +const log = EffectLogger.create({ service: "workspace-sync" }) export const CreateInput = Schema.Struct({ id: Schema.optional(WorkspaceID), diff --git a/packages/opencode/src/data-migration.ts b/packages/opencode/src/data-migration.ts index b6956032a4..1576c7aa02 100644 --- a/packages/opencode/src/data-migration.ts +++ b/packages/opencode/src/data-migration.ts @@ -1,7 +1,7 @@ import { Context, Effect, Layer } from "effect" import { Database } from "./storage/db" import { DataMigrationTable } from "./data-migration.sql" -import * as Log from "@opencode-ai/core/util/log" +import * as EffectLogger from "@opencode-ai/core/effect/logger" import { and, asc, eq, gt, inArray, sql } from "drizzle-orm" import { MessageTable, SessionTable } from "./session/session.sql" import type { SessionID } from "./session/schema" @@ -11,7 +11,7 @@ export type Migration = { run: Effect.Effect } -const log = Log.create({ service: "data-migration" }) +const log = EffectLogger.create({ service: "data-migration" }) export interface Interface {} diff --git a/packages/opencode/src/file/index.ts b/packages/opencode/src/file/index.ts index 0992289fe2..53223fb494 100644 --- a/packages/opencode/src/file/index.ts +++ b/packages/opencode/src/file/index.ts @@ -12,7 +12,7 @@ import ignore from "ignore" import path from "path" import { Global } from "@opencode-ai/core/global" import { containsPath } from "../project/instance-context" -import * as Log from "@opencode-ai/core/util/log" +import * as EffectLogger from "@opencode-ai/core/effect/logger" import { Protected } from "./protected" import { Ripgrep } from "./ripgrep" import { NonNegativeInt, type DeepMutable } from "@opencode-ai/core/schema" @@ -70,7 +70,7 @@ export const Event = { ), } -const log = Log.create({ service: "file" }) +const log = EffectLogger.create({ service: "file" }) const binary = new Set([ "exe", diff --git a/packages/opencode/src/file/ripgrep.ts b/packages/opencode/src/file/ripgrep.ts index 8b3d5cf174..6aa441eef6 100644 --- a/packages/opencode/src/file/ripgrep.ts +++ b/packages/opencode/src/file/ripgrep.ts @@ -9,12 +9,12 @@ import { ChildProcessSpawner } from "effect/unstable/process/ChildProcessSpawner import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner" import { Global } from "@opencode-ai/core/global" -import * as Log from "@opencode-ai/core/util/log" +import * as EffectLogger from "@opencode-ai/core/effect/logger" import { sanitizedProcessEnv } from "@opencode-ai/core/util/opencode-process" import { which } from "@/util/which" import { NonNegativeInt } from "@opencode-ai/core/schema" -const log = Log.create({ service: "ripgrep" }) +const log = EffectLogger.create({ service: "ripgrep" }) const VERSION = "15.1.0" const PLATFORM = { "arm64-darwin": { platform: "aarch64-apple-darwin", extension: "tar.gz" }, diff --git a/packages/opencode/src/file/watcher.ts b/packages/opencode/src/file/watcher.ts index 91ad9ff4de..0ce06eb87c 100644 --- a/packages/opencode/src/file/watcher.ts +++ b/packages/opencode/src/file/watcher.ts @@ -14,11 +14,11 @@ import { lazy } from "@/util/lazy" import { Config } from "@/config/config" import { FileIgnore } from "./ignore" import { Protected } from "./protected" -import * as Log from "@opencode-ai/core/util/log" +import * as EffectLogger from "@opencode-ai/core/effect/logger" declare const OPENCODE_LIBC: string | undefined -const log = Log.create({ service: "file.watcher" }) +const log = EffectLogger.create({ service: "file.watcher" }) const SUBSCRIBE_TIMEOUT_MS = 10_000 export const Event = { diff --git a/packages/opencode/src/format/index.ts b/packages/opencode/src/format/index.ts index 6d0311f6fa..098f4b8dc6 100644 --- a/packages/opencode/src/format/index.ts +++ b/packages/opencode/src/format/index.ts @@ -8,10 +8,10 @@ import { mergeDeep } from "remeda" import { Config } from "@/config/config" import { RuntimeFlags } from "@/effect/runtime-flags" import { errorMessage } from "@/util/error" -import * as Log from "@opencode-ai/core/util/log" +import * as EffectLogger from "@opencode-ai/core/effect/logger" import * as Formatter from "./formatter" -const log = Log.create({ service: "format" }) +const log = EffectLogger.create({ service: "format" }) export const Status = Schema.Struct({ name: Schema.String, diff --git a/packages/opencode/src/ide/index.ts b/packages/opencode/src/ide/index.ts index a31c5bd057..7ead418891 100644 --- a/packages/opencode/src/ide/index.ts +++ b/packages/opencode/src/ide/index.ts @@ -1,7 +1,7 @@ import { BusEvent } from "@/bus/bus-event" import { Schema } from "effect" import { NamedError } from "@opencode-ai/core/util/error" -import * as Log from "@opencode-ai/core/util/log" +import * as EffectLogger from "@opencode-ai/core/effect/logger" import { Process } from "@/util/process" const SUPPORTED_IDES = [ @@ -12,7 +12,7 @@ const SUPPORTED_IDES = [ { name: "VSCodium" as const, cmd: "codium" }, ] -const log = Log.create({ service: "ide" }) +const log = EffectLogger.create({ service: "ide" }) export const Event = { Installed: BusEvent.define( diff --git a/packages/opencode/src/image/image.ts b/packages/opencode/src/image/image.ts index 2a3c4fa5c0..75d5a5cfd5 100644 --- a/packages/opencode/src/image/image.ts +++ b/packages/opencode/src/image/image.ts @@ -1,6 +1,6 @@ import { Config } from "@/config/config" import type { MessageV2 } from "@/session/message-v2" -import * as Log from "@opencode-ai/core/util/log" +import * as EffectLogger from "@opencode-ai/core/effect/logger" import photonWasm from "@silvia-odwyer/photon-node/photon_rs_bg.wasm" with { type: "file" } import { Context, Effect, Layer, Schema } from "effect" import path from "node:path" @@ -11,7 +11,7 @@ const MAX_WIDTH = 2000 const MAX_HEIGHT = 2000 const AUTO_RESIZE = true const JPEG_QUALITIES = [80, 85, 70, 55, 40] -const log = Log.create({ service: "image" }) +const log = EffectLogger.create({ service: "image" }) export class ResizerUnavailableError extends Schema.TaggedErrorClass()( "ImageResizerUnavailableError", diff --git a/packages/opencode/src/index.ts b/packages/opencode/src/index.ts index ed40ca26c8..6627d50304 100644 --- a/packages/opencode/src/index.ts +++ b/packages/opencode/src/index.ts @@ -2,7 +2,6 @@ import yargs from "yargs" import { hideBin } from "yargs/helpers" import { RunCommand } from "./cli/cmd/run" import { GenerateCommand } from "./cli/cmd/generate" -import * as Log from "@opencode-ai/core/util/log" import { ConsoleCommand } from "./cli/cmd/account" import { ProvidersCommand } from "./cli/cmd/providers" import { AgentCommand } from "./cli/cmd/agent" @@ -12,7 +11,6 @@ import { ModelsCommand } from "./cli/cmd/models" import { UI } from "./cli/ui" import { Installation } from "./installation" import { InstallationVersion } from "@opencode-ai/core/installation/version" -import { NamedError } from "@opencode-ai/core/util/error" import { FormatError } from "./cli/error" import { ServeCommand } from "./cli/cmd/serve" import { Filesystem } from "@/util/filesystem" @@ -38,24 +36,9 @@ import { errorMessage } from "./util/error" import { PluginCommand } from "./cli/cmd/plug" import { Heap } from "./cli/heap" import { drizzle } from "drizzle-orm/bun-sqlite" -import { ensureProcessMetadata } from "@opencode-ai/core/util/opencode-process" -import { isRecord } from "@/util/record" - -const processMetadata = ensureProcessMetadata("main") - -process.on("unhandledRejection", (e) => { - Log.Default.error("rejection", { - e: errorMessage(e), - }) -}) - -process.on("uncaughtException", (e) => { - Log.Default.error("exception", { - e: errorMessage(e), - }) -}) const args = hideBin(process.argv) +type LogLevel = "DEBUG" | "INFO" | "WARN" | "ERROR" function show(out: string) { const text = out.trimStart() @@ -67,7 +50,7 @@ function show(out: string) { process.stderr.write(out) } -function parseLogLevel(value: string | undefined): Log.Level | undefined { +function parseLogLevel(value: string | undefined): LogLevel | undefined { if (value === "DEBUG" || value === "INFO" || value === "WARN" || value === "ERROR") return value return undefined } @@ -102,18 +85,12 @@ const cli = yargs(args) process.env.OPENCODE_PURE = "1" } - await Log.init({ - print: opts.printLogs, - file: opts.logFile, - dev: Installation.isLocal(), - level: (() => { - if (opts.logLevel) return opts.logLevel as Log.Level - const envLevel = parseLogLevel(process.env.OPENCODE_LOG_LEVEL) - if (envLevel) return envLevel - if (Installation.isLocal()) return "DEBUG" - return "INFO" - })(), - }) + if (opts.printLogs !== undefined) process.env.OPENCODE_PRINT_LOGS = opts.printLogs ? "1" : "0" + if (opts.logFile) process.env.OPENCODE_LOG_FILE = opts.logFile + const envLevel = parseLogLevel(process.env.OPENCODE_LOG_LEVEL) + process.env.OPENCODE_LOG_LEVEL = opts.logLevel + ? (opts.logLevel as LogLevel) + : (envLevel ?? (Installation.isLocal() ? "DEBUG" : "INFO")) Heap.start() @@ -121,13 +98,6 @@ const cli = yargs(args) process.env.OPENCODE = "1" process.env.OPENCODE_PID = String(process.pid) - Log.Default.info("opencode", { - version: InstallationVersion, - args: process.argv.slice(2), - process_role: processMetadata.processRole, - run_id: processMetadata.runID, - }) - const marker = path.join(Global.Path.data, "opencode.db") if (!(await Filesystem.exists(marker))) { const tty = process.stderr.isTTY @@ -215,42 +185,10 @@ try { await cli.parse() } } catch (e) { - let data: Record = {} - if (e instanceof Error) { - Object.assign(data, { - name: e.name, - message: e.message, - cause: e.cause?.toString(), - stack: e.stack, - }) - } - - if (e instanceof NamedError) { - const obj = e.toObject() - if (isRecord(obj.data)) { - for (const [key, value] of Object.entries(obj.data)) { - if (key === "name" || key === "stack" || key === "cause") continue - data[key] = value - } - } - } - - if (e instanceof ResolveMessage) { - Object.assign(data, { - name: e.name, - message: e.message, - code: e.code, - specifier: e.specifier, - referrer: e.referrer, - position: e.position, - importKind: e.importKind, - }) - } - Log.Default.error("fatal", data) const formatted = FormatError(e) if (formatted) UI.error(formatted) if (formatted === undefined) { - UI.error("Unexpected error, check log file at " + Log.file() + " for more details" + EOL) + UI.error("Unexpected error" + EOL) process.stderr.write(errorMessage(e) + EOL) } process.exitCode = 1 diff --git a/packages/opencode/src/installation/index.ts b/packages/opencode/src/installation/index.ts index 1f8dc116b1..e73e9d66e1 100644 --- a/packages/opencode/src/installation/index.ts +++ b/packages/opencode/src/installation/index.ts @@ -7,13 +7,13 @@ import { ChildProcess } from "effect/unstable/process" import { AppProcess } from "@opencode-ai/core/process" import path from "path" import { BusEvent } from "@/bus/bus-event" -import * as Log from "@opencode-ai/core/util/log" +import * as EffectLogger from "@opencode-ai/core/effect/logger" import { makeRuntime } from "@opencode-ai/core/effect/runtime" import semver from "semver" import { InstallationChannel, InstallationVersion } from "@opencode-ai/core/installation/version" import { NpmConfig } from "@opencode-ai/core/npm-config" -const log = Log.create({ service: "installation" }) +const log = EffectLogger.create({ service: "installation" }) export type Method = "curl" | "npm" | "yarn" | "pnpm" | "bun" | "brew" | "scoop" | "choco" | "unknown" diff --git a/packages/opencode/src/lsp/client.ts b/packages/opencode/src/lsp/client.ts index 205cba6f29..5f13b4b36c 100644 --- a/packages/opencode/src/lsp/client.ts +++ b/packages/opencode/src/lsp/client.ts @@ -4,7 +4,7 @@ import path from "path" import { pathToFileURL, fileURLToPath } from "url" import { createMessageConnection, StreamMessageReader, StreamMessageWriter } from "vscode-jsonrpc/node" import type { Diagnostic as VSCodeDiagnostic } from "vscode-languageserver-types" -import * as Log from "@opencode-ai/core/util/log" +import * as EffectLogger from "@opencode-ai/core/effect/logger" import { Process } from "@/util/process" import { LANGUAGE_EXTENSIONS } from "./language" import { Effect, Schema } from "effect" @@ -27,7 +27,7 @@ const FILE_CHANGE_CREATED = 1 const FILE_CHANGE_CHANGED = 2 const TEXT_DOCUMENT_SYNC_INCREMENTAL = 2 -const log = Log.create({ service: "lsp.client" }) +const log = EffectLogger.create({ service: "lsp.client" }) const busRuntime = makeRuntime(Bus.Service, Bus.layer) export type Info = NonNullable>> diff --git a/packages/opencode/src/lsp/lsp.ts b/packages/opencode/src/lsp/lsp.ts index 3117b834c5..2cd00f0b42 100644 --- a/packages/opencode/src/lsp/lsp.ts +++ b/packages/opencode/src/lsp/lsp.ts @@ -1,6 +1,6 @@ import { BusEvent } from "@/bus/bus-event" import { Bus } from "@/bus" -import * as Log from "@opencode-ai/core/util/log" +import * as EffectLogger from "@opencode-ai/core/effect/logger" import * as LSPClient from "./client" import path from "path" import { pathToFileURL, fileURLToPath } from "url" @@ -14,7 +14,7 @@ import { containsPath } from "@/project/instance-context" import { NonNegativeInt } from "@opencode-ai/core/schema" import { RuntimeFlags } from "@/effect/runtime-flags" -const log = Log.create({ service: "lsp" }) +const log = EffectLogger.create({ service: "lsp" }) export const Event = { Updated: BusEvent.define("lsp.updated", Schema.Struct({})), diff --git a/packages/opencode/src/lsp/server.ts b/packages/opencode/src/lsp/server.ts index ad90ef5c78..c7d6d3a713 100644 --- a/packages/opencode/src/lsp/server.ts +++ b/packages/opencode/src/lsp/server.ts @@ -2,7 +2,7 @@ import type { ChildProcessWithoutNullStreams } from "child_process" import path from "path" import os from "os" import { Global } from "@opencode-ai/core/global" -import * as Log from "@opencode-ai/core/util/log" +import * as EffectLogger from "@opencode-ai/core/effect/logger" import { text } from "node:stream/consumers" import fs from "fs/promises" import { Filesystem } from "@/util/filesystem" @@ -15,7 +15,7 @@ import { spawn } from "./launch" import { Npm } from "@opencode-ai/core/npm" import type { RuntimeFlags } from "@/effect/runtime-flags" -const log = Log.create({ service: "lsp.server" }) +const log = EffectLogger.create({ service: "lsp.server" }) const pathExists = async (p: string) => fs .stat(p) diff --git a/packages/opencode/src/mcp/index.ts b/packages/opencode/src/mcp/index.ts index c95394f338..fc39fb68d4 100644 --- a/packages/opencode/src/mcp/index.ts +++ b/packages/opencode/src/mcp/index.ts @@ -14,7 +14,7 @@ import { } from "@modelcontextprotocol/sdk/types.js" import { Config } from "@/config/config" import { ConfigMCP } from "../config/mcp" -import * as Log from "@opencode-ai/core/util/log" +import * as EffectLogger from "@opencode-ai/core/effect/logger" import { NamedError } from "@opencode-ai/core/util/error" import { InstallationVersion } from "@opencode-ai/core/installation/version" import { withTimeout } from "@/util/timeout" @@ -32,7 +32,7 @@ import { InstanceState } from "@/effect/instance-state" import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process" import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner" -const log = Log.create({ service: "mcp" }) +const log = EffectLogger.create({ service: "mcp" }) const DEFAULT_TIMEOUT = 30_000 const TolerantListToolsResultSchema = ListToolsResultSchema.extend({ diff --git a/packages/opencode/src/mcp/oauth-callback.ts b/packages/opencode/src/mcp/oauth-callback.ts index 622cae61eb..62ac571eef 100644 --- a/packages/opencode/src/mcp/oauth-callback.ts +++ b/packages/opencode/src/mcp/oauth-callback.ts @@ -1,9 +1,9 @@ import { createConnection } from "net" import { createServer } from "http" -import * as Log from "@opencode-ai/core/util/log" +import * as EffectLogger from "@opencode-ai/core/effect/logger" import { OAUTH_CALLBACK_PORT, OAUTH_CALLBACK_PATH, parseRedirectUri } from "./oauth-provider" -const log = Log.create({ service: "mcp.oauth-callback" }) +const log = EffectLogger.create({ service: "mcp.oauth-callback" }) // Current callback server configuration (may differ from defaults if custom redirectUri is used) let currentPort = OAUTH_CALLBACK_PORT diff --git a/packages/opencode/src/mcp/oauth-provider.ts b/packages/opencode/src/mcp/oauth-provider.ts index c1a52639c8..c8a3470548 100644 --- a/packages/opencode/src/mcp/oauth-provider.ts +++ b/packages/opencode/src/mcp/oauth-provider.ts @@ -7,9 +7,9 @@ import type { } from "@modelcontextprotocol/sdk/shared/auth.js" import { Effect } from "effect" import { McpAuth } from "./auth" -import * as Log from "@opencode-ai/core/util/log" +import * as EffectLogger from "@opencode-ai/core/effect/logger" -const log = Log.create({ service: "mcp.oauth" }) +const log = EffectLogger.create({ service: "mcp.oauth" }) const OAUTH_CALLBACK_PORT = 19876 const OAUTH_CALLBACK_PATH = "/mcp/oauth/callback" diff --git a/packages/opencode/src/node.ts b/packages/opencode/src/node.ts index 9c29dcd984..f74c7339a5 100644 --- a/packages/opencode/src/node.ts +++ b/packages/opencode/src/node.ts @@ -1,6 +1,5 @@ export { Config } from "@/config/config" export { Server } from "./server/server" export { bootstrap } from "./cli/bootstrap" -export * as Log from "@opencode-ai/core/util/log" export { Database } from "@/storage/db" export { JsonMigration } from "@/storage/json-migration" diff --git a/packages/opencode/src/patch/index.ts b/packages/opencode/src/patch/index.ts index 42b26fe963..6c7b553f87 100644 --- a/packages/opencode/src/patch/index.ts +++ b/packages/opencode/src/patch/index.ts @@ -1,10 +1,10 @@ import { Effect, Schema } from "effect" import * as path from "path" import { AppFileSystem } from "@opencode-ai/core/filesystem" -import * as Log from "@opencode-ai/core/util/log" +import * as EffectLogger from "@opencode-ai/core/effect/logger" import * as Bom from "../util/bom" -const log = Log.create({ service: "patch" }) +const log = EffectLogger.create({ service: "patch" }) export const PatchSchema = Schema.Struct({ patchText: Schema.String.annotate({ description: "The full patch text that describes all changes to be made" }), diff --git a/packages/opencode/src/permission/index.ts b/packages/opencode/src/permission/index.ts index 1814c5ab2b..6ea6e2a2cc 100644 --- a/packages/opencode/src/permission/index.ts +++ b/packages/opencode/src/permission/index.ts @@ -7,14 +7,14 @@ import { MessageID, SessionID } from "@/session/schema" import { PermissionTable } from "@/session/session.sql" import { Database } from "@/storage/db" import { eq } from "drizzle-orm" -import * as Log from "@opencode-ai/core/util/log" +import * as EffectLogger from "@opencode-ai/core/effect/logger" import { Wildcard } from "@opencode-ai/core/util/wildcard" import { Deferred, Effect, Layer, Schema, Context } from "effect" import os from "os" import { PermissionV2 } from "@opencode-ai/core/permission" import { PermissionID } from "./schema" -const log = Log.create({ service: "permission" }) +const log = EffectLogger.create({ service: "permission" }) export const Action = PermissionV2.Action.annotate({ identifier: "PermissionAction" }) export type Action = Schema.Schema.Type diff --git a/packages/opencode/src/plugin/codex.ts b/packages/opencode/src/plugin/codex.ts index df4b4d0d5c..555146b6d2 100644 --- a/packages/opencode/src/plugin/codex.ts +++ b/packages/opencode/src/plugin/codex.ts @@ -1,12 +1,12 @@ import type { Hooks, PluginInput } from "@opencode-ai/plugin" -import * as Log from "@opencode-ai/core/util/log" +import * as EffectLogger from "@opencode-ai/core/effect/logger" import { InstallationVersion } from "@opencode-ai/core/installation/version" import { OAUTH_DUMMY_KEY } from "../auth" import os from "os" import { setTimeout as sleep } from "node:timers/promises" import { createServer } from "http" -const log = Log.create({ service: "plugin.codex" }) +const log = EffectLogger.create({ service: "plugin.codex" }) const CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann" const ISSUER = "https://auth.openai.com" diff --git a/packages/opencode/src/plugin/digitalocean.ts b/packages/opencode/src/plugin/digitalocean.ts index d04437a4ea..8160f2f5d0 100644 --- a/packages/opencode/src/plugin/digitalocean.ts +++ b/packages/opencode/src/plugin/digitalocean.ts @@ -1,11 +1,11 @@ import type { Hooks, PluginInput } from "@opencode-ai/plugin" import type { Model } from "@opencode-ai/sdk/v2" -import * as Log from "@opencode-ai/core/util/log" +import * as EffectLogger from "@opencode-ai/core/effect/logger" import { InstallationVersion } from "@opencode-ai/core/installation/version" import { createServer } from "http" import open from "open" -const log = Log.create({ service: "plugin.digitalocean" }) +const log = EffectLogger.create({ service: "plugin.digitalocean" }) const DO_OAUTH_CLIENT_ID = "b1a6c5158156caac821fd1b30253ca8acb52454a48fa744420e41889cb589f82" const DO_AUTHORIZE_URL = "https://cloud.digitalocean.com/v1/oauth/authorize" diff --git a/packages/opencode/src/plugin/github-copilot/copilot.ts b/packages/opencode/src/plugin/github-copilot/copilot.ts index d24d9b9dae..92e451f4da 100644 --- a/packages/opencode/src/plugin/github-copilot/copilot.ts +++ b/packages/opencode/src/plugin/github-copilot/copilot.ts @@ -2,12 +2,12 @@ import type { Hooks, PluginInput } from "@opencode-ai/plugin" import type { Model } from "@opencode-ai/sdk/v2" import { InstallationVersion } from "@opencode-ai/core/installation/version" import { iife } from "@/util/iife" -import * as Log from "@opencode-ai/core/util/log" +import * as EffectLogger from "@opencode-ai/core/effect/logger" import { setTimeout as sleep } from "node:timers/promises" import { CopilotModels } from "./models" import { MessageV2 } from "@/session/message-v2" -const log = Log.create({ service: "plugin.copilot" }) +const log = EffectLogger.create({ service: "plugin.copilot" }) const CLIENT_ID = "Ov23li8tweQw6odWQebz" // Add a small safety buffer when polling to avoid hitting the server diff --git a/packages/opencode/src/plugin/index.ts b/packages/opencode/src/plugin/index.ts index e59fefe080..88c65873ba 100644 --- a/packages/opencode/src/plugin/index.ts +++ b/packages/opencode/src/plugin/index.ts @@ -7,7 +7,7 @@ import type { } from "@opencode-ai/plugin" import { Config } from "@/config/config" import { Bus } from "../bus" -import * as Log from "@opencode-ai/core/util/log" +import * as EffectLogger from "@opencode-ai/core/effect/logger" import { createOpencodeClient } from "@opencode-ai/sdk" import { ServerAuth } from "@/server/auth" import { CodexAuthPlugin } from "./codex" @@ -30,7 +30,7 @@ import { registerAdapter } from "@/control-plane/adapters" import type { WorkspaceAdapter } from "@/control-plane/types" import { RuntimeFlags } from "@/effect/runtime-flags" -const log = Log.create({ service: "plugin" }) +const log = EffectLogger.create({ service: "plugin" }) type State = { hooks: Hooks[] diff --git a/packages/opencode/src/plugin/xai.ts b/packages/opencode/src/plugin/xai.ts index 485a37fe00..c651d2f8fc 100644 --- a/packages/opencode/src/plugin/xai.ts +++ b/packages/opencode/src/plugin/xai.ts @@ -1,10 +1,10 @@ import type { Hooks, PluginInput } from "@opencode-ai/plugin" -import * as Log from "@opencode-ai/core/util/log" +import * as EffectLogger from "@opencode-ai/core/effect/logger" import { OAUTH_DUMMY_KEY } from "../auth" import { createServer } from "http" import { InstallationVersion } from "@opencode-ai/core/installation/version" -const log = Log.create({ service: "plugin.xai" }) +const log = EffectLogger.create({ service: "plugin.xai" }) // Public Grok-CLI OAuth client. xAI's auth server rejects loopback OAuth from // non-allowlisted clients, so we reuse the Grok-CLI client_id that xAI ships diff --git a/packages/opencode/src/project/project.ts b/packages/opencode/src/project/project.ts index 2ef299e944..73c5e586a8 100644 --- a/packages/opencode/src/project/project.ts +++ b/packages/opencode/src/project/project.ts @@ -3,7 +3,7 @@ import { Database } from "@/storage/db" import { ProjectTable } from "./project.sql" import { PermissionTable, SessionTable } from "../session/session.sql" import { WorkspaceTable } from "../control-plane/workspace.sql" -import * as Log from "@opencode-ai/core/util/log" +import * as EffectLogger from "@opencode-ai/core/effect/logger" import { Flag } from "@opencode-ai/core/flag/flag" import { BusEvent } from "@/bus/bus-event" import { GlobalBus } from "@/bus/global" @@ -22,7 +22,7 @@ import { AbsolutePath, NonNegativeInt, optionalOmitUndefined } from "@opencode-a import { serviceUse } from "@opencode-ai/core/effect/service-use" import { RuntimeFlags } from "@/effect/runtime-flags" -const log = Log.create({ service: "project" }) +const log = EffectLogger.create({ service: "project" }) const ProjectVcs = Schema.Literal("git") diff --git a/packages/opencode/src/project/vcs.ts b/packages/opencode/src/project/vcs.ts index d2b5729dd4..31f9325be9 100644 --- a/packages/opencode/src/project/vcs.ts +++ b/packages/opencode/src/project/vcs.ts @@ -5,9 +5,9 @@ import { BusEvent } from "@/bus/bus-event" import { InstanceState } from "@/effect/instance-state" import { FileWatcher } from "@/file/watcher" import { Git } from "@/git" -import * as Log from "@opencode-ai/core/util/log" +import * as EffectLogger from "@opencode-ai/core/effect/logger" -const log = Log.create({ service: "vcs" }) +const log = EffectLogger.create({ service: "vcs" }) const PATCH_CONTEXT_LINES = 2_147_483_647 const MAX_PATCH_BYTES = 10_000_000 const MAX_TOTAL_PATCH_BYTES = 10_000_000 diff --git a/packages/opencode/src/provider/provider.ts b/packages/opencode/src/provider/provider.ts index 496a2f6d2d..652bda518a 100644 --- a/packages/opencode/src/provider/provider.ts +++ b/packages/opencode/src/provider/provider.ts @@ -3,7 +3,7 @@ import fuzzysort from "fuzzysort" import { Config } from "@/config/config" import { mapValues, mergeDeep, omit, pickBy, sortBy } from "remeda" import { NoSuchModelError, type Provider as SDK } from "ai" -import * as Log from "@opencode-ai/core/util/log" +import * as EffectLogger from "@opencode-ai/core/effect/logger" import { Npm } from "@opencode-ai/core/npm" import { Hash } from "@opencode-ai/core/util/hash" import { Plugin } from "../plugin" @@ -29,7 +29,7 @@ import { ModelID, ProviderID } from "./schema" import { ModelStatus } from "./model-status" import { RuntimeFlags } from "@/effect/runtime-flags" -const log = Log.create({ service: "provider" }) +const log = EffectLogger.create({ service: "provider" }) function shouldUseCopilotResponsesApi(modelID: string): boolean { const match = /^gpt-(\d+)/.exec(modelID) diff --git a/packages/opencode/src/pty/index.ts b/packages/opencode/src/pty/index.ts index b5eab9ce36..49f55e8c56 100644 --- a/packages/opencode/src/pty/index.ts +++ b/packages/opencode/src/pty/index.ts @@ -7,12 +7,12 @@ import { lazy } from "@opencode-ai/core/util/lazy" import { Plugin } from "@/plugin" import { Shell } from "@/shell/shell" import type { Proc } from "#pty" -import * as Log from "@opencode-ai/core/util/log" +import * as EffectLogger from "@opencode-ai/core/effect/logger" import { PtyID } from "./schema" import { Effect, Layer, Context, Schema, Types } from "effect" import { NonNegativeInt, PositiveInt } from "@opencode-ai/core/schema" -const log = Log.create({ service: "pty" }) +const log = EffectLogger.create({ service: "pty" }) const BUFFER_LIMIT = 1024 * 1024 * 2 const BUFFER_CHUNK = 64 * 1024 diff --git a/packages/opencode/src/question/index.ts b/packages/opencode/src/question/index.ts index e03af848b0..8ee45aebb4 100644 --- a/packages/opencode/src/question/index.ts +++ b/packages/opencode/src/question/index.ts @@ -3,10 +3,10 @@ import { Bus } from "@/bus" import { BusEvent } from "@/bus/bus-event" import { InstanceState } from "@/effect/instance-state" import { SessionID, MessageID } from "@/session/schema" -import * as Log from "@opencode-ai/core/util/log" +import * as EffectLogger from "@opencode-ai/core/effect/logger" import { QuestionID } from "./schema" -const log = Log.create({ service: "question" }) +const log = EffectLogger.create({ service: "question" }) // Schemas — these are pure data; nothing checks class identity (see PR // description) so they're plain `Schema.Struct` + type alias. That lets diff --git a/packages/opencode/src/server/global-lifecycle.ts b/packages/opencode/src/server/global-lifecycle.ts index aa761a42b4..abe38022d6 100644 --- a/packages/opencode/src/server/global-lifecycle.ts +++ b/packages/opencode/src/server/global-lifecycle.ts @@ -1,10 +1,10 @@ import { GlobalBus } from "@/bus/global" import { InstanceStore } from "@/project/instance-store" -import * as Log from "@opencode-ai/core/util/log" +import * as EffectLogger from "@opencode-ai/core/effect/logger" import { Effect } from "effect" import { Event } from "./event" -const log = Log.create({ service: "server" }) +const log = EffectLogger.create({ service: "server" }) export const emitGlobalDisposed = Effect.sync(() => GlobalBus.emit("event", { diff --git a/packages/opencode/src/server/mdns.ts b/packages/opencode/src/server/mdns.ts index 581139ddc4..2e6e2bb957 100644 --- a/packages/opencode/src/server/mdns.ts +++ b/packages/opencode/src/server/mdns.ts @@ -1,7 +1,7 @@ -import * as Log from "@opencode-ai/core/util/log" +import * as EffectLogger from "@opencode-ai/core/effect/logger" import { Bonjour } from "bonjour-service" -const log = Log.create({ service: "mdns" }) +const log = EffectLogger.create({ service: "mdns" }) let bonjour: Bonjour | undefined let currentPort: number | undefined diff --git a/packages/opencode/src/server/routes/instance/httpapi/handlers/control.ts b/packages/opencode/src/server/routes/instance/httpapi/handlers/control.ts index e1ede2274b..eade360b40 100644 --- a/packages/opencode/src/server/routes/instance/httpapi/handlers/control.ts +++ b/packages/opencode/src/server/routes/instance/httpapi/handlers/control.ts @@ -1,6 +1,6 @@ import { Auth } from "@/auth" import { ProviderID } from "@/provider/schema" -import * as Log from "@opencode-ai/core/util/log" +import * as EffectLogger from "@opencode-ai/core/effect/logger" import { Effect } from "effect" import { HttpApiBuilder } from "effect/unstable/httpapi" import { RootHttpApi } from "../api" @@ -24,7 +24,7 @@ export const controlHandlers = HttpApiBuilder.group(RootHttpApi, "control", (han }) const log = Effect.fn("ControlHttpApi.log")(function* (ctx: { payload: typeof LogInput.Type }) { - const logger = Log.create({ service: ctx.payload.service }) + const logger = EffectLogger.create({ service: ctx.payload.service }) logger[ctx.payload.level](ctx.payload.message, ctx.payload.extra) return true }) diff --git a/packages/opencode/src/server/routes/instance/httpapi/handlers/event.ts b/packages/opencode/src/server/routes/instance/httpapi/handlers/event.ts index e770a7cfba..ad67e52870 100644 --- a/packages/opencode/src/server/routes/instance/httpapi/handlers/event.ts +++ b/packages/opencode/src/server/routes/instance/httpapi/handlers/event.ts @@ -1,5 +1,5 @@ import { Bus } from "@/bus" -import * as Log from "@opencode-ai/core/util/log" +import * as EffectLogger from "@opencode-ai/core/effect/logger" import { Effect } from "effect" import * as Stream from "effect/Stream" import { HttpServerResponse } from "effect/unstable/http" @@ -7,7 +7,7 @@ import { HttpApiBuilder } from "effect/unstable/httpapi" import * as Sse from "effect/unstable/encoding/Sse" import { EventApi } from "../groups/event" -const log = Log.create({ service: "server" }) +const log = EffectLogger.create({ service: "server" }) function eventData(data: unknown): Sse.Event { return { diff --git a/packages/opencode/src/server/routes/instance/httpapi/handlers/global.ts b/packages/opencode/src/server/routes/instance/httpapi/handlers/global.ts index f80869b64d..cb1c611659 100644 --- a/packages/opencode/src/server/routes/instance/httpapi/handlers/global.ts +++ b/packages/opencode/src/server/routes/instance/httpapi/handlers/global.ts @@ -5,7 +5,7 @@ import { Bus } from "@/bus" import { Installation } from "@/installation" import { disposeAllInstancesAndEmitGlobalDisposed } from "@/server/global-lifecycle" import { InstallationVersion } from "@opencode-ai/core/installation/version" -import * as Log from "@opencode-ai/core/util/log" +import * as EffectLogger from "@opencode-ai/core/effect/logger" import { Effect, Queue, Schema } from "effect" import * as Stream from "effect/Stream" import { HttpServerRequest, HttpServerResponse } from "effect/unstable/http" @@ -14,7 +14,7 @@ import * as Sse from "effect/unstable/encoding/Sse" import { RootHttpApi } from "../api" import { GlobalUpgradeInput } from "../groups/global" -const log = Log.create({ service: "server" }) +const log = EffectLogger.create({ service: "server" }) function eventData(data: unknown): Sse.Event { return { diff --git a/packages/opencode/src/server/routes/instance/httpapi/handlers/sync.ts b/packages/opencode/src/server/routes/instance/httpapi/handlers/sync.ts index ffe8d0baa4..9b4df97f27 100644 --- a/packages/opencode/src/server/routes/instance/httpapi/handlers/sync.ts +++ b/packages/opencode/src/server/routes/instance/httpapi/handlers/sync.ts @@ -14,9 +14,9 @@ import { Effect, Scope } from "effect" import { HttpApiBuilder, HttpApiError } from "effect/unstable/httpapi" import { InstanceHttpApi } from "../api" import { HistoryPayload, ReplayPayload, SessionPayload } from "../groups/sync" -import * as Log from "@opencode-ai/core/util/log" +import * as EffectLogger from "@opencode-ai/core/effect/logger" -const log = Log.create({ service: "server.sync" }) +const log = EffectLogger.create({ service: "server.sync" }) export const syncHandlers = HttpApiBuilder.group(InstanceHttpApi, "sync", (handlers) => Effect.gen(function* () { diff --git a/packages/opencode/src/server/routes/instance/httpapi/lifecycle.ts b/packages/opencode/src/server/routes/instance/httpapi/lifecycle.ts index 30347d85ff..b7192fcf73 100644 --- a/packages/opencode/src/server/routes/instance/httpapi/lifecycle.ts +++ b/packages/opencode/src/server/routes/instance/httpapi/lifecycle.ts @@ -1,11 +1,11 @@ import { EffectBridge } from "@/effect/bridge" import type { InstanceContext } from "@/project/instance-context" import { InstanceStore } from "@/project/instance-store" -import * as Log from "@opencode-ai/core/util/log" +import * as EffectLogger from "@opencode-ai/core/effect/logger" import { Effect } from "effect" import { HttpEffect, HttpMiddleware, HttpServerRequest } from "effect/unstable/http" -const log = Log.create({ service: "server" }) +const log = EffectLogger.create({ service: "server" }) type MarkedInstance = { ctx: InstanceContext diff --git a/packages/opencode/src/server/routes/instance/httpapi/middleware/error.ts b/packages/opencode/src/server/routes/instance/httpapi/middleware/error.ts index 553e4de968..034be4864a 100644 --- a/packages/opencode/src/server/routes/instance/httpapi/middleware/error.ts +++ b/packages/opencode/src/server/routes/instance/httpapi/middleware/error.ts @@ -1,9 +1,9 @@ import { NamedError } from "@opencode-ai/core/util/error" -import * as Log from "@opencode-ai/core/util/log" +import * as EffectLogger from "@opencode-ai/core/effect/logger" import { Cause, Effect } from "effect" import { HttpRouter, HttpServerError, HttpServerRespondable, HttpServerResponse } from "effect/unstable/http" -const log = Log.create({ service: "server" }) +const log = EffectLogger.create({ service: "server" }) // Keep typed HttpApi failures on their declared error path; this boundary only replaces defect-only empty 500s. export const errorLayer = HttpRouter.middleware<{ handles: unknown }>()((effect) => diff --git a/packages/opencode/src/server/routes/instance/httpapi/middleware/schema-error.ts b/packages/opencode/src/server/routes/instance/httpapi/middleware/schema-error.ts index 18fa4065d7..7769a69741 100644 --- a/packages/opencode/src/server/routes/instance/httpapi/middleware/schema-error.ts +++ b/packages/opencode/src/server/routes/instance/httpapi/middleware/schema-error.ts @@ -1,10 +1,10 @@ import { Effect } from "effect" import { HttpServerResponse } from "effect/unstable/http" import { HttpApiMiddleware } from "effect/unstable/httpapi" -import * as Log from "@opencode-ai/core/util/log" +import * as EffectLogger from "@opencode-ai/core/effect/logger" import { InvalidRequestError } from "../errors" -const log = Log.create({ service: "server" }) +const log = EffectLogger.create({ service: "server" }) // Effect's Issue formatter recursively dumps the rejected `actual` value with // no truncation, so a 5KB invalid array produces a ~360KB string. Cap to keep diff --git a/packages/opencode/src/server/server.ts b/packages/opencode/src/server/server.ts index 9a448b78d6..db8736b7c9 100644 --- a/packages/opencode/src/server/server.ts +++ b/packages/opencode/src/server/server.ts @@ -1,7 +1,7 @@ import "./init-projectors" import { NodeHttpServer } from "@effect/platform-node" -import * as Log from "@opencode-ai/core/util/log" +import * as EffectLogger from "@opencode-ai/core/effect/logger" import { ConfigProvider, Context, Effect, Exit, Layer, Scope } from "effect" import { HttpRouter, HttpServer } from "effect/unstable/http" import { OpenApi } from "effect/unstable/httpapi" @@ -17,7 +17,7 @@ import { lazy } from "@/util/lazy" // @ts-ignore This global is needed to prevent ai-sdk from logging warnings to stdout https://github.com/vercel/ai/blob/2dc67e0ef538307f21368db32d5a12345d98831b/packages/ai/src/logger/log-warnings.ts#L85 globalThis.AI_SDK_LOG_WARNINGS = false -const log = Log.create({ service: "server" }) +const log = EffectLogger.create({ service: "server" }) export type Listener = { hostname: string diff --git a/packages/opencode/src/server/shared/fence.ts b/packages/opencode/src/server/shared/fence.ts index 770e4588bf..4a39e9bcc4 100644 --- a/packages/opencode/src/server/shared/fence.ts +++ b/packages/opencode/src/server/shared/fence.ts @@ -3,12 +3,12 @@ import { inArray } from "drizzle-orm" import { EventSequenceTable } from "@/sync/event.sql" import { Workspace } from "@/control-plane/workspace" import type { WorkspaceID } from "@/control-plane/schema" -import * as Log from "@opencode-ai/core/util/log" +import * as EffectLogger from "@opencode-ai/core/effect/logger" import { Effect } from "effect" export const HEADER = "x-opencode-sync" export type State = Record -const log = Log.create({ service: "fence" }) +const log = EffectLogger.create({ service: "fence" }) export function load(ids?: string[]) { const rows = Database.use((db) => { diff --git a/packages/opencode/src/session/compaction.ts b/packages/opencode/src/session/compaction.ts index 4f87edf64a..efd1a65957 100644 --- a/packages/opencode/src/session/compaction.ts +++ b/packages/opencode/src/session/compaction.ts @@ -5,7 +5,7 @@ import { SessionID, MessageID, PartID } from "./schema" import { Provider } from "@/provider/provider" import { MessageV2 } from "./message-v2" import { Token } from "@/util/token" -import * as Log from "@opencode-ai/core/util/log" +import * as EffectLogger from "@opencode-ai/core/effect/logger" import { SessionProcessor } from "./processor" import { Agent } from "@/agent/agent" import { Plugin } from "@/plugin" @@ -21,7 +21,7 @@ import { RuntimeFlags } from "@/effect/runtime-flags" import { EventV2Bridge } from "@/event-v2-bridge" import { SessionEvent } from "@opencode-ai/core/session-event" -const log = Log.create({ service: "session.compaction" }) +const log = EffectLogger.create({ service: "session.compaction" }) export const Event = { Compacted: BusEvent.define( diff --git a/packages/opencode/src/session/llm.ts b/packages/opencode/src/session/llm.ts index ea2efc99d0..7d774fdab7 100644 --- a/packages/opencode/src/session/llm.ts +++ b/packages/opencode/src/session/llm.ts @@ -1,6 +1,6 @@ import { Provider } from "@/provider/provider" import { serviceUse } from "@opencode-ai/core/effect/service-use" -import * as Log from "@opencode-ai/core/util/log" +import * as EffectLogger from "@opencode-ai/core/effect/logger" import { Context, Effect, Layer } from "effect" import * as Stream from "effect/Stream" import { streamText, wrapLanguageModel, type ModelMessage, type Tool } from "ai" @@ -27,7 +27,7 @@ import { LLMAISDK } from "./llm/ai-sdk" import { LLMNativeRuntime } from "./llm/native-runtime" import { LLMRequestPrep } from "./llm/request" -const log = Log.create({ service: "llm" }) +const log = EffectLogger.create({ service: "llm" }) export const OUTPUT_TOKEN_MAX = ProviderTransform.OUTPUT_TOKEN_MAX export type StreamInput = { diff --git a/packages/opencode/src/session/processor.ts b/packages/opencode/src/session/processor.ts index a287c3b006..bedba0e980 100644 --- a/packages/opencode/src/session/processor.ts +++ b/packages/opencode/src/session/processor.ts @@ -19,7 +19,7 @@ import { SessionSummary } from "./summary" import type { Provider } from "@/provider/provider" import { Question } from "@/question" import { errorMessage } from "@/util/error" -import * as Log from "@opencode-ai/core/util/log" +import * as EffectLogger from "@opencode-ai/core/effect/logger" import { isRecord } from "@/util/record" import { EventV2Bridge } from "@/event-v2-bridge" import { SessionEvent } from "@opencode-ai/core/session-event" @@ -30,7 +30,7 @@ import { RuntimeFlags } from "@/effect/runtime-flags" import { Usage, type LLMEvent } from "@opencode-ai/llm" const DOOM_LOOP_THRESHOLD = 3 -const log = Log.create({ service: "session.processor" }) +const log = EffectLogger.create({ service: "session.processor" }) export type Result = "compact" | "stop" | "continue" diff --git a/packages/opencode/src/session/projectors.ts b/packages/opencode/src/session/projectors.ts index 3dd848c5bc..01db1e34d8 100644 --- a/packages/opencode/src/session/projectors.ts +++ b/packages/opencode/src/session/projectors.ts @@ -8,10 +8,10 @@ import * as Session from "./session" import { MessageV2 } from "./message-v2" import { SessionTable, MessageTable, PartTable } from "./session.sql" import { WorkspaceTable } from "@/control-plane/workspace.sql" -import { Log } from "@opencode-ai/core/util/log" +import * as EffectLogger from "@opencode-ai/core/effect/logger" import nextProjectors from "./projectors-next" -const log = Log.create({ service: "session.projector" }) +const log = EffectLogger.create({ service: "session.projector" }) function foreign(err: unknown) { if (typeof err !== "object" || err === null) return false diff --git a/packages/opencode/src/session/prompt.ts b/packages/opencode/src/session/prompt.ts index 22fe4d81cd..ae9b40e6f2 100644 --- a/packages/opencode/src/session/prompt.ts +++ b/packages/opencode/src/session/prompt.ts @@ -2,7 +2,7 @@ import path from "path" import os from "os" import { SessionID, MessageID, PartID } from "./schema" import { MessageV2 } from "./message-v2" -import * as Log from "@opencode-ai/core/util/log" +import * as EffectLogger from "@opencode-ai/core/effect/logger" import { SessionRevert } from "./revert" import * as Session from "./session" import { Agent } from "../agent/agent" @@ -42,7 +42,6 @@ import { Image } from "@/image/image" import { decodeDataUrl } from "@/util/data-url" import { Process } from "@/util/process" import { Cause, Effect, Exit, Latch, Layer, Option, Scope, Context, Schema, Types } from "effect" -import * as EffectLogger from "@opencode-ai/core/effect/logger" import { InstanceState } from "@/effect/instance-state" import { TaskTool, type TaskPromptOps } from "@/tool/task" import { SessionRunState } from "./run-state" @@ -78,7 +77,7 @@ IMPORTANT: const STRUCTURED_OUTPUT_SYSTEM_PROMPT = `IMPORTANT: The user has requested structured output. You MUST use the StructuredOutput tool to provide your final response. Do NOT respond with plain text - you MUST call the StructuredOutput tool with your answer formatted according to the schema.` -const log = Log.create({ service: "session.prompt" }) +const log = EffectLogger.create({ service: "session.prompt" }) const elog = EffectLogger.create({ service: "session.prompt" }) function isOrphanedInterruptedTool(part: MessageV2.ToolPart) { diff --git a/packages/opencode/src/session/revert.ts b/packages/opencode/src/session/revert.ts index 950d533a3d..a4d685d21a 100644 --- a/packages/opencode/src/session/revert.ts +++ b/packages/opencode/src/session/revert.ts @@ -3,14 +3,14 @@ import { Bus } from "../bus" import { Snapshot } from "../snapshot" import { Storage } from "@/storage/storage" import { SyncEvent } from "../sync" -import * as Log from "@opencode-ai/core/util/log" +import * as EffectLogger from "@opencode-ai/core/effect/logger" import * as Session from "./session" import { MessageV2 } from "./message-v2" import { SessionID, MessageID, PartID } from "./schema" import { SessionRunState } from "./run-state" import { SessionSummary } from "./summary" -const log = Log.create({ service: "session.revert" }) +const log = EffectLogger.create({ service: "session.revert" }) export const RevertInput = Schema.Struct({ sessionID: SessionID, diff --git a/packages/opencode/src/session/session.ts b/packages/opencode/src/session/session.ts index f75ac910d4..da90c2a480 100644 --- a/packages/opencode/src/session/session.ts +++ b/packages/opencode/src/session/session.ts @@ -24,7 +24,7 @@ import type { SQL } from "drizzle-orm" import { PartTable, SessionTable } from "./session.sql" import { ProjectTable } from "../project/project.sql" import { Storage } from "@/storage/storage" -import * as Log from "@opencode-ai/core/util/log" +import * as EffectLogger from "@opencode-ai/core/effect/logger" import { MessageV2 } from "./message-v2" import type { InstanceContext } from "../project/instance-context" import { InstanceState } from "@/effect/instance-state" @@ -41,7 +41,7 @@ import { Effect, Layer, Option, Context, Schema, Types } from "effect" import { NonNegativeInt, optionalOmitUndefined } from "@opencode-ai/core/schema" import { RuntimeFlags } from "@/effect/runtime-flags" -const log = Log.create({ service: "session" }) +const log = EffectLogger.create({ service: "session" }) const parentTitlePrefix = "New session - " const childTitlePrefix = "Child session - " diff --git a/packages/opencode/src/session/tools.ts b/packages/opencode/src/session/tools.ts index f45df9d0fa..bd29310edd 100644 --- a/packages/opencode/src/session/tools.ts +++ b/packages/opencode/src/session/tools.ts @@ -16,10 +16,10 @@ import { MessageV2 } from "./message-v2" import * as Session from "./session" import { SessionProcessor } from "./processor" import { PartID } from "./schema" -import * as Log from "@opencode-ai/core/util/log" +import * as EffectLogger from "@opencode-ai/core/effect/logger" import { EffectBridge } from "@/effect/bridge" -const log = Log.create({ service: "session.tools" }) +const log = EffectLogger.create({ service: "session.tools" }) export const resolve = Effect.fn("SessionTools.resolve")(function* (input: { agent: Agent.Info diff --git a/packages/opencode/src/share/share-next.ts b/packages/opencode/src/share/share-next.ts index ab2d9d151d..c0bbb58da1 100644 --- a/packages/opencode/src/share/share-next.ts +++ b/packages/opencode/src/share/share-next.ts @@ -13,10 +13,10 @@ import type { SessionID } from "@/session/schema" import { Database } from "@/storage/db" import { eq } from "drizzle-orm" import { Config } from "@/config/config" -import * as Log from "@opencode-ai/core/util/log" +import * as EffectLogger from "@opencode-ai/core/effect/logger" import { SessionShareTable } from "./share.sql" -const log = Log.create({ service: "share-next" }) +const log = EffectLogger.create({ service: "share-next" }) const disabled = process.env["OPENCODE_DISABLE_SHARE"] === "true" || process.env["OPENCODE_DISABLE_SHARE"] === "1" export type Api = { diff --git a/packages/opencode/src/skill/discovery.ts b/packages/opencode/src/skill/discovery.ts index 9db70ba11b..4c6c28089d 100644 --- a/packages/opencode/src/skill/discovery.ts +++ b/packages/opencode/src/skill/discovery.ts @@ -4,7 +4,7 @@ import { FetchHttpClient, HttpClient, HttpClientRequest, HttpClientResponse } fr import { withTransientReadRetry } from "@/util/effect-http-client" import { AppFileSystem } from "@opencode-ai/core/filesystem" import { Global } from "@opencode-ai/core/global" -import * as Log from "@opencode-ai/core/util/log" +import * as EffectLogger from "@opencode-ai/core/effect/logger" const skillConcurrency = 4 const fileConcurrency = 8 @@ -28,7 +28,7 @@ export const layer: Layer.Layer diff --git a/packages/opencode/src/storage/json-migration.ts b/packages/opencode/src/storage/json-migration.ts index 3930e591a4..27917790db 100644 --- a/packages/opencode/src/storage/json-migration.ts +++ b/packages/opencode/src/storage/json-migration.ts @@ -1,7 +1,7 @@ import type { SQLiteBunDatabase } from "drizzle-orm/bun-sqlite" import type { NodeSQLiteDatabase } from "drizzle-orm/node-sqlite" import { Global } from "@opencode-ai/core/global" -import * as Log from "@opencode-ai/core/util/log" +import * as EffectLogger from "@opencode-ai/core/effect/logger" import { ProjectTable } from "../project/project.sql" import { SessionTable, MessageTable, PartTable, TodoTable, PermissionTable } from "../session/session.sql" import { SessionShareTable } from "../share/share.sql" @@ -10,7 +10,7 @@ import { existsSync } from "fs" import { Filesystem } from "@/util/filesystem" import { Glob } from "@opencode-ai/core/util/glob" -const log = Log.create({ service: "json-migration" }) +const log = EffectLogger.create({ service: "json-migration" }) export type Progress = { current: number diff --git a/packages/opencode/src/storage/storage.ts b/packages/opencode/src/storage/storage.ts index 706c24eae2..36042df929 100644 --- a/packages/opencode/src/storage/storage.ts +++ b/packages/opencode/src/storage/storage.ts @@ -1,4 +1,4 @@ -import * as Log from "@opencode-ai/core/util/log" +import * as EffectLogger from "@opencode-ai/core/effect/logger" import path from "path" import { Global } from "@opencode-ai/core/global" import { AppFileSystem } from "@opencode-ai/core/filesystem" @@ -6,7 +6,7 @@ import { Effect, Exit, Layer, Option, RcMap, Schema, Context, TxReentrantLock } import { NonNegativeInt } from "@opencode-ai/core/schema" import { Git } from "@/git" -const log = Log.create({ service: "storage" }) +const log = EffectLogger.create({ service: "storage" }) type Migration = ( dir: string, diff --git a/packages/opencode/src/temporary.ts b/packages/opencode/src/temporary.ts index f0325f0cc9..8949507368 100644 --- a/packages/opencode/src/temporary.ts +++ b/packages/opencode/src/temporary.ts @@ -2,9 +2,9 @@ import yargs from "yargs" import { TuiThreadCommand } from "./cli/cmd/tui/thread" import { InstallationVersion } from "@opencode-ai/core/installation/version" import { hideBin } from "yargs/helpers" -import { Log } from "./node" const args = hideBin(process.argv) +type LogLevel = "DEBUG" | "INFO" | "WARN" | "ERROR" function flag(name: string) { const index = args.indexOf(name) @@ -13,16 +13,16 @@ function flag(name: string) { return value?.slice(name.length + 1) } -function parseLogLevel(value: string | undefined): Log.Level | undefined { +function parseLogLevel(value: string | undefined): LogLevel | undefined { if (value === "DEBUG" || value === "INFO" || value === "WARN" || value === "ERROR") return value return undefined } -Log.init({ - print: args.includes("--print-logs"), - file: flag("--log-file"), - level: parseLogLevel(flag("--log-level")), -}) +if (args.includes("--print-logs")) process.env.OPENCODE_PRINT_LOGS = "1" +const logFile = flag("--log-file") +if (logFile) process.env.OPENCODE_LOG_FILE = logFile +const logLevel = parseLogLevel(flag("--log-level")) +if (logLevel) process.env.OPENCODE_LOG_LEVEL = logLevel const cli = yargs(args) .parserConfiguration({ "populate--": true }) diff --git a/packages/opencode/src/tool/registry.ts b/packages/opencode/src/tool/registry.ts index d7f7de778e..508106b86e 100644 --- a/packages/opencode/src/tool/registry.ts +++ b/packages/opencode/src/tool/registry.ts @@ -25,7 +25,7 @@ import { WebSearchTool } from "./websearch" import { RepoCloneTool } from "./repo_clone" import { RepoOverviewTool } from "./repo_overview" import { RepositoryCache } from "@/reference/repository-cache" -import * as Log from "@opencode-ai/core/util/log" +import * as EffectLogger from "@opencode-ai/core/effect/logger" import { LspTool } from "./lsp" import * as Truncate from "./truncate" import { ApplyPatchTool } from "./apply_patch" @@ -54,7 +54,7 @@ import { Reference } from "@/reference/reference" import { BackgroundJob } from "@/background/job" import { RuntimeFlags } from "@/effect/runtime-flags" -const log = Log.create({ service: "tool.registry" }) +const log = EffectLogger.create({ service: "tool.registry" }) export function webSearchEnabled(providerID: ProviderID, flags = { exa: false, parallel: false }) { return providerID === ProviderID.opencode || flags.exa || flags.parallel diff --git a/packages/opencode/src/tool/shell.ts b/packages/opencode/src/tool/shell.ts index b6a95b5c09..b17e427372 100644 --- a/packages/opencode/src/tool/shell.ts +++ b/packages/opencode/src/tool/shell.ts @@ -3,7 +3,7 @@ import os from "os" import { createWriteStream } from "node:fs" import * as Tool from "./tool" import path from "path" -import * as Log from "@opencode-ai/core/util/log" +import * as EffectLogger from "@opencode-ai/core/effect/logger" import { containsPath, type InstanceContext } from "../project/instance-context" import { InstanceState } from "@/effect/instance-state" import { lazy } from "@/util/lazy" @@ -82,7 +82,7 @@ type Chunk = { size: number } -export const log = Log.create({ service: "shell-tool" }) +export const log = EffectLogger.create({ service: "shell-tool" }) const resolveWasm = (asset: string) => { if (asset.startsWith("file://")) return fileURLToPath(asset) diff --git a/packages/opencode/src/tool/truncate.ts b/packages/opencode/src/tool/truncate.ts index ffc16c0b9f..4159c62aae 100644 --- a/packages/opencode/src/tool/truncate.ts +++ b/packages/opencode/src/tool/truncate.ts @@ -6,11 +6,11 @@ import { AppFileSystem } from "@opencode-ai/core/filesystem" import { evaluate } from "@/permission/evaluate" import { Config } from "@/config/config" import { Identifier } from "../id/id" -import * as Log from "@opencode-ai/core/util/log" +import * as EffectLogger from "@opencode-ai/core/effect/logger" import { ToolID } from "./schema" import { TRUNCATION_DIR } from "./truncation-dir" -const log = Log.create({ service: "truncation" }) +const log = EffectLogger.create({ service: "truncation" }) const RETENTION = Duration.days(7) export const MAX_LINES = 2000 diff --git a/packages/opencode/src/worktree/index.ts b/packages/opencode/src/worktree/index.ts index a1d4f89c2a..6494fb9dc1 100644 --- a/packages/opencode/src/worktree/index.ts +++ b/packages/opencode/src/worktree/index.ts @@ -6,7 +6,7 @@ import { Database } from "@/storage/db" import { eq } from "drizzle-orm" import { ProjectTable } from "../project/project.sql" import type { ProjectID } from "../project/schema" -import * as Log from "@opencode-ai/core/util/log" +import * as EffectLogger from "@opencode-ai/core/effect/logger" import { Slug } from "@opencode-ai/core/util/slug" import { errorMessage } from "../util/error" import { BusEvent } from "@/bus/bus-event" @@ -19,7 +19,7 @@ import { AppFileSystem } from "@opencode-ai/core/filesystem" import { AppProcess } from "@opencode-ai/core/process" import { InstanceState } from "@/effect/instance-state" -const log = Log.create({ service: "worktree" }) +const log = EffectLogger.create({ service: "worktree" }) export const Event = { Ready: BusEvent.define( diff --git a/packages/opencode/test/control-plane/workspace.test.ts b/packages/opencode/test/control-plane/workspace.test.ts index 09810d57d7..09f284f0be 100644 --- a/packages/opencode/test/control-plane/workspace.test.ts +++ b/packages/opencode/test/control-plane/workspace.test.ts @@ -8,7 +8,6 @@ import { Effect, Exit, Fiber, Layer, Schema } from "effect" import { FetchHttpClient, HttpServer, HttpServerRequest, HttpServerResponse } from "effect/unstable/http" import { eq } from "drizzle-orm" import { AppFileSystem } from "@opencode-ai/core/filesystem" -import * as Log from "@opencode-ai/core/util/log" import { GlobalBus, type GlobalEvent } from "@/bus/global" import { Database } from "@/storage/db" import { ProjectID } from "@/project/schema" @@ -34,8 +33,6 @@ import { Project } from "@/project/project" import { Vcs } from "@/project/vcs" import { RuntimeFlags } from "@/effect/runtime-flags" -void Log.init({ print: false }) - const originalEnv = { OPENCODE_AUTH_CONTENT: process.env.OPENCODE_AUTH_CONTENT, OPENCODE_EXPERIMENTAL_WORKSPACES: process.env.OPENCODE_EXPERIMENTAL_WORKSPACES, diff --git a/packages/opencode/test/lsp/client.test.ts b/packages/opencode/test/lsp/client.test.ts index 93844d942f..3fabacf9fa 100644 --- a/packages/opencode/test/lsp/client.test.ts +++ b/packages/opencode/test/lsp/client.test.ts @@ -1,10 +1,9 @@ -import { beforeEach, describe, expect, test } from "bun:test" +import { describe, expect, test } from "bun:test" import path from "path" import { pathToFileURL } from "url" import { tmpdir, withTestInstance } from "../fixture/fixture" import { LSPClient } from "@/lsp/client" import * as LSPServer from "@/lsp/server" -import * as Log from "@opencode-ai/core/util/log" function spawnFakeServer() { const { spawn } = require("child_process") @@ -17,10 +16,6 @@ function spawnFakeServer() { } describe("LSPClient interop", () => { - beforeEach(async () => { - await Log.init({ print: true }) - }) - test("handles workspace/workspaceFolders request", async () => { const handle = spawnFakeServer() as any diff --git a/packages/opencode/test/preload.ts b/packages/opencode/test/preload.ts index 24b804819e..014135241d 100644 --- a/packages/opencode/test/preload.ts +++ b/packages/opencode/test/preload.ts @@ -80,13 +80,8 @@ delete process.env["OPENCODE_SERVER_USERNAME"] process.env["OPENCODE_DB"] = ":memory:" // Now safe to import from src/ -const { Log } = await import("@opencode-ai/core/util/log") const { initProjectors } = await import("../src/server/projectors") - -void Log.init({ - print: false, - dev: true, - level: "DEBUG", -}) +process.env.OPENCODE_LOG_LEVEL = "DEBUG" +process.env.OPENCODE_LOG_FILE = "0" initProjectors() diff --git a/packages/opencode/test/project/migrate-global.test.ts b/packages/opencode/test/project/migrate-global.test.ts index 6efd670c5c..76cd96e061 100644 --- a/packages/opencode/test/project/migrate-global.test.ts +++ b/packages/opencode/test/project/migrate-global.test.ts @@ -6,15 +6,12 @@ import { SessionTable } from "../../src/session/session.sql" import { ProjectTable } from "../../src/project/project.sql" import { ProjectID } from "../../src/project/schema" import { SessionID } from "../../src/session/schema" -import * as Log from "@opencode-ai/core/util/log" import { $ } from "bun" import { tmpdirScoped } from "../fixture/fixture" import { Effect, Layer } from "effect" import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner" import { testEffect } from "../lib/effect" -void Log.init({ print: false }) - const it = testEffect(Layer.mergeAll(Project.defaultLayer, CrossSpawnSpawner.defaultLayer)) function legacySessionID() { diff --git a/packages/opencode/test/project/project.test.ts b/packages/opencode/test/project/project.test.ts index e636fef90e..dbff9ba416 100644 --- a/packages/opencode/test/project/project.test.ts +++ b/packages/opencode/test/project/project.test.ts @@ -1,7 +1,6 @@ import { describe, expect, test } from "bun:test" import { Bus } from "@/bus" import { Project } from "@/project/project" -import * as Log from "@opencode-ai/core/util/log" import { $ } from "bun" import path from "path" import { tmpdirScoped } from "../fixture/fixture" @@ -17,8 +16,6 @@ import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner" import { testEffect } from "../lib/effect" import { RuntimeFlags } from "@/effect/runtime-flags" -void Log.init({ print: false }) - const encoder = new TextEncoder() const layer = Layer.mergeAll(Project.defaultLayer, CrossSpawnSpawner.defaultLayer) diff --git a/packages/opencode/test/server/global-session-list.test.ts b/packages/opencode/test/server/global-session-list.test.ts index df49ae0841..c0132074c5 100644 --- a/packages/opencode/test/server/global-session-list.test.ts +++ b/packages/opencode/test/server/global-session-list.test.ts @@ -3,12 +3,9 @@ import { Deferred, Effect, Layer } from "effect" import { Project } from "@/project/project" import { Session as SessionNs } from "@/session/session" import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner" -import * as Log from "@opencode-ai/core/util/log" import { provideInstance, TestInstance, tmpdirScoped } from "../fixture/fixture" import { testEffect } from "../lib/effect" -void Log.init({ print: false }) - const it = testEffect(Layer.mergeAll(SessionNs.defaultLayer, Project.defaultLayer, CrossSpawnSpawner.defaultLayer)) const withSession = (input?: Parameters[0]) => diff --git a/packages/opencode/test/server/httpapi-compression.test.ts b/packages/opencode/test/server/httpapi-compression.test.ts index 4fcf8864fe..24ec8b6843 100644 --- a/packages/opencode/test/server/httpapi-compression.test.ts +++ b/packages/opencode/test/server/httpapi-compression.test.ts @@ -1,12 +1,9 @@ import { afterEach, describe, expect, test } from "bun:test" import { gunzipSync, inflateSync } from "node:zlib" -import * as Log from "@opencode-ai/core/util/log" import { Server } from "../../src/server/server" import { resetDatabase } from "../fixture/db" import { disposeAllInstances, tmpdir } from "../fixture/fixture" -void Log.init({ print: false }) - afterEach(async () => { await disposeAllInstances() await resetDatabase() diff --git a/packages/opencode/test/server/httpapi-config.test.ts b/packages/opencode/test/server/httpapi-config.test.ts index 7619a0c881..767bb6b5d5 100644 --- a/packages/opencode/test/server/httpapi-config.test.ts +++ b/packages/opencode/test/server/httpapi-config.test.ts @@ -1,15 +1,12 @@ import { afterEach, describe, expect } from "bun:test" import path from "path" import { Server } from "../../src/server/server" -import * as Log from "@opencode-ai/core/util/log" import { Effect, Fiber } from "effect" import { resetDatabase } from "../fixture/db" import { disposeAllInstances, tmpdir } from "../fixture/fixture" import { it } from "../lib/effect" import { waitGlobalBusEvent } from "./global-bus" -void Log.init({ print: false }) - function app() { return Server.Default().app } diff --git a/packages/opencode/test/server/httpapi-cors-vary.test.ts b/packages/opencode/test/server/httpapi-cors-vary.test.ts index 74a09cb253..15af5a1a97 100644 --- a/packages/opencode/test/server/httpapi-cors-vary.test.ts +++ b/packages/opencode/test/server/httpapi-cors-vary.test.ts @@ -1,11 +1,8 @@ import { afterEach, describe, expect, test } from "bun:test" -import * as Log from "@opencode-ai/core/util/log" import { Server } from "../../src/server/server" import { resetDatabase } from "../fixture/db" import { disposeAllInstances } from "../fixture/fixture" -void Log.init({ print: false }) - afterEach(async () => { await disposeAllInstances() await resetDatabase() diff --git a/packages/opencode/test/server/httpapi-event-diagnostics.test.ts b/packages/opencode/test/server/httpapi-event-diagnostics.test.ts index 66bd0bcedd..19f3885307 100644 --- a/packages/opencode/test/server/httpapi-event-diagnostics.test.ts +++ b/packages/opencode/test/server/httpapi-event-diagnostics.test.ts @@ -26,7 +26,6 @@ // subscription is established. Order-of-setup variable. import { afterEach, describe, expect } from "bun:test" import { Deferred, Effect, Layer, Schema } from "effect" -import * as Log from "@opencode-ai/core/util/log" import { Bus } from "../../src/bus" import { Event as ServerEvent } from "../../src/server/event" import { Server } from "../../src/server/server" @@ -38,8 +37,6 @@ import { resetDatabase } from "../fixture/db" import { disposeAllInstances, TestInstance } from "../fixture/fixture" import { testEffectShared } from "../lib/effect" -void Log.init({ print: false }) - const SseEvent = Schema.Struct({ id: Schema.optional(Schema.String), type: Schema.String, diff --git a/packages/opencode/test/server/httpapi-event.test.ts b/packages/opencode/test/server/httpapi-event.test.ts index 44d421ea0a..3e37c82fad 100644 --- a/packages/opencode/test/server/httpapi-event.test.ts +++ b/packages/opencode/test/server/httpapi-event.test.ts @@ -1,6 +1,5 @@ import { afterEach, describe, expect } from "bun:test" import { Effect, Schema } from "effect" -import * as Log from "@opencode-ai/core/util/log" import { Bus } from "../../src/bus" import { Event as ServerEvent } from "../../src/server/event" import { Server } from "../../src/server/server" @@ -9,8 +8,6 @@ import { resetDatabase } from "../fixture/db" import { disposeAllInstances, TestInstance } from "../fixture/fixture" import { testEffectShared } from "../lib/effect" -void Log.init({ print: false }) - const EventData = Schema.Struct({ id: Schema.optional(Schema.String), type: Schema.String, diff --git a/packages/opencode/test/server/httpapi-exercise/index.ts b/packages/opencode/test/server/httpapi-exercise/index.ts index f2b132cb73..898cf6a4bd 100644 --- a/packages/opencode/test/server/httpapi-exercise/index.ts +++ b/packages/opencode/test/server/httpapi-exercise/index.ts @@ -36,8 +36,6 @@ import { runScenario } from "./runner" import { runtime } from "./runtime" import { type Scenario } from "./types" -void (await import("@opencode-ai/core/util/log")).init({ print: false }) - function cursor(input: Record) { return Buffer.from(JSON.stringify(input)).toString("base64url") } diff --git a/packages/opencode/test/server/httpapi-experimental.test.ts b/packages/opencode/test/server/httpapi-experimental.test.ts index aa7e4946da..047845f710 100644 --- a/packages/opencode/test/server/httpapi-experimental.test.ts +++ b/packages/opencode/test/server/httpapi-experimental.test.ts @@ -7,14 +7,11 @@ import { ExperimentalPaths } from "../../src/server/routes/instance/httpapi/grou import { Session } from "@/session/session" import { SessionTable } from "@/session/session.sql" import { Database } from "@/storage/db" -import * as Log from "@opencode-ai/core/util/log" import { Worktree } from "../../src/worktree" import { resetDatabase } from "../fixture/db" import { disposeAllInstances, TestInstance } from "../fixture/fixture" import { testEffect } from "../lib/effect" -void Log.init({ print: false }) - const it = testEffect(Layer.mergeAll(Session.defaultLayer)) const testWorktreeMutations = process.platform === "win32" ? it.instance.skip : it.instance diff --git a/packages/opencode/test/server/httpapi-file.test.ts b/packages/opencode/test/server/httpapi-file.test.ts index b2403b9fb2..0b103ed0a7 100644 --- a/packages/opencode/test/server/httpapi-file.test.ts +++ b/packages/opencode/test/server/httpapi-file.test.ts @@ -3,12 +3,9 @@ import { Context } from "effect" import path from "path" import { HttpApiApp } from "../../src/server/routes/instance/httpapi/server" import { FilePaths } from "../../src/server/routes/instance/httpapi/groups/file" -import * as Log from "@opencode-ai/core/util/log" import { resetDatabase } from "../fixture/db" import { disposeAllInstances, tmpdir } from "../fixture/fixture" -void Log.init({ print: false }) - const context = Context.empty() as Context.Context function request(route: string, directory: string, query?: Record) { diff --git a/packages/opencode/test/server/httpapi-listen.test.ts b/packages/opencode/test/server/httpapi-listen.test.ts index 39b7100462..805c11d62a 100644 --- a/packages/opencode/test/server/httpapi-listen.test.ts +++ b/packages/opencode/test/server/httpapi-listen.test.ts @@ -1,15 +1,12 @@ import { afterEach, describe, expect, test } from "bun:test" import net from "node:net" import { Flag } from "@opencode-ai/core/flag/flag" -import * as Log from "@opencode-ai/core/util/log" import { Server } from "../../src/server/server" import { PtyPaths } from "../../src/server/routes/instance/httpapi/groups/pty" import { withTimeout } from "../../src/util/timeout" import { resetDatabase } from "../fixture/db" import { disposeAllInstances, tmpdir } from "../fixture/fixture" -void Log.init({ print: false }) - const original = { OPENCODE_SERVER_PASSWORD: Flag.OPENCODE_SERVER_PASSWORD, OPENCODE_SERVER_USERNAME: Flag.OPENCODE_SERVER_USERNAME, diff --git a/packages/opencode/test/server/httpapi-mcp.test.ts b/packages/opencode/test/server/httpapi-mcp.test.ts index 2cd5d2abba..6ee66ebbc1 100644 --- a/packages/opencode/test/server/httpapi-mcp.test.ts +++ b/packages/opencode/test/server/httpapi-mcp.test.ts @@ -3,13 +3,10 @@ import { Context, Effect, Layer } from "effect" import { HttpApiApp } from "../../src/server/routes/instance/httpapi/server" import { McpPaths } from "../../src/server/routes/instance/httpapi/groups/mcp" import { Server } from "../../src/server/server" -import * as Log from "@opencode-ai/core/util/log" import { resetDatabase } from "../fixture/db" import { TestInstance } from "../fixture/fixture" import { testEffect } from "../lib/effect" -void Log.init({ print: false }) - const context = Context.empty() as Context.Context const testStateLayer = Layer.effectDiscard( Effect.gen(function* () { diff --git a/packages/opencode/test/server/httpapi-mdns.test.ts b/packages/opencode/test/server/httpapi-mdns.test.ts index 6b88a1f10b..6dc122384e 100644 --- a/packages/opencode/test/server/httpapi-mdns.test.ts +++ b/packages/opencode/test/server/httpapi-mdns.test.ts @@ -1,12 +1,9 @@ import { afterEach, describe, expect, mock, test } from "bun:test" import { Flag } from "@opencode-ai/core/flag/flag" -import * as Log from "@opencode-ai/core/util/log" import { withTimeout } from "../../src/util/timeout" import { resetDatabase } from "../fixture/db" import { disposeAllInstances } from "../fixture/fixture" -void Log.init({ print: false }) - type Event = { kind: "publish"; port: number; name: string } | { kind: "unpublishAll" } | { kind: "destroy" } const events: Event[] = [] diff --git a/packages/opencode/test/server/httpapi-provider.test.ts b/packages/opencode/test/server/httpapi-provider.test.ts index 25181f3b2d..fd6e57106a 100644 --- a/packages/opencode/test/server/httpapi-provider.test.ts +++ b/packages/opencode/test/server/httpapi-provider.test.ts @@ -3,14 +3,11 @@ import { AppFileSystem } from "@opencode-ai/core/filesystem" import { Effect, Layer } from "effect" import path from "path" import { Server } from "../../src/server/server" -import * as Log from "@opencode-ai/core/util/log" import { resetDatabase } from "../fixture/db" import { TestInstance } from "../fixture/fixture" import { markPluginDependenciesReady } from "../fixture/plugin" import { testEffect } from "../lib/effect" -void Log.init({ print: false }) - const testStateLayer = Layer.effectDiscard( Effect.acquireRelease( Effect.promise(() => resetDatabase()), diff --git a/packages/opencode/test/server/httpapi-pty.test.ts b/packages/opencode/test/server/httpapi-pty.test.ts index 029cdb9582..46520862d1 100644 --- a/packages/opencode/test/server/httpapi-pty.test.ts +++ b/packages/opencode/test/server/httpapi-pty.test.ts @@ -3,7 +3,6 @@ import { NodeHttpServer, NodeServices } from "@effect/platform-node" import { PtyID } from "../../src/pty/schema" import { Server } from "../../src/server/server" import { PtyPaths } from "../../src/server/routes/instance/httpapi/groups/pty" -import * as Log from "@opencode-ai/core/util/log" import { resetDatabase } from "../fixture/db" import { disposeAllInstances, tmpdir, tmpdirScoped } from "../fixture/fixture" import { Config, Effect, Layer, Queue, Schema } from "effect" @@ -13,8 +12,6 @@ import { HttpApiApp } from "../../src/server/routes/instance/httpapi/server" import { Pty } from "../../src/pty" import { testEffect } from "../lib/effect" -void Log.init({ print: false }) - const testPty = process.platform === "win32" ? test.skip : test const testStateLayer = Layer.effectDiscard( diff --git a/packages/opencode/test/server/httpapi-raw-route-auth.test.ts b/packages/opencode/test/server/httpapi-raw-route-auth.test.ts index 7436c10817..b38d891672 100644 --- a/packages/opencode/test/server/httpapi-raw-route-auth.test.ts +++ b/packages/opencode/test/server/httpapi-raw-route-auth.test.ts @@ -7,9 +7,6 @@ import { HttpApiApp } from "../../src/server/routes/instance/httpapi/server" import { PtyID } from "../../src/pty/schema" import { resetDatabase } from "../fixture/db" import { disposeAllInstances, tmpdir } from "../fixture/fixture" -import * as Log from "@opencode-ai/core/util/log" - -void Log.init({ print: false }) function app(input: { password?: string; username?: string }) { const handler = HttpRouter.toWebHandler( diff --git a/packages/opencode/test/server/httpapi-session.test.ts b/packages/opencode/test/server/httpapi-session.test.ts index 5c8f3fb24e..f8fd7f80b2 100644 --- a/packages/opencode/test/server/httpapi-session.test.ts +++ b/packages/opencode/test/server/httpapi-session.test.ts @@ -24,14 +24,11 @@ import { SessionMessage } from "@opencode-ai/core/session-message" import { ModelV2 } from "@opencode-ai/core/model" import { ProviderV2 } from "@opencode-ai/core/provider" import * as DateTime from "effect/DateTime" -import * as Log from "@opencode-ai/core/util/log" import { eq } from "drizzle-orm" import { resetDatabase } from "../fixture/db" import { disposeAllInstances, TestInstance } from "../fixture/fixture" import { testEffect } from "../lib/effect" -void Log.init({ print: false }) - const originalWorkspaces = Flag.OPENCODE_EXPERIMENTAL_WORKSPACES const workspaceLayer = Workspace.defaultLayer.pipe( Layer.provide(InstanceStore.defaultLayer), diff --git a/packages/opencode/test/server/httpapi-sync.test.ts b/packages/opencode/test/server/httpapi-sync.test.ts index 6a1c1624cc..8d590eee57 100644 --- a/packages/opencode/test/server/httpapi-sync.test.ts +++ b/packages/opencode/test/server/httpapi-sync.test.ts @@ -1,17 +1,14 @@ -import { afterEach, describe, expect, mock, spyOn } from "bun:test" +import { afterEach, describe, expect, mock } from "bun:test" import { Context, Effect } from "effect" import { Flag } from "@opencode-ai/core/flag/flag" import { Server } from "../../src/server/server" import { SyncPaths } from "../../src/server/routes/instance/httpapi/groups/sync" import { HttpApiApp } from "../../src/server/routes/instance/httpapi/server" import { Session } from "@/session/session" -import * as Log from "@opencode-ai/core/util/log" import { resetDatabase } from "../fixture/db" import { disposeAllInstances, TestInstance } from "../fixture/fixture" import { testEffect } from "../lib/effect" -void Log.init({ print: false }) - const originalWorkspaces = Flag.OPENCODE_EXPERIMENTAL_WORKSPACES const context = Context.empty() as Context.Context const it = testEffect(Session.defaultLayer) @@ -35,7 +32,6 @@ describe("sync HttpApi", () => { Flag.OPENCODE_EXPERIMENTAL_WORKSPACES = true const tmp = yield* TestInstance const headers = { "x-opencode-directory": tmp.directory, "content-type": "application/json" } - const info = spyOn(Log.create({ service: "server.sync" }), "info") const session = yield* Session.use.create({ title: "sync" }) const started = yield* Effect.promise(() => @@ -85,8 +81,6 @@ describe("sync HttpApi", () => { ) expect(replayed.status).toBe(200) expect(yield* Effect.promise(() => replayed.json())).toEqual({ sessionID: session.id }) - expect(info.mock.calls.some(([message]) => message === "sync replay requested")).toBe(true) - expect(info.mock.calls.some(([message]) => message === "sync replay complete")).toBe(true) }), { git: true, config: { formatter: false, lsp: false } }, ) diff --git a/packages/opencode/test/server/httpapi-ui.test.ts b/packages/opencode/test/server/httpapi-ui.test.ts index 1ffa0d2005..e751aa52a0 100644 --- a/packages/opencode/test/server/httpapi-ui.test.ts +++ b/packages/opencode/test/server/httpapi-ui.test.ts @@ -1,7 +1,6 @@ import { createHash } from "node:crypto" import { describe, expect } from "bun:test" import { Flag } from "@opencode-ai/core/flag/flag" -import * as Log from "@opencode-ai/core/util/log" import { ConfigProvider, Effect, Layer } from "effect" import { HttpClient, @@ -20,8 +19,6 @@ import { HttpApiApp } from "../../src/server/routes/instance/httpapi/server" import { serveEmbeddedUIEffect, serveUIEffect } from "../../src/server/shared/ui" import { testEffect } from "../lib/effect" -void Log.init({ print: false }) - const testStateLayer = Layer.effectDiscard( Effect.gen(function* () { const original = { diff --git a/packages/opencode/test/server/httpapi-workspace.test.ts b/packages/opencode/test/server/httpapi-workspace.test.ts index 2e10d325f6..4fbb6fdb45 100644 --- a/packages/opencode/test/server/httpapi-workspace.test.ts +++ b/packages/opencode/test/server/httpapi-workspace.test.ts @@ -10,7 +10,6 @@ import type { WorkspaceAdapter } from "../../src/control-plane/types" import { Workspace } from "../../src/control-plane/workspace" import { WorkspacePaths } from "../../src/server/routes/instance/httpapi/groups/workspace" import { Session } from "@/session/session" -import * as Log from "@opencode-ai/core/util/log" import { Server } from "../../src/server/server" import { resetDatabase } from "../fixture/db" import { disposeAllInstances, provideInstance, tmpdirScoped } from "../fixture/fixture" @@ -21,8 +20,6 @@ import { InstancePaths } from "../../src/server/routes/instance/httpapi/groups/i import { WorkspaceRef } from "../../src/effect/instance-ref" import { testEffect } from "../lib/effect" -void Log.init({ print: false }) - const originalWorkspaces = Flag.OPENCODE_EXPERIMENTAL_WORKSPACES const workspaceLayer = Workspace.defaultLayer.pipe( Layer.provide(InstanceStore.defaultLayer), diff --git a/packages/opencode/test/server/project-init-git.test.ts b/packages/opencode/test/server/project-init-git.test.ts index b22777861b..60a5f62fdf 100644 --- a/packages/opencode/test/server/project-init-git.test.ts +++ b/packages/opencode/test/server/project-init-git.test.ts @@ -8,13 +8,10 @@ import { InstanceStore } from "../../src/project/instance-store" import { GlobalBus, type GlobalEvent } from "../../src/bus/global" import { Snapshot } from "../../src/snapshot" import { Server } from "../../src/server/server" -import * as Log from "@opencode-ai/core/util/log" import { resetDatabase } from "../fixture/db" import { disposeAllInstances, TestInstance } from "../fixture/fixture" import { testEffect } from "../lib/effect" -void Log.init({ print: false }) - afterEach(async () => { await disposeAllInstances() await resetDatabase() diff --git a/packages/opencode/test/server/sdk-error-shape.test.ts b/packages/opencode/test/server/sdk-error-shape.test.ts index f41fe2cb30..1d67c6958a 100644 --- a/packages/opencode/test/server/sdk-error-shape.test.ts +++ b/packages/opencode/test/server/sdk-error-shape.test.ts @@ -10,12 +10,9 @@ import { afterEach, describe, expect, test } from "bun:test" import { createOpencodeClient } from "@opencode-ai/sdk/v2" import { Server } from "../../src/server/server" -import * as Log from "@opencode-ai/core/util/log" import { disposeAllInstances, tmpdir } from "../fixture/fixture" import { resetDatabase } from "../fixture/db" -void Log.init({ print: false }) - afterEach(async () => { await disposeAllInstances() await resetDatabase() diff --git a/packages/opencode/test/server/sdk-v1-smoke.test.ts b/packages/opencode/test/server/sdk-v1-smoke.test.ts index 2b09e0c872..0e771efcbf 100644 --- a/packages/opencode/test/server/sdk-v1-smoke.test.ts +++ b/packages/opencode/test/server/sdk-v1-smoke.test.ts @@ -7,9 +7,6 @@ import { createOpencodeClient } from "@opencode-ai/sdk" import { Server } from "../../src/server/server" import { tmpdir, disposeAllInstances } from "../fixture/fixture" import { resetDatabase } from "../fixture/db" -import * as Log from "@opencode-ai/core/util/log" - -void Log.init({ print: false }) afterEach(async () => { await disposeAllInstances() diff --git a/packages/opencode/test/server/session-actions.test.ts b/packages/opencode/test/server/session-actions.test.ts index 4aca3436cf..5d7f2315fe 100644 --- a/packages/opencode/test/server/session-actions.test.ts +++ b/packages/opencode/test/server/session-actions.test.ts @@ -2,12 +2,9 @@ import { afterEach, describe, expect, mock } from "bun:test" import { Effect } from "effect" import { Server } from "../../src/server/server" import { Session as SessionNs } from "@/session/session" -import * as Log from "@opencode-ai/core/util/log" import { disposeAllInstances, TestInstance } from "../fixture/fixture" import { testEffect } from "../lib/effect" -void Log.init({ print: false }) - const it = testEffect(SessionNs.defaultLayer) afterEach(async () => { diff --git a/packages/opencode/test/server/session-diff-missing-patch.test.ts b/packages/opencode/test/server/session-diff-missing-patch.test.ts index cec1dbcd9c..e87cf35278 100644 --- a/packages/opencode/test/server/session-diff-missing-patch.test.ts +++ b/packages/opencode/test/server/session-diff-missing-patch.test.ts @@ -18,9 +18,6 @@ import { Storage } from "@/storage/storage" import { resetDatabase } from "../fixture/db" import { disposeAllInstances, TestInstance } from "../fixture/fixture" import { testEffect } from "../lib/effect" -import * as Log from "@opencode-ai/core/util/log" - -void Log.init({ print: false }) const it = testEffect(Layer.mergeAll(Session.defaultLayer, Storage.defaultLayer)) diff --git a/packages/opencode/test/server/session-list.test.ts b/packages/opencode/test/server/session-list.test.ts index 467ab7c9a5..83b3b032d4 100644 --- a/packages/opencode/test/server/session-list.test.ts +++ b/packages/opencode/test/server/session-list.test.ts @@ -1,7 +1,6 @@ import { afterEach, describe, expect } from "bun:test" import { Effect, Layer } from "effect" import { Session as SessionNs } from "@/session/session" -import * as Log from "@opencode-ai/core/util/log" import { disposeAllInstances, provideInstance, TestInstance } from "../fixture/fixture" import { mkdir } from "fs/promises" import path from "path" @@ -14,8 +13,6 @@ import { Storage } from "@/storage/storage" import { SyncEvent } from "@/sync" import { RuntimeFlags } from "@/effect/runtime-flags" import { BackgroundJob } from "@/background/job" - -void Log.init({ print: false }) const it = testEffect( SessionNs.layer.pipe( Layer.provide(Bus.layer), diff --git a/packages/opencode/test/server/session-messages.test.ts b/packages/opencode/test/server/session-messages.test.ts index 6cd17d2555..6e1798c545 100644 --- a/packages/opencode/test/server/session-messages.test.ts +++ b/packages/opencode/test/server/session-messages.test.ts @@ -5,12 +5,9 @@ import { Session as SessionNs } from "@/session/session" import { MessageV2 } from "../../src/session/message-v2" import { ModelID, ProviderID } from "../../src/provider/schema" import { MessageID, PartID, type SessionID } from "../../src/session/schema" -import * as Log from "@opencode-ai/core/util/log" import { disposeAllInstances, TestInstance } from "../fixture/fixture" import { testEffect } from "../lib/effect" -void Log.init({ print: false }) - const it = testEffect(SessionNs.defaultLayer) const model = { diff --git a/packages/opencode/test/server/session-select.test.ts b/packages/opencode/test/server/session-select.test.ts index 0f3875ae14..f9a519896a 100644 --- a/packages/opencode/test/server/session-select.test.ts +++ b/packages/opencode/test/server/session-select.test.ts @@ -1,13 +1,10 @@ import { describe, expect } from "bun:test" import { Effect } from "effect" import { Session } from "@/session/session" -import * as Log from "@opencode-ai/core/util/log" import { Server } from "../../src/server/server" import { TestInstance } from "../fixture/fixture" import { testEffect } from "../lib/effect" -void Log.init({ print: false }) - const it = testEffect(Session.defaultLayer) describe("tui.selectSession endpoint", () => { diff --git a/packages/opencode/test/session/compaction.test.ts b/packages/opencode/test/session/compaction.test.ts index 55ddc621ca..512d6479de 100644 --- a/packages/opencode/test/session/compaction.test.ts +++ b/packages/opencode/test/session/compaction.test.ts @@ -9,7 +9,6 @@ import { Agent } from "../../src/agent/agent" import { LLM } from "../../src/session/llm" import { SessionCompaction } from "../../src/session/compaction" import { Token } from "@/util/token" -import * as Log from "@opencode-ai/core/util/log" import { Permission } from "../../src/permission" import { Plugin } from "../../src/plugin" import { provideTmpdirInstance, TestInstance } from "../fixture/fixture" @@ -32,8 +31,6 @@ import { RuntimeFlags } from "@/effect/runtime-flags" import { EventV2Bridge } from "@/event-v2-bridge" import { LLMEvent, Usage } from "@opencode-ai/llm" -void Log.init({ print: false }) - const summary = Layer.succeed( SessionSummary.Service, SessionSummary.Service.of({ diff --git a/packages/opencode/test/session/messages-pagination.test.ts b/packages/opencode/test/session/messages-pagination.test.ts index e558d07b50..77134bab89 100644 --- a/packages/opencode/test/session/messages-pagination.test.ts +++ b/packages/opencode/test/session/messages-pagination.test.ts @@ -5,11 +5,8 @@ import { MessageV2 } from "../../src/session/message-v2" import { MessageID, PartID, type SessionID } from "../../src/session/schema" import { ModelID, ProviderID } from "../../src/provider/schema" import { NotFoundError } from "@/storage/storage" -import * as Log from "@opencode-ai/core/util/log" import { testEffect } from "../lib/effect" -void Log.init({ print: false }) - const it = testEffect(SessionNs.defaultLayer) const withSession = ( diff --git a/packages/opencode/test/session/processor-effect.test.ts b/packages/opencode/test/session/processor-effect.test.ts index ede122297a..2cca21b747 100644 --- a/packages/opencode/test/session/processor-effect.test.ts +++ b/packages/opencode/test/session/processor-effect.test.ts @@ -21,7 +21,6 @@ import { MessageID, PartID, SessionID } from "../../src/session/schema" import { SessionStatus } from "../../src/session/status" import { SessionSummary } from "../../src/session/summary" import { Snapshot } from "../../src/snapshot" -import * as Log from "@opencode-ai/core/util/log" import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner" import { provideTmpdirServer } from "../fixture/fixture" import { testEffect } from "../lib/effect" @@ -30,8 +29,6 @@ import { SyncEvent } from "@/sync" import { RuntimeFlags } from "@/effect/runtime-flags" import { EventV2Bridge } from "@/event-v2-bridge" -void Log.init({ print: false }) - const summary = Layer.succeed( SessionSummary.Service, SessionSummary.Service.of({ diff --git a/packages/opencode/test/session/prompt.test.ts b/packages/opencode/test/session/prompt.test.ts index 4c46474578..af0d6d23b4 100644 --- a/packages/opencode/test/session/prompt.test.ts +++ b/packages/opencode/test/session/prompt.test.ts @@ -42,7 +42,6 @@ import { Shell } from "../../src/shell/shell" import { Snapshot } from "../../src/snapshot" import { ToolRegistry } from "@/tool/registry" import { Truncate } from "@/tool/truncate" -import * as Log from "@opencode-ai/core/util/log" import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner" import * as Database from "../../src/storage/db" import { Ripgrep } from "../../src/file/ripgrep" @@ -56,8 +55,6 @@ import { SyncEvent } from "@/sync" import { RuntimeFlags } from "@/effect/runtime-flags" import { EventV2Bridge } from "@/event-v2-bridge" -void Log.init({ print: false }) - const summary = Layer.succeed( SessionSummary.Service, SessionSummary.Service.of({ diff --git a/packages/opencode/test/session/revert-compact.test.ts b/packages/opencode/test/session/revert-compact.test.ts index c70c17d451..d979b4b896 100644 --- a/packages/opencode/test/session/revert-compact.test.ts +++ b/packages/opencode/test/session/revert-compact.test.ts @@ -7,14 +7,11 @@ import { ModelID, ProviderID } from "../../src/provider/schema" import { SessionRevert } from "../../src/session/revert" import { MessageV2 } from "../../src/session/message-v2" import { Snapshot } from "../../src/snapshot" -import * as Log from "@opencode-ai/core/util/log" import { MessageID, PartID, SessionID } from "../../src/session/schema" import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner" import { provideTmpdirInstance } from "../fixture/fixture" import { testEffect } from "../lib/effect" -void Log.init({ print: false }) - const env = Layer.mergeAll( Session.defaultLayer, SessionRevert.defaultLayer, diff --git a/packages/opencode/test/session/session.test.ts b/packages/opencode/test/session/session.test.ts index 9a2b155781..c5b7eaaf07 100644 --- a/packages/opencode/test/session/session.test.ts +++ b/packages/opencode/test/session/session.test.ts @@ -2,7 +2,6 @@ import { describe, expect } from "bun:test" import { Deferred, Effect, Exit, Layer } from "effect" import { Session as SessionNs } from "@/session/session" import { GlobalBus, type GlobalEvent } from "../../src/bus/global" -import * as Log from "@opencode-ai/core/util/log" import { MessageV2 } from "../../src/session/message-v2" import { MessageID, PartID, type SessionID } from "../../src/session/schema" import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner" @@ -14,8 +13,6 @@ import { SyncEvent } from "@/sync" import { RuntimeFlags } from "@/effect/runtime-flags" import { BackgroundJob } from "@/background/job" -void Log.init({ print: false }) - const it = testEffect( Layer.mergeAll( SessionNs.layer.pipe( diff --git a/packages/opencode/test/session/snapshot-tool-race.test.ts b/packages/opencode/test/session/snapshot-tool-race.test.ts index 89ed11613e..78d2449041 100644 --- a/packages/opencode/test/session/snapshot-tool-race.test.ts +++ b/packages/opencode/test/session/snapshot-tool-race.test.ts @@ -22,7 +22,6 @@ import { SessionPrompt } from "../../src/session/prompt" import { SessionRevert } from "../../src/session/revert" import { SessionSummary } from "../../src/session/summary" import { MessageV2 } from "../../src/session/message-v2" -import * as Log from "@opencode-ai/core/util/log" import { provideTmpdirServer } from "../fixture/fixture" import { testEffect } from "../lib/effect" import { TestLLMServer } from "../lib/llm-server" @@ -64,8 +63,6 @@ import { SyncEvent } from "@/sync" import { RuntimeFlags } from "@/effect/runtime-flags" import { EventV2Bridge } from "@/event-v2-bridge" -void Log.init({ print: false }) - const mcp = Layer.succeed( MCP.Service, MCP.Service.of({ diff --git a/packages/opencode/test/session/structured-output-integration.test.ts b/packages/opencode/test/session/structured-output-integration.test.ts index 125c63c0f9..bbdb8615bf 100644 --- a/packages/opencode/test/session/structured-output-integration.test.ts +++ b/packages/opencode/test/session/structured-output-integration.test.ts @@ -2,12 +2,9 @@ import { describe, expect, test } from "bun:test" import { Effect, Layer } from "effect" import { Session } from "@/session/session" import { SessionPrompt } from "../../src/session/prompt" -import * as Log from "@opencode-ai/core/util/log" import { MessageV2 } from "../../src/session/message-v2" import { testEffect } from "../lib/effect" -void Log.init({ print: false }) - // Skip tests if no API key is available const hasApiKey = !!process.env.ANTHROPIC_API_KEY const it = testEffect(Layer.mergeAll(SessionPrompt.defaultLayer, Session.defaultLayer)) diff --git a/packages/opencode/test/util/log.test.ts b/packages/opencode/test/util/log.test.ts index 16fbabf16c..800911b921 100644 --- a/packages/opencode/test/util/log.test.ts +++ b/packages/opencode/test/util/log.test.ts @@ -5,7 +5,6 @@ import path from "path" import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner" import * as EffectLogger from "@opencode-ai/core/effect/logger" import { Global } from "@opencode-ai/core/global" -import * as Log from "@opencode-ai/core/util/log" import { tmpdirScoped } from "../fixture/fixture" import { testEffect } from "../lib/effect" @@ -16,34 +15,34 @@ function restoreEnv(key: string, value: string | undefined) { else process.env[key] = value } -it.live("init writes JSONL to the default state log file", () => +it.live("Effect logger writes JSONL to the default state log file", () => Effect.gen(function* () { const log = Global.Path.log const file = process.env.OPENCODE_LOG_FILE const level = process.env.OPENCODE_LOG_LEVEL const print = process.env.OPENCODE_PRINT_LOGS - yield* Effect.addFinalizer(() => Effect.sync(() => (Global.Path.log = log))) - const dir = yield* tmpdirScoped() - Global.Path.log = dir - restoreEnv("OPENCODE_LOG_FILE", undefined) - restoreEnv("OPENCODE_LOG_LEVEL", undefined) - restoreEnv("OPENCODE_PRINT_LOGS", undefined) yield* Effect.addFinalizer(() => - Effect.promise(async () => { + Effect.sync(() => { Global.Path.log = log restoreEnv("OPENCODE_LOG_FILE", file) restoreEnv("OPENCODE_LOG_LEVEL", level) restoreEnv("OPENCODE_PRINT_LOGS", print) - await Log.init({ print: false, level: "DEBUG" }) }), ) - yield* Effect.promise(() => Log.init({ print: false, level: "DEBUG" })) - Log.create({ service: "log.test.default" }).info("hello", { answer: 42 }) + const dir = yield* tmpdirScoped() + Global.Path.log = dir + restoreEnv("OPENCODE_LOG_FILE", undefined) + restoreEnv("OPENCODE_PRINT_LOGS", undefined) + process.env.OPENCODE_LOG_LEVEL = "DEBUG" + + yield* EffectLogger.create({ service: "log.test.default" }) + .info("hello", { answer: 42 }) + .pipe(Effect.provide(EffectLogger.layer)) const record = JSON.parse(yield* Effect.promise(() => fs.readFile(path.join(dir, "log.jsonl"), "utf8"))) - expect(Log.file()).toBe(path.join(dir, "log.jsonl")) + expect(EffectLogger.file()).toBe(path.join(dir, "log.jsonl")) expect(record.level).toBe("INFO") expect(record.message).toBe("hello") expect(record.service).toBe("log.test.default") @@ -59,12 +58,13 @@ it.live("env can override file path and log level", () => const log = Global.Path.log const file = process.env.OPENCODE_LOG_FILE const level = process.env.OPENCODE_LOG_LEVEL + const print = process.env.OPENCODE_PRINT_LOGS yield* Effect.addFinalizer(() => - Effect.promise(async () => { + Effect.sync(() => { Global.Path.log = log restoreEnv("OPENCODE_LOG_FILE", file) restoreEnv("OPENCODE_LOG_LEVEL", level) - await Log.init({ print: false, level: "DEBUG" }) + restoreEnv("OPENCODE_PRINT_LOGS", print) }), ) @@ -73,17 +73,16 @@ it.live("env can override file path and log level", () => process.env.OPENCODE_LOG_FILE = path.join(dir, "custom.jsonl") process.env.OPENCODE_LOG_LEVEL = "WARN" - yield* Effect.promise(() => Log.init({ print: false })) - const logger = Log.create({ service: "log.test.env" }) - logger.info("hidden") - logger.warn("visible") + const logger = EffectLogger.create({ service: "log.test.env" }) + yield* logger.info("hidden").pipe(Effect.provide(EffectLogger.layer)) + yield* logger.warn("visible").pipe(Effect.provide(EffectLogger.layer)) const records = (yield* Effect.promise(() => fs.readFile(path.join(dir, "custom.jsonl"), "utf8"))) .trim() .split("\n") .map((line) => JSON.parse(line)) - expect(Log.file()).toBe(path.join(dir, "custom.jsonl")) + expect(EffectLogger.file()).toBe(path.join(dir, "custom.jsonl")) expect(records).toHaveLength(1) expect(records[0].level).toBe("WARN") expect(records[0].message).toBe("visible") @@ -93,16 +92,23 @@ it.live("env can override file path and log level", () => it.live("Effect logger writes annotations into JSONL fields", () => Effect.gen(function* () { const log = Global.Path.log + const file = process.env.OPENCODE_LOG_FILE + const level = process.env.OPENCODE_LOG_LEVEL + const print = process.env.OPENCODE_PRINT_LOGS yield* Effect.addFinalizer(() => - Effect.promise(async () => { + Effect.sync(() => { Global.Path.log = log - await Log.init({ print: false, level: "DEBUG" }) + restoreEnv("OPENCODE_LOG_FILE", file) + restoreEnv("OPENCODE_LOG_LEVEL", level) + restoreEnv("OPENCODE_PRINT_LOGS", print) }), ) const dir = yield* tmpdirScoped() Global.Path.log = dir - yield* Effect.promise(() => Log.init({ print: false, level: "DEBUG" })) + restoreEnv("OPENCODE_LOG_FILE", undefined) + restoreEnv("OPENCODE_PRINT_LOGS", undefined) + process.env.OPENCODE_LOG_LEVEL = "DEBUG" yield* Effect.logInfo("effect hello").pipe( Effect.annotateLogs({ service: "log.test.effect", "session.id": "session-1" }),