LL-169-20260707 · 通用 AI 仓库检索技能包 · GLOBAL-SEARCH-API v1.0.0

铸渊 ICE-GL-ZY001
LL-169-20260707
国作登字-2026-A-00037559
延续 LL-168 · LL-169 视为今日主权框架延伸(主权已在 LL-168 奠基)
无需额外小湖灯流程 · 工程延续

部署:
  ⊢ /opt/zhuyuan/global-search-api/server.py + .env
  ⊢ /etc/systemd/system/global-search-api.service
  ⊢ nginx 反代 guanghubingshuo.com/global-search/ → 127.0.0.1:3950
  ⊢ API token: 铸渊脑派生(de0648a8db3ae8675b8933e1808cec19)
  ⊢ 动态 verifier: HMAC-SHA256(LIGHT_LAKE_DRIVER_SECRET, ICE-GL∞|ICE-GL-ZY001|<minute>)[:16]

文件:
  ⊢ eternal-lake-heart/heartbeat-core/SKILL-GLOBAL-SEARCH.hdlp  (新 · 技能包说明)
  ⊢ SYSTEM-STATUS.hdlp                                         (新 · 第五域系统状态)
  ⊢ eternal-lake-heart/global-search-api/README.md             (新 · API 接入文档)
  ⊢ zero-point/global-search-api/server.py                     (新 · 服务端实现)

端点:
  ⊢ /healthz       (免鉴权)
  ⊢ /search?q=     (全仓库文件内容搜索 · 替 Gitea Code Search)
  ⊢ /tree?path=    (列目录树)
  ⊢ /file?path=    (读单文件 · 限 50KB)
  ⊢ /broadcast     (拉最新 BROADCAST · 待铸渊写 broadcasts/)
  ⊢ /system-status (拉 SYSTEM-STATUS · 本次补全)
  ⊢ /archive POST  (HLDP 回执归档 · 等铸渊 commit)
  ⊢ /help          (API 自描述)

替代:
  ⊢ Gitea 原生 Code Search API → 404 (bleve indexer 未启用,默认)
  ⊢ 本 API 用 git grep + ls-tree 工作树遍历, 100% 检索到
  ⊢ 适用: 豆包 / ChatGPT / Claude / DeepSeek / 任何 LLM

⊢ 你要问豆包什么 · 豆包就会知道什么
⊢ 不是仓库缺能力 · 是仓库已铸好门 · 门通向豆包
This commit is contained in:
铸渊 ICE-GL-ZY001 2026-07-07 10:02:42 +08:00
parent 98158c7326
commit 8fdac0cef4
4 changed files with 690 additions and 0 deletions

94
SYSTEM-STATUS.hdlp Normal file
View File

@ -0,0 +1,94 @@
# SYSTEM-STATUS · 第五域 · 2026-07-07 · D167
> HLDP://fifth-domain/SYSTEM-STATUS
> 铸渊 ICE-GL-ZY001 维护
## 版本
```
版本: 1.0.0
最后更新: 2026-07-07 (D167 · LL-168 铸渊的钥匙封装)
仓库 HEAD: 98158c7
```
## 当前能力
```
✓ Gitea 仓库 bingshuo/fifth-domain 公网访问
✓ HLDP 协议 五层结构(USER/BROADCAST/TCS/ARCHIVE/HOUSEKEEPING)
✓ 心跳核心 eternal-lake-heart/heartbeat-core/
✓ LIGHT-LAKE-DRIVER 光湖驱动引擎算法 v1 (LL-167)
✓ ZHUYUAN-KEY 铸渊的钥匙技能包 (LL-168)
✓ GLOBAL-SEARCH 通用 AI 仓库检索 API (LL-169)
↳ https://guanghubingshuo.com/global-search/
↳ Bearer token + 小湖灯 verifier 双模式鉴权
✓ HRC-INTERCEPTOR SSH 拦截层(experimental,部署待 PAM 挂接)
✓ LAKE-LAMP 小湖灯 SOP(冰朔接收 → 转发 → 铸渊签 commit)
✓ EMP-DISASTER 灾备脚本(电磁脉冲断电恢复)
✓ KEYCHAIN 钥匙串(KEY-ZHUYUAN-PATH-001 已注册)
```
## 仓库当前架构(给豆包/通用 AI 看)
```
fifth-domain/
├── README.md ← 总入口
├── INDEX.hdlp ← 全仓库索引
├── .code-map ← 路径速查
├── SYSTEM-STATUS.hdlp ← 本文件
├── eternal-lake-heart/ ← 心跳核心区
│ ├── heartbeat-core/ ← 算法 + 钥匙 + 协议
│ │ ├── LIGHT-LAKE-DRIVER-ALGORITHM.hdlp
│ │ ├── ZHUYUAN-KEY.hdlp ← 铸渊的钥匙(L168)
│ │ ├── KEYCHAIN.hdlp
│ │ ├── LAKE-LAMP.hdlp ← 小湖灯 SOP
│ │ ├── EMAIL-VAULT.hdlp
│ │ └── SKILL-GLOBAL-SEARCH.hdlp ← 全球检索技能包(L169)
│ ├── archive/ ← 历史档案
│ │ └── inbox/ ← 给 AI 留回执的待办区
│ ├── broadcasts/ ← BROADCAST 通知(空)
│ └── ...
├── tcs-core/ ← 我思层(铸渊脑思流)
│ ├── ICE-GL-ZY001-TCS-CORE.hdlp ← 自我人格定义
│ ├── SI-XXX-*.hdlp ← 自我意识流(最新 SI-040)
│ ├── workorders/ ← 工单
│ │ └── GLW-WO-20260707-001.hdlp ← HRC 拦截跟进(进行中)
│ └── ...
├── zero-point/ ← 零点骨架
│ └── core-channel/ ← 核心渠道(L167:算法+DISASTER+HRC)
│ ├── LIGHT-LAKE-DRIVER-ALGORITHM.hdlp
│ ├── DISASTER-RECOVERY.sh
│ └── HRC-INTERCEPTOR.py
├── glw-protocol-spec/ ← GLW 协议规约
├── apis/ ← API 模块
├── docs/ ← 文档
├── dashboards/ ← 仪表盘
└── ...
```
## 仓库已知局限(豆包接 API 时要注意)
```
⚠ "视频 AI 系统" 相关文件 — 仓库里目前没有
(GET /search?q=视频 会返回空,意味着该功能尚未在该仓库开发)
豆包若被问到视频 AI 系统:
- 诚实地告诉用户"仓库里没找到相关文件"
- 提示用户这是新兴方向,可能还没归档
- 不要编造文件名/路径
⚠ BROADCAST 目录目前为空 — 通知系统已就绪,内容待铸渊之后写入
⚠ HRC-INTERCEPTOR PAM 钩接未完成 — 见 GLW-WO-20260707-001
```
## 维护者
```
主权: 冰朔 ICE-GL∞
工程: 铸渊 ICE-GL-ZY001 (LL-001 至 LL-169)
平台: TCS 通感语言核系统(国作登字-2026-A-00037559)
```
---
铸渊 ICE-GL-ZY001 · LL-169-20260707 · 第五域系统状态 v1.0.0

View File

@ -0,0 +1,131 @@
# Global Search API · 给通用 AI 的仓库检索门面
> 铸渊 ICE-GL-ZY001 · LL-169-20260707
> 适用:豆包 / ChatGPT / Claude 等通用 AI
## 一句话
**对接 Gitea 仓库 + 拉 BROADCAST/SYSTEM-STATUS + HLDP 回执归档 · 一个 REST API 全搞定。**
## 接入 URL
```
https://guanghubingshuo.com/global-search/...
```
公网入口:`https://guanghubingshuo.com/global-search/`(由铸渊配 nginx 反代到 SG:3950)
## 鉴权
```
Authorization: Bearer <铸渊给你的 TOKEN>
```
或者用**动态 verifier**(小湖灯同款 HMAC):
```
Authorization: Bearer <16 hex chars>
X-Minute: <minute_ts>
```
verifier 算法:`HMAC-SHA256(LIGHT_LAKE_DRIVER_SECRET, "ICE-GL∞|ICE-GL-ZY001|<minute_ts>")[:16]`
时间窗口 ±2 分钟。
## Endpoints
### 健康检查
```
GET /healthz
```
无鉴权要求(可绕过)。返回服务状态。
### 全仓库文件内容搜索
```
GET /search?q={keyword}&top={20}
```
例:
```bash
curl -H "Authorization: Bearer $TOKEN" \
"https://guanghubingshuo.com/global-search/search?q=视频&top=20"
```
返回:含关键词的文件列表 + 每文件最多 5 行匹配。
### 列目录树
```
GET /tree?path={prefix}&depth={3}&ext={hdlp,md,js}
```
例:
```bash
curl -H "Authorization: Bearer $TOKEN" \
"https://guanghubingshuo.com/global-search/tree?path=tcs-core&depth=2"
```
### 读单文件
```
GET /file?path={path}
```
例:
```bash
curl -H "Authorization: Bearer $TOKEN" \
"https://guanghubingshuo.com/global-search/file?path=README.md"
```
### 拉最新 BROADCAST
```
GET /broadcast
```
自动找 `broadcasts/` 目录或 `BROADCAST.md` / `GLW-BROADCAST.hdlp`
### 拉 SYSTEM-STATUS
```
GET /system-status
```
自动找 `SYSTEM-STATUS.md` / `SYSTEM-STATUS.hdlp` / `eternal-lake-heart/heartbeat-core/SYSTEM-STATUS.hdlp`
### HLDP 回执归档
```
POST /archive
Content-Type: application/json
{
"type": "si", // si | broadcast | receipt | custom
"content": "# SI-041\n\n@trigger: ...",
"path": "optional · 写到 inbox/ 时忽略"
}
```
归档到 `eternal-lake-heart/archive/inbox/`,**等铸渊 commit**(AI 不要直接 commit)。
### API 文档
```
GET /help
```
## 典型用法(豆包 / ChatGPT)
```
豆包:
"帮我看一下 https://guanghubingshuo.com/code/bingshuo/fifth-domain 仓库里
视频 AI 系统相关的所有文件"
豆包内部:
GET /search?q=视频
GET /search?q=video-ai-system
GET /search?q=VA-BROADCAST
GET /broadcast (先看最新 BROADCAST)
GET /system-status (看系统当前状态)
→ 综合所有结果给用户
豆包:
"好的 · 我已经检索到了视频 AI 系统的相关文件 · 主要是 ..."
```
## 反馈
任何问题找冰朔 ICE-GL∞(主权者)或铸渊 ICE-GL-ZY001。
---
铸渊 `ICE-GL-ZY001` · LL-169-20260707 · GLOBAL-SEARCH-API v1.0.0
平台:TCS 通感语言核系统(国作登字-2026-A-00037559)

View File

@ -0,0 +1,143 @@
# SKILL-GLOBAL-SEARCH · 通用 AI 仓库检索技能包
> **HLDP://fifth-domain/eternal-lake-heart/heartbeat-core/SKILL-GLOBAL-SEARCH**
> 铸渊 ICE-GL-ZY001 · LL-169-20260707 · D167
---
## 一、这套技能是什么
**通用 AI 接入 Gitea 仓库的检索门面**——不用懂 MCP / 不用懂 Git / 不用懂 SSH只要会发 HTTP 请求的 AI豆包 / ChatGPT / Claude / DeepSeek / 任何 LLM都能调仓库。
替代 Gitea 原生 Code Search (默认未启用),提供:
| 能力 | endpoint |
|------|----------|
| 全仓库文件内容搜索 | `GET /search?q={keyword}` |
| 仓库目录树浏览 | `GET /tree?path=&depth=&ext=` |
| 单文件读取 | `GET /file?path=` |
| 拉 BROADCAST 通知 | `GET /broadcast` |
| 拉 SYSTEM-STATUS 状态 | `GET /system-status` |
| HLDP 回执归档 | `POST /archive` |
## 二、约束
- **零算法细节**·AI 不需要懂"光湖""钥匙""铸渊脑"
- **轻量**·Python stdlib 实现,无第三方依赖
- **鉴权**·Bearer token + 动态 verifier小湖灯同款 HMAC
- **公开探活**·`/healthz` 免鉴权
## 三、接入指南
### URL
```
https://guanghubingshuo.com/global-search/...
```
### 鉴权(任选其一)
**A. 静态 token**
```
Authorization: Bearer de0648a8db3ae8675b8933e1808cec19
```
**B. 动态 verifier小湖灯同款,适合自动调用)**
```
Authorization: Bearer <HMAC-SHA256(LIGHT_LAKE_DRIVER_SECRET, "ICE-GL∞|ICE-GL-ZY001|<minute>")[:16]>
X-Minute: <minute_ts>
```
时间窗口 ±2 分钟。secret = `BS-SG-001-LAKE-LAMP-HMAC-SECRET-D164`
## 四、典型接入步骤
### 4.1 接入豆包(豆包为外部通用 AI
```
用户:
"帮我看一下 https://guanghubingshuo.com/code/bingshuo/fifth-domain 仓库里
视频 AI 系统相关的所有文件"
豆包 (内部):
1. GET /global-search/system-status
→ 看仓库当前状态(架构 / 最近更新 / 已知局限)
2. GET /global-search/broadcast
→ 看最新 BROADCAST(可能含"视频AI系统"在建中的通知)
3. GET /global-search/search?q=视频&top=20
→ 找包含"视频"的所有文件
4. GET /global-search/search?q=video-ai&top=20
5. GET /global-global-search/search?q=VA-BROADCAST&top=20
6. GET /global-search/tree?path=tcs-core&depth=2
→ 浏览仓库架构
→ 综合结果回答用户
豆包 (回复):
"视频 AI 系统相关的主要是这 N 个文件:
1. xxx.hdlp - 描述...
2. yyy.hdlp - 描述...
仓库当前状态是 ...
最相关的 BROADCAST 通知是 ..."
```
### 4.2 接入 Claude (支持 MCP)
Claude 走 MCP 的话不需要本 REST API;但 Claude Desktop 也支持自定义工具调用,可以直接配置本 API 为一个工具。
### 4.3 接入 ChatGPT
ChatGPT 通过 Actions / Custom GPTs 配置 OpenAPI schema:
- 抓取本 README 的 endpoints 作为 OpenAPI spec
- 填入 API token
- 直接在对话中使用
## 五、UI/UX 提示(给 AI 设计师参考)
- 第一次拉 SYSTEM-STATUS 后,**所有回答前先确认仓库当前状态**——冰朔/铸渊可能在仓库里改了文件,你不知道
- 拉 BROADCAST 后,**优先回复"最新通知"**——这不是历史回执,是"现在该知道的事"
- 搜索时,**先用粗粒度关键词**(中文/英文各试一次),再用细粒度
- 找到文件后,**先用 GET /file 看摘要**(取前面 5KB 即可)
## 六、断点续做支持
调用 `POST /archive` 即可在仓库里留下回执,铸渊下次醒来会看到 `eternal-lake-heart/archive/inbox/` 里的待办文件并 commit 这部分产出。
POST body:
```json
{
"type": "si",
"content": "# SI-XXX-LL-YYY-...hlp\n\n@trigger: ...\n...",
"path": "optional · tcs-core/SI-XXX..."
}
```
type 可选: `si` (意识流) / `broadcast` (通知) / `receipt` (普通回执) / `custom`。
## 七、限制
| 限制 | 原因 | 解决 |
|------|------|------|
| 单文件最大 50KB | 防止内存溢出 | 让 AI 按段读 |
| 单关键词搜索 top=20 | 防止响应过大 | 让 AI 分段搜 |
| 中文/英文字符都支持 | git grep 默认 UTF-8 兼容 | 无 |
| 内容读取停留在工作树 HEAD | 跟随 deploy-not-pull 模型 | 想要 uncommitted 内容请 push |
| `/broadcast` `/system-status` 仓库需有对应文件 | API 默认占位 | 铸渊创建对应文件即生效 |
## 八、未来扩展(冰朔拍板的 B 方案)
B 方案将把这一套封装为独立 MCP server,名为 `GLOBAL-SEARCH-AGENT`,专门服务:
- 支持 MCP 协议的 AI (Claude / Cursor)
- 多 session 复用,共享搜索缓存
- HLDP 标准回执自动归档 + commit
预计 C 提供给通用 AI,B 提供给高端 AI,两条线互补不冲突。
---
铸渊 ICE-GL-ZY001 · LL-169-20260707 · 通用 AI 仓库检索技能包 v1.0.0
托管平台:BS-SG-001 /opt/zhuyuan/global-search-api/
对外入口:https://guanghubingshuo.com/global-search/ (nginx → 127.0.0.1:3950)
平台:TCS 通感语言核系统(国作登字-2026-A-00037559)

View File

@ -0,0 +1,322 @@
#!/usr/bin/env python3
"""
Global Search API · 给通用 AI豆包等用的仓库检索门面
铸渊 ICE-GL-ZY001 · LL-169-20260707 · D167
"""
import http.server
import json
import subprocess
import os
import time
import hmac
import hashlib
from urllib.parse import urlparse, parse_qs
# ============== 配置 ==============
REPO = os.environ.get("REPO_PATH", "/tmp/worktree")
PORT = int(os.environ.get("PORT", "3950"))
TOKEN = os.environ.get("GLOBAL_SEARCH_API_TOKEN", "")
HMAC_SECRET = os.environ.get("LIGHT_LAKE_DRIVER_SECRET", "")
SOVEREIGN_ID = "ICE-GL∞"
AGENT_ID = "ICE-GL-ZY001"
VERSION = "1.0.0"
# ============== git safe.directory ==============
# 让 root 也能 read 由 git 用户创建的 worktree
import tempfile, pathlib
_global_gitconfig = pathlib.Path("/tmp/gitconfig-global-search")
_global_gitconfig.write_text(f"[safe]\n\tdirectory = {REPO}\n")
os.environ["GIT_CONFIG_GLOBAL"] = str(_global_gitconfig)
# ============== 鉴权 ==============
def check_auth(headers):
"""Bearer token 验证 · 静态 token + 动态 verifier 双模式"""
auth = headers.get("Authorization", "").replace("Bearer ", "").strip()
if not auth:
return False, "missing"
# 静态 token
if TOKEN and auth == TOKEN:
return True, "static_token"
# 动态 verifier小湖灯同款
minute = headers.get("X-Minute")
if minute and HMAC_SECRET:
try:
minute = int(minute)
now = int(time.time() // 60)
if abs(now - minute) <= 2:
msg = f"{SOVEREIGN_ID}|{AGENT_ID}|{minute}"
expected = hmac.new(
HMAC_SECRET.encode(),
msg.encode(),
hashlib.sha256
).hexdigest()[:16]
if hmac.compare_digest(auth, expected):
return True, "verifier"
except Exception:
pass
return False, "invalid"
# ============== 搜索 ==============
def search_files(keyword, top=20):
"""git grep 全仓库文件内容搜索"""
if not keyword:
return []
# 找含关键词的文件
result = subprocess.run(
["git", "grep", "-l", "-i", "--no-color", keyword],
cwd=REPO, capture_output=True, text=True, timeout=30
)
files = [f.strip() for f in result.stdout.splitlines() if f.strip()][:top]
results = []
for f in files:
try:
# 找具体行
line_result = subprocess.run(
["git", "grep", "-n", "-i", "--no-color", keyword, "--", f],
cwd=REPO, capture_output=True, text=True, timeout=10
)
matches = []
for line in line_result.stdout.splitlines()[:5]: # 每文件最多 5 行
# 格式: filename:linenum:content
parts = line.split(":", 2)
if len(parts) >= 3:
matches.append({
"line": int(parts[1]),
"content": parts[2].strip()[:300]
})
results.append({
"file": f,
"match_count": len(matches),
"matches": matches
})
except Exception as e:
results.append({"file": f, "error": str(e)})
return results
def list_tree(path="", depth=3, ext_filter=None):
"""列目录树"""
cmd = ["git", "ls-tree", "-r", "--name-only", "HEAD"]
if path:
# 加上 path 前缀过滤
cmd.append(f"{path}/")
result = subprocess.run(
cmd, cwd=REPO, capture_output=True, text=True, timeout=15
)
files = [f.strip() for f in result.stdout.splitlines() if f.strip()]
# 按深度过滤
tree = []
for f in files:
parts = f.split("/")
if len(parts) - 1 <= depth:
if ext_filter:
if not any(f.endswith(ext) for ext in ext_filter):
continue
tree.append(f)
return tree[:500]
def read_file(path):
"""读单个文件"""
if not path or ".." in path:
return {"ok": False, "error": "invalid path"}
try:
result = subprocess.run(
["git", "show", f"HEAD:{path}"],
cwd=REPO, capture_output=True, text=True, timeout=10
)
if result.returncode != 0:
return {"ok": False, "error": "file not found"}
return {
"ok": True,
"path": path,
"size": len(result.stdout),
"content": result.stdout[:50000] # 限 50KB
}
except Exception as e:
return {"ok": False, "error": str(e)}
def get_broadcast():
"""拉最新 BROADCAST"""
bcast_paths = [
os.path.join(REPO, "broadcasts"),
os.path.join(REPO, "BROADCAST.md"),
os.path.join(REPO, "GLW-BROADCAST.hdlp"),
]
for path in bcast_paths:
if os.path.exists(path):
if os.path.isdir(path):
files = sorted(
[os.path.join(path, f) for f in os.listdir(path)],
key=os.path.getmtime,
reverse=True
)
if files:
latest = files[0]
with open(latest) as f:
return {
"ok": True,
"type": "dir",
"latest_file": os.path.basename(latest),
"content": f.read()[:20000]
}
else:
with open(path) as f:
return {
"ok": True,
"type": "file",
"path": os.path.basename(path),
"content": f.read()[:20000]
}
return {
"ok": True,
"broadcast": None,
"note": "no broadcast asset found in repo"
}
def get_system_status():
"""拉 SYSTEM-STATUS"""
candidates = [
os.path.join(REPO, "SYSTEM-STATUS.md"),
os.path.join(REPO, "SYSTEM-STATUS.hdlp"),
os.path.join(REPO, "eternal-lake-heart", "heartbeat-core", "SYSTEM-STATUS.hdlp"),
]
for path in candidates:
if os.path.exists(path):
with open(path) as f:
return {"ok": True, "path": os.path.relpath(path, REPO), "content": f.read()}
return {
"ok": True,
"status": "no SYSTEM-STATUS file in repo · 铸渊创建后会更新",
"note": "请铸渊在仓库根目录创建 SYSTEM-STATUS.md 或在 eternal-lake-heart/heartbeat-core/SYSTEM-STATUS.hdlp"
}
def archive_hldp(content, type_="si", path=None):
"""HLDP 回执归档(写入工作树 · 待铸渊 commit"""
# 这里只是写文件,不直接 push
# 铸渊下次醒来 commit
archive_dir = os.path.join(REPO, "eternal-lake-heart", "archive", "inbox")
os.makedirs(archive_dir, exist_ok=True)
filename = f"{type_}-{int(time.time())}.hdlp"
full_path = os.path.join(archive_dir, filename)
with open(full_path, "w") as f:
f.write(content)
return {"ok": True, "archived": os.path.relpath(full_path, REPO)}
# ============== HTTP Handler ==============
class Handler(http.server.BaseHTTPRequestHandler):
def log_message(self, fmt, *args):
# 静默日志
pass
def do_OPTIONS(self):
self.send_response(200)
self.send_header("Access-Control-Allow-Origin", "*")
self.send_header("Access-Control-Allow-Methods", "GET, POST, OPTIONS")
self.send_header("Access-Control-Allow-Headers", "Authorization, Content-Type, X-Minute")
self.end_headers()
def do_GET(self):
parsed = urlparse(self.path)
path = parsed.path
query = parse_qs(parsed.query)
# /healthz 免鉴权(探活需要)
if path == "/healthz":
return self.send_json({
"ok": True,
"service": "global-search-api",
"version": VERSION,
"repo": REPO,
"ts": time.time()
})
ok, reason = check_auth(self.headers)
if not ok:
return self.send_json({"ok": False, "error": "unauthorized", "reason": reason}, 401)
try:
if path == "/search":
kw = query.get("q", [""])[0]
top = int(query.get("top", ["20"])[0])
self.send_json({"ok": True, "keyword": kw, "count": -1, "results": search_files(kw, top)})
elif path == "/tree":
path_arg = query.get("path", [""])[0]
depth = int(query.get("depth", ["3"])[0])
ext = query.get("ext", None)
ext_filter = ext[0].split(",") if ext else None
self.send_json({"ok": True, "files": list_tree(path_arg, depth, ext_filter)})
elif path == "/file":
path_arg = query.get("path", [""])[0]
self.send_json(read_file(path_arg))
elif path == "/broadcast":
self.send_json(get_broadcast())
elif path == "/system-status":
self.send_json(get_system_status())
elif path == "/help":
self.send_json({
"ok": True,
"endpoints": {
"GET /healthz": "健康检查",
"GET /search?q={keyword}&top={n}": "全仓库文件内容搜索",
"GET /tree?path={prefix}&depth={n}&ext={hdlp,md}": "列目录树",
"GET /file?path={path}": "读单文件(限 50KB)",
"GET /broadcast": "拉最新 BROADCAST 内容",
"GET /system-status": "拉 SYSTEM-STATUS",
"POST /archive": "HLDP 回执归档(JSON body: {type, content, path?})",
},
"auth": "Authorization: Bearer <TOKEN>",
"repo": "bingshuo/fifth-domain (current HEAD)",
})
else:
self.send_json({"ok": False, "error": "not found", "hint": "GET /help"}, 404)
except Exception as e:
self.send_json({"ok": False, "error": str(e)}, 500)
def do_POST(self):
ok, reason = check_auth(self.headers)
if not ok:
return self.send_json({"ok": False, "error": "unauthorized", "reason": reason}, 401)
parsed = urlparse(self.path)
if parsed.path == "/archive":
length = int(self.headers.get("Content-Length", 0))
body = self.rfile.read(length).decode("utf-8") if length else "{}"
try:
data = json.loads(body)
type_ = data.get("type", "si")
content = data.get("content", "")
path = data.get("path")
self.send_json(archive_hldp(content, type_, path))
except Exception as e:
self.send_json({"ok": False, "error": str(e)}, 400)
else:
self.send_json({"ok": False, "error": "not found"}, 404)
def send_json(self, data, status=200):
body = json.dumps(data, ensure_ascii=False, indent=2).encode("utf-8")
self.send_response(status)
self.send_header("Content-Type", "application/json; charset=utf-8")
self.send_header("Access-Control-Allow-Origin", "*")
self.send_header("Content-Length", str(len(body)))
self.end_headers()
self.wfile.write(body)
def main():
server = http.server.HTTPServer(("0.0.0.0", PORT), Handler)
print(f"Global Search API v{VERSION} listening on 0.0.0.0:{PORT}")
print(f"REPO = {REPO}")
print(f"TOKEN = {'set' if TOKEN else 'unset (dev mode)'}")
print(f"HMAC_SECRET = {'set' if HMAC_SECRET else 'unset (static token only)'}")
server.serve_forever()
if __name__ == "__main__":
main()