Metadata-Version: 2.5
Name: file-search-system
Version: 1.0.0
Summary: 基于 Everything 的本地毫秒级文件搜索系统：Python 库 + 全功能 CLI + HTTP JSON API（非管理员文件夹索引、junction 死循环免疫、非阻塞搜索）
Project-URL: Documentation, https://pypi.org/project/file-search-system/
Project-URL: Source, https://pypi.org/project/file-search-system/
License-Expression: MIT
License-File: LICENSE
Keywords: cli,desktop-search,everything,file-index,file-search,http-api,indexer,search,windows
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: Microsoft :: Windows
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Desktop Environment
Classifier: Topic :: System :: Filesystems
Classifier: Topic :: Utilities
Requires-Python: >=3.12
Description-Content-Type: text/markdown

# file-search-system v1.0.0

基于 [Everything](https://www.voidtools.com/) 的本地毫秒级文件搜索系统。
纯 Python 标准库（`ctypes`）零依赖；非管理员文件夹索引（NTFS / ReFS /
FAT32 / exFAT / 可移动盘 / 网络共享）；Everything 后台静默运行（无窗口、
无托盘、便携模式）；全部能力经 CLI / Python / HTTP 三种接口提供。

- 环境要求：Windows 8.1+（推荐 Win10/11）· Python 3.12+
- 自带引擎：`pip install` / `uv add` 装完即用，无需单独安装 Everything
- 搜索语法即 Everything 官方语法：`ext:` `size:` `dm:` `path:` …
  <https://www.voidtools.com/support/everything/searching/>

---

## 安装

```bash
pip install file-search-system
```

```bash
uv add file-search-system
```

装好即带 Windows 版 Everything 引擎（`Everything.exe` + 四架构 SDK DLL），
首次运行自动就绪，无需另行下载。

```bash
file-search init            # 一次性初始化：定位引擎 + 配置 + 静默拉起 + 就绪
file-search "README" -n 5   # 立刻可用
```

### 引擎与配置落在哪里

| 安装方式 | 引擎目录（`Everything.exe` + DLL） | 配置文件 |
|---|---|---|
| `pip` / `uv` 安装 | `%LOCALAPPDATA%\file-search-system\bin`（首次使用自动落地） | `%LOCALAPPDATA%\file-search-system\config.ini` |
| 源码运行 | `<项目根>\bin` | `<项目根>\config.ini` |

原因是 Everything 的便携模式必须在 `Everything.exe` 旁写
`Everything.ini` / `Everything.db`，而安装目录通常不可写，所以首次使用会把
引擎释放到用户可写目录，之后一直复用该副本。

自定义位置（任选其一）：

```bash
set FILE_SEARCH_BIN=D:\my-everything\bin   # 指定引擎目录（bin_dir= 参数同义）
set FILE_SEARCH_HOME=D:\my-data            # 指定引擎落地与配置目录
set FILE_SEARCH_DATA=D:\my-data            # 只指定 config.ini 所在目录
```

查看实际路径、或把自带引擎导出到任意目录：

```bash
file-search engine info                    # 显示引擎 / DLL / 配置文件的实际位置
file-search engine install D:\my-app\bin   # 复制一份自带引擎过去（独立分发用）
```

### 运行要求与安全说明

- 需要 Windows 与可运行的 Everything 引擎；非 Windows 平台会在导入时给出
  明确报错。
- 本系统内部所有"重启/关闭引擎"的路径（`shutdown` / `set_config` 重启 /
  看门狗恢复 / `kill_own_process`）都与查询串行，引擎死亡瞬间无在途查询。
  若**外部**（任务管理器、`taskkill /F`）恰在查询在途时强杀引擎，SDK DLL
  内部锁会被卡死线程持有，宿主的 Python 进程需要重启（`is_running` 会探测
  到并告警）。

---

## 一、CLI

### 搜索

```bash
file-search search "关键词" [选项]        # search 可省略
file-search "ext:py size:>10mb"           # 等价写法
file-search "*.pdf" --paths-only -n 20
```

| 选项 | 说明 |
|---|---|
| `-n, --max N` | 最多返回 N 条 |
| `--offset N` | 跳过前 N 条（分页；须 ≥0） |
| `--sort KEY` | 排序：`size-desc`、`date-modified-desc`、`name-asc` 等 |
| `--regex` | 按正则解析搜索词 |
| `--case` | 大小写敏感 |
| `--whole-word` | 全字匹配 |
| `--match-path` | 匹配整个路径（默认只匹配文件名） |
| `--highlight` | 输出高亮字段（匹配段以 `*` 包裹） |
| `--paths-only` | 每行只输出完整路径（便于管道） |
| `--json` | JSON 输出 |
| `--export FMT --out FILE` | 导出 `csv` / `json` / `txt` / `efu` |
| `--timeout 秒` | 查询保护超时（默认 15） |
| `--wait-index 秒` | 索引扫描中的等待秒数（0=立即报错并显示进度） |
| `--no-start` | Everything 未运行时不自动启动 |

### 索引管理

```bash
file-search index add "D:\docs" "E:\media"      # 添加目录（立即返回，后台扫描）
file-search index add --disks C D               # 整盘
file-search index add "D:\data" --exclude temp  # 自定义排除目录
file-search index add "D:\data" --prescan       # 预扫描 junction（最安全，较慢）
file-search index add "D:\data" --no-monitor    # 不实时监控变化（更省资源）
file-search index snapshot "E:\backup"          # EFU 快照模式（秒级加载）
file-search index status [--wait 300]           # 扫描进度（可等待完成）
file-search index list                          # 当前索引配置（写法 -> 本机展开）
file-search index defaults                      # 预览默认目录发现（不改动）
file-search index disks                         # 本机磁盘（盘符/类型/文件系统）
file-search index network [--add]               # 发现网络共享（--add 加入索引）
file-search index remove "E:\media"             # 移除目录
file-search index clear                         # 清空索引
```

`index add` 参数：`--disks`（整盘列表）、`--exclude`（排除目录名）、
`--exclude-files`（排除文件通配）、`--prescan` / `--no-prescan`、
`--monitor` / `--no-monitor`。默认排除 `node_modules / .pnpm / .git /
__pycache__`。

### 其他命令

```bash
file-search init                                # 初始化（幂等：配置+拉起+就绪）
file-search status                              # 运行/就绪/索引/OS 兼容等级
file-search compat                              # 本机兼容体检（10 项只读）
file-search config list [前缀]                  # 引擎键列表
file-search config get 键1 键2                  # 读引擎键
file-search config set 键=值 [键=值...]         # 写引擎键并重启生效
file-search runcount get "C:\a.exe"             # 运行次数 get / inc / set
file-search runcount inc "C:\a.exe"
file-search runcount set "C:\a.exe" 5
file-search db save                             # 数据库 save / rebuild /
file-search db rebuild                          #        update-folders /
file-search db run-history-delete               #        run-history-save / delete
file-search serve --host 0.0.0.0 --port 8465    # HTTP 服务（见第三节）
file-search engine info|install                 # 引擎目录查看 / 导出
file-search exit [--force]                      # 退出 Everything
```

`python -m file_search` 与 `file-search` 等价。

---

## 二、Python 库

### 导入与初始化

```python
from file_search import EverythingManager

mgr = EverythingManager()      # 构造零副作用；首条命令自动完成启动链
info = mgr.init()              # 或显式初始化（幂等），返回状态摘要
```

**构造参数：**

| 参数 | 默认 | 说明 |
|---|---|---|
| `bin_dir` | `None` | 直接指定 bin 目录 |
| `base_dir` | `None` | 项目根目录（其下应有 `bin/`） |
| `dll_path` | `None` | SDK DLL 路径（`None` = 自动按解释器架构选择 x64/x86/ARM64/ARM） |
| `exe_path` | `None` | Everything.exe 路径（默认 `<bin>/Everything.exe`） |
| `startup_timeout` | `60.0` | 等待启动（IPC 可达）的秒数 |

自动定位优先级：`bin_dir` 参数 → `FILE_SEARCH_BIN` 环境变量 →
`base_dir/bin` → 源码树 `<项目根>/bin` → 用户数据目录（首次自动释放引擎）。

### 搜索

```python
results = mgr.search("*.pdf size:>1mb", max_results=50, sort="date-modified")
for r in results:
    print(r["full_path"], r["size"], r["date_modified"])

total = mgr.last_total_for_thread()    # 本次查询的总命中数（并发安全）
```

**`search()` / `submit_search()` 参数：**

| 参数 | 默认 | 说明 |
|---|---|---|
| `search_string` | 必填 | Everything 搜索语法文本 |
| `max_results` | `None` | 最多返回条数（`None` = 不限；`<1` 抛 `ValueError`） |
| `offset` | `0` | 跳过前 N 条（分页；负值抛 `ValueError`） |
| `sort` | `None` | `SORT_NAMES` 键名（如 `"size-desc"`）或 `EVERYTHING_SORT_*` 常量 |
| `match_path` | `False` | 匹配整个路径 |
| `match_case` | `False` | 大小写敏感 |
| `match_whole_word` | `False` | 全字匹配 |
| `regex` | `False` | 搜索词按正则解析 |
| `request_flags` | `REQUEST_ALL` | 请求字段位掩码（请求了才出现在结果里） |
| `query_timeout` | `15.0` | 查询保护超时秒数 |
| `wait_for_index` | `0.0` | 索引扫描中的等待秒数；`0` = 抛 `IndexScanningError` |

**结果字段**（按 `request_flags`）：`full_path`、`file_name`、`path`、
`extension`、`size`、`date_created/modified/accessed/run/recently_changed`、
`attributes` + `attributes_str`、`cloud_only`（OneDrive 云占位）、
`run_count`、`is_file/folder/volume`；高亮三字段需显式请求。

### 非阻塞搜索

```python
h = mgr.submit_search("ext:log", max_results=100)   # 立即返回，绝不阻塞
if h.wait(3.0):                                     # 最多等 3 秒
    for r in h.results():
        print(r["full_path"])
else:
    print(h.status())      # {"state": "pending"|"scanning"|"complete"|...}

# 句柄方法：done() / wait(秒) / results() / status() / cancel()
```

### 索引与生命周期

```python
mgr.index_add([r"D:\docs"], excludes=["temp"], monitor=True)   # live 索引
mgr.index_snapshot([r"E:\backup"])                             # EFU 快照
mgr.index_remove([r"D:\docs"])
mgr.index_clear()
mgr.prescan_junctions([r"F:\Data"])     # 并行 junction 预扫描
mgr.wait_index_stable(timeout=600)      # 等扫描完成
mgr.export("*.md", "out.csv", fmt="csv")# 导出 csv/json/txt/efu

mgr.start()                             # 静默启动（未运行时）
mgr.status()                            # 完整状态 dict
mgr.shutdown(force=True)                # 用完即关
mgr.cleanup()                           # 关闭自启实例 + 释放 SDK
```

默认退出语义为**长驻**（Python 结束后 Everything 留守，下次毫秒级可用）；
`shutdown(force=True)` 为用完即关。

外部应用自带引擎时，可先用 CLI 复制一份再显式指定：

```bash
file-search engine install D:\my-app\bin
```

```python
mgr = EverythingManager(bin_dir=r"D:\my-app\bin")
```

---

## 三、HTTP API

```bash
file-search serve                                  # 默认 127.0.0.1:8465
file-search serve --host 0.0.0.0 --port 8465       # 对外暴露（建议加 token）
file-search serve --token my-secret                # 令牌鉴权
```

所有端点在启用令牌后须带 `X-Api-Token` 头（不符 → 401）。

**端点：**

| 方法 | 路径 | 说明 |
|---|---|---|
| GET | `/health` | 存活与就绪探针 |
| GET | `/status` | 完整状态（JSON） |
| GET | `/search` | 阻塞搜索 |
| GET | `/search/submit` | 非阻塞搜索（返回 id） |
| GET | `/search/result` | 轮询非阻塞结果 |
| GET | `/index` | 当前索引配置 |
| POST | `/init` | 触发初始化（幂等） |
| POST | `/index/add` | 添加索引目录（后台生效） |
| POST | `/index/clear` | 清空索引 |
| GET | `/config?key=...` | 读引擎键（无 key = 全部） |
| POST | `/config` | 写引擎键（`{"settings": {...}, "restart": true}`） |
| GET | `/runcount?path=...` | 读取运行次数 |
| POST | `/runcount` | 运行次数操作（get / inc / set） |
| POST | `/exit` | 退出 Everything |

**请求示例：**

```bash
curl "http://127.0.0.1:8465/search?q=ext:pdf&max=20&sort=size-desc"
curl -H "X-Api-Token: my-secret" "http://127.0.0.1:8465/search?q=readme"

# /search 参数：q, max(≤10000), offset(≥0), sort, regex, case,
#               whole-word, match-path, timeout, wait_index

curl "http://127.0.0.1:8465/search/submit?q=readme&max=10"
# -> {"id": "e3ecfb87..."}
curl "http://127.0.0.1:8465/search/result?id=e3ecfb87..."
# -> {"status": {"state": "complete"}, "results": [...]}

curl -X POST "http://127.0.0.1:8465/index/add" \
     -H "Content-Type: application/json" \
     -d '{"folders": ["D:\\docs"], "monitor": true}'
```

内置限制：请求体 ≤ 1 MiB（413）、搜索串 ≤ 8192 字符与 `max` ≤ 10000（400）、
非阻塞句柄注册表 ≤ 4096（429）。

---

## 四、config.ini（唯一配置文件）

三节结构，每键带注释，可手编（支持记事本 UTF-8 BOM）；程序改写时注释
自动再生、原子写不损坏。默认位置见上文"引擎与配置落在哪里"，
也可用 `FILE_SEARCH_HOME` 指定目录。

```ini
[index]
# live = 实时文件夹索引（默认）；snapshot = EFU 快照（秒级加载，静态）
mode = live
# 索引目录列表（逗号分隔；支持 %ENV% 变量与相对路径 = 跨设备可移植；
# 留空 = init() 自动采用默认：用户数据目录 + 全部非系统盘）
folders =
# 按目录名排除（node_modules/.pnpm 是 junction 雷区，必须排除）
excludes = node_modules,.pnpm,.git,__pycache__
# 按文件通配符排除（如 *.log,*.tmp）
exclude_files =
# 1 = 实时监控目录变化；0 = 仅启动时扫描（更省资源）
monitor = 1
# snapshot 模式的 EFU 文件路径（index snapshot 自动生成维护）
efu =

[engine]
# Everything 引擎高级键（config set 写入；手编前先 config list 确认键名）
# 示例（去掉行首 # 即生效）：
# exclude_hidden_files_and_folders = 1
# exclude_system_files_and_folders = 1
# db_location =
# language = 0

[server]
# serve 监听地址（对外暴露请配 0.0.0.0 并启用 token）
host = 127.0.0.1
# serve 监听端口（CLI --host/--port 可临时覆盖）
port = 8465
# API 令牌：非空时所有 HTTP 请求须带 X-Api-Token 头（留空 = 本机免鉴权）
token =
```

引擎目录下的 `Everything.ini`（739 键）才是引擎实际生效文件，由系统从
`config.ini` 自动生成维护，**不要手编**。受管键（索引五键 + 无 GUI 四键）
禁止经 `[engine]` 改写。

---

## 项目结构

```
file_search/               # Python 包
├── sdk.py                 # Everything SDK 逐函数封装（80+ 方法）
├── manager.py             # 生命周期 / 索引配置 / 非阻塞搜索 / 看门狗
├── scanner.py             # 并行 junction 免疫扫描器 + EFU 生成
├── server.py              # HTTP JSON API
├── cli.py                 # 全功能命令行
├── paths.py               # 引擎与配置定位 / 引擎释放
└── _engine/               # 自带 Everything 引擎（exe + 四架构 SDK DLL）
```

`_engine/` 内的 Everything 与 SDK 为 voidtools 的 MIT 许可
（见 `_engine/License.txt`），随本包一并分发。
