refactor(cli): single password config with legacy fallback

This commit is contained in:
Dax Raad 2026-07-03 13:41:15 -04:00
commit 08293e74a0
4 changed files with 13 additions and 11 deletions

View file

@ -19,7 +19,7 @@ export default Runtime.handler(Commands, (input) =>
return yield* Effect.fail(new Error("--server and --standalone cannot be combined")) return yield* Effect.fail(new Error("--server and --standalone cannot be combined"))
const transport = yield* Effect.gen(function* () { const transport = yield* Effect.gen(function* () {
if (server !== undefined) { if (server !== undefined) {
const password = Option.getOrUndefined(yield* Env.password) const password = yield* Env.password
const explicit = { const explicit = {
url: server, url: server,
headers: password headers: password

View file

@ -27,10 +27,13 @@ export default Runtime.handler(
if (input.service) yield* Effect.sync(() => process.chdir(Global.Path.home)) if (input.service) yield* Effect.sync(() => process.chdir(Global.Path.home))
return yield* Effect.scoped( return yield* Effect.scoped(
Effect.gen(function* () { Effect.gen(function* () {
const standalonePassword = Option.getOrUndefined(yield* Env.serverPassword) const standalonePassword = yield* Env.password
// Keep the lease credential out of the environment inherited by any // Keep the lease credential out of the environment inherited by any
// process this server spawns. // process this server spawns.
if (input.stdio) delete process.env.OPENCODE_SERVER_PASSWORD if (input.stdio) {
delete process.env.OPENCODE_PASSWORD
delete process.env.OPENCODE_SERVER_PASSWORD
}
const config = input.service ? yield* ServiceConfig.read() : {} const config = input.service ? yield* ServiceConfig.read() : {}
const password = input.service const password = input.service
? yield* ServiceConfig.password() ? yield* ServiceConfig.password()

View file

@ -4,15 +4,12 @@ import { Config } from "effect"
// these instead of touching process.env so the full surface stays visible, // these instead of touching process.env so the full surface stays visible,
// typed, and redacted where secret. // typed, and redacted where secret.
// Client-side password for an explicit --server target. The legacy name is // The opencode server password: sent by clients connecting to an explicit
// still honored; it also remains the variable a standalone child inherits. // --server, and adopted by a manually run or standalone server. The legacy
// name is still honored.
export const password = Config.redacted("OPENCODE_PASSWORD").pipe( export const password = Config.redacted("OPENCODE_PASSWORD").pipe(
Config.orElse(() => Config.redacted("OPENCODE_SERVER_PASSWORD")), Config.orElse(() => Config.redacted("OPENCODE_SERVER_PASSWORD")),
Config.option, Config.withDefault(undefined),
) )
// Server-side lease password: set by the standalone spawner for its child,
// or preset for a manually managed `opencode serve`.
export const serverPassword = Config.redacted("OPENCODE_SERVER_PASSWORD").pipe(Config.option)
export * as Env from "./env" export * as Env from "./env"

View file

@ -15,7 +15,9 @@ function command(password: string) {
if (!compiled && entrypoint.length === 0) throw new Error("Failed to resolve CLI entrypoint") if (!compiled && entrypoint.length === 0) throw new Error("Failed to resolve CLI entrypoint")
return ChildProcess.make(process.execPath, [...entrypoint, "serve", "--stdio", "--port", "0"], { return ChildProcess.make(process.execPath, [...entrypoint, "serve", "--stdio", "--port", "0"], {
cwd: process.cwd(), cwd: process.cwd(),
env: { OPENCODE_SERVER_PASSWORD: password }, // Explicit entry wins over anything inherited, so a user-exported
// OPENCODE_PASSWORD cannot shadow the child's lease credential.
env: { OPENCODE_PASSWORD: password },
extendEnv: true, extendEnv: true,
// The server treats EOF on this pipe as the end of its ownership lease. // The server treats EOF on this pipe as the end of its ownership lease.
// The OS closes it even when the TUI is killed before Effect finalizers run. // The OS closes it even when the TUI is killed before Effect finalizers run.