@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,56 @@
1
+ import express, {} from 'express';
2
+ const METHODS = ['get', 'post', 'put', 'delete', 'patch'];
3
+ function isMethod(m) {
4
+ return METHODS.includes(m);
5
+ }
6
+ /** Express implementation of the `RuntimeAdapter` seam. Keep it thin — this is
7
+ * the seam other runtimes (Deno/Bun) implement later. */
8
+ export class ExpressAdapter {
9
+ _app;
10
+ server;
11
+ seen = new Set();
12
+ constructor(app) {
13
+ this._app = app ?? express();
14
+ }
15
+ get app() {
16
+ return this._app;
17
+ }
18
+ /** The bound address once `listen()` resolves (useful in tests to find an
19
+ * ephemeral port). `null` before listening / after close. */
20
+ get address() {
21
+ const addr = this.server?.address();
22
+ return addr && typeof addr === 'object' ? addr : null;
23
+ }
24
+ registerOperation(op, handler) {
25
+ const method = op.method.toUpperCase();
26
+ const key = `${method} ${op.path}`;
27
+ if (this.seen.has(key)) {
28
+ throw new Error(`Duplicate operation route: ${key}`);
29
+ }
30
+ const lower = method.toLowerCase();
31
+ if (!isMethod(lower)) {
32
+ throw new Error(`Unsupported HTTP method for operation route: ${method} ${op.path}`);
33
+ }
34
+ this.seen.add(key);
35
+ this._app[lower](op.path, handler);
36
+ }
37
+ mountRouter(basePath, router) {
38
+ this._app.use(basePath, router);
39
+ }
40
+ listen(port, host) {
41
+ return new Promise((resolve) => {
42
+ this.server = this._app.listen(port, host, () => resolve());
43
+ });
44
+ }
45
+ close() {
46
+ return new Promise((resolve, reject) => {
47
+ const server = this.server;
48
+ if (!server) {
49
+ resolve();
50
+ return;
51
+ }
52
+ this.server = undefined;
53
+ server.close((err) => (err ? reject(err) : resolve()));
54
+ });
55
+ }
56
+ }
@@ -0,0 +1,14 @@
1
+ import type { ScenarioExpectation, WorkflowDefinition, WorkflowRun } from '../src/engine/index.js';
2
+ /** Deep-subset match: every key/index present in `expected` must match the
3
+ * corresponding key/index in `actual`, recursively. Extra keys/indices in
4
+ * `actual` are ignored. `null` matches only `null`. Returns human-readable
5
+ * failure strings with dotted (and `[i]`) paths; empty array = match. */
6
+ export declare function deepSubsetMatch(expected: unknown, actual: unknown, path?: string): string[];
7
+ /** Check a scenario's finished run against its `expect` assertion. `status`/
8
+ * `body` come from `extractResponse` (the flow's Response-node mapping,
9
+ * defaulting to status 200); `executedNodes` checks each id's node state is
10
+ * `succeeded`. */
11
+ export declare function evaluateExpectation(run: WorkflowRun, flow: WorkflowDefinition, expect: ScenarioExpectation): {
12
+ ok: boolean;
13
+ failures: string[];
14
+ };
@@ -0,0 +1,66 @@
1
+ import { extractResponse } from './operationResult.js';
2
+ function fmt(value) {
3
+ if (value === undefined)
4
+ return 'undefined';
5
+ return JSON.stringify(value);
6
+ }
7
+ /** Deep-subset match: every key/index present in `expected` must match the
8
+ * corresponding key/index in `actual`, recursively. Extra keys/indices in
9
+ * `actual` are ignored. `null` matches only `null`. Returns human-readable
10
+ * failure strings with dotted (and `[i]`) paths; empty array = match. */
11
+ export function deepSubsetMatch(expected, actual, path = '') {
12
+ const label = path === '' ? '' : `${path}: `;
13
+ if (expected === null) {
14
+ return actual === null ? [] : [`${label}expected null, got ${fmt(actual)}`];
15
+ }
16
+ if (Array.isArray(expected)) {
17
+ if (!Array.isArray(actual)) {
18
+ return [`${label}expected array, got ${fmt(actual)}`];
19
+ }
20
+ const failures = [];
21
+ for (let i = 0; i < expected.length; i++) {
22
+ failures.push(...deepSubsetMatch(expected[i], actual[i], `${path}[${i}]`));
23
+ }
24
+ return failures;
25
+ }
26
+ if (typeof expected === 'object') {
27
+ if (typeof actual !== 'object' || actual === null || Array.isArray(actual)) {
28
+ return [`${label}expected object, got ${fmt(actual)}`];
29
+ }
30
+ const failures = [];
31
+ for (const key of Object.keys(expected)) {
32
+ const childPath = path === '' ? key : `${path}.${key}`;
33
+ failures.push(...deepSubsetMatch(expected[key], actual[key], childPath));
34
+ }
35
+ return failures;
36
+ }
37
+ // Primitive.
38
+ return expected === actual ? [] : [`${label}expected ${fmt(expected)}, got ${fmt(actual)}`];
39
+ }
40
+ /** Check a scenario's finished run against its `expect` assertion. `status`/
41
+ * `body` come from `extractResponse` (the flow's Response-node mapping,
42
+ * defaulting to status 200); `executedNodes` checks each id's node state is
43
+ * `succeeded`. */
44
+ export function evaluateExpectation(run, flow, expect) {
45
+ const failures = [];
46
+ if (expect.status !== undefined || expect.body !== undefined) {
47
+ const { status, body } = extractResponse(run, flow);
48
+ if (expect.status !== undefined && expect.status !== status) {
49
+ failures.push(`status: expected ${expect.status}, got ${status}`);
50
+ }
51
+ if (expect.body !== undefined) {
52
+ for (const failure of deepSubsetMatch(expect.body, body)) {
53
+ failures.push(`body.${failure}`);
54
+ }
55
+ }
56
+ }
57
+ if (expect.executedNodes) {
58
+ for (const id of expect.executedNodes) {
59
+ const state = run.nodeStates[id];
60
+ if (state?.status !== 'succeeded') {
61
+ failures.push(`executedNodes: ${id} did not execute (status: ${state?.status ?? 'not run'})`);
62
+ }
63
+ }
64
+ }
65
+ return { ok: failures.length === 0, failures };
66
+ }
@@ -0,0 +1,32 @@
1
+ import type { LogLine, NodeRegistry, StartRunOptions, TraceSink, WorkflowDefinition } from '../src/engine/index.js';
2
+ /** Max depth of nested Subflow calls (ancestry chain length) before a run fails. */
3
+ export declare const SUBFLOW_DEPTH_CAP = 8;
4
+ export interface SubflowRunnerOptions {
5
+ /** Resolves a child workflow by id (server hosts this via apiStore.load). */
6
+ loadFlow: (id: string) => WorkflowDefinition | undefined;
7
+ registry: NodeRegistry;
8
+ secrets: Record<string, string>;
9
+ vars: Record<string, string>;
10
+ environment: string;
11
+ safeMode: boolean;
12
+ /** When true, child runs execute in Mock mode too — nothing real is ever
13
+ * touched during a mock run, including inside Subflow children. See
14
+ * `StartRunOptions.mockRun`. */
15
+ mockRun?: boolean;
16
+ /** Optional trace sink shared with the parent run (SSE/samples plumbing). */
17
+ trace?: TraceSink;
18
+ /** Optional child-log forwarder — receives the child's log lines already
19
+ * prefixed with the child flow's name and the caller node's id, exactly as
20
+ * the live runner streams them over SSE. Absent for headless runs. */
21
+ onLog?: (line: LogLine) => void;
22
+ }
23
+ /**
24
+ * Shared Subflow-runner factory used by BOTH the live runner
25
+ * (server/runRegistry.ts) and the in-process test runner
26
+ * (server/testRunner.ts): resolve the child via `loadFlow`, run it to
27
+ * completion on the same registry sharing the parent's environment
28
+ * (secrets/vars/safeMode), and return its collected Result output. The
29
+ * ancestry chain enforces the depth cap and catches A→B→A cycles; nested
30
+ * children keep their own execution sequence.
31
+ */
32
+ export declare function makeSubflowRunner(opts: SubflowRunnerOptions, ancestry: string[]): NonNullable<StartRunOptions['subflowRunner']>;
@@ -0,0 +1,72 @@
1
+ import { runOutput, startRun } from '../src/engine/index.js';
2
+ /** Max depth of nested Subflow calls (ancestry chain length) before a run fails. */
3
+ export const SUBFLOW_DEPTH_CAP = 8;
4
+ /**
5
+ * Shared Subflow-runner factory used by BOTH the live runner
6
+ * (server/runRegistry.ts) and the in-process test runner
7
+ * (server/testRunner.ts): resolve the child via `loadFlow`, run it to
8
+ * completion on the same registry sharing the parent's environment
9
+ * (secrets/vars/safeMode), and return its collected Result output. The
10
+ * ancestry chain enforces the depth cap and catches A→B→A cycles; nested
11
+ * children keep their own execution sequence.
12
+ */
13
+ export function makeSubflowRunner(opts, ancestry) {
14
+ return async (workflowId, input, callerNodeId) => {
15
+ if (ancestry.length >= SUBFLOW_DEPTH_CAP) {
16
+ return { status: 'failed', error: `subflow depth cap (${SUBFLOW_DEPTH_CAP}) exceeded` };
17
+ }
18
+ if (ancestry.includes(workflowId)) {
19
+ return { status: 'failed', error: `subflow cycle: ${[...ancestry, workflowId].join(' → ')}` };
20
+ }
21
+ const childFlow = opts.loadFlow(workflowId);
22
+ if (!childFlow)
23
+ return { status: 'failed', error: `Unknown workflow: ${workflowId}` };
24
+ try {
25
+ const childHandle = startRun({
26
+ flow: childFlow,
27
+ registry: opts.registry,
28
+ secrets: opts.secrets,
29
+ vars: opts.vars,
30
+ environment: opts.environment,
31
+ safeMode: opts.safeMode,
32
+ input,
33
+ // Mock mode propagates into the child: nothing real is ever touched
34
+ // in a mock run, including inside Subflow children. Scenario context
35
+ // never propagates (the parent didn't pick one for the child) — only
36
+ // the CHILD op's own op-level mocks apply, since nodeIds are scoped
37
+ // per-flow and reusing the parent's mock map would be meaningless
38
+ // (or worse, silently wrong) for the child's node ids.
39
+ mockRun: opts.mockRun,
40
+ mocks: childFlow.mocks ?? {},
41
+ trace: opts.trace,
42
+ subflowRunner: makeSubflowRunner(opts, [...ancestry, workflowId]),
43
+ events: opts.onLog
44
+ ? {
45
+ onLog: (line) => opts.onLog({
46
+ ...line,
47
+ nodeLabel: `${childFlow.name} › ${line.nodeLabel ?? ''}`,
48
+ nodeId: line.nodeId ? `${callerNodeId}/${line.nodeId}` : callerNodeId,
49
+ }),
50
+ }
51
+ : undefined,
52
+ });
53
+ const childRun = await childHandle.runToEnd();
54
+ if (childRun.status !== 'succeeded') {
55
+ // Surface the child's actual failure (e.g. the Mock-mode "would touch
56
+ // real infrastructure" boundary message) through the Subflow node's
57
+ // own error, rather than an opaque "failed" — the first failed
58
+ // node's `error`, if any, wins.
59
+ const failedNode = Object.values(childRun.nodeStates).find((s) => s.status === 'failed' && s.error);
60
+ const detail = failedNode?.error;
61
+ return {
62
+ status: 'failed',
63
+ error: `subflow "${childFlow.name}" ${childRun.status}${detail ? `: ${detail}` : ''}`,
64
+ };
65
+ }
66
+ return { status: 'succeeded', output: runOutput(childRun, childFlow) };
67
+ }
68
+ catch (err) {
69
+ return { status: 'failed', error: err instanceof Error ? err.message : String(err) };
70
+ }
71
+ };
72
+ }
@@ -0,0 +1,70 @@
1
+ import { type NodeRegistry } from '../src/engine/index.js';
2
+ import { type EnvironmentsFile } from './environments.js';
3
+ import type { ApiStore } from './apiStore.js';
4
+ /** Thrown for CLI usage errors (unknown opId / unknown environment) — the CLI
5
+ * layer turns this into exit code 2, distinct from an assertion failure
6
+ * (exit 1) or an unexpected error (also exit 1, via runCli's generic catch). */
7
+ export declare class ScenarioTestUsageError extends Error {
8
+ }
9
+ export type SkipReason = 'no expect' | 'empty expect';
10
+ export interface ScenarioResult {
11
+ opId: string;
12
+ scenario: string;
13
+ status: 'passed' | 'failed' | 'skipped';
14
+ /** Present only when status === 'failed' — evaluateExpectation's failure strings. */
15
+ failures?: string[];
16
+ /** Present only when status === 'skipped'. */
17
+ reason?: SkipReason;
18
+ }
19
+ export interface TestReport {
20
+ results: ScenarioResult[];
21
+ passed: number;
22
+ failed: number;
23
+ skipped: number;
24
+ }
25
+ /**
26
+ * Runs every scenario with `expect` across the given ops (or a single op when
27
+ * `opId` is given), entirely in-process: `startRun` is called directly per
28
+ * scenario, mirroring the options shape `RunRegistry.create` passes
29
+ * (server/runRegistry.ts) — but bypassing RunRegistry entirely, so these runs
30
+ * never enter it (no SSE, no run history).
31
+ *
32
+ * This is the shared seam both the CLI (`runScenarioSuite`, which bootstraps
33
+ * its own project/registries/apiStore from disk) and the studio route
34
+ * (`POST /workflows/:id/test` in server/index.ts, which reuses the
35
+ * already-booted server's apiStore/registry/environmentsFile — no re-read
36
+ * from disk, no drift from in-memory writes) call into. NO second expectation
37
+ * engine — both paths land here.
38
+ *
39
+ * Throws `ScenarioTestUsageError` for usage problems (unknown opId, unknown
40
+ * environment) — the CLI layer maps that to exit code 2; the route maps it to
41
+ * 400/404.
42
+ */
43
+ export declare function runScenarioSuiteFor(ctx: {
44
+ apiStore: ApiStore;
45
+ registry: NodeRegistry;
46
+ environmentsFile: EnvironmentsFile;
47
+ }, opts: {
48
+ opId?: string;
49
+ environmentName?: string;
50
+ mock?: boolean;
51
+ }): Promise<TestReport>;
52
+ /**
53
+ * CLI entry point: bootstraps a project from disk (loadProjectConfig/
54
+ * buildRegistries/buildApiStore/loadEnvironments — the same bootstrap
55
+ * `server/index.ts` uses) and delegates to `runScenarioSuiteFor`. NO runner
56
+ * process, no HTTP.
57
+ */
58
+ export declare function runScenarioSuite(opts: {
59
+ projectDir: string;
60
+ opId?: string;
61
+ environmentName?: string;
62
+ mock?: boolean;
63
+ }): Promise<TestReport>;
64
+ /** `✓ opId · scenario` / `✗ opId · scenario (failure; failure)` — undefined for
65
+ * skipped scenarios, which aren't printed as individual lines (only counted
66
+ * in the summary), keeping default output focused on assertable scenarios. */
67
+ export declare function formatScenarioLine(r: ScenarioResult): string | undefined;
68
+ /** `12 passed, 1 failed, 3 skipped (no expect)` — the parenthetical lists the
69
+ * distinct skip reasons present (omitted entirely when nothing was skipped). */
70
+ export declare function formatSummary(report: TestReport): string;
@@ -0,0 +1,151 @@
1
+ import { mergeMocks, startRun, } from '../src/engine/index.js';
2
+ import { loadProjectConfig } from './projectConfig.js';
3
+ import { buildApiStore, buildRegistries, requireProjectWhenExplicit } from './projectMode.js';
4
+ import { loadEnvironments, resolveRunSafety } from './environments.js';
5
+ import { evaluateExpectation } from './scenarioTest.js';
6
+ import { makeSubflowRunner } from './subflowRunner.js';
7
+ import { redactSecrets } from './redact.js';
8
+ /** Thrown for CLI usage errors (unknown opId / unknown environment) — the CLI
9
+ * layer turns this into exit code 2, distinct from an assertion failure
10
+ * (exit 1) or an unexpected error (also exit 1, via runCli's generic catch). */
11
+ export class ScenarioTestUsageError extends Error {
12
+ }
13
+ async function runScenario(op, scenario, apiStore, registry, env, mock) {
14
+ if (!scenario.expect) {
15
+ return { opId: op.id, scenario: scenario.name, status: 'skipped', reason: 'no expect' };
16
+ }
17
+ if (Object.keys(scenario.expect).length === 0) {
18
+ return { opId: op.id, scenario: scenario.name, status: 'skipped', reason: 'empty expect' };
19
+ }
20
+ // INTENTIONAL (controller decision): NO environment auth auto-attach here,
21
+ // unlike POST /runs (server/index.ts attachCredential). Tests are hermetic —
22
+ // auto-attaching the environment's credential would silently satisfy the very
23
+ // auth checks a deliberate 401-expectation scenario exists to exercise. If a
24
+ // scenario needs auth, embed the headers directly in `scenario.input`.
25
+ //
26
+ // --mock: same merge as the live serving mock-run path (mergeMocks,
27
+ // src/engine/scenarios.ts) — op-level `flow.mocks` overlaid by this
28
+ // scenario's `mocks`, scenario wins per nodeId. Nothing real is ever
29
+ // touched: the executor's mockRun short-circuit fails loudly on any
30
+ // infrastructure node (traceKind db/http/llm) that has no mock.
31
+ const handle = startRun({
32
+ flow: op,
33
+ registry,
34
+ secrets: env.secrets,
35
+ vars: env.vars,
36
+ environment: env.environment,
37
+ safeMode: env.safeMode,
38
+ input: scenario.input,
39
+ mockRun: mock,
40
+ mocks: mock ? mergeMocks(op, scenario) : undefined,
41
+ // Shared factory (server/subflowRunner.ts) — same depth cap/cycle guard as
42
+ // the live runner, minus the SSE trace/log plumbing a headless run skips.
43
+ subflowRunner: makeSubflowRunner({
44
+ loadFlow: (id) => apiStore.load(id),
45
+ registry,
46
+ secrets: env.secrets,
47
+ vars: env.vars,
48
+ environment: env.environment,
49
+ safeMode: env.safeMode,
50
+ mockRun: mock,
51
+ }, [op.id]),
52
+ });
53
+ const run = await handle.runToEnd();
54
+ const { ok, failures } = evaluateExpectation(run, op, scenario.expect);
55
+ return {
56
+ opId: op.id,
57
+ scenario: scenario.name,
58
+ status: ok ? 'passed' : 'failed',
59
+ ...(ok ? {} : { failures }),
60
+ };
61
+ }
62
+ /**
63
+ * Runs every scenario with `expect` across the given ops (or a single op when
64
+ * `opId` is given), entirely in-process: `startRun` is called directly per
65
+ * scenario, mirroring the options shape `RunRegistry.create` passes
66
+ * (server/runRegistry.ts) — but bypassing RunRegistry entirely, so these runs
67
+ * never enter it (no SSE, no run history).
68
+ *
69
+ * This is the shared seam both the CLI (`runScenarioSuite`, which bootstraps
70
+ * its own project/registries/apiStore from disk) and the studio route
71
+ * (`POST /workflows/:id/test` in server/index.ts, which reuses the
72
+ * already-booted server's apiStore/registry/environmentsFile — no re-read
73
+ * from disk, no drift from in-memory writes) call into. NO second expectation
74
+ * engine — both paths land here.
75
+ *
76
+ * Throws `ScenarioTestUsageError` for usage problems (unknown opId, unknown
77
+ * environment) — the CLI layer maps that to exit code 2; the route maps it to
78
+ * 400/404.
79
+ */
80
+ export async function runScenarioSuiteFor(ctx, opts) {
81
+ const { apiStore, registry, environmentsFile } = ctx;
82
+ const { opId, environmentName, mock = false } = opts;
83
+ const envName = environmentName ?? environmentsFile.defaultEnvironment;
84
+ const envDef = environmentsFile.environments[envName];
85
+ if (!envDef) {
86
+ throw new ScenarioTestUsageError(`Unknown environment: '${envName}'`);
87
+ }
88
+ const safety = resolveRunSafety(envName, envDef, {});
89
+ const runEnv = {
90
+ secrets: envDef.secrets,
91
+ vars: envDef.vars,
92
+ environment: envName,
93
+ safeMode: safety.ok ? safety.safeMode : false,
94
+ };
95
+ const allOps = apiStore.list();
96
+ let targetOps = allOps;
97
+ if (opId !== undefined) {
98
+ const op = allOps.find((o) => o.id === opId);
99
+ if (!op)
100
+ throw new ScenarioTestUsageError(`Unknown operation: '${opId}'`);
101
+ targetOps = [op];
102
+ }
103
+ const rawResults = [];
104
+ for (const op of targetOps) {
105
+ for (const scenario of op.scenarios ?? []) {
106
+ rawResults.push(await runScenario(op, scenario, apiStore, registry, runEnv, mock));
107
+ }
108
+ }
109
+ // evaluateExpectation diffs RAW run state, so a failure string can quote a
110
+ // secret value the flow echoed into its response body. Redact the whole
111
+ // reporter payload (value-based; raw/url-encoded/base64 forms) before it can
112
+ // reach stdout/CI logs — same redactSecrets the SSE event path uses.
113
+ const results = redactSecrets(rawResults, runEnv.secrets);
114
+ return {
115
+ results,
116
+ passed: results.filter((r) => r.status === 'passed').length,
117
+ failed: results.filter((r) => r.status === 'failed').length,
118
+ skipped: results.filter((r) => r.status === 'skipped').length,
119
+ };
120
+ }
121
+ /**
122
+ * CLI entry point: bootstraps a project from disk (loadProjectConfig/
123
+ * buildRegistries/buildApiStore/loadEnvironments — the same bootstrap
124
+ * `server/index.ts` uses) and delegates to `runScenarioSuiteFor`. NO runner
125
+ * process, no HTTP.
126
+ */
127
+ export async function runScenarioSuite(opts) {
128
+ const { projectDir, opId, environmentName, mock } = opts;
129
+ const project = requireProjectWhenExplicit(await loadProjectConfig(projectDir), process.env.EMBERFLOW_PROJECT, projectDir);
130
+ const environmentsFile = loadEnvironments(project ? project.root : projectDir);
131
+ const { execution } = buildRegistries(project);
132
+ const apiStore = buildApiStore(project);
133
+ return runScenarioSuiteFor({ apiStore, registry: execution, environmentsFile }, { opId, environmentName, mock });
134
+ }
135
+ /** `✓ opId · scenario` / `✗ opId · scenario (failure; failure)` — undefined for
136
+ * skipped scenarios, which aren't printed as individual lines (only counted
137
+ * in the summary), keeping default output focused on assertable scenarios. */
138
+ export function formatScenarioLine(r) {
139
+ if (r.status === 'skipped')
140
+ return undefined;
141
+ if (r.status === 'passed')
142
+ return `✓ ${r.opId} · ${r.scenario}`;
143
+ return `✗ ${r.opId} · ${r.scenario} (${(r.failures ?? []).join('; ')})`;
144
+ }
145
+ /** `12 passed, 1 failed, 3 skipped (no expect)` — the parenthetical lists the
146
+ * distinct skip reasons present (omitted entirely when nothing was skipped). */
147
+ export function formatSummary(report) {
148
+ const reasons = [...new Set(report.results.filter((r) => r.status === 'skipped').map((r) => r.reason))];
149
+ const suffix = report.skipped > 0 ? ` (${reasons.join(', ')})` : '';
150
+ return `${report.passed} passed, ${report.failed} failed, ${report.skipped} skipped${suffix}`;
151
+ }
@@ -0,0 +1,3 @@
1
+ export { defineConfig } from '../server/projectConfig.js';
2
+ export type { EmberflowUserConfig } from '../server/projectConfig.js';
3
+ export type { NodeRegistry, NodeDefinition, NodeExecutionContext } from './engine/index.js';
@@ -0,0 +1,2 @@
1
+ // src/config.ts — the `emberflow` package root: what a consumer's emberflow.config imports.
2
+ export { defineConfig } from '../server/projectConfig.js';
@@ -0,0 +1,33 @@
1
+ import type { AuthPolicy } from './types.js';
2
+ export interface VerifyResult {
3
+ user: unknown;
4
+ }
5
+ /** Given the request + the resolved secret value, return a user or throw HttpError(401). */
6
+ export type Verifier = (args: {
7
+ request: {
8
+ headers: Record<string, unknown>;
9
+ };
10
+ policy: AuthPolicy;
11
+ secret: string | undefined;
12
+ }) => VerifyResult;
13
+ export declare const bearerVerifier: Verifier;
14
+ export declare const apiKeyVerifier: Verifier;
15
+ export declare class VerifierRegistry {
16
+ private map;
17
+ register(name: string, v: Verifier): void;
18
+ get(name: string): Verifier | undefined;
19
+ }
20
+ export declare function createDefaultVerifierRegistry(): VerifierRegistry;
21
+ /**
22
+ * Picks the verifier (policy.verify ? verifiers.get(policy.verify) : verifiers.get(policy.scheme)),
23
+ * fails closed with HttpError(500) if it's missing, resolves the secret from
24
+ * `secrets[policy.secretRef]`, and runs the verifier (its HttpError, if any, propagates).
25
+ */
26
+ export declare function enforceAuth(args: {
27
+ policy: AuthPolicy;
28
+ request: {
29
+ headers: Record<string, unknown>;
30
+ };
31
+ secrets: Record<string, unknown>;
32
+ verifiers: VerifierRegistry;
33
+ }): VerifyResult;
@@ -0,0 +1,63 @@
1
+ // Browser-safe auth verification core. Lives here (not under server/auth/)
2
+ // because src/nodes/requireAuth.ts (bundled into the studio browser build via
3
+ // src/store/builderStore.ts -> src/nodes) must not import from server/. The
4
+ // server-side enforce.ts + verifiers.ts re-export/wrap this module so there's
5
+ // a single implementation instead of two diverging copies.
6
+ import { HttpError } from './httpError.js';
7
+ const bearerToken = (headers) => {
8
+ const raw = headers['authorization'] ?? headers['Authorization'];
9
+ if (typeof raw !== 'string')
10
+ return undefined;
11
+ const m = /^Bearer\s+(.+)$/i.exec(raw);
12
+ return m ? m[1] : undefined;
13
+ };
14
+ // Note: a plain string comparison is used here rather than a timing-safe
15
+ // comparison; timing-safe comparison is a later hardening item.
16
+ export const bearerVerifier = ({ request, secret }) => {
17
+ if (secret === undefined)
18
+ throw new HttpError(500, { error: 'auth misconfigured: secret not set' });
19
+ const token = bearerToken(request.headers);
20
+ if (!token || token !== secret)
21
+ throw new HttpError(401, { error: 'unauthorized' });
22
+ return { user: { scheme: 'bearer' } };
23
+ };
24
+ export const apiKeyVerifier = ({ request, policy, secret }) => {
25
+ if (secret === undefined)
26
+ throw new HttpError(500, { error: 'auth misconfigured: secret not set' });
27
+ const headerName = (policy.header ?? 'x-api-key').toLowerCase();
28
+ const provided = request.headers[headerName];
29
+ if (typeof provided !== 'string' || provided !== secret)
30
+ throw new HttpError(401, { error: 'unauthorized' });
31
+ return { user: { scheme: 'apiKey' } };
32
+ };
33
+ export class VerifierRegistry {
34
+ map = new Map();
35
+ register(name, v) {
36
+ this.map.set(name, v);
37
+ }
38
+ get(name) {
39
+ return this.map.get(name);
40
+ }
41
+ }
42
+ export function createDefaultVerifierRegistry() {
43
+ const reg = new VerifierRegistry();
44
+ reg.register('bearer', bearerVerifier);
45
+ reg.register('apiKey', apiKeyVerifier);
46
+ return reg;
47
+ }
48
+ /**
49
+ * Picks the verifier (policy.verify ? verifiers.get(policy.verify) : verifiers.get(policy.scheme)),
50
+ * fails closed with HttpError(500) if it's missing, resolves the secret from
51
+ * `secrets[policy.secretRef]`, and runs the verifier (its HttpError, if any, propagates).
52
+ */
53
+ export function enforceAuth(args) {
54
+ const { policy, request, secrets, verifiers } = args;
55
+ const verifierName = policy.verify ?? policy.scheme;
56
+ const verifier = policy.verify ? verifiers.get(policy.verify) : verifiers.get(policy.scheme);
57
+ if (!verifier) {
58
+ throw new HttpError(500, { error: `auth misconfigured: no verifier ${verifierName}` });
59
+ }
60
+ const rawSecret = secrets[policy.secretRef];
61
+ const secret = typeof rawSecret === 'string' ? rawSecret : undefined;
62
+ return verifier({ request, policy, secret });
63
+ }
@@ -0,0 +1,62 @@
1
+ import type { ScenarioDefinition, WorkflowDefinition } from './types.js';
2
+ export interface OperationDiagnostic {
3
+ severity: 'error' | 'warning' | 'info';
4
+ code: string;
5
+ message: string;
6
+ nodeId?: string;
7
+ param?: string;
8
+ }
9
+ /** Registry-derived facts diagnoseOperation cannot compute on its own — it is
10
+ * a pure function over (flow, scenarios) and has no node-definition lookup.
11
+ * Callers that hold a registry (server/index.ts's diagnostics route, the
12
+ * `doctor` CLI, the studio's client-side registry) derive `infraNodes` by
13
+ * looking up each flow node's type and keeping the ones whose `traceKind` is
14
+ * 'db' | 'http' | 'llm'. Callers without a registry (or without meta yet)
15
+ * omit `extras` entirely — `missing-node-mock` simply doesn't fire. */
16
+ export interface DiagnoseOperationExtras {
17
+ infraNodes?: Array<{
18
+ id: string;
19
+ traceKind: string;
20
+ }>;
21
+ /** Op-level mocks to check coverage against, when the caller has a more
22
+ * authoritative source than `flow.mocks` (e.g. an in-progress edit not yet
23
+ * saved). Falls back to `flow.mocks` when omitted. */
24
+ opMocks?: Record<string, unknown>;
25
+ /** Declared output field names per flow-node id, for `inputmap-schema-mismatch`.
26
+ * Callers that hold a registry populate this ONLY for nodes whose type is
27
+ * registered AND whose outputSchema declares fields (an entry's presence is
28
+ * the "checkable" signal). Nodes absent from the map — unregistered types, or
29
+ * registered ones with an empty/absent outputSchema (Input, Subflow, …) — are
30
+ * never flagged, so dynamic-output nodes cannot false-positive. Omitted
31
+ * entirely → the diagnostic simply doesn't fire. */
32
+ outputFieldsByNode?: Record<string, string[]>;
33
+ /** Implementation source per flow-node id, for `simulated-commit`. Callers
34
+ * that hold a registry populate this ONLY for nodes whose type is
35
+ * registered AND whose registered `definition.effects === 'mutation'` AND
36
+ * whose implementation is a function (`String(implementation)`). A node
37
+ * absent from the map — unregistered type, non-mutation node, or a native/
38
+ * bound implementation whose source isn't meaningfully inspectable — is
39
+ * never flagged. Omitted entirely → the diagnostic simply doesn't fire. */
40
+ mutationSourcesByNode?: Record<string, string>;
41
+ /** Project-level signal for `language-drift`, NOT per-node. Pass this ONLY
42
+ * when the project config's `language:` field was EXPLICIT and disagrees
43
+ * with the config file's own extension (e.g. `language: 'javascript'` in
44
+ * an `emberflow.config.ts`) — inference makes language and extension agree
45
+ * by construction, so that's the only case worth flagging. Callers that
46
+ * hold a loaded ProjectConfig (server/index.ts's diagnostics route, the
47
+ * `doctor` CLI) plumb this from `ProjectConfig.languageDrift`. The studio
48
+ * doesn't currently have project-language access client-side, so it omits
49
+ * this and the diagnostic simply doesn't fire there. */
50
+ languageDrift?: {
51
+ projectLanguage: 'javascript' | 'typescript';
52
+ configPathExtension: string;
53
+ };
54
+ }
55
+ /** Diagnose an operation (a flow with an `http` trigger, plus its scenarios)
56
+ * for issues a studio user or the `emberflow doctor` CLI should surface
57
+ * before relying on the operation. Deterministic order: all
58
+ * `missing-param-default` (path order), then all `param-no-real-scenario`
59
+ * (path order), then `no-expects`, then `missing-node-mock` (extras order),
60
+ * then `inputmap-schema-mismatch` (node order), then `simulated-commit`
61
+ * (node order), then `language-drift` (at most one, project-level). */
62
+ export declare function diagnoseOperation(flow: WorkflowDefinition, scenarios: ScenarioDefinition[] | undefined, extras?: DiagnoseOperationExtras): OperationDiagnostic[];