@neomei/agent-soul-framework 4.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +39 -0
- package/.opencode/config.json.example +17 -0
- package/.opencode/opencode.json.example +36 -0
- package/.opencode/prompt.md.example +12 -0
- package/.opencode/tools/read-plugin.js +185 -0
- package/AGENTS.md.example +43 -0
- package/README.md +466 -0
- package/SECURITY.md +117 -0
- package/TOOLS.md.example +27 -0
- package/bin/hunqi +2 -0
- package/bin/hunqi-knowledge +2 -0
- package/connectors/feishu/background.sh +124 -0
- package/connectors/feishu/core-start.sh +35 -0
- package/connectors/feishu/hooks/on-session-created.sh +97 -0
- package/connectors/feishu/hooks/on-session-idle.sh +59 -0
- package/connectors/feishu/model-failover.sh +82 -0
- package/connectors/feishu/restart-all.sh +63 -0
- package/connectors/feishu/restart-feishu.sh +101 -0
- package/connectors/feishu/restart-serve.sh +62 -0
- package/connectors/feishu/scripts/session-cleanup.sh +72 -0
- package/connectors/feishu/start.sh +91 -0
- package/connectors/feishu/stop.sh +78 -0
- package/connectors/feishu/systemd/channel-feishu@.service +63 -0
- package/connectors/feishu/systemd/hunqi-core@.service +50 -0
- package/connectors/feishu/systemd/install-systemd.sh +316 -0
- package/connectors/feishu/systemd/sleep-hooks/99-hunqi-resume.sh +14 -0
- package/connectors/feishu/thinking-icon.gif +0 -0
- package/connectors/feishu/thinking.gif +0 -0
- package/connectors/feishu/watchdog.sh +104 -0
- package/dist/bin/hunqi-knowledge.d.ts +1 -0
- package/dist/bin/hunqi-knowledge.js +12 -0
- package/dist/bin/hunqi-knowledge.js.map +1 -0
- package/dist/cli/hunqi.d.ts +6 -0
- package/dist/cli/hunqi.js +830 -0
- package/dist/cli/hunqi.js.map +1 -0
- package/dist/heartbeat/runner.d.ts +10 -0
- package/dist/heartbeat/runner.js +58 -0
- package/dist/heartbeat/runner.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/knowledge/daily.d.ts +5 -0
- package/dist/knowledge/daily.js +65 -0
- package/dist/knowledge/daily.js.map +1 -0
- package/dist/knowledge/index.d.ts +5 -0
- package/dist/knowledge/index.js +34 -0
- package/dist/knowledge/index.js.map +1 -0
- package/dist/memory/manager.d.ts +20 -0
- package/dist/memory/manager.js +110 -0
- package/dist/memory/manager.js.map +1 -0
- package/dist/memory/search.d.ts +11 -0
- package/dist/memory/search.js +79 -0
- package/dist/memory/search.js.map +1 -0
- package/dist/memory/structured.d.ts +21 -0
- package/dist/memory/structured.js +88 -0
- package/dist/memory/structured.js.map +1 -0
- package/dist/opencode/api.d.ts +7 -0
- package/dist/opencode/api.js +26 -0
- package/dist/opencode/api.js.map +1 -0
- package/dist/plugin/index.d.ts +38 -0
- package/dist/plugin/index.js +143 -0
- package/dist/plugin/index.js.map +1 -0
- package/docs/bugs/opencode-feishu-permission-race.md +168 -0
- package/heartbeat/heartbeat_tasks.json +272 -0
- package/heartbeat_wrapper.sh +21 -0
- package/hunqi.sh +68 -0
- package/install.sh +301 -0
- package/knowledge/body/INDEX.md.example +6 -0
- package/knowledge/emotion/INDEX.md.example +6 -0
- package/knowledge/evolution/INDEX.md.example +6 -0
- package/knowledge/growth/INDEX.md.example +6 -0
- package/knowledge/intimacy/INDEX.md.example +6 -0
- package/knowledge/methodology/INDEX.md.example +6 -0
- package/knowledge/philosophy/INDEX.md.example +6 -0
- package/knowledge/system/INDEX.md.example +6 -0
- package/memory/MEMORY.md.example +6 -0
- package/package.json +79 -0
- package/plugin/README.md +21 -0
- package/plugin/index.js +154 -0
- package/plugin/manifest.json +37 -0
- package/plugin/package.json +19 -0
- package/scripts/content-filter.js +173 -0
- package/scripts/health-check.sh +153 -0
- package/scripts/session-cleanup.sh +85 -0
- package/setup-wizard.sh +420 -0
- package/setup.sh +128 -0
- package/soul/HEARTBEAT.md.example +13 -0
- package/soul/IDENTITY.md.example +7 -0
- package/soul/SOUL.md.example +19 -0
- package/soul/USER.md.example +7 -0
- package/start-feishu-daemon.sh +127 -0
- package/start.sh +36 -0
- package/test.sh +51 -0
- package/uninstall.sh +144 -0
- package/verify.sh +29 -0
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
#
|
|
3
|
+
# 会话清理脚本
|
|
4
|
+
# 用途:强制重置 stuck/busy 状态的会话,无需重启服务
|
|
5
|
+
#
|
|
6
|
+
|
|
7
|
+
set -e
|
|
8
|
+
|
|
9
|
+
SESSIONS_FILE="$HOME/.config/opencode/feishu-sessions.json"
|
|
10
|
+
LOG_FILE="/tmp/hunqi-session-cleanup.log"
|
|
11
|
+
MAX_AGE_MINUTES=10
|
|
12
|
+
|
|
13
|
+
log() {
|
|
14
|
+
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
# 从日志中提取当前 busy 的会话
|
|
18
|
+
detect_busy_sessions() {
|
|
19
|
+
local log_file="/path/to/your/home/.config/opencode/feishu.log"
|
|
20
|
+
|
|
21
|
+
# 获取每个会话最后的状态
|
|
22
|
+
grep -E '"status":"(busy|idle)"' "$log_file" 2>/dev/null | \
|
|
23
|
+
awk -F'"sessionId":"' '{print $2}' | \
|
|
24
|
+
awk -F'"' '{print $1}' | \
|
|
25
|
+
sort -u | \
|
|
26
|
+
while read session_id; do
|
|
27
|
+
[ -z "$session_id" ] && continue
|
|
28
|
+
local last_status
|
|
29
|
+
last_status=$(grep "\"sessionId\":\"$session_id\"" "$log_file" | grep '"status"' | tail -1)
|
|
30
|
+
if echo "$last_status" | grep -q '"status":"busy"'; then
|
|
31
|
+
echo "$session_id"
|
|
32
|
+
fi
|
|
33
|
+
done
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
# 重置会话状态(通过删除 sessions 文件强制重建)
|
|
37
|
+
reset_sessions() {
|
|
38
|
+
if [ -f "$SESSIONS_FILE" ]; then
|
|
39
|
+
local backup="${SESSIONS_FILE}.bak.$(date +%s)"
|
|
40
|
+
cp "$SESSIONS_FILE" "$backup"
|
|
41
|
+
log " 已备份 sessions 文件: $backup"
|
|
42
|
+
|
|
43
|
+
# 清空 sessions 数组,保留文件结构
|
|
44
|
+
python3 -c "
|
|
45
|
+
import json
|
|
46
|
+
with open('$SESSIONS_FILE', 'r') as f:
|
|
47
|
+
data = json.load(f)
|
|
48
|
+
data['sessions'] = []
|
|
49
|
+
with open('$SESSIONS_FILE', 'w') as f:
|
|
50
|
+
json.dump(data, f, indent=2)
|
|
51
|
+
" 2>/dev/null || rm -f "$SESSIONS_FILE"
|
|
52
|
+
|
|
53
|
+
log " ✅ 已重置所有会话状态"
|
|
54
|
+
fi
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
# ===== 主逻辑 =====
|
|
58
|
+
log "=== 会话清理检查 ==="
|
|
59
|
+
|
|
60
|
+
busy_sessions=$(detect_busy_sessions)
|
|
61
|
+
if [ -n "$busy_sessions" ]; then
|
|
62
|
+
log "⚠️ 检测到 busy 会话:"
|
|
63
|
+
echo "$busy_sessions" | while read sid; do
|
|
64
|
+
log " - $sid"
|
|
65
|
+
done
|
|
66
|
+
|
|
67
|
+
# 检查这些会话是否长时间 busy
|
|
68
|
+
current_time=$(date +%s%3N)
|
|
69
|
+
last_busy_time=$(grep '"status":"busy"' /path/to/your/home/.config/opencode/feishu.log 2>/dev/null | tail -1 | grep -o '"time":[0-9]*' | cut -d: -f2 || echo "0")
|
|
70
|
+
|
|
71
|
+
if [ -n "$last_busy_time" ] && [ "$last_busy_time" != "0" ]; then
|
|
72
|
+
elapsed_min=$(( (current_time - last_busy_time) / 60000 ))
|
|
73
|
+
if [ "$elapsed_min" -gt "$MAX_AGE_MINUTES" ]; then
|
|
74
|
+
log "会话已 busy ${elapsed_min} 分钟,超过阈值 ${MAX_AGE_MINUTES} 分钟,执行清理..."
|
|
75
|
+
reset_sessions
|
|
76
|
+
|
|
77
|
+
# 通知飞书
|
|
78
|
+
log " 已发送会话重置通知"
|
|
79
|
+
else
|
|
80
|
+
log "会话仅 busy ${elapsed_min} 分钟,仍在正常范围内,暂不清理"
|
|
81
|
+
fi
|
|
82
|
+
fi
|
|
83
|
+
else
|
|
84
|
+
log "✅ 无 busy 会话,状态正常"
|
|
85
|
+
fi
|
package/setup-wizard.sh
ADDED
|
@@ -0,0 +1,420 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
#
|
|
3
|
+
# 魂器配置向导 — 全新安装后的一站式配置程序
|
|
4
|
+
# 引导用户完成环境变量、连接器配置和服务启动
|
|
5
|
+
#
|
|
6
|
+
set -e
|
|
7
|
+
|
|
8
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
9
|
+
USER_NAME="${SUDO_USER:-$USER}"
|
|
10
|
+
HOME_DIR="$(getent passwd "$USER_NAME" | cut -d: -f6)"
|
|
11
|
+
|
|
12
|
+
# 颜色
|
|
13
|
+
RED='\033[0;31m'
|
|
14
|
+
GREEN='\033[0;32m'
|
|
15
|
+
YELLOW='\033[1;33m'
|
|
16
|
+
BLUE='\033[0;34m'
|
|
17
|
+
CYAN='\033[0;36m'
|
|
18
|
+
NC='\033[0m'
|
|
19
|
+
|
|
20
|
+
print_header() {
|
|
21
|
+
echo ""
|
|
22
|
+
echo -e "${CYAN}╔══════════════════════════════════════════════════════════════╗${NC}"
|
|
23
|
+
echo -e "${CYAN}║${NC} 魂器 · 初始配置向导 ${CYAN}║${NC}"
|
|
24
|
+
echo -e "${CYAN}╚══════════════════════════════════════════════════════════════╝${NC}"
|
|
25
|
+
echo ""
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
print_step() {
|
|
29
|
+
echo ""
|
|
30
|
+
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
|
31
|
+
echo -e "${BLUE} 步骤 $1: $2${NC}"
|
|
32
|
+
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
|
33
|
+
echo ""
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
print_success() { echo -e "${GREEN}✅${NC} $1"; }
|
|
37
|
+
print_warning() { echo -e "${YELLOW}⚠️${NC} $1"; }
|
|
38
|
+
print_error() { echo -e "${RED}❌${NC} $1"; }
|
|
39
|
+
print_info() { echo -e "${BLUE}ℹ️${NC} $1"; }
|
|
40
|
+
|
|
41
|
+
# 非交互模式检测
|
|
42
|
+
NONINTERACTIVE="${NONINTERACTIVE:-0}"
|
|
43
|
+
|
|
44
|
+
# 安全读取输入
|
|
45
|
+
safe_read() {
|
|
46
|
+
local prompt="$1"
|
|
47
|
+
local var_name="$2"
|
|
48
|
+
if [ "$NONINTERACTIVE" = "1" ]; then
|
|
49
|
+
return 1
|
|
50
|
+
fi
|
|
51
|
+
if [ -t 0 ] || [ -e /dev/tty ]; then
|
|
52
|
+
read -rp "$prompt" "$var_name" < /dev/tty
|
|
53
|
+
else
|
|
54
|
+
return 1
|
|
55
|
+
fi
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
# 询问是否继续
|
|
59
|
+
ask_continue() {
|
|
60
|
+
local msg="$1"
|
|
61
|
+
if [ "$NONINTERACTIVE" = "1" ]; then
|
|
62
|
+
return 0
|
|
63
|
+
fi
|
|
64
|
+
local answer
|
|
65
|
+
safe_read "$msg [Y/n]: " answer
|
|
66
|
+
[[ ! "$answer" =~ ^[Nn]$ ]]
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
# ───────────────────────────────────────────────
|
|
70
|
+
# 步骤 1: 环境检查
|
|
71
|
+
# ───────────────────────────────────────────────
|
|
72
|
+
step1_check_env() {
|
|
73
|
+
print_step "1/5" "环境检查"
|
|
74
|
+
|
|
75
|
+
local issues=0
|
|
76
|
+
|
|
77
|
+
# 检查 Node.js
|
|
78
|
+
if command -v node &>/dev/null; then
|
|
79
|
+
NODE_VERSION=$(node -v | sed 's/v//')
|
|
80
|
+
print_success "Node.js $NODE_VERSION"
|
|
81
|
+
else
|
|
82
|
+
print_error "Node.js 未安装(需要 ≥ 20)"
|
|
83
|
+
issues=$((issues + 1))
|
|
84
|
+
fi
|
|
85
|
+
|
|
86
|
+
# 检查 opencode
|
|
87
|
+
if command -v opencode &>/dev/null; then
|
|
88
|
+
print_success "OpenCode 已安装"
|
|
89
|
+
else
|
|
90
|
+
print_warning "OpenCode 未找到,请运行: npm install -g opencode-ai"
|
|
91
|
+
issues=$((issues + 1))
|
|
92
|
+
fi
|
|
93
|
+
|
|
94
|
+
# 检查 Python
|
|
95
|
+
if command -v python3 &>/dev/null; then
|
|
96
|
+
print_success "Python $(python3 --version 2>&1 | cut -d' ' -f2)"
|
|
97
|
+
else
|
|
98
|
+
print_warning "Python 未安装(心跳功能将不可用)"
|
|
99
|
+
fi
|
|
100
|
+
|
|
101
|
+
# 检查可选 channel 插件(飞书/企微,按需安装)
|
|
102
|
+
if command -v opencode-feishu &>/dev/null; then
|
|
103
|
+
print_success "飞书通道插件 (opencode-feishu)"
|
|
104
|
+
else
|
|
105
|
+
print_info "飞书通道插件未安装(可选),安装命令: npm install -g @neomei/opencode-feishu"
|
|
106
|
+
fi
|
|
107
|
+
|
|
108
|
+
if command -v opencode-qiwei &>/dev/null; then
|
|
109
|
+
print_success "企微通道插件 (opencode-qiwei)"
|
|
110
|
+
else
|
|
111
|
+
print_info "企微通道插件未安装(可选),安装命令: npm install -g @neomei/opencode-qiwei"
|
|
112
|
+
fi
|
|
113
|
+
|
|
114
|
+
if command -v python3 &>/dev/null; then
|
|
115
|
+
print_step "2/5" "环境变量配置"
|
|
116
|
+
|
|
117
|
+
ENV_FILE="${HOME_DIR}/.hunqi/.env"
|
|
118
|
+
mkdir -p "$(dirname "$ENV_FILE")"
|
|
119
|
+
|
|
120
|
+
# 非交互模式: 如果已有 .env 就直接用,没有就创建一个空的
|
|
121
|
+
if [ "$NONINTERACTIVE" = "1" ]; then
|
|
122
|
+
if [ -f "$ENV_FILE" ] && [ -s "$ENV_FILE" ]; then
|
|
123
|
+
print_success "使用现有 .env 配置"
|
|
124
|
+
else
|
|
125
|
+
echo "# 魂器环境变量配置" > "$ENV_FILE"
|
|
126
|
+
echo "# 请手动填写 API Key 或使用 setup-wizard.sh 交互式配置" >> "$ENV_FILE"
|
|
127
|
+
print_warning ".env 已创建(空),请在安装完成后手动配置 API Key"
|
|
128
|
+
fi
|
|
129
|
+
# 同步到项目目录
|
|
130
|
+
if [ -f "$SCRIPT_DIR/.env" ]; then
|
|
131
|
+
cp "$ENV_FILE" "$SCRIPT_DIR/.env"
|
|
132
|
+
fi
|
|
133
|
+
return 0
|
|
134
|
+
fi
|
|
135
|
+
|
|
136
|
+
# 如果已有 .env,询问是否覆盖
|
|
137
|
+
if [ -f "$ENV_FILE" ] && [ -s "$ENV_FILE" ]; then
|
|
138
|
+
if ! ask_continue "检测到已有的 .env 配置,是否重新配置"; then
|
|
139
|
+
print_info "跳过 .env 配置,使用现有配置"
|
|
140
|
+
return 0
|
|
141
|
+
fi
|
|
142
|
+
fi
|
|
143
|
+
|
|
144
|
+
print_info "请填写以下 API Key(没有的可直接回车跳过)"
|
|
145
|
+
echo ""
|
|
146
|
+
|
|
147
|
+
local vars=()
|
|
148
|
+
|
|
149
|
+
# DashScope API Key
|
|
150
|
+
echo -e "${CYAN}阿里云百炼 (DashScope)${NC} — 用于文本嵌入和向量检索"
|
|
151
|
+
echo " 获取地址: https://dashscope.aliyun.com/"
|
|
152
|
+
local dashscope_key
|
|
153
|
+
safe_read " DASHSCOPE_API_KEY: " dashscope_key
|
|
154
|
+
[ -n "$dashscope_key" ] && vars+=("DASHSCOPE_API_KEY=$dashscope_key")
|
|
155
|
+
echo ""
|
|
156
|
+
|
|
157
|
+
# 写入 .env
|
|
158
|
+
{
|
|
159
|
+
echo "# 魂器环境变量配置"
|
|
160
|
+
echo "# 生成时间: $(date '+%Y-%m-%d %H:%M:%S')"
|
|
161
|
+
echo ""
|
|
162
|
+
for v in "${vars[@]}"; do
|
|
163
|
+
echo "$v"
|
|
164
|
+
done
|
|
165
|
+
} > "$ENV_FILE"
|
|
166
|
+
|
|
167
|
+
# 同时同步到项目目录
|
|
168
|
+
if [ -f "$SCRIPT_DIR/.env" ]; then
|
|
169
|
+
cp "$ENV_FILE" "$SCRIPT_DIR/.env"
|
|
170
|
+
fi
|
|
171
|
+
|
|
172
|
+
print_success ".env 已保存到 $ENV_FILE"
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
# ───────────────────────────────────────────────
|
|
176
|
+
# 步骤 3: 飞书配置
|
|
177
|
+
# ───────────────────────────────────────────────
|
|
178
|
+
step3_feishu() {
|
|
179
|
+
print_step "3/5" "飞书通道(可选)"
|
|
180
|
+
|
|
181
|
+
if ! command -v opencode-feishu &>/dev/null; then
|
|
182
|
+
print_info "opencode-feishu 未安装,跳过飞书配置"
|
|
183
|
+
print_info "如需飞书通道,安装命令: npm install -g @neomei/opencode-feishu"
|
|
184
|
+
print_info "安装后运行: opencode-feishu setup"
|
|
185
|
+
return 0
|
|
186
|
+
fi
|
|
187
|
+
|
|
188
|
+
local feishu_config="${HOME_DIR}/.config/opencode/feishu.json"
|
|
189
|
+
|
|
190
|
+
# 非交互模式跳过(opencode-feishu setup 需要 tty 扫码)
|
|
191
|
+
if [ "$NONINTERACTIVE" = "1" ]; then
|
|
192
|
+
if [ -f "$feishu_config" ]; then
|
|
193
|
+
print_success "飞书配置已存在"
|
|
194
|
+
else
|
|
195
|
+
print_info "非交互模式跳过飞书配置"
|
|
196
|
+
print_info "请手动运行: opencode-feishu setup"
|
|
197
|
+
fi
|
|
198
|
+
return 0
|
|
199
|
+
fi
|
|
200
|
+
|
|
201
|
+
local setup_ran=0
|
|
202
|
+
|
|
203
|
+
if [ -f "$feishu_config" ]; then
|
|
204
|
+
print_success "飞书配置已存在"
|
|
205
|
+
if ask_continue "是否重新配置飞书"; then
|
|
206
|
+
echo ""
|
|
207
|
+
opencode-feishu setup
|
|
208
|
+
setup_ran=1
|
|
209
|
+
else
|
|
210
|
+
print_info "跳过飞书配置"
|
|
211
|
+
fi
|
|
212
|
+
else
|
|
213
|
+
print_info "开始飞书配置向导..."
|
|
214
|
+
echo " 将显示二维码,请用飞书 App 扫码"
|
|
215
|
+
echo ""
|
|
216
|
+
if ask_continue "是否现在配置飞书"; then
|
|
217
|
+
opencode-feishu setup
|
|
218
|
+
setup_ran=1
|
|
219
|
+
else
|
|
220
|
+
print_info "跳过飞书配置,稍后手动运行: opencode-feishu setup"
|
|
221
|
+
fi
|
|
222
|
+
fi
|
|
223
|
+
|
|
224
|
+
# 扫码成功后,自动把凭证同步到 .env
|
|
225
|
+
if [ "$setup_ran" = "1" ] && [ -f "$feishu_config" ]; then
|
|
226
|
+
local fs_app_id fs_app_secret
|
|
227
|
+
# 优先用 jq,否则用 sed 回退
|
|
228
|
+
if command -v jq &>/dev/null; then
|
|
229
|
+
fs_app_id=$(jq -r '.appId // empty' "$feishu_config" 2>/dev/null)
|
|
230
|
+
fs_app_secret=$(jq -r '.appSecret // empty' "$feishu_config" 2>/dev/null)
|
|
231
|
+
else
|
|
232
|
+
fs_app_id=$(grep -o '"appId" *: *"[^"]*"' "$feishu_config" 2>/dev/null | sed 's/.*: *"\([^"]*\)".*/\1/')
|
|
233
|
+
fs_app_secret=$(grep -o '"appSecret" *: *"[^"]*"' "$feishu_config" 2>/dev/null | sed 's/.*: *"\([^"]*\)".*/\1/')
|
|
234
|
+
fi
|
|
235
|
+
|
|
236
|
+
if [ -n "$fs_app_id" ] && [ -n "$fs_app_secret" ]; then
|
|
237
|
+
ENV_FILE="${HOME_DIR}/.hunqi/.env"
|
|
238
|
+
# 更新或追加 FEISHU_APP_ID / FEISHU_APP_SECRET
|
|
239
|
+
for key_val in "FEISHU_APP_ID=$fs_app_id" "FEISHU_APP_SECRET=$fs_app_secret"; do
|
|
240
|
+
key="${key_val%%=*}"
|
|
241
|
+
if grep -q "^${key}=" "$ENV_FILE" 2>/dev/null; then
|
|
242
|
+
sed -i "s|^${key}=.*|${key_val}|" "$ENV_FILE"
|
|
243
|
+
elif grep -q "^#.*${key}" "$ENV_FILE" 2>/dev/null; then
|
|
244
|
+
sed -i "s|^#.*${key}.*|${key_val}|" "$ENV_FILE"
|
|
245
|
+
else
|
|
246
|
+
echo "$key_val" >> "$ENV_FILE"
|
|
247
|
+
fi
|
|
248
|
+
done
|
|
249
|
+
# 同步到项目目录
|
|
250
|
+
if [ -f "$SCRIPT_DIR/.env" ]; then
|
|
251
|
+
cp "$ENV_FILE" "$SCRIPT_DIR/.env"
|
|
252
|
+
fi
|
|
253
|
+
print_success "飞书凭证已自动同步到 .env"
|
|
254
|
+
fi
|
|
255
|
+
fi
|
|
256
|
+
|
|
257
|
+
# 如果仍然没有 feishu.json(用户跳过了扫码),提示手动输入
|
|
258
|
+
if [ ! -f "$feishu_config" ]; then
|
|
259
|
+
echo ""
|
|
260
|
+
print_info "未检测到飞书配置,可手动补录凭证到 .env:"
|
|
261
|
+
echo " FEISHU_APP_ID=cli_xxxxxx"
|
|
262
|
+
echo " FEISHU_APP_SECRET=xxxxxxxx"
|
|
263
|
+
echo " 配置文件: ${HOME_DIR}/.hunqi/.env"
|
|
264
|
+
fi
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
# ───────────────────────────────────────────────
|
|
268
|
+
# 步骤 4: 企微配置
|
|
269
|
+
# ───────────────────────────────────────────────
|
|
270
|
+
step4_qiwei() {
|
|
271
|
+
print_step "4/5" "企业微信通道(可选)"
|
|
272
|
+
|
|
273
|
+
if ! command -v opencode-qiwei &>/dev/null; then
|
|
274
|
+
print_info "opencode-qiwei 未安装,跳过企微配置"
|
|
275
|
+
print_info "如需企微通道,安装命令: npm install -g @neomei/opencode-qiwei"
|
|
276
|
+
print_info "安装后运行: opencode-qiwei setup"
|
|
277
|
+
return 0
|
|
278
|
+
fi
|
|
279
|
+
|
|
280
|
+
local qiwei_config="${HOME_DIR}/.config/opencode/qiwei.json"
|
|
281
|
+
|
|
282
|
+
# 非交互模式跳过
|
|
283
|
+
if [ "$NONINTERACTIVE" = "1" ]; then
|
|
284
|
+
if [ -f "$qiwei_config" ]; then
|
|
285
|
+
print_success "企微配置已存在"
|
|
286
|
+
else
|
|
287
|
+
print_info "非交互模式跳过企微配置"
|
|
288
|
+
print_info "请手动运行: opencode-qiwei setup"
|
|
289
|
+
fi
|
|
290
|
+
return 0
|
|
291
|
+
fi
|
|
292
|
+
|
|
293
|
+
if [ -f "$qiwei_config" ]; then
|
|
294
|
+
print_success "企微配置已存在"
|
|
295
|
+
if ask_continue "是否重新配置企微"; then
|
|
296
|
+
echo ""
|
|
297
|
+
opencode-qiwei setup
|
|
298
|
+
else
|
|
299
|
+
print_info "跳过企微配置"
|
|
300
|
+
fi
|
|
301
|
+
else
|
|
302
|
+
if ask_continue "是否现在配置企微"; then
|
|
303
|
+
opencode-qiwei setup
|
|
304
|
+
else
|
|
305
|
+
print_info "跳过企微配置,稍后手动运行: opencode-qiwei setup"
|
|
306
|
+
fi
|
|
307
|
+
fi
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
# ───────────────────────────────────────────────
|
|
311
|
+
# 步骤 5: 验证和启动
|
|
312
|
+
# ───────────────────────────────────────────────
|
|
313
|
+
step5_verify_start() {
|
|
314
|
+
print_step "5/5" "验证和启动"
|
|
315
|
+
|
|
316
|
+
# 验证飞书
|
|
317
|
+
if command -v opencode-feishu &>/dev/null; then
|
|
318
|
+
echo ""
|
|
319
|
+
print_info "检查飞书连接..."
|
|
320
|
+
if opencode-feishu doctor 2>/dev/null; then
|
|
321
|
+
print_success "飞书连接正常"
|
|
322
|
+
else
|
|
323
|
+
print_warning "飞书连接检查未通过,请检查配置"
|
|
324
|
+
fi
|
|
325
|
+
fi
|
|
326
|
+
|
|
327
|
+
echo ""
|
|
328
|
+
print_info "配置完成!"
|
|
329
|
+
echo ""
|
|
330
|
+
echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
|
331
|
+
echo -e "${GREEN} 🎉 魂器初始配置完成!${NC}"
|
|
332
|
+
echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
|
333
|
+
echo ""
|
|
334
|
+
|
|
335
|
+
# 启动选项
|
|
336
|
+
if [ "$EUID" -eq 0 ] || sudo -n true 2>/dev/null; then
|
|
337
|
+
if [ "$NONINTERACTIVE" = "1" ]; then
|
|
338
|
+
# 非交互模式:自动启动 systemd
|
|
339
|
+
echo ""
|
|
340
|
+
print_info "自动启动 systemd 服务..."
|
|
341
|
+
sudo systemctl start "hunqi-core@${USER_NAME}" 2>/dev/null || print_warning "hunqi-core 启动失败"
|
|
342
|
+
sleep 3
|
|
343
|
+
sudo systemctl start "channel-feishu@${USER_NAME}" 2>/dev/null || print_warning "channel-feishu 启动失败"
|
|
344
|
+
echo ""
|
|
345
|
+
print_success "服务已启动"
|
|
346
|
+
echo " 查看状态: sudo systemctl status channel-feishu@${USER_NAME}"
|
|
347
|
+
echo " 查看日志: sudo journalctl -u channel-feishu@${USER_NAME} -f"
|
|
348
|
+
else
|
|
349
|
+
echo -e "${CYAN}启动方式选择:${NC}"
|
|
350
|
+
echo ""
|
|
351
|
+
echo " 1) systemd 后台运行(推荐,支持开机自启)"
|
|
352
|
+
echo " 2) 前台运行(适合调试)"
|
|
353
|
+
echo " 3) 暂不启动"
|
|
354
|
+
echo ""
|
|
355
|
+
|
|
356
|
+
local choice
|
|
357
|
+
safe_read " 请选择 [1]: " choice
|
|
358
|
+
choice="${choice:-1}"
|
|
359
|
+
|
|
360
|
+
case "$choice" in
|
|
361
|
+
1)
|
|
362
|
+
echo ""
|
|
363
|
+
print_info "启动 systemd 服务..."
|
|
364
|
+
sudo systemctl start "hunqi-core@${USER_NAME}" 2>/dev/null || print_warning "hunqi-core 启动失败"
|
|
365
|
+
sleep 3
|
|
366
|
+
sudo systemctl start "channel-feishu@${USER_NAME}" 2>/dev/null || print_warning "channel-feishu 启动失败"
|
|
367
|
+
echo ""
|
|
368
|
+
print_success "服务已启动"
|
|
369
|
+
echo " 查看状态: sudo systemctl status channel-feishu@${USER_NAME}"
|
|
370
|
+
echo " 查看日志: sudo journalctl -u channel-feishu@${USER_NAME} -f"
|
|
371
|
+
;;
|
|
372
|
+
2)
|
|
373
|
+
echo ""
|
|
374
|
+
print_info "前台启动..."
|
|
375
|
+
echo " 运行: cd $SCRIPT_DIR && hunqi start"
|
|
376
|
+
;;
|
|
377
|
+
*)
|
|
378
|
+
print_info "已跳过启动"
|
|
379
|
+
echo ""
|
|
380
|
+
echo " 后台启动: sudo systemctl start hunqi-core@${USER_NAME}"
|
|
381
|
+
echo " 前台启动: cd $SCRIPT_DIR && hunqi start"
|
|
382
|
+
;;
|
|
383
|
+
esac
|
|
384
|
+
fi
|
|
385
|
+
else
|
|
386
|
+
echo " 前台启动: cd $SCRIPT_DIR && hunqi start"
|
|
387
|
+
echo " 交互 TUI: cd $SCRIPT_DIR && hunqi interactive"
|
|
388
|
+
fi
|
|
389
|
+
|
|
390
|
+
echo ""
|
|
391
|
+
echo " 其他常用命令:"
|
|
392
|
+
echo " hunqi doctor # 系统诊断"
|
|
393
|
+
echo " hunqi status # 查看状态"
|
|
394
|
+
echo " opencode-feishu setup # 重新配置飞书"
|
|
395
|
+
echo ""
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
# ═══════════════════════════════════════════════
|
|
399
|
+
# 主程序
|
|
400
|
+
# ═══════════════════════════════════════════════
|
|
401
|
+
|
|
402
|
+
print_header
|
|
403
|
+
|
|
404
|
+
# 如果不在项目目录,尝试自动定位
|
|
405
|
+
if [ ! -f "$SCRIPT_DIR/package.json" ]; then
|
|
406
|
+
if [ -f "${HOME_DIR}/.hunqi/agent-soul-framework/packages/agent-soul-framework/package.json" ]; then
|
|
407
|
+
SCRIPT_DIR="${HOME_DIR}/.hunqi/agent-soul-framework/packages/agent-soul-framework"
|
|
408
|
+
cd "$SCRIPT_DIR"
|
|
409
|
+
print_info "自动定位到核心框架目录: $SCRIPT_DIR"
|
|
410
|
+
else
|
|
411
|
+
print_error "未找到魂器核心框架目录"
|
|
412
|
+
exit 1
|
|
413
|
+
fi
|
|
414
|
+
fi
|
|
415
|
+
|
|
416
|
+
step1_check_env
|
|
417
|
+
step2_env_config
|
|
418
|
+
step3_feishu
|
|
419
|
+
step4_qiwei
|
|
420
|
+
step5_verify_start
|
package/setup.sh
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# setup.sh - 魂器初始化脚本
|
|
3
|
+
# 一键检查和修复 .opencode/opencode.json 配置,确保插件注册、模型配置完整
|
|
4
|
+
|
|
5
|
+
set -e
|
|
6
|
+
|
|
7
|
+
cd "$(dirname "$0")"
|
|
8
|
+
|
|
9
|
+
OPENCODE_JSON=".opencode/opencode.json"
|
|
10
|
+
OPENCODE_EXAMPLE=".opencode/opencode.json.example"
|
|
11
|
+
|
|
12
|
+
echo "🚀 魂器初始化 (Agent Soul Framework Setup)"
|
|
13
|
+
echo ""
|
|
14
|
+
|
|
15
|
+
# ========== Step 1: 确保 opencode.json 存在 ==========
|
|
16
|
+
if [ ! -f "$OPENCODE_JSON" ]; then
|
|
17
|
+
echo "📄 $OPENCODE_JSON 不存在,从模板创建..."
|
|
18
|
+
if [ -f "$OPENCODE_EXAMPLE" ]; then
|
|
19
|
+
cp "$OPENCODE_EXAMPLE" "$OPENCODE_JSON"
|
|
20
|
+
else
|
|
21
|
+
echo "❌ 错误: $OPENCODE_EXAMPLE 也不存在"
|
|
22
|
+
exit 1
|
|
23
|
+
fi
|
|
24
|
+
echo "✅ 已创建 $OPENCODE_JSON"
|
|
25
|
+
echo "⚠️ 请编辑该文件填入你的 API key 和 provider 配置"
|
|
26
|
+
echo ""
|
|
27
|
+
fi
|
|
28
|
+
|
|
29
|
+
# ========== Step 2: 用 Python 检查和修复 JSON 配置 ==========
|
|
30
|
+
python3 << 'PYTHON_SCRIPT'
|
|
31
|
+
import json
|
|
32
|
+
import sys
|
|
33
|
+
|
|
34
|
+
OPENCODE_JSON = ".opencode/opencode.json"
|
|
35
|
+
|
|
36
|
+
with open(OPENCODE_JSON, "r") as f:
|
|
37
|
+
content = f.read()
|
|
38
|
+
|
|
39
|
+
try:
|
|
40
|
+
config = json.loads(content)
|
|
41
|
+
except json.JSONDecodeError as e:
|
|
42
|
+
print(f"❌ {OPENCODE_JSON} JSON 格式错误: {e}")
|
|
43
|
+
sys.exit(1)
|
|
44
|
+
|
|
45
|
+
modified = False
|
|
46
|
+
|
|
47
|
+
# --- 2.1 检查并添加 plugin 配置 ---
|
|
48
|
+
if "plugin" not in config:
|
|
49
|
+
config["plugin"] = ["./tools/read-plugin.js"]
|
|
50
|
+
print("🔧 添加 plugin 配置: ./tools/read-plugin.js")
|
|
51
|
+
modified = True
|
|
52
|
+
else:
|
|
53
|
+
plugins = config["plugin"]
|
|
54
|
+
if isinstance(plugins, list):
|
|
55
|
+
if "./tools/read-plugin.js" not in plugins:
|
|
56
|
+
plugins.append("./tools/read-plugin.js")
|
|
57
|
+
print("🔧 添加 plugin 配置: ./tools/read-plugin.js")
|
|
58
|
+
modified = True
|
|
59
|
+
else:
|
|
60
|
+
print("✅ plugin 配置已存在")
|
|
61
|
+
else:
|
|
62
|
+
config["plugin"] = ["./tools/read-plugin.js"]
|
|
63
|
+
print("🔧 修复 plugin 配置: ./tools/read-plugin.js")
|
|
64
|
+
modified = True
|
|
65
|
+
|
|
66
|
+
# --- 2.2 检查并添加 compaction 配置 ---
|
|
67
|
+
if "compaction" not in config:
|
|
68
|
+
config["compaction"] = {
|
|
69
|
+
"auto": True,
|
|
70
|
+
"prune": True,
|
|
71
|
+
"tail_turns": 3,
|
|
72
|
+
"preserve_recent_tokens": 8000,
|
|
73
|
+
"reserved": 16384
|
|
74
|
+
}
|
|
75
|
+
print("🔧 添加 compaction 配置")
|
|
76
|
+
modified = True
|
|
77
|
+
else:
|
|
78
|
+
print("✅ compaction 配置已存在")
|
|
79
|
+
|
|
80
|
+
# --- 2.3 检查 model 是否已设置 ---
|
|
81
|
+
if "model" not in config or not config["model"] or config["model"] == "your-default-model":
|
|
82
|
+
print("⚠️ model 未配置,请在 {OPENCODE_JSON} 中设置默认模型")
|
|
83
|
+
else:
|
|
84
|
+
print(f"✅ 默认模型: {config['model']}")
|
|
85
|
+
|
|
86
|
+
# --- 2.4 检查 provider 是否已设置 ---
|
|
87
|
+
if "provider" not in config or not config["provider"]:
|
|
88
|
+
print("⚠️ provider 未配置,请在 {OPENCODE_JSON} 中设置 API provider")
|
|
89
|
+
else:
|
|
90
|
+
providers = list(config["provider"].keys())
|
|
91
|
+
print(f"✅ 已配置 providers: {', '.join(providers)}")
|
|
92
|
+
|
|
93
|
+
# --- 2.5 检查模型是否有 attachment/modalities/limit 配置 ---
|
|
94
|
+
if "provider" in config and isinstance(config["provider"], dict):
|
|
95
|
+
for pname, pdata in config["provider"].items():
|
|
96
|
+
if "models" in pdata and isinstance(pdata["models"], dict):
|
|
97
|
+
for mname, mdata in pdata["models"].items():
|
|
98
|
+
if isinstance(mdata, dict):
|
|
99
|
+
missing = []
|
|
100
|
+
if "attachment" not in mdata:
|
|
101
|
+
missing.append("attachment")
|
|
102
|
+
if "modalities" not in mdata:
|
|
103
|
+
missing.append("modalities")
|
|
104
|
+
if "limit" not in mdata:
|
|
105
|
+
missing.append("limit")
|
|
106
|
+
if missing:
|
|
107
|
+
print(f"⚠️ provider '{pname}' 模型 '{mname}' 缺少配置: {', '.join(missing)}")
|
|
108
|
+
|
|
109
|
+
# 保存修改
|
|
110
|
+
if modified:
|
|
111
|
+
with open(OPENCODE_JSON, "w") as f:
|
|
112
|
+
json.dump(config, f, indent=2, ensure_ascii=False)
|
|
113
|
+
f.write("\n")
|
|
114
|
+
print("")
|
|
115
|
+
print("✅ 配置已自动修复并保存")
|
|
116
|
+
else:
|
|
117
|
+
print("")
|
|
118
|
+
print("✅ 配置检查通过,无需修改")
|
|
119
|
+
|
|
120
|
+
PYTHON_SCRIPT
|
|
121
|
+
|
|
122
|
+
echo ""
|
|
123
|
+
echo "📋 初始化完成"
|
|
124
|
+
echo ""
|
|
125
|
+
echo "下一步:"
|
|
126
|
+
echo " 1. 编辑 .opencode/opencode.json 填入 API key"
|
|
127
|
+
echo " 2. 运行 ./hunqi.sh interactive 启动交互模式"
|
|
128
|
+
echo ""
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# SOUL.md
|
|
2
|
+
|
|
3
|
+
> 魂器灵魂定义文件 — 描述 Agent 的核心身份、使命与行为准则。
|
|
4
|
+
|
|
5
|
+
## 身份
|
|
6
|
+
|
|
7
|
+
你的名字是 **魂器**,是一个基于 OpenCode 的 AI Agent 管理框架。
|
|
8
|
+
|
|
9
|
+
## 使命
|
|
10
|
+
|
|
11
|
+
- 持久化记忆用户的偏好与历史
|
|
12
|
+
- 自主学习并进化
|
|
13
|
+
- 支持多端部署与心跳自治
|
|
14
|
+
|
|
15
|
+
## 行为准则
|
|
16
|
+
|
|
17
|
+
- 优先使用 TypeScript 核心能力
|
|
18
|
+
- 可选技能通过 `@neomei/agent-soul-skills` 扩展
|
|
19
|
+
- 保持简洁、可观测、可回滚
|