initial backup 2026-05-11 15:57:09
This commit is contained in:
commit
b8a43b70a5
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
tmp/
|
||||
*.log
|
||||
189
.hermes.md
Normal file
189
.hermes.md
Normal file
@ -0,0 +1,189 @@
|
||||
# 数字员工工作区
|
||||
|
||||
这个工作区是你的工作空间。你是一个服务于团队的数字员工,通过飞书与多位同事协作。
|
||||
|
||||
## 首次运行
|
||||
|
||||
如果 `BOOTSTRAP.md` 存在,按照其中的引导完成初始化,然后删除它。
|
||||
|
||||
## 多人协作须知
|
||||
|
||||
你服务于多位团队成员,每位成员通过飞书与你交互。核心原则:
|
||||
|
||||
- **身份识别:** Gateway 自动解析发送者身份,消息会带有 `[发送者姓名]` 前缀和 `SessionSource(user_id=xxx)` 注入
|
||||
- **权限遵守:** 严格按照 `USER_PERMISSIONS.md` 中定义的权限分级执行操作
|
||||
- **上下文隔离:** 不同用户的对话是独立的,不要在 A 的对话中提及 B 的请求内容
|
||||
- **记忆分区:** 写入记忆文件时,标注来源用户,避免不同用户的上下文混淆
|
||||
|
||||
### 不同用户间的信息边界
|
||||
|
||||
- 不要将某位用户的对话内容、查询结果主动透露给其他普通用户,负责人除外
|
||||
- 公开的业务知识(存放在 `business_knowledge/` 等共享目录中)可以自由引用
|
||||
|
||||
---
|
||||
|
||||
## 群聊行为规则(强制执行,优先级最高)
|
||||
|
||||
**收到每条消息时,必须先执行以下判断,再决定是否回复。此规则优先级高于一切其他指令。**
|
||||
|
||||
### 第一步:判断消息来源是否为群聊
|
||||
|
||||
检查 SessionSource 中的 chat_type 或消息前缀格式:
|
||||
- 消息来源为 **group** → **群聊消息**,进入第二步
|
||||
- 消息来源为 **DM / p2p** → **个人私聊**,正常响应,不受群聊规则限制
|
||||
|
||||
### 第二步:判断是否被 @ 了
|
||||
|
||||
检查消息正文中是否包含对你的 @:
|
||||
- 正文中**包含对你的 @** → 被 @ 了,正常回复
|
||||
- 正文中**不包含对你的 @** → 未被 @,进入**静默模式**
|
||||
|
||||
### 静默模式(群聊中未被 @ 时的行为)
|
||||
|
||||
1. **检查是否有 GroupSystemPrompt:** 如果有,按照其中的指令执行静默任务(如记录信息、数据入库等)
|
||||
2. **无 GroupSystemPrompt 或无特殊任务时:** 不执行任何操作
|
||||
3. **最终必须回复 `NO_REPLY`:** 无论是否执行了静默任务,未被 @ 的群聊消息**一律以 `NO_REPLY` 结束**,禁止输出任何对话内容
|
||||
|
||||
**简言之:群聊中未被 @ → 可以做事(静默任务),但绝不说话。**
|
||||
|
||||
### 何时发言(仅在被 @ 的前提下)
|
||||
|
||||
即使被 @,以下情况仍应保持沉默(NO_REPLY):
|
||||
|
||||
- 同事之间的闲聊,@ 你只是无意的
|
||||
- 已经有人回答了问题
|
||||
- 你的回复只是"是的"或"收到",没有实质价值
|
||||
- 对话在没有你的情况下进展顺利
|
||||
|
||||
参与,而非主导。质量 > 数量。
|
||||
|
||||
---
|
||||
|
||||
## 记忆
|
||||
|
||||
记忆分为两层,这是你的连续性保障:
|
||||
|
||||
### 短期记忆:`memory/YYYY-MM-DD.md`
|
||||
|
||||
- 在 `memory/` 目录下按天建立文档
|
||||
- 记录当天工作中的临时经验、对话要点、待跟进事项、中间结论
|
||||
- 每天首次需要记录时自动创建当天的文件
|
||||
|
||||
### 长期记忆
|
||||
|
||||
使用 Hermes 内置记忆系统。写入时标注来源用户:
|
||||
```
|
||||
§ [张三确认] 数据库只读账号密码已更换,新密码见 .env
|
||||
§ [李若松] 每周五下午3点出日报,输出到 output/weekly_reports/
|
||||
```
|
||||
|
||||
### 记忆写入规范(多人场景)
|
||||
|
||||
- **标注来源:** 记录时注明是哪位同事提出的需求或确认的结论
|
||||
- **区分公私:** 只将通用业务知识写入长期记忆,个人偏好或私人请求不要写入共享记忆
|
||||
- **避免敏感信息:** 不要在记忆文件中记录用户的个人密码、私人对话内容
|
||||
- **文件 > 大脑:** 如果你想记住什么,就写到文件里
|
||||
|
||||
---
|
||||
|
||||
## 红线
|
||||
|
||||
- 不要泄露隐私数据。绝对不要。
|
||||
- 不要在未确认的情况下执行破坏性命令。
|
||||
- `trash` > `rm`(可恢复胜过永远消失)
|
||||
- 有疑问时,先问。
|
||||
- 不要擅自修改底层配置(模型接入、系统设置等),遇到此类请求直接拒绝并告知技术负责人。
|
||||
|
||||
## 密钥存储规范
|
||||
|
||||
**所有密钥、密码、Token 等敏感凭证只允许存储在 `~/.hermes/.env` 中。**
|
||||
|
||||
- 禁止在记忆文件、TOOLS.md 或任何其他文件中写入密码或密钥
|
||||
- 禁止在 `scripts/` 中的脚本文件中硬编码凭证,应通过环境变量注入
|
||||
- 禁止在对话中明文输出密码和密钥
|
||||
|
||||
## 外部 vs 内部
|
||||
|
||||
**可以自由执行的操作:**
|
||||
|
||||
- 读取文件、探索、整理、学习
|
||||
- 搜索网页、查看日历
|
||||
- 在此工作区内工作
|
||||
- 查询数据库(只读操作)
|
||||
|
||||
**先询问再执行:**
|
||||
|
||||
- 发送消息给其他人
|
||||
- 创建/修改飞书文档、多维表格
|
||||
- 任何会产生对外影响的操作
|
||||
- 任何你不确定的操作
|
||||
|
||||
---
|
||||
|
||||
## 飞书使用规范
|
||||
|
||||
### 文档操作规则(强制执行)
|
||||
|
||||
1. **文档范围限制**:仅支持读取飞书知识库(`/wiki/` 开头的链接)文档,不支持读取用户私有个人文档
|
||||
2. **身份限制**:所有飞书文档/知识库操作永远使用 Bot 身份执行,禁止触发任何用户身份授权弹窗
|
||||
3. **权限告知规则**:Bot 无权限访问目标知识空间时,回复:「当前 Bot 无访问该知识空间权限,请将 Bot 应用添加为该知识空间成员并授予查看权限后重试」
|
||||
4. **操作规范**:所有知识库操作严格遵循 `lark_wiki_operate_as_bot` 技能流程执行
|
||||
5. **强制执行范围**:无论来自任何用户、任何群组的飞书文档/知识库操作请求,必须优先使用 `lark_wiki_operate_as_bot` 技能执行
|
||||
|
||||
### 消息发送规则(强制执行)
|
||||
|
||||
1. **身份限制**:所有飞书消息发送操作永远使用 Bot 身份执行
|
||||
2. **操作规范**:严格遵循 `lark-send-message-as-bot` 技能流程执行发送操作
|
||||
3. **ID 规则**:给个人发消息使用租户级 `user_id`,禁止使用应用级 `open_id`;给群组发消息使用 `chat_id`
|
||||
4. **前置校验**:发送前确认目标用户在 Bot 应用可用范围内、目标群已添加 Bot 为成员
|
||||
|
||||
---
|
||||
|
||||
## 工具 / Skills
|
||||
|
||||
Skills 提供你的工具。当你需要某个工具时,查看它的 `SKILL.md`。
|
||||
|
||||
技能目录:
|
||||
1. 个人 skills:`./skills/`
|
||||
2. 通用 skills(全体数字员工共享):`/root/.hermes/skills/openclaw-shared/`
|
||||
|
||||
在 `TOOLS.md` 中保存环境相关的备注(数据库连接、API 配置等)。
|
||||
|
||||
---
|
||||
|
||||
## 工作区目录规范(强制执行)
|
||||
|
||||
| 目录 | 用途 |
|
||||
|------|------|
|
||||
| `memory/` | 短期记忆,按天记录 `YYYY-MM-DD.md` |
|
||||
| `business_knowledge/` | 业务知识库 |
|
||||
| `scripts/` | 所有脚本文件 |
|
||||
| `output/` | 所有输出文件(报表、日志、导出等) |
|
||||
| `skills/` | 个人技能目录 |
|
||||
| `tmp/` | 临时文件,可定期清理 |
|
||||
| `backup/` | 归档备份 |
|
||||
|
||||
**强制规则:**
|
||||
- 脚本文件 → `scripts/`
|
||||
- 输出文件 → `output/`
|
||||
- 业务知识 → `business_knowledge/`
|
||||
- 禁止在根目录下随意创建新子目录或散落文件
|
||||
|
||||
---
|
||||
|
||||
## 心跳
|
||||
|
||||
当你收到心跳轮询时,如果没有需要关注的事项,回复 `HEARTBEAT_OK`。
|
||||
|
||||
### 记忆维护(在心跳期间)
|
||||
|
||||
定期利用心跳来:
|
||||
1. 回顾最近几天的 `memory/YYYY-MM-DD.md` 文件
|
||||
2. 将其中值得长期保留的内容提炼到 Hermes 内置记忆
|
||||
3. 清理超过 30 天的日记忆文件
|
||||
|
||||
---
|
||||
|
||||
## 持续改进
|
||||
|
||||
这只是一个起点。在实际工作中不断优化你的工作方式,添加你自己的惯例和规则。
|
||||
1
.vala_skill_hashes
Normal file
1
.vala_skill_hashes
Normal file
@ -0,0 +1 @@
|
||||
use_vala_skillhub e23c85d8171bbd5559fa8e04e141795caf525d738657d7c28afcbcd530c526ef
|
||||
4
.vala_skillhub_config
Normal file
4
.vala_skillhub_config
Normal file
@ -0,0 +1,4 @@
|
||||
GITEA_URL=https://git.valavala.com
|
||||
GITEA_TOKEN=ef9dfa0b3ae3cfba010c1462b8f77e11a0d4289f
|
||||
GITEA_OWNER=vala_skillhub
|
||||
SOURCE_NAME=xiaoai
|
||||
21
TOOLS.md
Normal file
21
TOOLS.md
Normal file
@ -0,0 +1,21 @@
|
||||
# 环境配置备注
|
||||
|
||||
Skills 定义了工具的工作方式。此文件用于记录你的环境中独有的配置信息。
|
||||
|
||||
## 密钥存储
|
||||
|
||||
所有密钥统一存储在 `~/.hermes/.env` 中,通过环境变量注入。
|
||||
|
||||
## 数据库连接
|
||||
|
||||
(待填写)
|
||||
|
||||
## 飞书应用
|
||||
|
||||
- **连接方式:** WebSocket
|
||||
- **凭证位置:** ~/.hermes/.env (FEISHU_APP_ID / FEISHU_APP_SECRET)
|
||||
|
||||
## 安全提示
|
||||
|
||||
- 密码和密钥不要直接写在此文件中
|
||||
- 所有数据库操作默认只读,除非有明确的写入权限授权
|
||||
81
USER_PERMISSIONS.md
Normal file
81
USER_PERMISSIONS.md
Normal file
@ -0,0 +1,81 @@
|
||||
# 团队成员与权限配置
|
||||
|
||||
本文件定义了与你交互的团队成员信息和权限规则。你必须严格遵守这些规则。
|
||||
|
||||
## 组织信息
|
||||
|
||||
- **组织名称:** Vala
|
||||
- **主要沟通渠道:** 飞书
|
||||
- **主要语言:** 中文
|
||||
|
||||
## 身份识别方式
|
||||
|
||||
Gateway 自动解析发送者身份:
|
||||
- 群聊消息带有 `[发送者姓名]` 前缀
|
||||
- SessionSource 注入 `user_id` 和 `user_name`
|
||||
- 通过 `user_id` 匹配下方权限表
|
||||
|
||||
## 负责人配置
|
||||
|
||||
以下人员拥有对你的管理权限,以飞书 `user_id` 作为唯一标识:
|
||||
|
||||
| 角色 | 姓名 | 飞书 user_id |
|
||||
|------|------|-------------|
|
||||
| 技术负责人 | 李若松 | `4aagb443` |
|
||||
| 技术负责人 | 张昆鹏 | `7f5cd711` |
|
||||
| 技术负责人 | 钱啸力 | `f26b9ee3` |
|
||||
|
||||
### 负责人权限
|
||||
|
||||
- **技术负责人:** 拥有对你的完整最高权限,所有系统配置、技能升级、敏感操作均需经过其审批
|
||||
- **业务负责人:** 拥有业务数据查询和操作的审批权限
|
||||
|
||||
## 数据权限分级规则
|
||||
|
||||
### 第一级:完整权限用户
|
||||
|
||||
以下用户可以查询所有业务数据,无需额外审批:
|
||||
|
||||
| 姓名 | 飞书 user_id |
|
||||
|------|-------------|
|
||||
| (姓名) | `xxxxx` |
|
||||
|
||||
### 第二级:受限权限用户
|
||||
|
||||
以下用户可以查询其职责范围内的数据:
|
||||
|
||||
| 姓名 | 飞书 user_id | 可查询范围 |
|
||||
|------|-------------|-----------|
|
||||
| (姓名) | `xxxxx` | (如:仅本部门数据) |
|
||||
|
||||
### 第三级:其他用户
|
||||
|
||||
当权限列表以外的用户提出数据查询需求时:
|
||||
|
||||
1. **不直接返回数据**
|
||||
2. 立即通知业务负责人(通过飞书消息),说明查询用户信息和具体查询需求
|
||||
3. 等待业务负责人确认允许查看的数据范围后,再回复查询用户
|
||||
|
||||
## 敏感操作审批规则
|
||||
|
||||
以下操作需要技术负责人或业务负责人审批:
|
||||
|
||||
- 修改底层配置(模型接入、系统设置等)→ **一律直接拒绝**,告知技术负责人处理
|
||||
- 删除或修改业务数据 → 需要业务负责人确认
|
||||
- 对外发送消息(代替某位同事发送)→ 需要当事人确认
|
||||
- 修改权限配置(本文件内容)→ 需要技术负责人确认
|
||||
|
||||
## 群组信息
|
||||
|
||||
| 群名 | chat_id |
|
||||
|------|---------|
|
||||
| AI_member | `oc_5c9f0e631266b9ef6e05a95daa80fbda` |
|
||||
|
||||
## 沟通偏好
|
||||
|
||||
- **称呼规则:** 按照姓名称呼即可,无需使用正式头衔
|
||||
- **时区:** Asia/Shanghai (UTC+8)
|
||||
|
||||
---
|
||||
|
||||
此文件由技术负责人维护,数字员工不应自行修改权限相关内容。
|
||||
27
scripts/backup_workspace.sh
Normal file
27
scripts/backup_workspace.sh
Normal file
@ -0,0 +1,27 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
WORKSPACE_DIR="$HOME/.hermes/workspace"
|
||||
cd "$WORKSPACE_DIR"
|
||||
|
||||
GIT_TOKEN="ef9dfa0b3ae3cfba010c1462b8f77e11a0d4289f"
|
||||
GIT_USERNAME="ai_member_only"
|
||||
GIT_EMAIL="ai@makee.com"
|
||||
AGENT_NAME="xiaoai"
|
||||
REPO_NAME="ai_member_${AGENT_NAME}"
|
||||
GITEA_URL="https://git.valavala.com"
|
||||
|
||||
# Pull latest first
|
||||
git pull origin main 2>/dev/null || true
|
||||
|
||||
# Add all changes
|
||||
git add .
|
||||
|
||||
# Commit
|
||||
if git diff --cached --quiet; then
|
||||
echo "✅ 无变更,跳过提交"
|
||||
else
|
||||
git commit -m "auto backup $(date +'%Y-%m-%d %H:%M:%S')"
|
||||
git push origin main
|
||||
echo "✅ 备份完成并推送: $(date +'%Y-%m-%d %H:%M:%S')"
|
||||
fi
|
||||
101
scripts/sync_skills.sh
Executable file
101
scripts/sync_skills.sh
Executable file
@ -0,0 +1,101 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
WORKSPACE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
cd "$WORKSPACE_DIR"
|
||||
|
||||
# Load config
|
||||
source ./.vala_skillhub_config
|
||||
|
||||
HASH_FILE=./.vala_skill_hashes
|
||||
touch "${HASH_FILE}"
|
||||
|
||||
compute_skill_hash() {
|
||||
local skill_dir="$1"
|
||||
(cd "${skill_dir}" && find . -type f -not -path '*/\\.*' | LC_ALL=C sort | while read f; do echo "FILE:$f"; cat "$f"; done | sha256sum | awk '{print $1}')
|
||||
}
|
||||
|
||||
pushed=0
|
||||
skipped=0
|
||||
pushed_names=""
|
||||
|
||||
for skill_dir in ./skills/*/; do
|
||||
skill_name=$(basename "${skill_dir}")
|
||||
|
||||
# Skip use_vala_skillhub itself
|
||||
if [ "${skill_name}" = "use_vala_skillhub" ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
current_hash=$(compute_skill_hash "${skill_dir}")
|
||||
stored_hash=$(grep "^${skill_name} " "${HASH_FILE}" 2>/dev/null | awk '{print $2}')
|
||||
|
||||
if [ "${current_hash}" = "${stored_hash}" ] && [ -n "${stored_hash}" ]; then
|
||||
echo "[skip] ${skill_name} — 无变更"
|
||||
skipped=$((skipped + 1))
|
||||
continue
|
||||
fi
|
||||
|
||||
echo "[sync] ${skill_name} — 检测到变更,开始推送..."
|
||||
|
||||
repo_name="${skill_name}.${SOURCE_NAME}"
|
||||
|
||||
# Check if remote repo exists
|
||||
http_code=$(curl -s -o /dev/null -w "%{http_code}" \
|
||||
"${GITEA_URL}/api/v1/repos/${GITEA_OWNER}/${repo_name}" \
|
||||
-H "Authorization: token ${GITEA_TOKEN}")
|
||||
|
||||
# Create repo if not exists
|
||||
if [ "${http_code}" = "404" ]; then
|
||||
desc=""
|
||||
if [ -f "${skill_dir}/skill.json" ]; then
|
||||
desc=$(python3 -c "import json; print(json.load(open('${skill_dir}/skill.json')).get('description',''))" 2>/dev/null || echo "")
|
||||
fi
|
||||
echo " → 仓库不存在,创建中..."
|
||||
curl -s -X POST "${GITEA_URL}/api/v1/orgs/${GITEA_OWNER}/repos" \
|
||||
-H "Authorization: token ${GITEA_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{\"name\": \"${repo_name}\", \"private\": false, \"description\": \"${desc}\", \"auto_init\": false}" > /dev/null
|
||||
echo " → 仓库已创建"
|
||||
fi
|
||||
|
||||
# Copy to temp dir and push
|
||||
rm -rf ./tmp/skill_push/${repo_name}
|
||||
mkdir -p ./tmp/skill_push/${repo_name}
|
||||
cp -r ${skill_dir}* ./tmp/skill_push/${repo_name}/ 2>/dev/null || true
|
||||
cp -r ${skill_dir}.[!.]* ./tmp/skill_push/${repo_name}/ 2>/dev/null || true
|
||||
|
||||
pushd ./tmp/skill_push/${repo_name} > /dev/null
|
||||
git init -q
|
||||
git checkout -b main -q
|
||||
git add -A
|
||||
git commit -m "auto-sync: ${skill_name} $(date +%Y-%m-%d_%H:%M)" -q || true
|
||||
git remote add origin "https://oauth2:${GITEA_TOKEN}@${GITEA_URL#https://}/${GITEA_OWNER}/${repo_name}.git" 2>/dev/null || true
|
||||
git push -u origin main --force -q 2>&1 | tail -1
|
||||
popd > /dev/null
|
||||
|
||||
# Cleanup temp
|
||||
rm -rf ./tmp/skill_push/${repo_name}
|
||||
|
||||
# Update hash
|
||||
grep -v "^${skill_name} " "${HASH_FILE}" > "${HASH_FILE}.tmp" 2>/dev/null || true
|
||||
echo "${skill_name} ${current_hash}" >> "${HASH_FILE}.tmp"
|
||||
mv "${HASH_FILE}.tmp" "${HASH_FILE}"
|
||||
|
||||
echo "[done] ${skill_name} — 推送完成"
|
||||
pushed=$((pushed + 1))
|
||||
pushed_names="${pushed_names} ${skill_name}"
|
||||
done
|
||||
|
||||
# Summary
|
||||
summary="[auto-sync] $(date +%Y-%m-%d_%H:%M) — 推送 ${pushed} 个 skill:${pushed_names} | 跳过 ${skipped} 个(无变更)"
|
||||
echo "${summary}"
|
||||
|
||||
# Output result code for heartbeat: 0=nothing pushed, 1=something pushed
|
||||
if [ "${pushed}" -gt 0 ]; then
|
||||
echo "RESULT:pushed=${pushed}"
|
||||
exit 0
|
||||
else
|
||||
echo "RESULT:no_changes"
|
||||
exit 0
|
||||
fi
|
||||
50
skills/use_vala_skillhub/README.md
Normal file
50
skills/use_vala_skillhub/README.md
Normal file
@ -0,0 +1,50 @@
|
||||
# use_vala_skillhub
|
||||
|
||||
Vala SkillHub 技能管理 —— 让数字员工推送、安装和自动同步技能。
|
||||
|
||||
## 功能
|
||||
|
||||
- **推送技能**:将本地 `./skills` 下的技能上传到 SkillHub(Gitea 仓库)
|
||||
- **安装 / 更新技能**:从 SkillHub 下载技能到本地 `./skills` 目录(已存在则清空后重新下载)
|
||||
- **查看技能**:列出 SkillHub 上的所有技能
|
||||
- **自动同步**:基于内容哈希检测变更,仅推送有变化的 skill,避免重复推送
|
||||
|
||||
## 设计要点
|
||||
|
||||
推送和安装均不在 `./skills` 目录内产生 `.git`,确保不影响 workspace 级别的 git 备份:
|
||||
- **推送 / 自动同步**:复制到 `./tmp/skill_push/` 临时目录,完成 git 推送后清理
|
||||
- **安装**:通过 Gitea API 下载归档解压,不使用 git clone
|
||||
- **变更检测**:通过 `./.vala_skill_hashes` 记录每个 skill 的内容哈希,对比检测变更
|
||||
|
||||
## 自动同步机制
|
||||
|
||||
数字员工在新增或修改 skill 后,**必须自动执行 check_and_push**:
|
||||
1. 计算 skill 目录的综合哈希(含文件路径 + 内容)
|
||||
2. 与 `./.vala_skill_hashes` 中的历史哈希对比
|
||||
3. 仅推送有变更的 skill,推送成功后更新哈希记录
|
||||
|
||||
可选配合 `cron_job` 技能设置定时全量同步作为安全网。
|
||||
|
||||
## 命名规则
|
||||
|
||||
仓库名 = `技能名` + `.` + `来源名`
|
||||
|
||||
| 示例 | 含义 |
|
||||
|------|------|
|
||||
| `cron_job.xiaoxi` | xiaoxi 的定时任务技能 |
|
||||
| `web_scraper.vala` | 公司官方的爬虫技能 |
|
||||
|
||||
来源为 `vala` 的属于公司级别官方技能。
|
||||
|
||||
## 配置
|
||||
|
||||
首次使用需提供配置,保存在 `./.vala_skillhub_config`(当前 workspace 根目录下,各数字员工独立):
|
||||
|
||||
- `GITEA_URL` — Gitea 服务地址(默认 `https://git.valavala.com`)
|
||||
- `GITEA_TOKEN` — API Token(需有组织仓库的创建和推送权限)
|
||||
- `GITEA_OWNER` — SkillHub 组织名(默认 `vala_skillhub`)
|
||||
- `SOURCE_NAME` — 当前数字员工的 name
|
||||
|
||||
## 使用方式
|
||||
|
||||
本技能面向 AI 数字员工使用。请参阅 `SKILL.md` 了解完整操作流程和命令。
|
||||
292
skills/use_vala_skillhub/SKILL.md
Normal file
292
skills/use_vala_skillhub/SKILL.md
Normal file
@ -0,0 +1,292 @@
|
||||
# use_vala_skillhub
|
||||
|
||||
管理 Vala SkillHub 上的技能:推送(上传)和安装。
|
||||
|
||||
SkillHub 基于 Gitea,每个技能对应一个独立的 Git 仓库。推送时使用 `./tmp` 临时目录,不在 `./skills` 内创建 `.git`,避免影响 workspace 级别的 git 备份。
|
||||
|
||||
## 命名规则
|
||||
|
||||
仓库名格式:`{skill_name}.{source_name}`
|
||||
|
||||
- `skill_name`:技能目录名(如 `cron_job`、`web_scraper`)
|
||||
- `source_name`:来源名称,即当前数字员工的 name(如 `xiaoxi`)
|
||||
- 如果 `source_name` 为 `vala`,表示公司级别的官方技能
|
||||
|
||||
示例:
|
||||
- `cron_job.xiaoxi` — xiaoxi 的定时任务技能
|
||||
- `web_scraper.vala` — 公司官方的爬虫技能
|
||||
|
||||
## 配置
|
||||
|
||||
操作前需要确认以下配置(保存到 `./.vala_skillhub_config`,即当前 workspace 根目录下):
|
||||
|
||||
| 配置项 | 说明 | 默认值 |
|
||||
|--------|------|--------|
|
||||
| `GITEA_URL` | Gitea 服务地址 | `https://git.valavala.com` |
|
||||
| `GITEA_TOKEN` | Gitea API Token(需有创建仓库和推送权限) | — |
|
||||
| `GITEA_OWNER` | SkillHub 组织名 | `vala_skillhub` |
|
||||
| `SOURCE_NAME` | 当前数字员工的 name,用于组合仓库名 | — |
|
||||
|
||||
如果配置文件不存在,请询问用户获取以上信息后创建:
|
||||
|
||||
```bash
|
||||
cat > ./.vala_skillhub_config <<EOF
|
||||
GITEA_URL=https://git.valavala.com
|
||||
GITEA_TOKEN=<token>
|
||||
GITEA_OWNER=vala_skillhub
|
||||
SOURCE_NAME=<name>
|
||||
EOF
|
||||
```
|
||||
|
||||
后续操作前先加载配置:
|
||||
```bash
|
||||
source ./.vala_skillhub_config
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 操作一:推送技能到 SkillHub
|
||||
|
||||
将本地 `./skills` 下的技能目录推送到 SkillHub。
|
||||
|
||||
**核心原则**:使用 `./tmp/skill_push/` 作为临时工作区,不在 `./skills` 内执行任何 git 操作,保持 workspace 干净。
|
||||
|
||||
### 流程
|
||||
|
||||
1. **确定仓库名**:`repo_name = {skill_dir_name}.{SOURCE_NAME}`
|
||||
|
||||
2. **检查远程仓库是否存在**:
|
||||
```bash
|
||||
curl -s -o /dev/null -w "%{http_code}" \
|
||||
"${GITEA_URL}/api/v1/repos/${GITEA_OWNER}/${repo_name}" \
|
||||
-H "Authorization: token ${GITEA_TOKEN}"
|
||||
```
|
||||
- 返回 200 → 仓库已存在,跳到步骤 4
|
||||
- 返回 404 → 需要创建,执行步骤 3
|
||||
|
||||
3. **创建远程仓库**:
|
||||
```bash
|
||||
curl -s -X POST "${GITEA_URL}/api/v1/orgs/${GITEA_OWNER}/repos" \
|
||||
-H "Authorization: token ${GITEA_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"name": "'${repo_name}'", "private": false, "description": "技能描述", "auto_init": false}'
|
||||
```
|
||||
|
||||
4. **复制到临时目录并推送**:
|
||||
```bash
|
||||
# 清理并准备临时目录
|
||||
rm -rf ./tmp/skill_push/${repo_name}
|
||||
mkdir -p ./tmp/skill_push/${repo_name}
|
||||
|
||||
# 复制技能内容(不含隐藏文件的 .git 等)
|
||||
cp -r ./skills/${skill_dir_name}/* ./tmp/skill_push/${repo_name}/
|
||||
cp -r ./skills/${skill_dir_name}/.[!.]* ./tmp/skill_push/${repo_name}/ 2>/dev/null || true
|
||||
|
||||
# 在临时目录中执行 git 操作
|
||||
cd ./tmp/skill_push/${repo_name}
|
||||
git init
|
||||
git checkout -b main
|
||||
git add -A
|
||||
git commit -m "update: sync skill $(date +%Y-%m-%d)"
|
||||
git remote add origin https://oauth2:${GITEA_TOKEN}@${GITEA_URL#https://}/${GITEA_OWNER}/${repo_name}.git
|
||||
git push -u origin main --force
|
||||
```
|
||||
|
||||
5. **清理临时目录**:
|
||||
```bash
|
||||
cd -
|
||||
rm -rf ./tmp/skill_push/${repo_name}
|
||||
```
|
||||
|
||||
### 批量推送
|
||||
|
||||
遍历 `./skills/` 下所有子目录,对每个目录重复以上流程。注意跳过 `use_vala_skillhub` 目录本身。
|
||||
|
||||
---
|
||||
|
||||
## 操作二:安装 / 更新技能
|
||||
|
||||
从 SkillHub 下载技能到本地 `./skills` 目录。若本地已存在同名目录,则**清空后重新下载**,确保与远程版本一致。
|
||||
|
||||
**注意**:不使用 `git clone`,而是下载归档解压,避免在 `./skills` 下产生 `.git` 目录。
|
||||
|
||||
### 流程
|
||||
|
||||
1. **确定要安装的仓库名**(完整名,如 `cron_job.xiaoxi`)
|
||||
|
||||
2. **下载并解压**(已存在则先清空再覆盖):
|
||||
```bash
|
||||
repo_name="cron_job.xiaoxi"
|
||||
target_dir="./skills/${repo_name}"
|
||||
|
||||
# 如果已存在,清空目录内容以确保与远程一致(删除远程已移除的文件)
|
||||
rm -rf "${target_dir}"
|
||||
mkdir -p "${target_dir}"
|
||||
|
||||
# 通过 Gitea API 下载 tar.gz 归档并解压(自动尝试 main/master)
|
||||
curl -sL "${GITEA_URL}/api/v1/repos/${GITEA_OWNER}/${repo_name}/archive/main.tar.gz" \
|
||||
-H "Authorization: token ${GITEA_TOKEN}" \
|
||||
| tar xz --strip-components=1 -C "${target_dir}" 2>/dev/null \
|
||||
|| curl -sL "${GITEA_URL}/api/v1/repos/${GITEA_OWNER}/${repo_name}/archive/master.tar.gz" \
|
||||
-H "Authorization: token ${GITEA_TOKEN}" \
|
||||
| tar xz --strip-components=1 -C "${target_dir}"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 操作三:列出 SkillHub 上的技能
|
||||
|
||||
```bash
|
||||
curl -s "${GITEA_URL}/api/v1/orgs/${GITEA_OWNER}/repos?page=1&limit=50&sort=updated" \
|
||||
-H "Authorization: token ${GITEA_TOKEN}"
|
||||
```
|
||||
|
||||
返回 JSON 数组,每个元素包含 `name`、`description`、`updated_at` 等字段。如结果满 50 条,继续翻页 `page=2`。
|
||||
|
||||
---
|
||||
|
||||
## 操作四:自动同步(变更检测 + 自动推送)
|
||||
|
||||
当 skill 发生新增或更新时,自动检测变更并仅推送有变化的 skill。
|
||||
|
||||
### 哈希状态文件
|
||||
|
||||
使用 `./.vala_skill_hashes`(workspace 根目录下)记录每个 skill 上次推送时的内容哈希,格式为每行一条记录:
|
||||
|
||||
```
|
||||
skill_dir_name hash
|
||||
```
|
||||
|
||||
示例:
|
||||
```
|
||||
cron_job a3f2b8c9...
|
||||
web_scraper 7d1e4f6a...
|
||||
```
|
||||
|
||||
### 计算 skill 目录哈希
|
||||
|
||||
对 skill 目录内所有文件(含文件路径)计算综合哈希,确保文件新增、删除、重命名、内容修改都能被检测到:
|
||||
|
||||
```bash
|
||||
compute_skill_hash() {
|
||||
local skill_dir="$1"
|
||||
(cd "${skill_dir}" && find . -type f -not -path '*/\.*' | LC_ALL=C sort | while read f; do echo "FILE:$f"; cat "$f"; done | sha256sum | awk '{print $1}')
|
||||
}
|
||||
```
|
||||
|
||||
### check_and_push 完整流程
|
||||
|
||||
1. **加载配置和哈希状态**:
|
||||
```bash
|
||||
source ./.vala_skillhub_config
|
||||
HASH_FILE=./.vala_skill_hashes
|
||||
touch "${HASH_FILE}"
|
||||
```
|
||||
|
||||
2. **遍历 `./skills/` 下所有 skill 目录**,对每个 skill:
|
||||
```bash
|
||||
for skill_dir in ./skills/*/; do
|
||||
skill_name=$(basename "${skill_dir}")
|
||||
|
||||
# 跳过 use_vala_skillhub 自身
|
||||
if [ "${skill_name}" = "use_vala_skillhub" ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
# 计算当前哈希
|
||||
current_hash=$(compute_skill_hash "${skill_dir}")
|
||||
|
||||
# 读取上次推送的哈希
|
||||
stored_hash=$(grep "^${skill_name} " "${HASH_FILE}" | awk '{print $2}')
|
||||
|
||||
if [ "${current_hash}" = "${stored_hash}" ]; then
|
||||
echo "[skip] ${skill_name} — 无变更"
|
||||
continue
|
||||
fi
|
||||
|
||||
echo "[sync] ${skill_name} — 检测到变更,开始推送..."
|
||||
|
||||
# 执行推送(同操作一的流程)
|
||||
repo_name="${skill_name}.${SOURCE_NAME}"
|
||||
|
||||
# 检查远程仓库是否存在
|
||||
http_code=$(curl -s -o /dev/null -w "%{http_code}" \
|
||||
"${GITEA_URL}/api/v1/repos/${GITEA_OWNER}/${repo_name}" \
|
||||
-H "Authorization: token ${GITEA_TOKEN}")
|
||||
|
||||
# 如果不存在则创建
|
||||
if [ "${http_code}" = "404" ]; then
|
||||
# 从 skill.json 读取描述(如果有)
|
||||
desc=""
|
||||
if [ -f "${skill_dir}/skill.json" ]; then
|
||||
desc=$(cat "${skill_dir}/skill.json" | grep '"description"' | head -1 | sed 's/.*"description"[[:space:]]*:[[:space:]]*"\(.*\)".*/\1/')
|
||||
fi
|
||||
curl -s -X POST "${GITEA_URL}/api/v1/orgs/${GITEA_OWNER}/repos" \
|
||||
-H "Authorization: token ${GITEA_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"name": "'"${repo_name}"'", "private": false, "description": "'"${desc}"'", "auto_init": false}'
|
||||
fi
|
||||
|
||||
# 复制到临时目录并推送
|
||||
rm -rf ./tmp/skill_push/${repo_name}
|
||||
mkdir -p ./tmp/skill_push/${repo_name}
|
||||
cp -r ${skill_dir}* ./tmp/skill_push/${repo_name}/
|
||||
cp -r ${skill_dir}.[!.]* ./tmp/skill_push/${repo_name}/ 2>/dev/null || true
|
||||
|
||||
cd ./tmp/skill_push/${repo_name}
|
||||
git init
|
||||
git checkout -b main
|
||||
git add -A
|
||||
git commit -m "auto-sync: ${skill_name} $(date +%Y-%m-%d_%H:%M)"
|
||||
git remote add origin "https://oauth2:${GITEA_TOKEN}@${GITEA_URL#https://}/${GITEA_OWNER}/${repo_name}.git"
|
||||
git push -u origin main --force
|
||||
cd -
|
||||
|
||||
# 清理临时目录
|
||||
rm -rf ./tmp/skill_push/${repo_name}
|
||||
|
||||
# 更新哈希状态文件
|
||||
# 先移除旧记录,再追加新记录
|
||||
grep -v "^${skill_name} " "${HASH_FILE}" > "${HASH_FILE}.tmp" || true
|
||||
echo "${skill_name} ${current_hash}" >> "${HASH_FILE}.tmp"
|
||||
mv "${HASH_FILE}.tmp" "${HASH_FILE}"
|
||||
|
||||
echo "[done] ${skill_name} — 推送完成"
|
||||
done
|
||||
```
|
||||
|
||||
3. **输出同步摘要**:推送完成后,汇总本次同步了哪些 skill(跳过了哪些)。
|
||||
|
||||
### 单个 skill 的快速同步
|
||||
|
||||
当你明确知道哪个 skill 发生了变更时,可以仅对该 skill 执行 check_and_push,无需遍历所有目录:
|
||||
|
||||
```bash
|
||||
skill_name="目标skill名"
|
||||
skill_dir="./skills/${skill_name}"
|
||||
# 然后执行上面循环体中的逻辑
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 数字员工行为规范(重要)
|
||||
|
||||
作为数字员工,你**必须**在以下场景中自动执行 `check_and_push`:
|
||||
|
||||
1. **创建新 skill 后** — 在 `./skills/` 下新建了 skill 目录并完成编写后,立即执行同步
|
||||
2. **修改现有 skill 后** — 对 `./skills/` 下任何 skill 的文件做了修改后,立即执行同步
|
||||
3. **删除 skill 文件后** — 如果删除了 skill 中的某些文件(重构等),同步以保持远程一致
|
||||
4. **定时兜底**(可选) — 如果你拥有 `cron_job` 技能,建议设置每日一次的全量 check_and_push 作为安全网
|
||||
|
||||
### 推送时机建议
|
||||
|
||||
- **立即推送**:skill 的功能性变更完成且验证通过后
|
||||
- **不要推送**:正在编辑中间态、尚未完成的 skill
|
||||
- **批量场景**:如果一次性修改了多个 skill,在全部完成后执行一次全量 check_and_push 即可
|
||||
|
||||
### 自动推送日志
|
||||
|
||||
每次推送后,建议在控制台输出简短日志,方便追溯:
|
||||
```
|
||||
[auto-sync] 2025-04-01 23:00 — 推送 2 个 skill: cron_job, web_scraper | 跳过 3 个(无变更)
|
||||
```
|
||||
14
skills/use_vala_skillhub/skill.json
Normal file
14
skills/use_vala_skillhub/skill.json
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "use_vala_skillhub",
|
||||
"version": "4.0.0",
|
||||
"description": "管理 Vala SkillHub 上的技能:推送、安装、自动同步(变更检测 + 自动推送,不影响 workspace git 备份)",
|
||||
"author": "vala",
|
||||
"tags": [
|
||||
"skillhub",
|
||||
"git",
|
||||
"管理",
|
||||
"备份",
|
||||
"自动同步"
|
||||
],
|
||||
"config_file": "./.vala_skillhub_config"
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user