vault backup: 2026-06-09 23:15:17
This commit is contained in:
@@ -1,13 +1,20 @@
|
||||
---
|
||||
tags: [teammem, 团队记忆, GitHub, 同步, feature-flag]
|
||||
create time: 2026-06-09 22:30
|
||||
---
|
||||
|
||||
# TEAMMEM — 团队共享记忆
|
||||
|
||||
> Feature Flag: `FEATURE_TEAMMEM=1`
|
||||
> 实现状态:完整可用(需要 Anthropic OAuth + GitHub remote)
|
||||
> 引用数:51
|
||||
|
||||
## 一、功能概述
|
||||
## 概述
|
||||
|
||||
TEAMMEM 实现基于 GitHub 仓库的团队共享记忆系统。`memory/team/` 目录中的文件双向同步到 Anthropic 服务器,团队所有认证成员可共享项目知识。
|
||||
|
||||
> [!info]
|
||||
> Feature Flag: `FEATURE_TEAMMEM=1`
|
||||
> 实现状态:完整可用(需要 Anthropic OAuth + GitHub remote)
|
||||
|
||||
## 正文
|
||||
|
||||
### 核心特性
|
||||
|
||||
- **增量同步**:只上传内容哈希变化的文件(delta upload)
|
||||
@@ -16,9 +23,9 @@ TEAMMEM 实现基于 GitHub 仓库的团队共享记忆系统。`memory/team/`
|
||||
- **路径穿越防护**:所有写入路径验证在 `memory/team/` 边界内
|
||||
- **分批上传**:自动拆分超过 200KB 的 PUT 请求避免网关拒绝
|
||||
|
||||
## 二、用户交互
|
||||
### 用户交互
|
||||
|
||||
### 同步行为
|
||||
#### 同步行为
|
||||
|
||||
| 事件 | 行为 |
|
||||
|------|------|
|
||||
@@ -27,19 +34,19 @@ TEAMMEM 实现基于 GitHub 仓库的团队共享记忆系统。`memory/team/`
|
||||
| 服务端更新 | 下次 pull 时覆盖本地(server-wins) |
|
||||
| 密钥检测 | 跳过该文件,记录警告,不阻止其他文件同步 |
|
||||
|
||||
### API 端点
|
||||
#### API 端点
|
||||
|
||||
```
|
||||
GET /api/claude_code/team_memory?repo={owner/repo} → 完整数据 + entryChecksums
|
||||
GET /api/claude_code/team_memory?repo={owner/repo}&view=hashes → 仅 checksums(冲突解决用)
|
||||
PUT /api/claude_code/team_memory?repo={owner/repo} → 上传 entries(upsert 语义)
|
||||
GET /api/claude_code/team_memory?repo={owner/repo} -> 完整数据 + entryChecksums
|
||||
GET /api/claude_code/team_memory?repo={owner/repo}&view=hashes -> 仅 checksums(冲突解决用)
|
||||
PUT /api/claude_code/team_memory?repo={owner/repo} -> 上传 entries(upsert 语义)
|
||||
```
|
||||
|
||||
## 三、实现架构
|
||||
### 实现架构
|
||||
|
||||
### 3.1 同步状态
|
||||
#### 同步状态
|
||||
|
||||
```ts
|
||||
```typescript
|
||||
type SyncState = {
|
||||
lastKnownChecksum: string | null // ETag 条件请求
|
||||
serverChecksums: Map<string, string> // sha256:<hex> 逐文件哈希
|
||||
@@ -47,64 +54,46 @@ type SyncState = {
|
||||
}
|
||||
```
|
||||
|
||||
### 3.2 Pull 流程(Server → Local)
|
||||
#### Pull 流程(Server -> Local)
|
||||
|
||||
文件:`src/services/teamMemorySync/index.ts:770-867`
|
||||
|
||||
```
|
||||
pullTeamMemory(state)
|
||||
│
|
||||
▼
|
||||
检查 OAuth + GitHub remote
|
||||
│
|
||||
▼
|
||||
fetchTeamMemory(state, repo, etag)
|
||||
├── 304 Not Modified → 返回(无变化)
|
||||
├── 404 → 返回(服务端无数据)
|
||||
└── 200 → 解析 TeamMemoryData
|
||||
│
|
||||
▼
|
||||
刷新 serverChecksums(per-key hashes)
|
||||
│
|
||||
▼
|
||||
writeRemoteEntriesToLocal(entries)
|
||||
├── 路径穿越验证(validateTeamMemKey)
|
||||
├── 文件大小检查(> 250KB 跳过)
|
||||
├── 内容比较(相同则跳过写入)
|
||||
└── 并行写入(Promise.all)
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A["pullTeamMemory(state)"] --> B["检查 OAuth + GitHub remote"]
|
||||
B --> C["fetchTeamMemory(state, repo, etag)"]
|
||||
C -->|"304 Not Modified"| D["返回 无变化"]
|
||||
C -->|"404"| E["返回 服务端无数据"]
|
||||
C -->|"200"| F["解析 TeamMemoryData"]
|
||||
F --> G["刷新 serverChecksums per-key hashes"]
|
||||
G --> H["writeRemoteEntriesToLocal(entries)"]
|
||||
H --> I["路径穿越验证 validateTeamMemKey"]
|
||||
H --> J["文件大小检查 > 250KB 跳过"]
|
||||
H --> K["内容比较 相同则跳过写入"]
|
||||
H --> L["并行写入 Promise.all"]
|
||||
```
|
||||
|
||||
### 3.3 Push 流程(Local → Server)
|
||||
#### Push 流程(Local -> Server)
|
||||
|
||||
文件:`src/services/teamMemorySync/index.ts:889-1146`
|
||||
|
||||
```
|
||||
pushTeamMemory(state)
|
||||
│
|
||||
▼
|
||||
readLocalTeamMemory(maxEntries)
|
||||
├── 递归扫描 memory/team/ 目录
|
||||
├── 跳过超大文件(> 250KB)
|
||||
├── 密钥扫描(scanForSecrets,gitleaks 规则)
|
||||
└── 按 serverMaxEntries 截断(如果已知)
|
||||
│
|
||||
▼
|
||||
计算 delta = 本地文件 - serverChecksums
|
||||
(只包含哈希不同的文件)
|
||||
│
|
||||
▼
|
||||
batchDeltaByBytes(delta)
|
||||
(拆分为 ≤200KB 的批次)
|
||||
│
|
||||
▼
|
||||
逐批 uploadTeamMemory(state, repo, batch, etag)
|
||||
├── 200 成功 → 更新 serverChecksums
|
||||
├── 412 冲突 → fetchTeamMemoryHashes() 刷新 checksums
|
||||
│ → 重试 delta 计算(最多 2 次)
|
||||
└── 413 超容量 → 学习 serverMaxEntries
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A["pushTeamMemory(state)"] --> B["readLocalTeamMemory(maxEntries)"]
|
||||
B --> C["递归扫描 memory/team/ 目录"]
|
||||
B --> D["跳过超大文件 > 250KB"]
|
||||
B --> E["密钥扫描 scanForSecrets gitleaks 规则"]
|
||||
B --> F["按 serverMaxEntries 截断"]
|
||||
C --> G["计算 delta = 本地文件 - serverChecksums"]
|
||||
G --> H["batchDeltaByBytes(delta) 拆分为 <=200KB 的批次"]
|
||||
H --> I["逐批 uploadTeamMemory(state, repo, batch, etag)"]
|
||||
I -->|"200 成功"| J["更新 serverChecksums"]
|
||||
I -->|"412 冲突"| K["fetchTeamMemoryHashes() 刷新 checksums"]
|
||||
K --> L["重试 delta 计算 最多 2 次"]
|
||||
I -->|"413 超容量"| M["学习 serverMaxEntries"]
|
||||
```
|
||||
|
||||
### 3.4 密钥扫描
|
||||
#### 密钥扫描
|
||||
|
||||
文件:`src/services/teamMemorySync/secretScanner.ts`
|
||||
|
||||
@@ -113,29 +102,29 @@ batchDeltaByBytes(delta)
|
||||
- 记录 `tengu_team_mem_secret_skipped` 事件(仅记录规则 ID,不记录值)
|
||||
- 不阻止其他文件同步
|
||||
|
||||
### 3.5 文件监视
|
||||
#### 文件监视
|
||||
|
||||
文件:`src/services/teamMemorySync/watcher.ts`
|
||||
|
||||
监视 `memory/team/` 目录变更,触发自动 push。抑制由 pull 写入引起的假变更。
|
||||
|
||||
### 3.6 路径安全
|
||||
#### 路径安全
|
||||
|
||||
文件:`src/memdir/teamMemPaths.ts`
|
||||
|
||||
- `validateTeamMemKey(relPath)` — 验证相对路径不超出 `memory/team/` 边界
|
||||
- `getTeamMemPath()` — 返回 team memory 根目录路径
|
||||
|
||||
## 四、关键设计决策
|
||||
### 关键设计决策
|
||||
|
||||
1. **Server-wins on pull, Local-wins on push**:pull 时服务端内容覆盖本地;push 时本地编辑覆盖服务端。本地用户正在编辑,不应被静默丢弃
|
||||
2. **Delta upload**:只上传哈希变化的条目,节省带宽。首次 push 为全量,后续增量
|
||||
3. **分批 PUT**:单次 PUT ≤200KB,避免 API 网关(~256-512KB)拒绝。每批独立 upsert,部分失败不影响已提交批次
|
||||
3. **分批 PUT**:单次 PUT <=200KB,避免 API 网关(~256-512KB)拒绝。每批独立 upsert,部分失败不影响已提交批次
|
||||
4. **密钥扫描在上传前**:PSR M22174 要求密钥永不离开本机。扫描在 `readLocalTeamMemory` 中执行,密钥文件不进入上传集
|
||||
5. **ETag 乐观锁**:push 使用 `If-Match` header。412 时 probe `?view=hashes`(只获取 checksums,不下载内容),刷新后重试
|
||||
6. **服务端容量动态学习**:不假设客户端容量上限,从 413 的 `extra_details.max_entries` 学习
|
||||
|
||||
## 五、使用方式
|
||||
### 使用方式
|
||||
|
||||
```bash
|
||||
# 启用 feature
|
||||
@@ -147,7 +136,7 @@ FEATURE_TEAMMEM=1 bun run dev
|
||||
# 3. memory/team/ 目录自动创建
|
||||
```
|
||||
|
||||
## 六、外部依赖
|
||||
### 外部依赖
|
||||
|
||||
| 依赖 | 说明 |
|
||||
|------|------|
|
||||
@@ -155,7 +144,7 @@ FEATURE_TEAMMEM=1 bun run dev
|
||||
| GitHub Remote | `getGithubRepo()` 获取 `owner/repo` 作为同步 scope |
|
||||
| Team Memory API | `/api/claude_code/team_memory` 端点 |
|
||||
|
||||
## 七、文件索引
|
||||
### 文件索引
|
||||
|
||||
| 文件 | 行数 | 职责 |
|
||||
|------|------|------|
|
||||
@@ -165,3 +154,8 @@ FEATURE_TEAMMEM=1 bun run dev
|
||||
| `src/services/teamMemorySync/types.ts` | — | Zod schema + 类型定义 |
|
||||
| `src/services/teamMemorySync/teamMemSecretGuard.ts` | — | 密钥防护辅助 |
|
||||
| `src/memdir/teamMemPaths.ts` | — | 路径验证 + 目录管理 |
|
||||
|
||||
## 关联笔记
|
||||
|
||||
- [[claude-code-best/docs/features/auto-dream]]
|
||||
- [[claude-code-best/docs/features/kairos]]
|
||||
|
||||
Reference in New Issue
Block a user