From 2aa607c8280fd0b672201471635451efeac8d472 Mon Sep 17 00:00:00 2001 From: John Wessel Date: Tue, 17 Mar 2026 12:58:13 -0400 Subject: [PATCH 1/2] fix(opencode-local): resolve HOME from os.userInfo() for model discovery When Paperclip's server is started via `runuser -u node` (common in Docker/Fly.io deployments), the HOME environment variable retains the parent process's value (e.g. /root) instead of the target user's home directory (/home/node). This causes `opencode models` to miss provider auth credentials stored under the actual user's home, resulting in "Configured OpenCode model is unavailable" errors for providers that require API keys (e.g. zai/zhipuai). Fix: use `os.userInfo().homedir` (reads from /etc/passwd, not env) to ensure the child process always sees the correct HOME, regardless of how the server was launched. Co-Authored-By: Claude Opus 4.6 (1M context) --- packages/adapters/opencode-local/src/server/models.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/adapters/opencode-local/src/server/models.ts b/packages/adapters/opencode-local/src/server/models.ts index a4d1a46d..aedc1967 100644 --- a/packages/adapters/opencode-local/src/server/models.ts +++ b/packages/adapters/opencode-local/src/server/models.ts @@ -1,4 +1,5 @@ import { createHash } from "node:crypto"; +import os from "node:os"; import type { AdapterModel } from "@paperclipai/adapter-utils"; import { asString, @@ -107,7 +108,12 @@ export async function discoverOpenCodeModels(input: { const command = resolveOpenCodeCommand(input.command); const cwd = asString(input.cwd, process.cwd()); const env = normalizeEnv(input.env); - const runtimeEnv = normalizeEnv(ensurePathInEnv({ ...process.env, ...env })); + // Ensure HOME points to the actual running user's home directory. + // When the server is started via `runuser -u `, HOME may still + // reflect the parent process (e.g. /root), causing OpenCode to miss + // provider auth credentials stored under the target user's home. + const resolvedHome = os.userInfo().homedir; + const runtimeEnv = normalizeEnv(ensurePathInEnv({ ...process.env, ...env, ...(resolvedHome ? { HOME: resolvedHome } : {}) })); const result = await runChildProcess( `opencode-models-${Date.now()}-${Math.random().toString(16).slice(2)}`, From 5965266cb89a56bb762ff0106a362542eaf3bfd3 Mon Sep 17 00:00:00 2001 From: John Wessel Date: Tue, 17 Mar 2026 13:05:23 -0400 Subject: [PATCH 2/2] fix: guard os.userInfo() for UID-only containers, exclude HOME from cache key MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Address Greptile review feedback: 1. Wrap os.userInfo() in try/catch — it throws SystemError when the current UID has no /etc/passwd entry (e.g. `docker run --user 1234` with a minimal image). Falls back to process.env.HOME gracefully. 2. Add HOME to VOLATILE_ENV_KEY_EXACT so the discovery cache key is not affected by the caller-supplied HOME vs the resolved HOME. os.userInfo().homedir is constant for the process lifetime, so HOME adds no useful cache differentiation. Co-Authored-By: Claude Opus 4.6 (1M context) --- packages/adapters/opencode-local/src/server/models.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/packages/adapters/opencode-local/src/server/models.ts b/packages/adapters/opencode-local/src/server/models.ts index aedc1967..94634d86 100644 --- a/packages/adapters/opencode-local/src/server/models.ts +++ b/packages/adapters/opencode-local/src/server/models.ts @@ -21,7 +21,7 @@ function resolveOpenCodeCommand(input: unknown): string { const discoveryCache = new Map(); const VOLATILE_ENV_KEY_PREFIXES = ["PAPERCLIP_", "npm_", "NPM_"] as const; -const VOLATILE_ENV_KEY_EXACT = new Set(["PWD", "OLDPWD", "SHLVL", "_", "TERM_SESSION_ID"]); +const VOLATILE_ENV_KEY_EXACT = new Set(["PWD", "OLDPWD", "SHLVL", "_", "TERM_SESSION_ID", "HOME"]); function dedupeModels(models: AdapterModel[]): AdapterModel[] { const seen = new Set(); @@ -112,7 +112,14 @@ export async function discoverOpenCodeModels(input: { // When the server is started via `runuser -u `, HOME may still // reflect the parent process (e.g. /root), causing OpenCode to miss // provider auth credentials stored under the target user's home. - const resolvedHome = os.userInfo().homedir; + let resolvedHome: string | undefined; + try { + resolvedHome = os.userInfo().homedir || undefined; + } catch { + // os.userInfo() throws a SystemError when the current UID has no + // /etc/passwd entry (e.g. `docker run --user 1234` with a minimal + // image). Fall back to process.env.HOME. + } const runtimeEnv = normalizeEnv(ensurePathInEnv({ ...process.env, ...env, ...(resolvedHome ? { HOME: resolvedHome } : {}) })); const result = await runChildProcess(