| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869 |
- import { Instance } from "../project/instance"
- import { Log } from "../util/log"
- import { Flag } from "../flag/flag"
- export namespace FileTime {
- const log = Log.create({ service: "file.time" })
- // Per-session read times plus per-file write locks.
- // All tools that overwrite existing files should run their
- // assert/read/write/update sequence inside withLock(filepath, ...)
- // so concurrent writes to the same file are serialized.
- export const state = Instance.state(() => {
- const read: {
- [sessionID: string]: {
- [path: string]: Date | undefined
- }
- } = {}
- const locks = new Map<string, Promise<void>>()
- return {
- read,
- locks,
- }
- })
- export function read(sessionID: string, file: string) {
- log.info("read", { sessionID, file })
- const { read } = state()
- read[sessionID] = read[sessionID] || {}
- read[sessionID][file] = new Date()
- }
- export function get(sessionID: string, file: string) {
- return state().read[sessionID]?.[file]
- }
- export async function withLock<T>(filepath: string, fn: () => Promise<T>): Promise<T> {
- const current = state()
- const currentLock = current.locks.get(filepath) ?? Promise.resolve()
- let release: () => void = () => {}
- const nextLock = new Promise<void>((resolve) => {
- release = resolve
- })
- const chained = currentLock.then(() => nextLock)
- current.locks.set(filepath, chained)
- await currentLock
- try {
- return await fn()
- } finally {
- release()
- if (current.locks.get(filepath) === chained) {
- current.locks.delete(filepath)
- }
- }
- }
- export async function assert(sessionID: string, filepath: string) {
- if (Flag.OPENCODE_DISABLE_FILETIME_CHECK === true) {
- return
- }
- const time = get(sessionID, filepath)
- if (!time) throw new Error(`You must read file ${filepath} before overwriting it. Use the Read tool first`)
- const stats = await Bun.file(filepath).stat()
- if (stats.mtime.getTime() > time.getTime()) {
- throw new Error(
- `File ${filepath} has been modified since it was last read.\nLast modification: ${stats.mtime.toISOString()}\nLast read: ${time.toISOString()}\n\nPlease read the file again before modifying it.`,
- )
- }
- }
- }
|