@fieldwangai/agentflow 0.1.70 → 0.1.71
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/bin/lib/catalog-flows.mjs +1 -0
- package/bin/lib/locales/en.json +8 -0
- package/bin/lib/locales/zh.json +8 -0
- package/bin/lib/marketplace.mjs +1 -0
- package/bin/lib/paths.mjs +2 -0
- package/bin/lib/ui-server.mjs +505 -28
- package/bin/lib/wecom.mjs +102 -0
- package/bin/pipeline/pre-process-node.mjs +40 -8
- package/builtin/nodes/tool_wecom_send_app_markdown.md +56 -0
- package/builtin/nodes/tool_wecom_send_group_markdown.md +44 -0
- package/builtin/web-ui/dist/assets/{index-ByGcCRCN.css → index-B3Wr-I3p.css} +1 -1
- package/builtin/web-ui/dist/assets/index-C8JMDUZb.js +296 -0
- package/builtin/web-ui/dist/index.html +2 -2
- package/package.json +1 -1
- package/builtin/web-ui/dist/assets/index-BdgCU6QK.js +0 -296
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
function requireValue(value, name) {
|
|
2
|
+
const text = String(value || "").trim();
|
|
3
|
+
if (!text) throw new Error(`${name} is required`);
|
|
4
|
+
return text;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function envValue(env, names) {
|
|
8
|
+
for (const name of names) {
|
|
9
|
+
const value = env?.[name];
|
|
10
|
+
if (value != null && String(value).trim()) return String(value).trim();
|
|
11
|
+
}
|
|
12
|
+
return "";
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function buildWebhookUrl(webhookUrl, webhookKey, env = {}) {
|
|
16
|
+
const explicitUrl = String(webhookUrl || "").trim() || envValue(env, ["WECOM_GROUP_WEBHOOK", "WECOM_BOT_WEBHOOK", "WECHAT_WORK_BOT_WEBHOOK"]);
|
|
17
|
+
if (explicitUrl) return explicitUrl;
|
|
18
|
+
const key = String(webhookKey || "").trim() || envValue(env, ["WECOM_GROUP_WEBHOOK_KEY", "WECOM_BOT_KEY", "WECHAT_WORK_BOT_KEY"]);
|
|
19
|
+
if (!key) throw new Error("webhookUrl or webhookKey is required");
|
|
20
|
+
return `https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=${encodeURIComponent(key)}`;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async function requestJson(url, options = {}) {
|
|
24
|
+
if (typeof fetch !== "function") {
|
|
25
|
+
throw new Error("global fetch is not available in this Node.js runtime");
|
|
26
|
+
}
|
|
27
|
+
const resp = await fetch(url, {
|
|
28
|
+
...options,
|
|
29
|
+
headers: {
|
|
30
|
+
"Content-Type": "application/json",
|
|
31
|
+
...(options.headers || {}),
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
const text = await resp.text();
|
|
35
|
+
let json = {};
|
|
36
|
+
try {
|
|
37
|
+
json = text ? JSON.parse(text) : {};
|
|
38
|
+
} catch {
|
|
39
|
+
json = { raw: text };
|
|
40
|
+
}
|
|
41
|
+
if (!resp.ok) throw new Error(`HTTP ${resp.status}: ${text.slice(0, 500)}`);
|
|
42
|
+
return json;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function assertWecomOk(json, action) {
|
|
46
|
+
const errcode = Number(json?.errcode ?? 0);
|
|
47
|
+
if (errcode !== 0) {
|
|
48
|
+
throw new Error(`${action} failed: ${json?.errmsg || JSON.stringify(json)}`);
|
|
49
|
+
}
|
|
50
|
+
return json;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export async function sendWecomGroupMarkdown(params = {}, env = {}) {
|
|
54
|
+
const content = requireValue(params.markdown ?? params.content, "markdown");
|
|
55
|
+
const webhookUrl = buildWebhookUrl(params.webhookUrl, params.webhookKey, env);
|
|
56
|
+
const json = await requestJson(webhookUrl, {
|
|
57
|
+
method: "POST",
|
|
58
|
+
body: JSON.stringify({
|
|
59
|
+
msgtype: "markdown",
|
|
60
|
+
markdown: { content },
|
|
61
|
+
}),
|
|
62
|
+
});
|
|
63
|
+
assertWecomOk(json, "send wecom group markdown");
|
|
64
|
+
return {
|
|
65
|
+
ok: true,
|
|
66
|
+
message: "企业微信群 Markdown 已发送",
|
|
67
|
+
response: json,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export async function getWecomAccessToken(params = {}, env = {}) {
|
|
72
|
+
const corpId = requireValue(params.corpId || envValue(env, ["WECOM_CORP_ID", "WECHAT_WORK_CORP_ID", "WXWORK_CORP_ID"]), "corpId");
|
|
73
|
+
const corpSecret = requireValue(params.corpSecret || envValue(env, ["WECOM_APP_SECRET", "WECOM_CORP_SECRET", "WECHAT_WORK_APP_SECRET", "WXWORK_APP_SECRET"]), "corpSecret");
|
|
74
|
+
const url = `https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=${encodeURIComponent(corpId)}&corpsecret=${encodeURIComponent(corpSecret)}`;
|
|
75
|
+
const json = await requestJson(url, { method: "GET" });
|
|
76
|
+
assertWecomOk(json, "get wecom access_token");
|
|
77
|
+
return requireValue(json.access_token, "access_token");
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export async function sendWecomAppMarkdown(params = {}, env = {}) {
|
|
81
|
+
const content = requireValue(params.markdown ?? params.content, "markdown");
|
|
82
|
+
const toUser = requireValue(params.toUser || envValue(env, ["WECOM_TO_USER", "WECHAT_WORK_TO_USER", "WXWORK_TO_USER"]), "toUser");
|
|
83
|
+
const agentIdRaw = requireValue(params.agentId || envValue(env, ["WECOM_AGENT_ID", "WECHAT_WORK_AGENT_ID", "WXWORK_AGENT_ID"]), "agentId");
|
|
84
|
+
const agentId = Number(agentIdRaw);
|
|
85
|
+
if (!Number.isFinite(agentId) || agentId <= 0) throw new Error(`Invalid agentId: ${agentIdRaw}`);
|
|
86
|
+
const accessToken = String(params.accessToken || "").trim() || await getWecomAccessToken(params, env);
|
|
87
|
+
const json = await requestJson(`https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=${encodeURIComponent(accessToken)}`, {
|
|
88
|
+
method: "POST",
|
|
89
|
+
body: JSON.stringify({
|
|
90
|
+
touser: toUser,
|
|
91
|
+
msgtype: "markdown",
|
|
92
|
+
agentid: agentId,
|
|
93
|
+
markdown: { content },
|
|
94
|
+
}),
|
|
95
|
+
});
|
|
96
|
+
assertWecomOk(json, "send wecom app markdown");
|
|
97
|
+
return {
|
|
98
|
+
ok: true,
|
|
99
|
+
message: "企业微信应用 Markdown 已发送",
|
|
100
|
+
response: json,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
@@ -50,6 +50,7 @@ import {
|
|
|
50
50
|
} from "../lib/runtime-context.mjs";
|
|
51
51
|
import { buildGitContext, inferGitRepoRootFromWorktree, loadGitWorktree, normalizeGitContext, unloadGitWorktree } from "../lib/git-worktree.mjs";
|
|
52
52
|
import { createGitLabMergeRequest } from "../lib/gitlab-mr.mjs";
|
|
53
|
+
import { sendWecomAppMarkdown, sendWecomGroupMarkdown } from "../lib/wecom.mjs";
|
|
53
54
|
|
|
54
55
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
55
56
|
|
|
@@ -470,6 +471,35 @@ async function emitGitLabCreateMrNode(workspaceRoot, flowName, uuid, instanceId,
|
|
|
470
471
|
return emitLocalNoopPrompt(workspaceRoot, runDir, instanceId, "gitlab-create-mr", `${result.message || "GitLab MR ready"}\n${result.mrUrl}\n`);
|
|
471
472
|
}
|
|
472
473
|
|
|
474
|
+
async function emitWecomMarkdownNode(workspaceRoot, flowName, uuid, instanceId, execId, definitionId) {
|
|
475
|
+
const runDir = getRunDir(workspaceRoot, flowName, uuid);
|
|
476
|
+
const data = getResolvedValues(workspaceRoot, flowName, uuid, instanceId);
|
|
477
|
+
const inputs = data.ok ? (data.resolvedInputs || {}) : {};
|
|
478
|
+
const result = definitionId === "tool_wecom_send_app_markdown"
|
|
479
|
+
? await sendWecomAppMarkdown({
|
|
480
|
+
markdown: inputs.markdown || inputs.content,
|
|
481
|
+
toUser: inputs.toUser,
|
|
482
|
+
corpId: inputs.corpId,
|
|
483
|
+
corpSecret: inputs.corpSecret,
|
|
484
|
+
agentId: inputs.agentId,
|
|
485
|
+
accessToken: inputs.accessToken,
|
|
486
|
+
}, process.env)
|
|
487
|
+
: await sendWecomGroupMarkdown({
|
|
488
|
+
markdown: inputs.markdown || inputs.content,
|
|
489
|
+
webhookUrl: inputs.webhookUrl,
|
|
490
|
+
webhookKey: inputs.webhookKey,
|
|
491
|
+
}, process.env);
|
|
492
|
+
writeOutputSlot(runDir, instanceId, execId, "sent", "true");
|
|
493
|
+
writeOutputSlot(runDir, instanceId, execId, "message", result.message);
|
|
494
|
+
writeOutputSlot(runDir, instanceId, execId, "response", JSON.stringify(result.response || {}));
|
|
495
|
+
writeResult(workspaceRoot, flowName, uuid, instanceId, {
|
|
496
|
+
status: "success",
|
|
497
|
+
message: result.message,
|
|
498
|
+
body: result.message,
|
|
499
|
+
}, { execId });
|
|
500
|
+
return emitLocalNoopPrompt(workspaceRoot, runDir, instanceId, "wecom-markdown", `${result.message}\n`);
|
|
501
|
+
}
|
|
502
|
+
|
|
473
503
|
function emitCdWorkspaceNode(workspaceRoot, flowName, uuid, instanceId, execId) {
|
|
474
504
|
const runDir = getRunDir(workspaceRoot, flowName, uuid);
|
|
475
505
|
const { inputs, workspaceContext } = resolveNodeRuntimeContexts(workspaceRoot, flowName, uuid, instanceId);
|
|
@@ -1173,7 +1203,7 @@ async function main() {
|
|
|
1173
1203
|
return;
|
|
1174
1204
|
}
|
|
1175
1205
|
|
|
1176
|
-
if (definitionId === "tool_git_checkout" || definitionId === "tool_git_worktree_load" || definitionId === "tool_git_worktree_unload" || definitionId === "tool_gitlab_create_mr" || definitionId === "control_cd_workspace" || definitionId === "control_user_workspace" || definitionId === "control_load_skills" || definitionId === "tool_print") {
|
|
1206
|
+
if (definitionId === "tool_git_checkout" || definitionId === "tool_git_worktree_load" || definitionId === "tool_git_worktree_unload" || definitionId === "tool_gitlab_create_mr" || definitionId === "tool_wecom_send_group_markdown" || definitionId === "tool_wecom_send_app_markdown" || definitionId === "control_cd_workspace" || definitionId === "control_user_workspace" || definitionId === "control_load_skills" || definitionId === "tool_print") {
|
|
1177
1207
|
try {
|
|
1178
1208
|
const promptPath =
|
|
1179
1209
|
definitionId === "tool_git_checkout"
|
|
@@ -1184,13 +1214,15 @@ async function main() {
|
|
|
1184
1214
|
? emitGitWorktreeUnloadNode(workspaceRoot, flowName, uuid, instanceId, execId)
|
|
1185
1215
|
: definitionId === "tool_gitlab_create_mr"
|
|
1186
1216
|
? await emitGitLabCreateMrNode(workspaceRoot, flowName, uuid, instanceId, execId)
|
|
1187
|
-
: definitionId === "
|
|
1188
|
-
?
|
|
1189
|
-
: definitionId === "
|
|
1190
|
-
?
|
|
1191
|
-
: definitionId === "
|
|
1192
|
-
?
|
|
1193
|
-
:
|
|
1217
|
+
: definitionId === "tool_wecom_send_group_markdown" || definitionId === "tool_wecom_send_app_markdown"
|
|
1218
|
+
? await emitWecomMarkdownNode(workspaceRoot, flowName, uuid, instanceId, execId, definitionId)
|
|
1219
|
+
: definitionId === "control_cd_workspace"
|
|
1220
|
+
? emitCdWorkspaceNode(workspaceRoot, flowName, uuid, instanceId, execId)
|
|
1221
|
+
: definitionId === "control_user_workspace"
|
|
1222
|
+
? emitUserWorkspaceNode(workspaceRoot, flowName, uuid, instanceId, execId)
|
|
1223
|
+
: definitionId === "control_load_skills"
|
|
1224
|
+
? emitLoadSkillsNode(workspaceRoot, flowName, uuid, instanceId, execId)
|
|
1225
|
+
: emitToolPrintNode(workspaceRoot, flowName, uuid, instanceId, execId);
|
|
1194
1226
|
writeCacheJsonForNode(workspaceRoot, flowName, uuid, instanceId, execId);
|
|
1195
1227
|
logToRunTag(workspaceRoot, flowName, uuid, "pre-process", { event: "runtime-context-node", instanceId, definitionId });
|
|
1196
1228
|
console.log(JSON.stringify({
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
---
|
|
2
|
+
# Built-in node: WeCom app markdown message
|
|
3
|
+
description: Send Markdown message to WeCom users through an enterprise application
|
|
4
|
+
displayName: WeCom App Markdown
|
|
5
|
+
input:
|
|
6
|
+
- type: node
|
|
7
|
+
name: prev
|
|
8
|
+
default: ""
|
|
9
|
+
- type: text
|
|
10
|
+
name: markdown
|
|
11
|
+
default: ""
|
|
12
|
+
required: true
|
|
13
|
+
description: "企业微信应用消息 markdown.content。请输出企业微信 Markdown 正文,不要包裹 agentflow envelope 或额外解释;适合通知、告警、任务摘要等个人消息。"
|
|
14
|
+
showOnNode: true
|
|
15
|
+
- type: text
|
|
16
|
+
name: toUser
|
|
17
|
+
default: ""
|
|
18
|
+
required: true
|
|
19
|
+
description: "接收人企业微信 userid;多个用户用 | 分隔,@all 表示全部。"
|
|
20
|
+
showOnNode: true
|
|
21
|
+
- type: text
|
|
22
|
+
name: corpId
|
|
23
|
+
default: ""
|
|
24
|
+
showOnNode: false
|
|
25
|
+
- type: text
|
|
26
|
+
name: corpSecret
|
|
27
|
+
default: ""
|
|
28
|
+
showOnNode: false
|
|
29
|
+
- type: text
|
|
30
|
+
name: agentId
|
|
31
|
+
default: ""
|
|
32
|
+
showOnNode: false
|
|
33
|
+
- type: text
|
|
34
|
+
name: accessToken
|
|
35
|
+
default: ""
|
|
36
|
+
showOnNode: false
|
|
37
|
+
output:
|
|
38
|
+
- type: node
|
|
39
|
+
name: next
|
|
40
|
+
default: ""
|
|
41
|
+
- type: bool
|
|
42
|
+
name: sent
|
|
43
|
+
default: ""
|
|
44
|
+
showOnNode: true
|
|
45
|
+
- type: text
|
|
46
|
+
name: message
|
|
47
|
+
default: ""
|
|
48
|
+
showOnNode: false
|
|
49
|
+
- type: text
|
|
50
|
+
name: response
|
|
51
|
+
default: ""
|
|
52
|
+
showOnNode: false
|
|
53
|
+
---
|
|
54
|
+
Send `${markdown}` to `${toUser}` using a WeCom enterprise application.
|
|
55
|
+
|
|
56
|
+
If credential inputs are empty, AgentFlow reads `WECOM_CORP_ID`, `WECOM_APP_SECRET` / `WECOM_CORP_SECRET`, `WECOM_AGENT_ID`, and optionally `WECOM_TO_USER` from environment config.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
---
|
|
2
|
+
# Built-in node: WeCom group robot markdown message
|
|
3
|
+
description: Send Markdown message to a WeCom group robot webhook
|
|
4
|
+
displayName: WeCom Group Markdown
|
|
5
|
+
input:
|
|
6
|
+
- type: node
|
|
7
|
+
name: prev
|
|
8
|
+
default: ""
|
|
9
|
+
- type: text
|
|
10
|
+
name: markdown
|
|
11
|
+
default: ""
|
|
12
|
+
required: true
|
|
13
|
+
description: "企业微信机器人 markdown.content。请输出企业微信 Markdown 正文,不要包裹 agentflow envelope 或额外解释;支持标题、列表、链接、引用、代码等企业微信 Markdown 子集。"
|
|
14
|
+
showOnNode: true
|
|
15
|
+
- type: text
|
|
16
|
+
name: webhookUrl
|
|
17
|
+
default: ""
|
|
18
|
+
description: "企业微信群机器人完整 webhook 地址。可以只填 webhookKey。"
|
|
19
|
+
showOnNode: true
|
|
20
|
+
- type: text
|
|
21
|
+
name: webhookKey
|
|
22
|
+
default: ""
|
|
23
|
+
description: "企业微信群机器人 key,用于拼出 https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=..."
|
|
24
|
+
showOnNode: false
|
|
25
|
+
output:
|
|
26
|
+
- type: node
|
|
27
|
+
name: next
|
|
28
|
+
default: ""
|
|
29
|
+
- type: bool
|
|
30
|
+
name: sent
|
|
31
|
+
default: ""
|
|
32
|
+
showOnNode: true
|
|
33
|
+
- type: text
|
|
34
|
+
name: message
|
|
35
|
+
default: ""
|
|
36
|
+
showOnNode: false
|
|
37
|
+
- type: text
|
|
38
|
+
name: response
|
|
39
|
+
default: ""
|
|
40
|
+
showOnNode: false
|
|
41
|
+
---
|
|
42
|
+
Send `${markdown}` to a WeCom group robot.
|
|
43
|
+
|
|
44
|
+
Use either `webhookUrl` or `webhookKey`. If both are empty, AgentFlow reads `WECOM_GROUP_WEBHOOK` / `WECOM_BOT_WEBHOOK` or `WECOM_GROUP_WEBHOOK_KEY` / `WECOM_BOT_KEY` from environment config.
|