fix(server): respect configured OpenCode auth (#366)
Fixes #315 ## Summary - stop overwriting configured `OPENCODE_SERVER_USERNAME` and `OPENCODE_SERVER_PASSWORD` when CodeNomad launches managed OpenCode servers - reuse user-provided OpenCode auth from workspace environment or process env before falling back to generated credentials - add focused tests for configured, inherited, and generated auth paths ## Testing - `npx tsx --test "packages/server/src/workspaces/opencode-auth.test.ts"` - `npx tsc --noEmit --target ES2020 --module ESNext --moduleResolution Node --strict --esModuleInterop --types node "packages/server/src/workspaces/opencode-auth.ts" "packages/server/src/workspaces/opencode-auth.test.ts"` - `git diff --check` ## Notes - full server workspace typecheck still has unrelated baseline failures in this branch (`commander` typings and missing `fuzzysort` types)
This commit is contained in:
@@ -13,10 +13,9 @@ import { Logger } from "../logger"
|
||||
import { getOpencodeConfigDir } from "../opencode-config.js"
|
||||
import {
|
||||
buildOpencodeBasicAuthHeader,
|
||||
DEFAULT_OPENCODE_USERNAME,
|
||||
generateOpencodeServerPassword,
|
||||
OPENCODE_SERVER_PASSWORD_ENV,
|
||||
OPENCODE_SERVER_USERNAME_ENV,
|
||||
resolveOpencodeServerAuth,
|
||||
} from "./opencode-auth"
|
||||
|
||||
const STARTUP_STABILITY_DELAY_MS = 1500
|
||||
@@ -124,8 +123,10 @@ export class WorkspaceManager {
|
||||
const envVars = (serverConfig as any)?.environmentVariables
|
||||
const userEnvironment = envVars && typeof envVars === "object" && !Array.isArray(envVars) ? (envVars as any) : {}
|
||||
|
||||
const opencodeUsername = DEFAULT_OPENCODE_USERNAME
|
||||
const opencodePassword = generateOpencodeServerPassword()
|
||||
const { username: opencodeUsername, password: opencodePassword } = resolveOpencodeServerAuth({
|
||||
userEnvironment,
|
||||
processEnv: process.env,
|
||||
})
|
||||
const authorization = buildOpencodeBasicAuthHeader({ username: opencodeUsername, password: opencodePassword })
|
||||
if (!authorization) {
|
||||
throw new Error("Failed to build OpenCode auth header")
|
||||
|
||||
41
packages/server/src/workspaces/opencode-auth.test.ts
Normal file
41
packages/server/src/workspaces/opencode-auth.test.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import assert from "node:assert/strict"
|
||||
import { describe, it } from "node:test"
|
||||
|
||||
import { resolveOpencodeServerAuth } from "./opencode-auth"
|
||||
|
||||
describe("resolveOpencodeServerAuth", () => {
|
||||
it("uses configured OpenCode auth from workspace environment", () => {
|
||||
const auth = resolveOpencodeServerAuth({
|
||||
userEnvironment: {
|
||||
OPENCODE_SERVER_USERNAME: "alice",
|
||||
OPENCODE_SERVER_PASSWORD: "secret",
|
||||
},
|
||||
processEnv: {},
|
||||
generatePassword: () => "generated",
|
||||
})
|
||||
|
||||
assert.deepEqual(auth, { username: "alice", password: "secret" })
|
||||
})
|
||||
|
||||
it("uses process environment when workspace environment does not provide credentials", () => {
|
||||
const auth = resolveOpencodeServerAuth({
|
||||
userEnvironment: {},
|
||||
processEnv: {
|
||||
OPENCODE_SERVER_PASSWORD: "process-secret",
|
||||
},
|
||||
generatePassword: () => "generated",
|
||||
})
|
||||
|
||||
assert.deepEqual(auth, { username: "codenomad", password: "process-secret" })
|
||||
})
|
||||
|
||||
it("falls back to generated credentials", () => {
|
||||
const auth = resolveOpencodeServerAuth({
|
||||
userEnvironment: {},
|
||||
processEnv: {},
|
||||
generatePassword: () => "generated",
|
||||
})
|
||||
|
||||
assert.deepEqual(auth, { username: "codenomad", password: "generated" })
|
||||
})
|
||||
})
|
||||
@@ -9,6 +9,32 @@ export function generateOpencodeServerPassword(): string {
|
||||
return crypto.randomBytes(32).toString("base64url")
|
||||
}
|
||||
|
||||
function readConfiguredValue(key: string, ...sources: Array<Record<string, unknown> | undefined>): string | undefined {
|
||||
for (const source of sources) {
|
||||
const value = source?.[key]
|
||||
if (typeof value === "string" && value.trim().length > 0) {
|
||||
return value
|
||||
}
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
export function resolveOpencodeServerAuth(options: {
|
||||
userEnvironment?: Record<string, unknown>
|
||||
processEnv?: NodeJS.ProcessEnv
|
||||
generatePassword?: () => string
|
||||
} = {}): { username: string; password: string } {
|
||||
const generatePassword = options.generatePassword ?? generateOpencodeServerPassword
|
||||
const username =
|
||||
readConfiguredValue(OPENCODE_SERVER_USERNAME_ENV, options.userEnvironment, options.processEnv) ??
|
||||
DEFAULT_OPENCODE_USERNAME
|
||||
const password =
|
||||
readConfiguredValue(OPENCODE_SERVER_PASSWORD_ENV, options.userEnvironment, options.processEnv) ??
|
||||
generatePassword()
|
||||
|
||||
return { username, password }
|
||||
}
|
||||
|
||||
export function buildOpencodeBasicAuthHeader(params: { username?: string; password?: string }): string | undefined {
|
||||
const username = params.username
|
||||
const password = params.password
|
||||
|
||||
Reference in New Issue
Block a user