import { Config, ConfigProvider, Context, Effect, Layer } from "effect"
import { ConfigService } from "@/effect/config-service"
const bool = (name: string) => Config.boolean(name).pipe(Config.withDefault(false))
const boolTrue = (name: string) => Config.boolean(name).pipe(Config.withDefault(true));
const positiveInteger = (name: string) =>
Config.number(name).pipe(
Config.map((value) => (Number.isInteger(value) && value > 0 ? value : undefined)),
Config.orElse(() => Config.succeed(undefined)),
)
const experimental = bool("DEVECO_EXPERIMENTAL")
const enabledByExperimental = (name: string) =>
Config.all({ experimental, enabled: bool(name) }).pipe(Config.map((flags) => flags.experimental || flags.enabled))
export class Service extends ConfigService.Service<Service>()("@opencode/RuntimeFlags", {
autoShare: bool("DEVECO_AUTO_SHARE"),
pure: bool("DEVECO_PURE"),
disableDefaultSkills: bool("DEVECO_DISABLE_DEFAULT_SKILLS"),
disableDefaultPlugins: bool("DEVECO_DISABLE_DEFAULT_PLUGINS"),
disableChannelDb: bool("DEVECO_DISABLE_CHANNEL_DB"),
disableEmbeddedWebUi: bool("DEVECO_DISABLE_EMBEDDED_WEB_UI"),
disableExternalSkills: bool("DEVECO_DISABLE_EXTERNAL_SKILLS"),
disableLspDownload: bool("DEVECO_DISABLE_LSP_DOWNLOAD"),
skipMigrations: bool("DEVECO_SKIP_MIGRATIONS"),
disableClaudeCodePrompt: Config.all({
broad: boolTrue("DEVECO_DISABLE_CLAUDE_CODE"),
direct: boolTrue("DEVECO_DISABLE_CLAUDE_CODE_PROMPT"),
}).pipe(Config.map((flags) => flags.broad || flags.direct)),
disableClaudeCodeSkills: Config.all({
broad: boolTrue("DEVECO_DISABLE_CLAUDE_CODE"),
direct: boolTrue("DEVECO_DISABLE_CLAUDE_CODE_SKILLS"),
}).pipe(Config.map((flags) => flags.broad || flags.direct)),
enableExa: Config.all({
experimental,
enabled: bool("DEVECO_ENABLE_EXA"),
legacy: bool("DEVECO_EXPERIMENTAL_EXA"),
}).pipe(Config.map((flags) => flags.experimental || flags.enabled || flags.legacy)),
enableParallel: Config.all({
enabled: bool("DEVECO_ENABLE_PARALLEL"),
legacy: bool("DEVECO_EXPERIMENTAL_PARALLEL"),
}).pipe(Config.map((flags) => flags.enabled || flags.legacy)),
enableExperimentalModels: bool("DEVECO_ENABLE_EXPERIMENTAL_MODELS"),
enableQuestionTool: bool("DEVECO_ENABLE_QUESTION_TOOL"),
experimentalScout: enabledByExperimental("DEVECO_EXPERIMENTAL_SCOUT"),
experimentalBackgroundSubagents: enabledByExperimental("DEVECO_EXPERIMENTAL_BACKGROUND_SUBAGENTS"),
experimentalLspTy: bool("DEVECO_EXPERIMENTAL_LSP_TY"),
experimentalLspTool: enabledByExperimental("DEVECO_EXPERIMENTAL_LSP_TOOL"),
experimentalOxfmt: enabledByExperimental("DEVECO_EXPERIMENTAL_OXFMT"),
experimentalPlanMode: enabledByExperimental("DEVECO_EXPERIMENTAL_PLAN_MODE"),
experimentalEventSystem: enabledByExperimental("DEVECO_EXPERIMENTAL_EVENT_SYSTEM"),
experimentalWorkspaces: enabledByExperimental("DEVECO_EXPERIMENTAL_WORKSPACES"),
experimentalIconDiscovery: enabledByExperimental("DEVECO_EXPERIMENTAL_ICON_DISCOVERY"),
exploreTaskRoute: boolTrue("DEVECO_ENABLE_EXPLORE_TASK_ROUTE"),
outputTokenMax: positiveInteger("DEVECO_EXPERIMENTAL_OUTPUT_TOKEN_MAX"),
bashDefaultTimeoutMs: positiveInteger("DEVECO_EXPERIMENTAL_BASH_DEFAULT_TIMEOUT_MS"),
experimentalNativeLlm: enabledByExperimental("DEVECO_EXPERIMENTAL_NATIVE_LLM"),
client: Config.string("DEVECO_CLIENT").pipe(Config.withDefault("cli")),
}) {}
export type Info = Context.Service.Shape<typeof Service>
const emptyConfigLayer = Service.defaultLayer.pipe(
Layer.provide(ConfigProvider.layer(ConfigProvider.fromUnknown({}))),
Layer.orDie,
)
export const layer = (overrides: Partial<Info> = {}) =>
Layer.effect(
Service,
Effect.gen(function* () {
const flags = yield* Service
return Service.of({ ...flags, ...overrides })
}),
).pipe(Layer.provide(emptyConfigLayer))
export const defaultLayer = Service.defaultLayer.pipe(Layer.orDie)
export * as RuntimeFlags from "./runtime-flags"