@fieldwangai/agentflow 0.1.153 → 0.1.156
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 +277 -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 +303 -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 +1092 -18123
- 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-preview.mjs +12 -2
- 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-ClNxY2Wu.js} +1 -1
- package/builtin/web-ui/dist/assets/index-BJzMYRK3.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 +3 -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
package/bin/lib/auth.mjs
CHANGED
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
getAgentflowDataRoot,
|
|
7
7
|
getUserPipelinesRoot,
|
|
8
8
|
sanitizeAgentflowUserId,
|
|
9
|
+
isFlowDir,
|
|
9
10
|
} from "./paths.mjs";
|
|
10
11
|
|
|
11
12
|
const SESSION_COOKIE = "af_session";
|
|
@@ -229,7 +230,7 @@ function listFlowDirs(root) {
|
|
|
229
230
|
return fs.readdirSync(root, { withFileTypes: true })
|
|
230
231
|
.filter((entry) => entry.isDirectory())
|
|
231
232
|
.filter((entry) => entry.name !== ARCHIVED_PIPELINES_DIR_NAME)
|
|
232
|
-
.filter((entry) =>
|
|
233
|
+
.filter((entry) => isFlowDir(path.join(root, entry.name)))
|
|
233
234
|
.map((entry) => entry.name)
|
|
234
235
|
.sort((a, b) => a.localeCompare(b));
|
|
235
236
|
} catch {
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
getUserAgentsDirAbs,
|
|
14
14
|
getUserAgentsJsonAbs,
|
|
15
15
|
getUserPipelinesRoot,
|
|
16
|
+
isFlowDir,
|
|
16
17
|
} from "./paths.mjs";
|
|
17
18
|
import { Table } from "./table.mjs";
|
|
18
19
|
|
|
@@ -283,7 +284,7 @@ export function copyBuiltinJson(workspaceRoot, flowId, targetFlowId, opts = {})
|
|
|
283
284
|
const srcDir = path.join(PACKAGE_BUILTIN_PIPELINES_DIR, flowId);
|
|
284
285
|
const pipelinesRoot = getUserPipelinesRoot(opts.userId);
|
|
285
286
|
const destDir = path.join(pipelinesRoot, destId);
|
|
286
|
-
if (!fs.existsSync(srcDir) || !
|
|
287
|
+
if (!fs.existsSync(srcDir) || !isFlowDir(srcDir)) {
|
|
287
288
|
return { success: false, error: t("catalog.builtin_flow_not_found") };
|
|
288
289
|
}
|
|
289
290
|
const existing = collectPipelineNamesFromDir(pipelinesRoot);
|
|
@@ -14,15 +14,21 @@ import {
|
|
|
14
14
|
PROJECT_NODES_DIR,
|
|
15
15
|
USER_AGENTFLOW_PIPELINES_LABEL,
|
|
16
16
|
getUserPipelinesRoot,
|
|
17
|
+
isFlowDir,
|
|
17
18
|
} from "./paths.mjs";
|
|
18
19
|
import {
|
|
19
20
|
readAdminBuiltinPipelineConfig,
|
|
20
21
|
resolveAdminBuiltinPipelineDir,
|
|
21
22
|
} from "./admin-builtin-pipelines.mjs";
|
|
22
23
|
import { Table } from "./table.mjs";
|
|
23
|
-
import {
|
|
24
|
+
import {
|
|
25
|
+
listMarketplaceNodes,
|
|
26
|
+
listNodePackagesInDir,
|
|
27
|
+
parseMarketplaceDefinitionId,
|
|
28
|
+
resolveMarketplaceNodePackage,
|
|
29
|
+
} from "./marketplace.mjs";
|
|
24
30
|
import { isWorkspacePreviewDir } from "./workspace-preview.mjs";
|
|
25
|
-
import {
|
|
31
|
+
import { RETIRED_NODE_IDS } from "./legacy-flow-execution.mjs";
|
|
26
32
|
|
|
27
33
|
/** 从指定目录收集含 flow.yaml 的子目录名。 */
|
|
28
34
|
export function collectPipelineNamesFromDir(dirPath) {
|
|
@@ -30,28 +36,45 @@ export function collectPipelineNamesFromDir(dirPath) {
|
|
|
30
36
|
const entries = fs.readdirSync(dirPath, { withFileTypes: true });
|
|
31
37
|
return entries
|
|
32
38
|
.filter((e) => e.isDirectory())
|
|
33
|
-
.filter((e) =>
|
|
39
|
+
.filter((e) => isFlowDir(path.join(dirPath, e.name)))
|
|
34
40
|
.filter((e) => !isWorkspacePreviewDir(path.join(dirPath, e.name)))
|
|
35
41
|
.map((e) => e.name);
|
|
36
42
|
}
|
|
37
43
|
|
|
44
|
+
const trimmedOrUndefined = (value) => {
|
|
45
|
+
if (typeof value !== "string") return undefined;
|
|
46
|
+
const t = value.trim();
|
|
47
|
+
return t === "" ? undefined : t;
|
|
48
|
+
};
|
|
49
|
+
|
|
38
50
|
/**
|
|
39
|
-
*
|
|
40
|
-
*
|
|
51
|
+
* 流水线级说明(列表里那一行)。
|
|
52
|
+
*
|
|
53
|
+
* 两个来源,代码化的图优先:`ui.description` 在往返时由 `extractLayout` 透传进
|
|
54
|
+
* `workspace.layout.json` 的顶层,所以代码化的流程说明在 layout.json 里;只有 flow.yaml
|
|
55
|
+
* 的老流程还从 yaml 的 `ui.description` 读。
|
|
56
|
+
*
|
|
57
|
+
* @param {string} flowDir
|
|
41
58
|
* @returns {string | undefined}
|
|
42
59
|
*/
|
|
43
60
|
export function readPipelineListDescription(flowDir) {
|
|
61
|
+
const layoutPath = path.join(flowDir, "workspace.layout.json");
|
|
62
|
+
if (fs.existsSync(layoutPath)) {
|
|
63
|
+
try {
|
|
64
|
+
const layout = JSON.parse(fs.readFileSync(layoutPath, "utf-8"));
|
|
65
|
+
const fromLayout = trimmedOrUndefined(layout?.description);
|
|
66
|
+
if (fromLayout) return fromLayout;
|
|
67
|
+
} catch {
|
|
68
|
+
/* layout 读不动就退回 yaml */
|
|
69
|
+
}
|
|
70
|
+
}
|
|
44
71
|
const yamlPath = path.join(flowDir, "flow.yaml");
|
|
45
72
|
if (!fs.existsSync(yamlPath)) return undefined;
|
|
46
73
|
try {
|
|
47
|
-
const
|
|
48
|
-
const data = yaml.load(raw);
|
|
74
|
+
const data = yaml.load(fs.readFileSync(yamlPath, "utf-8"));
|
|
49
75
|
if (!data || typeof data !== "object") return undefined;
|
|
50
76
|
const ui = data.ui && typeof data.ui === "object" ? data.ui : {};
|
|
51
|
-
|
|
52
|
-
if (typeof d !== "string") return undefined;
|
|
53
|
-
const t = d.trim();
|
|
54
|
-
return t === "" ? undefined : t;
|
|
77
|
+
return trimmedOrUndefined(ui.description);
|
|
55
78
|
} catch {
|
|
56
79
|
return undefined;
|
|
57
80
|
}
|
|
@@ -167,6 +190,22 @@ function normalizeFrontmatterSlots(arr) {
|
|
|
167
190
|
});
|
|
168
191
|
}
|
|
169
192
|
|
|
193
|
+
/** Workspace 运行时对该节点类型的支持程度;未声明按 native 处理(兼容旧的自定义节点 .md)。 */
|
|
194
|
+
const NODE_RUNTIME_TIERS = new Set(["native", "degraded", "none"]);
|
|
195
|
+
|
|
196
|
+
function normalizeNodeRuntimeTier(value) {
|
|
197
|
+
const tier = String(value ?? "").trim().toLowerCase();
|
|
198
|
+
return NODE_RUNTIME_TIERS.has(tier) ? tier : "native";
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/** 节点分类;未声明时由 id 前缀推断。 */
|
|
202
|
+
const NODE_CATEGORY_TYPES = new Set(["control", "provide", "agent"]);
|
|
203
|
+
|
|
204
|
+
function normalizeNodeCategory(value) {
|
|
205
|
+
const type = String(value ?? "").trim().toLowerCase();
|
|
206
|
+
return NODE_CATEGORY_TYPES.has(type) ? type : "";
|
|
207
|
+
}
|
|
208
|
+
|
|
170
209
|
/**
|
|
171
210
|
* 解析 .md 节点文件的 frontmatter。
|
|
172
211
|
* 优先用 js-yaml 解析整块 frontmatter,以支持 description: | / >- 等多行字段;
|
|
@@ -174,7 +213,16 @@ function normalizeFrontmatterSlots(arr) {
|
|
|
174
213
|
*/
|
|
175
214
|
export function parseNodeFrontmatter(raw) {
|
|
176
215
|
const m = raw.match(/^---\s*\r?\n([\s\S]*?)\r?\n---/);
|
|
177
|
-
const data = {
|
|
216
|
+
const data = {
|
|
217
|
+
input: [],
|
|
218
|
+
output: [],
|
|
219
|
+
displayName: undefined,
|
|
220
|
+
description: undefined,
|
|
221
|
+
guide: undefined,
|
|
222
|
+
runtime: "native",
|
|
223
|
+
type: "",
|
|
224
|
+
paletteHidden: false,
|
|
225
|
+
};
|
|
178
226
|
if (!m) return data;
|
|
179
227
|
const fm = m[1];
|
|
180
228
|
try {
|
|
@@ -189,6 +237,9 @@ export function parseNodeFrontmatter(raw) {
|
|
|
189
237
|
if (parsed.guide && typeof parsed.guide === "object" && !Array.isArray(parsed.guide)) {
|
|
190
238
|
data.guide = parsed.guide;
|
|
191
239
|
}
|
|
240
|
+
data.runtime = normalizeNodeRuntimeTier(parsed.runtime);
|
|
241
|
+
data.type = normalizeNodeCategory(parsed.type);
|
|
242
|
+
data.paletteHidden = String(parsed.palette ?? "").trim().toLowerCase() === "hidden";
|
|
192
243
|
data.input = normalizeFrontmatterSlots(parsed.input);
|
|
193
244
|
data.output = normalizeFrontmatterSlots(parsed.output);
|
|
194
245
|
return data;
|
|
@@ -225,6 +276,9 @@ export function parseNodeFrontmatter(raw) {
|
|
|
225
276
|
const displayM = fm.match(/\bdisplayName:\s*["']?([^"'\n#][^\n]*)["']?/);
|
|
226
277
|
if (descM) data.description = descM[1].trim().replace(/^["']|["']$/g, "");
|
|
227
278
|
if (displayM) data.displayName = displayM[1].trim().replace(/^["']|["']$/g, "");
|
|
279
|
+
data.runtime = normalizeNodeRuntimeTier((fm.match(/^\s*runtime:\s*(\S+)/m) || [])[1]);
|
|
280
|
+
data.type = normalizeNodeCategory((fm.match(/^\s*type:\s*(\S+)/m) || [])[1]);
|
|
281
|
+
data.paletteHidden = /^\s*palette:\s*hidden\s*$/m.test(fm);
|
|
228
282
|
return data;
|
|
229
283
|
}
|
|
230
284
|
|
|
@@ -232,7 +286,7 @@ export function parseNodeFrontmatter(raw) {
|
|
|
232
286
|
* @param {string} workspaceRoot
|
|
233
287
|
* @param {string} flowId
|
|
234
288
|
* @param {string} flowSource
|
|
235
|
-
* @param {{ archived?: boolean
|
|
289
|
+
* @param {{ archived?: boolean }} [opts]
|
|
236
290
|
*/
|
|
237
291
|
export function listNodesJson(workspaceRoot, flowId, flowSource, opts = {}) {
|
|
238
292
|
const root = path.resolve(workspaceRoot);
|
|
@@ -241,14 +295,7 @@ export function listNodesJson(workspaceRoot, flowId, flowSource, opts = {}) {
|
|
|
241
295
|
const byId = new Map();
|
|
242
296
|
const pipelineTranslations = {};
|
|
243
297
|
let marketplaceFlowData = null;
|
|
244
|
-
|
|
245
|
-
const staticFlowDir = staticFlowPath ? path.dirname(staticFlowPath) : "";
|
|
246
|
-
if (staticFlowPath && fs.existsSync(staticFlowPath) && fs.statSync(staticFlowPath).isFile()) {
|
|
247
|
-
try {
|
|
248
|
-
const parsed = yaml.load(fs.readFileSync(staticFlowPath, "utf-8"));
|
|
249
|
-
if (parsed && typeof parsed === "object") marketplaceFlowData = parsed;
|
|
250
|
-
} catch (_) {}
|
|
251
|
-
} else if (flowId && flowSource) {
|
|
298
|
+
if (flowId && flowSource) {
|
|
252
299
|
const flowPath = getFlowYamlAbs(workspaceRoot, flowId, flowSource, opts);
|
|
253
300
|
if (flowPath.path && fs.existsSync(flowPath.path)) {
|
|
254
301
|
try {
|
|
@@ -262,7 +309,7 @@ export function listNodesJson(workspaceRoot, flowId, flowSource, opts = {}) {
|
|
|
262
309
|
const files = fs.readdirSync(dir, { withFileTypes: true }).filter((e) => e.isFile() && e.name.endsWith(".md"));
|
|
263
310
|
for (const e of files) {
|
|
264
311
|
const id = e.name.replace(/\.mdx?$/i, "").replace(/\.markdown$/i, "");
|
|
265
|
-
if (
|
|
312
|
+
if (RETIRED_NODE_IDS.has(id)) continue;
|
|
266
313
|
let type = "agent";
|
|
267
314
|
if (/^control/i.test(id)) type = "control";
|
|
268
315
|
else if (/^provide/i.test(id)) type = "provide";
|
|
@@ -270,6 +317,10 @@ export function listNodesJson(workspaceRoot, flowId, flowSource, opts = {}) {
|
|
|
270
317
|
try {
|
|
271
318
|
const raw = fs.readFileSync(path.join(dir, e.name), "utf-8");
|
|
272
319
|
const data = parseNodeFrontmatter(raw);
|
|
320
|
+
// frontmatter 的 type: 优先于按 id 前缀的推断(workspace_run 等不带前缀的节点靠它归类)
|
|
321
|
+
if (data.type) type = data.type;
|
|
322
|
+
// frontmatter 的 palette: hidden 与 RETIRED_NODE_IDS 等价,让节点自带可见性
|
|
323
|
+
if (data.paletteHidden) continue;
|
|
273
324
|
const strippedId =
|
|
274
325
|
id.replace(/^agent_?/i, "").replace(/^control_?/i, "").replace(/^provide_?/i, "").replace(/^tool_?/i, "") || id;
|
|
275
326
|
const label = data.displayName ?? strippedId;
|
|
@@ -287,6 +338,7 @@ export function listNodesJson(workspaceRoot, flowId, flowSource, opts = {}) {
|
|
|
287
338
|
byId.set(id, {
|
|
288
339
|
id,
|
|
289
340
|
type,
|
|
341
|
+
runtimeTier: data.runtime,
|
|
290
342
|
label: translatedDisplayName || label,
|
|
291
343
|
displayName: translatedDisplayName || data.displayName,
|
|
292
344
|
description: translatedDescription || data.description,
|
|
@@ -298,6 +350,29 @@ export function listNodesJson(workspaceRoot, flowId, flowSource, opts = {}) {
|
|
|
298
350
|
});
|
|
299
351
|
} catch (_) {}
|
|
300
352
|
}
|
|
353
|
+
// 同一个 nodes/ 目录里,子目录形式的代码节点包(index.mjs 声明 + 实现)也进目录。
|
|
354
|
+
// 这样 AI 可以把节点实现直接写在流程目录里,不必先发布到 marketplace。
|
|
355
|
+
for (const manifest of listNodePackagesInDir(dir)) {
|
|
356
|
+
byId.set(manifest.definitionId, {
|
|
357
|
+
id: manifest.definitionId,
|
|
358
|
+
baseDefinitionId: manifest.baseDefinitionId || manifest.runtime?.type || "",
|
|
359
|
+
marketplaceDefinitionId: manifest.definitionId,
|
|
360
|
+
packageId: manifest.id,
|
|
361
|
+
version: manifest.version,
|
|
362
|
+
type: "agent",
|
|
363
|
+
runtimeTier: "native",
|
|
364
|
+
label: manifest.displayName,
|
|
365
|
+
displayName: manifest.displayName,
|
|
366
|
+
description: manifest.description,
|
|
367
|
+
inputs: manifest.input,
|
|
368
|
+
outputs: manifest.output,
|
|
369
|
+
source: flowIdOpt ? "flow" : "project",
|
|
370
|
+
flowId: flowIdOpt,
|
|
371
|
+
packageDir: manifest.packageDir,
|
|
372
|
+
// 与 marketplace 分支同名:前端 scriptFromMarketplaceRuntime 读的就是这个对象
|
|
373
|
+
runtime: manifest.runtime,
|
|
374
|
+
});
|
|
375
|
+
}
|
|
301
376
|
};
|
|
302
377
|
addFromDir(PACKAGE_BUILTIN_NODES_DIR, "project");
|
|
303
378
|
addFromDir(path.join(root, LEGACY_NODES_DIR), "project");
|
|
@@ -326,24 +401,6 @@ export function listNodesJson(workspaceRoot, flowId, flowSource, opts = {}) {
|
|
|
326
401
|
runtime: manifest.runtime,
|
|
327
402
|
});
|
|
328
403
|
}
|
|
329
|
-
if (staticFlowDir) {
|
|
330
|
-
addFromDir(path.join(staticFlowDir, "nodes"), "flow", flowId);
|
|
331
|
-
const flowData = marketplaceFlowData;
|
|
332
|
-
if (flowData?.instances) {
|
|
333
|
-
pipelineTranslations[flowId] = pipelineTranslations[flowId] || {};
|
|
334
|
-
for (const [nodeId, inst] of Object.entries(flowData.instances)) {
|
|
335
|
-
pipelineTranslations[flowId][nodeId] = {
|
|
336
|
-
label: inst?.label,
|
|
337
|
-
body: inst?.body,
|
|
338
|
-
description: inst?.description || inst?.userDescription,
|
|
339
|
-
};
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
if (flowData?.ui?.description) {
|
|
343
|
-
pipelineTranslations[flowId] = pipelineTranslations[flowId] || {};
|
|
344
|
-
pipelineTranslations[flowId].__flowDescription = flowData.ui.description;
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
404
|
if (flowId && flowSource) {
|
|
348
405
|
if (flowSource === "builtin") {
|
|
349
406
|
addFromDir(path.join(PACKAGE_BUILTIN_PIPELINES_DIR, flowId, "nodes"), "flow", flowId);
|
|
@@ -495,63 +552,79 @@ export function readFlowJson(workspaceRoot, flowId, flowSource, options = {}) {
|
|
|
495
552
|
}
|
|
496
553
|
|
|
497
554
|
/**
|
|
498
|
-
*
|
|
499
|
-
* @
|
|
500
|
-
* @returns {{ path: string } | { error: string }}
|
|
555
|
+
* 按 flowSource 列出候选目录(含 user→workspace→legacy 的历史回退顺序)。
|
|
556
|
+
* @returns {{ dirs: string[] } | { error: string }}
|
|
501
557
|
*/
|
|
502
|
-
|
|
558
|
+
function flowDirCandidates(workspaceRoot, flowId, flowSource, options = {}) {
|
|
503
559
|
const root = path.resolve(workspaceRoot);
|
|
504
|
-
const archived = Boolean(options.archived);
|
|
505
560
|
const userPipelinesRoot = getUserPipelinesRoot(options.userId);
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
if (flowSource === "builtin") {
|
|
509
|
-
return { error: t("catalog.builtin_flow_archive_path_not_supported") };
|
|
510
|
-
}
|
|
561
|
+
if (Boolean(options.archived)) {
|
|
562
|
+
if (flowSource === "builtin") return { error: t("catalog.builtin_flow_archive_path_not_supported") };
|
|
511
563
|
if (flowSource === "user") {
|
|
512
|
-
|
|
513
|
-
} else if (flowSource === "workspace") {
|
|
514
|
-
yamlPath = path.join(root, PIPELINES_DIR, ARCHIVED_PIPELINES_DIR_NAME, flowId, "flow.yaml");
|
|
515
|
-
if (!fs.existsSync(yamlPath)) {
|
|
516
|
-
const altLeg = path.join(root, LEGACY_PIPELINES_DIR, ARCHIVED_PIPELINES_DIR_NAME, flowId, "flow.yaml");
|
|
517
|
-
if (fs.existsSync(altLeg)) yamlPath = altLeg;
|
|
518
|
-
}
|
|
519
|
-
} else {
|
|
520
|
-
return { error: "Invalid flowSource" };
|
|
521
|
-
}
|
|
522
|
-
if (!fs.existsSync(yamlPath)) {
|
|
523
|
-
return { error: "Flow not found: " + flowId };
|
|
524
|
-
}
|
|
525
|
-
return { path: yamlPath };
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
if (flowSource === "builtin") {
|
|
529
|
-
yamlPath = path.join(PACKAGE_BUILTIN_PIPELINES_DIR, flowId, "flow.yaml");
|
|
530
|
-
} else if (flowSource === "admin") {
|
|
531
|
-
const flowDir = resolveAdminBuiltinPipelineDir(flowId);
|
|
532
|
-
yamlPath = flowDir ? path.join(flowDir, "flow.yaml") : "";
|
|
533
|
-
} else if (flowSource === "user") {
|
|
534
|
-
yamlPath = path.join(userPipelinesRoot, flowId, "flow.yaml");
|
|
535
|
-
if (!fs.existsSync(yamlPath)) {
|
|
536
|
-
const alt = path.join(root, PIPELINES_DIR, flowId, "flow.yaml");
|
|
537
|
-
if (fs.existsSync(alt)) yamlPath = alt;
|
|
538
|
-
}
|
|
539
|
-
if (!fs.existsSync(yamlPath)) {
|
|
540
|
-
const altLeg = path.join(root, LEGACY_PIPELINES_DIR, flowId, "flow.yaml");
|
|
541
|
-
if (fs.existsSync(altLeg)) yamlPath = altLeg;
|
|
564
|
+
return { dirs: [path.join(userPipelinesRoot, ARCHIVED_PIPELINES_DIR_NAME, flowId)] };
|
|
542
565
|
}
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
566
|
+
if (flowSource === "workspace") {
|
|
567
|
+
return {
|
|
568
|
+
dirs: [
|
|
569
|
+
path.join(root, PIPELINES_DIR, ARCHIVED_PIPELINES_DIR_NAME, flowId),
|
|
570
|
+
path.join(root, LEGACY_PIPELINES_DIR, ARCHIVED_PIPELINES_DIR_NAME, flowId),
|
|
571
|
+
],
|
|
572
|
+
};
|
|
548
573
|
}
|
|
549
|
-
} else {
|
|
550
574
|
return { error: "Invalid flowSource" };
|
|
551
575
|
}
|
|
552
|
-
if (
|
|
553
|
-
|
|
576
|
+
if (flowSource === "builtin") return { dirs: [path.join(PACKAGE_BUILTIN_PIPELINES_DIR, flowId)] };
|
|
577
|
+
if (flowSource === "admin") {
|
|
578
|
+
const dir = resolveAdminBuiltinPipelineDir(flowId);
|
|
579
|
+
return { dirs: dir ? [dir] : [] };
|
|
554
580
|
}
|
|
581
|
+
if (flowSource === "user") {
|
|
582
|
+
return {
|
|
583
|
+
dirs: [
|
|
584
|
+
path.join(userPipelinesRoot, flowId),
|
|
585
|
+
path.join(root, PIPELINES_DIR, flowId),
|
|
586
|
+
path.join(root, LEGACY_PIPELINES_DIR, flowId),
|
|
587
|
+
],
|
|
588
|
+
};
|
|
589
|
+
}
|
|
590
|
+
if (flowSource === "workspace") {
|
|
591
|
+
return {
|
|
592
|
+
dirs: [path.join(root, PIPELINES_DIR, flowId), path.join(root, LEGACY_PIPELINES_DIR, flowId)],
|
|
593
|
+
};
|
|
594
|
+
}
|
|
595
|
+
return { error: "Invalid flowSource" };
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
/**
|
|
599
|
+
* 解析流程目录绝对路径。
|
|
600
|
+
*
|
|
601
|
+
* 大多数调用方要的其实是**目录**,以前却只能 `path.dirname(getFlowYamlAbs().path)` —— 于是
|
|
602
|
+
* 「改个名」这种和 yaml 毫无关系的操作,也被 `flow.yaml` 是否存在卡住。目录存不存在现在由
|
|
603
|
+
* `isFlowDir` 说了算(见 paths.mjs),三种标记文件任一即可。
|
|
604
|
+
*
|
|
605
|
+
* @param {{ archived?: boolean, userId?: string }} [options]
|
|
606
|
+
* @returns {{ dir: string } | { error: string }}
|
|
607
|
+
*/
|
|
608
|
+
export function resolveFlowDirAbs(workspaceRoot, flowId, flowSource, options = {}) {
|
|
609
|
+
const candidates = flowDirCandidates(workspaceRoot, flowId, flowSource, options);
|
|
610
|
+
if (candidates.error) return { error: candidates.error };
|
|
611
|
+
for (const dir of candidates.dirs) {
|
|
612
|
+
if (isFlowDir(dir)) return { dir };
|
|
613
|
+
}
|
|
614
|
+
return { error: "Flow not found: " + flowId };
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
/**
|
|
618
|
+
* 解析 flow.yaml 绝对路径。**只给真的要读 yaml 内容的调用方用**;要目录请用
|
|
619
|
+
* `resolveFlowDirAbs`。
|
|
620
|
+
* @param {{ archived?: boolean }} [options]
|
|
621
|
+
* @returns {{ path: string } | { error: string }}
|
|
622
|
+
*/
|
|
623
|
+
export function getFlowYamlAbs(workspaceRoot, flowId, flowSource, options = {}) {
|
|
624
|
+
const resolved = resolveFlowDirAbs(workspaceRoot, flowId, flowSource, options);
|
|
625
|
+
if (resolved.error) return { error: resolved.error };
|
|
626
|
+
const yamlPath = path.join(resolved.dir, "flow.yaml");
|
|
627
|
+
if (!fs.existsSync(yamlPath)) return { error: "Flow not found: " + flowId };
|
|
555
628
|
return { path: yamlPath };
|
|
556
629
|
}
|
|
557
630
|
|
|
@@ -632,6 +705,7 @@ export function readNodeJson(workspaceRoot, nodeId, flowId, flowSource, opts = {
|
|
|
632
705
|
if (/^control/i.test(nodeId)) type = "control";
|
|
633
706
|
else if (/^provide/i.test(nodeId)) type = "provide";
|
|
634
707
|
else if (/^tool/i.test(nodeId)) type = "agent";
|
|
708
|
+
if (data.type) type = data.type;
|
|
635
709
|
const strippedId =
|
|
636
710
|
nodeId
|
|
637
711
|
.replace(/\.md$/, "")
|
|
@@ -642,6 +716,7 @@ export function readNodeJson(workspaceRoot, nodeId, flowId, flowSource, opts = {
|
|
|
642
716
|
const label = data.displayName ?? strippedId;
|
|
643
717
|
return {
|
|
644
718
|
type,
|
|
719
|
+
runtimeTier: data.runtime,
|
|
645
720
|
label,
|
|
646
721
|
displayName: data.displayName,
|
|
647
722
|
inputs: data.input,
|
|
@@ -874,13 +949,13 @@ export function listPipelines(workspaceRoot) {
|
|
|
874
949
|
return;
|
|
875
950
|
}
|
|
876
951
|
const table = new Table({
|
|
877
|
-
head: [chalk.cyan(t("catalog.pipeline_header")), chalk.cyan(t("catalog.source_header")), chalk.cyan(t("catalog.
|
|
952
|
+
head: [chalk.cyan(t("catalog.pipeline_header")), chalk.cyan(t("catalog.source_header")), chalk.cyan(t("catalog.open_example_header"))],
|
|
878
953
|
colWidths: [24, 10, 48],
|
|
879
954
|
style: { head: [], border: ["grey"] },
|
|
880
955
|
});
|
|
881
956
|
for (const row of rows) {
|
|
882
957
|
const sourceLabel = row.source === "builtin" || row.source === "admin" ? "builtin" : row.source === "workspace" ? "workspace" : "user";
|
|
883
|
-
table.push([row.id, sourceLabel, `agentflow
|
|
958
|
+
table.push([row.id, sourceLabel, `agentflow validate ${row.id}`]);
|
|
884
959
|
}
|
|
885
960
|
log.info("\n" + chalk.bold("Pipelines"));
|
|
886
961
|
log.info(table.toString());
|