@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.
- package/README.md +16 -34
- package/README.zh-CN.md +14 -34
- package/agents/agentflow-node-executor-code.md +13 -3
- package/agents/agentflow-node-executor-planning.md +13 -3
- package/agents/agentflow-node-executor-requirement.md +13 -3
- package/agents/agentflow-node-executor-test.md +13 -3
- package/agents/agentflow-node-executor-ui.md +13 -3
- package/agents/agentflow-node-executor.md +13 -3
- package/agents/en/agentflow-node-executor.md +13 -3
- package/agents/zh/agentflow-node-executor.md +13 -3
- package/bin/agentflow.mjs +3 -3
- package/bin/lib/admin-builtin-pipelines.mjs +3 -2
- package/bin/lib/agent-runners.mjs +0 -929
- package/bin/lib/auth.mjs +2 -1
- package/bin/lib/catalog-agents.mjs +2 -1
- package/bin/lib/catalog-flows.mjs +165 -90
- package/bin/lib/composer-agent.mjs +3 -793
- package/bin/lib/composer-skill-router.mjs +3 -323
- package/bin/lib/exec-buffered.mjs +32 -0
- package/bin/lib/flow-dsl/cli.mjs +276 -0
- package/bin/lib/flow-dsl/codegen.mjs +358 -0
- package/bin/lib/flow-dsl/defs.mjs +103 -0
- package/bin/lib/flow-dsl/index.mjs +77 -0
- package/bin/lib/flow-dsl/ir.mjs +305 -0
- package/bin/lib/flow-dsl/layout.mjs +115 -0
- package/bin/lib/flow-dsl/legacy-yaml.mjs +268 -0
- package/bin/lib/flow-dsl/lint.mjs +241 -0
- package/bin/lib/flow-dsl/packages.mjs +251 -0
- package/bin/lib/flow-dsl/parser.mjs +444 -0
- package/bin/lib/flow-import.mjs +96 -26
- package/bin/lib/flow-write.mjs +48 -12
- package/bin/lib/help.mjs +8 -28
- package/bin/lib/html-escape.mjs +19 -0
- package/bin/lib/http-util.mjs +49 -0
- package/bin/lib/legacy-flow-execution.mjs +37 -0
- package/bin/lib/locales/en.json +1 -1
- package/bin/lib/locales/zh.json +1 -1
- package/bin/lib/main.mjs +152 -286
- package/bin/lib/marketplace.mjs +206 -61
- package/bin/lib/model-config.mjs +10 -0
- package/bin/lib/node-package-archive.mjs +240 -0
- package/bin/lib/node-package-bootstrap.mjs +72 -0
- package/bin/lib/node-package-manifest.mjs +174 -0
- package/bin/lib/paths.mjs +42 -19
- package/bin/lib/prd-workflow-routes.mjs +2949 -0
- package/bin/lib/prd-workflow-server.mjs +4884 -0
- package/bin/lib/run-events.mjs +0 -66
- package/bin/lib/schedule-config.mjs +4 -4
- package/bin/lib/skill-runtime.mjs +35 -0
- package/bin/lib/startup-storage-migrations.mjs +274 -0
- package/bin/lib/ui-server.mjs +1087 -18122
- package/bin/lib/user-env.mjs +16 -0
- package/bin/lib/workspace-auto-layout.mjs +234 -0
- package/bin/lib/workspace-flow-store.mjs +478 -0
- package/bin/lib/workspace-graph-merge.mjs +41 -34
- package/bin/lib/workspace-routes.mjs +3088 -0
- package/bin/lib/workspace-server.mjs +6199 -0
- package/bin/lib/workspace-state.mjs +331 -0
- package/bin/lib/workspace.mjs +2 -1
- package/builtin/nodes/agent_subAgent.md +1 -0
- package/builtin/nodes/control_agent_toBool.md +2 -0
- package/builtin/nodes/control_anyOne.md +2 -0
- package/builtin/nodes/control_cancelled.md +2 -0
- package/builtin/nodes/control_cd_workspace.md +5 -11
- package/builtin/nodes/control_delay.md +2 -0
- package/builtin/nodes/control_end.md +2 -0
- package/builtin/nodes/control_if.md +1 -0
- package/builtin/nodes/control_interval_loop.md +2 -0
- package/builtin/nodes/control_load_mcp.md +24 -0
- package/builtin/nodes/control_load_skills.md +8 -23
- package/builtin/nodes/control_start.md +2 -0
- package/builtin/nodes/control_toBool.md +2 -0
- package/builtin/nodes/control_user_workspace.md +2 -0
- package/builtin/nodes/control_wait_until.md +2 -0
- package/builtin/nodes/display_ascii.md +1 -0
- package/builtin/nodes/display_chart.md +1 -0
- package/builtin/nodes/display_html.md +1 -0
- package/builtin/nodes/display_image.md +1 -0
- package/builtin/nodes/display_markdown.md +1 -0
- package/builtin/nodes/display_mermaid.md +1 -0
- package/builtin/nodes/display_react_app.md +1 -0
- package/builtin/nodes/display_table.md +1 -0
- package/builtin/nodes/provide_bool.md +1 -0
- package/builtin/nodes/provide_file.md +1 -0
- package/builtin/nodes/provide_password.md +1 -0
- package/builtin/nodes/provide_str.md +1 -0
- package/builtin/nodes/tool_display_share_link.md +1 -0
- package/builtin/nodes/tool_get_env.md +2 -0
- package/builtin/nodes/tool_git_checkout.md +1 -0
- package/builtin/nodes/tool_git_worktree_load.md +1 -0
- package/builtin/nodes/tool_git_worktree_unload.md +1 -0
- package/builtin/nodes/tool_gitlab_create_mr.md +1 -0
- package/builtin/nodes/tool_jenkins_build.md +2 -0
- package/builtin/nodes/tool_load_key.md +2 -0
- package/builtin/nodes/tool_nodejs.md +17 -19
- package/builtin/nodes/tool_print.md +2 -0
- package/builtin/nodes/tool_save_key.md +2 -0
- package/builtin/nodes/tool_set_run_env.md +1 -0
- package/builtin/nodes/tool_user_ask.md +2 -0
- package/builtin/nodes/tool_user_check.md +2 -0
- package/builtin/nodes/tool_wecom_send_app_markdown.md +1 -0
- package/builtin/nodes/tool_wecom_send_group_markdown.md +1 -0
- package/builtin/nodes/workspace_one_click_task.md +44 -0
- package/builtin/nodes/workspace_run.md +16 -0
- package/builtin/nodes/workspace_scheduled_run.md +16 -0
- package/builtin/pipelines/module-migrate/scripts/gate.mjs +37 -0
- package/builtin/pipelines/module-migrate/scripts/static-check.mjs +82 -0
- package/builtin/pipelines/module-migrate/workspace.flow.js +172 -0
- package/builtin/pipelines/module-migrate/workspace.layout.json +134 -0
- package/builtin/pipelines/new/scripts/lint-flow.mjs +38 -0
- package/builtin/pipelines/new/workspace.flow.js +91 -0
- package/builtin/pipelines/new/workspace.layout.json +70 -0
- package/builtin/web-ui/dist/assets/{WorkflowAssistantThread-B9xslwI0.js → WorkflowAssistantThread-CKClwj96.js} +1 -1
- package/builtin/web-ui/dist/assets/index-BZ5KqLur.js +870 -0
- package/builtin/web-ui/dist/assets/index-CEXmmwM2.css +1 -0
- package/builtin/web-ui/dist/index.html +2 -2
- package/package.json +2 -1
- package/reference/flow-control-capabilities.md +77 -158
- package/reference/flow-layout.md +1 -1
- package/reference/flow-prompt-handler-check.md +2 -2
- package/skills/agentflow-author-flow/SKILL.md +1 -1
- package/skills/agentflow-cli/SKILL.md +139 -24
- package/skills/agentflow-cli/agents/openai.yaml +2 -2
- package/skills/agentflow-cli/scripts/agentflow-cli.mjs +654 -20
- package/skills/agentflow-cli/scripts/agentflow-runtime.mjs +97 -0
- package/skills/agentflow-flow-add-instances/SKILL.md +1 -1
- package/skills/agentflow-flow-dsl/SKILL.md +206 -0
- package/skills/agentflow-flow-dsl/agents/openai.yaml +4 -0
- package/skills/agentflow-flow-dsl/references/node-calls.md +39 -0
- package/skills/agentflow-flow-edit-node-fields/SKILL.md +1 -1
- package/skills/agentflow-flow-recipes/SKILL.md +7 -4
- package/skills/agentflow-flow-recipes/references/recipes.md +97 -43
- package/skills/agentflow-flow-sync-ui/SKILL.md +1 -1
- package/skills/agentflow-node-dsl/SKILL.md +212 -0
- package/skills/agentflow-node-dsl/agents/openai.yaml +4 -0
- package/skills/agentflow-node-reference/SKILL.md +2 -2
- package/skills/agentflow-node-reference/references/builtin-nodes.md +166 -115
- package/skills/agentflow-runtime-reference/references/runtime.md +1 -1
- package/skills/agentflow-workspace-ascii/SKILL.md +9 -16
- package/skills/agentflow-workspace-graph/SKILL.md +62 -48
- package/skills/agentflow-workspace-html/SKILL.md +7 -2
- package/skills/agentflow-workspace-image/SKILL.md +6 -2
- package/skills/agentflow-workspace-markdown/SKILL.md +14 -21
- package/skills/agentflow-workspace-mermaid/SKILL.md +8 -16
- package/bin/lib/api-runner.mjs +0 -387
- package/bin/lib/apply.mjs +0 -903
- package/bin/lib/composer-flow-instances.mjs +0 -68
- package/bin/lib/composer-flow-skeleton.mjs +0 -334
- package/bin/lib/composer-flow-validate.mjs +0 -47
- package/bin/lib/composer-model-router.mjs +0 -185
- package/bin/lib/composer-node-schema.mjs +0 -303
- package/bin/lib/composer-planner.mjs +0 -751
- package/bin/lib/composer-script-ops.mjs +0 -233
- package/bin/lib/flow-static-preview.mjs +0 -104
- package/bin/lib/hub-login.mjs +0 -54
- package/bin/lib/hub-publish.mjs +0 -159
- package/bin/lib/hub-remote.mjs +0 -189
- package/bin/lib/hub.mjs +0 -299
- package/bin/lib/jenkins.mjs +0 -380
- package/bin/lib/node-execute.mjs +0 -539
- package/bin/lib/normalize-node-tool-command.mjs +0 -97
- package/bin/lib/runtime-context.mjs +0 -243
- package/bin/lib/scheduler.mjs +0 -601
- package/bin/lib/ui-print.mjs +0 -94
- package/bin/pipeline/build-node-prompt.mjs +0 -271
- package/bin/pipeline/check-cache.mjs +0 -191
- package/bin/pipeline/check-flow.mjs +0 -543
- package/bin/pipeline/collect-nodes.mjs +0 -212
- package/bin/pipeline/compute-cache-md5.mjs +0 -177
- package/bin/pipeline/ensure-run-dir.mjs +0 -71
- package/bin/pipeline/gc.mjs +0 -129
- package/bin/pipeline/get-env.mjs +0 -59
- package/bin/pipeline/get-resolved-values.mjs +0 -344
- package/bin/pipeline/load-key.mjs +0 -62
- package/bin/pipeline/parse-flow.mjs +0 -708
- package/bin/pipeline/post-process-control-if.mjs +0 -23
- package/bin/pipeline/post-process-node.mjs +0 -490
- package/bin/pipeline/pre-process-node.mjs +0 -1430
- package/bin/pipeline/resolve-inputs.mjs +0 -201
- package/bin/pipeline/run-tool-nodejs.mjs +0 -167
- package/bin/pipeline/save-key.mjs +0 -93
- package/bin/pipeline/snapshot-prior-round.mjs +0 -70
- package/bin/pipeline/validate-for-ui.mjs +0 -234
- package/bin/pipeline/validate-script-output.mjs +0 -130
- package/bin/pipeline/write-result.mjs +0 -182
- package/builtin/pipelines/module-migrate/flow.yaml +0 -819
- package/builtin/pipelines/new/flow.yaml +0 -545
- package/builtin/pipelines/new/scripts/check-flow.mjs +0 -9
- package/builtin/pipelines/new/scripts/collect-nodes.mjs +0 -211
- package/builtin/web-ui/dist/assets/index-DZ328oSo.css +0 -1
- package/builtin/web-ui/dist/assets/index-DmUV7ZCL.js +0 -888
- 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, "&")
|
|
33
|
-
.replace(/</g, "<")
|
|
34
|
-
.replace(/>/g, ">")
|
|
35
|
-
.replace(/"/g, """);
|
|
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
|
-
}
|
package/bin/lib/hub-login.mjs
DELETED
|
@@ -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
|
-
}
|
package/bin/lib/hub-publish.mjs
DELETED
|
@@ -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
|
-
}
|