Skip to content
Open
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
3 changes: 3 additions & 0 deletions packages/opencode/src/cli/cmd/tui/context/exit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ export const { use: useExit, provider: ExitProvider } = createSimpleContext({
},
)
process.on("SIGHUP", () => exit())
process.on("SIGINT", () => exit())
process.on("SIGTERM", () => exit())
process.on("beforeExit", () => exit())
return exit
},
})
2 changes: 2 additions & 0 deletions packages/opencode/src/cli/cmd/tui/thread.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,8 @@ export const TuiThreadCommand = cmd({
process.on("uncaughtException", error)
process.on("unhandledRejection", error)
process.on("SIGUSR2", reload)
process.on("SIGINT", () => stop())
process.on("SIGTERM", () => stop())

let stopped = false
const stop = async () => {
Expand Down
8 changes: 5 additions & 3 deletions packages/opencode/src/project/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import * as CrossSpawnSpawner from "@/effect/cross-spawn-spawner"
import { zod } from "@/util/effect-zod"
import { withStatics } from "@/util/schema"

// Best-effort: never let project-id bookkeeping crash startup (e.g. EEXIST/EPERM
// from mkdir on Windows network drives or read-only .git dirs).
async function setupProjectIdEnvironment(workingDir: string): Promise<void> {
const mainGit = resolveMainGitDir(workingDir)
if (!mainGit) return
Expand All @@ -29,19 +31,19 @@ async function setupProjectIdEnvironment(workingDir: string): Promise<void> {
if (await Bun.file(localFile).exists()) {
if (!(await Bun.file(idFile).exists())) {
const id = await Bun.file(localFile).text()
await Bun.write(idFile, id)
await Bun.write(idFile, id).catch(() => {})
}
await nodeFs.unlink(localFile).catch(() => {})
}

// Belt-and-suspenders: ensure .git/info/exclude lists .mimocode-project-id
const excludeFile = nodePath.join(mainGit, "info", "exclude")
await nodeFs.mkdir(nodePath.dirname(excludeFile), { recursive: true })
await nodeFs.mkdir(nodePath.dirname(excludeFile), { recursive: true }).catch(() => {})
const existing = await Bun.file(excludeFile)
.text()
.catch(() => "")
if (!existing.includes(".mimocode-project-id")) {
await nodeFs.appendFile(excludeFile, "\n.mimocode-project-id\n")
await nodeFs.appendFile(excludeFile, "\n.mimocode-project-id\n").catch(() => {})
}
}

Expand Down
19 changes: 18 additions & 1 deletion packages/opencode/src/util/log.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ const levelPriority: Record<Level, number> = {
ERROR: 3,
}
const keep = 10
// Cap individual log files so runaway logging can't fill the disk; with `keep`
// pruning this bounds the log directory to roughly keep * maxFileSize.
const maxFileSize = 20 * 1024 * 1024

let level: Level = "INFO"

Expand Down Expand Up @@ -79,7 +82,21 @@ export async function init(options: Options) {
await fs.truncate(logpath).catch(() => {})
}
const stream = createWriteStream(logpath, { flags: "a" })
let written = (await fs.stat(logpath).catch(() => null))?.size ?? 0
let rotations = 0
const rotate = () => {
stream.end()
rotations++
const stamp = new Date().toISOString().split(".")[0].replace(/:/g, "")
// Counter suffix keeps the name unique even when rotating within a second
logpath = path.join(Global.Path.log, `${stamp}_${rotations}.log`)
stream = createWriteStream(logpath, { flags: "a" })
written = 0
void cleanup(Global.Path.log)
}
write = async (msg: any) => {
if (!options.dev && written >= maxFileSize) rotate()
written += Buffer.byteLength(msg)
return new Promise((resolve, reject) => {
stream.write(msg, (err) => {
if (err) reject(err)
Expand All @@ -91,7 +108,7 @@ export async function init(options: Options) {

async function cleanup(dir: string) {
const files = (
await Glob.scan("????-??-??T??????.log", {
await Glob.scan("????-??-??T??????*.log", {
cwd: dir,
absolute: false,
include: "file",
Expand Down