@fieldwangai/agentflow 0.1.69 → 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.
@@ -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 === "control_cd_workspace"
1188
- ? emitCdWorkspaceNode(workspaceRoot, flowName, uuid, instanceId, execId)
1189
- : definitionId === "control_user_workspace"
1190
- ? emitUserWorkspaceNode(workspaceRoot, flowName, uuid, instanceId, execId)
1191
- : definitionId === "control_load_skills"
1192
- ? emitLoadSkillsNode(workspaceRoot, flowName, uuid, instanceId, execId)
1193
- : emitToolPrintNode(workspaceRoot, flowName, uuid, instanceId, execId);
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.