@xdelivered/emberflow 0.2.0

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 (414) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +153 -0
  3. package/bin/commands.test.ts +185 -0
  4. package/bin/commands.ts +306 -0
  5. package/bin/emberflow.mjs +38 -0
  6. package/bin/init.test.ts +165 -0
  7. package/bin/init.ts +297 -0
  8. package/bin/runtime.mjs +105 -0
  9. package/bin/runtime.test.ts +167 -0
  10. package/bin/skillTargets.test.ts +108 -0
  11. package/bin/skillTargets.ts +36 -0
  12. package/bin/skillTemplates.test.ts +37 -0
  13. package/bin/tsconfig.json +23 -0
  14. package/dist/bin/commands.d.ts +24 -0
  15. package/dist/bin/commands.js +252 -0
  16. package/dist/bin/init.d.ts +17 -0
  17. package/dist/bin/init.js +241 -0
  18. package/dist/bin/runtime.d.mts +39 -0
  19. package/dist/bin/runtime.mjs +96 -0
  20. package/dist/bin/skillTargets.d.ts +13 -0
  21. package/dist/bin/skillTargets.js +22 -0
  22. package/dist/server/agents/claudeAdapter.d.ts +8 -0
  23. package/dist/server/agents/claudeAdapter.js +190 -0
  24. package/dist/server/agents/claudeParse.d.ts +2 -0
  25. package/dist/server/agents/claudeParse.js +162 -0
  26. package/dist/server/agents/codexAdapter.d.ts +13 -0
  27. package/dist/server/agents/codexAdapter.js +174 -0
  28. package/dist/server/agents/codexParse.d.ts +2 -0
  29. package/dist/server/agents/codexParse.js +74 -0
  30. package/dist/server/agents/detect.d.ts +23 -0
  31. package/dist/server/agents/detect.js +35 -0
  32. package/dist/server/agents/gitScope.d.ts +24 -0
  33. package/dist/server/agents/gitScope.js +95 -0
  34. package/dist/server/agents/modelRejectionHint.d.ts +8 -0
  35. package/dist/server/agents/modelRejectionHint.js +12 -0
  36. package/dist/server/agents/prompt.d.ts +79 -0
  37. package/dist/server/agents/prompt.js +364 -0
  38. package/dist/server/agents/runManager.d.ts +90 -0
  39. package/dist/server/agents/runManager.js +259 -0
  40. package/dist/server/agents/types.d.ts +15 -0
  41. package/dist/server/agents/types.js +1 -0
  42. package/dist/server/apiStore.d.ts +60 -0
  43. package/dist/server/apiStore.js +249 -0
  44. package/dist/server/auth/enforce.d.ts +1 -0
  45. package/dist/server/auth/enforce.js +6 -0
  46. package/dist/server/auth/types.d.ts +1 -0
  47. package/dist/server/auth/types.js +1 -0
  48. package/dist/server/auth/verifiers.d.ts +1 -0
  49. package/dist/server/auth/verifiers.js +4 -0
  50. package/dist/server/authAttach.d.ts +19 -0
  51. package/dist/server/authAttach.js +79 -0
  52. package/dist/server/cli.d.ts +7 -0
  53. package/dist/server/cli.js +804 -0
  54. package/dist/server/client.d.ts +130 -0
  55. package/dist/server/client.js +247 -0
  56. package/dist/server/environments.d.ts +103 -0
  57. package/dist/server/environments.js +486 -0
  58. package/dist/server/flowStore.d.ts +37 -0
  59. package/dist/server/flowStore.js +117 -0
  60. package/dist/server/httpOperations.d.ts +32 -0
  61. package/dist/server/httpOperations.js +62 -0
  62. package/dist/server/index.d.ts +1 -0
  63. package/dist/server/index.js +1022 -0
  64. package/dist/server/infrastructure.d.ts +51 -0
  65. package/dist/server/infrastructure.js +116 -0
  66. package/dist/server/login.d.ts +21 -0
  67. package/dist/server/login.js +44 -0
  68. package/dist/server/mcp.d.ts +1 -0
  69. package/dist/server/mcp.js +182 -0
  70. package/dist/server/migrateFlows.d.ts +8 -0
  71. package/dist/server/migrateFlows.js +25 -0
  72. package/dist/server/mockHandler.d.ts +15 -0
  73. package/dist/server/mockHandler.js +35 -0
  74. package/dist/server/nodesPayload.d.ts +14 -0
  75. package/dist/server/nodesPayload.js +14 -0
  76. package/dist/server/normalizeFlow.d.ts +21 -0
  77. package/dist/server/normalizeFlow.js +48 -0
  78. package/dist/server/openBrowser.d.ts +8 -0
  79. package/dist/server/openBrowser.js +16 -0
  80. package/dist/server/operationResult.d.ts +12 -0
  81. package/dist/server/operationResult.js +26 -0
  82. package/dist/server/pathGuard.d.ts +1 -0
  83. package/dist/server/pathGuard.js +16 -0
  84. package/dist/server/pathSafety.d.ts +20 -0
  85. package/dist/server/pathSafety.js +35 -0
  86. package/dist/server/projectConfig.d.ts +47 -0
  87. package/dist/server/projectConfig.js +61 -0
  88. package/dist/server/projectMode.d.ts +26 -0
  89. package/dist/server/projectMode.js +44 -0
  90. package/dist/server/redact.d.ts +6 -0
  91. package/dist/server/redact.js +37 -0
  92. package/dist/server/runRegistry.d.ts +84 -0
  93. package/dist/server/runRegistry.js +200 -0
  94. package/dist/server/runtime/RuntimeAdapter.d.ts +22 -0
  95. package/dist/server/runtime/RuntimeAdapter.js +1 -0
  96. package/dist/server/runtime/expressAdapter.d.ts +19 -0
  97. package/dist/server/runtime/expressAdapter.js +56 -0
  98. package/dist/server/scenarioTest.d.ts +14 -0
  99. package/dist/server/scenarioTest.js +66 -0
  100. package/dist/server/subflowRunner.d.ts +32 -0
  101. package/dist/server/subflowRunner.js +72 -0
  102. package/dist/server/testRunner.d.ts +70 -0
  103. package/dist/server/testRunner.js +151 -0
  104. package/dist/src/config.d.ts +3 -0
  105. package/dist/src/config.js +2 -0
  106. package/dist/src/engine/authVerify.d.ts +33 -0
  107. package/dist/src/engine/authVerify.js +63 -0
  108. package/dist/src/engine/diagnostics.d.ts +62 -0
  109. package/dist/src/engine/diagnostics.js +148 -0
  110. package/dist/src/engine/executor.d.ts +151 -0
  111. package/dist/src/engine/executor.js +684 -0
  112. package/dist/src/engine/httpError.d.ts +7 -0
  113. package/dist/src/engine/httpError.js +14 -0
  114. package/dist/src/engine/index.d.ts +11 -0
  115. package/dist/src/engine/index.js +11 -0
  116. package/dist/src/engine/output.d.ts +11 -0
  117. package/dist/src/engine/output.js +20 -0
  118. package/dist/src/engine/publish.d.ts +30 -0
  119. package/dist/src/engine/publish.js +95 -0
  120. package/dist/src/engine/registry.d.ts +34 -0
  121. package/dist/src/engine/registry.js +67 -0
  122. package/dist/src/engine/scenarios.d.ts +12 -0
  123. package/dist/src/engine/scenarios.js +19 -0
  124. package/dist/src/engine/schemaCheck.d.ts +4 -0
  125. package/dist/src/engine/schemaCheck.js +34 -0
  126. package/dist/src/engine/trace.d.ts +12 -0
  127. package/dist/src/engine/trace.js +12 -0
  128. package/dist/src/engine/types.d.ts +308 -0
  129. package/dist/src/engine/types.js +1 -0
  130. package/dist/src/engine/validation.d.ts +26 -0
  131. package/dist/src/engine/validation.js +318 -0
  132. package/dist/src/flows/anomaly-flows.d.ts +23 -0
  133. package/dist/src/flows/anomaly-flows.js +151 -0
  134. package/dist/src/flows/city-sweep-flow.d.ts +10 -0
  135. package/dist/src/flows/city-sweep-flow.js +127 -0
  136. package/dist/src/flows/login-flow.d.ts +13 -0
  137. package/dist/src/flows/login-flow.js +145 -0
  138. package/dist/src/flows/pradar-flows.d.ts +5 -0
  139. package/dist/src/flows/pradar-flows.js +375 -0
  140. package/dist/src/flows/weather-flow.d.ts +12 -0
  141. package/dist/src/flows/weather-flow.js +150 -0
  142. package/dist/src/lib/pathParams.d.ts +3 -0
  143. package/dist/src/lib/pathParams.js +6 -0
  144. package/dist/src/nodes/anomaly.d.ts +2 -0
  145. package/dist/src/nodes/anomaly.js +556 -0
  146. package/dist/src/nodes/flow-control.d.ts +7 -0
  147. package/dist/src/nodes/flow-control.js +270 -0
  148. package/dist/src/nodes/index.d.ts +3 -0
  149. package/dist/src/nodes/index.js +20 -0
  150. package/dist/src/nodes/login.d.ts +7 -0
  151. package/dist/src/nodes/login.js +158 -0
  152. package/dist/src/nodes/loops.d.ts +11 -0
  153. package/dist/src/nodes/loops.js +60 -0
  154. package/dist/src/nodes/pradar/index.d.ts +7 -0
  155. package/dist/src/nodes/pradar/index.js +9 -0
  156. package/dist/src/nodes/pradar/logic.d.ts +189 -0
  157. package/dist/src/nodes/pradar/logic.js +370 -0
  158. package/dist/src/nodes/pradar/nodes.d.ts +42 -0
  159. package/dist/src/nodes/pradar/nodes.js +801 -0
  160. package/dist/src/nodes/requireAuth.d.ts +20 -0
  161. package/dist/src/nodes/requireAuth.js +48 -0
  162. package/dist/src/nodes/response.d.ts +8 -0
  163. package/dist/src/nodes/response.js +33 -0
  164. package/dist/src/nodes/weather.d.ts +33 -0
  165. package/dist/src/nodes/weather.js +260 -0
  166. package/package.json +92 -0
  167. package/server/agentRoute.test.ts +157 -0
  168. package/server/agents/__fixtures__/fake-claude-writer.mjs +23 -0
  169. package/server/agents/__fixtures__/fake-claude.mjs +55 -0
  170. package/server/agents/__fixtures__/fake-codex-writer.mjs +27 -0
  171. package/server/agents/__fixtures__/fake-codex.mjs +52 -0
  172. package/server/agents/__fixtures__/fake-version-empty.mjs +3 -0
  173. package/server/agents/__fixtures__/fake-version-fail.mjs +4 -0
  174. package/server/agents/__fixtures__/fake-version-garbage.mjs +4 -0
  175. package/server/agents/__fixtures__/fake-version-good.mjs +4 -0
  176. package/server/agents/__fixtures__/fake-version-hang.mjs +3 -0
  177. package/server/agents/claudeAdapter.test.ts +127 -0
  178. package/server/agents/claudeAdapter.ts +212 -0
  179. package/server/agents/claudeParse.ts +193 -0
  180. package/server/agents/codexAdapter.test.ts +125 -0
  181. package/server/agents/codexAdapter.ts +198 -0
  182. package/server/agents/codexParse.test.ts +58 -0
  183. package/server/agents/codexParse.ts +126 -0
  184. package/server/agents/detect.test.ts +65 -0
  185. package/server/agents/detect.ts +43 -0
  186. package/server/agents/gitScope.test.ts +129 -0
  187. package/server/agents/gitScope.ts +110 -0
  188. package/server/agents/modelRejectionHint.test.ts +30 -0
  189. package/server/agents/modelRejectionHint.ts +14 -0
  190. package/server/agents/prompt.test.ts +613 -0
  191. package/server/agents/prompt.ts +527 -0
  192. package/server/agents/runManager.test.ts +251 -0
  193. package/server/agents/runManager.ts +286 -0
  194. package/server/agents/types.ts +17 -0
  195. package/server/apiMount.test.ts +591 -0
  196. package/server/apiStore.test.ts +324 -0
  197. package/server/apiStore.ts +241 -0
  198. package/server/auth/enforce.test.ts +61 -0
  199. package/server/auth/enforce.ts +6 -0
  200. package/server/auth/types.ts +5 -0
  201. package/server/auth/verifiers.test.ts +37 -0
  202. package/server/auth/verifiers.ts +9 -0
  203. package/server/authAttach.test.ts +175 -0
  204. package/server/authAttach.ts +99 -0
  205. package/server/cli.ts +847 -0
  206. package/server/cliAuth.test.ts +229 -0
  207. package/server/cliCreate.test.ts +131 -0
  208. package/server/cliDoctor.test.ts +326 -0
  209. package/server/cliTest.test.ts +735 -0
  210. package/server/client.ts +314 -0
  211. package/server/demoProject.test.ts +29 -0
  212. package/server/diagnosticsRoute.test.ts +169 -0
  213. package/server/environments.test.ts +620 -0
  214. package/server/environments.ts +603 -0
  215. package/server/environmentsRoute.test.ts +385 -0
  216. package/server/errorWorkflow.test.ts +205 -0
  217. package/server/flowStore.test.ts +84 -0
  218. package/server/flowStore.ts +125 -0
  219. package/server/httpOperations.test.ts +377 -0
  220. package/server/httpOperations.ts +96 -0
  221. package/server/index.ts +1111 -0
  222. package/server/infrastructure.test.ts +154 -0
  223. package/server/infrastructure.ts +161 -0
  224. package/server/infrastructureRoute.test.ts +105 -0
  225. package/server/login.test.ts +153 -0
  226. package/server/login.ts +67 -0
  227. package/server/mcp.ts +278 -0
  228. package/server/migrateFlows.test.ts +55 -0
  229. package/server/migrateFlows.ts +23 -0
  230. package/server/mockHandler.test.ts +138 -0
  231. package/server/mockHandler.ts +52 -0
  232. package/server/mockRun.test.ts +367 -0
  233. package/server/mockServe.test.ts +467 -0
  234. package/server/nodesPayload.test.ts +29 -0
  235. package/server/nodesPayload.ts +26 -0
  236. package/server/normalizeFlow.test.ts +80 -0
  237. package/server/normalizeFlow.ts +59 -0
  238. package/server/openBrowser.test.ts +20 -0
  239. package/server/openBrowser.ts +22 -0
  240. package/server/operationResult.test.ts +67 -0
  241. package/server/operationResult.ts +27 -0
  242. package/server/pathGuard.test.ts +19 -0
  243. package/server/pathGuard.ts +12 -0
  244. package/server/pathSafety.test.ts +38 -0
  245. package/server/pathSafety.ts +33 -0
  246. package/server/projectConfig.test.ts +67 -0
  247. package/server/projectConfig.ts +101 -0
  248. package/server/projectMode.test.ts +106 -0
  249. package/server/projectMode.ts +59 -0
  250. package/server/redact.test.ts +88 -0
  251. package/server/redact.ts +35 -0
  252. package/server/redactEgress.test.ts +281 -0
  253. package/server/runRegistry.ts +270 -0
  254. package/server/runsAuth.test.ts +119 -0
  255. package/server/runtime/RuntimeAdapter.ts +24 -0
  256. package/server/runtime/expressAdapter.test.ts +38 -0
  257. package/server/runtime/expressAdapter.ts +70 -0
  258. package/server/scenarioTest.test.ts +148 -0
  259. package/server/scenarioTest.ts +88 -0
  260. package/server/setupStatus.test.ts +127 -0
  261. package/server/subflowRunner.ts +104 -0
  262. package/server/testRunner.ts +216 -0
  263. package/server/tsconfig.json +20 -0
  264. package/server/workflowTestRoute.test.ts +297 -0
  265. package/src/App.tsx +233 -0
  266. package/src/components/AgentConsole.tsx +461 -0
  267. package/src/components/BranchRulesEditor.tsx +156 -0
  268. package/src/components/CreateModal.tsx +275 -0
  269. package/src/components/Dock.tsx +487 -0
  270. package/src/components/EnvironmentDialog.tsx +569 -0
  271. package/src/components/EnvironmentPicker.tsx +357 -0
  272. package/src/components/EnvironmentsDialog.tsx +144 -0
  273. package/src/components/ExecutionPager.tsx +67 -0
  274. package/src/components/InfraPanel.test.tsx +103 -0
  275. package/src/components/InfraPanel.tsx +171 -0
  276. package/src/components/Inspector.tsx +1182 -0
  277. package/src/components/Json.tsx +50 -0
  278. package/src/components/JsonModal.tsx +52 -0
  279. package/src/components/LogMessage.tsx +61 -0
  280. package/src/components/NodeRunModal.tsx +213 -0
  281. package/src/components/RunLogPanel.tsx +397 -0
  282. package/src/components/RunbookView.tsx +1085 -0
  283. package/src/components/Scenarios.tsx +476 -0
  284. package/src/components/SettingsDialog.tsx +229 -0
  285. package/src/components/Sidebar.tsx +436 -0
  286. package/src/components/StatusBar.tsx +302 -0
  287. package/src/components/Toolbar.tsx +663 -0
  288. package/src/components/WelcomeDialog.test.tsx +107 -0
  289. package/src/components/WelcomeDialog.tsx +412 -0
  290. package/src/components/agentMarkdown.test.tsx +91 -0
  291. package/src/components/agentMarkdown.tsx +158 -0
  292. package/src/components/ui/badge.tsx +31 -0
  293. package/src/components/ui/button.tsx +46 -0
  294. package/src/components/ui/combobox.tsx +114 -0
  295. package/src/components/ui/command.tsx +82 -0
  296. package/src/components/ui/dialog.tsx +73 -0
  297. package/src/components/ui/input.tsx +20 -0
  298. package/src/components/ui/popover.tsx +32 -0
  299. package/src/components/ui/resizable.tsx +44 -0
  300. package/src/components/ui/tabs.tsx +36 -0
  301. package/src/config.ts +4 -0
  302. package/src/engine/authVerify.ts +81 -0
  303. package/src/engine/diagnostics.test.ts +437 -0
  304. package/src/engine/diagnostics.ts +211 -0
  305. package/src/engine/executor.test.ts +1339 -0
  306. package/src/engine/executor.ts +840 -0
  307. package/src/engine/httpError.test.ts +12 -0
  308. package/src/engine/httpError.ts +16 -0
  309. package/src/engine/index.ts +11 -0
  310. package/src/engine/output.ts +21 -0
  311. package/src/engine/publish.test.ts +176 -0
  312. package/src/engine/publish.ts +127 -0
  313. package/src/engine/registry.test.ts +64 -0
  314. package/src/engine/registry.ts +80 -0
  315. package/src/engine/scenarios.test.ts +55 -0
  316. package/src/engine/scenarios.ts +29 -0
  317. package/src/engine/schemaCheck.test.ts +21 -0
  318. package/src/engine/schemaCheck.ts +35 -0
  319. package/src/engine/trace.test.ts +19 -0
  320. package/src/engine/trace.ts +23 -0
  321. package/src/engine/types.test.ts +22 -0
  322. package/src/engine/types.ts +316 -0
  323. package/src/engine/validation.test.ts +314 -0
  324. package/src/engine/validation.ts +354 -0
  325. package/src/flows/anomaly-flows.test.ts +81 -0
  326. package/src/flows/anomaly-flows.ts +202 -0
  327. package/src/flows/city-sweep-flow.ts +131 -0
  328. package/src/flows/flows.test.ts +30 -0
  329. package/src/flows/login-flow.ts +150 -0
  330. package/src/flows/pradar-flows.test.ts +204 -0
  331. package/src/flows/pradar-flows.ts +419 -0
  332. package/src/flows/weather-flow.ts +154 -0
  333. package/src/globals.css +147 -0
  334. package/src/lib/pathParams.test.ts +24 -0
  335. package/src/lib/pathParams.ts +6 -0
  336. package/src/lib/registerLens.test.ts +79 -0
  337. package/src/lib/registerLens.ts +59 -0
  338. package/src/lib/runbookModel.test.ts +61 -0
  339. package/src/lib/runbookModel.ts +290 -0
  340. package/src/lib/runbookProjection.test.ts +417 -0
  341. package/src/lib/runbookProjection.ts +254 -0
  342. package/src/lib/useTailAnchor.ts +53 -0
  343. package/src/lib/utils.ts +7 -0
  344. package/src/main.tsx +13 -0
  345. package/src/nodes/anomaly.test.ts +98 -0
  346. package/src/nodes/anomaly.ts +637 -0
  347. package/src/nodes/effects-scan.test.ts +44 -0
  348. package/src/nodes/flow-control.test.ts +280 -0
  349. package/src/nodes/flow-control.ts +305 -0
  350. package/src/nodes/index.ts +22 -0
  351. package/src/nodes/login.test.ts +81 -0
  352. package/src/nodes/login.ts +186 -0
  353. package/src/nodes/loops.ts +69 -0
  354. package/src/nodes/pradar/index.ts +11 -0
  355. package/src/nodes/pradar/logic.test.ts +288 -0
  356. package/src/nodes/pradar/logic.ts +582 -0
  357. package/src/nodes/pradar/nodes.test.ts +146 -0
  358. package/src/nodes/pradar/nodes.ts +955 -0
  359. package/src/nodes/requireAuth.test.ts +75 -0
  360. package/src/nodes/requireAuth.ts +55 -0
  361. package/src/nodes/response.test.ts +33 -0
  362. package/src/nodes/response.ts +39 -0
  363. package/src/nodes/traceDetail.test.ts +39 -0
  364. package/src/nodes/weather.test.ts +65 -0
  365. package/src/nodes/weather.ts +306 -0
  366. package/src/store/agentClient.test.ts +103 -0
  367. package/src/store/agentClient.ts +131 -0
  368. package/src/store/apiTree.test.ts +17 -0
  369. package/src/store/apiTree.ts +73 -0
  370. package/src/store/builderStore.test.ts +1234 -0
  371. package/src/store/builderStore.ts +1879 -0
  372. package/src/store/infraClient.ts +63 -0
  373. package/src/store/nodeMeta.test.ts +24 -0
  374. package/src/store/nodeMeta.ts +21 -0
  375. package/src/store/persistence.test.ts +15 -0
  376. package/src/store/persistence.ts +54 -0
  377. package/src/store/serverRunner.ts +381 -0
  378. package/src/store/setupClient.ts +57 -0
  379. package/studio-dist/assets/ibm-plex-mono-cyrillic-400-normal-BSMlKf0J.woff2 +0 -0
  380. package/studio-dist/assets/ibm-plex-mono-cyrillic-400-normal-CEL4l2ZJ.woff +0 -0
  381. package/studio-dist/assets/ibm-plex-mono-cyrillic-500-normal-Ael50iVv.woff +0 -0
  382. package/studio-dist/assets/ibm-plex-mono-cyrillic-500-normal-Bq9vWWag.woff2 +0 -0
  383. package/studio-dist/assets/ibm-plex-mono-cyrillic-ext-400-normal-DMdlQ8Kv.woff +0 -0
  384. package/studio-dist/assets/ibm-plex-mono-cyrillic-ext-400-normal-xuaO2J-f.woff2 +0 -0
  385. package/studio-dist/assets/ibm-plex-mono-cyrillic-ext-500-normal-BIfNGwUT.woff +0 -0
  386. package/studio-dist/assets/ibm-plex-mono-cyrillic-ext-500-normal-BqneJy0T.woff2 +0 -0
  387. package/studio-dist/assets/ibm-plex-mono-latin-400-normal-CvHOgSBP.woff +0 -0
  388. package/studio-dist/assets/ibm-plex-mono-latin-400-normal-DMJ8VG8y.woff2 +0 -0
  389. package/studio-dist/assets/ibm-plex-mono-latin-500-normal-CB9ihrfo.woff +0 -0
  390. package/studio-dist/assets/ibm-plex-mono-latin-500-normal-DSY6xOcd.woff2 +0 -0
  391. package/studio-dist/assets/ibm-plex-mono-latin-ext-400-normal-BmRBH3aV.woff2 +0 -0
  392. package/studio-dist/assets/ibm-plex-mono-latin-ext-400-normal-D3D2R8hC.woff +0 -0
  393. package/studio-dist/assets/ibm-plex-mono-latin-ext-500-normal-CAhNIIs5.woff2 +0 -0
  394. package/studio-dist/assets/ibm-plex-mono-latin-ext-500-normal-CZ70TYgx.woff +0 -0
  395. package/studio-dist/assets/ibm-plex-mono-vietnamese-400-normal-BulugwFq.woff2 +0 -0
  396. package/studio-dist/assets/ibm-plex-mono-vietnamese-400-normal-DDuiU_S-.woff +0 -0
  397. package/studio-dist/assets/ibm-plex-mono-vietnamese-500-normal-C8zxqsMH.woff +0 -0
  398. package/studio-dist/assets/ibm-plex-mono-vietnamese-500-normal-DZ4AoWbu.woff2 +0 -0
  399. package/studio-dist/assets/index-DNJwW-hM.css +1 -0
  400. package/studio-dist/assets/index-O26dKRjW.js +65 -0
  401. package/studio-dist/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
  402. package/studio-dist/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
  403. package/studio-dist/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
  404. package/studio-dist/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
  405. package/studio-dist/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
  406. package/studio-dist/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
  407. package/studio-dist/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
  408. package/studio-dist/favicon.svg +1 -0
  409. package/studio-dist/icons.svg +24 -0
  410. package/studio-dist/index.html +14 -0
  411. package/templates/skills/emberflow-basics/SKILL.md +316 -0
  412. package/templates/skills/emberflow-model-process/SKILL.md +209 -0
  413. package/templates/skills/emberflow-new-workflow/SKILL.md +211 -0
  414. package/templates/skills/emberflow-review-workflow/SKILL.md +148 -0
@@ -0,0 +1,51 @@
1
+ /**
2
+ * The committed, secrets-free infrastructure manifest the scout writes to
3
+ * `emberflow/infrastructure.json` and agents/studio read back. It describes
4
+ * what a (usually brownfield) project already uses — databases, external APIs,
5
+ * providers — with evidence pointing at the files that prove it. Env-var
6
+ * NAMES only ever appear here; never values.
7
+ */
8
+ /** The closed set of infrastructure categories the scout classifies items into. */
9
+ export declare const INFRASTRUCTURE_KINDS: readonly ["database", "http-api", "queue", "cache", "email", "llm", "auth", "framework", "storage", "other"];
10
+ export type InfrastructureKind = (typeof INFRASTRUCTURE_KINDS)[number];
11
+ /** A single file:note pointer proving an item exists. `note` is optional. */
12
+ export interface InfrastructureEvidence {
13
+ file: string;
14
+ note?: string;
15
+ /** Unknown fields are preserved on read (forward-compatibility). */
16
+ [key: string]: unknown;
17
+ }
18
+ /** One discovered piece of infrastructure. */
19
+ export interface InfrastructureItem {
20
+ id: string;
21
+ kind: InfrastructureKind;
22
+ name: string;
23
+ evidence: InfrastructureEvidence[];
24
+ /** Env-var NAMES this item likely needs (e.g. "DATABASE_URL") — never values. */
25
+ suggestedSecretRefs: string[];
26
+ /** Non-secret config var NAMES this item likely needs. */
27
+ suggestedVars: string[];
28
+ notes?: string;
29
+ /** Unknown fields are preserved on read (forward-compatibility). */
30
+ [key: string]: unknown;
31
+ }
32
+ export interface InfrastructureManifest {
33
+ version: number;
34
+ scannedAt?: string;
35
+ greenfield: boolean;
36
+ summary?: string;
37
+ items: InfrastructureItem[];
38
+ /** Unknown top-level fields are preserved on read (forward-compatibility). */
39
+ [key: string]: unknown;
40
+ }
41
+ export declare const INFRASTRUCTURE_FILENAME: string;
42
+ /**
43
+ * Reads `emberflow/infrastructure.json` from `projectRoot`.
44
+ *
45
+ * - Absent file → `null` (no warning: not-scouted-yet is a normal state).
46
+ * - Malformed (unreadable, invalid JSON, or not the expected shape) → `null`
47
+ * plus a single warning per path per process. The CALLER decides keep-last-good.
48
+ * - Valid → a leniently-normalized `InfrastructureManifest`: required fields
49
+ * defaulted, item `kind`s clamped to the enum, unknown fields preserved.
50
+ */
51
+ export declare function loadInfrastructure(projectRoot: string): InfrastructureManifest | null;
@@ -0,0 +1,116 @@
1
+ import { existsSync, readFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ /**
4
+ * The committed, secrets-free infrastructure manifest the scout writes to
5
+ * `emberflow/infrastructure.json` and agents/studio read back. It describes
6
+ * what a (usually brownfield) project already uses — databases, external APIs,
7
+ * providers — with evidence pointing at the files that prove it. Env-var
8
+ * NAMES only ever appear here; never values.
9
+ */
10
+ /** The closed set of infrastructure categories the scout classifies items into. */
11
+ export const INFRASTRUCTURE_KINDS = [
12
+ 'database',
13
+ 'http-api',
14
+ 'queue',
15
+ 'cache',
16
+ 'email',
17
+ 'llm',
18
+ 'auth',
19
+ 'framework',
20
+ 'storage',
21
+ 'other',
22
+ ];
23
+ export const INFRASTRUCTURE_FILENAME = join('emberflow', 'infrastructure.json');
24
+ function isPlainObject(value) {
25
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
26
+ }
27
+ function isInfrastructureKind(value) {
28
+ return typeof value === 'string' && INFRASTRUCTURE_KINDS.includes(value);
29
+ }
30
+ /**
31
+ * Leniently coerces one raw item into an `InfrastructureItem`. Missing/invalid
32
+ * required fields are defaulted rather than rejected (name/id fall back,
33
+ * unknown `kind` normalizes to "other") and unknown fields pass through, so a
34
+ * slightly-off item from the agent still renders instead of nuking the whole
35
+ * manifest. Returns null only when the item isn't an object at all.
36
+ */
37
+ function coerceItem(raw, index) {
38
+ if (!isPlainObject(raw))
39
+ return null;
40
+ const evidenceRaw = Array.isArray(raw.evidence) ? raw.evidence : [];
41
+ const evidence = evidenceRaw
42
+ .filter(isPlainObject)
43
+ .map((e) => ({ ...e, file: typeof e.file === 'string' ? e.file : '', note: typeof e.note === 'string' ? e.note : undefined }));
44
+ const secretRefs = Array.isArray(raw.suggestedSecretRefs)
45
+ ? raw.suggestedSecretRefs.filter((s) => typeof s === 'string')
46
+ : [];
47
+ const vars = Array.isArray(raw.suggestedVars)
48
+ ? raw.suggestedVars.filter((s) => typeof s === 'string')
49
+ : [];
50
+ return {
51
+ ...raw,
52
+ id: typeof raw.id === 'string' && raw.id.length > 0 ? raw.id : `item-${index}`,
53
+ kind: isInfrastructureKind(raw.kind) ? raw.kind : 'other',
54
+ name: typeof raw.name === 'string' && raw.name.length > 0 ? raw.name : 'Unnamed',
55
+ evidence,
56
+ suggestedSecretRefs: secretRefs,
57
+ suggestedVars: vars,
58
+ ...(typeof raw.notes === 'string' ? { notes: raw.notes } : {}),
59
+ };
60
+ }
61
+ /** One-shot per-path warn dedupe so repeated GETs don't spam the log. */
62
+ const warnedOnce = new Set();
63
+ /**
64
+ * Reads `emberflow/infrastructure.json` from `projectRoot`.
65
+ *
66
+ * - Absent file → `null` (no warning: not-scouted-yet is a normal state).
67
+ * - Malformed (unreadable, invalid JSON, or not the expected shape) → `null`
68
+ * plus a single warning per path per process. The CALLER decides keep-last-good.
69
+ * - Valid → a leniently-normalized `InfrastructureManifest`: required fields
70
+ * defaulted, item `kind`s clamped to the enum, unknown fields preserved.
71
+ */
72
+ export function loadInfrastructure(projectRoot) {
73
+ const path = join(projectRoot, INFRASTRUCTURE_FILENAME);
74
+ if (!existsSync(path))
75
+ return null;
76
+ const warn = (message) => {
77
+ if (!warnedOnce.has(path)) {
78
+ warnedOnce.add(path);
79
+ console.warn(`[emberflow] ${INFRASTRUCTURE_FILENAME} ${message} — ignoring it (keeping last good state). Re-run the infrastructure scout to regenerate it.`);
80
+ }
81
+ return null;
82
+ };
83
+ let parsed;
84
+ try {
85
+ parsed = JSON.parse(readFileSync(path, 'utf8'));
86
+ }
87
+ catch {
88
+ // Deliberately NOT interpolating the parse error's message: on malformed
89
+ // JSON, JSON.parse's error text often echoes a fragment of the file
90
+ // content around the failure point (e.g. "Unexpected token o in JSON at
91
+ // position 5 ... <10 chars of the file>"), which could leak a fragment of
92
+ // a secret value if one was pasted into this file. Keep the warning generic.
93
+ return warn('is not valid JSON');
94
+ }
95
+ if (!isPlainObject(parsed)) {
96
+ return warn('is not a JSON object at the top level');
97
+ }
98
+ if (parsed.items !== undefined && !Array.isArray(parsed.items)) {
99
+ return warn('has an "items" field that is not an array');
100
+ }
101
+ // A successful read clears any prior warning for this path, so a fixed file
102
+ // warns again cleanly if it later regresses.
103
+ warnedOnce.delete(path);
104
+ const rawItems = Array.isArray(parsed.items) ? parsed.items : [];
105
+ const items = rawItems
106
+ .map((item, i) => coerceItem(item, i))
107
+ .filter((item) => item !== null);
108
+ return {
109
+ ...parsed,
110
+ version: typeof parsed.version === 'number' ? parsed.version : 1,
111
+ ...(typeof parsed.scannedAt === 'string' ? { scannedAt: parsed.scannedAt } : {}),
112
+ greenfield: parsed.greenfield === true,
113
+ ...(typeof parsed.summary === 'string' ? { summary: parsed.summary } : {}),
114
+ items,
115
+ };
116
+ }
@@ -0,0 +1,21 @@
1
+ import { type EnvironmentDefinition } from './environments.js';
2
+ /** Minimal response shape performLogin needs from the injected fetch. */
3
+ interface LoginResponse {
4
+ status: number;
5
+ headers: Headers;
6
+ json: () => Promise<any>;
7
+ }
8
+ export interface PerformLoginDeps {
9
+ fetch: (url: string, init?: RequestInit) => Promise<LoginResponse>;
10
+ }
11
+ /**
12
+ * Fires the configured login request for `envName`, captures the resulting
13
+ * credential per `envDef.auth.login.capture`, and persists it into the
14
+ * environment's secrets under `envDef.auth.attach.secretRef`.
15
+ *
16
+ * Never returns or logs the captured value — only the secretRef name.
17
+ */
18
+ export declare function performLogin(cwd: string, envName: string, envDef: EnvironmentDefinition, deps?: PerformLoginDeps): Promise<{
19
+ secretRef: string;
20
+ }>;
21
+ export {};
@@ -0,0 +1,44 @@
1
+ import { captureCredential } from './authAttach.js';
2
+ import { setEnvironmentSecret } from './environments.js';
3
+ /**
4
+ * Fires the configured login request for `envName`, captures the resulting
5
+ * credential per `envDef.auth.login.capture`, and persists it into the
6
+ * environment's secrets under `envDef.auth.attach.secretRef`.
7
+ *
8
+ * Never returns or logs the captured value — only the secretRef name.
9
+ */
10
+ export async function performLogin(cwd, envName, envDef, deps = { fetch: globalThis.fetch }) {
11
+ const login = envDef.auth?.login;
12
+ if (!login) {
13
+ throw new Error(`environment "${envName}" has no auth.login configured`);
14
+ }
15
+ const { method, url, headers, bodyRef } = login.request;
16
+ const init = { method, ...(headers ? { headers } : {}) };
17
+ if (bodyRef !== undefined) {
18
+ const rawBody = envDef.secrets[bodyRef];
19
+ let parsed;
20
+ try {
21
+ parsed = JSON.parse(rawBody);
22
+ }
23
+ catch {
24
+ // Never include the secret content in the message — only names.
25
+ throw new Error(`login body secret "${bodyRef}" for environment "${envName}" is not valid JSON`);
26
+ }
27
+ init.body = JSON.stringify(parsed);
28
+ // Lowercase all caller header keys so an explicit content-type (any casing)
29
+ // wins over — and never duplicates — the auto-added default.
30
+ const callerHeaders = Object.fromEntries(Object.entries(headers ?? {}).map(([k, v]) => [k.toLowerCase(), v]));
31
+ init.headers = { 'content-type': 'application/json', ...callerHeaders };
32
+ }
33
+ const res = await deps.fetch(url, init);
34
+ if (res.status < 200 || res.status >= 300) {
35
+ throw new Error(`login failed: ${res.status}`);
36
+ }
37
+ const value = await captureCredential(res, login.capture);
38
+ if (value === null) {
39
+ throw new Error('login succeeded but no credential captured');
40
+ }
41
+ const secretRef = envDef.auth.attach.secretRef;
42
+ await setEnvironmentSecret(cwd, envName, secretRef, value);
43
+ return { secretRef };
44
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,182 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
3
+ import { z } from 'zod';
4
+ import { findScenario, publishFlow, scenarioNames, validateFlow, } from '../src/engine/index.js';
5
+ import { createDefaultRegistry } from '../src/nodes/index.js';
6
+ import * as client from './client.js';
7
+ /** Wraps any value as an MCP text-content result. */
8
+ function json(value, isError = false) {
9
+ return {
10
+ content: [{ type: 'text', text: JSON.stringify(value, null, 2) }],
11
+ ...(isError ? { isError: true } : {}),
12
+ };
13
+ }
14
+ /** Runs a handler, turning any thrown error into an isError text result. */
15
+ async function guard(fn) {
16
+ try {
17
+ return await fn();
18
+ }
19
+ catch (err) {
20
+ return json({ error: err instanceof Error ? err.message : String(err) }, true);
21
+ }
22
+ }
23
+ const server = new McpServer({ name: 'emberflow', version: '0.0.0' });
24
+ server.tool('list_nodes', 'List all registered node types', {}, () => guard(async () => {
25
+ const registry = createDefaultRegistry();
26
+ return json(registry.list().map((d) => ({
27
+ type: d.type,
28
+ label: d.label,
29
+ category: d.category,
30
+ description: d.description,
31
+ })));
32
+ }));
33
+ server.tool('get_node_schema', 'Get the full NodeDefinition for a node type', { type: z.string() }, ({ type }) => guard(async () => {
34
+ const registry = createDefaultRegistry();
35
+ if (!registry.has(type))
36
+ return json({ error: `Unknown node type: ${type}` }, true);
37
+ return json(registry.get(type).definition);
38
+ }));
39
+ server.tool('list_operations', 'List every operation the runner knows, annotated with its API location. Each entry: ' +
40
+ '{ id, name, path, folder, http }. "path" is the apis/ tree location (api/…folders/op) ' +
41
+ 'and equals the id. "http" is the HTTP trigger { method, path } for operations exposed as ' +
42
+ 'endpoints — an HTTP operation runs as Input({ params, query, body, headers }) → ' +
43
+ 'Response({ status, body }); "http" is null for internal sub-flow operations invoked by ' +
44
+ 'other operations rather than over HTTP.', {}, () => guard(async () => {
45
+ const ops = await client.listOperations();
46
+ return json(ops.map((op) => ({
47
+ id: op.id,
48
+ name: op.name,
49
+ path: op.path,
50
+ folder: op.path.split('/')[0],
51
+ http: op.http ? { method: op.http.method, path: op.http.path } : null,
52
+ })));
53
+ }));
54
+ server.tool('list_apis', 'Return the API tree: top-level APIs → folders → operations (each op carries its ' +
55
+ 'id, name, path, and http trigger, if any). Built from the operations\' apis/ paths so an ' +
56
+ 'agent can see how operations are organised across APIs and which are HTTP endpoints ' +
57
+ 'vs internal sub-flows.', {}, () => guard(async () => json(await client.apiTree())));
58
+ server.tool('get_operation', "Get an operation's full JSON by id (its nodes, edges, http trigger + auth, scenarios). " +
59
+ 'The id equals the operation\'s apis/ path.', { id: z.string() }, ({ id }) => guard(async () => {
60
+ const flow = await client.getWorkflow(id);
61
+ if (!flow)
62
+ return json({ error: `Unknown operation: ${id}` }, true);
63
+ return json(flow);
64
+ }));
65
+ server.tool('create_operation', 'Create a brand-new operation at an explicit apis/ path via POST /operations. "path" is the ' +
66
+ 'apis/ tree location (api/…folders/op) and MUST equal flow.id — the runner rejects a ' +
67
+ 'mismatch and 409s if an operation already exists at that path (use save_operation to ' +
68
+ 'update an existing one). Set flow.http = { method, path[, auth] } to expose it as an HTTP ' +
69
+ 'endpoint (Input({ params, query, body, headers }) → Response({ status, body })); omit http ' +
70
+ 'for an internal sub-flow.', {
71
+ flow: z.record(z.string(), z.unknown()),
72
+ path: z.string(),
73
+ }, ({ flow, path }) => guard(async () => {
74
+ const definition = flow;
75
+ const registry = createDefaultRegistry();
76
+ const issues = validateFlow(definition, registry);
77
+ if (issues.some((i) => i.severity === 'error'))
78
+ return json({ issues }, true);
79
+ const result = await client.createOperation(definition, path);
80
+ if (!result.ok)
81
+ return json({ error: result.error }, true);
82
+ return json({ ok: true, id: definition.id, path });
83
+ }));
84
+ server.tool('delete_operation', 'Delete an operation by id (its apis/ tree path, e.g. "billing/charge"). Removes the ' +
85
+ 'operation file and its scenarios sidecar from the runner. Prefer this over editing files ' +
86
+ 'for removals — a deleted op is unmounted immediately. Returns { ok } or an error if the id ' +
87
+ 'is unknown.', { id: z.string() }, ({ id }) => guard(async () => {
88
+ const result = await client.deleteOperation(id);
89
+ if (!result.ok)
90
+ return json({ error: result.error }, true);
91
+ return json({ ok: true, id });
92
+ }));
93
+ server.tool('save_operation', 'Validate then save an existing operation to the runner (rejects operations with ' +
94
+ 'error-severity issues). Persists the whole flow including its http trigger ' +
95
+ '({ method, path, auth }) and auth fields. Use create_operation for a brand-new op.', { flow: z.record(z.string(), z.unknown()) }, ({ flow }) => guard(async () => {
96
+ const definition = flow;
97
+ const registry = createDefaultRegistry();
98
+ const issues = validateFlow(definition, registry);
99
+ if (issues.some((i) => i.severity === 'error'))
100
+ return json({ issues }, true);
101
+ await client.saveWorkflow(definition);
102
+ return json({ ok: true, id: definition.id });
103
+ }));
104
+ server.tool('validate_operation', 'Validate an operation flow and return its issues (does not save).', { flow: z.record(z.string(), z.unknown()) }, ({ flow }) => guard(async () => {
105
+ const registry = createDefaultRegistry();
106
+ const issues = validateFlow(flow, registry);
107
+ const valid = !issues.some((i) => i.severity === 'error');
108
+ return json({ valid, issues });
109
+ }));
110
+ server.tool('run_operation', 'Run an operation to completion by id or inline flow; returns status, nodeStates, logs. ' +
111
+ 'Accepts either an explicit "input" payload or a "scenario" name/id naming a test input ' +
112
+ 'embedded in the operation. For an HTTP operation the input is the request shape ' +
113
+ '{ params, query, body, headers } and the run resolves a Response({ status, body }). ' +
114
+ '"environment" selects a named environment from emberflow.environments.json (default: the ' +
115
+ "runner's default environment). \"safeMode\" overrides safe mode (default: the environment's " +
116
+ 'default — protected environments default to safe). Disabling safe mode on a protected ' +
117
+ 'environment additionally requires "confirm" to equal "environment" exactly, or the run is ' +
118
+ 'rejected.', {
119
+ id: z.string().optional(),
120
+ flow: z.record(z.string(), z.unknown()).optional(),
121
+ input: z.record(z.string(), z.unknown()).optional(),
122
+ scenario: z.string().optional(),
123
+ environment: z.string().optional(),
124
+ safeMode: z.boolean().optional(),
125
+ confirm: z.string().optional(),
126
+ }, ({ id, flow, input, scenario, environment, safeMode, confirm }) => guard(async () => {
127
+ let definition;
128
+ if (flow) {
129
+ definition = flow;
130
+ }
131
+ else if (id) {
132
+ definition = await client.getWorkflow(id);
133
+ if (!definition)
134
+ return json({ error: `Unknown operation: ${id}` }, true);
135
+ }
136
+ else {
137
+ return json({ error: 'run_operation requires either "id" or "flow"' }, true);
138
+ }
139
+ if (scenario && input) {
140
+ return json({ error: 'run_operation accepts either "scenario" or "input", not both' }, true);
141
+ }
142
+ let resolvedInput = input;
143
+ if (scenario) {
144
+ const found = findScenario(definition, scenario);
145
+ if (!found) {
146
+ const names = scenarioNames(definition);
147
+ return json({ error: `Unknown scenario: "${scenario}". Available: ${names.length ? names.join(', ') : 'none'}` }, true);
148
+ }
149
+ resolvedInput = found.input;
150
+ }
151
+ const runId = await client.startRun({
152
+ flow: definition,
153
+ input: resolvedInput,
154
+ environment,
155
+ safeMode,
156
+ confirm,
157
+ });
158
+ const { run, logs } = await client.waitForRun(runId);
159
+ return json({ status: run.status, environment: run.environment, safeMode: run.safeMode, nodeStates: run.nodeStates, logs }, run.status !== 'succeeded');
160
+ }));
161
+ server.tool('list_environments', 'List the named environments the runner offers (from emberflow.environments.json): each ' +
162
+ 'entry is { name, protected, varKeys, secretKeys } — only key NAMES are returned, never ' +
163
+ 'values/secrets. Also returns defaultEnvironment. Use a name as run_operation\'s ' +
164
+ '"environment". Protected environments default to safe mode.', {}, () => guard(async () => json(await client.listEnvironments())));
165
+ server.tool('publish_operation', 'Publish an operation by id into a sealed emberflow/v1 artifact (validates, strips pins, ' +
166
+ 'hashes node implementations); does not write any files.', { id: z.string() }, ({ id }) => guard(async () => {
167
+ const flow = await client.getWorkflow(id);
168
+ if (!flow)
169
+ return json({ error: `Unknown operation: ${id}` }, true);
170
+ const registry = createDefaultRegistry();
171
+ const artifact = await publishFlow(flow, registry);
172
+ return json(artifact);
173
+ }));
174
+ server.tool('get_node_samples', 'Get recorded execution samples for a node', { nodeId: z.string() }, ({ nodeId }) => guard(async () => json(await client.samples(nodeId))));
175
+ async function main() {
176
+ const transport = new StdioServerTransport();
177
+ await server.connect(transport);
178
+ }
179
+ main().catch((err) => {
180
+ process.stderr.write(`${err instanceof Error ? err.stack ?? err.message : String(err)}\n`);
181
+ process.exit(1);
182
+ });
@@ -0,0 +1,8 @@
1
+ /** One-time relocation of the legacy flat flows layout into `<apisDir>/default/`.
2
+ * Ids and sidecars are preserved; subflow references (by id) stay valid.
3
+ * Idempotent: a no-op once `apisDir` exists or `flowsDir` is absent.
4
+ * `flowsDir` is the actual configured flows directory — callers must not
5
+ * assume it is literally named `flows` (project configs can rename it). */
6
+ export declare function migrateFlowsToApis(flowsDir: string, apisDir: string): {
7
+ moved: string[];
8
+ };
@@ -0,0 +1,25 @@
1
+ import { existsSync, mkdirSync, readdirSync, renameSync, rmdirSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ /** One-time relocation of the legacy flat flows layout into `<apisDir>/default/`.
4
+ * Ids and sidecars are preserved; subflow references (by id) stay valid.
5
+ * Idempotent: a no-op once `apisDir` exists or `flowsDir` is absent.
6
+ * `flowsDir` is the actual configured flows directory — callers must not
7
+ * assume it is literally named `flows` (project configs can rename it). */
8
+ export function migrateFlowsToApis(flowsDir, apisDir) {
9
+ if (existsSync(apisDir) || !existsSync(flowsDir))
10
+ return { moved: [] };
11
+ const target = join(apisDir, 'default');
12
+ mkdirSync(target, { recursive: true });
13
+ const moved = [];
14
+ for (const file of readdirSync(flowsDir)) {
15
+ renameSync(join(flowsDir, file), join(target, file));
16
+ if (file.endsWith('.json') && !file.endsWith('.scenarios.json')) {
17
+ moved.push(file.slice(0, -'.json'.length));
18
+ }
19
+ }
20
+ try {
21
+ rmdirSync(flowsDir);
22
+ }
23
+ catch { /* non-empty or already gone — leave it */ }
24
+ return { moved };
25
+ }
@@ -0,0 +1,15 @@
1
+ import type { ScenarioDefinition, WorkflowDefinition } from '../src/engine/index.js';
2
+ export interface MockResponse {
3
+ status: number;
4
+ body: unknown;
5
+ scenario?: string;
6
+ }
7
+ /** Pure scenario→response selection for mock mode. No I/O, no auth, no node
8
+ * execution. Selection: `x-emberflow-scenario` header (exact scenario name)
9
+ * → `__scenario` query param → first scenario with an `expect`. A named but
10
+ * unknown scenario is a 404; a named scenario without an `expect` is a 501;
11
+ * no scenario with an `expect` at all is a 501. */
12
+ export declare function mockResponseFor(_flow: WorkflowDefinition, scenarios: ScenarioDefinition[], req: {
13
+ headers: Record<string, unknown>;
14
+ query: Record<string, unknown>;
15
+ }): MockResponse;
@@ -0,0 +1,35 @@
1
+ const SCENARIO_HEADER = 'x-emberflow-scenario';
2
+ const SCENARIO_QUERY = '__scenario';
3
+ function fromExpect(scenario) {
4
+ const expect = scenario.expect;
5
+ if (!expect) {
6
+ return {
7
+ status: 501,
8
+ body: { error: `scenario "${scenario.name}" has no expect to mock from` },
9
+ };
10
+ }
11
+ return { status: expect.status ?? 200, body: expect.body ?? {}, scenario: scenario.name };
12
+ }
13
+ function asScenarioName(value) {
14
+ return typeof value === 'string' ? value : undefined;
15
+ }
16
+ /** Pure scenario→response selection for mock mode. No I/O, no auth, no node
17
+ * execution. Selection: `x-emberflow-scenario` header (exact scenario name)
18
+ * → `__scenario` query param → first scenario with an `expect`. A named but
19
+ * unknown scenario is a 404; a named scenario without an `expect` is a 501;
20
+ * no scenario with an `expect` at all is a 501. */
21
+ export function mockResponseFor(_flow, scenarios, req) {
22
+ const requestedName = asScenarioName(req.headers[SCENARIO_HEADER]) ?? asScenarioName(req.query[SCENARIO_QUERY]);
23
+ if (requestedName !== undefined) {
24
+ const named = scenarios.find((s) => s.name === requestedName);
25
+ if (!named) {
26
+ return { status: 404, body: { error: `unknown scenario "${requestedName}"` } };
27
+ }
28
+ return fromExpect(named);
29
+ }
30
+ const defaultScenario = scenarios.find((s) => s.expect !== undefined);
31
+ if (!defaultScenario) {
32
+ return { status: 501, body: { error: 'no mockable scenario (add an expect to a scenario)' } };
33
+ }
34
+ return fromExpect(defaultScenario);
35
+ }
@@ -0,0 +1,14 @@
1
+ import type { NodeDefinition, NodeRegistry } from '../src/engine/index.js';
2
+ /**
3
+ * Node metadata served to the studio over HTTP. Definitions carry everything
4
+ * the UI needs (label, schemas, category, tags); `source` is the
5
+ * implementation's toString() so the Inspector can show a node's code without
6
+ * the browser bundling the implementation — the key to consumer nodes
7
+ * appearing in the palette without a per-project browser build.
8
+ */
9
+ export interface NodeMetaPayload {
10
+ nodes: Array<NodeDefinition & {
11
+ source?: string;
12
+ }>;
13
+ }
14
+ export declare function nodesPayload(registry: NodeRegistry): NodeMetaPayload;
@@ -0,0 +1,14 @@
1
+ export function nodesPayload(registry) {
2
+ return {
3
+ nodes: registry.list().map((definition) => {
4
+ let source;
5
+ try {
6
+ source = registry.get(definition.type).implementation.toString();
7
+ }
8
+ catch {
9
+ source = undefined;
10
+ }
11
+ return { ...definition, source };
12
+ }),
13
+ };
14
+ }
@@ -0,0 +1,21 @@
1
+ import type { WorkflowDefinition } from '../src/engine/index.js';
2
+ export interface SeedParamDefaultsResult {
3
+ flow: WorkflowDefinition;
4
+ /** Path-param names that were newly seeded with `''`, in path order. */
5
+ seeded: string[];
6
+ }
7
+ /**
8
+ * Pure normalization step run before validation/persist on every write path
9
+ * (PUT /workflows/:id, POST /operations): seeds an empty-string placeholder
10
+ * under the first Input node's `config.defaults.params` for every `:param`
11
+ * in `flow.http.path` that has no default yet, so a plain Run reaches nodes
12
+ * with `params.<name> === ''` instead of crashing on `undefined` (same
13
+ * scaffold shape `emberflow create` already produces).
14
+ *
15
+ * Mirrors the `missing-param-default` guard semantics (src/engine/diagnostics.ts):
16
+ * presence, not meaningfulness, is what's checked/seeded. No-ops (returning
17
+ * the SAME `flow` object, unmodified) when there's no `:param` in the path,
18
+ * no Input node, or every param already has a default — never overwrites an
19
+ * existing value, whatever it is (including `''`).
20
+ */
21
+ export declare function seedParamDefaults(flow: WorkflowDefinition): SeedParamDefaultsResult;
@@ -0,0 +1,48 @@
1
+ import { parsePathParams } from '../src/lib/pathParams.js';
2
+ /**
3
+ * Pure normalization step run before validation/persist on every write path
4
+ * (PUT /workflows/:id, POST /operations): seeds an empty-string placeholder
5
+ * under the first Input node's `config.defaults.params` for every `:param`
6
+ * in `flow.http.path` that has no default yet, so a plain Run reaches nodes
7
+ * with `params.<name> === ''` instead of crashing on `undefined` (same
8
+ * scaffold shape `emberflow create` already produces).
9
+ *
10
+ * Mirrors the `missing-param-default` guard semantics (src/engine/diagnostics.ts):
11
+ * presence, not meaningfulness, is what's checked/seeded. No-ops (returning
12
+ * the SAME `flow` object, unmodified) when there's no `:param` in the path,
13
+ * no Input node, or every param already has a default — never overwrites an
14
+ * existing value, whatever it is (including `''`).
15
+ */
16
+ export function seedParamDefaults(flow) {
17
+ const path = flow.http?.path;
18
+ const params = path ? parsePathParams(path) : [];
19
+ if (params.length === 0)
20
+ return { flow, seeded: [] };
21
+ const inputNode = flow.nodes.find((n) => n.type === 'Input');
22
+ if (!inputNode)
23
+ return { flow, seeded: [] };
24
+ const existingDefaults = inputNode.config?.defaults;
25
+ const existingParams = existingDefaults && typeof existingDefaults === 'object' && !Array.isArray(existingDefaults)
26
+ ? existingDefaults.params
27
+ : undefined;
28
+ const seeded = params.filter((name) => !existingParams || existingParams[name] === undefined);
29
+ if (seeded.length === 0)
30
+ return { flow, seeded: [] };
31
+ const newParams = { ...(existingParams ?? {}) };
32
+ for (const name of seeded)
33
+ newParams[name] = '';
34
+ const newInputNode = {
35
+ ...inputNode,
36
+ config: {
37
+ ...(inputNode.config ?? {}),
38
+ defaults: { ...(existingDefaults ?? {}), params: newParams },
39
+ },
40
+ };
41
+ return {
42
+ flow: {
43
+ ...flow,
44
+ nodes: flow.nodes.map((n) => (n === inputNode ? newInputNode : n)),
45
+ },
46
+ seeded,
47
+ };
48
+ }
@@ -0,0 +1,8 @@
1
+ import { spawn as nodeSpawn } from 'node:child_process';
2
+ interface OpenDeps {
3
+ platform?: NodeJS.Platform;
4
+ spawn?: typeof nodeSpawn;
5
+ }
6
+ /** Open the default browser at `url`. Best-effort — never throws into the caller. */
7
+ export declare function openBrowser(url: string, deps?: OpenDeps): void;
8
+ export {};
@@ -0,0 +1,16 @@
1
+ import { spawn as nodeSpawn } from 'node:child_process';
2
+ /** Open the default browser at `url`. Best-effort — never throws into the caller. */
3
+ export function openBrowser(url, deps = {}) {
4
+ const platform = deps.platform ?? process.platform;
5
+ const spawn = deps.spawn ?? nodeSpawn;
6
+ const [cmd, args] = platform === 'darwin' ? ['open', [url]]
7
+ : platform === 'win32' ? ['cmd', ['/c', 'start', '', url]]
8
+ : ['xdg-open', [url]];
9
+ try {
10
+ const child = spawn(cmd, args, { stdio: 'ignore', detached: true });
11
+ child.unref?.();
12
+ }
13
+ catch {
14
+ // headless / no browser — the URL is printed by the caller anyway
15
+ }
16
+ }
@@ -0,0 +1,12 @@
1
+ import { type WorkflowDefinition, type WorkflowRun } from '../src/engine/index.js';
2
+ /** Map a finished operation run to an HTTP response. A flow can have more than
3
+ * one `Response` node (e.g. a success branch and an error branch) — only one
4
+ * of them actually ran. Among the `Response`-type nodes, pick the one whose
5
+ * run state actually succeeded (first such node by array order if somehow
6
+ * more than one did); its output (already `{ status, body }`) wins.
7
+ * Otherwise — no Response node, or none of them succeeded — fall back to
8
+ * 200 + the run's Result output. */
9
+ export declare function extractResponse(run: WorkflowRun, flow: WorkflowDefinition): {
10
+ status: number;
11
+ body: unknown;
12
+ };