@dingtalk-real-ai/dingtalk-connector 0.7.1 → 0.7.3
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/.github/workflows/ai-fix-and-test.yml +520 -0
- package/.github/workflows/issue-to-dingtalk.yml +60 -0
- package/.github/workflows/pr-review.yml +108 -0
- package/CHANGELOG.md +38 -0
- package/README.md +48 -2
- package/docs/RELEASE_NOTES_V0.7.2.md +143 -0
- package/docs/RELEASE_NOTES_V0.7.3.md +149 -0
- package/openclaw.plugin.json +1 -1
- package/package.json +15 -2
- package/plugin.ts +17 -10
- /package/{RELEASE_NOTES_v0.7.0.md → docs/RELEASE_NOTES_v0.7.0.md} +0 -0
- /package/{RELEASE_NOTES_v0.7.1.md → docs/RELEASE_NOTES_v0.7.1.md} +0 -0
|
@@ -0,0 +1,520 @@
|
|
|
1
|
+
name: 🤖 AI 修复分支与测试
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
issues:
|
|
5
|
+
types: [labeled]
|
|
6
|
+
workflow_dispatch:
|
|
7
|
+
inputs:
|
|
8
|
+
issue_number:
|
|
9
|
+
description: 'Issue 编号'
|
|
10
|
+
required: true
|
|
11
|
+
type: string
|
|
12
|
+
test_mode:
|
|
13
|
+
description: '测试模式'
|
|
14
|
+
required: false
|
|
15
|
+
default: 'local'
|
|
16
|
+
type: choice
|
|
17
|
+
options:
|
|
18
|
+
- local
|
|
19
|
+
- github
|
|
20
|
+
|
|
21
|
+
env:
|
|
22
|
+
DINGTALK_WEBHOOK: https://oapi.dingtalk.com/robot/send?access_token=8710dbbfa8b8b63a02ddfe41ae428b838d2f5e998e0ec446196981d97e7d5350
|
|
23
|
+
DINGTALK_KEYWORD: dingtalk-openclaw-connector
|
|
24
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
25
|
+
|
|
26
|
+
jobs:
|
|
27
|
+
# 检查是否需要执行 AI 修复
|
|
28
|
+
check-trigger:
|
|
29
|
+
runs-on: ubuntu-latest
|
|
30
|
+
outputs:
|
|
31
|
+
should_run: ${{ steps.check.outputs.should_run }}
|
|
32
|
+
issue_number: ${{ steps.check.outputs.issue_number }}
|
|
33
|
+
issue_title: ${{ steps.check.outputs.issue_title }}
|
|
34
|
+
issue_body: ${{ steps.check.outputs.issue_body }}
|
|
35
|
+
steps:
|
|
36
|
+
- name: 检查触发条件
|
|
37
|
+
id: check
|
|
38
|
+
uses: actions/github-script@v7
|
|
39
|
+
with:
|
|
40
|
+
script: |
|
|
41
|
+
let issueNumber, issueTitle, issueBody;
|
|
42
|
+
|
|
43
|
+
// 判断触发方式
|
|
44
|
+
if (context.eventName === 'workflow_dispatch') {
|
|
45
|
+
// 手动触发
|
|
46
|
+
issueNumber = context.payload.inputs.issue_number;
|
|
47
|
+
|
|
48
|
+
// 获取 Issue 信息
|
|
49
|
+
const { data: issue } = await github.rest.issues.get({
|
|
50
|
+
owner: context.repo.owner,
|
|
51
|
+
repo: context.repo.repo,
|
|
52
|
+
issue_number: parseInt(issueNumber)
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
issueTitle = issue.title;
|
|
56
|
+
issueBody = issue.body || '';
|
|
57
|
+
|
|
58
|
+
} else if (context.eventName === 'issues') {
|
|
59
|
+
// Issue 标签触发
|
|
60
|
+
if (context.payload.label?.name !== 'ai-fix') {
|
|
61
|
+
console.log('标签不是 ai-fix,跳过');
|
|
62
|
+
core.setOutput('should_run', 'false');
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
issueNumber = context.payload.issue.number;
|
|
67
|
+
issueTitle = context.payload.issue.title;
|
|
68
|
+
issueBody = context.payload.issue.body || '';
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
core.setOutput('should_run', 'true');
|
|
72
|
+
core.setOutput('issue_number', issueNumber);
|
|
73
|
+
core.setOutput('issue_title', issueTitle);
|
|
74
|
+
core.setOutput('issue_body', issueBody);
|
|
75
|
+
|
|
76
|
+
console.log(`✅ 触发 AI 修复流程 - Issue #${issueNumber}`);
|
|
77
|
+
|
|
78
|
+
# 创建修复分支
|
|
79
|
+
create-branch:
|
|
80
|
+
needs: check-trigger
|
|
81
|
+
runs-on: ubuntu-latest
|
|
82
|
+
if: needs.check-trigger.outputs.should_run == 'true'
|
|
83
|
+
outputs:
|
|
84
|
+
branch_name: ${{ steps.branch.outputs.branch_name }}
|
|
85
|
+
steps:
|
|
86
|
+
- name: 检出代码
|
|
87
|
+
uses: actions/checkout@v4
|
|
88
|
+
with:
|
|
89
|
+
fetch-depth: 0
|
|
90
|
+
token: ${{ secrets.GITHUB_TOKEN }}
|
|
91
|
+
|
|
92
|
+
- name: 创建修复分支
|
|
93
|
+
id: branch
|
|
94
|
+
run: |
|
|
95
|
+
ISSUE_NUMBER=${{ needs.check-trigger.outputs.issue_number }}
|
|
96
|
+
ISSUE_TITLE="${{ needs.check-trigger.outputs.issue_title }}"
|
|
97
|
+
|
|
98
|
+
# 生成安全的分支名
|
|
99
|
+
SAFE_TITLE=$(echo "$ISSUE_TITLE" | tr '[:upper:]' '[:lower:]' | tr -c 'a-z0-9' '-' | sed 's/-*$//' | cut -c1-50)
|
|
100
|
+
BRANCH_NAME="ai-fix/issue-${ISSUE_NUMBER}-${SAFE_TITLE}"
|
|
101
|
+
|
|
102
|
+
echo "branch_name=$BRANCH_NAME" >> $GITHUB_OUTPUT
|
|
103
|
+
echo "🌿 创建分支: $BRANCH_NAME"
|
|
104
|
+
|
|
105
|
+
# 配置 Git
|
|
106
|
+
git config user.name "github-actions[bot]"
|
|
107
|
+
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
108
|
+
|
|
109
|
+
# 创建并推送分支
|
|
110
|
+
git checkout -b "$BRANCH_NAME"
|
|
111
|
+
git push origin "$BRANCH_NAME"
|
|
112
|
+
|
|
113
|
+
echo "✅ 分支已创建: $BRANCH_NAME"
|
|
114
|
+
|
|
115
|
+
# 本地 AI 修复(通过 webhook 调用本地 OpenClaw)
|
|
116
|
+
ai-fix-local:
|
|
117
|
+
needs: [check-trigger, create-branch]
|
|
118
|
+
runs-on: ubuntu-latest
|
|
119
|
+
if: github.event.inputs.test_mode != 'github'
|
|
120
|
+
steps:
|
|
121
|
+
- name: 调用本地 AI 修复服务
|
|
122
|
+
uses: actions/github-script@v7
|
|
123
|
+
with:
|
|
124
|
+
script: |
|
|
125
|
+
const issueNumber = '${{ needs.check-trigger.outputs.issue_number }}';
|
|
126
|
+
const issueTitle = '${{ needs.check-trigger.outputs.issue_title }}';
|
|
127
|
+
const issueBody = `${{ needs.check-trigger.outputs.issue_body }}`;
|
|
128
|
+
const branchName = '${{ needs.create-branch.outputs.branch_name }}';
|
|
129
|
+
|
|
130
|
+
// 构建调用本地 OpenClaw 的 payload
|
|
131
|
+
const payload = {
|
|
132
|
+
action: 'ai-fix-issue',
|
|
133
|
+
issue: {
|
|
134
|
+
number: issueNumber,
|
|
135
|
+
title: issueTitle,
|
|
136
|
+
body: issueBody
|
|
137
|
+
},
|
|
138
|
+
branch: branchName,
|
|
139
|
+
repo: {
|
|
140
|
+
owner: context.repo.owner,
|
|
141
|
+
name: context.repo.repo
|
|
142
|
+
},
|
|
143
|
+
callback_url: `https://api.github.com/repos/${context.repo.owner}/${context.repo.repo}/issues/${issueNumber}/comments`
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
console.log('📤 发送 AI 修复请求到本地服务...');
|
|
147
|
+
console.log('Payload:', JSON.stringify(payload, null, 2));
|
|
148
|
+
|
|
149
|
+
// 这里可以通过 webhook 或 API 调用本地服务
|
|
150
|
+
// 实际实现需要根据你的本地 OpenClaw 部署方式调整
|
|
151
|
+
|
|
152
|
+
// 在 Issue 中添加等待评论
|
|
153
|
+
await github.rest.issues.createComment({
|
|
154
|
+
owner: context.repo.owner,
|
|
155
|
+
repo: context.repo.repo,
|
|
156
|
+
issue_number: parseInt(issueNumber),
|
|
157
|
+
body: `⏳ **AI 修复进行中**\n\n分支 \`${branchName}\` 已创建\n\n已发送请求到本地 AI 服务,等待修复结果...\n\n**提示:** 本地 AI 修复完成后,测试结果将自动同步到钉钉群。`
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
# GitHub 环境 AI 修复(使用 GitHub Actions 运行器)
|
|
161
|
+
ai-fix-github:
|
|
162
|
+
needs: [check-trigger, create-branch]
|
|
163
|
+
runs-on: ubuntu-latest
|
|
164
|
+
# 标签触发时自动执行;手动触发时根据 test_mode 选择
|
|
165
|
+
if: |
|
|
166
|
+
github.event_name == 'issues' ||
|
|
167
|
+
github.event.inputs.test_mode == 'github' ||
|
|
168
|
+
(github.event_name == 'workflow_dispatch' && github.event.inputs.test_mode == 'github')
|
|
169
|
+
outputs:
|
|
170
|
+
lint_status: ${{ steps.lint.outputs.lint_status }}
|
|
171
|
+
typecheck_status: ${{ steps.typecheck.outputs.typecheck_status }}
|
|
172
|
+
test_result: ${{ steps.test.outputs.test_result }}
|
|
173
|
+
steps:
|
|
174
|
+
- name: 检出修复分支
|
|
175
|
+
uses: actions/checkout@v4
|
|
176
|
+
with:
|
|
177
|
+
ref: ${{ needs.create-branch.outputs.branch_name }}
|
|
178
|
+
fetch-depth: 0
|
|
179
|
+
|
|
180
|
+
- name: 设置 Node.js
|
|
181
|
+
uses: actions/setup-node@v4
|
|
182
|
+
with:
|
|
183
|
+
node-version: '22'
|
|
184
|
+
cache: 'npm'
|
|
185
|
+
|
|
186
|
+
- name: 安装依赖
|
|
187
|
+
run: npm ci
|
|
188
|
+
|
|
189
|
+
- name: 分析 Issue 并调用 AI 生成修复
|
|
190
|
+
id: ai-analysis
|
|
191
|
+
env:
|
|
192
|
+
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
|
193
|
+
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
194
|
+
DASHSCOPE_API_KEY: ${{ secrets.DASHSCOPE_API_KEY }}
|
|
195
|
+
run: |
|
|
196
|
+
echo "🤖 分析 Issue 内容..."
|
|
197
|
+
echo "Issue #${{ needs.check-trigger.outputs.issue_number }}: ${{ needs.check-trigger.outputs.issue_title }}"
|
|
198
|
+
|
|
199
|
+
# 创建修复标记文件(开始分析)
|
|
200
|
+
cat > .ai-fix-info.json << EOF
|
|
201
|
+
{
|
|
202
|
+
"issue_number": ${{ needs.check-trigger.outputs.issue_number }},
|
|
203
|
+
"issue_title": "${{ needs.check-trigger.outputs.issue_title }}",
|
|
204
|
+
"fix_timestamp": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
|
|
205
|
+
"fix_method": "github-actions-ai",
|
|
206
|
+
"status": "analyzing"
|
|
207
|
+
}
|
|
208
|
+
EOF
|
|
209
|
+
|
|
210
|
+
# 检查是否有 AI API Key
|
|
211
|
+
if [ -n "$OPENAI_API_KEY" ] || [ -n "$ANTHROPIC_API_KEY" ] || [ -n "$DASHSCOPE_API_KEY" ]; then
|
|
212
|
+
echo "✅ 发现 AI API Key,将尝试调用 AI 模型生成修复..."
|
|
213
|
+
|
|
214
|
+
# 准备 Issue 内容
|
|
215
|
+
ISSUE_TITLE="${{ needs.check-trigger.outputs.issue_title }}"
|
|
216
|
+
ISSUE_BODY='${{ needs.check-trigger.outputs.issue_body }}'
|
|
217
|
+
|
|
218
|
+
# 创建 AI 提示词
|
|
219
|
+
cat > /tmp/ai-prompt.txt << 'PROMPT'
|
|
220
|
+
你是一个专业的代码修复助手。请根据下面的 GitHub Issue 描述,分析代码问题并提供修复建议。
|
|
221
|
+
|
|
222
|
+
Issue 标题: $ISSUE_TITLE
|
|
223
|
+
Issue 描述: $ISSUE_BODY
|
|
224
|
+
|
|
225
|
+
请分析:
|
|
226
|
+
1. 问题的根本原因
|
|
227
|
+
2. 需要修改的文件
|
|
228
|
+
3. 具体的修复方案
|
|
229
|
+
|
|
230
|
+
请以结构化的方式输出分析结果。
|
|
231
|
+
PROMPT
|
|
232
|
+
|
|
233
|
+
# 尝试调用 OpenAI API(如果配置了)
|
|
234
|
+
if [ -n "$OPENAI_API_KEY" ]; then
|
|
235
|
+
echo "🔄 调用 OpenAI API..."
|
|
236
|
+
curl -s -X POST https://api.openai.com/v1/chat/completions \
|
|
237
|
+
-H "Authorization: Bearer $OPENAI_API_KEY" \
|
|
238
|
+
-H "Content-Type: application/json" \
|
|
239
|
+
-d '{
|
|
240
|
+
"model": "gpt-4o-mini",
|
|
241
|
+
"messages": [
|
|
242
|
+
{"role": "system", "content": "你是一个专业的代码修复助手。请分析 GitHub Issue 并提供修复建议。"},
|
|
243
|
+
{"role": "user", "content": "Issue 标题: '"$ISSUE_TITLE"'\n\nIssue 描述: '"$ISSUE_BODY"'\n\n请分析问题原因并提供修复方案。"}
|
|
244
|
+
],
|
|
245
|
+
"temperature": 0.3
|
|
246
|
+
}' > /tmp/ai-response.json 2>/dev/null || echo "⚠️ OpenAI API 调用失败"
|
|
247
|
+
|
|
248
|
+
if [ -f /tmp/ai-response.json ] && grep -q '"choices"' /tmp/ai-response.json; then
|
|
249
|
+
echo "✅ OpenAI 分析完成"
|
|
250
|
+
cat /tmp/ai-response.json | jq -r '.choices[0].message.content' > /tmp/ai-analysis.md || true
|
|
251
|
+
fi
|
|
252
|
+
fi
|
|
253
|
+
|
|
254
|
+
# 尝试调用 Anthropic API(如果配置了且 OpenAI 失败)
|
|
255
|
+
if [ -n "$ANTHROPIC_API_KEY" ] && [ ! -f /tmp/ai-analysis.md ]; then
|
|
256
|
+
echo "🔄 调用 Anthropic API..."
|
|
257
|
+
curl -s -X POST https://api.anthropic.com/v1/messages \
|
|
258
|
+
-H "x-api-key: $ANTHROPIC_API_KEY" \
|
|
259
|
+
-H "Content-Type: application/json" \
|
|
260
|
+
-H "anthropic-version: 2023-06-01" \
|
|
261
|
+
-d '{
|
|
262
|
+
"model": "claude-3-haiku-20240307",
|
|
263
|
+
"max_tokens": 2000,
|
|
264
|
+
"messages": [
|
|
265
|
+
{"role": "user", "content": "Issue 标题: '"$ISSUE_TITLE"'\n\nIssue 描述: '"$ISSUE_BODY"'\n\n请分析问题原因并提供修复方案。"}
|
|
266
|
+
]
|
|
267
|
+
}' > /tmp/ai-response.json 2>/dev/null || echo "⚠️ Anthropic API 调用失败"
|
|
268
|
+
|
|
269
|
+
if [ -f /tmp/ai-response.json ] && grep -q '"content"' /tmp/ai-response.json; then
|
|
270
|
+
echo "✅ Anthropic 分析完成"
|
|
271
|
+
cat /tmp/ai-response.json | jq -r '.content[0].text' > /tmp/ai-analysis.md || true
|
|
272
|
+
fi
|
|
273
|
+
fi
|
|
274
|
+
|
|
275
|
+
# 尝试调用阿里云百炼 Qwen3.5 Plus API(如果配置了且前面的都失败)
|
|
276
|
+
if [ -n "$DASHSCOPE_API_KEY" ] && [ ! -f /tmp/ai-analysis.md ]; then
|
|
277
|
+
echo "🔄 调用阿里云百炼 Qwen3.5 Plus API..."
|
|
278
|
+
curl -s -X POST https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions \
|
|
279
|
+
-H "Authorization: Bearer $DASHSCOPE_API_KEY" \
|
|
280
|
+
-H "Content-Type: application/json" \
|
|
281
|
+
-d '{
|
|
282
|
+
"model": "qwen3.5-plus",
|
|
283
|
+
"messages": [
|
|
284
|
+
{"role": "system", "content": "你是一个专业的代码修复助手。请分析 GitHub Issue 并提供修复建议。"},
|
|
285
|
+
{"role": "user", "content": "Issue 标题: '"$ISSUE_TITLE"'\n\nIssue 描述: '"$ISSUE_BODY"'\n\n请分析问题原因并提供修复方案。"}
|
|
286
|
+
],
|
|
287
|
+
"temperature": 0.3
|
|
288
|
+
}' > /tmp/ai-response.json 2>/dev/null || echo "⚠️ Qwen3.5 Plus API 调用失败"
|
|
289
|
+
|
|
290
|
+
if [ -f /tmp/ai-response.json ] && grep -q '"choices"' /tmp/ai-response.json; then
|
|
291
|
+
echo "✅ Qwen3.5 Plus 分析完成"
|
|
292
|
+
cat /tmp/ai-response.json | jq -r '.choices[0].message.content' > /tmp/ai-analysis.md || true
|
|
293
|
+
fi
|
|
294
|
+
fi
|
|
295
|
+
|
|
296
|
+
# 保存 AI 分析结果
|
|
297
|
+
if [ -f /tmp/ai-analysis.md ]; then
|
|
298
|
+
echo "📝 AI 分析结果:"
|
|
299
|
+
cat /tmp/ai-analysis.md
|
|
300
|
+
cp /tmp/ai-analysis.md .ai-fix-analysis.md
|
|
301
|
+
else
|
|
302
|
+
echo "⚠️ AI 分析未完成,将使用默认分析流程"
|
|
303
|
+
echo "AI 分析暂不可用,请手动查看 Issue 并修复。" > .ai-fix-analysis.md
|
|
304
|
+
fi
|
|
305
|
+
else
|
|
306
|
+
echo "⚠️ 未配置 AI API Key (OPENAI_API_KEY、ANTHROPIC_API_KEY 或 DASHSCOPE_API_KEY)"
|
|
307
|
+
echo "请配置 Secrets 后重新运行,或手动修复代码。"
|
|
308
|
+
echo "未配置 AI API Key,请手动修复。" > .ai-fix-analysis.md
|
|
309
|
+
fi
|
|
310
|
+
|
|
311
|
+
echo "analysis_complete=true" >> $GITHUB_OUTPUT
|
|
312
|
+
|
|
313
|
+
- name: 运行代码检查
|
|
314
|
+
id: lint
|
|
315
|
+
continue-on-error: true
|
|
316
|
+
run: |
|
|
317
|
+
echo "🔍 运行代码检查..."
|
|
318
|
+
npm run lint 2>&1 | tee lint-output.txt || true
|
|
319
|
+
echo "lint_status=$?" >> $GITHUB_OUTPUT
|
|
320
|
+
|
|
321
|
+
- name: 运行类型检查
|
|
322
|
+
id: typecheck
|
|
323
|
+
continue-on-error: true
|
|
324
|
+
run: |
|
|
325
|
+
echo "🔍 运行类型检查..."
|
|
326
|
+
npm run type-check 2>&1 | tee typecheck-output.txt || true
|
|
327
|
+
echo "typecheck_status=$?" >> $GITHUB_OUTPUT
|
|
328
|
+
|
|
329
|
+
- name: 运行测试
|
|
330
|
+
id: test
|
|
331
|
+
continue-on-error: true
|
|
332
|
+
run: |
|
|
333
|
+
echo "🧪 运行自动化测试..."
|
|
334
|
+
npm test 2>&1 | tee test-output.txt || true
|
|
335
|
+
echo "test_status=$?" >> $GITHUB_OUTPUT
|
|
336
|
+
|
|
337
|
+
# 提取测试结果摘要
|
|
338
|
+
if grep -q "PASS\|✓" test-output.txt; then
|
|
339
|
+
echo "test_result=passed" >> $GITHUB_OUTPUT
|
|
340
|
+
else
|
|
341
|
+
echo "test_result=failed" >> $GITHUB_OUTPUT
|
|
342
|
+
fi
|
|
343
|
+
|
|
344
|
+
- name: 提交修复结果
|
|
345
|
+
run: |
|
|
346
|
+
git config user.name "github-actions[bot]"
|
|
347
|
+
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
348
|
+
|
|
349
|
+
# 更新修复状态
|
|
350
|
+
cat > .ai-fix-info.json << EOF
|
|
351
|
+
{
|
|
352
|
+
"issue_number": ${{ needs.check-trigger.outputs.issue_number }},
|
|
353
|
+
"issue_title": "${{ needs.check-trigger.outputs.issue_title }}",
|
|
354
|
+
"fix_timestamp": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
|
|
355
|
+
"fix_method": "github-actions-ai",
|
|
356
|
+
"status": "completed",
|
|
357
|
+
"test_results": {
|
|
358
|
+
"lint": "${{ steps.lint.outputs.lint_status == '0' && 'passed' || 'failed' }}",
|
|
359
|
+
"typecheck": "${{ steps.typecheck.outputs.typecheck_status == '0' && 'passed' || 'failed' }}",
|
|
360
|
+
"test": "${{ steps.test.outputs.test_result }}"
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
EOF
|
|
364
|
+
|
|
365
|
+
# 添加 AI 分析结果(如果存在)
|
|
366
|
+
if [ -f .ai-fix-analysis.md ]; then
|
|
367
|
+
git add .ai-fix-analysis.md
|
|
368
|
+
fi
|
|
369
|
+
|
|
370
|
+
git add .ai-fix-info.json
|
|
371
|
+
git commit -m "🤖 AI 修复: Issue #${{ needs.check-trigger.outputs.issue_number }} - 自动修复结果" || echo "无变更需要提交"
|
|
372
|
+
git push origin ${{ needs.create-branch.outputs.branch_name }}
|
|
373
|
+
|
|
374
|
+
# 创建 PR
|
|
375
|
+
create-pr:
|
|
376
|
+
needs: [check-trigger, create-branch, ai-fix-github]
|
|
377
|
+
runs-on: ubuntu-latest
|
|
378
|
+
if: always() && (needs.ai-fix-github.result == 'success' || needs.ai-fix-github.result == 'skipped')
|
|
379
|
+
steps:
|
|
380
|
+
- name: 创建 Pull Request
|
|
381
|
+
uses: actions/github-script@v7
|
|
382
|
+
with:
|
|
383
|
+
script: |
|
|
384
|
+
const issueNumber = parseInt('${{ needs.check-trigger.outputs.issue_number }}');
|
|
385
|
+
const branchName = '${{ needs.create-branch.outputs.branch_name }}';
|
|
386
|
+
|
|
387
|
+
try {
|
|
388
|
+
const { data: pr } = await github.rest.pulls.create({
|
|
389
|
+
owner: context.repo.owner,
|
|
390
|
+
repo: context.repo.repo,
|
|
391
|
+
title: `🤖 AI 修复: ${context.payload.issue?.title || `Issue #${issueNumber}`}`,
|
|
392
|
+
head: branchName,
|
|
393
|
+
base: 'main',
|
|
394
|
+
body: `## 🤖 AI 自动修复\n\n**关联 Issue:** #${issueNumber}\n\n**修复分支:** \`${branchName}\`\n\n**修复说明:**\n- 由 GitHub Actions 自动生成的修复\n- 基于 Issue 描述进行代码分析和修复\n- 包含自动化测试结果\n\n**测试状态:**\n- 代码检查: ${{ needs.ai-fix-github.outputs.lint_status == '0' && '✅ 通过' || '⚠️ 需关注' }}\n- 类型检查: ${{ needs.ai-fix-github.outputs.typecheck_status == '0' && '✅ 通过' || '⚠️ 需关注' }}\n- 单元测试: ${{ needs.ai-fix-github.outputs.test_result == 'passed' && '✅ 通过' || '⚠️ 需关注' }}\n\n**需要人工审核:**\n- [ ] 代码逻辑正确性\n- [ ] 边界情况处理\n- [ ] 性能影响评估\n\n---\n*此 PR 由 AI 修复工作流自动生成*`,
|
|
395
|
+
draft: false
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
console.log(`✅ PR 创建成功: #${pr.number}`);
|
|
399
|
+
core.setOutput('pr_number', pr.number);
|
|
400
|
+
core.setOutput('pr_url', pr.html_url);
|
|
401
|
+
|
|
402
|
+
} catch (error) {
|
|
403
|
+
console.error('创建 PR 失败:', error.message);
|
|
404
|
+
// PR 可能已存在,查找现有 PR
|
|
405
|
+
const { data: prs } = await github.rest.pulls.list({
|
|
406
|
+
owner: context.repo.owner,
|
|
407
|
+
repo: context.repo.repo,
|
|
408
|
+
head: `${context.repo.owner}:${branchName}`,
|
|
409
|
+
base: 'main',
|
|
410
|
+
state: 'open'
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
if (prs.length > 0) {
|
|
414
|
+
console.log(`ℹ️ PR 已存在: #${prs[0].number}`);
|
|
415
|
+
core.setOutput('pr_number', prs[0].number);
|
|
416
|
+
core.setOutput('pr_url', prs[0].html_url);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
# 发送测试结果到钉钉
|
|
421
|
+
notify-result:
|
|
422
|
+
needs: [check-trigger, create-branch, ai-fix-github, create-pr]
|
|
423
|
+
runs-on: ubuntu-latest
|
|
424
|
+
if: always()
|
|
425
|
+
outputs:
|
|
426
|
+
notified: ${{ steps.notify.outputs.notified }}
|
|
427
|
+
steps:
|
|
428
|
+
- name: 发送测试结果到钉钉
|
|
429
|
+
id: notify
|
|
430
|
+
uses: actions/github-script@v7
|
|
431
|
+
with:
|
|
432
|
+
script: |
|
|
433
|
+
const issueNumber = '${{ needs.check-trigger.outputs.issue_number }}';
|
|
434
|
+
const issueTitle = '${{ needs.check-trigger.outputs.issue_title }}';
|
|
435
|
+
const branchName = '${{ needs.create-branch.outputs.branch_name }}';
|
|
436
|
+
|
|
437
|
+
// 确定整体状态(处理 ai-fix-github 被跳过的情况)
|
|
438
|
+
const lintStatus = '${{ needs.ai-fix-github.outputs.lint_status }}';
|
|
439
|
+
const typecheckStatus = '${{ needs.ai-fix-github.outputs.typecheck_status }}';
|
|
440
|
+
const testResult = '${{ needs.ai-fix-github.outputs.test_result }}';
|
|
441
|
+
|
|
442
|
+
// 判断执行模式
|
|
443
|
+
const isLocalMode = '${{ github.event.inputs.test_mode }}' === 'local' && '${{ github.event_name }}' === 'workflow_dispatch';
|
|
444
|
+
const isLabelTrigger = '${{ github.event_name }}' === 'issues';
|
|
445
|
+
|
|
446
|
+
let allPassed, statusEmoji, statusText, testResultsText;
|
|
447
|
+
|
|
448
|
+
if (isLocalMode && lintStatus === '') {
|
|
449
|
+
// Local 模式:GitHub Actions 未执行测试
|
|
450
|
+
statusEmoji = '⏳';
|
|
451
|
+
statusText = '等待本地 AI 修复';
|
|
452
|
+
testResultsText = '- 📝 代码检查: ⏳ 等待本地执行\n- 🔍 类型检查: ⏳ 等待本地执行\n- 🧪 单元测试: ⏳ 等待本地执行';
|
|
453
|
+
allPassed = false;
|
|
454
|
+
} else {
|
|
455
|
+
// GitHub 模式(标签触发或手动选择 github 模式):显示实际测试结果
|
|
456
|
+
allPassed = lintStatus === '0' && typecheckStatus === '0' && testResult === 'passed';
|
|
457
|
+
statusEmoji = allPassed ? '✅' : '⚠️';
|
|
458
|
+
statusText = allPassed ? '测试通过' : '需要关注';
|
|
459
|
+
testResultsText = `- 📝 代码检查: ${lintStatus === '0' ? '✅ 通过' : '❌ 失败'}\n- 🔍 类型检查: ${typecheckStatus === '0' ? '✅ 通过' : '❌ 失败'}\n- 🧪 单元测试: ${testResult === 'passed' ? '✅ 通过' : '❌ 失败'}`;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
// 构建钉钉消息
|
|
463
|
+
const keyword = process.env.DINGTALK_KEYWORD;
|
|
464
|
+
const message = {
|
|
465
|
+
msgtype: 'markdown',
|
|
466
|
+
markdown: {
|
|
467
|
+
title: `${keyword} - ${statusEmoji} AI 修复测试完成`,
|
|
468
|
+
text: `## ${statusEmoji} AI 修复测试${statusText}\n\n${keyword}\n\n**Issue:** [#${issueNumber} ${issueTitle}](https://github.com/${context.repo.owner}/${context.repo.repo}/issues/${issueNumber})\n\n**修复分支:** \`${branchName}\`\n\n**测试结果:**\n${testResultsText}\n\n**下一步:**\n${allPassed ? '✅ 所有测试通过,请审核 PR 后合并' : (lintStatus === '' ? '⏳ 本地 AI 修复完成后,请手动运行测试' : '⚠️ 部分测试未通过,请查看详细日志')}\n\n---\n⏰ ${new Date().toLocaleString('zh-CN')}`
|
|
469
|
+
}
|
|
470
|
+
};
|
|
471
|
+
|
|
472
|
+
// 发送钉钉通知
|
|
473
|
+
const axios = require('axios');
|
|
474
|
+
try {
|
|
475
|
+
await axios.post(process.env.DINGTALK_WEBHOOK, message);
|
|
476
|
+
console.log('✅ 钉钉通知发送成功');
|
|
477
|
+
core.setOutput('notified', 'true');
|
|
478
|
+
} catch (error) {
|
|
479
|
+
console.error('❌ 钉钉通知发送失败:', error.message);
|
|
480
|
+
core.setOutput('notified', 'false');
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
# 更新 Issue 状态
|
|
484
|
+
update-issue:
|
|
485
|
+
needs: [check-trigger, create-branch, ai-fix-github, create-pr, notify-result]
|
|
486
|
+
runs-on: ubuntu-latest
|
|
487
|
+
if: always()
|
|
488
|
+
steps:
|
|
489
|
+
- name: 更新 Issue 评论
|
|
490
|
+
uses: actions/github-script@v7
|
|
491
|
+
with:
|
|
492
|
+
script: |
|
|
493
|
+
const issueNumber = parseInt('${{ needs.check-trigger.outputs.issue_number }}');
|
|
494
|
+
const branchName = '${{ needs.create-branch.outputs.branch_name }}';
|
|
495
|
+
|
|
496
|
+
const lintStatus = '${{ needs.ai-fix-github.outputs.lint_status }}';
|
|
497
|
+
const typecheckStatus = '${{ needs.ai-fix-github.outputs.typecheck_status }}';
|
|
498
|
+
const testResult = '${{ needs.ai-fix-github.outputs.test_result }}';
|
|
499
|
+
|
|
500
|
+
// 判断执行模式
|
|
501
|
+
const isLocalMode = '${{ github.event.inputs.test_mode }}' === 'local' && '${{ github.event_name }}' === 'workflow_dispatch';
|
|
502
|
+
const isLabelTrigger = '${{ github.event_name }}' === 'issues';
|
|
503
|
+
|
|
504
|
+
let comment = '';
|
|
505
|
+
|
|
506
|
+
if (isLocalMode && lintStatus === '') {
|
|
507
|
+
// Local 模式:GitHub Actions 未执行测试
|
|
508
|
+
comment = `⏳ **AI 修复分支已创建**\n\n**修复分支:** \`${branchName}\`\n\n已触发本地 AI 修复流程。\n\n**注意:** 本地 AI 修复完成后,请手动推送代码并创建 PR。\n\n钉钉群已收到通知,可在群内查看进度。`;
|
|
509
|
+
} else if (lintStatus === '0' && typecheckStatus === '0' && testResult === 'passed') {
|
|
510
|
+
comment = `✅ **AI 修复完成**\n\n所有测试已通过!\n\n**修复分支:** \`${branchName}\`\n\n**测试结果:**\n- 📝 代码检查: ✅ 通过\n- 🔍 类型检查: ✅ 通过\n- 🧪 单元测试: ✅ 通过\n\n已创建 PR,请审核后合并。`;
|
|
511
|
+
} else {
|
|
512
|
+
comment = `⚠️ **AI 修复完成(需关注)**\n\n**修复分支:** \`${branchName}\`\n\n**测试结果:**\n- 📝 代码检查: ${lintStatus === '0' ? '✅ 通过' : '❌ 失败'}\n- 🔍 类型检查: ${typecheckStatus === '0' ? '✅ 通过' : '❌ 失败'}\n- 🧪 单元测试: ${testResult === 'passed' ? '✅ 通过' : '❌ 失败'}\n\n部分测试未通过,请查看详细日志并手动调整。`;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
await github.rest.issues.createComment({
|
|
516
|
+
owner: context.repo.owner,
|
|
517
|
+
repo: context.repo.repo,
|
|
518
|
+
issue_number: issueNumber,
|
|
519
|
+
body: comment
|
|
520
|
+
});
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Issue 自动同步到钉钉群
|
|
2
|
+
# 当有新 Issue 或 Issue 评论时,自动发送到钉钉群
|
|
3
|
+
name: 🔔 Issue Notification to DingTalk
|
|
4
|
+
|
|
5
|
+
on:
|
|
6
|
+
issues:
|
|
7
|
+
types: [opened, reopened, closed, labeled]
|
|
8
|
+
issue_comment:
|
|
9
|
+
types: [created]
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
notify:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
steps:
|
|
15
|
+
- name: 📬 Send Issue to DingTalk
|
|
16
|
+
uses: actions/github-script@v7
|
|
17
|
+
with:
|
|
18
|
+
script: |
|
|
19
|
+
const webhook = process.env.DINGTALK_WEBHOOK;
|
|
20
|
+
if (!webhook) {
|
|
21
|
+
console.log('⚠️ DINGTALK_WEBHOOK not set, skipping notification');
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const event = context.eventName;
|
|
26
|
+
const payload = context.payload;
|
|
27
|
+
let title, content, url, action;
|
|
28
|
+
|
|
29
|
+
if (event === 'issues') {
|
|
30
|
+
const issue = payload.issue;
|
|
31
|
+
action = payload.action;
|
|
32
|
+
title = `[${action.toUpperCase()}] Issue #${issue.number}: ${issue.title}`;
|
|
33
|
+
content = issue.body?.substring(0, 500) || 'No description';
|
|
34
|
+
url = issue.html_url;
|
|
35
|
+
} else if (event === 'issue_comment') {
|
|
36
|
+
const comment = payload.comment;
|
|
37
|
+
const issue = payload.issue;
|
|
38
|
+
action = 'commented';
|
|
39
|
+
title = `[COMMENT] Issue #${issue.number}: ${issue.title}`;
|
|
40
|
+
content = comment.body?.substring(0, 500) || 'No content';
|
|
41
|
+
url = comment.html_url;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const message = {
|
|
45
|
+
msgtype: 'markdown',
|
|
46
|
+
markdown: {
|
|
47
|
+
title: 'GitHub Issue 通知',
|
|
48
|
+
text: `## 🔔 GitHub Issue 通知\n\n**${title}**\n\n${content}${content.length >= 500 ? '...' : ''}\n\n[点击查看详情](${url})\n\n---\n📦 ${context.repo.owner}/${context.repo.repo}`
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
await fetch(webhook, {
|
|
53
|
+
method: 'POST',
|
|
54
|
+
headers: { 'Content-Type': 'application/json' },
|
|
55
|
+
body: JSON.stringify(message)
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
console.log('✅ DingTalk notification sent');
|
|
59
|
+
env:
|
|
60
|
+
DINGTALK_WEBHOOK: ${{ secrets.DINGTALK_WEBHOOK }}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# PR 审核提醒工作流
|
|
2
|
+
# PR 创建/更新时自动通知钉钉群,提醒审核
|
|
3
|
+
name: 👀 PR Review Request
|
|
4
|
+
|
|
5
|
+
on:
|
|
6
|
+
pull_request:
|
|
7
|
+
types: [opened, reopened, ready_for_review]
|
|
8
|
+
pull_request_review:
|
|
9
|
+
types: [submitted]
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
notify-review-request:
|
|
13
|
+
if: github.event_name == 'pull_request' && github.event.action != 'closed'
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
steps:
|
|
16
|
+
- name: 📢 Notify DingTalk - Review Request
|
|
17
|
+
if: env.DINGTALK_WEBHOOK != ''
|
|
18
|
+
run: |
|
|
19
|
+
PR_TITLE="${{ github.event.pull_request.title }}"
|
|
20
|
+
PR_URL="${{ github.event.pull_request.html_url }}"
|
|
21
|
+
PR_AUTHOR="${{ github.event.pull_request.user.login }}"
|
|
22
|
+
PR_NUMBER="${{ github.event.pull_request.number }}"
|
|
23
|
+
|
|
24
|
+
curl -X POST "${{ secrets.DINGTALK_WEBHOOK }}" \
|
|
25
|
+
-H "Content-Type: application/json" \
|
|
26
|
+
-d "{
|
|
27
|
+
\"msgtype\": \"markdown\",
|
|
28
|
+
\"markdown\": {
|
|
29
|
+
\"title\": \"PR审核提醒\",
|
|
30
|
+
\"text\": \"## 👀 新的 PR 需要审核\\n\\n**#${PR_NUMBER}**: ${PR_TITLE}\\n\\n**作者**: @${PR_AUTHOR}\\n\\n[点击审核](${PR_URL})\\n\\n---\\n📦 ${{ github.repository }}\"
|
|
31
|
+
}
|
|
32
|
+
}"
|
|
33
|
+
env:
|
|
34
|
+
DINGTALK_WEBHOOK: ${{ secrets.DINGTALK_WEBHOOK }}
|
|
35
|
+
|
|
36
|
+
notify-review-completed:
|
|
37
|
+
if: github.event_name == 'pull_request_review'
|
|
38
|
+
runs-on: ubuntu-latest
|
|
39
|
+
steps:
|
|
40
|
+
- name: 📢 Notify DingTalk - Review Completed
|
|
41
|
+
if: env.DINGTALK_WEBHOOK != ''
|
|
42
|
+
run: |
|
|
43
|
+
REVIEW_STATE="${{ github.event.review.state }}"
|
|
44
|
+
PR_TITLE="${{ github.event.pull_request.title }}"
|
|
45
|
+
PR_URL="${{ github.event.pull_request.html_url }}"
|
|
46
|
+
REVIEWER="${{ github.event.review.user.login }}"
|
|
47
|
+
PR_NUMBER="${{ github.event.pull_request.number }}"
|
|
48
|
+
|
|
49
|
+
if [ "$REVIEW_STATE" == "approved" ]; then
|
|
50
|
+
EMOJI="✅"
|
|
51
|
+
STATUS="已通过"
|
|
52
|
+
elif [ "$REVIEW_STATE" == "changes_requested" ]; then
|
|
53
|
+
EMOJI="🔄"
|
|
54
|
+
STATUS="需要修改"
|
|
55
|
+
else
|
|
56
|
+
EMOJI="💬"
|
|
57
|
+
STATUS="已评论"
|
|
58
|
+
fi
|
|
59
|
+
|
|
60
|
+
curl -X POST "${{ secrets.DINGTALK_WEBHOOK }}" \
|
|
61
|
+
-H "Content-Type: application/json" \
|
|
62
|
+
-d "{
|
|
63
|
+
\"msgtype\": \"markdown\",
|
|
64
|
+
\"markdown\": {
|
|
65
|
+
\"title\": \"PR审核完成\",
|
|
66
|
+
\"text\": \"## ${EMOJI} PR 审核${STATUS}\\n\\n**#${PR_NUMBER}**: ${PR_TITLE}\\n\\n**审核人**: @${REVIEWER}\\n\\n**状态**: ${REVIEW_STATE}\\n\\n[查看详情](${PR_URL})\\n\\n---\\n📦 ${{ github.repository }}\"
|
|
67
|
+
}
|
|
68
|
+
}"
|
|
69
|
+
env:
|
|
70
|
+
DINGTALK_WEBHOOK: ${{ secrets.DINGTALK_WEBHOOK }}
|
|
71
|
+
|
|
72
|
+
auto-merge-check:
|
|
73
|
+
if: github.event_name == 'pull_request_review' && github.event.review.state == 'approved'
|
|
74
|
+
runs-on: ubuntu-latest
|
|
75
|
+
steps:
|
|
76
|
+
- name: 📥 Checkout Code
|
|
77
|
+
uses: actions/checkout@v4
|
|
78
|
+
|
|
79
|
+
- name: 🔍 Check Auto-Merge Conditions
|
|
80
|
+
id: check
|
|
81
|
+
run: |
|
|
82
|
+
# 检查是否满足自动合并条件
|
|
83
|
+
REVIEWS=$(curl -s -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
|
|
84
|
+
"https://api.github.com/repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/reviews")
|
|
85
|
+
|
|
86
|
+
APPROVALS=$(echo "$REVIEWS" | grep -c '"state": "APPROVED"' || true)
|
|
87
|
+
|
|
88
|
+
echo "approvals=$APPROVALS" >> $GITHUB_OUTPUT
|
|
89
|
+
echo "✅ 当前 approvals: $APPROVALS"
|
|
90
|
+
|
|
91
|
+
- name: 📢 Notify DingTalk - Ready to Merge
|
|
92
|
+
if: steps.check.outputs.approvals >= 1 && env.DINGTALK_WEBHOOK != ''
|
|
93
|
+
run: |
|
|
94
|
+
PR_TITLE="${{ github.event.pull_request.title }}"
|
|
95
|
+
PR_URL="${{ github.event.pull_request.html_url }}"
|
|
96
|
+
PR_NUMBER="${{ github.event.pull_request.number }}"
|
|
97
|
+
|
|
98
|
+
curl -X POST "${{ secrets.DINGTALK_WEBHOOK }}" \
|
|
99
|
+
-H "Content-Type: application/json" \
|
|
100
|
+
-d "{
|
|
101
|
+
\"msgtype\": \"markdown\",
|
|
102
|
+
\"markdown\": {
|
|
103
|
+
\"title\": \"PR可合并\",
|
|
104
|
+
\"text\": \"## 🎉 PR 已审核通过,可以合并\\n\\n**#${PR_NUMBER}**: ${PR_TITLE}\\n\\n**审核状态**: ✅ 已通过\\n\\n**操作**: 点击合并到 main\\n\\n[立即合并](${PR_URL})\\n\\n---\\n📦 ${{ github.repository }}\"
|
|
105
|
+
}
|
|
106
|
+
}"
|
|
107
|
+
env:
|
|
108
|
+
DINGTALK_WEBHOOK: ${{ secrets.DINGTALK_WEBHOOK }}
|
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,44 @@
|
|
|
6
6
|
This document records all significant changes. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
7
7
|
and version numbers follow [Semantic Versioning](https://semver.org/).
|
|
8
8
|
|
|
9
|
+
## [0.7.3] - 2026-03-09
|
|
10
|
+
|
|
11
|
+
### 修复 / Fixes
|
|
12
|
+
- 🐛 **兼容性修复**:修复 0.7.0 引入的默认 Agent 路由回归问题。0.7.0 之前默认路由到 `main` agent,0.7.0 之后错误地路由到 `default` agent,现已恢复为 `main` agent
|
|
13
|
+
**Compatibility fix**: Fixed default agent routing regression introduced in 0.7.0. Before 0.7.0 default routed to `main` agent, after 0.7.0 incorrectly routed to `default` agent, now restored to `main` agent
|
|
14
|
+
- 🐛 修复用户显式配置名为 `default` 的账号时被错误映射的问题:使用 `__default__` 作为内部默认账号标识
|
|
15
|
+
Fixed issue where user-configured account named `default` was incorrectly mapped: Use `__default__` as internal default account identifier
|
|
16
|
+
|
|
17
|
+
### 改进 / Improvements
|
|
18
|
+
- 抽取 `DEFAULT_ACCOUNT_ID` 常量到文件顶部,统一管理默认账号标识
|
|
19
|
+
Extracted `DEFAULT_ACCOUNT_ID` constant to file top, unified management of default account identifier
|
|
20
|
+
- 更新 API 文档注释,移除对 `default` 的硬编码引用
|
|
21
|
+
Updated API documentation comments, removed hardcoded references to `default`
|
|
22
|
+
|
|
23
|
+
## [0.7.2] - 2026-03-05
|
|
24
|
+
|
|
25
|
+
### 新增功能 / Added Features
|
|
26
|
+
- ✅ 新增异步模式:立即回执用户消息,后台处理任务,然后主动推送最终结果作为独立消息
|
|
27
|
+
Added async mode: immediately acknowledge user messages, process in background, then push the final result as a separate message
|
|
28
|
+
- ✅ 支持自定义回执消息文本,可通过 `ackText` 配置项设置
|
|
29
|
+
Support custom acknowledgment message text, configurable via `ackText` option
|
|
30
|
+
|
|
31
|
+
### 修复 / Fixes
|
|
32
|
+
- 🐛 修复异步模式下 Agent 路由问题:`streamFromGateway` 调用时缺少 `accountId` 参数,导致会话路由到 undefined agent
|
|
33
|
+
Fixed agent routing in async mode: `streamFromGateway` was called without `accountId`, causing sessions to route to undefined agent
|
|
34
|
+
- 🐛 修复默认 Agent 路由:当 `accountId` 为 `'default'` 时跳过 `X-OpenClaw-Agent-Id` header,让 gateway 路由到其配置的默认 agent
|
|
35
|
+
Fixed default agent routing: Skip `X-OpenClaw-Agent-Id` header when `accountId` is `'default'`, letting gateway route to its configured default agent
|
|
36
|
+
- 🐛 修复异步模式内容处理:使用 `userContent`(包含文件附件)替代原始 `content.text`
|
|
37
|
+
Fixed async mode content: Use `userContent` (includes file attachments) instead of raw `content.text`
|
|
38
|
+
- 🐛 修复异步模式图片支持:将 `imageLocalPaths` 传递给 gateway stream
|
|
39
|
+
Fixed image support for async mode: Pass `imageLocalPaths` to gateway stream
|
|
40
|
+
|
|
41
|
+
### 配置 / Configuration
|
|
42
|
+
- 新增 `asyncMode` 配置项(默认:`false`)- 启用异步模式
|
|
43
|
+
Added `asyncMode` configuration option (default: `false`) - Enable async mode
|
|
44
|
+
- 新增 `ackText` 配置项(默认:`'🫡 任务已接收,处理中...'`)- 自定义回执消息文本
|
|
45
|
+
Added `ackText` configuration option (default: `'🫡 任务已接收,处理中...'`) - Custom ack message text
|
|
46
|
+
|
|
9
47
|
## [0.7.1] - 2026-03-05
|
|
10
48
|
|
|
11
49
|
### 修复 / Fixes
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
以下提供两种方案连接到 [OpenClaw](https://openclaw.ai) Gateway,分别是钉钉机器人和钉钉 DEAP Agent。
|
|
4
4
|
|
|
5
|
-
> 📝 **版本信息**:当前版本 v0.7.
|
|
5
|
+
> 📝 **版本信息**:当前版本 v0.7.3 | [查看变更日志](CHANGELOG.md) | [发布说明](docs/RELEASE_NOTES_V0.7.3.md) | [发布指南](RELEASE.md)
|
|
6
6
|
|
|
7
7
|
## 快速导航
|
|
8
8
|
|
|
@@ -24,9 +24,11 @@
|
|
|
24
24
|
- ✅ **主动发送消息** - 支持主动给钉钉个人或群发送消息
|
|
25
25
|
- ✅ **富媒体接收** - 支持接收 JPEG/PNG 图片消息,自动下载并传递给视觉模型
|
|
26
26
|
- ✅ **文件附件提取** - 支持解析 .docx、.pdf、纯文本文件(.txt、.md、.json 等)和二进制文件(.xlsx、.pptx、.zip 等)
|
|
27
|
+
- ✅ **音频消息支持** - 支持发送音频消息,支持多种格式(mp3、wav、amr、ogg),自动提取音频时长,支持通过标记或文件附件方式发送
|
|
27
28
|
- ✅ **钉钉文档 API** - 支持创建、追加、搜索、列举钉钉文档
|
|
28
29
|
- ✅ **多 Agent 路由** - 支持一个连接器实例连接多个 Agent,多个钉钉机器人可分别绑定到不同 Agent,实现角色分工和专业化服务
|
|
29
30
|
- ✅ **Markdown 表格转换** - 自动将 Markdown 表格转换为钉钉支持的文本格式,提升消息可读性
|
|
31
|
+
- ✅ **异步模式** - 立即回执用户消息,后台处理任务,然后主动推送最终结果作为独立消息(可选)
|
|
30
32
|
|
|
31
33
|
|
|
32
34
|
## 架构
|
|
@@ -87,7 +89,9 @@ openclaw plugins install -l .
|
|
|
87
89
|
"clientSecret": "your_secret_here", // 钉钉 AppSecret
|
|
88
90
|
"gatewayToken": "", // 可选:Gateway 认证 token, openclaw.json配置中 gateway.auth.token 的值
|
|
89
91
|
"gatewayPassword": "", // 可选:Gateway 认证 password(与 token 二选一)
|
|
90
|
-
"sessionTimeout": 1800000
|
|
92
|
+
"sessionTimeout": 1800000, // 可选:会话超时(ms),默认 30 分钟
|
|
93
|
+
"asyncMode": false, // 可选:异步模式,立即回执用户消息,后台处理并推送结果(默认:false)
|
|
94
|
+
"ackText": "🫡 任务已接收" // 可选:异步模式下的回执消息文本(默认:'🫡 任务已接收,处理中...')
|
|
91
95
|
}
|
|
92
96
|
},
|
|
93
97
|
"gateway": { // gateway通常是已有的节点,配置时注意把http部分追加到已有节点下
|
|
@@ -139,6 +143,48 @@ openclaw plugins list # 确认 dingtalk-connector 已加载
|
|
|
139
143
|
| `gatewayToken` | `OPENCLAW_GATEWAY_TOKEN` | Gateway 认证 token(可选) |
|
|
140
144
|
| `gatewayPassword` | — | Gateway 认证 password(可选,与 token 二选一) |
|
|
141
145
|
| `sessionTimeout` | — | 会话超时时间,单位毫秒(默认 1800000 = 30分钟) |
|
|
146
|
+
| `asyncMode` | — | 异步模式,立即回执用户消息,后台处理并推送结果(默认:false) |
|
|
147
|
+
| `ackText` | — | 异步模式下的回执消息文本(默认:'🫡 任务已接收,处理中...') |
|
|
148
|
+
|
|
149
|
+
## 异步模式
|
|
150
|
+
|
|
151
|
+
异步模式允许连接器立即回执用户消息,然后在后台处理任务,最后主动推送最终结果作为独立消息。这种模式特别适合处理耗时较长的任务,可以给用户更好的交互体验。
|
|
152
|
+
|
|
153
|
+
### 启用异步模式
|
|
154
|
+
|
|
155
|
+
在配置中设置 `asyncMode: true`:
|
|
156
|
+
|
|
157
|
+
```json5
|
|
158
|
+
{
|
|
159
|
+
"channels": {
|
|
160
|
+
"dingtalk-connector": {
|
|
161
|
+
"enabled": true,
|
|
162
|
+
"clientId": "dingxxxxxxxxx",
|
|
163
|
+
"clientSecret": "your_secret_here",
|
|
164
|
+
"asyncMode": true, // 启用异步模式
|
|
165
|
+
"ackText": "🫡 任务已接收" // 可选:自定义回执消息
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### 工作流程
|
|
172
|
+
|
|
173
|
+
1. **立即回执** - 用户发送消息后,连接器立即发送回执消息(默认:`🫡 任务已接收,处理中...`)
|
|
174
|
+
2. **后台处理** - 连接器在后台调用 Gateway 处理任务,支持文件附件和图片
|
|
175
|
+
3. **推送结果** - 处理完成后,连接器主动推送最终结果作为独立消息
|
|
176
|
+
|
|
177
|
+
### 适用场景
|
|
178
|
+
|
|
179
|
+
- ✅ 处理耗时较长的任务(如文档分析、代码生成等)
|
|
180
|
+
- ✅ 需要给用户即时反馈的场景
|
|
181
|
+
- ✅ 希望将处理过程和结果分离的场景
|
|
182
|
+
|
|
183
|
+
### 注意事项
|
|
184
|
+
|
|
185
|
+
- 异步模式下不支持 AI Card 流式响应(因为结果通过主动推送发送)
|
|
186
|
+
- 异步模式支持文件附件和图片处理
|
|
187
|
+
- 错误信息也会通过主动推送发送给用户
|
|
142
188
|
|
|
143
189
|
## 多 Agent 配置
|
|
144
190
|
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
# Release Notes - v0.7.2
|
|
2
|
+
|
|
3
|
+
## ✨ 功能增强版本 / Feature Enhancement Release
|
|
4
|
+
|
|
5
|
+
本次更新新增异步模式功能,并修复了多个关键问题,提升了连接器的稳定性和用户体验。
|
|
6
|
+
|
|
7
|
+
This update adds async mode functionality and fixes several critical issues, improving connector stability and user experience.
|
|
8
|
+
|
|
9
|
+
## ✨ 新增功能 / New Features
|
|
10
|
+
|
|
11
|
+
### 1. 异步模式 / Async Mode
|
|
12
|
+
|
|
13
|
+
**功能描述 / Feature Description**:立即回执用户消息,后台处理任务,然后主动推送最终结果作为独立消息
|
|
14
|
+
Immediately acknowledge user messages, process in background, then push the final result as a separate message
|
|
15
|
+
|
|
16
|
+
**使用场景 / Use Cases**:
|
|
17
|
+
- 处理耗时较长的任务(如文档分析、代码生成等)
|
|
18
|
+
Processing time-consuming tasks (e.g., document analysis, code generation)
|
|
19
|
+
- 需要给用户即时反馈的场景
|
|
20
|
+
Scenarios requiring immediate user feedback
|
|
21
|
+
- 希望将处理过程和结果分离的场景
|
|
22
|
+
Scenarios where separating processing and results is desired
|
|
23
|
+
|
|
24
|
+
**配置方式 / Configuration**:
|
|
25
|
+
|
|
26
|
+
```json5
|
|
27
|
+
{
|
|
28
|
+
"channels": {
|
|
29
|
+
"dingtalk-connector": {
|
|
30
|
+
"asyncMode": true, // 启用异步模式 / Enable async mode
|
|
31
|
+
"ackText": "🫡 任务已接收" // 可选:自定义回执消息 / Optional: Custom ack message
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
**工作流程 / Workflow**:
|
|
38
|
+
1. **立即回执** - 用户发送消息后,连接器立即发送回执消息
|
|
39
|
+
**Immediate Acknowledgment** - Connector immediately sends acknowledgment message after user sends message
|
|
40
|
+
2. **后台处理** - 连接器在后台调用 Gateway 处理任务,支持文件附件和图片
|
|
41
|
+
**Background Processing** - Connector processes task in background via Gateway, supports file attachments and images
|
|
42
|
+
3. **推送结果** - 处理完成后,连接器主动推送最终结果作为独立消息
|
|
43
|
+
**Push Result** - After processing completes, connector proactively pushes final result as separate message
|
|
44
|
+
|
|
45
|
+
**影响范围 / Impact**:所有用户均可选择启用此功能,默认关闭,不影响现有使用方式
|
|
46
|
+
All users can optionally enable this feature. Default is off, does not affect existing usage.
|
|
47
|
+
|
|
48
|
+
## 🐛 修复 / Fixes
|
|
49
|
+
|
|
50
|
+
### 1. 异步模式下 Agent 路由问题修复 / Agent Routing Fix in Async Mode
|
|
51
|
+
|
|
52
|
+
**问题描述 / Issue Description**:异步模式下 `streamFromGateway` 调用时缺少 `accountId` 参数,导致会话路由到 undefined agent
|
|
53
|
+
In async mode, `streamFromGateway` was called without `accountId`, causing sessions to route to undefined agent
|
|
54
|
+
|
|
55
|
+
**修复内容 / Fix**:
|
|
56
|
+
- 修复 `streamFromGateway` 调用,正确传递 `accountId` 参数
|
|
57
|
+
Fixed `streamFromGateway` call to correctly pass `accountId` parameter
|
|
58
|
+
- 确保异步模式下 Agent 路由正常工作
|
|
59
|
+
Ensures Agent routing works correctly in async mode
|
|
60
|
+
|
|
61
|
+
**影响范围 / Impact**:影响使用异步模式的用户,修复后异步模式下的 Agent 路由将正常工作
|
|
62
|
+
Affects users using async mode. After the fix, Agent routing in async mode will work correctly.
|
|
63
|
+
|
|
64
|
+
### 2. 默认 Agent 路由修复 / Default Agent Routing Fix
|
|
65
|
+
|
|
66
|
+
**问题描述 / Issue Description**:当 `accountId` 为 `'default'` 时,仍然发送 `X-OpenClaw-Agent-Id` header,导致路由异常
|
|
67
|
+
When `accountId` is `'default'`, `X-OpenClaw-Agent-Id` header was still sent, causing routing issues
|
|
68
|
+
|
|
69
|
+
**修复内容 / Fix**:
|
|
70
|
+
- 当 `accountId` 为 `'default'` 时跳过 `X-OpenClaw-Agent-Id` header
|
|
71
|
+
Skip `X-OpenClaw-Agent-Id` header when `accountId` is `'default'`
|
|
72
|
+
- 让 gateway 路由到其配置的默认 agent
|
|
73
|
+
Let gateway route to its configured default agent
|
|
74
|
+
|
|
75
|
+
**影响范围 / Impact**:影响使用默认 Agent 配置的用户,修复后默认 Agent 路由将正常工作
|
|
76
|
+
Affects users with default Agent configuration. After the fix, default Agent routing will work correctly.
|
|
77
|
+
|
|
78
|
+
### 3. 异步模式内容处理修复 / Async Mode Content Fix
|
|
79
|
+
|
|
80
|
+
**问题描述 / Issue Description**:异步模式使用原始 `content.text`,未包含文件附件内容
|
|
81
|
+
Async mode used raw `content.text`, did not include file attachment content
|
|
82
|
+
|
|
83
|
+
**修复内容 / Fix**:
|
|
84
|
+
- 使用 `userContent`(包含文件附件)替代原始 `content.text`
|
|
85
|
+
Use `userContent` (includes file attachments) instead of raw `content.text`
|
|
86
|
+
- 确保文件附件内容正确传递给 Gateway
|
|
87
|
+
Ensures file attachment content is correctly passed to Gateway
|
|
88
|
+
|
|
89
|
+
**影响范围 / Impact**:影响使用异步模式且发送文件附件的用户,修复后文件附件将正确处理
|
|
90
|
+
Affects users using async mode and sending file attachments. After the fix, file attachments will be processed correctly.
|
|
91
|
+
|
|
92
|
+
### 4. 异步模式图片支持修复 / Async Mode Image Support Fix
|
|
93
|
+
|
|
94
|
+
**问题描述 / Issue Description**:异步模式下未将图片路径传递给 Gateway stream
|
|
95
|
+
Image paths were not passed to Gateway stream in async mode
|
|
96
|
+
|
|
97
|
+
**修复内容 / Fix**:
|
|
98
|
+
- 将 `imageLocalPaths` 传递给 gateway stream
|
|
99
|
+
Pass `imageLocalPaths` to gateway stream
|
|
100
|
+
- 确保图片在异步模式下正确处理
|
|
101
|
+
Ensures images are processed correctly in async mode
|
|
102
|
+
|
|
103
|
+
**影响范围 / Impact**:影响使用异步模式且发送图片的用户,修复后图片将正确处理
|
|
104
|
+
Affects users using async mode and sending images. After the fix, images will be processed correctly.
|
|
105
|
+
|
|
106
|
+
## 🔧 配置更新 / Configuration Updates
|
|
107
|
+
|
|
108
|
+
### 新增配置项 / New Configuration Options
|
|
109
|
+
|
|
110
|
+
- **`asyncMode`**(类型:`boolean`,默认:`false`)- 启用异步模式
|
|
111
|
+
**`asyncMode`** (type: `boolean`, default: `false`) - Enable async mode
|
|
112
|
+
- **`ackText`**(类型:`string`,默认:`'🫡 任务已接收,处理中...'`)- 自定义回执消息文本
|
|
113
|
+
**`ackText`** (type: `string`, default: `'🫡 任务已接收,处理中...'`) - Custom ack message text
|
|
114
|
+
|
|
115
|
+
## 📥 安装升级 / Installation & Upgrade
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
# 通过 npm 安装最新版本 / Install latest version via npm
|
|
119
|
+
openclaw plugins install @dingtalk-real-ai/dingtalk-connector
|
|
120
|
+
|
|
121
|
+
# 或升级现有版本 / Or upgrade existing version
|
|
122
|
+
openclaw plugins update dingtalk-connector
|
|
123
|
+
|
|
124
|
+
# 通过 Git 安装 / Install via Git
|
|
125
|
+
openclaw plugins install https://github.com/DingTalk-Real-AI/dingtalk-openclaw-connector.git
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## 🔗 相关链接 / Related Links
|
|
129
|
+
|
|
130
|
+
- [完整变更日志 / Full Changelog](https://github.com/DingTalk-Real-AI/dingtalk-openclaw-connector/blob/main/CHANGELOG.md)
|
|
131
|
+
- [使用文档 / Documentation](https://github.com/DingTalk-Real-AI/dingtalk-openclaw-connector/blob/main/README.md)
|
|
132
|
+
- [问题反馈 / Issue Feedback](https://github.com/DingTalk-Real-AI/dingtalk-openclaw-connector/issues)
|
|
133
|
+
|
|
134
|
+
## 🙏 致谢 / Acknowledgments
|
|
135
|
+
|
|
136
|
+
感谢所有贡献者和用户的支持与反馈!
|
|
137
|
+
Thanks to all contributors and users for their support and feedback!
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
**发布日期 / Release Date**:2026-03-05
|
|
142
|
+
**版本号 / Version**:v0.7.2
|
|
143
|
+
**兼容性 / Compatibility**:OpenClaw Gateway 0.4.0+
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
# Release Notes - v0.7.3
|
|
2
|
+
|
|
3
|
+
## 🔧 兼容性修复版本 / Compatibility Fix Release
|
|
4
|
+
|
|
5
|
+
本次更新主要修复了 0.7.0 版本引入的默认 Agent 路由回归问题,确保与 0.7.0 之前版本的向下兼容性。
|
|
6
|
+
|
|
7
|
+
This update primarily fixes the default Agent routing regression introduced in version 0.7.0, ensuring backward compatibility with versions before 0.7.0.
|
|
8
|
+
|
|
9
|
+
## 🐛 修复 / Fixes
|
|
10
|
+
|
|
11
|
+
### 1. 默认 Agent 路由兼容性修复 / Default Agent Routing Compatibility Fix
|
|
12
|
+
|
|
13
|
+
**问题描述 / Issue Description**:
|
|
14
|
+
在 0.7.0 版本中,默认路由(没有配置 `accountId` 时)从 `main` agent 错误地改成了 `default` agent,导致与 0.7.0 之前版本的行为不一致,可能影响现有用户的配置和会话路由。
|
|
15
|
+
In version 0.7.0, the default route (when no `accountId` was configured) was incorrectly changed from `main` agent to `default` agent, causing inconsistency with versions before 0.7.0, which may affect existing user configurations and session routing.
|
|
16
|
+
|
|
17
|
+
**修复内容 / Fix**:
|
|
18
|
+
- 恢复默认路由到 `main` agent,与 0.7.0 之前版本保持一致
|
|
19
|
+
Restored default routing to `main` agent, consistent with versions before 0.7.0
|
|
20
|
+
- 使用 `__default__` 作为内部默认账号标识,避免与用户配置的 `default` 账号冲突
|
|
21
|
+
Use `__default__` as internal default account identifier to avoid conflicts with user-configured `default` accounts
|
|
22
|
+
- 在 `streamFromGateway` 中将 `__default__` 正确映射到 `main` agent
|
|
23
|
+
Correctly map `__default__` to `main` agent in `streamFromGateway`
|
|
24
|
+
|
|
25
|
+
**影响范围 / Impact**:
|
|
26
|
+
影响所有使用默认配置(未配置 `accounts`)的用户。修复后,默认路由将恢复到 0.7.0 之前的行为,路由到 `main` agent,确保向下兼容性。
|
|
27
|
+
Affects all users using default configuration (without `accounts` configuration). After the fix, default routing will be restored to pre-0.7.0 behavior, routing to `main` agent, ensuring backward compatibility.
|
|
28
|
+
|
|
29
|
+
### 2. 用户配置 `default` 账号映射修复 / User-Configured `default` Account Mapping Fix
|
|
30
|
+
|
|
31
|
+
**问题描述 / Issue Description**:
|
|
32
|
+
当用户显式配置名为 `default` 的账号时,系统会错误地将其映射为内部默认账号,导致用户配置的 `default` 账号无法正常使用。
|
|
33
|
+
When users explicitly configure an account named `default`, the system incorrectly maps it to the internal default account, preventing the user-configured `default` account from working properly.
|
|
34
|
+
|
|
35
|
+
**修复内容 / Fix**:
|
|
36
|
+
- 使用 `__default__` 作为内部默认账号标识,与用户配置的 `default` 账号区分开
|
|
37
|
+
Use `__default__` as internal default account identifier, separate from user-configured `default` accounts
|
|
38
|
+
- 确保用户配置的 `default` 账号能够正常使用
|
|
39
|
+
Ensure user-configured `default` accounts can work properly
|
|
40
|
+
|
|
41
|
+
**影响范围 / Impact**:
|
|
42
|
+
影响显式配置了名为 `default` 的账号的用户。修复后,用户配置的 `default` 账号将能够正常工作,不会被错误映射。
|
|
43
|
+
Affects users who explicitly configured an account named `default`. After the fix, user-configured `default` accounts will work properly and will not be incorrectly mapped.
|
|
44
|
+
|
|
45
|
+
## 🔧 改进 / Improvements
|
|
46
|
+
|
|
47
|
+
### 1. 代码结构优化 / Code Structure Optimization
|
|
48
|
+
|
|
49
|
+
**改进内容 / Improvements**:
|
|
50
|
+
- 抽取 `DEFAULT_ACCOUNT_ID` 常量到文件顶部(值为 `__default__`),统一管理默认账号标识
|
|
51
|
+
Extracted `DEFAULT_ACCOUNT_ID` constant to file top (value: `__default__`), unified management of default account identifier
|
|
52
|
+
- 更新所有相关代码,使用常量替代硬编码的字符串
|
|
53
|
+
Updated all related code to use constants instead of hardcoded strings
|
|
54
|
+
- 提高代码可维护性和可读性
|
|
55
|
+
Improved code maintainability and readability
|
|
56
|
+
|
|
57
|
+
**影响范围 / Impact**:
|
|
58
|
+
内部代码改进,不影响用户使用,但提高了代码质量和可维护性。
|
|
59
|
+
Internal code improvements, does not affect user usage, but improves code quality and maintainability.
|
|
60
|
+
|
|
61
|
+
### 2. API 文档更新 / API Documentation Updates
|
|
62
|
+
|
|
63
|
+
**改进内容 / Improvements**:
|
|
64
|
+
- 更新 API 文档注释,移除对 `default` 的硬编码引用
|
|
65
|
+
Updated API documentation comments, removed hardcoded references to `default`
|
|
66
|
+
- 明确说明 `accountId` 参数为可选,不传则使用默认配置
|
|
67
|
+
Clarified that `accountId` parameter is optional, uses default configuration if not provided
|
|
68
|
+
|
|
69
|
+
**影响范围 / Impact**:
|
|
70
|
+
文档改进,帮助开发者更好地理解 API 的使用方式。
|
|
71
|
+
Documentation improvements, helping developers better understand API usage.
|
|
72
|
+
|
|
73
|
+
## 📋 技术细节 / Technical Details
|
|
74
|
+
|
|
75
|
+
### 内部实现变更 / Internal Implementation Changes
|
|
76
|
+
|
|
77
|
+
**变更前 / Before**:
|
|
78
|
+
- 默认账号标识使用 `'default'` 字符串
|
|
79
|
+
- 当 `accountId` 为 `'default'` 时,不发送 `X-OpenClaw-Agent-Id` header,让 gateway 路由到其配置的默认 agent
|
|
80
|
+
|
|
81
|
+
**变更后 / After**:
|
|
82
|
+
- 默认账号标识使用 `'__default__'` 常量
|
|
83
|
+
- 在 `streamFromGateway` 中,将 `'__default__'` 映射到 `'main'` agent,并发送 `X-OpenClaw-Agent-Id: main` header
|
|
84
|
+
- 用户配置的 `'default'` 账号正常使用,不会被特殊处理
|
|
85
|
+
|
|
86
|
+
### 相关代码位置 / Related Code Locations
|
|
87
|
+
|
|
88
|
+
主要修改文件:
|
|
89
|
+
- `plugin.ts` - 核心逻辑修改
|
|
90
|
+
|
|
91
|
+
关键变更点:
|
|
92
|
+
- 新增 `DEFAULT_ACCOUNT_ID` 常量定义
|
|
93
|
+
- `streamFromGateway` 函数中的 agent 路由逻辑
|
|
94
|
+
- `listAccountIds`、`resolveAccount`、`defaultAccountId` 等配置相关函数
|
|
95
|
+
- API 方法文档注释
|
|
96
|
+
|
|
97
|
+
## 📥 安装升级 / Installation & Upgrade
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
# 通过 npm 安装最新版本 / Install latest version via npm
|
|
101
|
+
openclaw plugins install @dingtalk-real-ai/dingtalk-connector
|
|
102
|
+
|
|
103
|
+
# 或升级现有版本 / Or upgrade existing version
|
|
104
|
+
openclaw plugins update dingtalk-connector
|
|
105
|
+
|
|
106
|
+
# 通过 Git 安装 / Install via Git
|
|
107
|
+
openclaw plugins install https://github.com/DingTalk-Real-AI/dingtalk-openclaw-connector.git
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## ⚠️ 升级注意事项 / Upgrade Notes
|
|
111
|
+
|
|
112
|
+
### 兼容性说明 / Compatibility Notes
|
|
113
|
+
|
|
114
|
+
- **向下兼容**:本次更新恢复了 0.7.0 之前版本的默认路由行为,对现有用户完全兼容
|
|
115
|
+
**Backward Compatible**: This update restores the default routing behavior of versions before 0.7.0, fully compatible with existing users
|
|
116
|
+
- **无需配置变更**:现有配置无需修改即可正常工作
|
|
117
|
+
**No Configuration Changes Required**: Existing configurations work without modification
|
|
118
|
+
- **推荐升级**:使用默认配置的用户强烈建议升级到此版本,以确保正确的 Agent 路由
|
|
119
|
+
**Recommended Upgrade**: Users with default configuration are strongly recommended to upgrade to this version to ensure correct Agent routing
|
|
120
|
+
|
|
121
|
+
### 迁移指南 / Migration Guide
|
|
122
|
+
|
|
123
|
+
如果您在 0.7.0 或 0.7.1 版本中遇到了默认路由问题,升级到此版本后:
|
|
124
|
+
If you encountered default routing issues in versions 0.7.0 or 0.7.1, after upgrading to this version:
|
|
125
|
+
|
|
126
|
+
1. **默认路由将自动恢复**:无需任何配置,默认路由将自动恢复到 `main` agent
|
|
127
|
+
**Default routing will be automatically restored**: No configuration needed, default routing will automatically restore to `main` agent
|
|
128
|
+
2. **检查 Agent 配置**:确认您的 Gateway 中 `main` agent 的配置是否正确
|
|
129
|
+
**Check Agent Configuration**: Verify that your Gateway's `main` agent configuration is correct
|
|
130
|
+
3. **验证路由**:升级后测试会话路由,确认消息正确路由到预期的 agent
|
|
131
|
+
**Verify Routing**: Test session routing after upgrade to confirm messages are correctly routed to the expected agent
|
|
132
|
+
|
|
133
|
+
## 🔗 相关链接 / Related Links
|
|
134
|
+
|
|
135
|
+
- [完整变更日志 / Full Changelog](https://github.com/DingTalk-Real-AI/dingtalk-openclaw-connector/blob/main/CHANGELOG.md)
|
|
136
|
+
- [使用文档 / Documentation](https://github.com/DingTalk-Real-AI/dingtalk-openclaw-connector/blob/main/README.md)
|
|
137
|
+
- [问题反馈 / Issue Feedback](https://github.com/DingTalk-Real-AI/dingtalk-openclaw-connector/issues)
|
|
138
|
+
- [Pull Request #108](https://github.com/DingTalk-Real-AI/dingtalk-openclaw-connector/pull/108)
|
|
139
|
+
|
|
140
|
+
## 🙏 致谢 / Acknowledgments
|
|
141
|
+
|
|
142
|
+
感谢所有贡献者和用户的支持与反馈!
|
|
143
|
+
Thanks to all contributors and users for their support and feedback!
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
**发布日期 / Release Date**:2026-03-09
|
|
148
|
+
**版本号 / Version**:v0.7.3
|
|
149
|
+
**兼容性 / Compatibility**:OpenClaw Gateway 0.4.0+
|
package/openclaw.plugin.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "dingtalk-connector",
|
|
3
3
|
"name": "DingTalk Channel",
|
|
4
|
-
"version": "0.7.
|
|
4
|
+
"version": "0.7.3",
|
|
5
5
|
"description": "DingTalk (钉钉) messaging channel via Stream mode with AI Card streaming",
|
|
6
6
|
"author": "DingTalk Real Team",
|
|
7
7
|
"channels": ["dingtalk-connector"],
|
package/package.json
CHANGED
|
@@ -1,11 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dingtalk-real-ai/dingtalk-connector",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.3",
|
|
4
4
|
"description": "DingTalk (钉钉) channel connector — Stream mode with AI Card streaming",
|
|
5
5
|
"main": "plugin.ts",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"scripts": {
|
|
8
|
-
"build": "echo 'No build needed - jiti loads TS at runtime'"
|
|
8
|
+
"build": "echo 'No build needed - jiti loads TS at runtime'",
|
|
9
|
+
"lint": "echo 'Lint check skipped'",
|
|
10
|
+
"lint:fix": "echo 'Lint fix skipped'",
|
|
11
|
+
"test": "echo 'Tests skipped'",
|
|
12
|
+
"test:watch": "echo 'Tests skipped'",
|
|
13
|
+
"type-check": "npx tsc --noEmit",
|
|
14
|
+
"version:check": "echo 'Version check skipped'",
|
|
15
|
+
"release:prepare": "echo 'Release prepare skipped'",
|
|
16
|
+
"release:publish": "npm publish --access public",
|
|
17
|
+
"release:verify": "npm view @dingtalk-real-ai/dingtalk-connector version",
|
|
18
|
+
"clean": "rm -rf node_modules package-lock.json",
|
|
19
|
+
"install:fresh": "npm run clean && npm install",
|
|
20
|
+
"dev": "echo 'Run: openclaw start'",
|
|
21
|
+
"validate": "npm run lint && npm run type-check && npm run version:check"
|
|
9
22
|
},
|
|
10
23
|
"keywords": [
|
|
11
24
|
"dingtalk",
|
package/plugin.ts
CHANGED
|
@@ -16,6 +16,9 @@ import type { ClawdbotPluginApi, PluginRuntime, ClawdbotConfig } from 'clawdbot/
|
|
|
16
16
|
|
|
17
17
|
export const id = 'dingtalk-connector';
|
|
18
18
|
|
|
19
|
+
/** 默认账号 ID,用于标记单账号模式(无 accounts 配置)时的内部标识,映射到 'main' agent */
|
|
20
|
+
const DEFAULT_ACCOUNT_ID = '__default__';
|
|
21
|
+
|
|
19
22
|
let runtime: PluginRuntime | null = null;
|
|
20
23
|
|
|
21
24
|
function getRuntime(): PluginRuntime {
|
|
@@ -1220,7 +1223,9 @@ async function* streamFromGateway(options: GatewayOptions, accountId: string): A
|
|
|
1220
1223
|
headers['Authorization'] = `Bearer ${gatewayAuth}`;
|
|
1221
1224
|
}
|
|
1222
1225
|
// 使用 HTTP Header 传递 accountId 用于 agent 路由
|
|
1223
|
-
|
|
1226
|
+
// DEFAULT_ACCOUNT_ID 映射到 'main' agent
|
|
1227
|
+
const agentId = accountId === DEFAULT_ACCOUNT_ID ? 'main' : accountId;
|
|
1228
|
+
headers['X-OpenClaw-Agent-Id'] = agentId;
|
|
1224
1229
|
|
|
1225
1230
|
log?.info?.(`[DingTalk][Gateway] POST ${gatewayUrl}, session=${sessionKey}, accountId=${accountId}, messages=${messages.length}`);
|
|
1226
1231
|
|
|
@@ -2492,13 +2497,13 @@ async function handleDingTalkMessage(params: {
|
|
|
2492
2497
|
let fullResponse = '';
|
|
2493
2498
|
try {
|
|
2494
2499
|
for await (const chunk of streamFromGateway({
|
|
2495
|
-
userContent
|
|
2500
|
+
userContent,
|
|
2496
2501
|
systemPrompts,
|
|
2497
2502
|
sessionKey,
|
|
2498
2503
|
gatewayAuth,
|
|
2499
2504
|
imageLocalPaths: imageLocalPaths.length > 0 ? imageLocalPaths : undefined,
|
|
2500
2505
|
log,
|
|
2501
|
-
})) {
|
|
2506
|
+
}, accountId)) {
|
|
2502
2507
|
fullResponse += chunk;
|
|
2503
2508
|
}
|
|
2504
2509
|
|
|
@@ -3042,19 +3047,21 @@ const dingtalkPlugin = {
|
|
|
3042
3047
|
config: {
|
|
3043
3048
|
listAccountIds: (cfg: ClawdbotConfig) => {
|
|
3044
3049
|
const config = getConfig(cfg);
|
|
3050
|
+
// __default__ 是内部标记,表示使用顶层配置(单账号模式)
|
|
3045
3051
|
return config.accounts
|
|
3046
3052
|
? Object.keys(config.accounts)
|
|
3047
|
-
: (isConfigured(cfg) ? ['
|
|
3053
|
+
: (isConfigured(cfg) ? ['__default__'] : []);
|
|
3048
3054
|
},
|
|
3049
3055
|
resolveAccount: (cfg: ClawdbotConfig, accountId?: string) => {
|
|
3050
3056
|
const config = getConfig(cfg);
|
|
3051
|
-
const id = accountId ||
|
|
3057
|
+
const id = accountId || DEFAULT_ACCOUNT_ID;
|
|
3052
3058
|
if (config.accounts?.[id]) {
|
|
3053
3059
|
return { accountId: id, config: config.accounts[id], enabled: config.accounts[id].enabled !== false };
|
|
3054
3060
|
}
|
|
3055
|
-
|
|
3061
|
+
// 没有 accounts 配置或找不到指定账号时,使用顶层配置
|
|
3062
|
+
return { accountId: DEFAULT_ACCOUNT_ID, config, enabled: config.enabled !== false };
|
|
3056
3063
|
},
|
|
3057
|
-
defaultAccountId: () => '
|
|
3064
|
+
defaultAccountId: () => '__default__',
|
|
3058
3065
|
isConfigured: (account: any) => Boolean(account.config?.clientId && account.config?.clientSecret),
|
|
3059
3066
|
describeAccount: (account: any) => ({
|
|
3060
3067
|
accountId: account.accountId,
|
|
@@ -3275,7 +3282,7 @@ const dingtalkPlugin = {
|
|
|
3275
3282
|
},
|
|
3276
3283
|
},
|
|
3277
3284
|
status: {
|
|
3278
|
-
defaultRuntime: { accountId:
|
|
3285
|
+
defaultRuntime: { accountId: DEFAULT_ACCOUNT_ID, running: false, lastStartAt: null, lastStopAt: null, lastError: null },
|
|
3279
3286
|
probe: async ({ cfg }: any) => {
|
|
3280
3287
|
if (!isConfigured(cfg)) return { ok: false, error: 'Not configured' };
|
|
3281
3288
|
try {
|
|
@@ -3332,7 +3339,7 @@ const plugin = {
|
|
|
3332
3339
|
* - title?: markdown 消息标题
|
|
3333
3340
|
* - useAICard?: 是否使用 AI Card(默认 true)
|
|
3334
3341
|
* - fallbackToNormal?: AI Card 失败时是否降级到普通消息(默认 true)
|
|
3335
|
-
* - accountId?: 使用的账号 ID
|
|
3342
|
+
* - accountId?: 使用的账号 ID(可选,不传则使用默认配置)
|
|
3336
3343
|
*/
|
|
3337
3344
|
api.registerGatewayMethod('dingtalk-connector.sendToUser', async ({ respond, cfg, params, log }: any) => {
|
|
3338
3345
|
const { userId, userIds, content, msgType, title, useAICard, fallbackToNormal, accountId } = params || {};
|
|
@@ -3370,7 +3377,7 @@ const plugin = {
|
|
|
3370
3377
|
* - title?: markdown 消息标题
|
|
3371
3378
|
* - useAICard?: 是否使用 AI Card(默认 true)
|
|
3372
3379
|
* - fallbackToNormal?: AI Card 失败时是否降级到普通消息(默认 true)
|
|
3373
|
-
* - accountId?: 使用的账号 ID
|
|
3380
|
+
* - accountId?: 使用的账号 ID(可选,不传则使用默认配置)
|
|
3374
3381
|
*/
|
|
3375
3382
|
api.registerGatewayMethod('dingtalk-connector.sendToGroup', async ({ respond, cfg, params, log }: any) => {
|
|
3376
3383
|
const { openConversationId, content, msgType, title, useAICard, fallbackToNormal, accountId } = params || {};
|
|
File without changes
|
|
File without changes
|