iflow-feishu 1.0.0 → 1.0.2
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/README.md +70 -78
- package/package.json +1 -1
- package/src/config/config.js +148 -50
- package/src/index.js +6 -3
package/README.md
CHANGED
|
@@ -9,41 +9,56 @@
|
|
|
9
9
|
- **会话管理** - 自动保存上下文历史
|
|
10
10
|
- **模式切换** - 支持 default/yolo/plan/smart 模式
|
|
11
11
|
- **自动搜索** - 检测搜索关键词自动启用网络搜索
|
|
12
|
+
- **交互式配置** - 首次运行自动引导配置
|
|
13
|
+
|
|
14
|
+
## 安装
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install -g iflow-feishu
|
|
18
|
+
```
|
|
12
19
|
|
|
13
20
|
## 快速开始
|
|
14
21
|
|
|
15
|
-
###
|
|
22
|
+
### 方式一:交互式配置(推荐)
|
|
23
|
+
|
|
24
|
+
直接运行,首次使用会引导你配置飞书凭证:
|
|
16
25
|
|
|
17
26
|
```bash
|
|
18
27
|
iflow-feishu
|
|
19
28
|
```
|
|
20
29
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
1. **检查 iFlow CLI** - 未安装时询问是否安装
|
|
24
|
-
2. **安装插件依赖** - 自动安装 npm 依赖
|
|
25
|
-
3. **安装 PM2** - 自动安装进程管理器
|
|
26
|
-
4. **配置飞书凭证** - 首次使用时引导输入
|
|
27
|
-
5. **启动服务**
|
|
30
|
+
按提示输入飞书机器人的 App ID 和 App Secret 即可。
|
|
28
31
|
|
|
29
|
-
###
|
|
30
|
-
|
|
31
|
-
- Node.js >= 16.0.0
|
|
32
|
+
### 方式二:环境变量
|
|
32
33
|
|
|
33
|
-
|
|
34
|
+
```bash
|
|
35
|
+
export FEISHU_APP_ID="cli_xxxxxxxxxxxx"
|
|
36
|
+
export FEISHU_APP_SECRET="xxxxxxxxxxxxxxxx"
|
|
37
|
+
iflow-feishu
|
|
38
|
+
```
|
|
34
39
|
|
|
35
|
-
|
|
40
|
+
### 方式三:配置文件
|
|
36
41
|
|
|
37
42
|
```bash
|
|
38
43
|
mkdir -p ~/.feishu-config
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
"
|
|
42
|
-
|
|
44
|
+
cat > ~/.feishu-config/feishu-app.json << 'EOF'
|
|
45
|
+
{
|
|
46
|
+
"appId": "cli_xxxxxxxxxxxx",
|
|
47
|
+
"appSecret": "xxxxxxxxxxxxxxxx"
|
|
48
|
+
}
|
|
49
|
+
EOF
|
|
50
|
+
iflow-feishu
|
|
43
51
|
```
|
|
44
52
|
|
|
53
|
+
## 前置要求
|
|
54
|
+
|
|
55
|
+
- Node.js >= 16.0.0
|
|
56
|
+
- iFlow CLI(可选,用于本地开发)
|
|
57
|
+
|
|
45
58
|
## 命令列表
|
|
46
59
|
|
|
60
|
+
在飞书中给机器人发送以下命令:
|
|
61
|
+
|
|
47
62
|
| 命令 | 说明 |
|
|
48
63
|
|------|------|
|
|
49
64
|
| `/help` | 显示帮助信息 |
|
|
@@ -52,6 +67,44 @@ echo '{
|
|
|
52
67
|
| `/mode <模式>` | 切换模式 (default/yolo/plan/smart) |
|
|
53
68
|
| `/status` | 查看会话状态 |
|
|
54
69
|
|
|
70
|
+
## 环境变量
|
|
71
|
+
|
|
72
|
+
| 变量 | 说明 | 默认值 |
|
|
73
|
+
|------|------|--------|
|
|
74
|
+
| `FEISHU_APP_ID` | 飞书应用 ID | - |
|
|
75
|
+
| `FEISHU_APP_SECRET` | 飞书应用密钥 | - |
|
|
76
|
+
| `PORT` | HTTP 服务端口 | 18080 |
|
|
77
|
+
| `LOG_LEVEL` | 日志级别 (DEBUG/INFO/WARN/ERROR) | INFO |
|
|
78
|
+
|
|
79
|
+
## 常用操作
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
# 健康检查
|
|
83
|
+
curl http://localhost:18080/health
|
|
84
|
+
|
|
85
|
+
# 后台运行
|
|
86
|
+
nohup iflow-feishu > iflow-feishu.log 2>&1 &
|
|
87
|
+
|
|
88
|
+
# 查看日志
|
|
89
|
+
tail -f iflow-feishu.log
|
|
90
|
+
|
|
91
|
+
# 使用 PM2 管理
|
|
92
|
+
pm2 start iflow-feishu
|
|
93
|
+
pm2 logs iflow-feishu
|
|
94
|
+
pm2 restart iflow-feishu
|
|
95
|
+
pm2 stop iflow-feishu
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## 飞书机器人配置
|
|
99
|
+
|
|
100
|
+
1. 访问 [飞书开放平台](https://open.feishu.cn/)
|
|
101
|
+
2. 创建企业自建应用
|
|
102
|
+
3. 开通机器人能力
|
|
103
|
+
4. 配置事件订阅:
|
|
104
|
+
- URL: `http://your-server:18080/webhook`
|
|
105
|
+
- 事件:接收消息
|
|
106
|
+
5. 获取 App ID 和 App Secret
|
|
107
|
+
|
|
55
108
|
## 项目结构
|
|
56
109
|
|
|
57
110
|
```
|
|
@@ -81,67 +134,6 @@ iflow-feishu/
|
|
|
81
134
|
└── README.md
|
|
82
135
|
```
|
|
83
136
|
|
|
84
|
-
## 架构说明
|
|
85
|
-
|
|
86
|
-
### 模块职责
|
|
87
|
-
|
|
88
|
-
| 模块 | 职责 |
|
|
89
|
-
|------|------|
|
|
90
|
-
| service.js | 协调各模块,管理服务生命周期 |
|
|
91
|
-
| message-processor.js | 消息解析、上下文构建 |
|
|
92
|
-
| stream-handler.js | 流式响应处理、token 提取 |
|
|
93
|
-
| http-server.js | HTTP 服务,健康检查,事件回调 |
|
|
94
|
-
| websocket-client.js | 飞书 WebSocket 连接 |
|
|
95
|
-
| feishu-client.js | 飞书 API 封装 |
|
|
96
|
-
| iflow-client.js | iFlow CLI 调用封装 |
|
|
97
|
-
| session.js | 会话持久化存储 |
|
|
98
|
-
| card-builder.js | 飞书卡片构建 |
|
|
99
|
-
| commands.js | 命令处理逻辑 |
|
|
100
|
-
|
|
101
|
-
### 数据流
|
|
102
|
-
|
|
103
|
-
```
|
|
104
|
-
飞书消息 -> WebSocket -> service.handleMessageEvent()
|
|
105
|
-
-> messageProcessor.parseMessage()
|
|
106
|
-
-> commandHandler.handle() 或 processMessage()
|
|
107
|
-
-> iflowClient.execute() (流式)
|
|
108
|
-
-> streamHandler.extractResponse()
|
|
109
|
-
-> feishuClient.updateCardMessage()
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
## 配置选项
|
|
113
|
-
|
|
114
|
-
| 选项 | 说明 | 默认值 |
|
|
115
|
-
|------|------|--------|
|
|
116
|
-
| `feishu.appId` | 飞书应用 ID | - |
|
|
117
|
-
| `feishu.appSecret` | 飞书应用密钥 | - |
|
|
118
|
-
| `server.port` | HTTP 服务端口 | 18080 |
|
|
119
|
-
| `sessions.maxHistory` | 最大历史消息数 | 15 |
|
|
120
|
-
| `iflow.timeout` | CLI 超时时间(ms) | 300000 |
|
|
121
|
-
|
|
122
|
-
## 环境变量
|
|
123
|
-
|
|
124
|
-
| 变量 | 说明 |
|
|
125
|
-
|------|------|
|
|
126
|
-
| `PORT` | 服务端口 |
|
|
127
|
-
| `LOG_LEVEL` | 日志级别 (DEBUG/INFO/WARN/ERROR) |
|
|
128
|
-
|
|
129
|
-
## 常用操作
|
|
130
|
-
|
|
131
|
-
```bash
|
|
132
|
-
# 查看服务状态
|
|
133
|
-
pm2 status iflow-feishu
|
|
134
|
-
|
|
135
|
-
# 查看日志
|
|
136
|
-
pm2 logs iflow-feishu
|
|
137
|
-
|
|
138
|
-
# 重启服务
|
|
139
|
-
pm2 restart iflow-feishu
|
|
140
|
-
|
|
141
|
-
# 健康检查
|
|
142
|
-
curl http://localhost:18080/health
|
|
143
|
-
```
|
|
144
|
-
|
|
145
137
|
## 许可证
|
|
146
138
|
|
|
147
139
|
MIT
|
package/package.json
CHANGED
package/src/config/config.js
CHANGED
|
@@ -1,23 +1,38 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 配置管理模块
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* 负责加载和验证配置,支持交互式配置向导
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
const fs = require('fs');
|
|
8
8
|
const path = require('path');
|
|
9
|
+
const readline = require('readline');
|
|
9
10
|
const { DEFAULT_MODEL, DEFAULT_MAX_TOKENS, MODEL_MAX_TOKENS } = require('../core/constants');
|
|
10
11
|
|
|
12
|
+
const CONFIG_DIR = path.join(process.env.HOME, '.feishu-config');
|
|
13
|
+
const CONFIG_PATH = path.join(CONFIG_DIR, 'feishu-app.json');
|
|
14
|
+
|
|
15
|
+
// 获取版本号
|
|
16
|
+
function getVersion() {
|
|
17
|
+
try {
|
|
18
|
+
const pkgPath = path.join(__dirname, '..', '..', 'package.json');
|
|
19
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
20
|
+
return pkg.version || '1.0.0';
|
|
21
|
+
} catch {
|
|
22
|
+
return '1.0.0';
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const VERSION = getVersion();
|
|
27
|
+
|
|
11
28
|
/**
|
|
12
29
|
* 加载飞书配置
|
|
13
30
|
* @returns {Object|null}
|
|
14
31
|
*/
|
|
15
32
|
function loadFeishuConfig() {
|
|
16
|
-
const configPath = path.join(process.env.HOME, '.feishu-config', 'feishu-app.json');
|
|
17
|
-
|
|
18
33
|
try {
|
|
19
|
-
if (fs.existsSync(
|
|
20
|
-
const config = JSON.parse(fs.readFileSync(
|
|
34
|
+
if (fs.existsSync(CONFIG_PATH)) {
|
|
35
|
+
const config = JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf8'));
|
|
21
36
|
return {
|
|
22
37
|
appId: config.appId || config.app_id,
|
|
23
38
|
appSecret: config.appSecret || config.app_secret
|
|
@@ -30,6 +45,61 @@ function loadFeishuConfig() {
|
|
|
30
45
|
return null;
|
|
31
46
|
}
|
|
32
47
|
|
|
48
|
+
/**
|
|
49
|
+
* 保存飞书配置
|
|
50
|
+
* @param {string} appId
|
|
51
|
+
* @param {string} appSecret
|
|
52
|
+
*/
|
|
53
|
+
function saveFeishuConfig(appId, appSecret) {
|
|
54
|
+
if (!fs.existsSync(CONFIG_DIR)) {
|
|
55
|
+
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const config = { appId, appSecret };
|
|
59
|
+
fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2));
|
|
60
|
+
console.log(`\n✅ 配置已保存到: ${CONFIG_PATH}\n`);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* 配置向导 - 交互式创建配置
|
|
65
|
+
*/
|
|
66
|
+
async function setupWizard() {
|
|
67
|
+
const rl = readline.createInterface({
|
|
68
|
+
input: process.stdin,
|
|
69
|
+
output: process.stdout
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
const question = (prompt) => new Promise((resolve) => {
|
|
73
|
+
rl.question(prompt, resolve);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
console.log('\n╔══════════════════════════════════════╗');
|
|
77
|
+
console.log('║ iFlow Feishu 配置向导 ║');
|
|
78
|
+
console.log('╚══════════════════════════════════════╝\n');
|
|
79
|
+
console.log('请输入飞书机器人凭证(从飞书开放平台获取):');
|
|
80
|
+
console.log('文档: https://open.feishu.cn/document/home/introduction-to-feishu-open-platform\n');
|
|
81
|
+
|
|
82
|
+
const appId = await question('📱 App ID: ');
|
|
83
|
+
if (!appId || appId.trim() === '') {
|
|
84
|
+
console.error('\n❌ App ID 不能为空');
|
|
85
|
+
rl.close();
|
|
86
|
+
process.exit(1);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const appSecret = await question('🔐 App Secret: ');
|
|
90
|
+
if (!appSecret || appSecret.trim() === '') {
|
|
91
|
+
console.error('\n❌ App Secret 不能为空');
|
|
92
|
+
rl.close();
|
|
93
|
+
process.exit(1);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
rl.close();
|
|
97
|
+
|
|
98
|
+
saveFeishuConfig(appId.trim(), appSecret.trim());
|
|
99
|
+
|
|
100
|
+
return { appId: appId.trim(), appSecret: appSecret.trim() };
|
|
101
|
+
}
|
|
102
|
+
|
|
33
103
|
/**
|
|
34
104
|
* 验证配置
|
|
35
105
|
* @param {Object} config - 配置对象
|
|
@@ -51,53 +121,81 @@ function validateConfig(config) {
|
|
|
51
121
|
return true;
|
|
52
122
|
}
|
|
53
123
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
124
|
+
/**
|
|
125
|
+
* 构建完整配置对象
|
|
126
|
+
*/
|
|
127
|
+
function buildConfig(feishuConfig) {
|
|
128
|
+
return {
|
|
129
|
+
version: VERSION,
|
|
130
|
+
feishu: {
|
|
131
|
+
appId: feishuConfig.appId,
|
|
132
|
+
appSecret: feishuConfig.appSecret,
|
|
133
|
+
},
|
|
134
|
+
iflow: {
|
|
135
|
+
command: 'iflow',
|
|
136
|
+
timeout: 300000,
|
|
137
|
+
workDir: process.env.HOME || '/data/data/com.termux/files/home',
|
|
138
|
+
maxTokens: DEFAULT_MAX_TOKENS,
|
|
139
|
+
modelMaxTokens: MODEL_MAX_TOKENS
|
|
140
|
+
},
|
|
141
|
+
server: {
|
|
142
|
+
port: parseInt(process.env.PORT, 10) || 18080,
|
|
143
|
+
host: '0.0.0.0'
|
|
144
|
+
},
|
|
145
|
+
sessions: {
|
|
146
|
+
dir: path.join(process.env.HOME || '/tmp', '.iflow-feishu', 'sessions'),
|
|
147
|
+
maxHistory: 15,
|
|
148
|
+
},
|
|
149
|
+
card: {
|
|
150
|
+
titleFontSize: 'small',
|
|
151
|
+
colors: {
|
|
152
|
+
model: 'blue',
|
|
153
|
+
generating: 'orange',
|
|
154
|
+
completed: 'green'
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
};
|
|
62
158
|
}
|
|
63
159
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
server: {
|
|
78
|
-
port: parseInt(process.env.PORT, 10) || 18080,
|
|
79
|
-
host: '0.0.0.0'
|
|
80
|
-
},
|
|
81
|
-
sessions: {
|
|
82
|
-
dir: path.join(process.env.HOME || '/tmp', '.iflow-feishu', 'sessions'),
|
|
83
|
-
maxHistory: 15,
|
|
84
|
-
},
|
|
85
|
-
card: {
|
|
86
|
-
titleFontSize: 'small',
|
|
87
|
-
colors: {
|
|
88
|
-
model: 'blue',
|
|
89
|
-
generating: 'orange',
|
|
90
|
-
completed: 'green'
|
|
91
|
-
}
|
|
160
|
+
/**
|
|
161
|
+
* 初始化配置(异步,支持交互式向导)
|
|
162
|
+
* @returns {Promise<Object>} 配置对象
|
|
163
|
+
*/
|
|
164
|
+
async function initConfig() {
|
|
165
|
+
let feishuConfig = loadFeishuConfig();
|
|
166
|
+
|
|
167
|
+
// 检查环境变量
|
|
168
|
+
if (!feishuConfig && process.env.FEISHU_APP_ID && process.env.FEISHU_APP_SECRET) {
|
|
169
|
+
feishuConfig = {
|
|
170
|
+
appId: process.env.FEISHU_APP_ID,
|
|
171
|
+
appSecret: process.env.FEISHU_APP_SECRET
|
|
172
|
+
};
|
|
92
173
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
174
|
+
|
|
175
|
+
// 配置缺失,启动向导
|
|
176
|
+
if (!feishuConfig) {
|
|
177
|
+
console.log('\n⚠️ 未找到飞书配置文件');
|
|
178
|
+
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');
|
|
179
|
+
|
|
180
|
+
feishuConfig = await setupWizard();
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const config = buildConfig(feishuConfig);
|
|
184
|
+
|
|
185
|
+
// 验证配置
|
|
186
|
+
validateConfig(config);
|
|
187
|
+
|
|
188
|
+
// 确保会话目录存在
|
|
189
|
+
if (!fs.existsSync(config.sessions.dir)) {
|
|
190
|
+
fs.mkdirSync(config.sessions.dir, { recursive: true });
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return config;
|
|
101
194
|
}
|
|
102
195
|
|
|
103
|
-
module.exports =
|
|
196
|
+
module.exports = {
|
|
197
|
+
initConfig,
|
|
198
|
+
getVersion,
|
|
199
|
+
VERSION,
|
|
200
|
+
CONFIG_PATH
|
|
201
|
+
};
|
package/src/index.js
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* iFlow Feishu
|
|
2
|
+
* iFlow Feishu
|
|
3
3
|
* 将 iFlow CLI 接入飞书机器人
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
const { FeishuService } = require('./core/service');
|
|
7
|
-
const
|
|
7
|
+
const { initConfig, VERSION } = require('./config/config');
|
|
8
8
|
const { logger } = require('./utils/logger');
|
|
9
9
|
|
|
10
10
|
async function main() {
|
|
11
|
-
|
|
11
|
+
// 初始化配置(支持交互式向导)
|
|
12
|
+
const config = await initConfig();
|
|
13
|
+
|
|
14
|
+
console.log(`\n🚀 iFlow Feishu v${VERSION}`);
|
|
12
15
|
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
13
16
|
console.log(`📱 飞书 App ID: ${config.feishu.appId.substring(0, 15)}...`);
|
|
14
17
|
console.log(`🌐 服务端口: ${config.server.port}`);
|