Skip to content

Commit b6538a4

Browse files
committed
fix(config): fallback when user info unavailable
1 parent 8845a43 commit b6538a4

3 files changed

Lines changed: 32 additions & 3 deletions

File tree

packages/opencode/src/config/config.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -762,7 +762,14 @@ export const layer = Layer.effect(
762762
result.permission = mergeDeep(perms, result.permission ?? {})
763763
}
764764

765-
if (!result.username) result.username = os.userInfo().username
765+
if (!result.username) {
766+
try {
767+
result.username = os.userInfo().username || "user"
768+
} catch (err) {
769+
log.warn("failed to read system username, using fallback", { err })
770+
result.username = "user"
771+
}
772+
}
766773

767774
if (result.autoshare === true && !result.share) {
768775
result.share = "auto"

packages/opencode/src/config/managed.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,14 @@ export function parseManagedPlist(json: string): string {
4646
export async function readManagedPreferences() {
4747
if (process.platform !== "darwin") return
4848

49-
const user = os.userInfo().username
49+
const user = (() => {
50+
try {
51+
return os.userInfo().username || "user"
52+
} catch (err) {
53+
log.warn("failed to read system username, using fallback", { err })
54+
return "user"
55+
}
56+
})()
5057
const paths = [
5158
path.join("/Library/Managed Preferences", user, `${MANAGED_PLIST_DOMAIN}.plist`),
5259
path.join("/Library/Managed Preferences", `${MANAGED_PLIST_DOMAIN}.plist`),

packages/opencode/test/config/config.test.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { test, expect, describe, afterEach, beforeEach } from "bun:test"
1+
import { test, expect, describe, afterEach, beforeEach, spyOn } from "bun:test"
22
import { Effect, Exit, Layer, Option } from "effect"
33
import { FetchHttpClient, HttpClient, HttpClientResponse } from "effect/unstable/http"
44
import { NodeFileSystem, NodePath } from "@effect/platform-node"
@@ -28,6 +28,7 @@ import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner"
2828
import { testEffect } from "../lib/effect"
2929
import path from "path"
3030
import fs from "fs/promises"
31+
import os from "os"
3132
import { pathToFileURL } from "url"
3233
import { Global } from "@opencode-ai/core/global"
3334
import { ProjectID } from "../../src/project/schema"
@@ -291,6 +292,20 @@ it.instance("loads config with defaults when no files exist", () =>
291292
}),
292293
)
293294

295+
it.instance("falls back to generic username when system user info is unavailable", () =>
296+
Effect.gen(function* () {
297+
const userInfo = spyOn(os, "userInfo").mockImplementation(() => {
298+
throw Object.assign(new Error("missing passwd entry"), { code: "ENOENT" })
299+
})
300+
try {
301+
const config = yield* Config.use.get()
302+
expect(config.username).toBe("user")
303+
} finally {
304+
userInfo.mockRestore()
305+
}
306+
}),
307+
)
308+
294309
it.effect("creates global jsonc config with schema when no global configs exist", () =>
295310
withGlobalConfig({}, ({ dir }) =>
296311
Effect.gen(function* () {

0 commit comments

Comments
 (0)