@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,167 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { join } from 'node:path';
3
+ import {
4
+ configBasenameFor,
5
+ decideRuntime,
6
+ resolveProjectDir,
7
+ TSX_MISSING_MESSAGE,
8
+ } from './runtime.mjs';
9
+
10
+ // A fake filesystem: `decideRuntime`/`configBasenameFor` take an injectable
11
+ // `fsExists`, so the whole decision matrix runs without touching disk.
12
+ function fsWith(paths: string[]) {
13
+ const set = new Set(paths);
14
+ return (p: import('node:fs').PathLike) => set.has(String(p));
15
+ }
16
+
17
+ // An installed JS consumer: package under node_modules, dist present, no src.
18
+ const CONSUMER_ROOT = '/app/node_modules/emberflow';
19
+ const PROJECT = '/app';
20
+
21
+ function consumerFs(configBasename?: string) {
22
+ const paths = [join(CONSUMER_ROOT, 'dist')];
23
+ if (configBasename) paths.push(join(PROJECT, configBasename));
24
+ return fsWith(paths);
25
+ }
26
+
27
+ describe('configBasenameFor', () => {
28
+ it('finds .mjs before .ts', () => {
29
+ const fsExists = fsWith([join(PROJECT, 'emberflow.config.mjs')]);
30
+ expect(configBasenameFor(PROJECT, fsExists)).toBe('emberflow.config.mjs');
31
+ });
32
+ it('finds a .ts config', () => {
33
+ const fsExists = fsWith([join(PROJECT, 'emberflow.config.ts')]);
34
+ expect(configBasenameFor(PROJECT, fsExists)).toBe('emberflow.config.ts');
35
+ });
36
+ it('returns undefined when no config exists', () => {
37
+ expect(configBasenameFor(PROJECT, () => false)).toBeUndefined();
38
+ });
39
+ });
40
+
41
+ describe('resolveProjectDir', () => {
42
+ it('prefers EMBERFLOW_PROJECT (absolute) over --project and cwd', () => {
43
+ expect(
44
+ resolveProjectDir(['--project', './other'], { EMBERFLOW_PROJECT: '/env/proj' }, '/cwd')
45
+ ).toBe('/env/proj');
46
+ });
47
+ it('resolves a relative EMBERFLOW_PROJECT against cwd', () => {
48
+ expect(resolveProjectDir([], { EMBERFLOW_PROJECT: 'proj' }, '/cwd')).toBe('/cwd/proj');
49
+ });
50
+ it('falls back to --project when no env var', () => {
51
+ expect(resolveProjectDir(['--project', 'p'], {}, '/cwd')).toBe('/cwd/p');
52
+ });
53
+ it('falls back to cwd when neither is set', () => {
54
+ expect(resolveProjectDir([], {}, '/cwd')).toBe('/cwd');
55
+ });
56
+ });
57
+
58
+ describe('decideRuntime — JS consumer (installed under node_modules, dist present)', () => {
59
+ it('.mjs config → plain node, use dist, no tsx', () => {
60
+ const d = decideRuntime({
61
+ packageRoot: CONSUMER_ROOT,
62
+ projectDir: PROJECT,
63
+ env: {},
64
+ fsExists: consumerFs('emberflow.config.mjs'),
65
+ });
66
+ expect(d).toMatchObject({ needsTsx: false, runnerMode: 'node', useDist: true, sourceMode: false });
67
+ });
68
+
69
+ it('no config → still plain node + dist (e.g. `init`)', () => {
70
+ const d = decideRuntime({
71
+ packageRoot: CONSUMER_ROOT,
72
+ projectDir: PROJECT,
73
+ env: {},
74
+ fsExists: consumerFs(),
75
+ });
76
+ expect(d).toMatchObject({ needsTsx: false, runnerMode: 'node', useDist: true });
77
+ });
78
+
79
+ it('.ts config → needs tsx, still imports dist commands', () => {
80
+ const d = decideRuntime({
81
+ packageRoot: CONSUMER_ROOT,
82
+ projectDir: PROJECT,
83
+ env: {},
84
+ fsExists: consumerFs('emberflow.config.ts'),
85
+ });
86
+ expect(d).toMatchObject({ needsTsx: true, runnerMode: 'tsx', useDist: true });
87
+ });
88
+
89
+ it('EMBERFLOW_FORCE_TSX=1 forces tsx even with a .mjs config', () => {
90
+ const d = decideRuntime({
91
+ packageRoot: CONSUMER_ROOT,
92
+ projectDir: PROJECT,
93
+ env: { EMBERFLOW_FORCE_TSX: '1' },
94
+ fsExists: consumerFs('emberflow.config.mjs'),
95
+ });
96
+ expect(d).toMatchObject({ needsTsx: true, runnerMode: 'tsx' });
97
+ });
98
+
99
+ it('no dist installed → falls back to tsx AND requires it (importing .ts source without tsx would die with ERR_UNKNOWN_FILE_EXTENSION)', () => {
100
+ const d = decideRuntime({
101
+ packageRoot: CONSUMER_ROOT,
102
+ projectDir: PROJECT,
103
+ env: {},
104
+ fsExists: fsWith([join(PROJECT, 'emberflow.config.mjs')]), // no dist
105
+ });
106
+ expect(d).toMatchObject({ needsTsx: true, runnerMode: 'tsx', useDist: false });
107
+ });
108
+ });
109
+
110
+ describe('decideRuntime — source checkout (repo dev loop)', () => {
111
+ const REPO_ROOT = '/work/emberflow';
112
+ // src/ present, NOT under node_modules — the checkout signal. dist/ may or may
113
+ // not exist (after build:lib); source-mode must ignore it.
114
+ function repoFs(withDist: boolean, configBasename?: string) {
115
+ const paths = [join(REPO_ROOT, 'src')];
116
+ if (withDist) paths.push(join(REPO_ROOT, 'dist'));
117
+ if (configBasename) paths.push(join(PROJECT, configBasename));
118
+ return fsWith(paths);
119
+ }
120
+
121
+ it('always tsx + source commands, even with a .mjs project config', () => {
122
+ const d = decideRuntime({
123
+ packageRoot: REPO_ROOT,
124
+ projectDir: PROJECT,
125
+ env: {},
126
+ fsExists: repoFs(false, 'emberflow.config.mjs'),
127
+ });
128
+ expect(d).toMatchObject({ needsTsx: true, runnerMode: 'tsx', useDist: false, sourceMode: true });
129
+ });
130
+
131
+ it('dist present in the repo does NOT switch to node/dist', () => {
132
+ const d = decideRuntime({
133
+ packageRoot: REPO_ROOT,
134
+ projectDir: PROJECT,
135
+ env: {},
136
+ fsExists: repoFs(true, 'emberflow.config.mjs'),
137
+ });
138
+ expect(d).toMatchObject({ sourceMode: true, useDist: false, needsTsx: true });
139
+ });
140
+
141
+ it('EMBERFLOW_SOURCE=0 escape hatch opts a checkout out of source mode', () => {
142
+ const d = decideRuntime({
143
+ packageRoot: REPO_ROOT,
144
+ projectDir: PROJECT,
145
+ env: { EMBERFLOW_SOURCE: '0' },
146
+ fsExists: repoFs(true, 'emberflow.config.mjs'),
147
+ });
148
+ expect(d).toMatchObject({ sourceMode: false, runnerMode: 'node', useDist: true });
149
+ });
150
+
151
+ it('EMBERFLOW_SOURCE=1 forces source mode for an installed package', () => {
152
+ const d = decideRuntime({
153
+ packageRoot: CONSUMER_ROOT,
154
+ projectDir: PROJECT,
155
+ env: { EMBERFLOW_SOURCE: '1' },
156
+ fsExists: consumerFs('emberflow.config.mjs'),
157
+ });
158
+ expect(d).toMatchObject({ sourceMode: true, needsTsx: true, useDist: false });
159
+ });
160
+ });
161
+
162
+ describe('TSX_MISSING_MESSAGE', () => {
163
+ it('is actionable: names the install command and the JS-config alternative', () => {
164
+ expect(TSX_MISSING_MESSAGE).toContain('npm i -D tsx');
165
+ expect(TSX_MISSING_MESSAGE).toMatch(/emberflow\.config\.mjs/);
166
+ });
167
+ });
@@ -0,0 +1,108 @@
1
+ // bin/skillTargets.test.ts
2
+ import { mkdtempSync, mkdirSync, rmSync } from 'node:fs';
3
+ import { tmpdir } from 'node:os';
4
+ import { join } from 'node:path';
5
+ import { afterEach, describe, expect, it } from 'vitest';
6
+ import { detectHarnesses, resolveSkillDirs } from './skillTargets';
7
+
8
+ const dirs: string[] = [];
9
+ function scratch(): string {
10
+ const d = mkdtempSync(join(tmpdir(), 'ef-skilltargets-'));
11
+ dirs.push(d);
12
+ return d;
13
+ }
14
+ afterEach(() => {
15
+ for (const d of dirs.splice(0)) rmSync(d, { recursive: true, force: true });
16
+ });
17
+
18
+ describe('detectHarnesses', () => {
19
+ it('detects neither present when no markers exist', () => {
20
+ const cwd = scratch();
21
+ const home = scratch();
22
+ expect(detectHarnesses(cwd, home)).toEqual({ claude: false, codex: false });
23
+ });
24
+
25
+ it('detects claude present via repo dir', () => {
26
+ const cwd = scratch();
27
+ const home = scratch();
28
+ mkdirSync(join(cwd, '.claude'));
29
+ expect(detectHarnesses(cwd, home)).toEqual({ claude: true, codex: false });
30
+ });
31
+
32
+ it('detects claude present via home dir', () => {
33
+ const cwd = scratch();
34
+ const home = scratch();
35
+ mkdirSync(join(home, '.claude'));
36
+ expect(detectHarnesses(cwd, home)).toEqual({ claude: true, codex: false });
37
+ });
38
+
39
+ it('detects codex present via repo dir', () => {
40
+ const cwd = scratch();
41
+ const home = scratch();
42
+ mkdirSync(join(cwd, '.codex'));
43
+ expect(detectHarnesses(cwd, home)).toEqual({ claude: false, codex: true });
44
+ });
45
+
46
+ it('detects codex present via home dir', () => {
47
+ const cwd = scratch();
48
+ const home = scratch();
49
+ mkdirSync(join(home, '.codex'));
50
+ expect(detectHarnesses(cwd, home)).toEqual({ claude: false, codex: true });
51
+ });
52
+
53
+ it('detects both present', () => {
54
+ const cwd = scratch();
55
+ const home = scratch();
56
+ mkdirSync(join(cwd, '.claude'));
57
+ mkdirSync(join(home, '.codex'));
58
+ expect(detectHarnesses(cwd, home)).toEqual({ claude: true, codex: true });
59
+ });
60
+ });
61
+
62
+ describe('resolveSkillDirs', () => {
63
+ it('picks repo dirs for present harnesses at repo scope', () => {
64
+ const cwd = scratch();
65
+ const home = scratch();
66
+ const dirs = resolveSkillDirs({ claude: true, codex: true }, 'repo', cwd, home);
67
+ expect(dirs.sort()).toEqual(
68
+ [join(cwd, '.claude', 'skills'), join(cwd, '.codex', 'skills')].sort()
69
+ );
70
+ });
71
+
72
+ it('picks home dirs for present harnesses at global scope', () => {
73
+ const cwd = scratch();
74
+ const home = scratch();
75
+ const dirs = resolveSkillDirs({ claude: true, codex: true }, 'global', cwd, home);
76
+ expect(dirs.sort()).toEqual(
77
+ [join(home, '.claude', 'skills'), join(home, '.codex', 'skills')].sort()
78
+ );
79
+ });
80
+
81
+ it('returns only the present harness dir when just one is present (repo scope)', () => {
82
+ const cwd = scratch();
83
+ const home = scratch();
84
+ const dirs = resolveSkillDirs({ claude: true, codex: false }, 'repo', cwd, home);
85
+ expect(dirs).toEqual([join(cwd, '.claude', 'skills')]);
86
+ });
87
+
88
+ it('returns only the present harness dir when just one is present (global scope)', () => {
89
+ const cwd = scratch();
90
+ const home = scratch();
91
+ const dirs = resolveSkillDirs({ claude: false, codex: true }, 'global', cwd, home);
92
+ expect(dirs).toEqual([join(home, '.codex', 'skills')]);
93
+ });
94
+
95
+ it('falls back to claude at repo scope when neither harness is present', () => {
96
+ const cwd = scratch();
97
+ const home = scratch();
98
+ const dirs = resolveSkillDirs({ claude: false, codex: false }, 'repo', cwd, home);
99
+ expect(dirs).toEqual([join(cwd, '.claude', 'skills')]);
100
+ });
101
+
102
+ it('falls back to claude at global scope when neither harness is present', () => {
103
+ const cwd = scratch();
104
+ const home = scratch();
105
+ const dirs = resolveSkillDirs({ claude: false, codex: false }, 'global', cwd, home);
106
+ expect(dirs).toEqual([join(home, '.claude', 'skills')]);
107
+ });
108
+ });
@@ -0,0 +1,36 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+
4
+ export type Harness = 'claude' | 'codex';
5
+ export interface HarnessPresence {
6
+ claude: boolean;
7
+ codex: boolean;
8
+ }
9
+
10
+ const HARNESSES: Harness[] = ['claude', 'codex'];
11
+
12
+ /** Detect which harnesses the project/user uses: a harness counts as present if
13
+ * its repo dir (<cwd>/.claude or /.codex) OR its home dir (<home>/.claude or /.codex) exists. */
14
+ export function detectHarnesses(cwd: string, home: string): HarnessPresence {
15
+ const presence = {} as HarnessPresence;
16
+ for (const h of HARNESSES) {
17
+ presence[h] = existsSync(join(cwd, `.${h}`)) || existsSync(join(home, `.${h}`));
18
+ }
19
+ return presence;
20
+ }
21
+
22
+ /** The skills destination dirs to install into. For each PRESENT harness, choose
23
+ * the repo root (<cwd>/.<harness>/skills) when scope='repo', else the home root
24
+ * (<home>/.<harness>/skills). If NEITHER harness is present, default to Claude Code
25
+ * at the chosen scope (so a fresh project still gets skills). Returns absolute dirs. */
26
+ export function resolveSkillDirs(
27
+ presence: HarnessPresence,
28
+ scope: 'repo' | 'global',
29
+ cwd: string,
30
+ home: string
31
+ ): string[] {
32
+ const root = scope === 'repo' ? cwd : home;
33
+ const present = HARNESSES.filter((h) => presence[h]);
34
+ const targets = present.length > 0 ? present : (['claude'] as Harness[]);
35
+ return targets.map((h) => join(root, `.${h}`, 'skills'));
36
+ }
@@ -0,0 +1,37 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { describe, expect, it } from 'vitest';
4
+
5
+ const skill = (name: string): string =>
6
+ readFileSync(join('templates', 'skills', name, 'SKILL.md'), 'utf8');
7
+
8
+ describe('Emberflow skill templates', () => {
9
+ it('prevents scenario-only verification from hiding missing Input defaults', () => {
10
+ const basics = skill('emberflow-basics');
11
+ const newWorkflow = skill('emberflow-new-workflow');
12
+ const modelProcess = skill('emberflow-model-process');
13
+ const review = skill('emberflow-review-workflow');
14
+
15
+ expect(basics).toContain('Plain Run');
16
+ expect(newWorkflow).toContain('Run both: plain Run and scenarios');
17
+ expect(modelProcess).toContain('Run both: plain Run and every scenario');
18
+ expect(review).toContain('Plain Run / default input check');
19
+ });
20
+
21
+ it('documents the committed infrastructure manifest and points the intake skills at it first', () => {
22
+ const basics = skill('emberflow-basics');
23
+ const newWorkflow = skill('emberflow-new-workflow');
24
+ const modelProcess = skill('emberflow-model-process');
25
+
26
+ // Basics: file-layout entry — committed, structure-only, written by the scout.
27
+ expect(basics).toContain('emberflow/infrastructure.json');
28
+ expect(basics).toMatch(/committed.*structure-only|structure-only.*committed/i);
29
+ expect(basics).toMatch(/scout/i);
30
+
31
+ // Intake sections: check the manifest FIRST before interviewing about infra.
32
+ for (const skillText of [newWorkflow, modelProcess]) {
33
+ expect(skillText).toContain('emberflow/infrastructure.json');
34
+ expect(skillText).toMatch(/FIRST/);
35
+ }
36
+ });
37
+ });
@@ -0,0 +1,23 @@
1
+ {
2
+ "compilerOptions": {
3
+ "tsBuildInfoFile": "../node_modules/.tmp/tsconfig.bin.tsbuildinfo",
4
+ "target": "es2023",
5
+ "lib": ["ES2023", "DOM"],
6
+ "types": ["node"],
7
+ "skipLibCheck": true,
8
+ "module": "esnext",
9
+ "moduleResolution": "bundler",
10
+ "allowImportingTsExtensions": true,
11
+ "verbatimModuleSyntax": true,
12
+ "moduleDetection": "force",
13
+ "noEmit": true,
14
+ "strict": true,
15
+ "noUnusedLocals": true,
16
+ "noUnusedParameters": true,
17
+ "erasableSyntaxOnly": true,
18
+ "noFallthroughCasesInSwitch": true,
19
+ "allowJs": true
20
+ },
21
+ "include": ["."],
22
+ "exclude": ["emberflow.mjs"]
23
+ }
@@ -0,0 +1,24 @@
1
+ export interface ParsedArgs {
2
+ command: string;
3
+ port?: number;
4
+ project?: string;
5
+ scenario?: string;
6
+ noSkills?: boolean;
7
+ scope?: 'global' | 'repo';
8
+ noLaunch?: boolean;
9
+ mock?: boolean;
10
+ js?: boolean;
11
+ ts?: boolean;
12
+ rest: string[];
13
+ }
14
+ /** How the bin decided to run — passed down from bin/emberflow.mjs (see
15
+ * bin/runtime.mjs). `runnerMode` selects `node dist/server/*.js` vs a tsx spawn;
16
+ * `packageRoot` is the real package root (bin/emberflow.mjs is never in dist, so
17
+ * it always resolves this correctly — unlike this module's own location, which
18
+ * is dist/bin/commands.js when compiled). */
19
+ export interface RuntimeContext {
20
+ runnerMode: 'node' | 'tsx';
21
+ packageRoot: string;
22
+ }
23
+ export declare function parseArgs(argv: string[]): ParsedArgs;
24
+ export declare function runCommand(p: ParsedArgs, ctx?: RuntimeContext): Promise<number>;
@@ -0,0 +1,252 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { existsSync } from 'node:fs';
3
+ import { homedir } from 'node:os';
4
+ import { createInterface } from 'node:readline';
5
+ import { fileURLToPath } from 'node:url';
6
+ import { dirname, join, resolve } from 'node:path';
7
+ import { decideRuntime, resolveProjectDir } from './runtime.mjs';
8
+ export function parseArgs(argv) {
9
+ const [command = 'help', ...tail] = argv;
10
+ const out = { command, rest: [] };
11
+ for (let i = 0; i < tail.length; i++) {
12
+ const a = tail[i];
13
+ if (a === '--port')
14
+ out.port = Number(tail[++i]);
15
+ else if (a === '--project')
16
+ out.project = tail[++i];
17
+ else if (a === '--scenario')
18
+ out.scenario = tail[++i];
19
+ else if (a === '--no-skills')
20
+ out.noSkills = true;
21
+ else if (a === '--global')
22
+ out.scope = 'global';
23
+ else if (a === '--local')
24
+ out.scope = 'repo';
25
+ else if (a === '--no-launch')
26
+ out.noLaunch = true;
27
+ else if (a === '--mock')
28
+ out.mock = true;
29
+ else if (a === '--js')
30
+ out.js = true;
31
+ else if (a === '--ts')
32
+ out.ts = true;
33
+ else
34
+ out.rest.push(a);
35
+ }
36
+ return out;
37
+ }
38
+ /** Ask the user whether to install skills globally or per-repo. Guarded on TTY so
39
+ * it never hangs a non-interactive run (tests, CI, piped input). */
40
+ function promptScope() {
41
+ if (!process.stdin.isTTY)
42
+ return Promise.resolve('repo');
43
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
44
+ return new Promise((res) => {
45
+ rl.question('Install Emberflow skills for [g]lobal (all projects) or [r]epo (this project)? [r] ', (answer) => {
46
+ rl.close();
47
+ res(answer.trim().toLowerCase().startsWith('g') ? 'global' : 'repo');
48
+ });
49
+ });
50
+ }
51
+ /** Ask the user which language to author their APIs/nodes in. Same TTY guard as
52
+ * promptScope — never hangs a non-interactive run (tests, CI, piped input),
53
+ * defaulting to javascript. */
54
+ function promptLanguage() {
55
+ if (!process.stdin.isTTY)
56
+ return Promise.resolve('javascript');
57
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
58
+ return new Promise((res) => {
59
+ rl.question('Author your APIs in [j]avascript or [t]ypescript? [j] ', (answer) => {
60
+ rl.close();
61
+ res(answer.trim().toLowerCase().startsWith('t') ? 'typescript' : 'javascript');
62
+ });
63
+ });
64
+ }
65
+ /** Fallback package root used when runCommand is called without a RuntimeContext
66
+ * (e.g. a direct unit test). In normal use bin/emberflow.mjs passes the real
67
+ * root — important because this module compiles to dist/bin/commands.js, whose
68
+ * own `..` is dist/, not the package root. */
69
+ const selfPkgRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..');
70
+ const defaultCtx = { runnerMode: 'tsx', packageRoot: selfPkgRoot };
71
+ /** Spawn `tsx <entry>` inheriting stdio, with extra env. Returns the child's exit code. */
72
+ function runTsx(ctx, entry, env, args = []) {
73
+ return new Promise((res) => {
74
+ const child = spawn('npx', ['tsx', join(ctx.packageRoot, entry), ...args], {
75
+ stdio: 'inherit',
76
+ env: { ...process.env, ...env },
77
+ });
78
+ child.on('exit', (code) => res(code ?? 0));
79
+ });
80
+ }
81
+ /** Spawn `node <entry>` inheriting stdio, with extra env. Used for JS consumers,
82
+ * where the compiled dist/server runs on plain Node (no tsx child — so it works
83
+ * inside the codex sandbox). */
84
+ function runNode(entry, env, args = []) {
85
+ return new Promise((res) => {
86
+ const child = spawn(process.execPath, [entry, ...args], {
87
+ stdio: 'inherit',
88
+ env: { ...process.env, ...env },
89
+ });
90
+ child.on('exit', (code) => res(code ?? 0));
91
+ });
92
+ }
93
+ /** Launch a server entry ('index' or 'mcp') in the mode the bin decided: plain
94
+ * `node dist/server/<name>.js` for JS consumers, `tsx server/<name>.ts` otherwise. */
95
+ function launchServer(ctx, name, env, args = []) {
96
+ if (ctx.runnerMode === 'node') {
97
+ return runNode(join(ctx.packageRoot, 'dist', 'server', `${name}.js`), env, args);
98
+ }
99
+ return runTsx(ctx, join('server', `${name}.ts`), env, args);
100
+ }
101
+ /** Build (if needed) and launch the studio dev server. Shared by `dev` and by
102
+ * `init` (which launches after scaffolding unless --no-launch is passed). */
103
+ async function runDev(p, ctx) {
104
+ // Same precedence as bin/runtime.mjs's decideRuntime (EMBERFLOW_PROJECT env →
105
+ // --project flag → cwd) so the launched process boots the same project the
106
+ // runtime decision was made against — a bare --project flag no longer
107
+ // silently overwrites an already-set EMBERFLOW_PROJECT.
108
+ const project = resolveProjectDir(p.project ? ['--project', p.project] : [], process.env, process.cwd());
109
+ // From a consumer install the shipped studio-dist exists; from source it may not.
110
+ if (!existsSync(join(ctx.packageRoot, 'studio-dist'))) {
111
+ console.log('[emberflow] building studio…');
112
+ await runTsx(ctx, 'node_modules/.bin/vite', {}, ['build']).catch(() => 0);
113
+ }
114
+ return launchServer(ctx, 'index', {
115
+ EMBERFLOW_SERVE_STUDIO: '1',
116
+ EMBERFLOW_PROJECT: project,
117
+ // `dev` runs a single (non-watch) process, so opening the browser once
118
+ // is the intended UX. (The server default is off so this repo's watch-mode
119
+ // `npm run server` doesn't spawn a tab per restart — see server/index.ts.)
120
+ EMBERFLOW_OPEN_BROWSER: '1',
121
+ ...(p.port ? { EMBERFLOW_RUNNER_PORT: String(p.port) } : {}),
122
+ ...(p.mock ? { EMBERFLOW_MOCK: '1' } : {}),
123
+ });
124
+ }
125
+ /** Run the runner headless: API + operation routes, NO studio, NO browser.
126
+ * This is the production-ish API host — `dev` additionally serves the studio
127
+ * and opens a browser tab. */
128
+ function runServe(p, ctx) {
129
+ // See runDev's comment: same EMBERFLOW_PROJECT-first precedence as
130
+ // bin/runtime.mjs's resolveProjectDir.
131
+ const project = resolveProjectDir(p.project ? ['--project', p.project] : [], process.env, process.cwd());
132
+ return launchServer(ctx, 'index', {
133
+ EMBERFLOW_PROJECT: project,
134
+ ...(p.port ? { EMBERFLOW_RUNNER_PORT: String(p.port) } : {}),
135
+ ...(p.mock ? { EMBERFLOW_MOCK: '1' } : {}),
136
+ });
137
+ }
138
+ export async function runCommand(p, ctx = defaultCtx) {
139
+ const pkgRoot = ctx.packageRoot;
140
+ switch (p.command) {
141
+ case 'dev':
142
+ return runDev(p, ctx);
143
+ case 'serve':
144
+ return runServe(p, ctx);
145
+ case 'mcp':
146
+ return launchServer(ctx, 'mcp', {});
147
+ case 'run': {
148
+ // In-process via tsx's register() loader — NO `tsx` child spawn, so it
149
+ // works inside the codex sandbox (tsx's IPC pipe is blocked there).
150
+ const args = ['run', ...p.rest, ...(p.scenario ? ['--scenario', p.scenario] : [])];
151
+ return (await import('../server/cli.js')).runCli(args);
152
+ }
153
+ case 'list-nodes':
154
+ case 'node-schema':
155
+ case 'list-workflows':
156
+ case 'get-workflow':
157
+ case 'list-environments':
158
+ case 'login-environment':
159
+ case 'set-environment-auth':
160
+ case 'serving':
161
+ case 'validate':
162
+ case 'publish':
163
+ case 'save':
164
+ case 'samples':
165
+ case 'create':
166
+ case 'delete':
167
+ case 'rename': {
168
+ // Rich CLI operation commands, also in-process (no `tsx` child spawn).
169
+ return (await import('../server/cli.js')).runCli([p.command, ...p.rest]);
170
+ }
171
+ case 'test': {
172
+ // In-process scenario suite runner — NO runner process. Needs the
173
+ // project dir (server/cli.ts's `test` case reads EMBERFLOW_PROJECT),
174
+ // same convention runDev/runServe use for --project.
175
+ if (p.project)
176
+ process.env.EMBERFLOW_PROJECT = resolve(process.cwd(), p.project);
177
+ // --mock is parsed globally (dev/serve read it as EMBERFLOW_MOCK), so it
178
+ // never lands in p.rest — thread it through explicitly here.
179
+ return (await import('../server/cli.js')).runCli(['test', ...p.rest, ...(p.mock ? ['--mock'] : [])]);
180
+ }
181
+ case 'doctor': {
182
+ // In-process operation diagnostics — NO runner process, same
183
+ // EMBERFLOW_PROJECT convention as `test`.
184
+ if (p.project)
185
+ process.env.EMBERFLOW_PROJECT = resolve(process.cwd(), p.project);
186
+ return (await import('../server/cli.js')).runCli(['doctor', ...p.rest]);
187
+ }
188
+ case 'init': {
189
+ // bin/init compiles alongside this module (dist/bin/init.js when built),
190
+ // so resolve the guard against THIS module's own location + extension —
191
+ // init.ts under tsx source, init.js under plain-node dist.
192
+ const initExt = import.meta.url.endsWith('.js') ? '.js' : '.ts';
193
+ const initEntry = fileURLToPath(new URL(`./init${initExt}`, import.meta.url));
194
+ if (!existsSync(initEntry)) {
195
+ console.log('emberflow init lands in a later step');
196
+ return 0;
197
+ }
198
+ if (p.js && p.ts) {
199
+ console.error('[emberflow] --js and --ts are mutually exclusive');
200
+ return 1;
201
+ }
202
+ try {
203
+ const mod = (await import('./init.js'));
204
+ const scope = p.scope ?? (await promptScope());
205
+ const language = p.js ? 'javascript' : p.ts ? 'typescript' : await promptLanguage();
206
+ const code = await mod.runInit(process.cwd(), p.noSkills
207
+ ? { skills: false, packageRoot: pkgRoot, language }
208
+ : { skills: { scope, home: homedir() }, packageRoot: pkgRoot, language });
209
+ if (code !== 0)
210
+ return code;
211
+ console.log('');
212
+ console.log('[emberflow] next steps:');
213
+ console.log(' - run `npx emberflow dev` any time to relaunch the studio');
214
+ console.log(p.noSkills
215
+ ? ' - skills were not installed (--no-skills)'
216
+ : ` - skills installed at ${scope === 'global' ? '~' : '.'}/.claude (and/or .codex) /skills`);
217
+ console.log(' - your operations live in emberflow/apis (open the studio to build them)');
218
+ console.log('');
219
+ if (p.noLaunch)
220
+ return 0;
221
+ // `ctx` reflects the runtime decision made at bin startup — BEFORE
222
+ // scaffolding existed. For a fresh TS project that decision was made with
223
+ // no config on disk (→ node mode), which then dies launching against the
224
+ // just-scaffolded emberflow.config.ts. Re-decide against the now-real
225
+ // project instead of trusting the stale context.
226
+ if (language === 'typescript') {
227
+ if (!mod.tsxResolvable(process.cwd())) {
228
+ console.log('');
229
+ console.log('[emberflow] tsx is not installed — skipping auto-launch.');
230
+ console.log(' npm i -D tsx typescript');
231
+ console.log(' npx emberflow dev');
232
+ return 0;
233
+ }
234
+ const decision = decideRuntime({ packageRoot: pkgRoot, projectDir: process.cwd(), env: process.env });
235
+ console.log('[emberflow] Starting Emberflow…');
236
+ return runDev(p, { runnerMode: decision.runnerMode, packageRoot: pkgRoot });
237
+ }
238
+ console.log('[emberflow] Starting Emberflow…');
239
+ return runDev(p, ctx);
240
+ }
241
+ catch (err) {
242
+ console.error('[emberflow] init failed:', err instanceof Error ? err.message : err);
243
+ return 1;
244
+ }
245
+ }
246
+ default:
247
+ console.log('Usage: emberflow <dev|serve|mcp|init|run|test|doctor|list-nodes|node-schema|list-workflows|get-workflow|list-environments|login-environment|set-environment-auth|serving|validate|publish|save|create|delete|rename|samples> [--port N] [--project DIR] [--scenario NAME] [--no-skills] [--global|--local] [--no-launch] [--mock] [--js|--ts]\n' +
248
+ ' test [opId] [--environment NAME] [--json] Run scenario expectations in-process (no runner) — exit 0/1/2\n' +
249
+ ' doctor [opId] [--fix] Diagnose operation(s) in-process (no runner); --fix seeds param defaults — exit 0/1/2');
250
+ return 0;
251
+ }
252
+ }
@@ -0,0 +1,17 @@
1
+ /** Whether `tsx` resolves from the project's own node_modules (peer dep — optional).
2
+ * Exported so bin/commands.ts can reuse it when deciding whether `init --ts` can
3
+ * launch dev immediately after scaffolding, or must defer to the user. */
4
+ export declare function tsxResolvable(cwd: string): boolean;
5
+ export declare function runInit(cwd: string, opts?: {
6
+ skills?: false | {
7
+ scope: 'repo' | 'global';
8
+ home: string;
9
+ };
10
+ /** Real package root, passed by the bin. Falls back to this module's own
11
+ * location — which is wrong when compiled to dist/bin/init.js (its `..` is
12
+ * dist/, not the package root that holds templates/), so the bin supplies it. */
13
+ packageRoot?: string;
14
+ /** Which language to scaffold the config/nodes in. Defaults to javascript
15
+ * (matches the non-TTY prompt default in bin/commands.ts). */
16
+ language?: 'javascript' | 'typescript';
17
+ }): Promise<number>;