Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 5 additions & 6 deletions packages/opencode/src/cli/cmd/mcp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { Installation } from "../../installation"
import path from "path"
import { Global } from "../../global"
import { modify, applyEdits } from "jsonc-parser"
import { Filesystem } from "../../util/filesystem"
import { Bus } from "../../bus"

function getAuthStatusIcon(status: MCP.AuthStatus): string {
Expand Down Expand Up @@ -388,7 +389,7 @@ async function resolveConfigPath(baseDir: string, global = false) {
}

for (const candidate of candidates) {
if (await Bun.file(candidate).exists()) {
if (await Filesystem.exists(candidate)) {
return candidate
}
}
Expand All @@ -398,11 +399,9 @@ async function resolveConfigPath(baseDir: string, global = false) {
}

async function addMcpToConfig(name: string, mcpConfig: Config.Mcp, configPath: string) {
const file = Bun.file(configPath)

let text = "{}"
if (await file.exists()) {
text = await file.text()
if (await Filesystem.exists(configPath)) {
text = await Filesystem.readText(configPath)
}

// Use jsonc-parser to modify while preserving comments
Expand All @@ -411,7 +410,7 @@ async function addMcpToConfig(name: string, mcpConfig: Config.Mcp, configPath: s
})
const result = applyEdits(text, edits)

await Bun.write(configPath, result)
await Filesystem.write(configPath, result)

return configPath
}
Expand Down
76 changes: 35 additions & 41 deletions packages/opencode/src/config/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -255,19 +255,20 @@ export namespace Config {
const pkg = path.join(dir, "package.json")
const targetVersion = Installation.isLocal() ? "*" : Installation.VERSION

const json = await Bun.file(pkg)
.json()
.catch(() => ({}))
const json = await Filesystem.readJson<{ dependencies?: Record<string, string> }>(pkg).catch(() => ({
dependencies: {},
}))
json.dependencies = {
...json.dependencies,
"@opencode-ai/plugin": targetVersion,
}
await Bun.write(pkg, JSON.stringify(json, null, 2))
await Filesystem.writeJson(pkg, json)
await new Promise((resolve) => setTimeout(resolve, 3000))

const gitignore = path.join(dir, ".gitignore")
const hasGitIgnore = await Bun.file(gitignore).exists()
if (!hasGitIgnore) await Bun.write(gitignore, ["node_modules", "package.json", "bun.lock", ".gitignore"].join("\n"))
const hasGitIgnore = await Filesystem.exists(gitignore)
if (!hasGitIgnore)
await Filesystem.write(gitignore, ["node_modules", "package.json", "bun.lock", ".gitignore"].join("\n"))

// Install any additional dependencies defined in the package.json
// This allows local plugins and custom tools to use external packages
Expand Down Expand Up @@ -303,11 +304,10 @@ export namespace Config {
if (!existsSync(nodeModules)) return true

const pkg = path.join(dir, "package.json")
const pkgFile = Bun.file(pkg)
const pkgExists = await pkgFile.exists()
const pkgExists = await Filesystem.exists(pkg)
if (!pkgExists) return true

const parsed = await pkgFile.json().catch(() => null)
const parsed = await Filesystem.readJson<{ dependencies?: Record<string, string> }>(pkg).catch(() => null)
const dependencies = parsed?.dependencies ?? {}
const depVersion = dependencies["@opencode-ai/plugin"]
if (!depVersion) return true
Expand Down Expand Up @@ -1220,7 +1220,7 @@ export namespace Config {
if (provider && model) result.model = `${provider}/${model}`
result["$schema"] = "https://opencode.ai/config.json"
result = mergeDeep(result, rest)
await Bun.write(path.join(Global.Path.config, "config.json"), JSON.stringify(result, null, 2))
await Filesystem.writeJson(path.join(Global.Path.config, "config.json"), result)
await fs.unlink(legacy)
})
.catch(() => {})
Expand All @@ -1231,12 +1231,10 @@ export namespace Config {

async function loadFile(filepath: string): Promise<Info> {
log.info("loading", { path: filepath })
let text = await Bun.file(filepath)
.text()
.catch((err) => {
if (err.code === "ENOENT") return
throw new JsonError({ path: filepath }, { cause: err })
})
let text = await Filesystem.readText(filepath).catch((err: any) => {
if (err.code === "ENOENT") return
throw new JsonError({ path: filepath }, { cause: err })
})
if (!text) return {}
return load(text, filepath)
}
Expand All @@ -1263,21 +1261,19 @@ export namespace Config {
}
const resolvedPath = path.isAbsolute(filePath) ? filePath : path.resolve(configDir, filePath)
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 })
})
await Filesystem.readText(resolvedPath).catch((error: any) => {
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))
Expand Down Expand Up @@ -1314,7 +1310,7 @@ export namespace Config {
parsed.data.$schema = "https://opencode.ai/config.json"
// Write the $schema to the original text to preserve variables like {env:VAR}
const updated = original.replace(/^\s*\{/, '{\n "$schema": "https://opencode.ai/config.json",')
await Bun.write(configFilepath, updated).catch(() => {})
await Filesystem.write(configFilepath, updated).catch(() => {})
}
const data = parsed.data
if (data.plugin) {
Expand Down Expand Up @@ -1370,7 +1366,7 @@ export namespace Config {
export async function update(config: Info) {
const filepath = path.join(Instance.directory, "config.json")
const existing = await loadFile(filepath)
await Bun.write(filepath, JSON.stringify(mergeDeep(existing, config), null, 2))
await Filesystem.writeJson(filepath, mergeDeep(existing, config))
await Instance.dispose()
}

Expand Down Expand Up @@ -1441,24 +1437,22 @@ export namespace Config {

export async function updateGlobal(config: Info) {
const filepath = globalConfigFile()
const before = await Bun.file(filepath)
.text()
.catch((err) => {
if (err.code === "ENOENT") return "{}"
throw new JsonError({ path: filepath }, { cause: err })
})
const before = await Filesystem.readText(filepath).catch((err: any) => {
if (err.code === "ENOENT") return "{}"
throw new JsonError({ path: filepath }, { cause: err })
})

const next = await (async () => {
if (!filepath.endsWith(".jsonc")) {
const existing = parseConfig(before, filepath)
const merged = mergeDeep(existing, config)
await Bun.write(filepath, JSON.stringify(merged, null, 2))
await Filesystem.writeJson(filepath, merged)
return merged
}

const updated = patchJsonc(before, config)
const merged = parseConfig(updated, filepath)
await Bun.write(filepath, updated)
await Filesystem.write(filepath, updated)
return merged
})()

Expand Down
12 changes: 6 additions & 6 deletions packages/opencode/src/file/ripgrep.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import z from "zod"
import { NamedError } from "@opencode-ai/util/error"
import { lazy } from "../util/lazy"
import { $ } from "bun"
import { Filesystem } from "../util/filesystem"

import { ZipReader, BlobReader, BlobWriter } from "@zip.js/zip.js"
import { Log } from "@/util/log"
Expand Down Expand Up @@ -131,8 +132,7 @@ export namespace Ripgrep {
}
const filepath = path.join(Global.Path.bin, "rg" + (process.platform === "win32" ? ".exe" : ""))

const file = Bun.file(filepath)
if (!(await file.exists())) {
if (!(await Filesystem.exists(filepath))) {
const platformKey = `${process.arch}-${process.platform}` as keyof typeof PLATFORM
const config = PLATFORM[platformKey]
if (!config) throw new UnsupportedPlatformError({ platform: platformKey })
Expand All @@ -144,9 +144,9 @@ export namespace Ripgrep {
const response = await fetch(url)
if (!response.ok) throw new DownloadFailedError({ url, status: response.status })

const buffer = await response.arrayBuffer()
const arrayBuffer = await response.arrayBuffer()
const archivePath = path.join(Global.Path.bin, filename)
await Bun.write(archivePath, buffer)
await Filesystem.write(archivePath, Buffer.from(arrayBuffer))
if (config.extension === "tar.gz") {
const args = ["tar", "-xzf", archivePath, "--strip-components=1"]

Expand All @@ -166,7 +166,7 @@ export namespace Ripgrep {
})
}
if (config.extension === "zip") {
const zipFileReader = new ZipReader(new BlobReader(new Blob([await Bun.file(archivePath).arrayBuffer()])))
const zipFileReader = new ZipReader(new BlobReader(new Blob([arrayBuffer])))
const entries = await zipFileReader.getEntries()
let rgEntry: any
for (const entry of entries) {
Expand All @@ -190,7 +190,7 @@ export namespace Ripgrep {
stderr: "Failed to extract rg.exe from zip archive",
})
}
await Bun.write(filepath, await rgBlob.arrayBuffer())
await Filesystem.write(filepath, Buffer.from(await rgBlob.arrayBuffer()))
await zipFileReader.close()
}
await fs.unlink(archivePath)
Expand Down
Loading