@fieldwangai/agentflow 0.1.149 → 0.1.154

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (195) hide show
  1. package/README.md +20 -33
  2. package/README.zh-CN.md +14 -34
  3. package/agents/agentflow-node-executor-code.md +13 -3
  4. package/agents/agentflow-node-executor-planning.md +13 -3
  5. package/agents/agentflow-node-executor-requirement.md +13 -3
  6. package/agents/agentflow-node-executor-test.md +13 -3
  7. package/agents/agentflow-node-executor-ui.md +13 -3
  8. package/agents/agentflow-node-executor.md +13 -3
  9. package/agents/en/agentflow-node-executor.md +13 -3
  10. package/agents/zh/agentflow-node-executor.md +13 -3
  11. package/bin/agentflow.mjs +3 -3
  12. package/bin/lib/admin-builtin-pipelines.mjs +3 -2
  13. package/bin/lib/agent-runners.mjs +0 -929
  14. package/bin/lib/auth.mjs +2 -1
  15. package/bin/lib/catalog-agents.mjs +2 -1
  16. package/bin/lib/catalog-flows.mjs +167 -88
  17. package/bin/lib/composer-agent.mjs +3 -793
  18. package/bin/lib/composer-skill-router.mjs +3 -323
  19. package/bin/lib/exec-buffered.mjs +32 -0
  20. package/bin/lib/flow-dsl/cli.mjs +276 -0
  21. package/bin/lib/flow-dsl/codegen.mjs +358 -0
  22. package/bin/lib/flow-dsl/defs.mjs +103 -0
  23. package/bin/lib/flow-dsl/index.mjs +77 -0
  24. package/bin/lib/flow-dsl/ir.mjs +305 -0
  25. package/bin/lib/flow-dsl/layout.mjs +115 -0
  26. package/bin/lib/flow-dsl/legacy-yaml.mjs +268 -0
  27. package/bin/lib/flow-dsl/lint.mjs +241 -0
  28. package/bin/lib/flow-dsl/packages.mjs +251 -0
  29. package/bin/lib/flow-dsl/parser.mjs +444 -0
  30. package/bin/lib/flow-import.mjs +96 -26
  31. package/bin/lib/flow-write.mjs +48 -12
  32. package/bin/lib/help.mjs +10 -30
  33. package/bin/lib/html-escape.mjs +19 -0
  34. package/bin/lib/http-util.mjs +49 -0
  35. package/bin/lib/legacy-flow-execution.mjs +43 -0
  36. package/bin/lib/locales/en.json +1 -1
  37. package/bin/lib/locales/zh.json +1 -1
  38. package/bin/lib/main.mjs +153 -283
  39. package/bin/lib/marketplace.mjs +206 -61
  40. package/bin/lib/model-config.mjs +10 -0
  41. package/bin/lib/node-package-archive.mjs +240 -0
  42. package/bin/lib/node-package-bootstrap.mjs +72 -0
  43. package/bin/lib/node-package-manifest.mjs +174 -0
  44. package/bin/lib/paths.mjs +42 -19
  45. package/bin/lib/prd-workflow-routes.mjs +2949 -0
  46. package/bin/lib/prd-workflow-server.mjs +4884 -0
  47. package/bin/lib/run-events.mjs +0 -66
  48. package/bin/lib/schedule-config.mjs +4 -4
  49. package/bin/lib/skill-runtime.mjs +35 -0
  50. package/bin/lib/startup-storage-migrations.mjs +274 -0
  51. package/bin/lib/ui-server.mjs +1106 -18013
  52. package/bin/lib/user-env.mjs +16 -0
  53. package/bin/lib/workspace-auto-layout.mjs +234 -0
  54. package/bin/lib/workspace-flow-store.mjs +478 -0
  55. package/bin/lib/workspace-graph-merge.mjs +41 -34
  56. package/bin/lib/workspace-preview.mjs +74 -0
  57. package/bin/lib/workspace-routes.mjs +3088 -0
  58. package/bin/lib/workspace-server.mjs +6199 -0
  59. package/bin/lib/workspace-state.mjs +331 -0
  60. package/bin/lib/workspace.mjs +2 -1
  61. package/builtin/nodes/agent_subAgent.md +1 -0
  62. package/builtin/nodes/control_agent_toBool.md +2 -0
  63. package/builtin/nodes/control_anyOne.md +2 -0
  64. package/builtin/nodes/control_cancelled.md +2 -0
  65. package/builtin/nodes/control_cd_workspace.md +5 -11
  66. package/builtin/nodes/control_delay.md +2 -0
  67. package/builtin/nodes/control_end.md +2 -0
  68. package/builtin/nodes/control_if.md +1 -0
  69. package/builtin/nodes/control_interval_loop.md +2 -0
  70. package/builtin/nodes/control_load_mcp.md +24 -0
  71. package/builtin/nodes/control_load_skills.md +8 -23
  72. package/builtin/nodes/control_start.md +2 -0
  73. package/builtin/nodes/control_toBool.md +2 -0
  74. package/builtin/nodes/control_user_workspace.md +2 -0
  75. package/builtin/nodes/control_wait_until.md +2 -0
  76. package/builtin/nodes/display_ascii.md +1 -0
  77. package/builtin/nodes/display_chart.md +1 -0
  78. package/builtin/nodes/display_html.md +1 -0
  79. package/builtin/nodes/display_image.md +1 -0
  80. package/builtin/nodes/display_markdown.md +1 -0
  81. package/builtin/nodes/display_mermaid.md +1 -0
  82. package/builtin/nodes/display_react_app.md +1 -0
  83. package/builtin/nodes/display_table.md +1 -0
  84. package/builtin/nodes/provide_bool.md +1 -0
  85. package/builtin/nodes/provide_file.md +1 -0
  86. package/builtin/nodes/provide_password.md +1 -0
  87. package/builtin/nodes/provide_str.md +1 -0
  88. package/builtin/nodes/tool_display_share_link.md +1 -0
  89. package/builtin/nodes/tool_get_env.md +2 -0
  90. package/builtin/nodes/tool_git_checkout.md +1 -0
  91. package/builtin/nodes/tool_git_worktree_load.md +1 -0
  92. package/builtin/nodes/tool_git_worktree_unload.md +1 -0
  93. package/builtin/nodes/tool_gitlab_create_mr.md +1 -0
  94. package/builtin/nodes/tool_jenkins_build.md +2 -0
  95. package/builtin/nodes/tool_load_key.md +2 -0
  96. package/builtin/nodes/tool_nodejs.md +17 -19
  97. package/builtin/nodes/tool_print.md +2 -0
  98. package/builtin/nodes/tool_save_key.md +2 -0
  99. package/builtin/nodes/tool_set_run_env.md +1 -0
  100. package/builtin/nodes/tool_user_ask.md +2 -0
  101. package/builtin/nodes/tool_user_check.md +2 -0
  102. package/builtin/nodes/tool_wecom_send_app_markdown.md +1 -0
  103. package/builtin/nodes/tool_wecom_send_group_markdown.md +1 -0
  104. package/builtin/nodes/workspace_one_click_task.md +44 -0
  105. package/builtin/nodes/workspace_run.md +16 -0
  106. package/builtin/nodes/workspace_scheduled_run.md +16 -0
  107. package/builtin/pipelines/module-migrate/scripts/gate.mjs +37 -0
  108. package/builtin/pipelines/module-migrate/scripts/static-check.mjs +82 -0
  109. package/builtin/pipelines/module-migrate/workspace.flow.js +172 -0
  110. package/builtin/pipelines/module-migrate/workspace.layout.json +134 -0
  111. package/builtin/pipelines/new/scripts/lint-flow.mjs +38 -0
  112. package/builtin/pipelines/new/workspace.flow.js +91 -0
  113. package/builtin/pipelines/new/workspace.layout.json +70 -0
  114. package/builtin/web-ui/dist/assets/{WorkflowAssistantThread-DPbR2UaN.js → WorkflowAssistantThread-CKClwj96.js} +1 -1
  115. package/builtin/web-ui/dist/assets/index-BZ5KqLur.js +870 -0
  116. package/builtin/web-ui/dist/assets/index-CEXmmwM2.css +1 -0
  117. package/builtin/web-ui/dist/index.html +2 -2
  118. package/package.json +2 -1
  119. package/reference/flow-control-capabilities.md +77 -158
  120. package/reference/flow-layout.md +1 -1
  121. package/reference/flow-prompt-handler-check.md +2 -2
  122. package/skills/agentflow-author-flow/SKILL.md +8 -47
  123. package/skills/agentflow-cli/SKILL.md +158 -25
  124. package/skills/agentflow-cli/agents/openai.yaml +2 -2
  125. package/skills/agentflow-cli/scripts/agentflow-cli.mjs +668 -18
  126. package/skills/agentflow-cli/scripts/agentflow-runtime.mjs +97 -0
  127. package/skills/agentflow-flow-add-instances/SKILL.md +7 -248
  128. package/skills/agentflow-flow-dsl/SKILL.md +206 -0
  129. package/skills/agentflow-flow-dsl/agents/openai.yaml +4 -0
  130. package/skills/agentflow-flow-dsl/references/node-calls.md +39 -0
  131. package/skills/agentflow-flow-edit-node-fields/SKILL.md +4 -73
  132. package/skills/agentflow-flow-recipes/SKILL.md +7 -18
  133. package/skills/agentflow-flow-recipes/references/recipes.md +97 -43
  134. package/skills/agentflow-flow-sync-ui/SKILL.md +3 -54
  135. package/skills/agentflow-node-dsl/SKILL.md +212 -0
  136. package/skills/agentflow-node-dsl/agents/openai.yaml +4 -0
  137. package/skills/agentflow-node-reference/SKILL.md +5 -4
  138. package/skills/agentflow-node-reference/references/builtin-nodes.md +166 -115
  139. package/skills/agentflow-placeholder-reference/SKILL.md +2 -2
  140. package/skills/agentflow-runtime-reference/SKILL.md +2 -2
  141. package/skills/agentflow-runtime-reference/references/runtime.md +1 -1
  142. package/skills/agentflow-workspace-ascii/SKILL.md +9 -16
  143. package/skills/agentflow-workspace-graph/SKILL.md +62 -48
  144. package/skills/agentflow-workspace-html/SKILL.md +7 -2
  145. package/skills/agentflow-workspace-image/SKILL.md +6 -2
  146. package/skills/agentflow-workspace-markdown/SKILL.md +14 -21
  147. package/skills/agentflow-workspace-mermaid/SKILL.md +8 -16
  148. package/bin/lib/api-runner.mjs +0 -387
  149. package/bin/lib/apply.mjs +0 -903
  150. package/bin/lib/composer-flow-instances.mjs +0 -68
  151. package/bin/lib/composer-flow-skeleton.mjs +0 -334
  152. package/bin/lib/composer-flow-validate.mjs +0 -47
  153. package/bin/lib/composer-model-router.mjs +0 -185
  154. package/bin/lib/composer-node-schema.mjs +0 -303
  155. package/bin/lib/composer-planner.mjs +0 -751
  156. package/bin/lib/composer-script-ops.mjs +0 -233
  157. package/bin/lib/flow-static-preview.mjs +0 -104
  158. package/bin/lib/hub-login.mjs +0 -54
  159. package/bin/lib/hub-publish.mjs +0 -159
  160. package/bin/lib/hub-remote.mjs +0 -189
  161. package/bin/lib/hub.mjs +0 -299
  162. package/bin/lib/jenkins.mjs +0 -380
  163. package/bin/lib/node-execute.mjs +0 -539
  164. package/bin/lib/normalize-node-tool-command.mjs +0 -97
  165. package/bin/lib/runtime-context.mjs +0 -243
  166. package/bin/lib/scheduler.mjs +0 -601
  167. package/bin/lib/ui-print.mjs +0 -94
  168. package/bin/pipeline/build-node-prompt.mjs +0 -271
  169. package/bin/pipeline/check-cache.mjs +0 -191
  170. package/bin/pipeline/check-flow.mjs +0 -543
  171. package/bin/pipeline/collect-nodes.mjs +0 -212
  172. package/bin/pipeline/compute-cache-md5.mjs +0 -177
  173. package/bin/pipeline/ensure-run-dir.mjs +0 -71
  174. package/bin/pipeline/gc.mjs +0 -129
  175. package/bin/pipeline/get-env.mjs +0 -59
  176. package/bin/pipeline/get-resolved-values.mjs +0 -344
  177. package/bin/pipeline/load-key.mjs +0 -62
  178. package/bin/pipeline/parse-flow.mjs +0 -708
  179. package/bin/pipeline/post-process-control-if.mjs +0 -23
  180. package/bin/pipeline/post-process-node.mjs +0 -490
  181. package/bin/pipeline/pre-process-node.mjs +0 -1430
  182. package/bin/pipeline/resolve-inputs.mjs +0 -201
  183. package/bin/pipeline/run-tool-nodejs.mjs +0 -167
  184. package/bin/pipeline/save-key.mjs +0 -93
  185. package/bin/pipeline/snapshot-prior-round.mjs +0 -70
  186. package/bin/pipeline/validate-for-ui.mjs +0 -234
  187. package/bin/pipeline/validate-script-output.mjs +0 -130
  188. package/bin/pipeline/write-result.mjs +0 -182
  189. package/builtin/pipelines/module-migrate/flow.yaml +0 -819
  190. package/builtin/pipelines/new/flow.yaml +0 -545
  191. package/builtin/pipelines/new/scripts/check-flow.mjs +0 -9
  192. package/builtin/pipelines/new/scripts/collect-nodes.mjs +0 -211
  193. package/builtin/web-ui/dist/assets/index-BSCpd5la.js +0 -889
  194. package/builtin/web-ui/dist/assets/index-DZ328oSo.css +0 -1
  195. package/skills/agentflow-node-authoring/SKILL.md +0 -57
@@ -0,0 +1,97 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { fileURLToPath, pathToFileURL } from "node:url";
4
+
5
+ const PACKAGE_NAME = "@fieldwangai/agentflow";
6
+ const RUNTIME_ENTRY = path.join("bin", "lib", "skill-runtime.mjs");
7
+ let explicitRoot = "";
8
+ let resolvedRoot = "";
9
+ let runtimePromise = null;
10
+
11
+ function packageLooksUsable(root) {
12
+ if (!root) return false;
13
+ try {
14
+ const manifest = JSON.parse(fs.readFileSync(path.join(root, "package.json"), "utf-8"));
15
+ return manifest?.name === PACKAGE_NAME && fs.statSync(path.join(root, RUNTIME_ENTRY)).isFile();
16
+ } catch {
17
+ return false;
18
+ }
19
+ }
20
+
21
+ function ancestors(start) {
22
+ const out = [];
23
+ let current = path.resolve(start);
24
+ while (true) {
25
+ out.push(current);
26
+ const parent = path.dirname(current);
27
+ if (parent === current) break;
28
+ current = parent;
29
+ }
30
+ return out;
31
+ }
32
+
33
+ function rootsNear(start) {
34
+ const out = [];
35
+ for (const ancestor of ancestors(start)) {
36
+ out.push(ancestor, path.join(ancestor, "node_modules", "@fieldwangai", "agentflow"));
37
+ }
38
+ return out;
39
+ }
40
+
41
+ function executableCandidates() {
42
+ const names = process.platform === "win32" ? ["agentflow.cmd", "agentflow.exe", "agentflow"] : ["agentflow"];
43
+ const dirs = String(process.env.PATH || "").split(path.delimiter).filter(Boolean);
44
+ return dirs.flatMap((dir) => names.map((name) => path.join(dir, name)));
45
+ }
46
+
47
+ function rootsFromExecutable(candidate) {
48
+ try {
49
+ if (!fs.statSync(candidate).isFile() && !fs.lstatSync(candidate).isSymbolicLink()) return [];
50
+ const real = fs.realpathSync(candidate);
51
+ return rootsNear(path.dirname(real));
52
+ } catch {
53
+ return [];
54
+ }
55
+ }
56
+
57
+ export function configureAgentFlowRuntime(options = {}) {
58
+ const requested = String(options.packageRoot || "").trim();
59
+ if (!requested || requested === explicitRoot) return;
60
+ explicitRoot = path.resolve(requested);
61
+ resolvedRoot = "";
62
+ runtimePromise = null;
63
+ }
64
+
65
+ export function resolveAgentFlowPackageRoot() {
66
+ if (resolvedRoot) return resolvedRoot;
67
+ const scriptDir = path.dirname(fileURLToPath(import.meta.url));
68
+ const candidates = [
69
+ explicitRoot,
70
+ String(process.env.AGENTFLOW_PACKAGE_ROOT || "").trim(),
71
+ ...rootsNear(process.cwd()),
72
+ ...rootsNear(scriptDir),
73
+ ...executableCandidates().flatMap(rootsFromExecutable),
74
+ ].filter(Boolean);
75
+ const seen = new Set();
76
+ for (const candidate of candidates) {
77
+ const root = path.resolve(candidate);
78
+ if (seen.has(root)) continue;
79
+ seen.add(root);
80
+ if (packageLooksUsable(root)) {
81
+ resolvedRoot = root;
82
+ return root;
83
+ }
84
+ }
85
+ throw new Error(
86
+ "Cannot locate a compatible @fieldwangai/agentflow runtime. Install/update the agentflow CLI, "
87
+ + "put its executable on PATH, or set AGENTFLOW_PACKAGE_ROOT/--agentflow-package-root.",
88
+ );
89
+ }
90
+
91
+ export function loadAgentFlowRuntime() {
92
+ if (!runtimePromise) {
93
+ const entry = path.join(resolveAgentFlowPackageRoot(), RUNTIME_ENTRY);
94
+ runtimePromise = import(pathToFileURL(entry).href);
95
+ }
96
+ return runtimePromise;
97
+ }
@@ -1,257 +1,16 @@
1
1
  ---
2
2
  name: agentflow-flow-add-instances
3
- description: >-
4
- 向 AgentFlow 的 flow.yaml 新增实例:说明 instances/edges/ui 的 YAML 结构、
5
- 从内置定义拷贝 instance、设计连接点与内容;新增节点的 ui.nodePositions
6
- 默认放在画布视觉中心附近。不用于仅改已有节点文案或占位值。
3
+ description: Legacy Pipeline node authoring is retired. Redirect requests to add nodes to the Workspace Graph skill.
7
4
  ---
8
5
 
9
- # AgentFlow:新增 instances
6
+ # Legacy Flow Node Authoring(已下线)
10
7
 
11
- **向现有 flow 增加新节点(一个或多个)** 时使用本技能。
8
+ skill 原用于向 `flow.yaml` 添加实例,但旧 Pipeline 的 `Start/End` 执行模式已经下线,不再新增或编辑正式 `flow.yaml`。
12
9
 
13
- **路径说明**:`npx skills add` 后本文件不在 AgentFlow 仓库内;文中 `reference/`、`builtin/` 均相对 **AgentFlow 仓库根目录**,请在以该仓库为工作区时打开文件。
10
+ 用户要在画布中加节点时,请改用 `agentflow-workspace-graph`,编辑:
14
11
 
15
- **权威参考**(仓库根):
16
-
17
- - `reference/flow-control-capabilities.md` — handle、边、控制节点语义
18
- - `reference/flow-layout.md` — 整体布局原则(本技能对「新增」默认用**中心落点**,再按需微调)
19
- - `reference/flow-prompt-handler-check.md` — 增边后 USER_PROMPT 与 handler 一致
20
-
21
- ---
22
-
23
- ## flow.yaml 顶层结构
24
-
25
- ```yaml
26
- instances:
27
- <instanceId>:
28
- definitionId: <内置或已存在定义 id>
29
- label: ...
30
- role: ...
31
- input: [ { type, name, value }, ... ]
32
- output: [ { type, name, value }, ... ]
33
- script: <可选,tool_nodejs 直接执行的命令,支持 ${} 占位符>
34
- body: | ...
35
- edges:
36
- - source: <instanceId>
37
- target: <instanceId>
38
- sourceHandle: output-0
39
- targetHandle: input-0
40
- ui:
41
- nodePositions:
42
- <instanceId>:
43
- x: <number>
44
- 'y': <number>
45
- description: 可选
46
- ```
47
-
48
- - **`instances`**:键为 **instanceId**(全图唯一),值为该次运行的节点实例;**不要**写 **`description`**(系统说明由 `definitionId` 对应节点 `.md` 的 frontmatter 提供)。
49
- - **`script`**(仅 `tool_nodejs`):指定直接执行的命令,流水线将跳过 AI 直接运行 `run-tool-nodejs` 执行。支持 `${workspaceRoot}`、`${flowName}`、`${runDir}`、`${flowDir}` 及所有 input 槽位占位符,值自动 shell-quote。**引用 flow 自带脚本必须用 `${flowDir}/scripts/xxx.mjs`**(`${flowDir}` 指向 flow 当前所在目录,兼容 user/workspace/builtin 三种安装位置),**禁止**硬编码 `${workspaceRoot}/.workspace/agentflow/pipelines/${flowName}/scripts/...`(flow 安装到 `~/agentflow/pipelines/` 或 builtin 时会 Cannot find module)。脚本 stdout 须输出 `{"err_code":0,"message":{"result":"..."}}`。无 `script` 时回退到 AI 执行。
50
- - **`edges`**:有向边;多输入/多输出必须写对 **`targetHandle` / `sourceHandle`**(见下表)。
51
- - **`ui.nodePositions`**:编辑器坐标;**每个新 instanceId 必须新增一项**,否则画布会堆叠。
52
-
53
- ---
54
-
55
- ## 节点类型选择(必读)
56
-
57
- 新增实例时,**首先判断该步骤应使用哪种节点类型**。核心原则:**能用 tool 节点确定性执行的,不要用 agent_subAgent。**
58
-
59
- | 判断条件 | 推荐 definitionId | 说明 |
60
- |----------|-------------------|------|
61
- | 行为完全由输入决定,不需要 AI 推理 | **tool_nodejs** + `script` 字段 | 打印文本、运行已有脚本、文件操作、数据转换等 |
62
- | 向用户输出醒目信息 | **tool_print** | 专用输出节点 |
63
- | 需要 AI 理解上下文、做判断、生成内容 | **agent_subAgent** | 撰写文档、分析代码、理解语义后做决策 |
64
-
65
- **tool_nodejs + script 示例**(打印文本):
66
-
67
- ```yaml
68
- print_haha:
69
- definitionId: tool_nodejs
70
- label: 打印哈哈哈
71
- script: node -e "console.log(${value})"
72
- input:
73
- - type: 节点
74
- name: prev
75
- value: ''
76
- - type: 文本
77
- name: value
78
- value: ''
79
- output:
80
- - type: 节点
81
- name: next
82
- value: ''
83
- - type: 文本
84
- name: result
85
- value: ''
86
- ```
87
-
88
- - `script` 支持 `${}` 占位符引用 input 槽位,值自动 shell-quote。
89
- - input/output 须与 `tool_nodejs` 节点定义一致(prev/next + result),可按需添加额外文本/文件槽位供 `script` 引用。
90
- - **成败判定**:以脚本进程 **exit code** 为准(0 = success,非 0 = failed)。
91
- - **stdout → result**:脚本 stdout 直接作为 result 槽位内容,纯文本即可(如 `console.log("hello")`)。无需输出 JSON。
92
- - **JSON 兼容(可选)**:stdout 为 `{"err_code":0,"message":{"result":"..."}}` 时,err_code 覆盖 exit code;仅旧脚本或需要特殊成败语义时使用。
93
-
94
- **常见误用**:用 `agent_subAgent` 做「打印一段文字」「执行一个已知命令」「跑一个已有脚本」—— 这些都应该用 `tool_nodejs` + `script`。
95
-
96
- ### tool_nodejs 的 `script` 与 `body` 区分(关键规则)
97
-
98
- | 字段 | 作用 | 执行时行为 |
99
- |------|------|-----------|
100
- | `script` | **实际执行的 shell/node 命令** | 流水线直接 spawn 执行,stdout 作为 result |
101
- | `body` | **纯文档说明**(可选) | 有 `script` 时完全忽略;无 `script` 时作为 AI 指令(兜底) |
102
-
103
- **关键约束**:
104
- 1. **`tool_nodejs` 必须写 `script` 字段**,内容为完整可执行的命令或脚本,禁止用自然语言描述
105
- 2. `script` 中可通过 `${}` 引用 input 槽位和系统变量,值自动 shell-quote
106
- 3. **`script` 必须引用所有非 node 类型的 input 和 output 引脚**(硬性校验,validate-flow 会报错):
107
- - 所有 input 引脚(type ≠ node)必须在 script 中出现 `${slotName}`,用于接收上游数据
108
- - 所有 output 引脚(type ≠ node)必须在 script 中出现 `${slotName}`,系统将解析为 output 文件路径,脚本应直接 `fs.writeFileSync(path, value)` 写入
109
- - **禁止使用 JSON stdout 封装**(`{"err_code":0,"message":{...}}`),直接写文件、用 exit code 决定成败
110
- 4. `body` 仅用于给人类阅读的注释说明,**不会被执行**
111
- 5. 如果无法写出完整可执行的 `script`(如需要 AI 理解/判断),**必须改用 `agent_subAgent`**
112
- 6. **禁止**在 `script` 中写自然语言,**禁止**在 `body` 中写期望被执行的代码逻辑
113
-
114
- ### tool_nodejs 复杂脚本示例
115
-
116
- **多行脚本(API 调用 + 数据处理)**:
117
- ```yaml
118
- fetch_api_data:
119
- definitionId: tool_nodejs
120
- label: 调用API获取数据
121
- script: |
122
- curl -s -H "Authorization: Bearer ${token}" "https://api.example.com/data" | node -e "
123
- let d=''; process.stdin.on('data',c=>d+=c); process.stdin.on('end',()=>{
124
- const res=JSON.parse(d);
125
- const items=res.data.map(i=>({id:i.id,name:i.name}));
126
- console.log(JSON.stringify(items,null,2));
127
- });"
128
- body: |
129
- 调用 Example API 获取数据列表并提取 id 和 name 字段
130
- input:
131
- - { type: 节点, name: prev, value: '' }
132
- - { type: 文本, name: token, value: '' }
133
- output:
134
- - { type: 节点, name: next, value: '' }
135
- - { type: 文本, name: result, value: '' }
136
- ```
137
-
138
- **文件读写 + JSON 处理**:
139
- ```yaml
140
- merge_json_files:
141
- definitionId: tool_nodejs
142
- label: 合并JSON
143
- script: |
144
- node -e "
145
- const fs=require('fs');
146
- const a=JSON.parse(fs.readFileSync(${fileA},'utf8'));
147
- const b=JSON.parse(fs.readFileSync(${fileB},'utf8'));
148
- console.log(JSON.stringify({...a,...b},null,2));
149
- "
150
- body: |
151
- 合并两个 JSON 文件的内容
152
- input:
153
- - { type: 节点, name: prev, value: '' }
154
- - { type: 文件, name: fileA, value: '' }
155
- - { type: 文件, name: fileB, value: '' }
156
- output:
157
- - { type: 节点, name: next, value: '' }
158
- - { type: 文本, name: result, value: '' }
159
- ```
160
-
161
- **错误示范**(禁止):
162
- ```yaml
163
- # ❌ 错误:tool_nodejs 却没有 script,body 是自然语言
164
- bad_node:
165
- definitionId: tool_nodejs
166
- label: 获取截图
167
- body: |
168
- 调用 Figma REST API 获取整体 UI 截图
169
- - 解析 figmaUrl 提取 fileKey
170
- - 调用 GET /v1/images/:fileKey 获取截图
171
- - 将截图保存到 ${result}
172
- ```
173
- 上例应改为:写出完整可执行的 `script`,或改用 `agent_subAgent`(由 AI 执行复杂操作)。
174
-
175
- **节点单一职责**:**每个节点只做一件事,工作内容保持专注和专一。** 不要把多个无关操作塞进同一个 `body`。如果任务包含多个可独立完成的步骤,应拆分为多个节点通过边串联。例如"分析代码并生成测试"应拆为两个节点:节点 A 分析代码 → 节点 B 生成测试。拆分后可调试、可复用、可并行,AI 生成质量也更高。
176
-
177
- 详见 `reference/flow-control-capabilities.md` 第 6 节。
178
-
179
- ---
180
-
181
- ## 新增 instance 的步骤
182
-
183
- 1. 从 **`builtin/pipelines/new/flow.yaml`** 或同仓库已有 flow 中,拷贝**同类** `definitionId` 的实例块作为模板,改 **`instanceId`**、`label`,按需改 `body` / `value`。
184
- 2. **不要**自造 `input`/`output` 的 **顺序、name、type**;须与节点类定义一致,否则 handle 索引会错。
185
- 3. **默认不连线**:仅新增 `instances` 和 `ui.nodePositions`,**不写 `edges`**,重点关注节点内容与连接点的正确性设计。
186
- 4. **仅当用户明确说「连线」「接到 X」「插入到 A 和 B 之间」等指令时**,才在 `edges` 中增加对应的边,并写 **`sourceHandle` / `targetHandle`**。
187
- 5. 在 **`ui.nodePositions`** 中为该 instanceId 写入坐标(见下一节「屏幕中间」)。
188
- 6. 每个 flow 仍只能有一个 **`control_start`**、一个 **`control_end`** 实例(勿重复新增入口/出口)。
189
-
190
- **`control_toBool`**(本地确定性:parseBool 解析 true/1/yes/on → true,其余 → false,不调用 AI)和 **`control_agent_toBool`**(AI agent 语义判断,适用于不确定场景)的 prediction 输出文件内容必须仅为 **`true`** 或 **`false`**。
191
-
192
- ---
193
-
194
- ## Handle 速查(新增边时必填)
195
-
196
- | definitionId | 常用 output | 常用 input |
197
- |--------------|-------------|------------|
198
- | control_start | next → output-0 | — |
199
- | control_end | — | prev → input-0 |
200
- | control_if | next1(**TRUE**) → output-0, next2(**FALSE**) → output-1 | prev → input-0, prediction → input-1 |
201
- | control_toBool | next → output-0, prediction → output-1 | prev → input-0, value → input-1 |
202
- | control_agent_toBool | next → output-0, prediction → output-1 | prev → input-0, value → input-1 |
203
- | control_anyOne | next → output-0 | prev1 → input-0, prev2 → input-1 |
204
- | tool_load_key | next → output-0, result → output-1 | prev → input-0, key → input-1 |
205
- | tool_save_key | next → output-0 | prev → input-0, key → input-1, value → input-2 |
206
- | tool_get_env | value → output-0 | key → input-0 |
207
-
208
- **ToBool → If**:`output-1` → `input-1`(prediction)。**If**:`output-0` = 真分支,`output-1` = 假分支。
209
-
210
- 更多见 `reference/flow-control-capabilities.md` 第 8 节。
211
-
212
- ---
213
-
214
- ## 新增节点的 `ui.nodePositions`:放在屏幕中间
215
-
216
- 目标:新节点出现在画布**视区中心附近**,避免默认 (0,0) 挤在角落或与旧图重叠。
217
-
218
- - **单个新节点**:使用中心基准,例如 **`x: 400`**、**`y: 300`**(可按项目里现有节点的 x/y 范围微调;若已有图,可取现有 `nodePositions` 的 **x、y 中位数** 作为中心再落点)。
219
- - **多个新节点(主链/线性)**:主链节点从左到右排列,每个节点 **x 递增 280**,y 保持一致:
220
- `x: 100 + i * 280`,`y: 300`。
221
- - **分支节点**:分支的不同路径在 y 方向**错开 200**,例如 if 的两路分别 `y: 200` 和 `y: 400`。
222
- - **并行节点**:并行的节点保持相同 x,y 方向每个错开 200。
223
- - 后续若要融入主链/分支布局,再按 `reference/flow-layout.md` 做 x 递进、y 错开。
224
-
225
- ---
226
-
227
- ## 连线(仅用户明确要求时)
228
-
229
- **Fan-out / Fan-in 规则**:
230
- - **一个 output 可连多个 input**(fan-out 允许)。
231
- - **一个 input 只能有一条入边**(fan-in 禁止)。同一个 `target + targetHandle` 不得出现在多条 edge 中;若需替换连线,先删旧边再加新边。违反此规则会导致运行时只有一条边生效,其余静默丢失。
232
-
233
- **中间插入**:删 `A → B`,改为 `A → N`(N 的 `input-0`)、`N → B`(保持原 handle 语义)。
234
- **追加到 End**:删 `X → control_end`,改为 `X → Y → control_end`(常见 `output-0` / `input-0`)。
235
- **If 分支**:增加 `control_toBool`(确定性)或 `control_agent_toBool`(AI 判断)+ `control_if`,prediction 边与两条分支出边按上表连接。
236
-
237
- 完整图示例:`builtin/pipelines/new/flow.yaml`。
238
-
239
- ---
240
-
241
- ## 保存后同步 Web UI
242
-
243
- 将 `flow.yaml` 写入磁盘后,在 **Web UI + Composer** 场景下应通知浏览器刷新画布,见 `skills/agentflow-flow-sync-ui/SKILL.md`。
244
-
245
- ---
246
-
247
- ## 安装(vercel-labs [skills](https://github.com/vercel-labs/skills))
248
-
249
- 在 AgentFlow 仓库根目录:
250
-
251
- ```bash
252
- npx skills add ./skills --agent cursor --skill agentflow-flow-add-instances -y
253
- # 或安装本包全部技能:
254
- npx skills add ./skills --agent cursor -y
12
+ ```text
13
+ .workspace/agentflow/pipelines/<id>/workspace.flow.js
255
14
  ```
256
15
 
257
- 项目内默认 **`.agents/skills/`**;全局加 **`-g`** **`~/.cursor/skills/`**。
16
+ Workspace 图的入口使用 `workspace_run` `workspace_scheduled_run`,不要创建 `control_start`、`control_end`,也不要修改旧 Pipeline 拓扑来实现新需求。
@@ -0,0 +1,206 @@
1
+ ---
2
+ name: agentflow-flow-dsl
3
+ description: >-
4
+ 用 workspace.flow.js(受限 ESM)编写 AgentFlow Workspace 流程图。适用于新建流程、
5
+ 修改流程结构、添加或删除节点、修改连线、控制顺序、分支和定时入口;自定义代码节点包
6
+ 改用 agentflow-node-dsl。
7
+ ---
8
+
9
+ # AgentFlow Flow DSL
10
+
11
+ 流程图就是代码:`flow()` 是 main,节点是 func,连线是传参。
12
+
13
+ ## 目录结构
14
+
15
+ ```
16
+ <flowDir>/
17
+ workspace.flow.js 图结构。受限 JS,永不执行,只被静态解析
18
+ nodes/<name>/index.mjs 代码节点。普通 JS,会被真执行
19
+ prompts/*.md docs/*.html 超过 3000 字符的长文本
20
+ workspace.layout.json 画布坐标,由 layout 命令生成,不手写
21
+ workspace.nodes.json 图片和机器管理属性,别碰
22
+ workspace.state.json 运行产出,别碰
23
+ ```
24
+
25
+ **两类文件规则完全不同**:本 skill 负责 `workspace.flow.js`,并在完成后调用排版命令;需要
26
+ 编写 `nodes/*/index.mjs` 时读取 `agentflow-node-dsl`。
27
+
28
+ 写完必须依次执行:
29
+
30
+ ```bash
31
+ agentflow flow dsl lint <flowDir>
32
+ agentflow flow dsl layout <flowDir>
33
+ ```
34
+
35
+ lint 不通过先修结构,不要排版。`layout` 默认只给缺坐标的新节点补位置,保留用户手工布局。
36
+ 新建流程,或用户明确要求整理整张图时,使用 `agentflow flow dsl layout <flowDir> --all`。
37
+ 不要直接编辑 `workspace.layout.json` 里的 x/y。
38
+
39
+ ## 自动排版是交付步骤
40
+
41
+ 只生成正确连线还不算完成。AI 新建或改完流程后,必须保证节点在 UI 中可读:
42
+
43
+ 1. `lint` 校验节点、引脚和拓扑
44
+ 2. `layout` 生成或补齐坐标
45
+ 3. 如果本地 UI 已打开,再刷新或按 UI 同步 skill 通知画布加载磁盘结果
46
+
47
+ 自动排版按依赖从左到右放置,控制主链保持同一视觉轴,数据源放在上方,展示节点与同层主链
48
+ 错开,分支纵向展开。不要为了“看起来差不多”自行猜坐标。
49
+
50
+ ## 图结构:workspace.flow.js
51
+
52
+ ```js
53
+ import { agent, control, display, file, flow, provide, tool } from "agentflow/flow";
54
+ import collectMetrics from "./nodes/collect-metrics"; // 代码节点
55
+
56
+ const dateStr = provide.str("查询日期", { value: "2026-08-06" });
57
+ const collect = collectMetrics("统计语料", { date: dateStr.value });
58
+ const analyse = agent.subAgent("解读", { metrics: collect.result }, `读 metrics 指出趋势与异常。`);
59
+ const chart = display.chart("规模分布", { content: analyse.result });
60
+
61
+ export const run = flow("Run", collect, analyse, chart);
62
+ ```
63
+
64
+ | 位置 | 含义 |
65
+ |------|------|
66
+ | 第 1 个参数(字符串) | 节点显示名。可省略 |
67
+ | 第 2 个参数(对象) | **输入引脚**。每个键都是引脚名,没有例外 |
68
+ | 第 3 个参数(字符串) | body:agent 是 prompt,`tool.nodejs` 是 shell 命令 |
69
+ | `const 变量名` | **节点 id**。改名 = 重命名节点 |
70
+ | `x.slotName` | 引用上游输出引脚 = 连一条数据线 |
71
+
72
+ `provide.*` 只是数据源,没有 prev/next 槽,**不要放进 `flow(...)` 链**——被谁引用就跟谁跑。
73
+
74
+ ## 四条铁律
75
+
76
+ 1. 节点全部声明在**模块顶层**,不要写进函数
77
+ 2. `flow(...)` 里的顺序 = 控制流顺序;声明顺序不代表执行顺序
78
+ 3. **结构文件禁一切控制流**:`if` / `for` / `while` / `?:` / `.map()` / `await` /
79
+ 箭头函数 / `new` / 动态属性。要写逻辑就建代码节点
80
+ 4. 一个输出可接多个输入(fan-out 允许);**一个输入只能接一条边**(fan-in 禁止);
81
+ 不能成环,没有循环原语
82
+
83
+ ## 控制流
84
+
85
+ ```js
86
+ export const run1 = flow("Run", a, b, c); // 顺序
87
+
88
+ export const nightly = flow.schedule("每日", // 定时
89
+ `{"enabled":true,"cron":"0 8 * * *","timezone":"Asia/Shanghai","overlapPolicy":"skip"}`,
90
+ fetchData, sendReport,
91
+ );
92
+
93
+ const ok = control.agentToBool("判断", { value: review.result }, `合格返回 true`);
94
+ const gate = control.if("是否通过", { prediction: ok.prediction }, // 分支
95
+ flow(publish, notifySuccess), // then
96
+ flow(rollback, notifyFail), // else
97
+ );
98
+ export const run2 = flow("Run", review, ok, gate);
99
+
100
+ export const run3 = flow("Run", build, flow.fork(flow(testA), flow(testB, report))); // 控制边扇出
101
+
102
+ flow.resume(showPlan, stage2); // 闸门:跑到这停,人点第二个 run 才继续
103
+ flow.detached(draftA, draftB); // 有控制链但没 run 入口
104
+ ```
105
+
106
+ **`flow.fork` 不是并行。** 它是「一个 `next` 接多个下游」的写法——`flow(a, b, c)` 是线性的,
107
+ 没法在链里写出扇出,所以有了它。编译出来就是两条边,图里不存在 fork 这个东西:
108
+
109
+ ```
110
+ build.next → testA.prev
111
+ build.next → testB.prev
112
+ ```
113
+
114
+ 运行时把两条分支的节点都收进计划,然后**按拓扑序串行执行**。两个各睡 3 秒的分支跑完要
115
+ 6 秒,不是 3 秒。分支之间也没有隔离:任何一个节点失败,整个 run 就结束,另一支不会跑。
116
+
117
+ 用它的理由是画布结构(两件事都挂在 build 后面、互不依赖),不是省时间。
118
+
119
+ **分支不能汇合**——fan-in 禁止,`control.anyOne` 运行时没实现。两条分支各自收尾。
120
+
121
+ **没有循环**。「改到通过为止」只能展开成固定轮次的嵌套 gate。
122
+
123
+ `control.agentToBool` 是 `runtime: degraded`:它靠通用 agent 路径工作,没有任何东西
124
+ 约束模型输出,而 `parse-bool` 只认 `true` / `1` / `yes` / `on`。prompt 里必须写死
125
+ 「只回 true 或 false,多一个字都会被判成 false」。
126
+
127
+ ## 引脚值与正文插值
128
+
129
+ 引脚值可以写字符串、`file(...)`、上游引用,也可以写 `true` / `42`——非字符串会规范成
130
+ 字符串存图;只有定义表里声明为 `bool` 的槽(以及你自己用 `true`/`false` 建的槽)写回时
131
+ 还是裸的 `true`。
132
+
133
+ 正文(第 3 个参数)里的 `${...}` 有两种含义,按这个顺序判定:
134
+
135
+ ```js
136
+ // 1) 本节点已有同名引脚 -> 运行时占位符,跑的时候换成该槽的值
137
+ const a = agent.subAgent("解读", { metrics: collect.result }, `读 ${metrics} 指出趋势`);
138
+
139
+ // 2) 引用上游 -> 自动建一个同名引脚并连线,等价于上面那种写法
140
+ const b = agent.subAgent("解读", {}, `分析 ${dateStr.value} 的数据`);
141
+ // => 引脚 dateStr 接到 dateStr.value,正文存成 `分析 ${dateStr} 的数据`
142
+ ```
143
+
144
+ 第 2 种的槽名取**引用表达式的根标识符**(`dateStr.value` -> `dateStr`)。想让槽叫别的名字,
145
+ 就用第 1 种写法显式写引脚。插值只在正文里生效,引脚值里的 `${}` 运行时不会替换。
146
+
147
+ ## 自定义代码节点
148
+
149
+ 行为完全由输入决定、不需要 AI 推理时,创建代码节点。不要在本 skill 中推断节点包协议;
150
+ 读取 `agentflow-node-dsl`,由它负责 `nodes/<name>/index.mjs` 的声明、实现、测试和发布规则。
151
+
152
+ ## 自定义输出槽(agent 节点)
153
+
154
+ `agent.subAgent` 想多吐一个值,两件事都要做:
155
+
156
+ ```js
157
+ const breakdown = agent.subAgent("需求拆解", { tapdId: story.result },
158
+ `拆解需求……
159
+
160
+ 因为本节点有额外输出槽,回复必须只包含一个信封,不要有其它内容:
161
+ ---agentflow
162
+ result: |
163
+ <完整结果,每行缩进两个空格>
164
+ outParams:
165
+ storyId: <值>
166
+ ---end`,
167
+ );
168
+ const { storyId } = breakdown; // 声明额外输出槽,下游直接用
169
+
170
+ const notify = tool.wecomSendAppMarkdown("通知", { markdown: breakdown.result, toUser: storyId });
171
+ ```
172
+
173
+ 没有额外输出槽的 agent **不要**写信封,直接回正文。
174
+
175
+ `agent.subAgent` 可以自由加自定义**输入**槽,当实参写就行。声明了但暂时不接线的
176
+ 写 `{ url: null }`,否则这个槽不会存在。
177
+
178
+ ## 超长文本外置
179
+
180
+ body 或引脚值超过 3000 字符,抽成文件:
181
+
182
+ ```js
183
+ const plan = agent.subAgent("规划", { skillsContext }, file("prompts/plan.md"));
184
+ const doc = display.html("使用说明", { content: file("docs/guide.html") });
185
+ ```
186
+
187
+ ## 节点选型
188
+
189
+ | 场景 | 用 |
190
+ |------|----|
191
+ | 要 AI 理解 / 判断 / 生成 | `agent.subAgent` |
192
+ | 行为由输入完全决定,逻辑复杂 | **建代码节点** `nodes/<name>/index.mjs` |
193
+ | 一行 shell 就能搞定 | `tool.nodejs("名字", {}, \`node -e "..."\`)` |
194
+ | 给用户看结果 | `display.markdown` / `.html` / `.chart` / `.table` |
195
+ | 加载 skills 给下游 agent | `control.loadSkills` → `skillsContext` |
196
+ | 加载知识库 / 代码仓 | `control.cdWorkspace` → `knowledgeContext` |
197
+ | 固定文本 / 密钥 | `provide.str` / `provide.password` |
198
+ | 文本转 bool 做分支 | `control.agentToBool` → `prediction` |
199
+
200
+ `display.*` 的内容一般来自连线;写字面量则是手写文档节点(也合法,且不会被运行覆盖)。
201
+
202
+ ## 全部可用节点
203
+
204
+ 见 [node-calls.md](references/node-calls.md)。该表由
205
+ `scripts/generate-agentflow-skill-references.mjs` 从 `builtin/nodes/*.md` 生成,
206
+ 只列运行时真正支持的类型——用了表外的类型 lint 会直接报错。
@@ -0,0 +1,4 @@
1
+ interface:
2
+ display_name: "AgentFlow Flow DSL"
3
+ short_description: "Author AgentFlow workspace graphs and connections in DSL"
4
+ default_prompt: "Use $agentflow-flow-dsl to create or modify an AgentFlow workspace.flow.js graph."
@@ -0,0 +1,39 @@
1
+ # AgentFlow Flow DSL — 内置节点调用表
2
+
3
+ > Generated from `builtin/nodes/*.md` by `scripts/generate-agentflow-skill-references.mjs`.
4
+ > 只列 `runtime: native` 的节点——其余类型 lint 会直接报错。
5
+
6
+ `prev` / `next` / `next1` / `next2` 是控制引脚,由 `flow()` 自动接,**不要手写**。
7
+
8
+ | 调用 | 输入引脚 | 输出引脚 |
9
+ |------|----------|----------|
10
+ | `agent.subAgent` | workspaceContext:text, skillsContext:text, mcpContext:text, knowledgeContext:text | result:text |
11
+ | `control.cdWorkspace` | path:text, label:text, knowledgeContext:text, workspaceContext:text | knowledgeContext:text, workspaceContext:text, cwd:file |
12
+ | `control.if` | prediction:bool | — |
13
+ | `control.loadMcp` | serverNames:text | mcpContext:text |
14
+ | `control.loadSkills` | skillKeys:text | skillsContext:text |
15
+ | `control.userWorkspace` | — | workspaceContext:text, cwd:file |
16
+ | `display.ascii` | content:text | content:text |
17
+ | `display.chart` | content:text, filePath:file, workspaceContext:text | content:text |
18
+ | `display.html` | content:text, filePath:file, workspaceContext:text | content:text |
19
+ | `display.image` | src:text, filePath:file, alt:text, workspaceContext:text | src:text |
20
+ | `display.markdown` | content:text | content:text |
21
+ | `display.mermaid` | content:text | content:text |
22
+ | `display.reactApp` | content:text, filePath:file, workspaceContext:text | content:text |
23
+ | `display.table` | content:text, filePath:file, workspaceContext:text | content:text |
24
+ | `provide.bool` | — | value:bool |
25
+ | `provide.file` | — | value:file |
26
+ | `provide.password` | — | value:text |
27
+ | `provide.str` | — | value:text |
28
+ | `tool.displayShareLink` | title:text, layout:text, nodeIds:text, baseUrl:text | url:text, shareId:text, expiresAt:text |
29
+ | `tool.gitCheckout` | repoUrl:text, branch:text, targetDir:text, pullIfExists:bool, includeSubmodules:bool, remote:text, workspaceContext:text | repoPath:file, branch:text, commit:text, changed:bool, workspaceContext:text, gitContext:text |
30
+ | `tool.gitlabCreateMr` | repoPath:file, gitContext:text, workspaceContext:text, sourceBranch:text, targetBranch:text, title:text, description:text, draft:bool, labels:text, push:bool, remote:text, tokenEnv:text, gitlabApiBase:text, removeSourceBranch:bool, squash:bool | mrUrl:text, created:bool, mrIid:text, projectId:text, sourceBranch:text, targetBranch:text, title:text, message:text |
31
+ | `tool.gitWorktreeLoad` | repoPath:file, branch:text, worktreePath:file, pruneMissing:bool, force:bool, gitContext:text, workspaceContext:text | worktreePath:file, branch:text, commit:text, workspaceContext:text, gitContext:text |
32
+ | `tool.gitWorktreeUnload` | repoPath:file, worktreePath:file, gitContext:text, workspaceContext:text, force:bool, prune:bool | removed:bool, workspaceContext:text, message:text |
33
+ | `tool.nodejs` | workspaceContext:text, skillsContext:text, mcpContext:text | result:text |
34
+ | `tool.setRunEnv` | key:text, value:text, variables:text | keys:text, count:text |
35
+ | `tool.wecomSendAppMarkdown` | markdown:text, toUser:text, corpId:text, corpSecret:text, agentId:text, accessToken:text | sent:bool, message:text, response:text |
36
+ | `tool.wecomSendGroupMarkdown` | markdown:text, webhookUrl:text, webhookKey:text | sent:bool, message:text, response:text |
37
+ | `workspace.oneClickTask` | skillKeys:text, includeWorkspaceContext:bool, displayType:text, knowledgeContext:text, workspaceContext:text | content:text, displayType:text |
38
+ | `workspace.run` | — | — |
39
+ | `workspace.scheduledRun` | — | — |