@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
@@ -111,3 +111,19 @@ export function resolveUserEnvValue(key, userId) {
111
111
  const fromConfig = getFromConfig(readJsonObject(configPath), keyStr);
112
112
  return fromConfig !== undefined ? fromConfig : "";
113
113
  }
114
+
115
+ /**
116
+ * 子进程环境:进程自身的 env + 该用户配置的 env + 调用方补的 extra。
117
+ *
118
+ * `AGENTFLOW_USER_ID` 放最后,防止用户在自己的 env 里把它改成别人。
119
+ * @param {{ userId?: string }} [userCtx]
120
+ * @param {Record<string, string>} [extra]
121
+ */
122
+ export function runtimeEnvForUser(userCtx = {}, extra = {}) {
123
+ return {
124
+ ...process.env,
125
+ ...readMergedEnvObject(userCtx.userId),
126
+ ...extra,
127
+ AGENTFLOW_USER_ID: userCtx.userId || "",
128
+ };
129
+ }
@@ -0,0 +1,234 @@
1
+ /**
2
+ * Workspace graph 的确定性自动排版。
3
+ *
4
+ * 这是 CLI 和 Web UI 共用的纯函数:不读文件、不依赖 Node API,也不修改传入对象。
5
+ * DSL 只描述流程语义,坐标由这里生成,避免模型手写一堆既脆弱又难维护的数字。
6
+ */
7
+
8
+ const HORIZONTAL_GAP = 160;
9
+ const VERTICAL_GAP = 72;
10
+ const ORIGIN_X = 120;
11
+ const MAIN_Y = 360;
12
+ const COLLISION_MARGIN = 28;
13
+
14
+ function finite(value, fallback) {
15
+ return Number.isFinite(Number(value)) ? Number(value) : fallback;
16
+ }
17
+
18
+ function nodeSize(graph, id) {
19
+ const saved = graph?.ui?.nodeSizes?.[id];
20
+ if (saved && Number.isFinite(saved.width) && Number.isFinite(saved.height)) {
21
+ return { width: Math.max(80, saved.width), height: Math.max(60, saved.height) };
22
+ }
23
+ const definitionId = String(graph?.instances?.[id]?.definitionId || "");
24
+ if (definitionId === "workspace_run" || definitionId === "workspace_scheduled_run") {
25
+ return { width: 256, height: 156 };
26
+ }
27
+ if (definitionId.startsWith("provide_")) return { width: 220, height: 110 };
28
+ if (definitionId.startsWith("display_")) return { width: 496, height: 160 };
29
+ return { width: 260, height: 150 };
30
+ }
31
+
32
+ function handleIndex(handle) {
33
+ const matched = /-(\d+)$/.exec(String(handle || ""));
34
+ return matched ? Number(matched[1]) : 0;
35
+ }
36
+
37
+ function edgeIsControl(graph, edge) {
38
+ const source = graph?.instances?.[edge.source];
39
+ const target = graph?.instances?.[edge.target];
40
+ const output = Array.isArray(source?.output) ? source.output[handleIndex(edge.sourceHandle)] : null;
41
+ const input = Array.isArray(target?.input) ? target.input[handleIndex(edge.targetHandle)] : null;
42
+ return output?.type === "node" || input?.type === "node";
43
+ }
44
+
45
+ function orderedNodeIds(graph) {
46
+ const ids = [];
47
+ const seen = new Set();
48
+ const add = (value) => {
49
+ const id = String(value || "");
50
+ if (!id || seen.has(id)) return;
51
+ seen.add(id);
52
+ ids.push(id);
53
+ };
54
+ for (const id of Object.keys(graph?.instances || {})) add(id);
55
+ for (const edge of graph?.edges || []) {
56
+ add(edge?.source);
57
+ add(edge?.target);
58
+ }
59
+ return ids;
60
+ }
61
+
62
+ /** 所有语义边参与分层;同一对节点的控制线和数据线只计一次。 */
63
+ function graphLayers(graph, ids, order) {
64
+ const known = new Set(ids);
65
+ const outgoing = new Map(ids.map((id) => [id, new Set()]));
66
+ const indegree = new Map(ids.map((id) => [id, 0]));
67
+ for (const edge of graph?.edges || []) {
68
+ const source = String(edge?.source || "");
69
+ const target = String(edge?.target || "");
70
+ if (!known.has(source) || !known.has(target) || source === target || outgoing.get(source).has(target)) continue;
71
+ outgoing.get(source).add(target);
72
+ indegree.set(target, indegree.get(target) + 1);
73
+ }
74
+
75
+ const byOrder = (a, b) => order.get(a) - order.get(b);
76
+ const queue = ids.filter((id) => indegree.get(id) === 0).sort(byOrder);
77
+ const depth = new Map(ids.map((id) => [id, 0]));
78
+ const visited = new Set();
79
+ while (queue.length) {
80
+ const id = queue.shift();
81
+ visited.add(id);
82
+ for (const next of [...outgoing.get(id)].sort(byOrder)) {
83
+ depth.set(next, Math.max(depth.get(next), depth.get(id) + 1));
84
+ indegree.set(next, indegree.get(next) - 1);
85
+ if (indegree.get(next) === 0) {
86
+ queue.push(next);
87
+ queue.sort(byOrder);
88
+ }
89
+ }
90
+ }
91
+
92
+ // lint 会拒绝环;这里仍要保证坏图能打开。环里的节点依声明顺序放到后续列。
93
+ let cycleDepth = Math.max(0, ...depth.values());
94
+ for (const id of ids) {
95
+ if (visited.has(id)) continue;
96
+ cycleDepth += 1;
97
+ depth.set(id, cycleDepth);
98
+ }
99
+ return depth;
100
+ }
101
+
102
+ function stackAt(ids, startY, graph, positions) {
103
+ let y = startY;
104
+ for (const id of ids) {
105
+ positions[id] = { ...positions[id], y };
106
+ y += nodeSize(graph, id).height + VERTICAL_GAP;
107
+ }
108
+ return y;
109
+ }
110
+
111
+ function desiredPositions(graph) {
112
+ const ids = orderedNodeIds(graph);
113
+ const order = new Map(ids.map((id, index) => [id, index]));
114
+ const depth = graphLayers(graph, ids, order);
115
+ const controlIds = new Set();
116
+ for (const edge of graph?.edges || []) {
117
+ if (!edgeIsControl(graph, edge)) continue;
118
+ controlIds.add(String(edge.source));
119
+ controlIds.add(String(edge.target));
120
+ }
121
+ for (const id of ids) {
122
+ const definitionId = String(graph?.instances?.[id]?.definitionId || "");
123
+ if (definitionId === "workspace_run" || definitionId === "workspace_scheduled_run") controlIds.add(id);
124
+ }
125
+
126
+ const columns = new Map();
127
+ for (const id of ids) {
128
+ const d = depth.get(id) || 0;
129
+ if (!columns.has(d)) columns.set(d, []);
130
+ columns.get(d).push(id);
131
+ }
132
+ const depths = [...columns.keys()].sort((a, b) => a - b);
133
+ const positions = {};
134
+ let baseY = MAIN_Y;
135
+ for (const column of columns.values()) {
136
+ const main = column.filter((id) => controlIds.has(id));
137
+ const above = column.filter((id) => !controlIds.has(id)
138
+ && !String(graph?.instances?.[id]?.definitionId || "").startsWith("display_"));
139
+ const aboveHeight = above.reduce((sum, id) => sum + nodeSize(graph, id).height, 0)
140
+ + Math.max(0, above.length - 1) * VERTICAL_GAP;
141
+ if (main.length) baseY = Math.max(baseY, aboveHeight + VERTICAL_GAP + 80);
142
+ else {
143
+ const totalHeight = column.reduce((sum, id) => sum + nodeSize(graph, id).height, 0)
144
+ + Math.max(0, column.length - 1) * VERTICAL_GAP;
145
+ baseY = Math.max(baseY, totalHeight / 2 + 80);
146
+ }
147
+ }
148
+ let x = ORIGIN_X;
149
+ for (const d of depths) {
150
+ const column = columns.get(d).sort((a, b) => order.get(a) - order.get(b));
151
+ const main = column.filter((id) => controlIds.has(id));
152
+ const above = column.filter((id) => !controlIds.has(id)
153
+ && !String(graph?.instances?.[id]?.definitionId || "").startsWith("display_"));
154
+ const below = column.filter((id) => !controlIds.has(id) && !above.includes(id));
155
+ for (const id of column) positions[id] = { x, y: baseY };
156
+
157
+ if (main.length) {
158
+ const mainStart = baseY;
159
+ const mainEnd = stackAt(main, mainStart, graph, positions);
160
+
161
+ const aboveHeight = above.reduce((sum, id) => sum + nodeSize(graph, id).height, 0)
162
+ + Math.max(0, above.length - 1) * VERTICAL_GAP;
163
+ stackAt(above, mainStart - VERTICAL_GAP - aboveHeight, graph, positions);
164
+ stackAt(below, mainEnd, graph, positions);
165
+ } else {
166
+ const all = [...above, ...below];
167
+ const height = all.reduce((sum, id) => sum + nodeSize(graph, id).height, 0)
168
+ + Math.max(0, all.length - 1) * VERTICAL_GAP;
169
+ stackAt(all, baseY - height / 2, graph, positions);
170
+ }
171
+
172
+ const width = Math.max(...column.map((id) => nodeSize(graph, id).width), 0);
173
+ x += width + HORIZONTAL_GAP;
174
+ }
175
+ return positions;
176
+ }
177
+
178
+ function validPosition(value) {
179
+ return value && Number.isFinite(value.x) && Number.isFinite(value.y);
180
+ }
181
+
182
+ function overlaps(a, b) {
183
+ return a.x < b.x + b.width + COLLISION_MARGIN
184
+ && a.x + a.width + COLLISION_MARGIN > b.x
185
+ && a.y < b.y + b.height + COLLISION_MARGIN
186
+ && a.y + a.height + COLLISION_MARGIN > b.y;
187
+ }
188
+
189
+ /**
190
+ * 返回完整 nodePositions。默认保留人工拖过的坐标,只给新节点补位置;`preserveExisting:false`
191
+ * 则整图重排。已有坐标可能不在实例表里,原样保留,避免意外破坏 UI 扩展数据。
192
+ */
193
+ export function layoutWorkspaceNodePositions(graph, { preserveExisting = true } = {}) {
194
+ const desired = desiredPositions(graph || {});
195
+ const existing = graph?.ui?.nodePositions && typeof graph.ui.nodePositions === "object"
196
+ ? graph.ui.nodePositions
197
+ : {};
198
+ const result = preserveExisting ? { ...existing } : {};
199
+ const occupied = [];
200
+
201
+ if (preserveExisting) {
202
+ for (const [id, position] of Object.entries(existing)) {
203
+ if (!validPosition(position)) continue;
204
+ const size = nodeSize(graph, id);
205
+ occupied.push({ id, x: position.x, y: position.y, ...size });
206
+ }
207
+ }
208
+
209
+ for (const id of orderedNodeIds(graph || {})) {
210
+ if (preserveExisting && validPosition(existing[id])) continue;
211
+ const size = nodeSize(graph, id);
212
+ const position = { x: finite(desired[id]?.x, ORIGIN_X), y: finite(desired[id]?.y, MAIN_Y) };
213
+ let candidate = { id, ...position, ...size };
214
+ while (occupied.some((other) => overlaps(candidate, other))) {
215
+ position.y += size.height + VERTICAL_GAP;
216
+ candidate = { id, ...position, ...size };
217
+ }
218
+ result[id] = position;
219
+ occupied.push(candidate);
220
+ }
221
+ return result;
222
+ }
223
+
224
+ export function applyWorkspaceAutoLayout(graph, options = {}) {
225
+ const source = graph && typeof graph === "object" ? graph : {};
226
+ const ui = source.ui && typeof source.ui === "object" ? source.ui : {};
227
+ return {
228
+ ...source,
229
+ ui: {
230
+ ...ui,
231
+ nodePositions: layoutWorkspaceNodePositions(source, options),
232
+ },
233
+ };
234
+ }