@fieldwangai/agentflow 0.1.149 → 0.1.154

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (195) hide show
  1. package/README.md +20 -33
  2. package/README.zh-CN.md +14 -34
  3. package/agents/agentflow-node-executor-code.md +13 -3
  4. package/agents/agentflow-node-executor-planning.md +13 -3
  5. package/agents/agentflow-node-executor-requirement.md +13 -3
  6. package/agents/agentflow-node-executor-test.md +13 -3
  7. package/agents/agentflow-node-executor-ui.md +13 -3
  8. package/agents/agentflow-node-executor.md +13 -3
  9. package/agents/en/agentflow-node-executor.md +13 -3
  10. package/agents/zh/agentflow-node-executor.md +13 -3
  11. package/bin/agentflow.mjs +3 -3
  12. package/bin/lib/admin-builtin-pipelines.mjs +3 -2
  13. package/bin/lib/agent-runners.mjs +0 -929
  14. package/bin/lib/auth.mjs +2 -1
  15. package/bin/lib/catalog-agents.mjs +2 -1
  16. package/bin/lib/catalog-flows.mjs +167 -88
  17. package/bin/lib/composer-agent.mjs +3 -793
  18. package/bin/lib/composer-skill-router.mjs +3 -323
  19. package/bin/lib/exec-buffered.mjs +32 -0
  20. package/bin/lib/flow-dsl/cli.mjs +276 -0
  21. package/bin/lib/flow-dsl/codegen.mjs +358 -0
  22. package/bin/lib/flow-dsl/defs.mjs +103 -0
  23. package/bin/lib/flow-dsl/index.mjs +77 -0
  24. package/bin/lib/flow-dsl/ir.mjs +305 -0
  25. package/bin/lib/flow-dsl/layout.mjs +115 -0
  26. package/bin/lib/flow-dsl/legacy-yaml.mjs +268 -0
  27. package/bin/lib/flow-dsl/lint.mjs +241 -0
  28. package/bin/lib/flow-dsl/packages.mjs +251 -0
  29. package/bin/lib/flow-dsl/parser.mjs +444 -0
  30. package/bin/lib/flow-import.mjs +96 -26
  31. package/bin/lib/flow-write.mjs +48 -12
  32. package/bin/lib/help.mjs +10 -30
  33. package/bin/lib/html-escape.mjs +19 -0
  34. package/bin/lib/http-util.mjs +49 -0
  35. package/bin/lib/legacy-flow-execution.mjs +43 -0
  36. package/bin/lib/locales/en.json +1 -1
  37. package/bin/lib/locales/zh.json +1 -1
  38. package/bin/lib/main.mjs +153 -283
  39. package/bin/lib/marketplace.mjs +206 -61
  40. package/bin/lib/model-config.mjs +10 -0
  41. package/bin/lib/node-package-archive.mjs +240 -0
  42. package/bin/lib/node-package-bootstrap.mjs +72 -0
  43. package/bin/lib/node-package-manifest.mjs +174 -0
  44. package/bin/lib/paths.mjs +42 -19
  45. package/bin/lib/prd-workflow-routes.mjs +2949 -0
  46. package/bin/lib/prd-workflow-server.mjs +4884 -0
  47. package/bin/lib/run-events.mjs +0 -66
  48. package/bin/lib/schedule-config.mjs +4 -4
  49. package/bin/lib/skill-runtime.mjs +35 -0
  50. package/bin/lib/startup-storage-migrations.mjs +274 -0
  51. package/bin/lib/ui-server.mjs +1106 -18013
  52. package/bin/lib/user-env.mjs +16 -0
  53. package/bin/lib/workspace-auto-layout.mjs +234 -0
  54. package/bin/lib/workspace-flow-store.mjs +478 -0
  55. package/bin/lib/workspace-graph-merge.mjs +41 -34
  56. package/bin/lib/workspace-preview.mjs +74 -0
  57. package/bin/lib/workspace-routes.mjs +3088 -0
  58. package/bin/lib/workspace-server.mjs +6199 -0
  59. package/bin/lib/workspace-state.mjs +331 -0
  60. package/bin/lib/workspace.mjs +2 -1
  61. package/builtin/nodes/agent_subAgent.md +1 -0
  62. package/builtin/nodes/control_agent_toBool.md +2 -0
  63. package/builtin/nodes/control_anyOne.md +2 -0
  64. package/builtin/nodes/control_cancelled.md +2 -0
  65. package/builtin/nodes/control_cd_workspace.md +5 -11
  66. package/builtin/nodes/control_delay.md +2 -0
  67. package/builtin/nodes/control_end.md +2 -0
  68. package/builtin/nodes/control_if.md +1 -0
  69. package/builtin/nodes/control_interval_loop.md +2 -0
  70. package/builtin/nodes/control_load_mcp.md +24 -0
  71. package/builtin/nodes/control_load_skills.md +8 -23
  72. package/builtin/nodes/control_start.md +2 -0
  73. package/builtin/nodes/control_toBool.md +2 -0
  74. package/builtin/nodes/control_user_workspace.md +2 -0
  75. package/builtin/nodes/control_wait_until.md +2 -0
  76. package/builtin/nodes/display_ascii.md +1 -0
  77. package/builtin/nodes/display_chart.md +1 -0
  78. package/builtin/nodes/display_html.md +1 -0
  79. package/builtin/nodes/display_image.md +1 -0
  80. package/builtin/nodes/display_markdown.md +1 -0
  81. package/builtin/nodes/display_mermaid.md +1 -0
  82. package/builtin/nodes/display_react_app.md +1 -0
  83. package/builtin/nodes/display_table.md +1 -0
  84. package/builtin/nodes/provide_bool.md +1 -0
  85. package/builtin/nodes/provide_file.md +1 -0
  86. package/builtin/nodes/provide_password.md +1 -0
  87. package/builtin/nodes/provide_str.md +1 -0
  88. package/builtin/nodes/tool_display_share_link.md +1 -0
  89. package/builtin/nodes/tool_get_env.md +2 -0
  90. package/builtin/nodes/tool_git_checkout.md +1 -0
  91. package/builtin/nodes/tool_git_worktree_load.md +1 -0
  92. package/builtin/nodes/tool_git_worktree_unload.md +1 -0
  93. package/builtin/nodes/tool_gitlab_create_mr.md +1 -0
  94. package/builtin/nodes/tool_jenkins_build.md +2 -0
  95. package/builtin/nodes/tool_load_key.md +2 -0
  96. package/builtin/nodes/tool_nodejs.md +17 -19
  97. package/builtin/nodes/tool_print.md +2 -0
  98. package/builtin/nodes/tool_save_key.md +2 -0
  99. package/builtin/nodes/tool_set_run_env.md +1 -0
  100. package/builtin/nodes/tool_user_ask.md +2 -0
  101. package/builtin/nodes/tool_user_check.md +2 -0
  102. package/builtin/nodes/tool_wecom_send_app_markdown.md +1 -0
  103. package/builtin/nodes/tool_wecom_send_group_markdown.md +1 -0
  104. package/builtin/nodes/workspace_one_click_task.md +44 -0
  105. package/builtin/nodes/workspace_run.md +16 -0
  106. package/builtin/nodes/workspace_scheduled_run.md +16 -0
  107. package/builtin/pipelines/module-migrate/scripts/gate.mjs +37 -0
  108. package/builtin/pipelines/module-migrate/scripts/static-check.mjs +82 -0
  109. package/builtin/pipelines/module-migrate/workspace.flow.js +172 -0
  110. package/builtin/pipelines/module-migrate/workspace.layout.json +134 -0
  111. package/builtin/pipelines/new/scripts/lint-flow.mjs +38 -0
  112. package/builtin/pipelines/new/workspace.flow.js +91 -0
  113. package/builtin/pipelines/new/workspace.layout.json +70 -0
  114. package/builtin/web-ui/dist/assets/{WorkflowAssistantThread-DPbR2UaN.js → WorkflowAssistantThread-CKClwj96.js} +1 -1
  115. package/builtin/web-ui/dist/assets/index-BZ5KqLur.js +870 -0
  116. package/builtin/web-ui/dist/assets/index-CEXmmwM2.css +1 -0
  117. package/builtin/web-ui/dist/index.html +2 -2
  118. package/package.json +2 -1
  119. package/reference/flow-control-capabilities.md +77 -158
  120. package/reference/flow-layout.md +1 -1
  121. package/reference/flow-prompt-handler-check.md +2 -2
  122. package/skills/agentflow-author-flow/SKILL.md +8 -47
  123. package/skills/agentflow-cli/SKILL.md +158 -25
  124. package/skills/agentflow-cli/agents/openai.yaml +2 -2
  125. package/skills/agentflow-cli/scripts/agentflow-cli.mjs +668 -18
  126. package/skills/agentflow-cli/scripts/agentflow-runtime.mjs +97 -0
  127. package/skills/agentflow-flow-add-instances/SKILL.md +7 -248
  128. package/skills/agentflow-flow-dsl/SKILL.md +206 -0
  129. package/skills/agentflow-flow-dsl/agents/openai.yaml +4 -0
  130. package/skills/agentflow-flow-dsl/references/node-calls.md +39 -0
  131. package/skills/agentflow-flow-edit-node-fields/SKILL.md +4 -73
  132. package/skills/agentflow-flow-recipes/SKILL.md +7 -18
  133. package/skills/agentflow-flow-recipes/references/recipes.md +97 -43
  134. package/skills/agentflow-flow-sync-ui/SKILL.md +3 -54
  135. package/skills/agentflow-node-dsl/SKILL.md +212 -0
  136. package/skills/agentflow-node-dsl/agents/openai.yaml +4 -0
  137. package/skills/agentflow-node-reference/SKILL.md +5 -4
  138. package/skills/agentflow-node-reference/references/builtin-nodes.md +166 -115
  139. package/skills/agentflow-placeholder-reference/SKILL.md +2 -2
  140. package/skills/agentflow-runtime-reference/SKILL.md +2 -2
  141. package/skills/agentflow-runtime-reference/references/runtime.md +1 -1
  142. package/skills/agentflow-workspace-ascii/SKILL.md +9 -16
  143. package/skills/agentflow-workspace-graph/SKILL.md +62 -48
  144. package/skills/agentflow-workspace-html/SKILL.md +7 -2
  145. package/skills/agentflow-workspace-image/SKILL.md +6 -2
  146. package/skills/agentflow-workspace-markdown/SKILL.md +14 -21
  147. package/skills/agentflow-workspace-mermaid/SKILL.md +8 -16
  148. package/bin/lib/api-runner.mjs +0 -387
  149. package/bin/lib/apply.mjs +0 -903
  150. package/bin/lib/composer-flow-instances.mjs +0 -68
  151. package/bin/lib/composer-flow-skeleton.mjs +0 -334
  152. package/bin/lib/composer-flow-validate.mjs +0 -47
  153. package/bin/lib/composer-model-router.mjs +0 -185
  154. package/bin/lib/composer-node-schema.mjs +0 -303
  155. package/bin/lib/composer-planner.mjs +0 -751
  156. package/bin/lib/composer-script-ops.mjs +0 -233
  157. package/bin/lib/flow-static-preview.mjs +0 -104
  158. package/bin/lib/hub-login.mjs +0 -54
  159. package/bin/lib/hub-publish.mjs +0 -159
  160. package/bin/lib/hub-remote.mjs +0 -189
  161. package/bin/lib/hub.mjs +0 -299
  162. package/bin/lib/jenkins.mjs +0 -380
  163. package/bin/lib/node-execute.mjs +0 -539
  164. package/bin/lib/normalize-node-tool-command.mjs +0 -97
  165. package/bin/lib/runtime-context.mjs +0 -243
  166. package/bin/lib/scheduler.mjs +0 -601
  167. package/bin/lib/ui-print.mjs +0 -94
  168. package/bin/pipeline/build-node-prompt.mjs +0 -271
  169. package/bin/pipeline/check-cache.mjs +0 -191
  170. package/bin/pipeline/check-flow.mjs +0 -543
  171. package/bin/pipeline/collect-nodes.mjs +0 -212
  172. package/bin/pipeline/compute-cache-md5.mjs +0 -177
  173. package/bin/pipeline/ensure-run-dir.mjs +0 -71
  174. package/bin/pipeline/gc.mjs +0 -129
  175. package/bin/pipeline/get-env.mjs +0 -59
  176. package/bin/pipeline/get-resolved-values.mjs +0 -344
  177. package/bin/pipeline/load-key.mjs +0 -62
  178. package/bin/pipeline/parse-flow.mjs +0 -708
  179. package/bin/pipeline/post-process-control-if.mjs +0 -23
  180. package/bin/pipeline/post-process-node.mjs +0 -490
  181. package/bin/pipeline/pre-process-node.mjs +0 -1430
  182. package/bin/pipeline/resolve-inputs.mjs +0 -201
  183. package/bin/pipeline/run-tool-nodejs.mjs +0 -167
  184. package/bin/pipeline/save-key.mjs +0 -93
  185. package/bin/pipeline/snapshot-prior-round.mjs +0 -70
  186. package/bin/pipeline/validate-for-ui.mjs +0 -234
  187. package/bin/pipeline/validate-script-output.mjs +0 -130
  188. package/bin/pipeline/write-result.mjs +0 -182
  189. package/builtin/pipelines/module-migrate/flow.yaml +0 -819
  190. package/builtin/pipelines/new/flow.yaml +0 -545
  191. package/builtin/pipelines/new/scripts/check-flow.mjs +0 -9
  192. package/builtin/pipelines/new/scripts/collect-nodes.mjs +0 -211
  193. package/builtin/web-ui/dist/assets/index-BSCpd5la.js +0 -889
  194. package/builtin/web-ui/dist/assets/index-DZ328oSo.css +0 -1
  195. package/skills/agentflow-node-authoring/SKILL.md +0 -57
@@ -1,233 +0,0 @@
1
- /**
2
- * Composer 脚本操作层:直接用 Node.js 修改 flow.yaml,无需 AI。
3
- * 适用于确定性操作(改 label、body、role、value、连线、位置等)。
4
- */
5
- import fs from "fs";
6
- import yaml from "js-yaml";
7
-
8
- // ─── YAML 读写 ─────────────────────────────────────────────────────────────
9
-
10
- function readFlowYaml(flowYamlAbs) {
11
- const raw = fs.readFileSync(flowYamlAbs, "utf-8");
12
- const doc = yaml.load(raw);
13
- if (!doc || typeof doc !== "object") throw new Error("flow.yaml 解析失败或为空");
14
- return { doc, raw };
15
- }
16
-
17
- function writeFlowYaml(flowYamlAbs, doc) {
18
- const out = yaml.dump(doc, {
19
- lineWidth: -1,
20
- noRefs: true,
21
- quotingType: "'",
22
- forceQuotes: false,
23
- sortKeys: false,
24
- });
25
- fs.writeFileSync(flowYamlAbs, out, "utf-8");
26
- }
27
-
28
- function ensureInstances(doc) {
29
- if (!doc.instances || typeof doc.instances !== "object") {
30
- doc.instances = {};
31
- }
32
- return doc.instances;
33
- }
34
-
35
- function ensureEdges(doc) {
36
- if (!Array.isArray(doc.edges)) doc.edges = [];
37
- return doc.edges;
38
- }
39
-
40
- function ensureUi(doc) {
41
- if (!doc.ui || typeof doc.ui !== "object") doc.ui = {};
42
- if (!doc.ui.nodePositions || typeof doc.ui.nodePositions !== "object") doc.ui.nodePositions = {};
43
- return doc.ui;
44
- }
45
-
46
- // ─── 操作实现 ──────────────────────────────────────────────────────────────
47
-
48
- function opEditLabel(doc, params) {
49
- const inst = ensureInstances(doc);
50
- const { instanceId, value } = params;
51
- if (!inst[instanceId]) throw new Error(`实例 ${instanceId} 不存在`);
52
- inst[instanceId].label = value;
53
- return `节点 ${instanceId} 的 label 已改为「${value}」`;
54
- }
55
-
56
- function opEditBody(doc, params) {
57
- const inst = ensureInstances(doc);
58
- const { instanceId, value } = params;
59
- if (!inst[instanceId]) throw new Error(`实例 ${instanceId} 不存在`);
60
- inst[instanceId].body = value;
61
- return `节点 ${instanceId} 的 body 已更新`;
62
- }
63
-
64
- function opEditScript(doc, params) {
65
- const inst = ensureInstances(doc);
66
- const { instanceId, value } = params;
67
- if (!inst[instanceId]) throw new Error(`实例 ${instanceId} 不存在`);
68
- if (inst[instanceId].definitionId !== "tool_nodejs") {
69
- throw new Error(`节点 ${instanceId} 的 definitionId 不是 tool_nodejs,不能设置 script 字段`);
70
- }
71
- inst[instanceId].script = value;
72
- return `节点 ${instanceId} 的 script 已更新`;
73
- }
74
-
75
- function opEditRole(doc, params) {
76
- const inst = ensureInstances(doc);
77
- const { instanceId, value } = params;
78
- if (!inst[instanceId]) throw new Error(`实例 ${instanceId} 不存在`);
79
- inst[instanceId].role = value;
80
- return `节点 ${instanceId} 的 role 已改为「${value}」`;
81
- }
82
-
83
- function opEditModel(doc, params) {
84
- const inst = ensureInstances(doc);
85
- const { instanceId, value } = params;
86
- if (!inst[instanceId]) throw new Error(`实例 ${instanceId} 不存在`);
87
- inst[instanceId].model = value;
88
- return `节点 ${instanceId} 的 model 已改为「${value}」`;
89
- }
90
-
91
- function opEditInputValue(doc, params) {
92
- const inst = ensureInstances(doc);
93
- const { instanceId, inputName, value } = params;
94
- if (!inst[instanceId]) throw new Error(`实例 ${instanceId} 不存在`);
95
- const inputs = inst[instanceId].input;
96
- if (!Array.isArray(inputs)) throw new Error(`实例 ${instanceId} 没有 input 数组`);
97
- const slot = inputs.find((s) => s.name === inputName);
98
- if (!slot) throw new Error(`实例 ${instanceId} 没有名为 ${inputName} 的输入`);
99
- slot.value = value;
100
- return `节点 ${instanceId} 输入 ${inputName} 的 value 已更新`;
101
- }
102
-
103
- function opEditOutputValue(doc, params) {
104
- const inst = ensureInstances(doc);
105
- const { instanceId, outputName, value } = params;
106
- if (!inst[instanceId]) throw new Error(`实例 ${instanceId} 不存在`);
107
- const outputs = inst[instanceId].output;
108
- if (!Array.isArray(outputs)) throw new Error(`实例 ${instanceId} 没有 output 数组`);
109
- const slot = outputs.find((s) => s.name === outputName);
110
- if (!slot) throw new Error(`实例 ${instanceId} 没有名为 ${outputName} 的输出`);
111
- slot.value = value;
112
- return `节点 ${instanceId} 输出 ${outputName} 的 value 已更新`;
113
- }
114
-
115
- function opAddEdge(doc, params) {
116
- const edges = ensureEdges(doc);
117
- const { source, target, sourceHandle, targetHandle } = params;
118
- if (!source || !target) throw new Error("连线需要 source 和 target");
119
- const exists = edges.some(
120
- (e) =>
121
- e.source === source &&
122
- e.target === target &&
123
- (!sourceHandle || e.sourceHandle === sourceHandle) &&
124
- (!targetHandle || e.targetHandle === targetHandle),
125
- );
126
- if (exists) return `边 ${source} → ${target} 已存在,跳过`;
127
- const edge = { source, target };
128
- if (sourceHandle) edge.sourceHandle = sourceHandle;
129
- if (targetHandle) edge.targetHandle = targetHandle;
130
- edges.push(edge);
131
- return `已添加边 ${source} → ${target}`;
132
- }
133
-
134
- function opRemoveEdge(doc, params) {
135
- const edges = ensureEdges(doc);
136
- const { source, target, sourceHandle, targetHandle } = params;
137
- const before = edges.length;
138
- const filtered = edges.filter((e) => {
139
- if (source && e.source !== source) return true;
140
- if (target && e.target !== target) return true;
141
- if (sourceHandle && e.sourceHandle !== sourceHandle) return true;
142
- if (targetHandle && e.targetHandle !== targetHandle) return true;
143
- return false;
144
- });
145
- doc.edges = filtered;
146
- const removed = before - filtered.length;
147
- return removed > 0 ? `已删除 ${removed} 条边` : "未找到匹配的边";
148
- }
149
-
150
- function opUpdatePosition(doc, params) {
151
- const ui = ensureUi(doc);
152
- const { instanceId, x, y } = params;
153
- if (!ui.nodePositions[instanceId]) ui.nodePositions[instanceId] = {};
154
- if (x != null) ui.nodePositions[instanceId].x = Number(x);
155
- if (y != null) ui.nodePositions[instanceId].y = Number(y);
156
- return `节点 ${instanceId} 位置已更新为 (${x}, ${y})`;
157
- }
158
-
159
- const OP_HANDLERS = {
160
- "edit-label": opEditLabel,
161
- "edit-body": opEditBody,
162
- "edit-script": opEditScript,
163
- "edit-role": opEditRole,
164
- "edit-model": opEditModel,
165
- "edit-input-value": opEditInputValue,
166
- "edit-output-value": opEditOutputValue,
167
- "add-edge": opAddEdge,
168
- "remove-edge": opRemoveEdge,
169
- "update-position": opUpdatePosition,
170
- };
171
-
172
- // ─── 公开接口 ──────────────────────────────────────────────────────────────
173
-
174
- /**
175
- * 执行一个 script 类型步骤。
176
- * @param {string} flowYamlAbs flow.yaml 绝对路径
177
- * @param {{ op: string, params: object }} step
178
- * @returns {{ success: boolean, message: string }}
179
- */
180
- export function executeScriptOp(flowYamlAbs, step) {
181
- const handler = OP_HANDLERS[step.op];
182
- if (!handler) {
183
- return { success: false, message: `未知操作: ${step.op}` };
184
- }
185
- try {
186
- const { doc } = readFlowYaml(flowYamlAbs);
187
- const message = handler(doc, step.params || {});
188
- writeFlowYaml(flowYamlAbs, doc);
189
- return { success: true, message };
190
- } catch (e) {
191
- return { success: false, message: e.message || String(e) };
192
- }
193
- }
194
-
195
- /**
196
- * 批量执行多个 script 操作(共享一次 YAML 读写)。
197
- * @param {string} flowYamlAbs
198
- * @param {Array<{ op: string, params: object }>} steps
199
- * @returns {Array<{ success: boolean, message: string }>}
200
- */
201
- export function executeScriptOpsBatch(flowYamlAbs, steps) {
202
- const { doc } = readFlowYaml(flowYamlAbs);
203
- const results = [];
204
- for (const step of steps) {
205
- const handler = OP_HANDLERS[step.op];
206
- if (!handler) {
207
- results.push({ success: false, message: `未知操作: ${step.op}` });
208
- continue;
209
- }
210
- try {
211
- const message = handler(doc, step.params || {});
212
- results.push({ success: true, message });
213
- } catch (e) {
214
- results.push({ success: false, message: e.message || String(e) });
215
- }
216
- }
217
- writeFlowYaml(flowYamlAbs, doc);
218
- return results;
219
- }
220
-
221
- /**
222
- * 检查一个操作名称是否属于已支持的 script 操作。
223
- */
224
- export function isSupportedScriptOp(op) {
225
- return op in OP_HANDLERS;
226
- }
227
-
228
- /**
229
- * 返回所有支持的 script 操作名称列表。
230
- */
231
- export function listScriptOps() {
232
- return Object.keys(OP_HANDLERS);
233
- }
@@ -1,104 +0,0 @@
1
- import crypto from "node:crypto";
2
- import fs from "node:fs";
3
- import path from "node:path";
4
-
5
- function assetPathFromHtml(indexHtml, pattern, label, distDir) {
6
- const match = indexHtml.match(pattern);
7
- if (!match?.[1]) throw new Error(`AgentFlow Web UI ${label} asset is missing; run npm run build:web-ui first`);
8
- const assetPath = path.join(distDir, match[1].replace(/^\/+/, ""));
9
- if (!fs.existsSync(assetPath) || !fs.statSync(assetPath).isFile()) {
10
- throw new Error(`AgentFlow Web UI ${label} asset not found: ${assetPath}`);
11
- }
12
- return assetPath;
13
- }
14
-
15
- function safeInlineJson(value) {
16
- return JSON.stringify(value)
17
- .replace(/</g, "\\u003c")
18
- .replace(/\u2028/g, "\\u2028")
19
- .replace(/\u2029/g, "\\u2029");
20
- }
21
-
22
- function safeInlineScript(source) {
23
- return String(source).replace(/<\/script/gi, "<\\/script");
24
- }
25
-
26
- function safeInlineStyle(source) {
27
- return String(source).replace(/<\/style/gi, "<\\/style");
28
- }
29
-
30
- function escapeHtml(value) {
31
- return String(value)
32
- .replace(/&/g, "&amp;")
33
- .replace(/</g, "&lt;")
34
- .replace(/>/g, "&gt;")
35
- .replace(/"/g, "&quot;");
36
- }
37
-
38
- /**
39
- * Build one self-contained, read-only HTML snapshot using the production Web UI bundle.
40
- * No local HTTP server or AgentFlow process is required after this function returns.
41
- */
42
- export function writeStaticFlowPreview({
43
- flowId,
44
- flowPath,
45
- nodeCatalog,
46
- outputPath,
47
- distDir,
48
- }) {
49
- const indexPath = path.join(distDir, "index.html");
50
- if (!fs.existsSync(indexPath)) {
51
- throw new Error(`AgentFlow Web UI build not found: ${indexPath}; run npm run build:web-ui first`);
52
- }
53
- const indexHtml = fs.readFileSync(indexPath, "utf-8");
54
- const scriptPath = assetPathFromHtml(
55
- indexHtml,
56
- /<script[^>]+src=["']([^"']+\.js)["'][^>]*><\/script>/i,
57
- "JavaScript",
58
- distDir,
59
- );
60
- const stylePath = assetPathFromHtml(
61
- indexHtml,
62
- /<link[^>]+href=["']([^"']+\.css)["'][^>]*>/i,
63
- "CSS",
64
- distDir,
65
- );
66
- const flowYaml = fs.readFileSync(flowPath, "utf-8");
67
- const payload = {
68
- format: "agentflow-static-preview-v1",
69
- flow: { id: String(flowId || "local-preview"), source: "preview", archived: false },
70
- flowYaml,
71
- nodeCatalog: nodeCatalog && typeof nodeCatalog === "object" ? nodeCatalog : { nodes: [] },
72
- revision: crypto.createHash("sha256").update(flowYaml).digest("hex").slice(0, 24),
73
- };
74
- const appScript = safeInlineScript(fs.readFileSync(scriptPath, "utf-8"));
75
- const appStyle = safeInlineStyle(fs.readFileSync(stylePath, "utf-8"));
76
- const title = `${escapeHtml(flowId || "Flow")} · AgentFlow Preview`;
77
- const html = `<!doctype html>
78
- <html class="af-dark" lang="zh-CN">
79
- <head>
80
- <meta charset="UTF-8" />
81
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
82
- <meta name="generator" content="AgentFlow static Flow preview" />
83
- <title>${title}</title>
84
- <link rel="preconnect" href="https://fonts.googleapis.com" />
85
- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
86
- <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&family=Space+Grotesk:wght@400;500;600;700&display=swap" rel="stylesheet" />
87
- <link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@24,400,0,0" rel="stylesheet" />
88
- <style>${appStyle}</style>
89
- <script>window.__AGENTFLOW_STATIC_FLOW_PREVIEW__=${safeInlineJson(payload)};</script>
90
- </head>
91
- <body>
92
- <div id="root"></div>
93
- <script type="module">${appScript}</script>
94
- </body>
95
- </html>
96
- `;
97
- fs.mkdirSync(path.dirname(outputPath), { recursive: true });
98
- fs.writeFileSync(outputPath, html, "utf-8");
99
- return {
100
- outputPath,
101
- bytes: Buffer.byteLength(html),
102
- revision: payload.revision,
103
- };
104
- }
@@ -1,54 +0,0 @@
1
- /**
2
- * agentflow login [--provider github|google]
3
- * agentflow logout
4
- */
5
- import fs from "fs";
6
- import os from "os";
7
- import path from "path";
8
- import chalk from "chalk";
9
- import { log } from "./log.mjs";
10
- import { getStoredSession, getUserProfile, loginWithBrowser } from "./hub.mjs";
11
-
12
- export async function hubLogin(argv) {
13
- // Check if already logged in
14
- const existing = await getStoredSession();
15
- if (existing?.access_token) {
16
- const user = await getUserProfile(existing.access_token);
17
- if (user?.id) {
18
- log.info(chalk.green("✓") + " Already logged in as " + chalk.bold(user.email || user.id));
19
- log.info(" Use " + chalk.dim("agentflow logout") + " to sign out.");
20
- return;
21
- }
22
- }
23
-
24
- // Parse --provider flag
25
- let provider = "github";
26
- const providerIdx = argv.indexOf("--provider");
27
- if (providerIdx >= 0 && argv[providerIdx + 1]) {
28
- provider = argv[providerIdx + 1];
29
- }
30
- if (!["github", "google"].includes(provider)) {
31
- throw new Error("Invalid provider: " + provider + ". Use github or google.");
32
- }
33
-
34
- log.info("Logging in to AgentFlow Hub via " + chalk.bold(provider) + "...");
35
- const session = await loginWithBrowser(provider);
36
-
37
- // Fetch user info
38
- const user = await getUserProfile(session.access_token);
39
- if (user?.id) {
40
- log.info(chalk.green("✓") + " Logged in as " + chalk.bold(user.email || user.id));
41
- } else {
42
- log.info(chalk.green("✓") + " Login successful. Token stored.");
43
- }
44
- }
45
-
46
- export function hubLogout() {
47
- const configPath = path.join(os.homedir(), ".agentflow", "hub.json");
48
- try {
49
- fs.unlinkSync(configPath);
50
- log.info(chalk.green("✓") + " Logged out. Token removed.");
51
- } catch {
52
- log.info("Not logged in.");
53
- }
54
- }
@@ -1,159 +0,0 @@
1
- /**
2
- * agentflow publish <FlowName> [--title <title>] [--description <desc>] [--tags <t1,t2>]
3
- *
4
- * Reads flow.yaml (or zips the flow directory if scripts/ exists),
5
- * uploads to Hub, and inserts the flow record.
6
- */
7
- import fs from "fs";
8
- import path from "path";
9
- import { execSync } from "child_process";
10
- import chalk from "chalk";
11
- import yaml from "js-yaml";
12
- import { log } from "./log.mjs";
13
- import {
14
- getStoredSession,
15
- getUserProfile,
16
- uploadToStorage,
17
- insertFlow,
18
- findFlowByAuthorAndTitle,
19
- updateFlow,
20
- deleteStorageObject,
21
- } from "./hub.mjs";
22
- import { getFlowDir } from "./workspace.mjs";
23
-
24
- function slugify(text) {
25
- return text
26
- .toLowerCase()
27
- .replace(/[^a-z0-9]+/g, "-")
28
- .replace(/^-+|-+$/g, "")
29
- .slice(0, 60);
30
- }
31
-
32
- function countNodes(yamlContent) {
33
- try {
34
- const doc = yaml.load(yamlContent);
35
- if (doc?.nodes && Array.isArray(doc.nodes)) return doc.nodes.length;
36
- if (doc?.pipeline?.nodes && Array.isArray(doc.pipeline.nodes)) return doc.pipeline.nodes.length;
37
- return 0;
38
- } catch {
39
- return 0;
40
- }
41
- }
42
-
43
- export async function hubPublish(workspaceRoot, argv) {
44
- // Auth check
45
- const session = await getStoredSession();
46
- if (!session?.access_token) {
47
- throw new Error("Not logged in. Run: agentflow login");
48
- }
49
- const user = await getUserProfile(session.access_token);
50
- if (!user?.id) {
51
- throw new Error("Session expired. Run: agentflow login");
52
- }
53
-
54
- // Parse args
55
- const flowName = argv.find((a) => !a.startsWith("--"));
56
- if (!flowName) {
57
- throw new Error("Usage: agentflow publish <FlowName> [--title <title>] [--description <desc>] [--tags <t1,t2>]");
58
- }
59
-
60
- let titleOpt, descOpt, tagsOpt;
61
- const titleIdx = argv.indexOf("--title");
62
- if (titleIdx >= 0 && argv[titleIdx + 1]) titleOpt = argv[titleIdx + 1];
63
- const descIdx = argv.indexOf("--description");
64
- if (descIdx >= 0 && argv[descIdx + 1]) descOpt = argv[descIdx + 1];
65
- const tagsIdx = argv.indexOf("--tags");
66
- if (tagsIdx >= 0 && argv[tagsIdx + 1]) tagsOpt = argv[tagsIdx + 1];
67
-
68
- // Find flow directory
69
- const flowDir = getFlowDir(workspaceRoot, flowName);
70
- if (!flowDir) {
71
- throw new Error("Flow not found: " + flowName);
72
- }
73
-
74
- const flowYamlPath = path.join(flowDir, "flow.yaml");
75
- if (!fs.existsSync(flowYamlPath)) {
76
- throw new Error("flow.yaml not found in " + flowDir);
77
- }
78
-
79
- const yamlContent = fs.readFileSync(flowYamlPath, "utf8");
80
- const nodeCount = countNodes(yamlContent);
81
-
82
- // Auto-read metadata from flow.yaml
83
- let flowDesc = null;
84
- try {
85
- const doc = yaml.load(yamlContent);
86
- if (doc?.ui?.description) flowDesc = doc.ui.description;
87
- } catch {}
88
-
89
- const title = titleOpt || flowName;
90
- const description = descOpt || flowDesc || null;
91
- const tags = tagsOpt ? tagsOpt.split(",").map((s) => s.trim()).filter(Boolean) : [];
92
-
93
- // Check if flow directory has scripts/ or other files beyond flow.yaml
94
- const entries = fs.readdirSync(flowDir);
95
- const hasExtras = entries.some((e) => e !== "flow.yaml" && e !== ".DS_Store");
96
-
97
- // Check if this author already published a flow with this title — update instead of insert.
98
- const existing = await findFlowByAuthorAndTitle(session.access_token, user.id, title);
99
-
100
- let fileBuffer, fileKey, contentType;
101
- const ext = hasExtras ? ".zip" : ".yaml";
102
- const slug = existing?.slug || slugify(title) + "-" + Date.now().toString(36);
103
- fileKey = `${user.id}/${slug}${ext}`;
104
-
105
- if (hasExtras) {
106
- log.info("Flow has scripts/extras — creating zip...");
107
- const zipPath = path.join(flowDir, ".hub-upload.zip");
108
- try {
109
- execSync(`cd "${flowDir}" && zip -r "${zipPath}" . -x ".*"`, { stdio: "pipe" });
110
- fileBuffer = fs.readFileSync(zipPath);
111
- contentType = "application/zip";
112
- } finally {
113
- try { fs.unlinkSync(zipPath); } catch {}
114
- }
115
- } else {
116
- fileBuffer = Buffer.from(yamlContent, "utf8");
117
- contentType = "text/yaml";
118
- }
119
-
120
- // Updating:先删老 artifact 再 INSERT 新文件。
121
- // Supabase Storage 的 bucket 策略多数只放行 INSERT;x-upsert=true 走 UPDATE 路径
122
- // 会被拒成 "new row violates row-level security policy"。DELETE + INSERT 最稳:
123
- // 不论扩展名是否变,老对象先清掉,再走纯 INSERT。
124
- if (existing && existing.yaml_key) {
125
- log.info("Removing old artifact: " + existing.yaml_key);
126
- await deleteStorageObject(session.access_token, existing.yaml_key);
127
- }
128
-
129
- log.info("Uploading " + (hasExtras ? "zip" : "flow.yaml") + " (" + (fileBuffer.length / 1024).toFixed(1) + " KB)...");
130
- await uploadToStorage(session.access_token, fileKey, fileBuffer, contentType);
131
-
132
- if (existing) {
133
- log.info("Updating existing flow record...");
134
- await updateFlow(session.access_token, existing.id, {
135
- description,
136
- tags,
137
- yaml_key: fileKey,
138
- node_count: nodeCount,
139
- });
140
- log.info(chalk.green("✓") + " Updated: " + chalk.bold(title));
141
- } else {
142
- log.info("Publishing flow record...");
143
- await insertFlow(session.access_token, {
144
- slug,
145
- author_id: user.id,
146
- title,
147
- description,
148
- tags,
149
- yaml_key: fileKey,
150
- node_count: nodeCount,
151
- });
152
- log.info(chalk.green("✓") + " Published: " + chalk.bold(title));
153
- }
154
-
155
- log.info(" slug: " + slug);
156
- log.info(" nodes: " + nodeCount);
157
- if (hasExtras) log.info(" type: zip (includes scripts)");
158
- log.info(" " + chalk.dim("View at: https://agentflow-hub.com/flows/" + slug));
159
- }