Optimize workspace instruction change detection
This commit is contained in:
@@ -21,7 +21,7 @@
|
||||
import { randomUUID } from 'node:crypto'
|
||||
import { createReadStream } from 'node:fs'
|
||||
import { chmod, lstat, mkdir, open, readFile, realpath, readdir, rename, rm, stat } from 'node:fs/promises'
|
||||
import type { Dirent, Stats } from 'node:fs'
|
||||
import type { BigIntStats, Dirent, Stats } from 'node:fs'
|
||||
import { basename, dirname, join, resolve } from 'node:path'
|
||||
import { TextDecoder } from 'node:util'
|
||||
import { FsError, FsTargetKey, FsVersion } from '@deepseek-ai/dsh-fs'
|
||||
@@ -76,9 +76,9 @@ async function readFileAbortable(absolutePath: string, verb: 'read' | 'edit', si
|
||||
}
|
||||
}
|
||||
|
||||
/** Opaque version token from a stat: millisecond mtime plus byte size. */
|
||||
function versionOf(info: Stats): FsVersion {
|
||||
return FsVersion(`${info.mtimeMs}:${info.size}`)
|
||||
/** Opaque version token from high-resolution identity and freshness metadata. */
|
||||
function versionOf(info: BigIntStats): FsVersion {
|
||||
return FsVersion(`${info.dev}:${info.ino}:${info.size}:${info.mtimeNs}:${info.ctimeNs}`)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -176,18 +176,21 @@ export async function resolveLocalTarget(cwd: string, path: string): Promise<Loc
|
||||
}
|
||||
}
|
||||
|
||||
function pathType(info: Stats): PathInfo['type'] {
|
||||
function pathType(info: Stats | BigIntStats): PathInfo['type'] {
|
||||
if (info.isFile()) return 'file'
|
||||
if (info.isDirectory()) return 'directory'
|
||||
return 'other'
|
||||
}
|
||||
|
||||
function pathLinkType(info: Stats): PathLinkInfo['type'] {
|
||||
function pathLinkType(info: Stats | BigIntStats): PathLinkInfo['type'] {
|
||||
if (info.isSymbolicLink()) return 'symlink'
|
||||
return pathType(info)
|
||||
}
|
||||
|
||||
async function probeStats(absolutePath: string, readStats: (path: string) => Promise<Stats>): Promise<Stats | null> {
|
||||
async function probeStats<T extends Stats | BigIntStats>(
|
||||
absolutePath: string,
|
||||
readStats: (path: string) => Promise<T>,
|
||||
): Promise<T | null> {
|
||||
try {
|
||||
return await readStats(absolutePath)
|
||||
} catch (error: unknown) {
|
||||
@@ -206,9 +209,14 @@ async function probeStats(absolutePath: string, readStats: (path: string) => Pro
|
||||
* @returns the metadata, or null when the path — or a parent segment — does not exist.
|
||||
*/
|
||||
export async function probe(absolutePath: string): Promise<PathInfo | null> {
|
||||
const info = await probeStats(absolutePath, stat)
|
||||
const info = await probeStats(absolutePath, path => stat(path, { bigint: true }))
|
||||
if (!info) return null
|
||||
return { version: versionOf(info), mode: info.mode & 0o777, type: pathType(info), size: info.size }
|
||||
return {
|
||||
version: versionOf(info),
|
||||
mode: Number(info.mode & 0o777n),
|
||||
type: pathType(info),
|
||||
size: Number(info.size),
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -217,9 +225,14 @@ export async function probe(absolutePath: string): Promise<PathInfo | null> {
|
||||
* @returns path-entry metadata, or null when the entry is absent.
|
||||
*/
|
||||
export async function probeNoFollow(absolutePath: string): Promise<PathLinkInfo | null> {
|
||||
const info = await probeStats(absolutePath, lstat)
|
||||
const info = await probeStats(absolutePath, path => lstat(path, { bigint: true }))
|
||||
if (!info) return null
|
||||
return { version: versionOf(info), mode: info.mode & 0o777, type: pathLinkType(info), size: info.size }
|
||||
return {
|
||||
version: versionOf(info),
|
||||
mode: Number(info.mode & 0o777n),
|
||||
type: pathLinkType(info),
|
||||
size: Number(info.size),
|
||||
}
|
||||
}
|
||||
|
||||
// --- Directory listing ---
|
||||
|
||||
Reference in New Issue
Block a user