|
|
@@ -429,14 +429,14 @@ export namespace Config {
|
|
|
return load(text, filepath)
|
|
|
}
|
|
|
|
|
|
- async function load(text: string, filepath: string) {
|
|
|
+ async function load(text: string, configFilepath: string) {
|
|
|
text = text.replace(/\{env:([^}]+)\}/g, (_, varName) => {
|
|
|
return process.env[varName] || ""
|
|
|
})
|
|
|
|
|
|
const fileMatches = text.match(/\{file:[^}]+\}/g)
|
|
|
if (fileMatches) {
|
|
|
- const configDir = path.dirname(filepath)
|
|
|
+ const configDir = path.dirname(configFilepath)
|
|
|
const lines = text.split("\n")
|
|
|
|
|
|
for (const match of fileMatches) {
|
|
|
@@ -449,7 +449,20 @@ export namespace Config {
|
|
|
filePath = path.join(os.homedir(), filePath.slice(2))
|
|
|
}
|
|
|
const resolvedPath = path.isAbsolute(filePath) ? filePath : path.resolve(configDir, filePath)
|
|
|
- const fileContent = (await Bun.file(resolvedPath).text()).trim()
|
|
|
+ const fileContent = (
|
|
|
+ await Bun.file(resolvedPath)
|
|
|
+ .text()
|
|
|
+ .catch((error) => {
|
|
|
+ const errMsg = `bad file reference: "${match}"`
|
|
|
+ if (error.code === "ENOENT") {
|
|
|
+ throw new InvalidError(
|
|
|
+ { path: configFilepath, message: errMsg + ` ${resolvedPath} does not exist` },
|
|
|
+ { cause: error },
|
|
|
+ )
|
|
|
+ }
|
|
|
+ throw new InvalidError({ path: configFilepath, message: errMsg }, { cause: error })
|
|
|
+ })
|
|
|
+ ).trim()
|
|
|
// escape newlines/quotes, strip outer quotes
|
|
|
text = text.replace(match, JSON.stringify(fileContent).slice(1, -1))
|
|
|
}
|
|
|
@@ -474,7 +487,7 @@ export namespace Config {
|
|
|
.join("\n")
|
|
|
|
|
|
throw new JsonError({
|
|
|
- path: filepath,
|
|
|
+ path: configFilepath,
|
|
|
message: `\n--- JSONC Input ---\n${text}\n--- Errors ---\n${errorDetails}\n--- End ---`,
|
|
|
})
|
|
|
}
|
|
|
@@ -483,21 +496,21 @@ export namespace Config {
|
|
|
if (parsed.success) {
|
|
|
if (!parsed.data.$schema) {
|
|
|
parsed.data.$schema = "https://opencode.ai/config.json"
|
|
|
- await Bun.write(filepath, JSON.stringify(parsed.data, null, 2))
|
|
|
+ await Bun.write(configFilepath, JSON.stringify(parsed.data, null, 2))
|
|
|
}
|
|
|
const data = parsed.data
|
|
|
if (data.plugin) {
|
|
|
for (let i = 0; i < data.plugin?.length; i++) {
|
|
|
const plugin = data.plugin[i]
|
|
|
try {
|
|
|
- data.plugin[i] = import.meta.resolve(plugin, filepath)
|
|
|
+ data.plugin[i] = import.meta.resolve(plugin, configFilepath)
|
|
|
} catch (err) {}
|
|
|
}
|
|
|
}
|
|
|
return data
|
|
|
}
|
|
|
|
|
|
- throw new InvalidError({ path: filepath, issues: parsed.error.issues })
|
|
|
+ throw new InvalidError({ path: configFilepath, issues: parsed.error.issues })
|
|
|
}
|
|
|
export const JsonError = NamedError.create(
|
|
|
"ConfigJsonError",
|
|
|
@@ -512,6 +525,7 @@ export namespace Config {
|
|
|
z.object({
|
|
|
path: z.string(),
|
|
|
issues: z.custom<z.ZodIssue[]>().optional(),
|
|
|
+ message: z.string().optional(),
|
|
|
}),
|
|
|
)
|
|
|
|