fix: add oh-my-openagent.jsonc config file detection (fixes #2624)
This commit is contained in:
@@ -3736,6 +3736,147 @@
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"openclaw": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"enabled": {
|
||||
"default": false,
|
||||
"type": "boolean"
|
||||
},
|
||||
"gateways": {
|
||||
"default": {},
|
||||
"type": "object",
|
||||
"propertyNames": {
|
||||
"type": "string"
|
||||
},
|
||||
"additionalProperties": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"default": "http",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"http",
|
||||
"command"
|
||||
]
|
||||
},
|
||||
"url": {
|
||||
"type": "string"
|
||||
},
|
||||
"method": {
|
||||
"default": "POST",
|
||||
"type": "string"
|
||||
},
|
||||
"headers": {
|
||||
"type": "object",
|
||||
"propertyNames": {
|
||||
"type": "string"
|
||||
},
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"command": {
|
||||
"type": "string"
|
||||
},
|
||||
"timeout": {
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"type",
|
||||
"method"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"hooks": {
|
||||
"default": {},
|
||||
"type": "object",
|
||||
"propertyNames": {
|
||||
"type": "string"
|
||||
},
|
||||
"additionalProperties": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"enabled": {
|
||||
"default": true,
|
||||
"type": "boolean"
|
||||
},
|
||||
"gateway": {
|
||||
"type": "string"
|
||||
},
|
||||
"instruction": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"enabled",
|
||||
"gateway",
|
||||
"instruction"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"replyListener": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"discordBotToken": {
|
||||
"type": "string"
|
||||
},
|
||||
"discordChannelId": {
|
||||
"type": "string"
|
||||
},
|
||||
"discordMention": {
|
||||
"type": "string"
|
||||
},
|
||||
"authorizedDiscordUserIds": {
|
||||
"default": [],
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"telegramBotToken": {
|
||||
"type": "string"
|
||||
},
|
||||
"telegramChatId": {
|
||||
"type": "string"
|
||||
},
|
||||
"pollIntervalMs": {
|
||||
"default": 3000,
|
||||
"type": "number"
|
||||
},
|
||||
"rateLimitPerMinute": {
|
||||
"default": 10,
|
||||
"type": "number"
|
||||
},
|
||||
"maxMessageLength": {
|
||||
"default": 500,
|
||||
"type": "number"
|
||||
},
|
||||
"includePrefix": {
|
||||
"default": true,
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"authorizedDiscordUserIds",
|
||||
"pollIntervalMs",
|
||||
"rateLimitPerMinute",
|
||||
"maxMessageLength",
|
||||
"includePrefix"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"enabled",
|
||||
"gateways",
|
||||
"hooks"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"babysitting": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
||||
@@ -2,15 +2,15 @@ import { readFileSync } from "node:fs"
|
||||
import { join } from "node:path"
|
||||
|
||||
import { OhMyOpenCodeConfigSchema } from "../../../config"
|
||||
import { detectConfigFile, getOpenCodeConfigDir, parseJsonc } from "../../../shared"
|
||||
import { detectPluginConfigFile, getOpenCodeConfigDir, parseJsonc } from "../../../shared"
|
||||
import { CHECK_IDS, CHECK_NAMES, PACKAGE_NAME } from "../constants"
|
||||
import type { CheckResult, DoctorIssue } from "../types"
|
||||
import { loadAvailableModelsFromCache } from "./model-resolution-cache"
|
||||
import { getModelResolutionInfoWithOverrides } from "./model-resolution"
|
||||
import type { OmoConfig } from "./model-resolution-types"
|
||||
|
||||
const USER_CONFIG_BASE = join(getOpenCodeConfigDir({ binary: "opencode" }), PACKAGE_NAME)
|
||||
const PROJECT_CONFIG_BASE = join(process.cwd(), ".opencode", PACKAGE_NAME)
|
||||
const USER_CONFIG_DIR = getOpenCodeConfigDir({ binary: "opencode" })
|
||||
const PROJECT_CONFIG_DIR = join(process.cwd(), ".opencode")
|
||||
|
||||
interface ConfigValidationResult {
|
||||
exists: boolean
|
||||
@@ -21,10 +21,10 @@ interface ConfigValidationResult {
|
||||
}
|
||||
|
||||
function findConfigPath(): string | null {
|
||||
const projectConfig = detectConfigFile(PROJECT_CONFIG_BASE)
|
||||
const projectConfig = detectPluginConfigFile(PROJECT_CONFIG_DIR)
|
||||
if (projectConfig.format !== "none") return projectConfig.path
|
||||
|
||||
const userConfig = detectConfigFile(USER_CONFIG_BASE)
|
||||
const userConfig = detectPluginConfigFile(USER_CONFIG_DIR)
|
||||
if (userConfig.format !== "none") return userConfig.path
|
||||
|
||||
return null
|
||||
|
||||
@@ -1,17 +1,13 @@
|
||||
import { readFileSync } from "node:fs"
|
||||
import { join } from "node:path"
|
||||
import { detectConfigFile, getOpenCodeConfigPaths, parseJsonc } from "../../../shared"
|
||||
import { detectPluginConfigFile, getOpenCodeConfigPaths, parseJsonc } from "../../../shared"
|
||||
import type { OmoConfig } from "./model-resolution-types"
|
||||
|
||||
const PACKAGE_NAME = "oh-my-opencode"
|
||||
const USER_CONFIG_BASE = join(
|
||||
getOpenCodeConfigPaths({ binary: "opencode", version: null }).configDir,
|
||||
PACKAGE_NAME
|
||||
)
|
||||
const PROJECT_CONFIG_BASE = join(process.cwd(), ".opencode", PACKAGE_NAME)
|
||||
const USER_CONFIG_DIR = getOpenCodeConfigPaths({ binary: "opencode", version: null }).configDir
|
||||
const PROJECT_CONFIG_DIR = join(process.cwd(), ".opencode")
|
||||
|
||||
export function loadOmoConfig(): OmoConfig | null {
|
||||
const projectDetected = detectConfigFile(PROJECT_CONFIG_BASE)
|
||||
const projectDetected = detectPluginConfigFile(PROJECT_CONFIG_DIR)
|
||||
if (projectDetected.format !== "none") {
|
||||
try {
|
||||
const content = readFileSync(projectDetected.path, "utf-8")
|
||||
@@ -21,7 +17,7 @@ export function loadOmoConfig(): OmoConfig | null {
|
||||
}
|
||||
}
|
||||
|
||||
const userDetected = detectConfigFile(USER_CONFIG_BASE)
|
||||
const userDetected = detectPluginConfigFile(USER_CONFIG_DIR)
|
||||
if (userDetected.format !== "none") {
|
||||
try {
|
||||
const content = readFileSync(userDetected.path, "utf-8")
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
getOpenCodeConfigDir,
|
||||
addConfigLoadError,
|
||||
parseJsonc,
|
||||
detectConfigFile,
|
||||
detectPluginConfigFile,
|
||||
migrateConfigFile,
|
||||
} from "./shared";
|
||||
|
||||
@@ -162,20 +162,19 @@ export function loadPluginConfig(
|
||||
): OhMyOpenCodeConfig {
|
||||
// User-level config path - prefer .jsonc over .json
|
||||
const configDir = getOpenCodeConfigDir({ binary: "opencode" });
|
||||
const userBasePath = path.join(configDir, "oh-my-opencode");
|
||||
const userDetected = detectConfigFile(userBasePath);
|
||||
const userDetected = detectPluginConfigFile(configDir);
|
||||
const userConfigPath =
|
||||
userDetected.format !== "none"
|
||||
? userDetected.path
|
||||
: userBasePath + ".json";
|
||||
: path.join(configDir, "oh-my-openagent.json");
|
||||
|
||||
// Project-level config path - prefer .jsonc over .json
|
||||
const projectBasePath = path.join(directory, ".opencode", "oh-my-opencode");
|
||||
const projectDetected = detectConfigFile(projectBasePath);
|
||||
const projectBasePath = path.join(directory, ".opencode");
|
||||
const projectDetected = detectPluginConfigFile(projectBasePath);
|
||||
const projectConfigPath =
|
||||
projectDetected.format !== "none"
|
||||
? projectDetected.path
|
||||
: projectBasePath + ".json";
|
||||
: path.join(projectBasePath, "oh-my-openagent.json");
|
||||
|
||||
// Load user config first (base)
|
||||
let config: OhMyOpenCodeConfig =
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, test } from "bun:test"
|
||||
import { detectConfigFile, parseJsonc, parseJsoncSafe, readJsoncFile } from "./jsonc-parser"
|
||||
import { detectConfigFile, detectPluginConfigFile, parseJsonc, parseJsoncSafe, readJsoncFile } from "./jsonc-parser"
|
||||
import { existsSync, mkdirSync, rmSync, writeFileSync } from "node:fs"
|
||||
import { join } from "node:path"
|
||||
|
||||
@@ -264,3 +264,84 @@ describe("detectConfigFile", () => {
|
||||
expect(result.format).toBe("none")
|
||||
})
|
||||
})
|
||||
|
||||
describe("detectPluginConfigFile", () => {
|
||||
const testDir = join(__dirname, ".test-detect-plugin")
|
||||
|
||||
test("prefers oh-my-openagent over oh-my-opencode", () => {
|
||||
// given
|
||||
if (!existsSync(testDir)) mkdirSync(testDir, { recursive: true })
|
||||
writeFileSync(join(testDir, "oh-my-openagent.jsonc"), "{}")
|
||||
writeFileSync(join(testDir, "oh-my-opencode.jsonc"), "{}")
|
||||
|
||||
// when
|
||||
const result = detectPluginConfigFile(testDir)
|
||||
|
||||
// then
|
||||
expect(result.format).toBe("jsonc")
|
||||
expect(result.path).toBe(join(testDir, "oh-my-openagent.jsonc"))
|
||||
|
||||
rmSync(testDir, { recursive: true, force: true })
|
||||
})
|
||||
|
||||
test("falls back to oh-my-opencode when oh-my-openagent doesn't exist", () => {
|
||||
// given
|
||||
if (!existsSync(testDir)) mkdirSync(testDir, { recursive: true })
|
||||
writeFileSync(join(testDir, "oh-my-opencode.jsonc"), "{}")
|
||||
|
||||
// when
|
||||
const result = detectPluginConfigFile(testDir)
|
||||
|
||||
// then
|
||||
expect(result.format).toBe("jsonc")
|
||||
expect(result.path).toBe(join(testDir, "oh-my-opencode.jsonc"))
|
||||
|
||||
rmSync(testDir, { recursive: true, force: true })
|
||||
})
|
||||
|
||||
test("falls back to oh-my-opencode.json when no jsonc exists", () => {
|
||||
// given
|
||||
if (!existsSync(testDir)) mkdirSync(testDir, { recursive: true })
|
||||
writeFileSync(join(testDir, "oh-my-opencode.json"), "{}")
|
||||
|
||||
// when
|
||||
const result = detectPluginConfigFile(testDir)
|
||||
|
||||
// then
|
||||
expect(result.format).toBe("json")
|
||||
expect(result.path).toBe(join(testDir, "oh-my-opencode.json"))
|
||||
|
||||
rmSync(testDir, { recursive: true, force: true })
|
||||
})
|
||||
|
||||
test("returns none when no config files exist", () => {
|
||||
// given
|
||||
const emptyDir = join(testDir, "empty")
|
||||
if (!existsSync(emptyDir)) mkdirSync(emptyDir, { recursive: true })
|
||||
|
||||
// when
|
||||
const result = detectPluginConfigFile(emptyDir)
|
||||
|
||||
// then
|
||||
expect(result.format).toBe("none")
|
||||
expect(result.path).toBe(join(emptyDir, "oh-my-openagent.json"))
|
||||
|
||||
rmSync(testDir, { recursive: true, force: true })
|
||||
})
|
||||
|
||||
test("prefers oh-my-openagent.json over oh-my-opencode.jsonc", () => {
|
||||
// given
|
||||
if (!existsSync(testDir)) mkdirSync(testDir, { recursive: true })
|
||||
writeFileSync(join(testDir, "oh-my-openagent.json"), "{}")
|
||||
writeFileSync(join(testDir, "oh-my-opencode.jsonc"), "{}")
|
||||
|
||||
// when
|
||||
const result = detectPluginConfigFile(testDir)
|
||||
|
||||
// then
|
||||
expect(result.format).toBe("json")
|
||||
expect(result.path).toBe(join(testDir, "oh-my-openagent.json"))
|
||||
|
||||
rmSync(testDir, { recursive: true, force: true })
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { existsSync, readFileSync } from "node:fs"
|
||||
import { join } from "node:path"
|
||||
import { parse, ParseError, printParseErrorCode } from "jsonc-parser"
|
||||
|
||||
export interface JsoncParseResult<T> {
|
||||
@@ -64,3 +65,16 @@ export function detectConfigFile(basePath: string): {
|
||||
}
|
||||
return { format: "none", path: jsonPath }
|
||||
}
|
||||
|
||||
const PLUGIN_CONFIG_NAMES = ["oh-my-openagent", "oh-my-opencode"] as const
|
||||
|
||||
export function detectPluginConfigFile(dir: string): {
|
||||
format: "json" | "jsonc" | "none"
|
||||
path: string
|
||||
} {
|
||||
for (const name of PLUGIN_CONFIG_NAMES) {
|
||||
const result = detectConfigFile(join(dir, name))
|
||||
if (result.format !== "none") return result
|
||||
}
|
||||
return { format: "none", path: join(dir, PLUGIN_CONFIG_NAMES[0] + ".json") }
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import { join } from "path"
|
||||
import { BUILTIN_SERVERS } from "./constants"
|
||||
import type { ResolvedServer } from "./types"
|
||||
import { getOpenCodeConfigDir } from "../../shared"
|
||||
import { parseJsonc, detectConfigFile } from "../../shared/jsonc-parser"
|
||||
import { parseJsonc, detectConfigFile, detectPluginConfigFile } from "../../shared/jsonc-parser"
|
||||
|
||||
interface LspEntry {
|
||||
disabled?: boolean
|
||||
@@ -38,8 +38,8 @@ export function getConfigPaths(): { project: string; user: string; opencode: str
|
||||
const cwd = process.cwd()
|
||||
const configDir = getOpenCodeConfigDir({ binary: "opencode" })
|
||||
return {
|
||||
project: detectConfigFile(join(cwd, ".opencode", "oh-my-opencode")).path,
|
||||
user: detectConfigFile(join(configDir, "oh-my-opencode")).path,
|
||||
project: detectPluginConfigFile(join(cwd, ".opencode")).path,
|
||||
user: detectPluginConfigFile(configDir).path,
|
||||
opencode: detectConfigFile(join(configDir, "opencode")).path,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user