@fieldwangai/agentflow 0.1.153 → 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 (192) hide show
  1. package/README.md +16 -34
  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 +165 -90
  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 +8 -28
  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 +37 -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 +152 -286
  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 +1087 -18122
  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-routes.mjs +3088 -0
  57. package/bin/lib/workspace-server.mjs +6199 -0
  58. package/bin/lib/workspace-state.mjs +331 -0
  59. package/bin/lib/workspace.mjs +2 -1
  60. package/builtin/nodes/agent_subAgent.md +1 -0
  61. package/builtin/nodes/control_agent_toBool.md +2 -0
  62. package/builtin/nodes/control_anyOne.md +2 -0
  63. package/builtin/nodes/control_cancelled.md +2 -0
  64. package/builtin/nodes/control_cd_workspace.md +5 -11
  65. package/builtin/nodes/control_delay.md +2 -0
  66. package/builtin/nodes/control_end.md +2 -0
  67. package/builtin/nodes/control_if.md +1 -0
  68. package/builtin/nodes/control_interval_loop.md +2 -0
  69. package/builtin/nodes/control_load_mcp.md +24 -0
  70. package/builtin/nodes/control_load_skills.md +8 -23
  71. package/builtin/nodes/control_start.md +2 -0
  72. package/builtin/nodes/control_toBool.md +2 -0
  73. package/builtin/nodes/control_user_workspace.md +2 -0
  74. package/builtin/nodes/control_wait_until.md +2 -0
  75. package/builtin/nodes/display_ascii.md +1 -0
  76. package/builtin/nodes/display_chart.md +1 -0
  77. package/builtin/nodes/display_html.md +1 -0
  78. package/builtin/nodes/display_image.md +1 -0
  79. package/builtin/nodes/display_markdown.md +1 -0
  80. package/builtin/nodes/display_mermaid.md +1 -0
  81. package/builtin/nodes/display_react_app.md +1 -0
  82. package/builtin/nodes/display_table.md +1 -0
  83. package/builtin/nodes/provide_bool.md +1 -0
  84. package/builtin/nodes/provide_file.md +1 -0
  85. package/builtin/nodes/provide_password.md +1 -0
  86. package/builtin/nodes/provide_str.md +1 -0
  87. package/builtin/nodes/tool_display_share_link.md +1 -0
  88. package/builtin/nodes/tool_get_env.md +2 -0
  89. package/builtin/nodes/tool_git_checkout.md +1 -0
  90. package/builtin/nodes/tool_git_worktree_load.md +1 -0
  91. package/builtin/nodes/tool_git_worktree_unload.md +1 -0
  92. package/builtin/nodes/tool_gitlab_create_mr.md +1 -0
  93. package/builtin/nodes/tool_jenkins_build.md +2 -0
  94. package/builtin/nodes/tool_load_key.md +2 -0
  95. package/builtin/nodes/tool_nodejs.md +17 -19
  96. package/builtin/nodes/tool_print.md +2 -0
  97. package/builtin/nodes/tool_save_key.md +2 -0
  98. package/builtin/nodes/tool_set_run_env.md +1 -0
  99. package/builtin/nodes/tool_user_ask.md +2 -0
  100. package/builtin/nodes/tool_user_check.md +2 -0
  101. package/builtin/nodes/tool_wecom_send_app_markdown.md +1 -0
  102. package/builtin/nodes/tool_wecom_send_group_markdown.md +1 -0
  103. package/builtin/nodes/workspace_one_click_task.md +44 -0
  104. package/builtin/nodes/workspace_run.md +16 -0
  105. package/builtin/nodes/workspace_scheduled_run.md +16 -0
  106. package/builtin/pipelines/module-migrate/scripts/gate.mjs +37 -0
  107. package/builtin/pipelines/module-migrate/scripts/static-check.mjs +82 -0
  108. package/builtin/pipelines/module-migrate/workspace.flow.js +172 -0
  109. package/builtin/pipelines/module-migrate/workspace.layout.json +134 -0
  110. package/builtin/pipelines/new/scripts/lint-flow.mjs +38 -0
  111. package/builtin/pipelines/new/workspace.flow.js +91 -0
  112. package/builtin/pipelines/new/workspace.layout.json +70 -0
  113. package/builtin/web-ui/dist/assets/{WorkflowAssistantThread-B9xslwI0.js → WorkflowAssistantThread-CKClwj96.js} +1 -1
  114. package/builtin/web-ui/dist/assets/index-BZ5KqLur.js +870 -0
  115. package/builtin/web-ui/dist/assets/index-CEXmmwM2.css +1 -0
  116. package/builtin/web-ui/dist/index.html +2 -2
  117. package/package.json +2 -1
  118. package/reference/flow-control-capabilities.md +77 -158
  119. package/reference/flow-layout.md +1 -1
  120. package/reference/flow-prompt-handler-check.md +2 -2
  121. package/skills/agentflow-author-flow/SKILL.md +1 -1
  122. package/skills/agentflow-cli/SKILL.md +139 -24
  123. package/skills/agentflow-cli/agents/openai.yaml +2 -2
  124. package/skills/agentflow-cli/scripts/agentflow-cli.mjs +654 -20
  125. package/skills/agentflow-cli/scripts/agentflow-runtime.mjs +97 -0
  126. package/skills/agentflow-flow-add-instances/SKILL.md +1 -1
  127. package/skills/agentflow-flow-dsl/SKILL.md +206 -0
  128. package/skills/agentflow-flow-dsl/agents/openai.yaml +4 -0
  129. package/skills/agentflow-flow-dsl/references/node-calls.md +39 -0
  130. package/skills/agentflow-flow-edit-node-fields/SKILL.md +1 -1
  131. package/skills/agentflow-flow-recipes/SKILL.md +7 -4
  132. package/skills/agentflow-flow-recipes/references/recipes.md +97 -43
  133. package/skills/agentflow-flow-sync-ui/SKILL.md +1 -1
  134. package/skills/agentflow-node-dsl/SKILL.md +212 -0
  135. package/skills/agentflow-node-dsl/agents/openai.yaml +4 -0
  136. package/skills/agentflow-node-reference/SKILL.md +2 -2
  137. package/skills/agentflow-node-reference/references/builtin-nodes.md +166 -115
  138. package/skills/agentflow-runtime-reference/references/runtime.md +1 -1
  139. package/skills/agentflow-workspace-ascii/SKILL.md +9 -16
  140. package/skills/agentflow-workspace-graph/SKILL.md +62 -48
  141. package/skills/agentflow-workspace-html/SKILL.md +7 -2
  142. package/skills/agentflow-workspace-image/SKILL.md +6 -2
  143. package/skills/agentflow-workspace-markdown/SKILL.md +14 -21
  144. package/skills/agentflow-workspace-mermaid/SKILL.md +8 -16
  145. package/bin/lib/api-runner.mjs +0 -387
  146. package/bin/lib/apply.mjs +0 -903
  147. package/bin/lib/composer-flow-instances.mjs +0 -68
  148. package/bin/lib/composer-flow-skeleton.mjs +0 -334
  149. package/bin/lib/composer-flow-validate.mjs +0 -47
  150. package/bin/lib/composer-model-router.mjs +0 -185
  151. package/bin/lib/composer-node-schema.mjs +0 -303
  152. package/bin/lib/composer-planner.mjs +0 -751
  153. package/bin/lib/composer-script-ops.mjs +0 -233
  154. package/bin/lib/flow-static-preview.mjs +0 -104
  155. package/bin/lib/hub-login.mjs +0 -54
  156. package/bin/lib/hub-publish.mjs +0 -159
  157. package/bin/lib/hub-remote.mjs +0 -189
  158. package/bin/lib/hub.mjs +0 -299
  159. package/bin/lib/jenkins.mjs +0 -380
  160. package/bin/lib/node-execute.mjs +0 -539
  161. package/bin/lib/normalize-node-tool-command.mjs +0 -97
  162. package/bin/lib/runtime-context.mjs +0 -243
  163. package/bin/lib/scheduler.mjs +0 -601
  164. package/bin/lib/ui-print.mjs +0 -94
  165. package/bin/pipeline/build-node-prompt.mjs +0 -271
  166. package/bin/pipeline/check-cache.mjs +0 -191
  167. package/bin/pipeline/check-flow.mjs +0 -543
  168. package/bin/pipeline/collect-nodes.mjs +0 -212
  169. package/bin/pipeline/compute-cache-md5.mjs +0 -177
  170. package/bin/pipeline/ensure-run-dir.mjs +0 -71
  171. package/bin/pipeline/gc.mjs +0 -129
  172. package/bin/pipeline/get-env.mjs +0 -59
  173. package/bin/pipeline/get-resolved-values.mjs +0 -344
  174. package/bin/pipeline/load-key.mjs +0 -62
  175. package/bin/pipeline/parse-flow.mjs +0 -708
  176. package/bin/pipeline/post-process-control-if.mjs +0 -23
  177. package/bin/pipeline/post-process-node.mjs +0 -490
  178. package/bin/pipeline/pre-process-node.mjs +0 -1430
  179. package/bin/pipeline/resolve-inputs.mjs +0 -201
  180. package/bin/pipeline/run-tool-nodejs.mjs +0 -167
  181. package/bin/pipeline/save-key.mjs +0 -93
  182. package/bin/pipeline/snapshot-prior-round.mjs +0 -70
  183. package/bin/pipeline/validate-for-ui.mjs +0 -234
  184. package/bin/pipeline/validate-script-output.mjs +0 -130
  185. package/bin/pipeline/write-result.mjs +0 -182
  186. package/builtin/pipelines/module-migrate/flow.yaml +0 -819
  187. package/builtin/pipelines/new/flow.yaml +0 -545
  188. package/builtin/pipelines/new/scripts/check-flow.mjs +0 -9
  189. package/builtin/pipelines/new/scripts/collect-nodes.mjs +0 -211
  190. package/builtin/web-ui/dist/assets/index-DZ328oSo.css +0 -1
  191. package/builtin/web-ui/dist/assets/index-DmUV7ZCL.js +0 -888
  192. 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
- }