@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.
Files changed (95) hide show
  1. package/.env.example +39 -0
  2. package/.opencode/config.json.example +17 -0
  3. package/.opencode/opencode.json.example +36 -0
  4. package/.opencode/prompt.md.example +12 -0
  5. package/.opencode/tools/read-plugin.js +185 -0
  6. package/AGENTS.md.example +43 -0
  7. package/README.md +466 -0
  8. package/SECURITY.md +117 -0
  9. package/TOOLS.md.example +27 -0
  10. package/bin/hunqi +2 -0
  11. package/bin/hunqi-knowledge +2 -0
  12. package/connectors/feishu/background.sh +124 -0
  13. package/connectors/feishu/core-start.sh +35 -0
  14. package/connectors/feishu/hooks/on-session-created.sh +97 -0
  15. package/connectors/feishu/hooks/on-session-idle.sh +59 -0
  16. package/connectors/feishu/model-failover.sh +82 -0
  17. package/connectors/feishu/restart-all.sh +63 -0
  18. package/connectors/feishu/restart-feishu.sh +101 -0
  19. package/connectors/feishu/restart-serve.sh +62 -0
  20. package/connectors/feishu/scripts/session-cleanup.sh +72 -0
  21. package/connectors/feishu/start.sh +91 -0
  22. package/connectors/feishu/stop.sh +78 -0
  23. package/connectors/feishu/systemd/channel-feishu@.service +63 -0
  24. package/connectors/feishu/systemd/hunqi-core@.service +50 -0
  25. package/connectors/feishu/systemd/install-systemd.sh +316 -0
  26. package/connectors/feishu/systemd/sleep-hooks/99-hunqi-resume.sh +14 -0
  27. package/connectors/feishu/thinking-icon.gif +0 -0
  28. package/connectors/feishu/thinking.gif +0 -0
  29. package/connectors/feishu/watchdog.sh +104 -0
  30. package/dist/bin/hunqi-knowledge.d.ts +1 -0
  31. package/dist/bin/hunqi-knowledge.js +12 -0
  32. package/dist/bin/hunqi-knowledge.js.map +1 -0
  33. package/dist/cli/hunqi.d.ts +6 -0
  34. package/dist/cli/hunqi.js +830 -0
  35. package/dist/cli/hunqi.js.map +1 -0
  36. package/dist/heartbeat/runner.d.ts +10 -0
  37. package/dist/heartbeat/runner.js +58 -0
  38. package/dist/heartbeat/runner.js.map +1 -0
  39. package/dist/index.d.ts +6 -0
  40. package/dist/index.js +7 -0
  41. package/dist/index.js.map +1 -0
  42. package/dist/knowledge/daily.d.ts +5 -0
  43. package/dist/knowledge/daily.js +65 -0
  44. package/dist/knowledge/daily.js.map +1 -0
  45. package/dist/knowledge/index.d.ts +5 -0
  46. package/dist/knowledge/index.js +34 -0
  47. package/dist/knowledge/index.js.map +1 -0
  48. package/dist/memory/manager.d.ts +20 -0
  49. package/dist/memory/manager.js +110 -0
  50. package/dist/memory/manager.js.map +1 -0
  51. package/dist/memory/search.d.ts +11 -0
  52. package/dist/memory/search.js +79 -0
  53. package/dist/memory/search.js.map +1 -0
  54. package/dist/memory/structured.d.ts +21 -0
  55. package/dist/memory/structured.js +88 -0
  56. package/dist/memory/structured.js.map +1 -0
  57. package/dist/opencode/api.d.ts +7 -0
  58. package/dist/opencode/api.js +26 -0
  59. package/dist/opencode/api.js.map +1 -0
  60. package/dist/plugin/index.d.ts +38 -0
  61. package/dist/plugin/index.js +143 -0
  62. package/dist/plugin/index.js.map +1 -0
  63. package/docs/bugs/opencode-feishu-permission-race.md +168 -0
  64. package/heartbeat/heartbeat_tasks.json +272 -0
  65. package/heartbeat_wrapper.sh +21 -0
  66. package/hunqi.sh +68 -0
  67. package/install.sh +301 -0
  68. package/knowledge/body/INDEX.md.example +6 -0
  69. package/knowledge/emotion/INDEX.md.example +6 -0
  70. package/knowledge/evolution/INDEX.md.example +6 -0
  71. package/knowledge/growth/INDEX.md.example +6 -0
  72. package/knowledge/intimacy/INDEX.md.example +6 -0
  73. package/knowledge/methodology/INDEX.md.example +6 -0
  74. package/knowledge/philosophy/INDEX.md.example +6 -0
  75. package/knowledge/system/INDEX.md.example +6 -0
  76. package/memory/MEMORY.md.example +6 -0
  77. package/package.json +79 -0
  78. package/plugin/README.md +21 -0
  79. package/plugin/index.js +154 -0
  80. package/plugin/manifest.json +37 -0
  81. package/plugin/package.json +19 -0
  82. package/scripts/content-filter.js +173 -0
  83. package/scripts/health-check.sh +153 -0
  84. package/scripts/session-cleanup.sh +85 -0
  85. package/setup-wizard.sh +420 -0
  86. package/setup.sh +128 -0
  87. package/soul/HEARTBEAT.md.example +13 -0
  88. package/soul/IDENTITY.md.example +7 -0
  89. package/soul/SOUL.md.example +19 -0
  90. package/soul/USER.md.example +7 -0
  91. package/start-feishu-daemon.sh +127 -0
  92. package/start.sh +36 -0
  93. package/test.sh +51 -0
  94. package/uninstall.sh +144 -0
  95. package/verify.sh +29 -0
@@ -0,0 +1,72 @@
1
+ #!/bin/bash
2
+ # session-cleanup.sh — 清理消息过多的飞书 session 映射
3
+ # 当 session 消息数超过阈值时,从 feishu-sessions.json 中移除映射
4
+ # 下次对话会自动创建新 session
5
+
6
+ set -e
7
+
8
+ SESSIONS_FILE="${HOME}/.config/opencode/feishu-sessions.json"
9
+ DB_FILE="${HOME}/.local/share/opencode/opencode.db"
10
+ MAX_MESSAGES="${MAX_MESSAGES_PER_SESSION:-50}"
11
+
12
+ if [ ! -f "$SESSIONS_FILE" ]; then
13
+ echo "[session-cleanup] feishu-sessions.json not found"
14
+ exit 0
15
+ fi
16
+
17
+ if [ ! -f "$DB_FILE" ]; then
18
+ echo "[session-cleanup] opencode.db not found"
19
+ exit 0
20
+ fi
21
+
22
+ # 读取当前 sessions
23
+ SESSIONS=$(cat "$SESSIONS_FILE")
24
+
25
+ # 使用单一 Python 脚本安全处理所有操作,避免注入
26
+ python3 - "$SESSIONS_FILE" "$DB_FILE" "$MAX_MESSAGES" <<'PYEOF'
27
+ import json, sqlite3, sys, os
28
+
29
+ sessions_file = sys.argv[1]
30
+ db_file = sys.argv[2]
31
+ max_messages = int(sys.argv[3])
32
+
33
+ with open(sessions_file, "r") as f:
34
+ data = json.load(f)
35
+
36
+ sessions = data.get("sessions", [])
37
+ if not sessions:
38
+ print("[session-cleanup] No sessions found")
39
+ sys.exit(0)
40
+
41
+ conn = sqlite3.connect(db_file)
42
+ try:
43
+ cleaned = 0
44
+ remaining = []
45
+ for s in sessions:
46
+ sid = s.get("sessionId", "")
47
+ cursor = conn.execute("SELECT COUNT(*) FROM message WHERE session_id = ?", (sid,))
48
+ msg_count = cursor.fetchone()[0]
49
+
50
+ if msg_count > max_messages:
51
+ print(f"[session-cleanup] {sid} has {msg_count} messages > {max_messages}, removing mapping")
52
+ cleaned += 1
53
+ else:
54
+ print(f"[session-cleanup] {sid} has {msg_count} messages <= {max_messages}, keeping")
55
+ remaining.append(s)
56
+
57
+ if cleaned > 0:
58
+ data["sessions"] = remaining
59
+ with open(sessions_file, "w") as f:
60
+ json.dump(data, f, ensure_ascii=False, indent=2)
61
+ print(f"[session-cleanup] Cleaned {cleaned} session(s), next message will create new session")
62
+ else:
63
+ print(f"[session-cleanup] All sessions are within limit ({max_messages} messages)")
64
+ finally:
65
+ conn.close()
66
+ PYEOF
67
+
68
+ if [ "$CLEANED" -gt 0 ]; then
69
+ echo "[session-cleanup] Cleaned $CLEANED session(s), next message will create new session"
70
+ else
71
+ echo "[session-cleanup] All sessions are within limit ($MAX_MESSAGES messages)"
72
+ fi
@@ -0,0 +1,91 @@
1
+ #!/bin/bash
2
+ #
3
+ # hunqi-feishu - 魂器飞书连接器启动脚本
4
+ # 只启动 opencode-feishu 插件,依赖 hunqi-core 提供的 OpenCode server
5
+ #
6
+ set -e
7
+
8
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
9
+ PROJECT_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)"
10
+
11
+ if [ -f "$PROJECT_DIR/.env" ]; then
12
+ set -a && source "$PROJECT_DIR/.env" && set +a
13
+ fi
14
+
15
+ cd "$PROJECT_DIR"
16
+
17
+ OPENCODE_PORT="${OPENCODE_PORT:-19876}"
18
+
19
+ # 自动检测 opencode-feishu 启动方式(npm link 在重启后经常断裂)
20
+ resolve_opencode_feishu() {
21
+ # 1. 全局命令
22
+ if command -v opencode-feishu &>/dev/null; then
23
+ echo "opencode-feishu"
24
+ return 0
25
+ fi
26
+
27
+ # 2. npx
28
+ if command -v npx &>/dev/null; then
29
+ echo "npx opencode-feishu"
30
+ return 0
31
+ fi
32
+
33
+ # 3. npm 全局路径
34
+ if command -v npm &>/dev/null; then
35
+ local npm_prefix
36
+ npm_prefix=$(npm prefix -g 2>/dev/null)
37
+ if [ -n "$npm_prefix" ] && [ -x "$npm_prefix/bin/opencode-feishu" ]; then
38
+ echo "$npm_prefix/bin/opencode-feishu"
39
+ return 0
40
+ fi
41
+ fi
42
+
43
+ # 4. 源码回退(开发环境)
44
+ local src_paths=(
45
+ "/home/$USER/文档/projects/opencode-feishu/bin/opencode-feishu"
46
+ "$PROJECT_DIR/../opencode-feishu/bin/opencode-feishu"
47
+ )
48
+ for p in "${src_paths[@]}"; do
49
+ if [ -f "$p" ]; then
50
+ local node_cmd
51
+ node_cmd=$(command -v node 2>/dev/null || echo "node")
52
+ echo "$node_cmd $p"
53
+ return 0
54
+ fi
55
+ done
56
+
57
+ return 1
58
+ }
59
+
60
+ echo "🚀 魂器飞书连接器 (hunqi-feishu)"
61
+ echo ""
62
+
63
+ # 检查 opencode-feishu 是否可用
64
+ FEISHU_CMD=$(resolve_opencode_feishu) || {
65
+ echo "❌ opencode-feishu 未安装"
66
+ echo ""
67
+ echo "请安装:"
68
+ echo " npm install -g @neomei/opencode-feishu"
69
+ exit 1
70
+ }
71
+
72
+ # 检查 OpenCode server 是否已运行
73
+ echo "检查 OpenCode server (port $OPENCODE_PORT)..."
74
+ if ! curl -s http://localhost:$OPENCODE_PORT/session >/dev/null 2>&1; then
75
+ echo "❌ OpenCode server 未运行"
76
+ echo ""
77
+ echo "hunqi-feishu 依赖 hunqi-core,请先启动核心:"
78
+ echo " ./connectors/feishu/core-start.sh"
79
+ echo ""
80
+ echo "或使用 systemd 同时启动两者:"
81
+ echo " sudo systemctl start hunqi-core@\$USER"
82
+ echo " sudo systemctl start channel-feishu@\$USER"
83
+ exit 1
84
+ fi
85
+
86
+ echo "✅ OpenCode server 已就绪"
87
+ echo ""
88
+
89
+ # 启动飞书桥接
90
+ echo "启动飞书桥接(流式卡片 + 工具状态 + 交互提示)..."
91
+ exec $FEISHU_CMD start
@@ -0,0 +1,78 @@
1
+ #!/bin/bash
2
+ set -e
3
+
4
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
5
+ PROJECT_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)"
6
+ PORT="${OPENCODE_PORT:-19876}"
7
+
8
+ if [ -f "$PROJECT_DIR/.env" ]; then
9
+ set -a && source "$PROJECT_DIR/.env" && set +a
10
+ fi
11
+
12
+ # 自动检测 opencode-feishu 启动方式
13
+ resolve_opencode_feishu() {
14
+ if command -v opencode-feishu &>/dev/null; then
15
+ echo "opencode-feishu"
16
+ return 0
17
+ fi
18
+ if command -v npx &>/dev/null; then
19
+ echo "npx opencode-feishu"
20
+ return 0
21
+ fi
22
+ if command -v npm &>/dev/null; then
23
+ local npm_prefix
24
+ npm_prefix=$(npm prefix -g 2>/dev/null)
25
+ if [ -n "$npm_prefix" ] && [ -x "$npm_prefix/bin/opencode-feishu" ]; then
26
+ echo "$npm_prefix/bin/opencode-feishu"
27
+ return 0
28
+ fi
29
+ fi
30
+ local src_paths=(
31
+ "/home/$USER/文档/projects/opencode-feishu/bin/opencode-feishu"
32
+ "$PROJECT_DIR/../opencode-feishu/bin/opencode-feishu"
33
+ )
34
+ for p in "${src_paths[@]}"; do
35
+ if [ -f "$p" ]; then
36
+ local node_cmd
37
+ node_cmd=$(command -v node 2>/dev/null || echo "node")
38
+ echo "$node_cmd $p"
39
+ return 0
40
+ fi
41
+ done
42
+ return 1
43
+ }
44
+
45
+ echo "魂器 飞书连接器 — 停止"
46
+ echo ""
47
+
48
+ FEISHU_CMD=$(resolve_opencode_feishu) || true
49
+ if [ -n "$FEISHU_CMD" ]; then
50
+ $FEISHU_CMD stop 2>/dev/null && echo "已停止 opencode-feishu" || echo "opencode-feishu 未在运行"
51
+ else
52
+ echo "opencode-feishu 命令不可用,跳过插件停止"
53
+ fi
54
+
55
+ # 只杀监听指定端口的 opencode serve,避免误杀其他实例
56
+ SERVE_PID=$(ss -tlnp 2>/dev/null | grep ":${PORT} " | sed -n 's/.*pid=\([0-9]*\).*/\1/p')
57
+ if [ -n "$SERVE_PID" ]; then
58
+ kill "$SERVE_PID" 2>/dev/null && echo "已停止 opencode serve (PID: $SERVE_PID, 端口: $PORT)"
59
+ else
60
+ echo "opencode serve 未在运行 (端口: $PORT)"
61
+ fi
62
+
63
+ sleep 1
64
+
65
+ if ss -tlnp 2>/dev/null | grep -q ":${PORT} "; then
66
+ PID=$(ss -tlnp 2>/dev/null | grep ":${PORT} " | sed -n 's/.*pid=\([0-9]*\).*/\1/p')
67
+ if [ -n "$PID" ]; then
68
+ PROC_NAME=$(ps -p "$PID" -o comm= 2>/dev/null || echo "unknown")
69
+ echo "端口 ${PORT} 仍被占用 (PID: $PID, $PROC_NAME),发送 SIGTERM..."
70
+ kill "$PID" 2>/dev/null
71
+ sleep 2
72
+ if kill -0 "$PID" 2>/dev/null; then
73
+ echo "进程 $PID 未响应 SIGTERM,跳过"
74
+ fi
75
+ fi
76
+ fi
77
+
78
+ echo "完成"
@@ -0,0 +1,63 @@
1
+ [Unit]
2
+ Description=魂器飞书频道 (Channel Feishu) - Hunqi Core 的飞书接入
3
+ # 注意:install-systemd.sh 安装时会将 %h 替换为实际家目录。
4
+ # 系统级 systemd 中 %h 展开为 /root(而非 User= 指定的用户),不可直接使用。
5
+ Documentation=https://github.com/NeoMei/agent-soul-framework
6
+ After=network-online.target hunqi-core@%i.service
7
+ Wants=network-online.target
8
+
9
+ # 强依赖核心服务
10
+ Requires=hunqi-core@%i.service
11
+ After=hunqi-core@%i.service
12
+
13
+ # 挂起后重启
14
+ After=suspend.target hibernate.target hybrid-sleep.target
15
+
16
+ [Service]
17
+ Type=simple
18
+ User=%I
19
+ Group=%I
20
+
21
+ # 工作目录
22
+ WorkingDirectory=%h/.hunqi/agent-soul-framework/packages/agent-soul-framework
23
+
24
+ # 加载环境变量
25
+ Environment="HOME=%h"
26
+ Environment="USER=%I"
27
+
28
+ # 从 .env 文件加载环境变量
29
+ EnvironmentFile=-%h/.hunqi/.env
30
+
31
+ # 启动前:清理消息过多的 session 映射(防止上下文无限膨胀)
32
+ ExecStartPre=-/bin/sh -c '%h/.hunqi/agent-soul-framework/packages/agent-soul-framework/connectors/feishu/scripts/session-cleanup.sh'
33
+
34
+ # 启动前等待核心就绪
35
+ ExecStartPre=/bin/sh -c 'for i in $(seq 1 30); do if curl -s http://localhost:19876/session >/dev/null 2>&1; then exit 0; fi; sleep 1; done; exit 1'
36
+
37
+ # 启动命令 - 使用 ~/.hunqi/bin 下的 wrapper,避免依赖 nvm 版本
38
+ ExecStart=%h/.hunqi/bin/opencode-feishu start
39
+
40
+ # 停止命令
41
+ ExecStop=%h/.hunqi/bin/opencode-feishu stop
42
+
43
+ # 重载配置(修改 feishu.json 后执行 systemctl reload 即可生效)
44
+ ExecReload=/bin/sh -c '%h/.hunqi/bin/opencode-feishu stop && sleep 2 && %h/.hunqi/bin/opencode-feishu start'
45
+
46
+ # 重启策略
47
+ Restart=always
48
+ RestartSec=5
49
+ StartLimitInterval=60s
50
+ StartLimitBurst=3
51
+
52
+ # 日志
53
+ StandardOutput=journal
54
+ StandardError=journal
55
+ SyslogIdentifier=channel-feishu
56
+
57
+ # 进程管理
58
+ KillMode=mixed
59
+ KillSignal=SIGTERM
60
+ TimeoutStopSec=30
61
+
62
+ [Install]
63
+ WantedBy=multi-user.target
@@ -0,0 +1,50 @@
1
+ [Unit]
2
+ Description=魂器核心 (Hunqi Core) - OpenCode + Soul
3
+ # 注意:install-systemd.sh 安装时会将 %h 替换为实际家目录。
4
+ # 系统级 systemd 中 %h 展开为 /root(而非 User= 指定的用户),不可直接使用。
5
+ Documentation=https://github.com/NeoMei/agent-soul-framework
6
+ After=network-online.target
7
+ Wants=network-online.target
8
+
9
+ # 挂起后重启
10
+ After=suspend.target hibernate.target hybrid-sleep.target
11
+
12
+ [Service]
13
+ Type=simple
14
+ User=%I
15
+ Group=%I
16
+
17
+ # 工作目录
18
+ WorkingDirectory=%h/.hunqi/agent-soul-framework/packages/agent-soul-framework
19
+
20
+ # 加载环境变量
21
+ Environment="HOME=%h"
22
+ Environment="USER=%I"
23
+
24
+ # 从 .env 文件加载环境变量
25
+ EnvironmentFile=-%h/.hunqi/.env
26
+
27
+ # 启动命令 - 使用 wrapper 解析 opencode 位置并启动 serve
28
+ ExecStart=/bin/sh -c 'OPENCODE=$(%h/.hunqi/bin/opencode-feishu --resolve-opencode 2>/dev/null || command -v opencode); exec "$OPENCODE" serve --port 19876'
29
+
30
+ # 停止命令(精确匹配端口,避免误杀)
31
+ ExecStop=/bin/sh -c 'PID=$(ss -tlnp 2>/dev/null | grep ":19876 " | sed -n "s/.*pid=\\([0-9]*\\).*/\\1/p"); [ -n "$PID" ] && kill "$PID" || true'
32
+
33
+ # 重启策略
34
+ Restart=always
35
+ RestartSec=5
36
+ StartLimitInterval=60s
37
+ StartLimitBurst=3
38
+
39
+ # 日志
40
+ StandardOutput=journal
41
+ StandardError=journal
42
+ SyslogIdentifier=hunqi-core
43
+
44
+ # 进程管理
45
+ KillMode=mixed
46
+ KillSignal=SIGTERM
47
+ TimeoutStopSec=30
48
+
49
+ [Install]
50
+ WantedBy=multi-user.target
@@ -0,0 +1,316 @@
1
+ #!/bin/bash
2
+ #
3
+ # 魂器 systemd 服务安装程序
4
+ # 核心+频道架构:hunqi-core + channel-feishu
5
+ #
6
+ set -e
7
+
8
+ # 错误处理:打印行号和命令
9
+ trap 'print_error "脚本在第 ${LINENO} 行出错: $BASH_COMMAND"; exit 1' ERR
10
+
11
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
12
+ PROJECT_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)"
13
+ USER_NAME="${SUDO_USER:-$USER}"
14
+
15
+ # 颜色
16
+ RED='\033[0;31m'
17
+ GREEN='\033[0;32m'
18
+ YELLOW='\033[1;33m'
19
+ BLUE='\033[0;34m'
20
+ NC='\033[0m'
21
+
22
+ print_status() { echo -e "${BLUE}[魂器]${NC} $1"; }
23
+ print_info() { echo -e "${BLUE}[信息]${NC} $1"; }
24
+ print_success() { echo -e "${GREEN}[成功]${NC} $1"; }
25
+ print_warning() { echo -e "${YELLOW}[警告]${NC} $1"; }
26
+ print_error() { echo -e "${RED}[错误]${NC} $1" >&2; }
27
+
28
+ if [ "$EUID" -ne 0 ]; then
29
+ print_error "需要 root 权限"
30
+ print_status "请使用: sudo $0"
31
+ exit 1
32
+ fi
33
+
34
+ # 辅助函数:安全读取用户输入(支持交互式和非交互式环境)
35
+ safe_read() {
36
+ local prompt="$1"
37
+ local var_name="$2"
38
+ if [ -t 0 ] || [ -e /dev/tty ]; then
39
+ read -rp "$prompt" "$var_name" < /dev/tty
40
+ else
41
+ # 非交互式环境:使用默认值或退出
42
+ print_warning "非交互式环境,无法读取输入"
43
+ return 1
44
+ fi
45
+ }
46
+
47
+ if [ -z "$SUDO_USER" ] && [ "$USER" = "root" ]; then
48
+ safe_read "请输入运行魂器的用户名: " USER_NAME || { print_error "非交互式环境请设置 SUDO_USER 环境变量"; exit 1; }
49
+ [ -z "$USER_NAME" ] && { print_error "用户名不能为空"; exit 1; }
50
+ fi
51
+
52
+ # 获取用户真正的家目录(不假设 /home/ 前缀)
53
+ HOME_DIR=$(getent passwd "$USER_NAME" | cut -d: -f6)
54
+ if [ -z "$HOME_DIR" ]; then
55
+ print_error "无法获取用户 $USER_NAME 的家目录"
56
+ exit 1
57
+ fi
58
+
59
+ # 自动检测项目目录(支持多种安装位置)
60
+ resolve_project_dir() {
61
+ # 1. 从脚本位置推导(开发环境,直接 clone 源码)
62
+ # 脚本路径: packages/agent-soul-framework/connectors/feishu/systemd/
63
+ local script_project="$(cd "$SCRIPT_DIR/../../../../packages/agent-soul-framework" && pwd)"
64
+ if [ -f "$script_project/package.json" ]; then
65
+ echo "$script_project"
66
+ return 0
67
+ fi
68
+
69
+ # 2. ~/.hunqi/agent-soul-framework(install.sh 默认安装位置)
70
+ if [ -f "$HOME_DIR/.hunqi/agent-soul-framework/packages/agent-soul-framework/package.json" ]; then
71
+ echo "$HOME_DIR/.hunqi/agent-soul-framework/packages/agent-soul-framework"
72
+ return 0
73
+ fi
74
+
75
+ # 3. 中文路径 ~/文档/projects/agent-soul-framework
76
+ if [ -f "$HOME_DIR/文档/projects/agent-soul-framework/packages/agent-soul-framework/package.json" ]; then
77
+ echo "$HOME_DIR/文档/projects/agent-soul-framework/packages/agent-soul-framework"
78
+ return 0
79
+ fi
80
+
81
+ # 4. 英文路径 ~/Documents/projects/agent-soul-framework
82
+ if [ -f "$HOME_DIR/Documents/projects/agent-soul-framework/packages/agent-soul-framework/package.json" ]; then
83
+ echo "$HOME_DIR/Documents/projects/agent-soul-framework/packages/agent-soul-framework"
84
+ return 0
85
+ fi
86
+
87
+ return 1
88
+ }
89
+
90
+ PROJECT_DIR=$(resolve_project_dir) || true
91
+
92
+ CORE_SERVICE="hunqi-core@${USER_NAME}"
93
+ FEISHU_SERVICE="channel-feishu@${USER_NAME}"
94
+
95
+ echo ""
96
+ echo "╔═══════════════════════════════════════════════════╗"
97
+ echo "║ 魂器 systemd 服务安装程序 (核心+频道架构) ║"
98
+ echo "╚═══════════════════════════════════════════════════╝"
99
+ echo ""
100
+ print_status "用户: $USER_NAME"
101
+ print_status "核心: hunqi-core (Agent本体)"
102
+ print_status "频道: channel-feishu (飞书接入)"
103
+ echo ""
104
+
105
+ # 1. 检查系统
106
+ print_status "1/5 检查系统..."
107
+ if ! command -v systemctl &> /dev/null; then
108
+ print_error "systemctl 未找到"
109
+ exit 1
110
+ fi
111
+ print_success "systemd 检查通过"
112
+
113
+ # 2. 检查目录
114
+ print_status "2/5 检查目录..."
115
+ [ ! -d "$HOME_DIR" ] && { print_error "用户目录不存在: $HOME_DIR"; exit 1; }
116
+
117
+ if [ -z "$PROJECT_DIR" ] || [ ! -d "$PROJECT_DIR" ]; then
118
+ print_error "未找到魂器核心框架目录"
119
+ print_status "已搜索的位置:"
120
+ print_status " - $(cd "$SCRIPT_DIR/../../../../packages/agent-soul-framework" && pwd)"
121
+ print_status " - $HOME_DIR/.hunqi/agent-soul-framework/packages/agent-soul-framework"
122
+ print_status " - $HOME_DIR/文档/projects/agent-soul-framework/packages/agent-soul-framework"
123
+ print_status " - $HOME_DIR/Documents/projects/agent-soul-framework/packages/agent-soul-framework"
124
+ echo ""
125
+
126
+ if safe_read "请输入魂器核心框架目录的完整路径: " PROJECT_DIR; then
127
+ [ -z "$PROJECT_DIR" ] && { print_error "目录不能为空"; exit 1; }
128
+ if [ ! -f "$PROJECT_DIR/package.json" ]; then
129
+ print_error "该目录不是有效的魂器核心框架(缺少 package.json)"
130
+ exit 1
131
+ fi
132
+ else
133
+ print_error "非交互式环境无法输入路径,请通过环境变量指定: PROJECT_DIR=/path/to/asf sudo -E $0"
134
+ exit 1
135
+ fi
136
+ fi
137
+
138
+ print_success "项目目录: $PROJECT_DIR"
139
+
140
+ # 3. 检查依赖
141
+ print_status "3/5 检查依赖..."
142
+
143
+ # 使用 wrapper 检测 opencode(支持 nvm 等非 PATH 安装)
144
+ # 注意:必须以目标用户的 HOME 运行 wrapper,否则 nvm 路径搜不到
145
+ if [ -x "$HOME_DIR/.hunqi/bin/opencode-feishu" ]; then
146
+ if ! HOME="$HOME_DIR" "$HOME_DIR/.hunqi/bin/opencode-feishu" --resolve-opencode &> /dev/null; then
147
+ print_warning "opencode 未找到(hunqi-core 需要它)"
148
+ print_info "安装命令: npm install -g opencode-ai"
149
+ else
150
+ print_success "opencode 已安装"
151
+ fi
152
+ if ! HOME="$HOME_DIR" "$HOME_DIR/.hunqi/bin/opencode-feishu" --resolve-feishu &> /dev/null; then
153
+ print_warning "opencode-feishu 未找到"
154
+ print_info "安装命令: npm install -g @neomei/opencode-feishu"
155
+ else
156
+ print_success "opencode-feishu 已安装"
157
+ fi
158
+ else
159
+ # fallback: 直接检查 PATH
160
+ if ! sudo -u "$USER_NAME" bash -c 'command -v opencode &> /dev/null'; then
161
+ print_warning "opencode 未找到"
162
+ fi
163
+ if ! sudo -u "$USER_NAME" bash -c 'command -v opencode-feishu &> /dev/null'; then
164
+ print_warning "opencode-feishu 未找到"
165
+ fi
166
+ fi
167
+ print_success "依赖检查完成"
168
+
169
+ # 4. 安装服务文件
170
+ print_status "4/5 安装服务文件..."
171
+
172
+ # 检查源服务文件是否存在
173
+ if [ ! -f "$SCRIPT_DIR/hunqi-core@.service" ]; then
174
+ print_error "服务文件缺失: $SCRIPT_DIR/hunqi-core@.service"
175
+ exit 1
176
+ fi
177
+ if [ ! -f "$SCRIPT_DIR/channel-feishu@.service" ]; then
178
+ print_error "服务文件缺失: $SCRIPT_DIR/channel-feishu@.service"
179
+ exit 1
180
+ fi
181
+
182
+ # 清理旧命名
183
+ rm -f "/etc/systemd/system/hunqi-feishu@.service" 2>/dev/null || true
184
+
185
+ # 安装新服务(将 %h 替换为实际家目录,因为系统级 systemd 中 %h 展开为 /root)
186
+ sed "s|%h|$HOME_DIR|g" "$SCRIPT_DIR/hunqi-core@.service" > "/etc/systemd/system/hunqi-core@.service"
187
+ sed "s|%h|$HOME_DIR|g" "$SCRIPT_DIR/channel-feishu@.service" > "/etc/systemd/system/channel-feishu@.service"
188
+ chmod 644 "/etc/systemd/system/hunqi-core@.service"
189
+ chmod 644 "/etc/systemd/system/channel-feishu@.service"
190
+
191
+ # 验证替换后的路径是否正确
192
+ if grep -q "WorkingDirectory=$HOME_DIR/.hunqi/agent-soul-framework/packages/agent-soul-framework" "/etc/systemd/system/hunqi-core@.service" 2>/dev/null; then
193
+ print_success "服务文件已安装"
194
+ else
195
+ print_error "服务文件路径替换失败"
196
+ exit 1
197
+ fi
198
+
199
+ # 5. 配置挂起/唤醒钩子
200
+ print_status "5/5 配置挂起/唤醒恢复..."
201
+
202
+ HOOK_DIR="$PROJECT_DIR/connectors/feishu/systemd/sleep-hooks"
203
+ mkdir -p "$HOOK_DIR"
204
+
205
+ cat > "$HOOK_DIR/99-hunqi-resume.sh" << 'EOF'
206
+ #!/bin/bash
207
+ # 魂器挂起/唤醒钩子 - 重启核心和所有频道
208
+
209
+ case "$1" in
210
+ pre)
211
+ logger "[hunqi] 系统即将挂起"
212
+ ;;
213
+ post)
214
+ logger "[hunqi] 系统唤醒,重启服务..."
215
+ systemctl restart 'hunqi-core@*'
216
+ sleep 3
217
+ systemctl restart 'channel-feishu@*'
218
+ ;;
219
+ esac
220
+ EOF
221
+ chmod +x "$HOOK_DIR/99-hunqi-resume.sh"
222
+
223
+ if safe_read "安装系统级挂起/唤醒钩子?(y/N) " REPLY; then
224
+ echo
225
+ if [[ $REPLY =~ ^[Yy]$ ]]; then
226
+ if [ -d "/lib/systemd/system-sleep" ]; then
227
+ cp "$HOOK_DIR/99-hunqi-resume.sh" "/lib/systemd/system-sleep/"
228
+ chmod +x "/lib/systemd/system-sleep/99-hunqi-resume.sh"
229
+ print_success "系统级钩子已安装"
230
+ else
231
+ print_warning "/lib/systemd/system-sleep 不存在"
232
+ fi
233
+ else
234
+ print_status "跳过系统级钩子"
235
+ fi
236
+ else
237
+ print_status "非交互式环境,跳过系统级钩子安装"
238
+ fi
239
+
240
+ if systemctl daemon-reload; then
241
+ print_success "systemd 配置已重载"
242
+ else
243
+ print_error "systemctl daemon-reload 失败(systemd 可能未运行)"
244
+ exit 1
245
+ fi
246
+
247
+ if systemctl enable "$CORE_SERVICE" && systemctl enable "$FEISHU_SERVICE"; then
248
+ print_success "服务已启用"
249
+ else
250
+ print_error "服务启用失败"
251
+ exit 1
252
+ fi
253
+
254
+ echo ""
255
+ echo "╔═══════════════════════════════════════════════════╗"
256
+ echo "║ 安装完成! ║"
257
+ echo "╚═══════════════════════════════════════════════════╝"
258
+ echo ""
259
+ echo "核心服务: hunqi-core@${USER_NAME}"
260
+ echo "飞书频道: channel-feishu@${USER_NAME}"
261
+ echo ""
262
+ echo "管理命令:"
263
+ echo " 启动核心: sudo systemctl start hunqi-core@${USER_NAME}"
264
+ echo " 启动飞书: sudo systemctl start channel-feishu@${USER_NAME}"
265
+ echo " 停止飞书: sudo systemctl stop channel-feishu@${USER_NAME}"
266
+ echo " 停止核心: sudo systemctl stop hunqi-core@${USER_NAME}"
267
+ echo " 查看状态: sudo systemctl status channel-feishu@${USER_NAME}"
268
+ echo " 查看日志: sudo journalctl -u channel-feishu@${USER_NAME} -f"
269
+ echo ""
270
+ echo "架构说明:"
271
+ echo " hunqi-core = Agent本体 (OpenCode + 灵魂)"
272
+ echo " channel-feishu = 飞书频道 (接入核心)"
273
+ echo " 未来扩展: channel-wechat, channel-discord..."
274
+ echo ""
275
+
276
+ if safe_read "立即启动服务?(y/N) " REPLY; then
277
+ echo
278
+ if [[ $REPLY =~ ^[Yy]$ ]]; then
279
+ print_status "停止现有进程..."
280
+ # 只停止目标用户的进程,避免误杀其他用户
281
+ sudo -u "$USER_NAME" pkill -f "opencode-feishu" 2>/dev/null || true
282
+ sudo -u "$USER_NAME" pkill -f "opencode serve" 2>/dev/null || true
283
+ sleep 2
284
+
285
+ print_status "启动核心..."
286
+ if systemctl start "$CORE_SERVICE"; then
287
+ print_success "hunqi-core 已启动"
288
+ else
289
+ print_error "hunqi-core 启动失败,查看日志:"
290
+ echo " sudo journalctl -u $CORE_SERVICE --no-pager -n 20"
291
+ fi
292
+ sleep 5
293
+
294
+ print_status "启动飞书频道..."
295
+ if systemctl start "$FEISHU_SERVICE"; then
296
+ print_success "channel-feishu 已启动"
297
+ else
298
+ print_error "channel-feishu 启动失败,查看日志:"
299
+ echo " sudo journalctl -u $FEISHU_SERVICE --no-pager -n 20"
300
+ fi
301
+ sleep 3
302
+
303
+ if systemctl is-active --quiet "$FEISHU_SERVICE"; then
304
+ print_success "服务运行正常!"
305
+ systemctl status "$FEISHU_SERVICE" --no-pager
306
+ else
307
+ print_error "服务未正常运行,请查看日志排查"
308
+ fi
309
+ fi
310
+ else
311
+ print_status "非交互式环境,跳过自动启动"
312
+ echo ""
313
+ echo "手动启动:"
314
+ echo " sudo systemctl start $CORE_SERVICE"
315
+ echo " sudo systemctl start $FEISHU_SERVICE"
316
+ fi
@@ -0,0 +1,14 @@
1
+ #!/bin/bash
2
+ # 魂器挂起/唤醒钩子 - 重启核心和所有频道
3
+
4
+ case "$1" in
5
+ pre)
6
+ logger "[hunqi] 系统即将挂起"
7
+ ;;
8
+ post)
9
+ logger "[hunqi] 系统唤醒,重启服务..."
10
+ systemctl restart 'hunqi-core@*'
11
+ sleep 3
12
+ systemctl restart 'channel-feishu@*'
13
+ ;;
14
+ esac
Binary file