mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2026-07-08 08:27:39 +08:00
### What this PR does
Adds the **Phase 1a contract surface** for the file module — types, DB
schema, DataApi + File IPC contracts, FileManager skeleton, and
architecture docs.
**Phase 1b.1 (Read Path & Repository), 1b.2 (Write Path & Lifecycle),
1b.3 (Watcher & DanglingCache), and 1b.4 (OrphanSweep &
FileRefCheckerRegistry) are now all landed on top of 1a in this same
PR.** This is the complete Phase 1b runtime — reviewers see the full
read + write + watcher + orphan-sweep picture in one place.
Design, contracts, and decision rationale live in the architecture docs:
-
[`docs/references/file/architecture.md`](docs/references/file/architecture.md)
— module boundaries, type system, IPC/DataApi contracts, layered
architecture, service lifecycle, mutation propagation
-
[`docs/references/file/file-manager-architecture.md`](docs/references/file/file-manager-architecture.md)
— FileManager internals (storage, version detection, atomic writes,
reference cleanup, DirectoryWatcher, orphan sweep, DanglingCache state
machine, key design decisions)
#### Phase 1a deliverables
- Types (`FileEntry` / `FileInfo` / `FileHandle` /
`CanonicalExternalPath` brand)
- DB schema (`file_entry` + `file_ref`) with per-origin CHECK
constraints
- DataApi schemas + stub handlers
- File IPC contract (polymorphic `FileHandle` dispatch;
`batchGetMetadata` included)
- FileManager skeleton + `internal/*` + `ops/*` + `watcher/` +
`DanglingCache` + `versionCache`
- Mutation propagation design (three typed events + prefix-based
queryKey invalidation)
#### Phase 1b.1 deliverables (read-path runtime)
- Shared utilities: `getFileTypeByExt`, `sanitizeFilename`,
`validateFileName` extracted to `@shared/file/types`
- Path utilities: `canonicalizeExternalPath` (NFC + null-byte guard +
trailing-sep strip), `isPathInside`, `isUnderInternalStorage`,
`canWrite`
- FS read primitives: `stat`, `exists`, `read` (text/base64/binary
overloads), `hash` (initial MD5; swapped to xxhash-h64 in 1b.2)
- Metadata utilities: `getFileType(path)`, `isTextFile`, `mimeToExt`
- Repositories: `FileEntryService` + `FileRefService` read methods
(Drizzle-backed, Zod-branded outputs)
- Pure-function modules: `internal/content/{read,hash}`,
`internal/dispatch.ts` (FileHandle dispatcher)
- `toFileInfo(entry)` projection
- `FileManager` class as `BaseService` (`@Injectable('FileManager')`
`@ServicePhase(WhenReady)`); read methods only
- `DanglingCache` + `VersionCache` minimal viable singletons (full impls
in 1b.3 / 1b.2)
- DataApi `/files/*` read handlers fully implemented (entries / single /
ref-counts / refs-by-source)
- 60+ TDD tests (unit + boundary + setupTestDatabase integration)
#### Phase 1b.2 deliverables (write-path runtime)
- **FS atomic primitives** (open to non-file-module consumers per
architecture §5.3): `atomicWriteFile` (tmp + fsync + rename +
fsync(dir)), `atomicWriteIfUnchanged` (re-stat OCC + content-hash
fallback for second-precision mtime), `createAtomicWriteStream`
(Writable wrapper, abort/destroy unlinks tmp)
- **FS general primitives**: `write` (delegates to atomicWriteFile),
`copy` (atomic dest), `move` (rename + EXDEV → copy+unlink fallback),
`remove` (idempotent ENOENT), `mkdir` / `ensureDir` / `removeDir`,
`download` (fetch → atomic stream)
- **Hash swap**: `hash()` migrated MD5 → `xxhash-wasm` h64 streaming;
legacy `md5` dep retained for KnowledgeService loaders
- **VersionCache LRU**: capacity-bounded (default 2000) with
re-insert-on-touch recency
- **Repository mutations**: `FileEntryService.create/update/delete`
(auto UUIDv7 default id, raw DB CHECK errors propagate);
`FileRefService.create/createMany/cleanupBySource/cleanupBySourceBatch`
(`onConflictDoNothing` for batch upsert)
- **internal/entry/**: `create.createInternal` (4 source variants: bytes
/ base64 / path / url) + `create.ensureExternal` (canonicalize + stat +
idempotent upsert + duplicate-suspect peer warn);
`lifecycle.trash/restore/permanentDelete` + batch variants (DB+FS
decoupled, internal best-effort unlink); `rename` (internal DB-only,
external fs.move + canonical externalPath); `copy` (pipes through
createInternal with rollback)
- **internal/content/write.ts**: `write` / `writeIfUnchanged`
(cache-not-trusted re-stat OCC, `StaleVersionError` rewrap from
`PathStaleVersionError`) / `createWriteStream` / `*ByPath` variants
- **internal/system/**: `shell.open` / `shell.showInFolder` (electron
`shell` wrappers); `tempCopy.withTempCopy` (isolated tmp dir; cleanup on
throw)
- **FileManager facade**: every IFileManager mutation method now
delegates to its `internal/*` counterpart (`createInternalEntry` /
`ensureExternalEntry` / `batchCreate*` / `batchEnsure*` / `write` /
`writeIfUnchanged` / `createWriteStream` / `createReadStream` / `trash`
/ `restore` / `permanentDelete` + batch / `rename` / `copy` /
`withTempCopy` / `open` / `showInFolder`); no method throws
notImplemented anymore
- ~60 new TDD tests (each behavior unit = one RED→GREEN→REFACTOR
commit); end-to-end integration scenarios via `setupTestDatabase` cover
atomic-rollback zero-residue, OCC second-precision-mtime
no-false-positive, trash-external CHECK enforcement, full
create→write→read→trash→restore→permanentDelete round-trip, and external
permanentDelete-leaves-user-file-untouched
#### Phase 1b.3 deliverables (watcher + DanglingCache observability)
- **DanglingCache class** (replaces 1a const-literal skeleton):
`byEntryId: Map<entryId, CachedState>` + `pathToEntryIds:
Map<canonicalPath, Set<entryId>>` reverse index, lazy TTL expiration
(default 30 min per architecture §11.2), `forceRecheck` escape hatch,
`Emitter<DanglingStateChangedEvent>` firing only on genuine state
transitions (same-state observations are silent). Injectable `now` /
`statProbe` / `ttlMs` / `fileEntryService` seams for deterministic
tests.
- **createDirectoryWatcher** chokidar v4 wrapper: `add` / `unlink` /
`change` / `ready` / `error` events; built-in OS-junk basename ignores
(`.DS_Store` / `.localized` / `Thumbs.db` / `desktop.ini`); idempotent
`close()`. Factory auto-wires `add` →
`danglingCache.onFsEvent(path,'present')` and `unlink` → `'missing'`.
(Architecture §8.2's richer `onAddDir`/`onUnlinkDir`/`onRename` events
deferred — no consumer needs them in scope.)
- **Reverse-index maintenance from mutation flows**: `ensureExternal`
calls `addEntry` + `onFsEvent('present','ops')` on insert (no-op on
reuse); `permanentDelete(external)` calls `removeEntry`;
`rename(external)` swaps `removeEntry(oldPath) + addEntry(newPath) +
onFsEvent(newPath,'present','ops')`.
- **FileManager surface**: `getDanglingState({id})` (internal →
'present', external → cache check, unknown id → 'unknown');
`batchGetDanglingStates({ids})` (parallel fan-out, unknown ids mapped to
'unknown'); `subscribeDangling({id}, listener)` (in-process per-entry
filter; renderer fan-out via `file-manager-event` IPC channel deferred
to Phase 2).
- **FileManager.onInit**: awaits `danglingCache.initFromDb()` (populates
reverse index from non-trashed external entries; no startup stat probe
per architecture §10.6); registers `File_GetDanglingState` /
`File_BatchGetDanglingStates` IPC handlers via `this.ipcHandle`
(auto-disposed on stop).
- New `IpcChannel` constants: `File_GetDanglingState`,
`File_BatchGetDanglingStates`.
- ~30 new TDD tests across DanglingCache (18 unit) + watcher (6 real-FS)
+ FileManager integration (INT-7..INT-10).
#### Phase 1b.4 deliverables (orphan sweep + FileRefCheckerRegistry)
- **FileRefCheckerRegistry**: `Record<FileRefSourceType,
SourceTypeChecker<...>>` typed registry forces exhaustive coverage at
compile time — adding a new variant to `FileRefSourceType` without a
checker triggers a TS build error. Phase 1 ships `FileRefSourceType =
'temp_session' | 'knowledge_item'`: real DB-backed checker for
`knowledge_item` (Drizzle `inArray` against `knowledge_item`);
`temp_session` checker treats every sourceId as gone (sessions are
in-memory only). `chat_message` / `painting` / `note` are **deliberately
not in the union yet** — each will be added in lockstep (tuple entry in
`allSourceTypes` + `createRefSchema` variant + `SourceTypeChecker`) by
the PR that migrates the owning domain's DB tables to v2. Stray writes
during the migration window fail fast at `FileRefSchema.parse` rather
than being silently persisted under a no-op stub.
- **OrphanRefScanner** (RFC §6.4): `scanOneType(sourceType)` enumerates
distinct `file_ref.sourceId` per type, asks the checker which are alive,
deletes the rest via `cleanupBySourceBatch`. `scanAll()` aggregates
across every registered sourceType. Backed by new
`FileRefService.listDistinctSourceIds` to keep all SQL inside the repo.
- **Report-only orphan-entry pass** (architecture §7.1 default policy is
"preserve"): `scanOrphanEntries` groups active entries with zero
`file_ref` rows by origin. **No deletion** — surfaced via
`getOrphanReport()` for the cleanup-UI consumer. Backed by new
`FileEntryService.findUnreferenced` LEFT JOIN-based query.
- **Startup file sweep** (architecture §10): `runStartupFileSweep`
snapshots `file_entry.id` (active + trashed) into a `Set` via new
`FileEntryService.listAllIds`, walks `{userData}/files/`, plans unlink
for (a) UUID-named files whose id is not in the snapshot and (b)
`*.tmp-<UUID>` atomic-write residue. Applies the `mtime > 5min`
freshness gate (§10.3) — files newer than that are presumed in-flight
and preserved. Plan-then-execute with the `50% / 20-count-floor /
10MB-floor` safety threshold (§10.4); aborts emit
`abortReason='count-fraction'|'byte-fraction'`. Single structured
`orphan-file-sweep` log per run (info / warn / error per outcome,
§10.5).
- **DB-sweep umbrella + observability** (`runDbSweep`): runs scanAll +
scanOrphanEntries, emits one `orphan-sweep` structured record
summarising both passes; failure path returns `outcome='failed'` +
`errorMessage` so callers don't throw on background fire-and-forget.
- **FileManager integration**: `onInit` schedules a fire-and-forget
`runStartupSweeps` that runs the FS-level + DB-level sweeps in parallel;
failures of either are logged but never block ready. `getOrphanReport()`
exposes the most recent `DbSweepReport` (orphan-ref counts already
cleaned + orphan-entry counts preserved) + `lastRunAt` for the cleanup
UI surface.
- ~30 new TDD tests across registry (14 unit) + orphan sweep (16 unit +
integration) + FileManager integration (INT-11/INT-12) + repo
(`findUnreferenced`, `listAllIds`, `listDistinctSourceIds`).
**Out of scope (deferred to Phase 2)**:
- Architecture §7.2 dangling-external auto-cleanup (external + missing +
0-ref + >30d retention) — narrow extension shipping with the cleanup UI.
- Adding `chat_message` / `painting` / `note` as `FileRefSourceType`
variants (tuple entry + schema + checker added together) — gated on each
domain's v2 batch migration.
- Cleanup-UI surface that consumes `getOrphanReport()` — Phase 2
renderer work.
Renderer-side File IPC bridge for write/dangling methods stays deferred
to Phase 2 alongside the consumer-batch migrations. The Phase 1b runtime
is consumable from main-side business services through
`application.get('FileManager')`.
### Why we need it and why it was done in this way
Contract-first concentrates design review in one place; Phase 1b.x then
becomes pure "honor the contracts". Each 1b.x phase keeps strict TDD
(RED → GREEN → REFACTOR per behavior, ~one commit per cycle); each phase
ends with a verification gate (push → CI green) before the next phase
begins.
Core decisions (origin two-state, `FileEntry`/`FileInfo` split, DataApi
SQL-only, external `permanentDelete` DB-only, TTL `DanglingCache`, OCC
trust boundary, atomic write fsync default, etc.) and their rationale
are recorded in `file-manager-architecture.md §12 Key Design Decisions`
— not duplicated here.
### Breaking changes
None — purely additive (read+write paths are new, no existing callers
replaced yet).
### Special notes for your reviewer
- Review focus: contracts (1a) + read-path runtime (1b.1) + write-path
runtime (1b.2) + watcher/DanglingCache (1b.3) + orphan sweep / registry
(1b.4). Phase 1b is now complete on this branch.
- **Phase 1a contract stability policy** (architecture.md top) is
binding — any 1b.x PR that finds a contract mismatch PRs the doc
revision first.
- Deferred (Phase 2): renderer-side File IPC bridge for
write/dangling/orphan methods (alongside consumer migration); cleanup-UI
surface consuming `getOrphanReport()`; architecture §7.2
dangling-external auto-cleanup (>30d retention); adding `chat_message` /
`painting` / `note` as `FileRefSourceType` variants (each adds tuple
entry + schema + checker in lockstep, gated on the owning domain's v2
batch migration); DanglingCache periodic snapshot logger (architecture
§11.8); `listDirectory` ripgrep wrapper; `compressImage`
(KnowledgeService consumer); FileUploadService + `file_upload` table
(Vercel AI SDK Files API).
### Checklist
- [x] PR: The PR description is expressive enough and will help future
contributors
- [x] Code: Write code that humans can understand and Keep it simple
- [x] Refactor: You have left the code cleaner than you found it (Boy
Scout Rule)
- [ ] Upgrade: N/A — purely additive
- [ ] Documentation: Internal architecture docs included
(`docs/references/file/`); no user-facing docs change
- [x] Self-review: I have reviewed my own code before requesting review
from others
### Release note
```release-note
NONE
```
---------
Signed-off-by: icarus <eurfelux@gmail.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: fullex <106392080+0xfullex@users.noreply.github.com>
39 KiB
39 KiB
Main Process fs Usage Audit
追踪
src/main/中所有直接使用 Node.jsfs模块的文件, 为 File Manager 统一封装提供迁移依据。
- 生成日期: 2026-04-06
- 分析范围:
src/main/下非测试文件(测试文件单独列出)
图例
| 标记 | 含义 |
|---|---|
sync |
使用同步 API(readFileSync 等) |
async |
使用异步/Promise API(fs/promises、回调) |
stream |
使用流 API(createReadStream 等) |
watch |
使用文件监听(watch/watchFile) |
stat |
使用 stat/lstat/access 等元数据 API |
dir |
使用目录操作(mkdir/readdir/rmdir) |
rw |
使用读写操作(readFile/writeFile) |
del |
使用删除操作(unlink/rm) |
copy |
使用复制/重命名(copyFile/rename) |
1. Utils(src/main/utils/)
utils/index.ts
- Import:
import fs from 'node:fs',import fsAsync from 'node:fs/promises' - Tags:
syncasyncrwstatdir
| API | 同步/异步 | 用途 |
|---|---|---|
fs.existsSync() |
同步 | 创建前检查数据目录是否存在 |
fs.mkdirSync() |
同步 | 递归创建数据目录 |
fsAsync.readdir() |
异步 | 列出目录内容用于计算大小 |
fsAsync.stat() |
异步 | 获取文件/目录元数据用于计算大小 |
utils/file.ts
- Import:
import * as fs from 'node:fs',import { readFile } from 'node:fs/promises' - Tags:
syncasyncrwstatdirdelcopy
| API | 同步/异步 | 用途 |
|---|---|---|
fs.readdirSync() |
同步 | 递归扫描目录查找匹配文件 |
fs.statSync() |
同步 | 获取文件信息以判断是目录还是文件 |
fs.existsSync() |
同步 | 检查笔记目录是否存在 |
fs.mkdirSync() |
同步 | 递归创建笔记目录 |
fs.promises.access() |
异步 | 检查目录的写权限 |
readFile() |
异步 | 自动检测编码并读取文本文件内容 |
fs.promises.open() |
异步 | 打开锁文件用于原子写入操作 |
fs.promises.writeFile() |
异步 | 写入文件内容(原子或非原子) |
fs.promises.rename() |
异步 | 将临时文件重命名为最终文件名(原子写入) |
fs.promises.unlink() |
异步 | 删除锁文件或临时文件 |
fs.promises.stat() |
异步 | 获取文件信息以检查锁的过期状态 |
fs.promises.readFile() |
异步 | 读取二进制文件用于 base64 编码 |
fs.promises.readdir() |
异步 | 读取目录条目(含文件类型信息) |
utils/fileOperations.ts
- Import:
import * as fs from 'node:fs' - Tags:
asynccopydeldirrwstat
| API | 同步/异步 | 用途 |
|---|---|---|
fs.promises.lstat() |
异步 | 获取文件信息以检测符号链接(不跟随) |
fs.promises.mkdir() |
异步 | 递归创建目标目录 |
fs.promises.readdir() |
异步 | 列出目录条目(含文件类型信息) |
fs.promises.copyFile() |
异步 | 复制单个文件 |
fs.promises.chmod() |
异步 | 复制后保留文件权限 |
fs.promises.rm() |
异步 | 强制递归删除目录 |
utils/init.ts
- Import:
import * as fs from 'node:fs' - Tags:
syncrwstat
| API | 同步/异步 | 用途 |
|---|---|---|
fs.accessSync() |
同步 | 检查目录的写权限 |
fs.existsSync() |
同步 | 读取前检查配置文件是否存在 |
fs.readFileSync() |
同步 | 同步读取 config.json 文件 |
fs.mkdirSync() |
同步 | 递归创建配置目录 |
fs.writeFileSync() |
同步 | 同步写入 config.json 文件 |
utils/process.ts
- Import:
import fs from 'fs' - Tags:
syncstat
| API | 同步/异步 | 用途 |
|---|---|---|
fs.existsSync() |
同步 | 检查 git 可执行文件是否存在 |
fs.existsSync() |
同步 | 检查二进制目录是否存在 |
utils/rtk.ts
- Import:
import fs from 'node:fs' - Tags:
syncrwstatcopy
| API | 同步/异步 | 用途 |
|---|---|---|
fs.existsSync() |
同步 | 检查打包的/已安装的 rtk 二进制文件是否存在 |
fs.mkdirSync() |
同步 | 递归创建用户 bin 目录 |
fs.readFileSync() |
同步 | 读取版本文件内容 |
fs.copyFileSync() |
同步 | 从打包目录复制 rtk 二进制到用户 bin 目录 |
fs.chmodSync() |
同步 | 设置复制后二进制的可执行权限(仅 Unix) |
fs.writeFileSync() |
同步 | 写入版本文件以记录已安装版本 |
utils/ocr.ts
- Import:
import { readFile } from 'fs/promises' - Tags:
asyncrw
| API | 同步/异步 | 用途 |
|---|---|---|
readFile() |
异步 | 读取图片文件到 buffer 用于预处理 |
utils/markdownParser.ts
- Import:
import * as fs from 'fs' - Tags:
asyncrwstatdir
| API | 同步/异步 | 用途 |
|---|---|---|
fs.promises.stat() |
异步 | 检查技能 markdown 文件是否存在并获取信息 |
fs.promises.readFile() |
异步 | 读取 markdown 文件内容用于解析 |
fs.promises.readdir() |
异步 | 列出目录条目用于技能发现 |
utils/builtinSkills.ts
- Import:
import fs from 'node:fs/promises' - Tags:
asyncrwdircopydelstat
| API | 同步/异步 | 用途 |
|---|---|---|
fs.access() |
异步 | 检查资源技能目录是否可访问 |
fs.readdir() |
异步 | 列出内置技能目录 |
fs.mkdir() |
异步 | 创建全局技能目录和符号链接父目录 |
fs.cp() |
异步 | 递归复制技能目录(从资源到存储) |
fs.writeFile() |
异步 | 写入版本文件以追踪已安装的 app 版本 |
fs.readlink() |
异步 | 检查现有符号链接的目标 |
fs.rm() |
异步 | 移除错误的符号链接或过期条目 |
fs.symlink() |
异步 | 创建 junction 符号链接用于技能发现 |
fs.readFile() |
异步 | 读取 SKILL.md 文件用于哈希计算 |
2. Services(src/main/services/,非 agent)
services/AppService.ts
- Import:
import fs from 'fs' - Tags:
asyncdirrwdelstat
| API | 同步/异步 | 用途 |
|---|---|---|
fs.promises.access() |
异步 | mkdir 前检查路径是否存在 |
fs.promises.mkdir() |
异步 | 递归创建自启动目录 |
fs.promises.writeFile() |
异步 | 写入 Linux 自启动 desktop 文件 |
fs.promises.unlink() |
异步 | 登出时删除 desktop 文件 |
services/FileSystemService.ts
- Import:
import fs from 'fs/promises' - Tags:
asyncrw
| API | 同步/异步 | 用途 |
|---|---|---|
fs.readFile() |
异步 | 按可选编码读取文件 |
services/FileStorage.ts
- Import:
import * as fs from 'fs',import { writeFileSync } from 'fs',import { readFile } from 'fs/promises' - Tags:
syncasyncstreamstatrwdelcopydir
| API | 同步/异步 | 用途 |
|---|---|---|
fs.existsSync() |
同步 | 检查临时/存储目录路径是否存在 |
fs.mkdirSync() |
同步 | 创建临时/存储/笔记目录 |
fs.createReadStream() |
流 | 创建可读流用于文件哈希 |
fs.statSync() |
同步 | 获取文件信息(大小、创建时间)用于去重 |
fs.promises.readdir() |
异步 | 列出存储目录中的文件 |
fs.promises.copyFile() |
异步 | 复制文件(压缩图片或直接复制) |
fs.promises.stat() |
异步 | 获取文件元数据 |
fs.promises.unlink() |
异步 | 删除单个文件 |
fs.promises.rm() |
异步 | 递归删除目录 |
fs.promises.rename() |
异步 | 重命名/移动文件和目录 |
fs.promises.mkdir() |
异步 | 递归创建目录 |
fs.readFileSync() |
同步 | 同步读取文本文件内容 |
fs.promises.readFile() |
异步 | 以 buffer 或指定编码读取文件 |
fs.promises.writeFile() |
异步 | 写入文件内容(base64 图片、buffer) |
fs.accessSync() |
同步 | 检查文件访问权限 |
services/CopilotService.ts
- Import:
import fs from 'fs' - Tags:
syncasyncrwdirdelstat
| API | 同步/异步 | 用途 |
|---|---|---|
fs.existsSync() |
同步 | 检查 token 文件或目录是否存在 |
fs.promises.mkdir() |
异步 | 创建 token 存储目录 |
fs.promises.writeFile() |
异步 | 保存加密的 Copilot token |
fs.promises.readFile() |
异步 | 读取加密的 token 文件 |
fs.promises.access() |
异步 | 删除前检查 token 文件是否存在 |
fs.promises.unlink() |
异步 | 登出时删除 token 文件 |
services/ObsidianVaultService.ts
- Import:
import fs from 'fs' - Tags:
syncstatdirrw
| API | 同步/异步 | 用途 |
|---|---|---|
fs.existsSync() |
同步 | 检查配置/仓库路径是否存在 |
fs.readFileSync() |
同步 | 读取 Obsidian 配置 JSON 文件 |
fs.statSync() |
同步 | 检查仓库路径是否为目录 |
fs.readdirSync() |
同步 | 列出目录内容(含文件类型信息) |
services/OpenClawService.ts
- Import:
import fs from 'node:fs' - Tags:
syncdelrwdircopy
| API | 同步/异步 | 用途 |
|---|---|---|
fs.existsSync() |
同步 | 检查二进制/配置/符号链接路径是否存在 |
fs.unlinkSync() |
同步 | 删除符号链接、二进制和配置文件 |
fs.rmSync() |
同步 | 递归删除目录 |
fs.readFileSync() |
同步 | 读取配置 JSON 文件 |
fs.writeFileSync() |
同步 | 保存配置 JSON 文件 |
fs.mkdirSync() |
同步 | 创建配置目录 |
fs.renameSync() |
同步 | 重命名配置文件(备份和恢复) |
services/KnowledgeService.ts
- Import:
import * as fs from 'node:fs' - Tags:
syncdeldirrw
| API | 同步/异步 | 用途 |
|---|---|---|
fs.existsSync() |
同步 | 检查存储目录和待删除文件是否存在 |
fs.mkdirSync() |
同步 | 创建知识库存储目录 |
fs.rmSync() |
同步 | 递归删除知识库目录 |
fs.readFileSync() |
同步 | 读取待删除列表文件 |
fs.writeFileSync() |
同步 | 写入待删除列表文件 |
fs.unlinkSync() |
同步 | 删除待删除记录文件 |
services/AnthropicService.ts
- Import:
import { promises } from 'fs' - Tags:
asyncrwdeldir
| API | 同步/异步 | 用途 |
|---|---|---|
promises.mkdir() |
异步 | 创建 OAuth 配置目录 |
promises.writeFile() |
异步 | 保存 OAuth 凭证到文件 |
promises.readFile() |
异步 | 读取 OAuth 凭证文件 |
promises.chmod() |
异步 | 设置文件权限(0o600)确保安全 |
promises.unlink() |
异步 | 删除凭证文件 |
services/CodeCliService.ts
- Import:
import fs from 'node:fs' - Tags:
syncrwdirdel
| API | 同步/异步 | 用途 |
|---|---|---|
fs.existsSync() |
同步 | 检查配置/二进制/目录是否存在 |
fs.readFileSync() |
同步 | 读取配置文件和 manifest JSON |
fs.writeFileSync() |
同步 | 写入配置文件和 manifest |
fs.mkdirSync() |
同步 | 创建日志和临时目录 |
fs.chmodSync() |
同步 | 设置批处理文件的可执行权限 |
fs.unlinkSync() |
同步 | 删除临时文件 |
services/ProtocolClient.ts
- Import:
import fs from 'node:fs/promises' - Tags:
asyncrwdir
| API | 同步/异步 | 用途 |
|---|---|---|
fs.mkdir() |
异步 | 创建 .local/share/applications 目录 |
fs.writeFile() |
异步 | 写入 desktop 文件用于深度链接 |
services/SpanCacheService.ts
- Import:
import fs from 'fs/promises' - Tags:
asyncdeldirrwstatstream
| API | 同步/异步 | 用途 |
|---|---|---|
fs.rm() |
异步 | 递归删除 trace 文件和目录 |
fs.readdir() |
异步 | 列出 topic 目录中的 trace 文件 |
fs.mkdir() |
异步 | 创建 trace 目录 |
fs.access() |
异步 | 检查 trace 文件是否存在 |
fs.appendFile() |
异步 | 追加 span 数据到 trace 文件 |
fs.open() |
异步 | 打开 trace 文件用于流式读取 |
services/VersionService.ts
- Import:
import fs from 'fs' - Tags:
syncstatrw
| API | 同步/异步 | 用途 |
|---|---|---|
fs.existsSync() |
同步 | 检查版本日志文件是否存在 |
fs.statSync() |
同步 | 获取文件大小用于部分读取 |
fs.openSync() |
同步 | 打开文件进行底层读取 |
fs.readSync() |
同步 | 从文件末尾读取最后 1KB |
fs.closeSync() |
同步 | 关闭文件描述符 |
fs.appendFileSync() |
同步 | 追加版本记录到日志 |
services/WebviewService.ts
- Import:
import { promises as fs } from 'fs' - Tags:
asyncrw
| API | 同步/异步 | 用途 |
|---|---|---|
fs.writeFile() |
异步 | 将 PDF 和 HTML 文件写入磁盘 |
services/DxtService.ts
- Import:
import * as fs from 'fs' - Tags:
syncdelcopydirrw
| API | 同步/异步 | 用途 |
|---|---|---|
fs.existsSync() |
同步 | 检查目录和文件是否存在 |
fs.mkdirSync() |
同步 | 创建临时和 MCP 目录 |
fs.renameSync() |
同步 | 移动目录(重命名操作) |
fs.rmSync() |
同步 | 强制递归删除目录 |
fs.readdirSync() |
同步 | 列出目录条目(含类型信息) |
fs.copyFileSync() |
同步 | 目录复制操作中复制文件 |
fs.readFileSync() |
同步 | 读取 manifest.json 文件 |
fs.unlinkSync() |
同步 | 删除临时 DXT 文件 |
services/mcp/oauth/storage.ts
- Import:
import fs from 'fs/promises' - Tags:
asyncrwdeldircopy
| API | 同步/异步 | 用途 |
|---|---|---|
fs.readFile() |
异步 | 读取 OAuth 存储 JSON 文件 |
fs.mkdir() |
异步 | 创建 oauth 目录 |
fs.writeFile() |
异步 | 将 OAuth 数据写入临时文件 |
fs.rename() |
异步 | 原子性重命名临时文件为正式文件 |
fs.unlink() |
异步 | 删除 OAuth 存储文件 |
services/ocr/builtin/TesseractService.ts
- Import:
import fs from 'fs' - Tags:
asyncstatdir
| API | 同步/异步 | 用途 |
|---|---|---|
fs.promises.stat() |
异步 | OCR 前检查图片文件大小 |
fs.promises.access() |
异步 | 检查缓存目录是否存在 |
fs.promises.mkdir() |
异步 | 创建 Tesseract 缓存目录 |
services/ocr/builtin/OvOcrService.ts
- Import:
import * as fs from 'fs' - Tags:
syncasyncstatdirdelrwcopy
| API | 同步/异步 | 用途 |
|---|---|---|
fs.existsSync() |
同步 | 检查 OCR 可执行文件和目录是否存在 |
fs.promises.readdir() |
异步 | 列出 img/output 目录中的文件 |
fs.promises.stat() |
异步 | 检查条目是否为目录 |
fs.promises.rmdir() |
异步 | 删除空目录 |
fs.promises.unlink() |
异步 | 删除目录中的文件 |
fs.promises.mkdir() |
异步 | 创建 img/output 目录 |
fs.promises.copyFile() |
异步 | 复制图片文件到 img 目录 |
fs.promises.readFile() |
异步 | 读取 OCR 输出文本文件 |
services/remotefile/MistralService.ts
- Import:
import fs from 'node:fs/promises' - Tags:
asyncrw
| API | 同步/异步 | 用途 |
|---|---|---|
fs.readFile() |
异步 | 读取文件 buffer 用于上传到 Mistral |
services/remotefile/OpenAIService.ts
- Import:
import * as fs from 'fs' - Tags:
streamrw
| API | 同步/异步 | 用途 |
|---|---|---|
fs.createReadStream() |
流 | 创建可读流用于上传文件到 OpenAI |
services/memory/MemoryService.ts
- Import:
import fs from 'fs' - Tags:
syncstatcopy
| API | 同步/异步 | 用途 |
|---|---|---|
fs.existsSync() |
同步 | 检查旧内存数据库路径是否存在 |
fs.renameSync() |
同步 | 将内存数据库从旧位置迁移到新位置 |
services/lanTransfer/handlers/fileTransfer.ts
- Import:
import * as fs from 'node:fs' - Tags:
asyncstreamstat
| API | 同步/异步 | 用途 |
|---|---|---|
fs.promises.stat() |
异步 | 获取文件信息(存在性、大小检查) |
fs.createReadStream() |
流 | 流式传输文件块用于局域网传输 |
3. Agent Services(src/main/services/agents/)
services/agents/BaseService.ts
- Import:
import fs from 'fs' - Tags:
syncstatdir
| API | 同步/异步 | 用途 |
|---|---|---|
fs.existsSync() |
同步 | 检查可访问路径是否已存在 |
fs.statSync() |
同步 | 获取文件/目录信息以判断是文件还是目录 |
fs.mkdirSync() |
同步 | 递归创建 agent 工作区目录 |
services/agents/database/DatabaseManager.ts
- Import:
import fs from 'fs' - Tags:
syncdirstatdel
| API | 同步/异步 | 用途 |
|---|---|---|
fs.existsSync() |
同步 | 检查新旧数据库路径是否存在(用于迁移) |
fs.mkdirSync() |
同步 | 初始化前创建数据库目录 |
fs.renameSync() |
同步 | 将数据库文件从旧位置迁移到新位置 |
fs.statSync() |
同步 | 检查数据库文件信息以检测损坏(空文件) |
fs.unlinkSync() |
同步 | 删除损坏的空数据库文件 |
services/agents/database/MigrationService.ts
- Import:
import fs from 'fs' - Tags:
syncrw
| API | 同步/异步 | 用途 |
|---|---|---|
fs.existsSync() |
同步 | 检查迁移日志文件是否存在 |
fs.readFileSync() |
同步 | 读取迁移日志 JSON 文件以解析待执行迁移 |
services/agents/services/claudecode/index.ts
- Import:
import * as fs from 'node:fs' - Tags:
syncstat
| API | 同步/异步 | 用途 |
|---|---|---|
fs.existsSync() |
同步 | 在多种场景下检查文件是否存在 |
services/agents/services/SessionService.ts
- Import:
import * as fs from 'node:fs' - Tags:
asyncdir
| API | 同步/异步 | 用途 |
|---|---|---|
fs.promises.readdir() |
异步 | 列出 .claude/commands/ 目录中的命令文件 |
services/agents/services/channels/ChannelMessageHandler.ts
- Import:
import fs from 'node:fs/promises' - Tags:
asyncdirrw
| API | 同步/异步 | 用途 |
|---|---|---|
fs.mkdir() |
异步 | 创建频道图片/文件存储目录 |
fs.writeFile() |
异步 | 将频道图片持久化到工作区(base64 转文件) |
fs.writeFile() |
异步 | 将频道文件持久化到工作区(base64 转文件) |
services/agents/services/channels/adapters/wechat/WeChatProtocol.ts
- Import:
import fs from 'node:fs',import { chmod, mkdir, readFile, rm, writeFile } from 'node:fs/promises' - Tags:
syncasyncrwdeldir
| API | 同步/异步 | 用途 |
|---|---|---|
fs.existsSync() |
同步 | 读取前检查上下文 token 文件是否存在 |
fs.readFileSync() |
同步 | 从磁盘恢复微信上下文 token |
readFile() |
异步 | 从 token 文件加载 bot 凭证 |
mkdir() |
异步 | 创建 token 目录(限制权限 0o700) |
writeFile() |
异步 | 将 bot 凭证保存到 token 文件(0o600) |
chmod() |
异步 | 设置 token 文件权限为仅所有者可访问 |
rm() |
异步 | 通过删除 token 文件清除凭证 |
services/agents/services/builtin/BuiltinAgentProvisioner.ts
- Import:
import fs from 'fs' - Tags:
syncdirrwcopy
| API | 同步/异步 | 用途 |
|---|---|---|
fs.mkdirSync() |
同步 | 复制内置 agent 模板时创建目标目录 |
fs.readdirSync() |
同步 | 列出模板目录中的文件用于递归复制 |
fs.copyFileSync() |
同步 | 复制单个模板文件到 agent 工作区 |
fs.existsSync() |
同步 | 检查模板目录或 agent.json 是否存在 |
fs.readFileSync() |
同步 | 读取 agent.json 配置文件以提取元数据 |
services/agents/skills/SkillInstaller.ts
- Import:
import * as fs from 'fs' - Tags:
asyncrwcopy
| API | 同步/异步 | 用途 |
|---|---|---|
fs.promises.rename() |
异步 | 替换前备份已有技能目录 |
fs.promises.rename() |
异步 | 安装失败时从备份恢复技能文件夹 |
fs.promises.readFile() |
异步 | 读取 SKILL.md 内容以计算 SHA-256 哈希 |
services/agents/skills/SkillService.ts
- Import:
import * as fs from 'node:fs' - Tags:
asyncdirrwstatdel
| API | 同步/异步 | 用途 |
|---|---|---|
fs.promises.readdir() |
异步 | 列出技能目录以构建文件树 |
fs.promises.readFile() |
异步 | 读取技能文件用于内容访问 API |
fs.promises.mkdir() |
异步 | 创建 .claude/skills 目录用于符号链接 |
fs.promises.lstat() |
异步 | 删除前检查符号链接/目录状态 |
fs.promises.rm() |
异步 | 重新链接前移除已有符号链接或目录 |
fs.promises.symlink() |
异步 | 从 global-skills 创建 junction 符号链接到 .claude/skills |
fs.promises.unlink() |
异步 | 卸载时移除技能符号链接 |
fs.promises.writeFile() |
异步 | 将下载的 ZIP 文件写入临时目录 |
fs.promises.stat() |
异步 | 处理前验证 ZIP 文件 |
services/agents/services/cherryclaw/seedWorkspace.ts
- Import:
import { mkdir, stat, writeFile } from 'node:fs/promises' - Tags:
asyncdirrwstat
| API | 同步/异步 | 用途 |
|---|---|---|
mkdir() |
异步 | 创建工作区和 memory 子目录 |
writeFile() |
异步 | 写入 SOUL.md 和 USER.md 模板文件 |
stat() |
异步 | 检查模板文件是否已存在(幂等播种) |
services/agents/services/cherryclaw/prompt.ts
- Import:
import { readdir, readFile, stat } from 'node:fs/promises' - Tags:
asyncrwstatdir
| API | 同步/异步 | 用途 |
|---|---|---|
readdir() |
异步 | 列出工作区文件用于大小写不敏感匹配 |
stat() |
异步 | 检查文件存在性并获取 mtime 用于缓存 |
readFile() |
异步 | 读取 SOUL.md、USER.md、FACT.md 记忆文件用于构建提示词 |
services/agents/services/cherryclaw/heartbeat.ts
- Import:
import { readFile } from 'node:fs/promises' - Tags:
asyncrw
| API | 同步/异步 | 用途 |
|---|---|---|
readFile() |
异步 | 读取 heartbeat.md 文件(含路径遍历保护) |
4. Data Layer(src/main/data/)
data/db/DbService.ts
- Import:
import fs from 'fs' - Tags:
syncstatdel
| API | 同步/异步 | 用途 |
|---|---|---|
fs.existsSync() |
同步 | 检查数据库文件是否存在 |
fs.statSync() |
同步 | 获取数据库文件信息(大小) |
fs.unlinkSync() |
同步 | 删除损坏的/空的数据库文件 |
fs.unlinkSync() |
同步 | 删除孤立的 WAL/SHM 辅助文件 |
services/file/utils/pathResolver.ts
- Import:
import fs from 'node:fs' - Tags:
syncstat
| API | 同步/异步 | 用途 |
|---|---|---|
fs.realpathSync() |
同步 | 解析 local_external 挂载路径的符号链接 |
fs.realpathSync() |
同步 | 解析基础路径符号链接用于安全检查 |
data/bootConfig/BootConfigService.ts
- Import:
import fs from 'node:fs' - Tags:
syncrwstatdeldir
| API | 同步/异步 | 用途 |
|---|---|---|
fs.existsSync() |
同步 | 检查启动配置文件是否存在 |
fs.readFileSync() |
同步 | 从文件加载启动配置 |
fs.unlinkSync() |
同步 | 全部为默认值时删除配置文件 |
fs.mkdirSync() |
同步 | 配置目录不存在时创建 |
fs.writeFileSync() |
同步 | 将配置写入临时文件 |
fs.renameSync() |
同步 | 原子性重命名临时文件为配置文件 |
data/migration/v2/window/MigrationIpcHandler.ts
- Import:
import fs from 'fs/promises' - Tags:
asyncdirrw
| API | 同步/异步 | 用途 |
|---|---|---|
fs.mkdir() |
异步 | 确保导出目录存在 |
fs.writeFile() |
异步 | 将表数据 JSON 写入文件 |
data/migration/v2/utils/DexieFileReader.ts
- Import:
import fs from 'fs/promises' - Tags:
asyncrwstat
| API | 同步/异步 | 用途 |
|---|---|---|
fs.readFile() |
异步 | 将导出的表 JSON 文件读入内存 |
fs.access() |
异步 | 检查表导出文件是否存在 |
fs.stat() |
异步 | 获取表导出文件的大小 |
data/migration/v2/utils/JSONStreamReader.ts
- Import:
import { createReadStream } from 'fs' - Tags:
stream
| API | 同步/异步 | 用途 |
|---|---|---|
createReadStream() |
流 | 创建可读流用于 JSON 文件批量读取 |
createReadStream() |
流 | 创建可读流用于 JSON 文件计数 |
createReadStream() |
流 | 创建可读流用于 JSON 文件采样 |
data/migration/v2/core/MigrationDbService.ts
- Import:
import fs from 'fs' - Tags:
syncstatdel
| API | 同步/异步 | 用途 |
|---|---|---|
fs.existsSync() |
同步 | 检查数据库文件是否存在 |
fs.statSync() |
同步 | 获取数据库文件大小 |
fs.unlinkSync() |
同步 | 删除损坏的/空的数据库文件 |
fs.unlinkSync() |
同步 | 删除孤立的 WAL/SHM 文件 |
data/migration/v2/core/MigrationContext.ts
- Import:
import fs from 'fs/promises' - Tags:
asyncrw
| API | 同步/异步 | 用途 |
|---|---|---|
fs.readFile() |
异步 | 读取 localStorage 导出 JSON 文件 |
data/migration/v2/core/MigrationEngine.ts
- Import:
import fs from 'fs/promises' - Tags:
asyncdel
| API | 同步/异步 | 用途 |
|---|---|---|
fs.rm() |
异步 | 递归删除临时导出目录 |
data/migration/v2/migrators/KnowledgeMigrator.ts
- Import:
import fs from 'node:fs' - Tags:
syncstat
| API | 同步/异步 | 用途 |
|---|---|---|
fs.existsSync() |
同步 | 检查旧版向量数据库文件是否存在 |
fs.statSync() |
同步 | 检查路径是目录还是文件 |
5. Knowledge(src/main/knowledge/)
knowledge/embedjs/loader/draftsExportLoader.ts
- Import:
import * as fs from 'node:fs' - Tags:
syncrw
| API | 同步/异步 | 用途 |
|---|---|---|
fs.readFileSync() |
同步 | 读取 Drafts 导出 JSON 文件 |
knowledge/embedjs/loader/epubLoader.ts
- Import:
import * as fs from 'fs' - Tags:
syncstreamrwdel
| API | 同步/异步 | 用途 |
|---|---|---|
fs.existsSync() |
同步 | 检查 EPUB 文件是否存在 |
fs.createWriteStream() |
流 | 创建写入流用于临时文本文件 |
fs.readFileSync() |
同步 | 读取提取的临时文本内容 |
fs.unlinkSync() |
同步 | 删除临时文本文件 |
knowledge/preprocess/BasePreprocessProvider.ts
- Import:
import fs from 'node:fs' - Tags:
syncasyncstatdirrwcopydel
| API | 同步/异步 | 用途 |
|---|---|---|
fs.existsSync() |
同步 | 检查预处理存储目录是否存在 |
fs.mkdirSync() |
同步 | 创建预处理存储目录 |
fs.existsSync() |
同步 | 检查文件是否已预处理(目录检查) |
fs.promises.stat() |
异步 | 检查路径是否为目录 |
fs.promises.readdir() |
异步 | 列出已预处理目录中的文件 |
fs.promises.stat() |
异步 | 获取文件信息(大小、创建时间) |
fs.existsSync() |
同步 | 检查附件目录是否存在 |
fs.mkdirSync() |
同步 | 创建附件目录 |
fs.copyFileSync() |
同步 | 复制文件到附件目录 |
fs.unlinkSync() |
同步 | 复制后删除原始文件 |
knowledge/preprocess/MistralPreprocessProvider.ts
- Import:
import fs from 'node:fs' - Tags:
syncrwdirstat
| API | 同步/异步 | 用途 |
|---|---|---|
fs.readFileSync() |
同步 | 读取图片文件并转换为 base64 |
fs.mkdirSync() |
同步 | 创建 OCR 结果输出目录 |
fs.writeFileSync() |
同步 | 保存 base64 编码的图片文件 |
fs.writeFileSync() |
同步 | 保存合并后的 markdown 文件 |
fs.statSync() |
同步 | 获取 markdown 文件大小 |
knowledge/preprocess/OpenMineruPreprocessProvider.ts
- Import:
import fs from 'node:fs' - Tags:
syncasyncrwdirstatdelcopy
| API | 同步/异步 | 用途 |
|---|---|---|
fs.promises.stat() |
异步 | 获取文件大小以验证 PDF |
fs.promises.readFile() |
异步 | 读取 PDF 文件用于验证 |
fs.readdirSync() |
同步 | 列出提取输出目录中的文件 |
fs.renameSync() |
同步 | 重命名提取的 markdown 文件 |
fs.existsSync() |
同步 | 检查提取目录是否存在 |
fs.mkdirSync() |
同步 | 创建提取目录 |
fs.writeFileSync() |
同步 | 写入下载的 ZIP 文件 |
fs.unlinkSync() |
同步 | 删除临时 ZIP 文件 |
knowledge/preprocess/Doc2xPreprocessProvider.ts
- Import:
import fs from 'node:fs' - Tags:
syncasyncstreamrwdirstatdel
| API | 同步/异步 | 用途 |
|---|---|---|
fs.promises.stat() |
异步 | 验证文件大小 |
fs.promises.readFile() |
异步 | 读取文件用于上传 |
fs.createReadStream() |
流 | 创建可读流用于文件上传 |
fs.mkdirSync() |
同步 | 创建 ZIP/提取目录 |
fs.existsSync() |
同步 | 检查提取目录是否存在 |
fs.writeFileSync() |
同步 | 写入下载的 ZIP 文件 |
fs.unlinkSync() |
同步 | 删除临时 ZIP 文件 |
fs.statSync() |
同步 | 获取 markdown 文件大小 |
knowledge/preprocess/MineruPreprocessProvider.ts
- Import:
import fs from 'node:fs' - Tags:
syncasyncrwdirstatdel
| API | 同步/异步 | 用途 |
|---|---|---|
fs.promises.stat() |
异步 | 验证文件大小 |
fs.promises.readFile() |
异步 | 读取文件用于上传 |
fs.readdirSync() |
同步 | 列出提取输出中的文件 |
fs.renameSync() |
同步 | 重命名提取的 markdown 文件 |
fs.existsSync() |
同步 | 检查提取的文件是否存在 |
fs.statSync() |
同步 | 获取已处理文件的大小 |
fs.writeFileSync() |
同步 | 写入下载的 ZIP 文件 |
fs.mkdirSync() |
同步 | 创建提取目录 |
fs.unlinkSync() |
同步 | 删除临时 ZIP 文件 |
knowledge/preprocess/PaddleocrPreprocessProvider.ts
- Import:
import fs from 'node:fs' - Tags:
syncasyncrwdirstatdel
| API | 同步/异步 | 用途 |
|---|---|---|
fs.promises.stat() |
异步 | 验证文件大小 |
fs.promises.readFile() |
异步 | 读取文件以验证 PDF |
fs.existsSync() |
同步 | 检查输出目录是否存在 |
fs.rmSync() |
同步 | 删除已有的输出目录 |
fs.mkdirSync() |
同步 | 创建输出目录 |
fs.writeFileSync() |
同步 | 写入 markdown 结果到文件 |
6. MCP Servers(src/main/mcpServers/)
mcpServers/memory.ts
- Import:
import { promises as fs } from 'fs' - Tags:
asyncrwdirstat
| API | 同步/异步 | 用途 |
|---|---|---|
fs.mkdir() |
异步 | 递归创建 memory 文件路径目录 |
fs.access() |
异步 | 检查 memory 文件是否存在 |
fs.writeFile() |
异步 | 写入/创建 memory 文件(初始空结构) |
fs.readFile() |
异步 | 从磁盘加载知识图谱数据 |
fs.writeFile() |
异步 | 将内存中的图谱持久化到磁盘(带互斥锁) |
mcpServers/assistant.ts
- Import:
import fs from 'node:fs' - Tags:
syncrwstatdir
| API | 同步/异步 | 用途 |
|---|---|---|
fs.existsSync() |
同步 | 检查日志目录是否存在 |
fs.readdirSync() |
同步 | 列出日志目录中的 .log 文件 |
fs.statSync() |
同步 | 获取日志文件修改时间用于排序 |
fs.readFileSync() |
同步 | 读取最新日志文件内容 |
fs.existsSync() |
同步 | 检查源文件路径是否存在 |
fs.statSync() |
同步 | 检查路径是文件还是目录 |
fs.readdirSync() |
同步 | 列出目录内容(含文件类型) |
fs.readFileSync() |
同步 | 读取源文件内容用于诊断 |
mcpServers/claw.ts
- Import:
import { appendFile, mkdir, readdir, readFile, rename, stat, writeFile } from 'node:fs/promises' - Tags:
asyncrwdirstatcopy
| API | 同步/异步 | 用途 |
|---|---|---|
mkdir() |
异步 | 创建工作区目录 |
readdir() |
异步 | 列出目录内容 |
readFile() |
异步 | 读取文件内容 |
writeFile() |
异步 | 写入文件内容 |
appendFile() |
异步 | 追加内容到文件 |
rename() |
异步 | 重命名/移动文件 |
stat() |
异步 | 获取文件元数据 |
mcpServers/filesystem/server.ts
- Import:
import fs from 'fs/promises' - Tags:
asyncdir
| API | 同步/异步 | 用途 |
|---|---|---|
fs.mkdir() |
异步 | 确保 filesystem MCP 的 baseDir 存在 |
mcpServers/filesystem/tools/delete.ts
- Import:
import fs from 'fs/promises' - Tags:
asyncstatdel
| API | 同步/异步 | 用途 |
|---|---|---|
fs.stat() |
异步 | 获取文件/目录信息以判断类型 |
fs.rm() |
异步 | 强制递归删除目录 |
fs.rmdir() |
异步 | 删除空目录 |
fs.unlink() |
异步 | 删除文件 |
mcpServers/filesystem/tools/ls.ts
- Import:
import fs from 'fs/promises' - Tags:
asyncdir
| API | 同步/异步 | 用途 |
|---|---|---|
fs.readdir() |
异步 | 列出目录内容(含文件类型信息) |
mcpServers/filesystem/tools/edit.ts
- Import:
import fs from 'fs/promises' - Tags:
asyncrwstatdir
| API | 同步/异步 | 用途 |
|---|---|---|
fs.stat() |
异步 | 检查文件是否存在且为文件 |
fs.mkdir() |
异步 | 父目录不存在时创建 |
fs.readFile() |
异步 | 编辑前读取当前文件内容 |
fs.writeFile() |
异步 | 替换后写入修改的文件内容 |
mcpServers/filesystem/tools/write.ts
- Import:
import fs from 'fs/promises' - Tags:
asyncrwstatdir
| API | 同步/异步 | 用途 |
|---|---|---|
fs.mkdir() |
异步 | 需要时创建父目录 |
fs.stat() |
异步 | 检查文件是否已存在(用于日志) |
fs.writeFile() |
异步 | 写入文件内容(覆盖或创建) |
mcpServers/filesystem/tools/read.ts
- Import:
import fs from 'fs/promises' - Tags:
asyncrwstat
| API | 同步/异步 | 用途 |
|---|---|---|
fs.stat() |
异步 | 检查文件是否存在且为文件 |
fs.readFile() |
异步 | 以 UTF-8 读取文件内容 |
mcpServers/filesystem/tools/glob.ts
- Import:
import fs from 'fs/promises' - Tags:
asyncstat
| API | 同步/异步 | 用途 |
|---|---|---|
fs.stat() |
异步 | 验证搜索目录是否存在且为目录 |
fs.stat() |
异步 | 获取文件大小和修改时间用于结果 |
mcpServers/filesystem/tools/grep.ts
- Import:
import fs from 'fs/promises' - Tags:
asyncrwstatdir
| API | 同步/异步 | 用途 |
|---|---|---|
fs.readFile() |
异步 | 读取文件内容用于模式匹配 |
fs.readdir() |
异步 | 列出目录条目用于递归搜索 |
fs.stat() |
异步 | 检查搜索路径是文件还是目录 |
mcpServers/filesystem/types.ts
- Import:
import fs from 'fs/promises' - Tags:
asyncstatrw
| API | 同步/异步 | 用途 |
|---|---|---|
fs.realpath() |
异步 | 解析真实路径处理符号链接(安全) |
fs.open() |
异步 | 打开文件用于二进制检测 |
7. 顶层文件(src/main/)
bootstrap.ts
- Import:
import fs from 'fs' - Tags:
syncstatcopy
| API | 同步/异步 | 用途 |
|---|---|---|
fs.existsSync() |
同步 | 检查被占用目录是否存在 |
fs.cpSync() |
同步 | 递归复制被占用目录 |
ipc.ts
- Import:
import fs from 'node:fs' - Tags: (存在 import,具体用法待验证)
8. 总结统计
文件数量(非测试)
| 分组 | 文件数 |
|---|---|
| Utils | 9 |
| Services(非 agent) | 21 |
| Agent Services | 13 |
| Data Layer | 10 |
| Knowledge | 8 |
| MCP Servers | 12 |
| Top-level | 2 |
| 合计 | 75 |
Import 风格分布
| 风格 | 使用次数 | 示例 |
|---|---|---|
import fs from 'fs' |
~12 | AppService, DbService |
import fs from 'node:fs' |
~15 | CodeCliService, BootConfigService |
import * as fs from 'fs' |
~5 | FileStorage, DxtService |
import * as fs from 'node:fs' |
~5 | KnowledgeService, SkillService |
import fs from 'fs/promises' |
~10 | SpanCacheService, MigrationEngine |
import fs from 'node:fs/promises' |
~4 | ProtocolClient, MistralService |
import { promises } from 'fs' |
~2 | AnthropicService |
import { promises as fs } from 'fs' |
~2 | WebviewService, memory.ts |
Named imports ({ readFile, ... }) |
~5 | claw.ts, cherryclaw/* |
问题:8+ 种 import 风格混用,无统一规范。
Sync vs Async 分布
| 类型 | 文件数 | 占比 |
|---|---|---|
| 仅 sync | ~18 | 24% |
| 仅 async | ~30 | 40% |
| sync + async 混用 | ~27 | 36% |
最常用 API Top 10
| API | 出现文件数 | 用途类别 |
|---|---|---|
existsSync() |
30+ | 存在性检查 |
mkdirSync() / mkdir() |
25+ | 目录创建 |
readFileSync() / readFile() |
25+ | 文件读取 |
writeFileSync() / writeFile() |
20+ | 文件写入 |
statSync() / stat() |
20+ | 元数据获取 |
unlinkSync() / unlink() |
15+ | 文件删除 |
readdirSync() / readdir() |
15+ | 目录列表 |
rmSync() / rm() |
8+ | 递归删除 |
renameSync() / rename() |
8+ | 重命名/移动 |
copyFileSync() / copyFile() |
6+ | 文件复制 |
关键发现
- 无统一文件操作抽象:每个文件直接
import fs,无中间层 - Import 风格碎片化:
'fs'vs'node:fs'vs'fs/promises'混用 - Sync/Async 不一致:同类操作有的用 sync 有的用 async,部分文件混用两种风格
- 重复模式多:
existsSync() + mkdirSync({ recursive: true })在 20+ 个文件中重复出现 - 原子写入不统一:
mcp/oauth/storage.ts和BootConfigService.ts各自实现了临时文件+rename 的原子写入,utils/file.ts有锁文件机制 - 权限设置分散:
chmod在AnthropicService、WeChatProtocol、rtk.ts等多处独立使用 - FileStorage.ts 是最大用户:覆盖了几乎所有 fs 操作类别(sync/async/stream/stat/rw/del/copy/dir)
- MCP filesystem tools 最规范:统一使用
fs/promises,纯 async
迁移优先级建议
| 优先级 | 目标 | 理由 |
|---|---|---|
| P0 | FileStorage.ts |
最大 fs 用户,核心文件操作服务 |
| P0 | utils/file.ts + utils/fileOperations.ts |
底层工具函数,被广泛调用 |
| P1 | knowledge/preprocess/* |
8 个文件大量重复模式 |
| P1 | data/ 层 |
数据库和迁移相关文件操作 |
| P2 | services/agents/* |
Agent 子系统,相对独立 |
| P2 | mcpServers/* |
已经比较规范,迁移成本低 |
| P3 | 其余 services | 零散使用,逐步迁移 |