refactor: isolate legacy flags

This commit is contained in:
Dax Raad 2026-07-20 21:42:02 -04:00
commit 43c08387f1
65 changed files with 282 additions and 147 deletions

View file

@ -1,11 +1,9 @@
export * as ServerAuth from "./auth"
import { Context, Effect, Layer, Option, Redacted } from "effect"
import { all, option, string, withDefault } from "effect/Config"
import { Context, Layer, Option, Redacted } from "effect"
export type Credentials = {
password?: string
username?: string
}
export type DecodedCredentials = {
@ -15,7 +13,6 @@ export type DecodedCredentials = {
export type Info = {
readonly password: Option.Option<string>
readonly username: string
}
export class Config extends Context.Service<Config, Info>()("@opencode/ServerAuthConfig") {
@ -24,17 +21,7 @@ export class Config extends Context.Service<Config, Info>()("@opencode/ServerAut
}
static get layer() {
return Layer.effect(
this,
Effect.gen(function* () {
return Config.of(
yield* all({
password: string("OPENCODE_SERVER_PASSWORD").pipe(option),
username: string("OPENCODE_SERVER_USERNAME").pipe(withDefault("opencode")),
}),
)
}),
)
return this.configLayer({ password: Option.none() })
}
}
@ -45,16 +32,16 @@ export function required(config: Info) {
export function authorized(credentials: DecodedCredentials, config: Info) {
return (
Option.isSome(config.password) &&
credentials.username === config.username &&
credentials.username === "opencode" &&
Redacted.value(credentials.password) === config.password.value
)
}
export function header(credentials?: Credentials) {
const password = credentials?.password ?? process.env.OPENCODE_SERVER_PASSWORD
const password = credentials?.password
if (!password) return undefined
return `Basic ${Buffer.from(`${credentials?.username ?? process.env.OPENCODE_SERVER_USERNAME ?? "opencode"}:${password}`).toString("base64")}`
return `Basic ${Buffer.from(`opencode:${password}`).toString("base64")}`
}
export function headers(credentials?: Credentials) {

View file

@ -4,11 +4,13 @@ import { Observability } from "@opencode-ai/core/observability"
import { Schema } from "effect"
export const ServerOptions = Schema.Struct({
client: Schema.optional(Schema.String),
hostname: Schema.optional(Schema.String),
port: Schema.optional(
Schema.Int.check(Schema.isGreaterThanOrEqualTo(1), Schema.isLessThanOrEqualTo(65_535)),
),
password: Schema.optional(Schema.String),
simulation: Schema.optional(Schema.Boolean),
database: Schema.optional(Database.Options),
models: Schema.optional(ModelsDev.Options),
observability: Schema.optional(Observability.Options),
@ -16,6 +18,8 @@ export const ServerOptions = Schema.Struct({
Schema.Struct({
directory: Schema.optional(Schema.String),
project: Schema.optional(Schema.Boolean),
file: Schema.optional(Schema.String),
content: Schema.optional(Schema.String),
}),
),
windows: Schema.optional(

View file

@ -148,7 +148,7 @@ function dispatch(
application: Ref.Ref<Option.Option<App>>,
shutdown: Deferred.Deferred<void>,
): App {
const auth = ServerAuth.Config.of({ username: "opencode", password: Option.some(password) })
const auth = ServerAuth.Config.of({ password: Option.some(password) })
return Effect.gen(function* () {
const request = yield* HttpServerRequest.HttpServerRequest
const url = new URL(request.url, "http://localhost")

View file

@ -14,6 +14,10 @@ import { PtyTicket } from "@opencode-ai/core/pty/ticket"
import { Pty } from "@opencode-ai/core/pty"
import { Project } from "@opencode-ai/core/project"
import { SessionV2 } from "@opencode-ai/core/session"
import { SessionCompaction } from "@opencode-ai/core/session/compaction"
import { SessionGenerateNode } from "@opencode-ai/core/session/generate-node"
import { SessionModelRequest } from "@opencode-ai/core/session/model-request"
import { SessionTitle } from "@opencode-ai/core/session/title"
import { Shell } from "@opencode-ai/core/shell"
import { Job } from "@opencode-ai/core/job"
import { Global } from "@opencode-ai/core/global"
@ -64,7 +68,7 @@ const applicationServices = LayerNode.group([
export function createRoutes(options: ServerOptions = {}, serviceURLs: () => ReadonlyArray<string> = () => []) {
return makeRoutes(
options.password
? ServerAuth.Config.configLayer({ username: "opencode", password: Option.some(options.password) })
? ServerAuth.Config.configLayer({ password: Option.some(options.password) })
: ServerAuth.Config.layer,
options,
serviceURLs,
@ -72,7 +76,7 @@ export function createRoutes(options: ServerOptions = {}, serviceURLs: () => Rea
}
export function createEmbeddedRoutes(options: ServerOptions = {}) {
return makeRoutes(ServerAuth.Config.configLayer({ username: "opencode", password: Option.none() }), options, () => [])
return makeRoutes(ServerAuth.Config.configLayer({ password: Option.none() }), options, () => [])
}
function makeRoutes<AuthError, AuthServices>(
@ -83,19 +87,30 @@ function makeRoutes<AuthError, AuthServices>(
const pluginRuntimeCell = PluginRuntime.makeCell()
const replacements: LayerNode.Replacements = [
[Database.node, Database.configured(options.database)],
[ModelsDev.node, ModelsDev.configured(options.models)],
[ModelsDev.node, ModelsDev.configured({ ...options.models, client: options.client })],
[Watcher.node, Watcher.configured({ enabled: options.fs?.filewatcher })],
[FileSystemSearch.node, FileSystemSearch.configured({ fff: options.fs?.fff })],
[Global.node, Global.layerWith(options.config?.directory ? { config: options.config.directory } : {})],
[Config.node, Config.configured({ project: options.config?.project })],
[
Config.node,
Config.configured({
project: options.config?.project,
file: options.config?.file,
content: options.config?.content,
}),
],
[InstructionDiscovery.node, InstructionDiscovery.configured({ project: options.config?.project })],
[CommandV2.node, CommandV2.configured({ gitbash: options.windows?.gitbash })],
[Pty.node, Pty.configured({ gitbash: options.windows?.gitbash })],
[Shell.node, Shell.configured({ gitbash: options.windows?.gitbash })],
[SessionCompaction.node, SessionCompaction.configured({ client: options.client })],
[SessionGenerateNode.node, SessionGenerateNode.configured({ client: options.client })],
[SessionModelRequest.node, SessionModelRequest.configured({ client: options.client })],
[SessionTitle.node, SessionTitle.configured({ client: options.client })],
[PluginRuntime.node, PluginRuntime.layerWithCell(pluginRuntimeCell)],
[PluginRuntime.providerNode, PluginRuntime.providerNodeWithCell(pluginRuntimeCell)],
]
const serviceLayer = simulateEnabled()
const serviceLayer = options.simulation
? Layer.unwrap(
Effect.gen(function* () {
const { simulationReplacements } = yield* Effect.promise(() => import("@opencode-ai/simulation/backend"))
@ -120,7 +135,7 @@ function makeRoutes<AuthError, AuthServices>(
Layer.provide(authorizationLayer),
Layer.provide(schemaErrorLayer),
Layer.provide(auth),
Layer.provide(Observability.layer(options.observability)),
Layer.provide(Observability.layer({ ...options.observability, client: options.client })),
HttpRouter.provideRequest(requestServices),
Layer.provideMerge(services),
Layer.provideMerge(HttpRouter.layer),
@ -129,8 +144,4 @@ function makeRoutes<AuthError, AuthServices>(
)
}
function simulateEnabled() {
return !!process.env.OPENCODE_SIMULATE
}
export const webHandler = () => HttpRouter.toWebHandler(createRoutes().pipe(Layer.provide(HttpServer.layerServices)))