@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.
- package/LICENSE +21 -0
- package/README.md +153 -0
- package/bin/commands.test.ts +185 -0
- package/bin/commands.ts +306 -0
- package/bin/emberflow.mjs +38 -0
- package/bin/init.test.ts +165 -0
- package/bin/init.ts +297 -0
- package/bin/runtime.mjs +105 -0
- package/bin/runtime.test.ts +167 -0
- package/bin/skillTargets.test.ts +108 -0
- package/bin/skillTargets.ts +36 -0
- package/bin/skillTemplates.test.ts +37 -0
- package/bin/tsconfig.json +23 -0
- package/dist/bin/commands.d.ts +24 -0
- package/dist/bin/commands.js +252 -0
- package/dist/bin/init.d.ts +17 -0
- package/dist/bin/init.js +241 -0
- package/dist/bin/runtime.d.mts +39 -0
- package/dist/bin/runtime.mjs +96 -0
- package/dist/bin/skillTargets.d.ts +13 -0
- package/dist/bin/skillTargets.js +22 -0
- package/dist/server/agents/claudeAdapter.d.ts +8 -0
- package/dist/server/agents/claudeAdapter.js +190 -0
- package/dist/server/agents/claudeParse.d.ts +2 -0
- package/dist/server/agents/claudeParse.js +162 -0
- package/dist/server/agents/codexAdapter.d.ts +13 -0
- package/dist/server/agents/codexAdapter.js +174 -0
- package/dist/server/agents/codexParse.d.ts +2 -0
- package/dist/server/agents/codexParse.js +74 -0
- package/dist/server/agents/detect.d.ts +23 -0
- package/dist/server/agents/detect.js +35 -0
- package/dist/server/agents/gitScope.d.ts +24 -0
- package/dist/server/agents/gitScope.js +95 -0
- package/dist/server/agents/modelRejectionHint.d.ts +8 -0
- package/dist/server/agents/modelRejectionHint.js +12 -0
- package/dist/server/agents/prompt.d.ts +79 -0
- package/dist/server/agents/prompt.js +364 -0
- package/dist/server/agents/runManager.d.ts +90 -0
- package/dist/server/agents/runManager.js +259 -0
- package/dist/server/agents/types.d.ts +15 -0
- package/dist/server/agents/types.js +1 -0
- package/dist/server/apiStore.d.ts +60 -0
- package/dist/server/apiStore.js +249 -0
- package/dist/server/auth/enforce.d.ts +1 -0
- package/dist/server/auth/enforce.js +6 -0
- package/dist/server/auth/types.d.ts +1 -0
- package/dist/server/auth/types.js +1 -0
- package/dist/server/auth/verifiers.d.ts +1 -0
- package/dist/server/auth/verifiers.js +4 -0
- package/dist/server/authAttach.d.ts +19 -0
- package/dist/server/authAttach.js +79 -0
- package/dist/server/cli.d.ts +7 -0
- package/dist/server/cli.js +804 -0
- package/dist/server/client.d.ts +130 -0
- package/dist/server/client.js +247 -0
- package/dist/server/environments.d.ts +103 -0
- package/dist/server/environments.js +486 -0
- package/dist/server/flowStore.d.ts +37 -0
- package/dist/server/flowStore.js +117 -0
- package/dist/server/httpOperations.d.ts +32 -0
- package/dist/server/httpOperations.js +62 -0
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.js +1022 -0
- package/dist/server/infrastructure.d.ts +51 -0
- package/dist/server/infrastructure.js +116 -0
- package/dist/server/login.d.ts +21 -0
- package/dist/server/login.js +44 -0
- package/dist/server/mcp.d.ts +1 -0
- package/dist/server/mcp.js +182 -0
- package/dist/server/migrateFlows.d.ts +8 -0
- package/dist/server/migrateFlows.js +25 -0
- package/dist/server/mockHandler.d.ts +15 -0
- package/dist/server/mockHandler.js +35 -0
- package/dist/server/nodesPayload.d.ts +14 -0
- package/dist/server/nodesPayload.js +14 -0
- package/dist/server/normalizeFlow.d.ts +21 -0
- package/dist/server/normalizeFlow.js +48 -0
- package/dist/server/openBrowser.d.ts +8 -0
- package/dist/server/openBrowser.js +16 -0
- package/dist/server/operationResult.d.ts +12 -0
- package/dist/server/operationResult.js +26 -0
- package/dist/server/pathGuard.d.ts +1 -0
- package/dist/server/pathGuard.js +16 -0
- package/dist/server/pathSafety.d.ts +20 -0
- package/dist/server/pathSafety.js +35 -0
- package/dist/server/projectConfig.d.ts +47 -0
- package/dist/server/projectConfig.js +61 -0
- package/dist/server/projectMode.d.ts +26 -0
- package/dist/server/projectMode.js +44 -0
- package/dist/server/redact.d.ts +6 -0
- package/dist/server/redact.js +37 -0
- package/dist/server/runRegistry.d.ts +84 -0
- package/dist/server/runRegistry.js +200 -0
- package/dist/server/runtime/RuntimeAdapter.d.ts +22 -0
- package/dist/server/runtime/RuntimeAdapter.js +1 -0
- package/dist/server/runtime/expressAdapter.d.ts +19 -0
- package/dist/server/runtime/expressAdapter.js +56 -0
- package/dist/server/scenarioTest.d.ts +14 -0
- package/dist/server/scenarioTest.js +66 -0
- package/dist/server/subflowRunner.d.ts +32 -0
- package/dist/server/subflowRunner.js +72 -0
- package/dist/server/testRunner.d.ts +70 -0
- package/dist/server/testRunner.js +151 -0
- package/dist/src/config.d.ts +3 -0
- package/dist/src/config.js +2 -0
- package/dist/src/engine/authVerify.d.ts +33 -0
- package/dist/src/engine/authVerify.js +63 -0
- package/dist/src/engine/diagnostics.d.ts +62 -0
- package/dist/src/engine/diagnostics.js +148 -0
- package/dist/src/engine/executor.d.ts +151 -0
- package/dist/src/engine/executor.js +684 -0
- package/dist/src/engine/httpError.d.ts +7 -0
- package/dist/src/engine/httpError.js +14 -0
- package/dist/src/engine/index.d.ts +11 -0
- package/dist/src/engine/index.js +11 -0
- package/dist/src/engine/output.d.ts +11 -0
- package/dist/src/engine/output.js +20 -0
- package/dist/src/engine/publish.d.ts +30 -0
- package/dist/src/engine/publish.js +95 -0
- package/dist/src/engine/registry.d.ts +34 -0
- package/dist/src/engine/registry.js +67 -0
- package/dist/src/engine/scenarios.d.ts +12 -0
- package/dist/src/engine/scenarios.js +19 -0
- package/dist/src/engine/schemaCheck.d.ts +4 -0
- package/dist/src/engine/schemaCheck.js +34 -0
- package/dist/src/engine/trace.d.ts +12 -0
- package/dist/src/engine/trace.js +12 -0
- package/dist/src/engine/types.d.ts +308 -0
- package/dist/src/engine/types.js +1 -0
- package/dist/src/engine/validation.d.ts +26 -0
- package/dist/src/engine/validation.js +318 -0
- package/dist/src/flows/anomaly-flows.d.ts +23 -0
- package/dist/src/flows/anomaly-flows.js +151 -0
- package/dist/src/flows/city-sweep-flow.d.ts +10 -0
- package/dist/src/flows/city-sweep-flow.js +127 -0
- package/dist/src/flows/login-flow.d.ts +13 -0
- package/dist/src/flows/login-flow.js +145 -0
- package/dist/src/flows/pradar-flows.d.ts +5 -0
- package/dist/src/flows/pradar-flows.js +375 -0
- package/dist/src/flows/weather-flow.d.ts +12 -0
- package/dist/src/flows/weather-flow.js +150 -0
- package/dist/src/lib/pathParams.d.ts +3 -0
- package/dist/src/lib/pathParams.js +6 -0
- package/dist/src/nodes/anomaly.d.ts +2 -0
- package/dist/src/nodes/anomaly.js +556 -0
- package/dist/src/nodes/flow-control.d.ts +7 -0
- package/dist/src/nodes/flow-control.js +270 -0
- package/dist/src/nodes/index.d.ts +3 -0
- package/dist/src/nodes/index.js +20 -0
- package/dist/src/nodes/login.d.ts +7 -0
- package/dist/src/nodes/login.js +158 -0
- package/dist/src/nodes/loops.d.ts +11 -0
- package/dist/src/nodes/loops.js +60 -0
- package/dist/src/nodes/pradar/index.d.ts +7 -0
- package/dist/src/nodes/pradar/index.js +9 -0
- package/dist/src/nodes/pradar/logic.d.ts +189 -0
- package/dist/src/nodes/pradar/logic.js +370 -0
- package/dist/src/nodes/pradar/nodes.d.ts +42 -0
- package/dist/src/nodes/pradar/nodes.js +801 -0
- package/dist/src/nodes/requireAuth.d.ts +20 -0
- package/dist/src/nodes/requireAuth.js +48 -0
- package/dist/src/nodes/response.d.ts +8 -0
- package/dist/src/nodes/response.js +33 -0
- package/dist/src/nodes/weather.d.ts +33 -0
- package/dist/src/nodes/weather.js +260 -0
- package/package.json +92 -0
- package/server/agentRoute.test.ts +157 -0
- package/server/agents/__fixtures__/fake-claude-writer.mjs +23 -0
- package/server/agents/__fixtures__/fake-claude.mjs +55 -0
- package/server/agents/__fixtures__/fake-codex-writer.mjs +27 -0
- package/server/agents/__fixtures__/fake-codex.mjs +52 -0
- package/server/agents/__fixtures__/fake-version-empty.mjs +3 -0
- package/server/agents/__fixtures__/fake-version-fail.mjs +4 -0
- package/server/agents/__fixtures__/fake-version-garbage.mjs +4 -0
- package/server/agents/__fixtures__/fake-version-good.mjs +4 -0
- package/server/agents/__fixtures__/fake-version-hang.mjs +3 -0
- package/server/agents/claudeAdapter.test.ts +127 -0
- package/server/agents/claudeAdapter.ts +212 -0
- package/server/agents/claudeParse.ts +193 -0
- package/server/agents/codexAdapter.test.ts +125 -0
- package/server/agents/codexAdapter.ts +198 -0
- package/server/agents/codexParse.test.ts +58 -0
- package/server/agents/codexParse.ts +126 -0
- package/server/agents/detect.test.ts +65 -0
- package/server/agents/detect.ts +43 -0
- package/server/agents/gitScope.test.ts +129 -0
- package/server/agents/gitScope.ts +110 -0
- package/server/agents/modelRejectionHint.test.ts +30 -0
- package/server/agents/modelRejectionHint.ts +14 -0
- package/server/agents/prompt.test.ts +613 -0
- package/server/agents/prompt.ts +527 -0
- package/server/agents/runManager.test.ts +251 -0
- package/server/agents/runManager.ts +286 -0
- package/server/agents/types.ts +17 -0
- package/server/apiMount.test.ts +591 -0
- package/server/apiStore.test.ts +324 -0
- package/server/apiStore.ts +241 -0
- package/server/auth/enforce.test.ts +61 -0
- package/server/auth/enforce.ts +6 -0
- package/server/auth/types.ts +5 -0
- package/server/auth/verifiers.test.ts +37 -0
- package/server/auth/verifiers.ts +9 -0
- package/server/authAttach.test.ts +175 -0
- package/server/authAttach.ts +99 -0
- package/server/cli.ts +847 -0
- package/server/cliAuth.test.ts +229 -0
- package/server/cliCreate.test.ts +131 -0
- package/server/cliDoctor.test.ts +326 -0
- package/server/cliTest.test.ts +735 -0
- package/server/client.ts +314 -0
- package/server/demoProject.test.ts +29 -0
- package/server/diagnosticsRoute.test.ts +169 -0
- package/server/environments.test.ts +620 -0
- package/server/environments.ts +603 -0
- package/server/environmentsRoute.test.ts +385 -0
- package/server/errorWorkflow.test.ts +205 -0
- package/server/flowStore.test.ts +84 -0
- package/server/flowStore.ts +125 -0
- package/server/httpOperations.test.ts +377 -0
- package/server/httpOperations.ts +96 -0
- package/server/index.ts +1111 -0
- package/server/infrastructure.test.ts +154 -0
- package/server/infrastructure.ts +161 -0
- package/server/infrastructureRoute.test.ts +105 -0
- package/server/login.test.ts +153 -0
- package/server/login.ts +67 -0
- package/server/mcp.ts +278 -0
- package/server/migrateFlows.test.ts +55 -0
- package/server/migrateFlows.ts +23 -0
- package/server/mockHandler.test.ts +138 -0
- package/server/mockHandler.ts +52 -0
- package/server/mockRun.test.ts +367 -0
- package/server/mockServe.test.ts +467 -0
- package/server/nodesPayload.test.ts +29 -0
- package/server/nodesPayload.ts +26 -0
- package/server/normalizeFlow.test.ts +80 -0
- package/server/normalizeFlow.ts +59 -0
- package/server/openBrowser.test.ts +20 -0
- package/server/openBrowser.ts +22 -0
- package/server/operationResult.test.ts +67 -0
- package/server/operationResult.ts +27 -0
- package/server/pathGuard.test.ts +19 -0
- package/server/pathGuard.ts +12 -0
- package/server/pathSafety.test.ts +38 -0
- package/server/pathSafety.ts +33 -0
- package/server/projectConfig.test.ts +67 -0
- package/server/projectConfig.ts +101 -0
- package/server/projectMode.test.ts +106 -0
- package/server/projectMode.ts +59 -0
- package/server/redact.test.ts +88 -0
- package/server/redact.ts +35 -0
- package/server/redactEgress.test.ts +281 -0
- package/server/runRegistry.ts +270 -0
- package/server/runsAuth.test.ts +119 -0
- package/server/runtime/RuntimeAdapter.ts +24 -0
- package/server/runtime/expressAdapter.test.ts +38 -0
- package/server/runtime/expressAdapter.ts +70 -0
- package/server/scenarioTest.test.ts +148 -0
- package/server/scenarioTest.ts +88 -0
- package/server/setupStatus.test.ts +127 -0
- package/server/subflowRunner.ts +104 -0
- package/server/testRunner.ts +216 -0
- package/server/tsconfig.json +20 -0
- package/server/workflowTestRoute.test.ts +297 -0
- package/src/App.tsx +233 -0
- package/src/components/AgentConsole.tsx +461 -0
- package/src/components/BranchRulesEditor.tsx +156 -0
- package/src/components/CreateModal.tsx +275 -0
- package/src/components/Dock.tsx +487 -0
- package/src/components/EnvironmentDialog.tsx +569 -0
- package/src/components/EnvironmentPicker.tsx +357 -0
- package/src/components/EnvironmentsDialog.tsx +144 -0
- package/src/components/ExecutionPager.tsx +67 -0
- package/src/components/InfraPanel.test.tsx +103 -0
- package/src/components/InfraPanel.tsx +171 -0
- package/src/components/Inspector.tsx +1182 -0
- package/src/components/Json.tsx +50 -0
- package/src/components/JsonModal.tsx +52 -0
- package/src/components/LogMessage.tsx +61 -0
- package/src/components/NodeRunModal.tsx +213 -0
- package/src/components/RunLogPanel.tsx +397 -0
- package/src/components/RunbookView.tsx +1085 -0
- package/src/components/Scenarios.tsx +476 -0
- package/src/components/SettingsDialog.tsx +229 -0
- package/src/components/Sidebar.tsx +436 -0
- package/src/components/StatusBar.tsx +302 -0
- package/src/components/Toolbar.tsx +663 -0
- package/src/components/WelcomeDialog.test.tsx +107 -0
- package/src/components/WelcomeDialog.tsx +412 -0
- package/src/components/agentMarkdown.test.tsx +91 -0
- package/src/components/agentMarkdown.tsx +158 -0
- package/src/components/ui/badge.tsx +31 -0
- package/src/components/ui/button.tsx +46 -0
- package/src/components/ui/combobox.tsx +114 -0
- package/src/components/ui/command.tsx +82 -0
- package/src/components/ui/dialog.tsx +73 -0
- package/src/components/ui/input.tsx +20 -0
- package/src/components/ui/popover.tsx +32 -0
- package/src/components/ui/resizable.tsx +44 -0
- package/src/components/ui/tabs.tsx +36 -0
- package/src/config.ts +4 -0
- package/src/engine/authVerify.ts +81 -0
- package/src/engine/diagnostics.test.ts +437 -0
- package/src/engine/diagnostics.ts +211 -0
- package/src/engine/executor.test.ts +1339 -0
- package/src/engine/executor.ts +840 -0
- package/src/engine/httpError.test.ts +12 -0
- package/src/engine/httpError.ts +16 -0
- package/src/engine/index.ts +11 -0
- package/src/engine/output.ts +21 -0
- package/src/engine/publish.test.ts +176 -0
- package/src/engine/publish.ts +127 -0
- package/src/engine/registry.test.ts +64 -0
- package/src/engine/registry.ts +80 -0
- package/src/engine/scenarios.test.ts +55 -0
- package/src/engine/scenarios.ts +29 -0
- package/src/engine/schemaCheck.test.ts +21 -0
- package/src/engine/schemaCheck.ts +35 -0
- package/src/engine/trace.test.ts +19 -0
- package/src/engine/trace.ts +23 -0
- package/src/engine/types.test.ts +22 -0
- package/src/engine/types.ts +316 -0
- package/src/engine/validation.test.ts +314 -0
- package/src/engine/validation.ts +354 -0
- package/src/flows/anomaly-flows.test.ts +81 -0
- package/src/flows/anomaly-flows.ts +202 -0
- package/src/flows/city-sweep-flow.ts +131 -0
- package/src/flows/flows.test.ts +30 -0
- package/src/flows/login-flow.ts +150 -0
- package/src/flows/pradar-flows.test.ts +204 -0
- package/src/flows/pradar-flows.ts +419 -0
- package/src/flows/weather-flow.ts +154 -0
- package/src/globals.css +147 -0
- package/src/lib/pathParams.test.ts +24 -0
- package/src/lib/pathParams.ts +6 -0
- package/src/lib/registerLens.test.ts +79 -0
- package/src/lib/registerLens.ts +59 -0
- package/src/lib/runbookModel.test.ts +61 -0
- package/src/lib/runbookModel.ts +290 -0
- package/src/lib/runbookProjection.test.ts +417 -0
- package/src/lib/runbookProjection.ts +254 -0
- package/src/lib/useTailAnchor.ts +53 -0
- package/src/lib/utils.ts +7 -0
- package/src/main.tsx +13 -0
- package/src/nodes/anomaly.test.ts +98 -0
- package/src/nodes/anomaly.ts +637 -0
- package/src/nodes/effects-scan.test.ts +44 -0
- package/src/nodes/flow-control.test.ts +280 -0
- package/src/nodes/flow-control.ts +305 -0
- package/src/nodes/index.ts +22 -0
- package/src/nodes/login.test.ts +81 -0
- package/src/nodes/login.ts +186 -0
- package/src/nodes/loops.ts +69 -0
- package/src/nodes/pradar/index.ts +11 -0
- package/src/nodes/pradar/logic.test.ts +288 -0
- package/src/nodes/pradar/logic.ts +582 -0
- package/src/nodes/pradar/nodes.test.ts +146 -0
- package/src/nodes/pradar/nodes.ts +955 -0
- package/src/nodes/requireAuth.test.ts +75 -0
- package/src/nodes/requireAuth.ts +55 -0
- package/src/nodes/response.test.ts +33 -0
- package/src/nodes/response.ts +39 -0
- package/src/nodes/traceDetail.test.ts +39 -0
- package/src/nodes/weather.test.ts +65 -0
- package/src/nodes/weather.ts +306 -0
- package/src/store/agentClient.test.ts +103 -0
- package/src/store/agentClient.ts +131 -0
- package/src/store/apiTree.test.ts +17 -0
- package/src/store/apiTree.ts +73 -0
- package/src/store/builderStore.test.ts +1234 -0
- package/src/store/builderStore.ts +1879 -0
- package/src/store/infraClient.ts +63 -0
- package/src/store/nodeMeta.test.ts +24 -0
- package/src/store/nodeMeta.ts +21 -0
- package/src/store/persistence.test.ts +15 -0
- package/src/store/persistence.ts +54 -0
- package/src/store/serverRunner.ts +381 -0
- package/src/store/setupClient.ts +57 -0
- package/studio-dist/assets/ibm-plex-mono-cyrillic-400-normal-BSMlKf0J.woff2 +0 -0
- package/studio-dist/assets/ibm-plex-mono-cyrillic-400-normal-CEL4l2ZJ.woff +0 -0
- package/studio-dist/assets/ibm-plex-mono-cyrillic-500-normal-Ael50iVv.woff +0 -0
- package/studio-dist/assets/ibm-plex-mono-cyrillic-500-normal-Bq9vWWag.woff2 +0 -0
- package/studio-dist/assets/ibm-plex-mono-cyrillic-ext-400-normal-DMdlQ8Kv.woff +0 -0
- package/studio-dist/assets/ibm-plex-mono-cyrillic-ext-400-normal-xuaO2J-f.woff2 +0 -0
- package/studio-dist/assets/ibm-plex-mono-cyrillic-ext-500-normal-BIfNGwUT.woff +0 -0
- package/studio-dist/assets/ibm-plex-mono-cyrillic-ext-500-normal-BqneJy0T.woff2 +0 -0
- package/studio-dist/assets/ibm-plex-mono-latin-400-normal-CvHOgSBP.woff +0 -0
- package/studio-dist/assets/ibm-plex-mono-latin-400-normal-DMJ8VG8y.woff2 +0 -0
- package/studio-dist/assets/ibm-plex-mono-latin-500-normal-CB9ihrfo.woff +0 -0
- package/studio-dist/assets/ibm-plex-mono-latin-500-normal-DSY6xOcd.woff2 +0 -0
- package/studio-dist/assets/ibm-plex-mono-latin-ext-400-normal-BmRBH3aV.woff2 +0 -0
- package/studio-dist/assets/ibm-plex-mono-latin-ext-400-normal-D3D2R8hC.woff +0 -0
- package/studio-dist/assets/ibm-plex-mono-latin-ext-500-normal-CAhNIIs5.woff2 +0 -0
- package/studio-dist/assets/ibm-plex-mono-latin-ext-500-normal-CZ70TYgx.woff +0 -0
- package/studio-dist/assets/ibm-plex-mono-vietnamese-400-normal-BulugwFq.woff2 +0 -0
- package/studio-dist/assets/ibm-plex-mono-vietnamese-400-normal-DDuiU_S-.woff +0 -0
- package/studio-dist/assets/ibm-plex-mono-vietnamese-500-normal-C8zxqsMH.woff +0 -0
- package/studio-dist/assets/ibm-plex-mono-vietnamese-500-normal-DZ4AoWbu.woff2 +0 -0
- package/studio-dist/assets/index-DNJwW-hM.css +1 -0
- package/studio-dist/assets/index-O26dKRjW.js +65 -0
- package/studio-dist/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
- package/studio-dist/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
- package/studio-dist/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
- package/studio-dist/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
- package/studio-dist/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
- package/studio-dist/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
- package/studio-dist/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
- package/studio-dist/favicon.svg +1 -0
- package/studio-dist/icons.svg +24 -0
- package/studio-dist/index.html +14 -0
- package/templates/skills/emberflow-basics/SKILL.md +316 -0
- package/templates/skills/emberflow-model-process/SKILL.md +209 -0
- package/templates/skills/emberflow-new-workflow/SKILL.md +211 -0
- package/templates/skills/emberflow-review-workflow/SKILL.md +148 -0
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="46" fill="none" viewBox="0 0 48 46"><path fill="#863bff" d="M25.946 44.938c-.664.845-2.021.375-2.021-.698V33.937a2.26 2.26 0 0 0-2.262-2.262H10.287c-.92 0-1.456-1.04-.92-1.788l7.48-10.471c1.07-1.497 0-3.578-1.842-3.578H1.237c-.92 0-1.456-1.04-.92-1.788L10.013.474c.214-.297.556-.474.92-.474h28.894c.92 0 1.456 1.04.92 1.788l-7.48 10.471c-1.07 1.498 0 3.579 1.842 3.579h11.377c.943 0 1.473 1.088.89 1.83L25.947 44.94z" style="fill:#863bff;fill:color(display-p3 .5252 .23 1);fill-opacity:1"/><mask id="a" width="48" height="46" x="0" y="0" maskUnits="userSpaceOnUse" style="mask-type:alpha"><path fill="#000" d="M25.842 44.938c-.664.844-2.021.375-2.021-.698V33.937a2.26 2.26 0 0 0-2.262-2.262H10.183c-.92 0-1.456-1.04-.92-1.788l7.48-10.471c1.07-1.498 0-3.579-1.842-3.579H1.133c-.92 0-1.456-1.04-.92-1.787L9.91.473c.214-.297.556-.474.92-.474h28.894c.92 0 1.456 1.04.92 1.788l-7.48 10.471c-1.07 1.498 0 3.578 1.842 3.578h11.377c.943 0 1.473 1.088.89 1.832L25.843 44.94z" style="fill:#000;fill-opacity:1"/></mask><g mask="url(#a)"><g filter="url(#b)"><ellipse cx="5.508" cy="14.704" fill="#ede6ff" rx="5.508" ry="14.704" style="fill:#ede6ff;fill:color(display-p3 .9275 .9033 1);fill-opacity:1" transform="matrix(.00324 1 1 -.00324 -4.47 31.516)"/></g><g filter="url(#c)"><ellipse cx="10.399" cy="29.851" fill="#ede6ff" rx="10.399" ry="29.851" style="fill:#ede6ff;fill:color(display-p3 .9275 .9033 1);fill-opacity:1" transform="matrix(.00324 1 1 -.00324 -39.328 7.883)"/></g><g filter="url(#d)"><ellipse cx="5.508" cy="30.487" fill="#7e14ff" rx="5.508" ry="30.487" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(89.814 -25.913 -14.639)scale(1 -1)"/></g><g filter="url(#e)"><ellipse cx="5.508" cy="30.599" fill="#7e14ff" rx="5.508" ry="30.599" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(89.814 -32.644 -3.334)scale(1 -1)"/></g><g filter="url(#f)"><ellipse cx="5.508" cy="30.599" fill="#7e14ff" rx="5.508" ry="30.599" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="matrix(.00324 1 1 -.00324 -34.34 30.47)"/></g><g filter="url(#g)"><ellipse cx="14.072" cy="22.078" fill="#ede6ff" rx="14.072" ry="22.078" style="fill:#ede6ff;fill:color(display-p3 .9275 .9033 1);fill-opacity:1" transform="rotate(93.35 24.506 48.493)scale(-1 1)"/></g><g filter="url(#h)"><ellipse cx="3.47" cy="21.501" fill="#7e14ff" rx="3.47" ry="21.501" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(89.009 28.708 47.59)scale(-1 1)"/></g><g filter="url(#i)"><ellipse cx="3.47" cy="21.501" fill="#7e14ff" rx="3.47" ry="21.501" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(89.009 28.708 47.59)scale(-1 1)"/></g><g filter="url(#j)"><ellipse cx=".387" cy="8.972" fill="#7e14ff" rx="4.407" ry="29.108" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(39.51 .387 8.972)"/></g><g filter="url(#k)"><ellipse cx="47.523" cy="-6.092" fill="#7e14ff" rx="4.407" ry="29.108" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(37.892 47.523 -6.092)"/></g><g filter="url(#l)"><ellipse cx="41.412" cy="6.333" fill="#47bfff" rx="5.971" ry="9.665" style="fill:#47bfff;fill:color(display-p3 .2799 .748 1);fill-opacity:1" transform="rotate(37.892 41.412 6.333)"/></g><g filter="url(#m)"><ellipse cx="-1.879" cy="38.332" fill="#7e14ff" rx="4.407" ry="29.108" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(37.892 -1.88 38.332)"/></g><g filter="url(#n)"><ellipse cx="-1.879" cy="38.332" fill="#7e14ff" rx="4.407" ry="29.108" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(37.892 -1.88 38.332)"/></g><g filter="url(#o)"><ellipse cx="35.651" cy="29.907" fill="#7e14ff" rx="4.407" ry="29.108" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(37.892 35.651 29.907)"/></g><g filter="url(#p)"><ellipse cx="38.418" cy="32.4" fill="#47bfff" rx="5.971" ry="15.297" style="fill:#47bfff;fill:color(display-p3 .2799 .748 1);fill-opacity:1" transform="rotate(37.892 38.418 32.4)"/></g></g><defs><filter id="b" width="60.045" height="41.654" x="-19.77" y="16.149" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="7.659"/></filter><filter id="c" width="90.34" height="51.437" x="-54.613" y="-7.533" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="7.659"/></filter><filter id="d" width="79.355" height="29.4" x="-49.64" y="2.03" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="e" width="79.579" height="29.4" x="-45.045" y="20.029" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="f" width="79.579" height="29.4" x="-43.513" y="21.178" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="g" width="74.749" height="58.852" x="15.756" y="-17.901" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="7.659"/></filter><filter id="h" width="61.377" height="25.362" x="23.548" y="2.284" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="i" width="61.377" height="25.362" x="23.548" y="2.284" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="j" width="56.045" height="63.649" x="-27.636" y="-22.853" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="k" width="54.814" height="64.646" x="20.116" y="-38.415" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="l" width="33.541" height="35.313" x="24.641" y="-11.323" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="m" width="54.814" height="64.646" x="-29.286" y="6.009" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="n" width="54.814" height="64.646" x="-29.286" y="6.009" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="o" width="54.814" height="64.646" x="8.244" y="-2.416" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="p" width="39.409" height="43.623" x="18.713" y="10.588" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter></defs></svg>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<symbol id="bluesky-icon" viewBox="0 0 16 17">
|
|
3
|
+
<g clip-path="url(#bluesky-clip)"><path fill="#08060d" d="M7.75 7.735c-.693-1.348-2.58-3.86-4.334-5.097-1.68-1.187-2.32-.981-2.74-.79C.188 2.065.1 2.812.1 3.251s.241 3.602.398 4.13c.52 1.744 2.367 2.333 4.07 2.145-2.495.37-4.71 1.278-1.805 4.512 3.196 3.309 4.38-.71 4.987-2.746.608 2.036 1.307 5.91 4.93 2.746 2.72-2.746.747-4.143-1.747-4.512 1.702.189 3.55-.4 4.07-2.145.156-.528.397-3.691.397-4.13s-.088-1.186-.575-1.406c-.42-.19-1.06-.395-2.741.79-1.755 1.24-3.64 3.752-4.334 5.099"/></g>
|
|
4
|
+
<defs><clipPath id="bluesky-clip"><path fill="#fff" d="M.1.85h15.3v15.3H.1z"/></clipPath></defs>
|
|
5
|
+
</symbol>
|
|
6
|
+
<symbol id="discord-icon" viewBox="0 0 20 19">
|
|
7
|
+
<path fill="#08060d" d="M16.224 3.768a14.5 14.5 0 0 0-3.67-1.153c-.158.286-.343.67-.47.976a13.5 13.5 0 0 0-4.067 0c-.128-.306-.317-.69-.476-.976A14.4 14.4 0 0 0 3.868 3.77C1.546 7.28.916 10.703 1.231 14.077a14.7 14.7 0 0 0 4.5 2.306q.545-.748.965-1.587a9.5 9.5 0 0 1-1.518-.74q.191-.14.372-.293c2.927 1.369 6.107 1.369 8.999 0q.183.152.372.294-.723.437-1.52.74.418.838.963 1.588a14.6 14.6 0 0 0 4.504-2.308c.37-3.911-.63-7.302-2.644-10.309m-9.13 8.234c-.878 0-1.599-.82-1.599-1.82 0-.998.705-1.82 1.6-1.82.894 0 1.614.82 1.599 1.82.001 1-.705 1.82-1.6 1.82m5.91 0c-.878 0-1.599-.82-1.599-1.82 0-.998.705-1.82 1.6-1.82.893 0 1.614.82 1.599 1.82 0 1-.706 1.82-1.6 1.82"/>
|
|
8
|
+
</symbol>
|
|
9
|
+
<symbol id="documentation-icon" viewBox="0 0 21 20">
|
|
10
|
+
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="m15.5 13.333 1.533 1.322c.645.555.967.833.967 1.178s-.322.623-.967 1.179L15.5 18.333m-3.333-5-1.534 1.322c-.644.555-.966.833-.966 1.178s.322.623.966 1.179l1.534 1.321"/>
|
|
11
|
+
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M17.167 10.836v-4.32c0-1.41 0-2.117-.224-2.68-.359-.906-1.118-1.621-2.08-1.96-.599-.21-1.349-.21-2.848-.21-2.623 0-3.935 0-4.983.369-1.684.591-3.013 1.842-3.641 3.428C3 6.449 3 7.684 3 10.154v2.122c0 2.558 0 3.838.706 4.726q.306.383.713.671c.76.536 1.79.64 3.581.66"/>
|
|
12
|
+
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M3 10a2.78 2.78 0 0 1 2.778-2.778c.555 0 1.209.097 1.748-.047.48-.129.854-.503.982-.982.145-.54.048-1.194.048-1.749a2.78 2.78 0 0 1 2.777-2.777"/>
|
|
13
|
+
</symbol>
|
|
14
|
+
<symbol id="github-icon" viewBox="0 0 19 19">
|
|
15
|
+
<path fill="#08060d" fill-rule="evenodd" d="M9.356 1.85C5.05 1.85 1.57 5.356 1.57 9.694a7.84 7.84 0 0 0 5.324 7.44c.387.079.528-.168.528-.376 0-.182-.013-.805-.013-1.454-2.165.467-2.616-.935-2.616-.935-.349-.91-.864-1.143-.864-1.143-.71-.48.051-.48.051-.48.787.051 1.2.805 1.2.805.695 1.194 1.817.857 2.268.649.064-.507.27-.857.49-1.052-1.728-.182-3.545-.857-3.545-3.87 0-.857.31-1.558.8-2.104-.078-.195-.349-1 .077-2.078 0 0 .657-.208 2.14.805a7.5 7.5 0 0 1 1.946-.26c.657 0 1.328.092 1.946.26 1.483-1.013 2.14-.805 2.14-.805.426 1.078.155 1.883.078 2.078.502.546.799 1.247.799 2.104 0 3.013-1.818 3.675-3.558 3.87.284.247.528.714.528 1.454 0 1.052-.012 1.896-.012 2.156 0 .208.142.455.528.377a7.84 7.84 0 0 0 5.324-7.441c.013-4.338-3.48-7.844-7.773-7.844" clip-rule="evenodd"/>
|
|
16
|
+
</symbol>
|
|
17
|
+
<symbol id="social-icon" viewBox="0 0 20 20">
|
|
18
|
+
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M12.5 6.667a4.167 4.167 0 1 0-8.334 0 4.167 4.167 0 0 0 8.334 0"/>
|
|
19
|
+
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M2.5 16.667a5.833 5.833 0 0 1 8.75-5.053m3.837.474.513 1.035c.07.144.257.282.414.309l.93.155c.596.1.736.536.307.965l-.723.73a.64.64 0 0 0-.152.531l.207.903c.164.715-.213.991-.84.618l-.872-.52a.63.63 0 0 0-.577 0l-.872.52c-.624.373-1.003.094-.84-.618l.207-.903a.64.64 0 0 0-.152-.532l-.723-.729c-.426-.43-.289-.864.306-.964l.93-.156a.64.64 0 0 0 .412-.31l.513-1.034c.28-.562.735-.562 1.012 0"/>
|
|
20
|
+
</symbol>
|
|
21
|
+
<symbol id="x-icon" viewBox="0 0 19 19">
|
|
22
|
+
<path fill="#08060d" fill-rule="evenodd" d="M1.893 1.98c.052.072 1.245 1.769 2.653 3.77l2.892 4.114c.183.261.333.48.333.486s-.068.089-.152.183l-.522.593-.765.867-3.597 4.087c-.375.426-.734.834-.798.905a1 1 0 0 0-.118.148c0 .01.236.017.664.017h.663l.729-.83c.4-.457.796-.906.879-.999a692 692 0 0 0 1.794-2.038c.034-.037.301-.34.594-.675l.551-.624.345-.392a7 7 0 0 1 .34-.374c.006 0 .93 1.306 2.052 2.903l2.084 2.965.045.063h2.275c1.87 0 2.273-.003 2.266-.021-.008-.02-1.098-1.572-3.894-5.547-2.013-2.862-2.28-3.246-2.273-3.266.008-.019.282-.332 2.085-2.38l2-2.274 1.567-1.782c.022-.028-.016-.03-.65-.03h-.674l-.3.342a871 871 0 0 1-1.782 2.025c-.067.075-.405.458-.75.852a100 100 0 0 1-.803.91c-.148.172-.299.344-.99 1.127-.304.343-.32.358-.345.327-.015-.019-.904-1.282-1.976-2.808L6.365 1.85H1.8zm1.782.91 8.078 11.294c.772 1.08 1.413 1.973 1.425 1.984.016.017.241.02 1.05.017l1.03-.004-2.694-3.766L7.796 5.75 5.722 2.852l-1.039-.004-1.039-.004z" clip-rule="evenodd"/>
|
|
23
|
+
</symbol>
|
|
24
|
+
</svg>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
|
+
<title>Emberflow</title>
|
|
8
|
+
<script type="module" crossorigin src="/assets/index-O26dKRjW.js"></script>
|
|
9
|
+
<link rel="stylesheet" crossorigin href="/assets/index-DNJwW-hM.css">
|
|
10
|
+
</head>
|
|
11
|
+
<body>
|
|
12
|
+
<div id="root"></div>
|
|
13
|
+
</body>
|
|
14
|
+
</html>
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: emberflow-basics
|
|
3
|
+
description: Use whenever working with Emberflow in this project — running the studio, understanding the file layout, adding or editing API operations and scenarios, registering custom nodes, or using the CLI/MCP. The foundation the other emberflow-* skills build on. Trigger on "emberflow", "workflow builder", "operation", "api", "flow", "run a scenario", "emberflow dev".
|
|
4
|
+
metadata:
|
|
5
|
+
version: 2.7.0
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Emberflow basics
|
|
9
|
+
|
|
10
|
+
Emberflow is a visual builder for **API operations** in this project. An operation
|
|
11
|
+
is a JSON file describing a node graph (a "flow"); the project's own code is
|
|
12
|
+
exposed as **nodes**; runs execute on a local runner and are visualised in a
|
|
13
|
+
browser studio. An operation with an `http` trigger is a live HTTP endpoint; one
|
|
14
|
+
without is an internal sub-flow other operations call. This skill is the
|
|
15
|
+
mechanics reference — how the pieces fit and how to drive them correctly.
|
|
16
|
+
|
|
17
|
+
## File layout (in the consumer project)
|
|
18
|
+
|
|
19
|
+
- `emberflow.config.mjs` (project root) — the contract. Exports `defineConfig({ flowsDir, registerNodes })`.
|
|
20
|
+
- `emberflow/apis/<api>/<folder…>/<operation>.json` — one operation per file, in a tree. `<api>` is the top-level API (e.g. `default`, `billing`); folders under it are free-form grouping.
|
|
21
|
+
- **An operation's `id` is its path relative to the apis dir, without `.json`.** `emberflow/apis/billing/charge.json` → `id: "billing/charge"`; `emberflow/apis/default/greet.json` → `id: "default/greet"`. The store keys ops by their in-file `id` but stores them by path, so the two MUST match — a mismatched id makes the op unreachable.
|
|
22
|
+
- `emberflow/apis/…/<operation>.scenarios.json` — that op's scenarios ("stories": named inputs), a sidecar array alongside the op file. The op file never contains a `scenarios` key.
|
|
23
|
+
- `emberflow/apis/<api>/<folder…>/_meta.json` — optional. Holds the `auth` policy inherited by every operation at or below that directory (see Auth below).
|
|
24
|
+
- `emberflow.environments.json` — **structure only**: named environments, their non-secret `vars`, and each env's `secrets` as a LIST of key NAMES (no values). Safe for you to read and edit. `emberflow.secrets.json` — secret **values only**, shape `{ "<envName>": { "<KEY>": "<value>" } }`, chmod `0600`; you NEVER read or write it (the user sets values via the studio's Manage Environment dialog or `login-environment`). A value of `"$ENV:VAR_NAME"` indirects to `process.env.VAR_NAME` at load. Both files are git-ignored (machine-local). The config and the `emberflow/apis` tree ARE committed.
|
|
25
|
+
- `emberflow/infrastructure.json` — **committed, structure-only** manifest of the infrastructure this project already uses (databases, external APIs, providers) with evidence pointing at the files that prove it. Written by the infrastructure scout (the studio's Infra tab / the `scout-infrastructure` agent intent); READ by agents before authoring operations so they REUSE existing systems and secret NAMES instead of inventing parallel config. Holds env-var NAMES only — never secret values.
|
|
26
|
+
|
|
27
|
+
## Running it
|
|
28
|
+
|
|
29
|
+
- `npx emberflow dev` — boots the runner **and** the studio in one process and opens the browser (`http://127.0.0.1:8092`). This is the primary loop: edit an operation file or config, the studio picks it up.
|
|
30
|
+
- `npx emberflow run <id> --scenario <name>` — run an operation to completion against a running runner; prints the result JSON. `<id>` is the operation's path-based id (e.g. `billing/charge`).
|
|
31
|
+
- `npx emberflow run <id> --input '{}'` — same as the studio's Plain Run button: it uses only the Input node's `defaults`. Use this to catch missing defaults before relying on scenarios.
|
|
32
|
+
- `npx emberflow mcp` — expose the MCP tools (list/get/save/validate/run/publish operations) to an agent.
|
|
33
|
+
- `npx emberflow init` — scaffold config + an example operation (already done if this project has an `emberflow.config.(mjs|js|ts)`).
|
|
34
|
+
|
|
35
|
+
Always have `emberflow dev` running while authoring — it's how you see and test.
|
|
36
|
+
|
|
37
|
+
## The config and custom nodes
|
|
38
|
+
|
|
39
|
+
The project's `language:` field (`'javascript'` or `'typescript'`, in
|
|
40
|
+
`emberflow.config.(mjs|js|ts)`) is the **authoritative signal for which
|
|
41
|
+
language to author in** — always match it, whichever scaffold shape below
|
|
42
|
+
the project actually uses.
|
|
43
|
+
|
|
44
|
+
JavaScript (`emberflow.config.mjs`, JSDoc types):
|
|
45
|
+
|
|
46
|
+
```js
|
|
47
|
+
import { defineConfig } from '@xdelivered/emberflow';
|
|
48
|
+
export default defineConfig({
|
|
49
|
+
language: 'javascript',
|
|
50
|
+
flowsDir: 'emberflow/flows', // anchor: the apis tree lives at emberflow/apis (sibling)
|
|
51
|
+
/** @param {import('@xdelivered/emberflow').NodeRegistry} registry */
|
|
52
|
+
registerNodes(registry) {
|
|
53
|
+
registry.register(
|
|
54
|
+
{
|
|
55
|
+
type: 'Greet', // unique id, referenced by operations
|
|
56
|
+
label: 'Greet',
|
|
57
|
+
description: 'Greets by name.',
|
|
58
|
+
category: 'my-app',
|
|
59
|
+
inputSchema: { fields: [{ name: 'name', type: 'string', required: true }] },
|
|
60
|
+
outputSchema: { fields: [{ name: 'hello', type: 'string' }] },
|
|
61
|
+
// effects: 'mutation' ← add for nodes that write/POST/email; they dry-run under safe mode
|
|
62
|
+
// traceKind: 'db'|'http'|'llm' ← add for nodes that touch infrastructure; mock mode intercepts by this
|
|
63
|
+
},
|
|
64
|
+
async (ctx) => ({ hello: `Hello, ${ctx.input.name}!` }),
|
|
65
|
+
);
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
TypeScript (`emberflow.config.ts`, typed) — same shape, typed instead of JSDoc:
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
import { defineConfig } from '@xdelivered/emberflow';
|
|
74
|
+
import type { NodeRegistry } from '@xdelivered/emberflow';
|
|
75
|
+
export default defineConfig({
|
|
76
|
+
language: 'typescript',
|
|
77
|
+
flowsDir: 'emberflow/flows',
|
|
78
|
+
registerNodes(registry: NodeRegistry) {
|
|
79
|
+
registry.register(
|
|
80
|
+
{ type: 'Greet', label: 'Greet', /* schemas/category/effects/traceKind as above */ },
|
|
81
|
+
async (ctx) => ({ hello: `Hello, ${ctx.input.name}!` }),
|
|
82
|
+
);
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
- A node = a **definition** (metadata: type/label/schemas/category/tags/effects — what the studio shows) plus an **implementation** (an `async (ctx) => output`). The studio fetches definitions over HTTP; implementations run on the runner (next to your app code, so they can touch your DB/services).
|
|
88
|
+
- `ctx.input` = the node's resolved inputs. `ctx.config` = its per-node config. Return a plain object; downstream nodes read fields off it.
|
|
89
|
+
- Mark side-effecting nodes `effects: 'mutation'` so safe mode can dry-run them,
|
|
90
|
+
and infrastructure-touching nodes with `traceKind` so mock mode can intercept them.
|
|
91
|
+
- **Design for both worlds.** An operation ships with real node implementations
|
|
92
|
+
(so it serves live) AND sidecar `mocks` covering every infra node (so it runs
|
|
93
|
+
in mock mode). Neither substitutes for the other — see Mock mode below.
|
|
94
|
+
- **A mutation node has exactly three behaviours** — mock run → canned output
|
|
95
|
+
from the sidecar; dry-run (`config.commit` false, or safe mode) → a
|
|
96
|
+
`wouldSend`/`wouldWrite` summary, nothing touched; commit
|
|
97
|
+
(`config.commit === true && !ctx.safeMode`) → the REAL side effect. There is
|
|
98
|
+
no fourth state: a commit path that logs "[SIMULATED]" and returns success is
|
|
99
|
+
FORBIDDEN — these are operational APIs, not demos. If the real call can't be
|
|
100
|
+
made (a missing secretRef, no webhook configured), the commit path THROWS a
|
|
101
|
+
clear error naming the missing key. Fake success is worse than failure;
|
|
102
|
+
`doctor` flags simulated commit paths as `simulated-commit`.
|
|
103
|
+
|
|
104
|
+
## Operation file shape
|
|
105
|
+
|
|
106
|
+
```json
|
|
107
|
+
{
|
|
108
|
+
"id": "default/greet", "name": "Greet", "version": 1,
|
|
109
|
+
"http": { "method": "GET", "path": "/greet" },
|
|
110
|
+
"nodes": [
|
|
111
|
+
{ "id": "in", "type": "Input", "label": "Request", "position": {"x":0,"y":0},
|
|
112
|
+
"config": { "fields": [
|
|
113
|
+
{"name":"params","type":"object"}, {"name":"query","type":"object"},
|
|
114
|
+
{"name":"body","type":"object"}, {"name":"headers","type":"object"}
|
|
115
|
+
], "defaults": { "query": {"name":"world"} } } },
|
|
116
|
+
{ "id": "greet", "type": "Greet", "label": "Greet", "position": {"x":300,"y":0},
|
|
117
|
+
"config": {}, "inputMap": { "name": {"sourceNodeId":"in","sourceField":"query.name"} } },
|
|
118
|
+
{ "id": "res", "type": "Response", "label": "Response", "position": {"x":600,"y":0},
|
|
119
|
+
"config": {}, "inputMap": { "body": {"sourceNodeId":"greet","sourceField":"$"} } }
|
|
120
|
+
],
|
|
121
|
+
"edges": [ {"id":"e1","source":"in","target":"greet"}, {"id":"e2","source":"greet","target":"res"} ],
|
|
122
|
+
"createdAt": "2026-07-05T00:00:00Z", "updatedAt": "2026-07-05T00:00:00Z"
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
- **`http`** = `{ method, path }`. Present → the operation is mounted as an HTTP endpoint. Absent → it's an internal sub-flow (call it from another op's `Subflow` node). Optional `auth` field (see below).
|
|
127
|
+
- **`Input`** is the entry node. For an HTTP operation the run payload is the request shape `{ params, query, body, headers }` (plus the request body's own fields promoted to the top level, and `user` when auth ran). Read a request part with a dotted `sourceField` like `query.name` or `body.amount`.
|
|
128
|
+
- **`Response`** is the terminus of an HTTP operation. Its input `{ status, body }` becomes the HTTP response; omit `status` and it defaults to `200`.
|
|
129
|
+
- **`inputMap`** wires a node's input field to a source node's output: `{ sourceNodeId, sourceField }`. `sourceField: "$"` means the whole output; otherwise a dot-path into it.
|
|
130
|
+
- **`edges`** draw the graph and, critically, define execution order (topological). **Every inputMap needs a matching edge** — an inputMap without an edge renders as a floating node AND the ordering can't see the dependency. Rule: if node B reads from node A, there is an `A→B` edge.
|
|
131
|
+
- **Input defaults matter.** The studio's Plain Run button sends no request. Every required Input field must either have a sensible `config.defaults` value or the author must intentionally accept/report that Plain Run fails. Do not call an operation done after only scenario runs.
|
|
132
|
+
|
|
133
|
+
## Built-in node types
|
|
134
|
+
|
|
135
|
+
You'll use these most (custom project nodes come from `registerNodes`):
|
|
136
|
+
|
|
137
|
+
- `Input` — entry; emits the run payload merged over `defaults`. For an HTTP op the payload is `{ params, query, body, headers }`.
|
|
138
|
+
- `Response` — **HTTP terminus.** Input `{ status, body }` → the HTTP response. Use this for any operation with an `http` trigger.
|
|
139
|
+
- `Result` — **internal terminus / display sink.** The collected output of a non-HTTP (internal) flow comes from its `Result` nodes. Use `Response` for HTTP endpoints, `Result` for internal sub-flows and while iterating on logic in the studio.
|
|
140
|
+
- `Conditional`/`Route` — branching. Emit `$branch`; downstream edges carry a `sourceHandle` = branch name and only run when that branch is taken.
|
|
141
|
+
- `ForEach`/`Collect` — loop over an array and gather the results.
|
|
142
|
+
- `Subflow` — runs another operation (by id) with mapped input and emits its output. This is how one operation calls an internal flow.
|
|
143
|
+
- `requireAuth` — verifies the request against an auth policy and attaches `{ user }` for downstream nodes (usually you rely on `_meta.json`/`http.auth` instead; use this node only for in-flow checks).
|
|
144
|
+
|
|
145
|
+
## Auth
|
|
146
|
+
|
|
147
|
+
Auth is resolved per operation by walking its path from the API root down:
|
|
148
|
+
|
|
149
|
+
- `_meta.json` at any level sets a default `auth` policy `{ scheme: 'bearer'|'apiKey', secretRef, header?, verify? }` inherited by every op at or below it. The nearest ancestor wins.
|
|
150
|
+
- An operation overrides via `http.auth`: a policy object (use that policy), `'none'` (explicitly public, ignoring inherited policy), or `'inherit'`/absent (defer to the nearest `_meta.json`).
|
|
151
|
+
- No `_meta.json` and no `http.auth` → public. A present-but-corrupt `_meta.json` fails closed (the op is denied, not mounted open).
|
|
152
|
+
|
|
153
|
+
## Environment auth (login once, auto-attach)
|
|
154
|
+
|
|
155
|
+
Separate from operation `auth` above: an *environment* in `emberflow.environments.json`
|
|
156
|
+
(git-ignored) can carry an `auth` block so runs against a real backend authenticate
|
|
157
|
+
automatically instead of failing 401 — `{ attach: {as:'cookie'|'header', name, secretRef},
|
|
158
|
+
login?: {request:{method,url,headers?,bodyRef?}, capture} }`, where `capture` is
|
|
159
|
+
`{from:'set-cookie', cookieName?}` / `{from:'json', path}` / `{from:'header', name}`
|
|
160
|
+
and `bodyRef` names a secret holding the login credentials as a JSON string. Once
|
|
161
|
+
configured and logged in, the runner attaches the stored credential to every
|
|
162
|
+
studio/CLI run against that environment — you never see the secret value, only
|
|
163
|
+
`«secret:KEY»` in run output/logs. Run `login-environment <name>` when an op fails
|
|
164
|
+
401/unauthorized and `list-environments` shows auth configured but not authenticated
|
|
165
|
+
(also use it after scaffolding a new auth block, to verify capture works). If the
|
|
166
|
+
login target enforces CSRF origin checks (e.g. better-auth), add a trusted `origin`
|
|
167
|
+
header to `login.request.headers`.
|
|
168
|
+
|
|
169
|
+
`attach.prefix` prepends a fixed string to the credential at attach time (e.g.
|
|
170
|
+
`"Bearer "` for a captured token, `"Basic "` when the secret already holds
|
|
171
|
+
`base64(user:pass)`). Set (or clear with `--json 'null'`) an environment's auth
|
|
172
|
+
block from the CLI: `set-environment-auth <name> --json '<EnvAuth JSON>'`.
|
|
173
|
+
|
|
174
|
+
When a user asks you to set up auth for an environment (the studio's Manage
|
|
175
|
+
Environment dialog's "Set up with AI"), the flow is: compose the `EnvAuth`
|
|
176
|
+
JSON from what they describe (curl command, prose, or both) — `attach`
|
|
177
|
+
(`as`, `name`, `secretRef`, optional `prefix`) plus an optional `login`
|
|
178
|
+
(`request` + `capture`) — apply it with `set-environment-auth`, then tell the
|
|
179
|
+
user exactly which secret key(s) to fill in under the dialog's Secrets section
|
|
180
|
+
(a `bodyRef` for the login request body, or the `attach.secretRef` for a
|
|
181
|
+
static key with no login), and verify with `login-environment` once those
|
|
182
|
+
secrets are set. If `login-environment` fails, read the error, adjust the
|
|
183
|
+
auth JSON, re-apply, and retry.
|
|
184
|
+
|
|
185
|
+
Two rules are load-bearing here: **never** edit an environment's `auth`
|
|
186
|
+
block in `emberflow.environments.json` directly — `set-environment-auth` is
|
|
187
|
+
the only way to change it (the rest of the file is structure-only and safe to
|
|
188
|
+
read/edit); and **never** ask for, accept, or handle a credential value
|
|
189
|
+
yourself — secret values live in `emberflow.secrets.json` (chmod `0600`,
|
|
190
|
+
which you never touch), and the user always enters them in the studio dialog,
|
|
191
|
+
never in chat or a CLI arg.
|
|
192
|
+
|
|
193
|
+
## Scenarios
|
|
194
|
+
|
|
195
|
+
A scenario is `{ id, name, input, description?, expect? }` in the
|
|
196
|
+
`<operation>.scenarios.json` sidecar. For an HTTP operation, `input` is the
|
|
197
|
+
request shape `{ params?, query?, body?, headers? }` — running the scenario
|
|
198
|
+
passes it through the exact same path a live HTTP request uses, so a green
|
|
199
|
+
scenario is real parity. `emberflow run <id> --scenario <name>` runs the op
|
|
200
|
+
with that input. In the studio, the Run split-button's dropdown lists
|
|
201
|
+
scenarios; each row has a play (run) and a step (step-through) button.
|
|
202
|
+
Adding/removing scenarios in the studio round-trips to the sidecar file.
|
|
203
|
+
|
|
204
|
+
**IMPORTANT: when you build or edit an operation, give its scenarios an
|
|
205
|
+
`expect`** so `emberflow test` actually covers them — a scenario with no
|
|
206
|
+
`expect` (or `expect: {}`) is silently skipped, not asserted. `expect` is
|
|
207
|
+
`{ status?, body?, executedNodes? }`: `body` is checked as a **deep subset**
|
|
208
|
+
of the real response (every key you list must match, recursively; extra
|
|
209
|
+
response keys are fine — don't restate the whole payload), `executedNodes`
|
|
210
|
+
lists node ids that must have reached `succeeded`. Every scenario that
|
|
211
|
+
exercises a distinct branch should assert `status` + `executedNodes` for
|
|
212
|
+
that branch, so a suite of green scenarios is proof, not vibes:
|
|
213
|
+
|
|
214
|
+
```json
|
|
215
|
+
{
|
|
216
|
+
"id": "not-found",
|
|
217
|
+
"name": "unknown id",
|
|
218
|
+
"input": { "params": { "id": "missing" } },
|
|
219
|
+
"expect": { "status": 404, "executedNodes": ["lookup", "notFoundResponse"] }
|
|
220
|
+
}
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
Run the suite with `npx emberflow test [opId] [--environment NAME]
|
|
224
|
+
[--json]` — in-process, no runner needed, exit `0`/`1`/`2`. It does **not**
|
|
225
|
+
auto-attach environment auth (hermetic by design); a scenario that needs
|
|
226
|
+
auth must carry the header/cookie in its own `input`. It also never fires
|
|
227
|
+
`errorOperation` (below) — treat `emberflow test` failures as assertion
|
|
228
|
+
failures, not incidents.
|
|
229
|
+
|
|
230
|
+
Add `--mock` to run scenarios as Mock runs (`mockRun: true`) instead of for
|
|
231
|
+
real: infrastructure nodes (`traceKind` db/http/llm) return their sidecar
|
|
232
|
+
mock (op-level `mocks`, overlaid by the scenario's own `mocks` per nodeId)
|
|
233
|
+
instead of touching real Postgres/HTTP/LLM, and fail loudly if a scenario
|
|
234
|
+
exercises one with no mock. It's the hermetic way to prove branch coverage
|
|
235
|
+
on infra-heavy ops without live infrastructure — `npx emberflow test <opId>
|
|
236
|
+
--mock`.
|
|
237
|
+
|
|
238
|
+
**After creating or editing any operation, run `npx emberflow doctor
|
|
239
|
+
<opId>` and resolve every finding before you consider the work done.** It
|
|
240
|
+
reports coded diagnostics — `missing-param-default` (a `:param` in the
|
|
241
|
+
HTTP path with no value under the Input node's `defaults.params`; leaves
|
|
242
|
+
the studio Run button disabled), `param-no-real-scenario` (no scenario
|
|
243
|
+
supplies a real value for a path param), `no-expects` (nothing asserts the
|
|
244
|
+
operation). `doctor --fix` seeds missing param defaults as `""`
|
|
245
|
+
placeholders; real ids in scenarios are your job — pull one from the
|
|
246
|
+
project's data when you can. Exit `0` means advisory-only findings; the
|
|
247
|
+
studio shows the same diagnostics as a problems chip in the toolbar.
|
|
248
|
+
|
|
249
|
+
## Retry and error workflow
|
|
250
|
+
|
|
251
|
+
- **`retry` on a node** — `{ "maxTries": 3, "waitMs": 500 }`, sibling of
|
|
252
|
+
`config`/`optional` in the node JSON. Reach for it on nodes that call a
|
|
253
|
+
flaky external (HTTP, a third-party SDK): `maxTries` is total attempts
|
|
254
|
+
including the first, `waitMs` a fixed delay between them, and only the
|
|
255
|
+
implementation call retries (input/config resolve once). Pair with
|
|
256
|
+
`"optional": true` when the flow should fail soft after retries exhaust
|
|
257
|
+
rather than abort the run.
|
|
258
|
+
- **`errorOperation` in `emberflow.config.mjs`** — an op id to run whenever
|
|
259
|
+
a *server* run (live HTTP endpoint or `POST /runs`) finishes `failed`. It
|
|
260
|
+
receives `{ failedRunId, failedWorkflowId, failedNodeId, error,
|
|
261
|
+
environment }`. Use it for alerting/logging side-effect ops, not for
|
|
262
|
+
business logic — it never fires for its own runs, firing is best-effort,
|
|
263
|
+
and `emberflow test`/scenario runs never trigger it.
|
|
264
|
+
|
|
265
|
+
## Mock mode
|
|
266
|
+
|
|
267
|
+
Mock is the studio's design-first mode. The environment dropdown's first
|
|
268
|
+
row is Mock; picking it is instant, and picking a real environment while
|
|
269
|
+
mocked goes live behind a "Go live?" confirm. A project with NO
|
|
270
|
+
`emberflow.environments.json` boots in Mock by default (nothing real to
|
|
271
|
+
serve against); creating environments — by hand, or via the dropdown's
|
|
272
|
+
"Manage environments…" modal whose Set-up-with-AI action opens the agent
|
|
273
|
+
panel in environment-setup mode (chat dispatches the `setup-environments`
|
|
274
|
+
intent) — is what unlocks going live. That intent scaffolds
|
|
275
|
+
environment entries, vars and `protected` flags but never secret values.
|
|
276
|
+
|
|
277
|
+
**Mock runs execute.** In Mock, Run/Step/scenario runs execute the flow
|
|
278
|
+
for real — routing, mappings, step-through — except infrastructure nodes
|
|
279
|
+
(`traceKind` `db`/`http`/`llm`) return canned outputs from the scenarios
|
|
280
|
+
sidecar's `mocks` maps instead of calling their implementations. Shape:
|
|
281
|
+
top-level `"mocks": { "<nodeId>": <output> }` (plain Run uses this) plus
|
|
282
|
+
optional per-scenario `mocks` (scenario wins per nodeId); the value is the
|
|
283
|
+
node's OUTPUT verbatim — read the node implementation to match its shape.
|
|
284
|
+
An infra node with no mock FAILS the mock run loudly (nothing real is ever
|
|
285
|
+
touched; no auth attached; subflow children inherit mock mode and use
|
|
286
|
+
their own op-level mocks). `compute`/unmarked nodes run their real logic —
|
|
287
|
+
so set `traceKind` honestly when authoring nodes: an unmarked infra node
|
|
288
|
+
silently executes for real in mock runs. `doctor` reports uncovered infra
|
|
289
|
+
nodes as `missing-node-mock`. When you author scenarios for an op with
|
|
290
|
+
infra nodes, ALWAYS write the mocks maps (cover-operation does this).
|
|
291
|
+
Never put secret values in mocks.
|
|
292
|
+
|
|
293
|
+
Serving is switchable at runtime via `POST /serving {mode}`, `emberflow serving <real|mock>` from the terminal, or the studio's environment dropdown; `emberflow serve --mock` / `dev --mock` mounts every HTTP endpoint but
|
|
294
|
+
answers purely from a scenario's `expect` — no auth, no node execution.
|
|
295
|
+
Selection order: `x-emberflow-scenario` request header (exact scenario
|
|
296
|
+
name) → `__scenario` query param → the first scenario on that op with an
|
|
297
|
+
`expect`. A named-but-unknown scenario is `404`; an op with no mockable
|
|
298
|
+
scenario is `501`. Every response carries `x-emberflow-mock: true`, and the
|
|
299
|
+
server logs a MOCK MODE banner at boot. Useful for pointing a frontend at
|
|
300
|
+
stable canned responses without wiring real backends — but it means an op
|
|
301
|
+
needs at least one scenario with `expect` before `--mock` can serve it.
|
|
302
|
+
Note: mock serves `expect.body` verbatim as the WHOLE response — a partial subset body written for testing is returned as-is.
|
|
303
|
+
|
|
304
|
+
## Validation
|
|
305
|
+
|
|
306
|
+
The runner validates operations: unknown node types, missing edges/handles, orphan
|
|
307
|
+
regions, self-referential subflows all surface as errors/warnings. A clean op
|
|
308
|
+
runs; an invalid one is rejected. Run the plain Run/default-input path plus one
|
|
309
|
+
scenario per branch, and check the run console — the logs stream node-by-node
|
|
310
|
+
with input/output you can click into.
|
|
311
|
+
|
|
312
|
+
## Related skills
|
|
313
|
+
|
|
314
|
+
- **emberflow-new-workflow** — design and build a brand-new HTTP operation from a goal.
|
|
315
|
+
- **emberflow-model-process** — port existing functionality into Emberflow: one process as an operation, or a whole subsystem as an API (its "At subsystem scale" section).
|
|
316
|
+
- **emberflow-review-workflow** — review an operation before it ships.
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: emberflow-model-process
|
|
3
|
+
description: Use when modeling or MIGRATING EXISTING functionality into Emberflow — a single process (script, controller, job, runbook) as one operation, or a whole subsystem (a worker, a pipeline, a service's job handlers) as an API of operations. Emphasises verbatim porting, branch fidelity, determinism, and (at subsystem scale) decomposition and seam verification. For designing something brand-new, use emberflow-new-workflow instead.
|
|
4
|
+
metadata:
|
|
5
|
+
version: 2.7.0
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Modeling an existing process as an Emberflow operation
|
|
9
|
+
|
|
10
|
+
Read **emberflow-basics** first. This skill is the discipline for turning a real,
|
|
11
|
+
already-working process into an operation that mirrors it — the goal is
|
|
12
|
+
*fidelity*, not reinvention. Done well, the operation becomes executable
|
|
13
|
+
documentation of the system. Sections 1–6 govern each individual port; when
|
|
14
|
+
the source is a whole subsystem spanning several operations, ALSO apply
|
|
15
|
+
**At subsystem scale** at the end.
|
|
16
|
+
|
|
17
|
+
If the process is (or is triggered by) an HTTP endpoint, model it as an HTTP
|
|
18
|
+
operation: create `emberflow/apis/<api>/<folder…>/<name>.json` with `id` equal to
|
|
19
|
+
that path (`emberflow/apis/billing/charge.json` → `"billing/charge"`), add the
|
|
20
|
+
`http` trigger `{ method, path }` matching the real route, start with an `Input`
|
|
21
|
+
receiving `{ params, query, body, headers }`, and end in a `Response` emitting
|
|
22
|
+
`{ status, body }` — mirror the real endpoint's status codes. A process with no
|
|
23
|
+
HTTP surface is an internal flow (no `http`) ending in `Result`.
|
|
24
|
+
|
|
25
|
+
## 0. Intake — interview the user before you port
|
|
26
|
+
|
|
27
|
+
The source code tells you the logic; it cannot tell you how the user wants the
|
|
28
|
+
model to run. Interview them BEFORE porting — one short question at a time,
|
|
29
|
+
skipping anything already answered by the project or the source:
|
|
30
|
+
|
|
31
|
+
Check `emberflow/infrastructure.json` FIRST: if the scout has already mapped
|
|
32
|
+
this project's databases, APIs and providers, reuse those systems and their
|
|
33
|
+
secret NAMES instead of re-deriving infrastructure the manifest already names.
|
|
34
|
+
|
|
35
|
+
1. **Environments — only when the project doesn't already answer it.**
|
|
36
|
+
Environments are PROJECT-level (`emberflow.environments.json` is shared by
|
|
37
|
+
every API and operation) — read it first. If it already covers what the
|
|
38
|
+
port needs, note that and move on; don't re-interview per operation. Ask
|
|
39
|
+
only when the file doesn't exist ("Which environments — local, dev,
|
|
40
|
+
staging, prod? Default? Production-like?") or the port introduces a delta
|
|
41
|
+
(a var or environment nothing carries yet). Production-like →
|
|
42
|
+
`protected: true` (safe mode by default; mutations dry-run). Scaffold via
|
|
43
|
+
`setup-environments`; environment auth via `set-environment-auth`, never a
|
|
44
|
+
hand-edit.
|
|
45
|
+
2. **Secrets — same scoping.** Existing keys need no questions. For NEW keys
|
|
46
|
+
the ported dependencies introduce (the DB URL, API keys you found in the
|
|
47
|
+
source), propose names and ask whether values differ per environment. You
|
|
48
|
+
only handle `secretRef` NAMES — the user enters values in the studio's
|
|
49
|
+
Manage Environment dialog, never in chat or any file/argument you produce.
|
|
50
|
+
3. **Mocks.** "Do you have a captured response or a real representative row
|
|
51
|
+
from the source system I should use as the canned data?" Mock values
|
|
52
|
+
sourced from the real system make mock runs exercise real shapes — this is
|
|
53
|
+
the difference between a trustworthy model and a green-but-lying one.
|
|
54
|
+
4. **Scenarios.** "Beyond one scenario per branch of the source, which
|
|
55
|
+
specific cases do you want named and asserted?" (A known incident, a
|
|
56
|
+
particular account shape, the case that motivated the port.)
|
|
57
|
+
|
|
58
|
+
Confirm the summary before porting. The answers feed sections 4–6.
|
|
59
|
+
|
|
60
|
+
## 1. Read the source first, fully
|
|
61
|
+
|
|
62
|
+
Find and read the actual implementation — the script, controller, job, or written
|
|
63
|
+
runbook. Identify:
|
|
64
|
+
|
|
65
|
+
- **The steps**, in execution order.
|
|
66
|
+
- **The decisions** — every `if`/`switch`/early-return/threshold. These become
|
|
67
|
+
`Conditional`/`Route` nodes. Note the exact conditions and constants.
|
|
68
|
+
- **The side effects** — DB writes, external POSTs, emails, queue sends. These
|
|
69
|
+
become custom nodes marked `effects: 'mutation'`.
|
|
70
|
+
- **The inputs and external data sources** it reads.
|
|
71
|
+
|
|
72
|
+
Do not skim. A faithful model requires knowing what the code actually does,
|
|
73
|
+
including the edge cases.
|
|
74
|
+
|
|
75
|
+
## 2. Port logic VERBATIM
|
|
76
|
+
|
|
77
|
+
Copy the real constants, formulas, thresholds, and branch order into your custom
|
|
78
|
+
node implementations — do not "improve", round, or simplify them. If the source
|
|
79
|
+
uses `>= 0.7` and `MAX = 30`, your node uses `>= 0.7` and `30`. The whole value
|
|
80
|
+
is that the flow behaves exactly like the system.
|
|
81
|
+
|
|
82
|
+
- Keep a `PORT NOTE:` comment where your model diverges from the source for a
|
|
83
|
+
real reason (e.g. the original interleaves two passes; the flow does them in
|
|
84
|
+
order). Divergences must be deliberate and documented.
|
|
85
|
+
- Where the source calls an in-process function you can't run here, model it as a
|
|
86
|
+
node with the same inputs/outputs and note the substitution.
|
|
87
|
+
|
|
88
|
+
## 3. Call out ambiguities — do not silently resolve them
|
|
89
|
+
|
|
90
|
+
If the source is contradictory (a comment says one threshold, the code uses
|
|
91
|
+
another), port the **code** (the source of truth), and surface the discrepancy to
|
|
92
|
+
the user rather than quietly picking one. Dead code, unreachable branches, and
|
|
93
|
+
stale constants are findings worth reporting — not things to tidy away.
|
|
94
|
+
|
|
95
|
+
## 4. Map the shape onto Emberflow
|
|
96
|
+
|
|
97
|
+
- Request in → an `Input` node reading `{ params, query, body, headers }` (for an
|
|
98
|
+
HTTP op); response out → a `Response` node `{ status, body }` per real exit,
|
|
99
|
+
matching the source's status codes (internal flow → `Result`).
|
|
100
|
+
- Linear steps → a chain of nodes with `inputMap` + edges.
|
|
101
|
+
- Decisions → `Conditional` (ordered rules) or `Route` (switch on a field), one
|
|
102
|
+
branch per real code path, including the else/default. A branch that returns a
|
|
103
|
+
different HTTP status gets its own `Response`.
|
|
104
|
+
- Loops over collections → `ForEach`/`Collect`.
|
|
105
|
+
- Sub-procedures the source factors out → a `Subflow` node calling a separate
|
|
106
|
+
operation (mirror the real call structure — if `handleA()` calls `handleB()`,
|
|
107
|
+
operation A calls operation B).
|
|
108
|
+
- Effects → `effects: 'mutation'` nodes that dry-run by default and only commit
|
|
109
|
+
under an explicit opt-in. The commit path performs the REAL side effect the
|
|
110
|
+
source performs — the same insert, the same webhook, the same send. A
|
|
111
|
+
"[SIMULATED]"-success commit path is forbidden: if the credential isn't set,
|
|
112
|
+
commit THROWS naming the secretRef. The port isn't operational until a
|
|
113
|
+
mutation has committed for real against a real environment (or is explicitly
|
|
114
|
+
reported "operational-pending" with the exact secrets needed).
|
|
115
|
+
- Infrastructure boundaries → set `traceKind` (`'db'|'http'|'llm'`) on every
|
|
116
|
+
node that reads a database, calls an external service, or invokes a model.
|
|
117
|
+
Mock mode intercepts by `traceKind`; an unmarked infra node silently executes
|
|
118
|
+
for real during mock runs.
|
|
119
|
+
|
|
120
|
+
**Reuse a node when one fits; author a new one when none does** — inventing
|
|
121
|
+
nodes is expected, it's how a process becomes real code. The only rule: register
|
|
122
|
+
a node's implementation in `registerNodes` before you reference its `type`; the
|
|
123
|
+
runner rejects an unregistered type (a name with no implementation), but a type
|
|
124
|
+
you register in the same change is exactly how you model the step.
|
|
125
|
+
|
|
126
|
+
## 5. Make it deterministic and reproducible
|
|
127
|
+
|
|
128
|
+
Real processes read the clock, the DB, live data. For a faithful, testable model,
|
|
129
|
+
lift those into node **inputs** (a `now` timestamp, a fetched snapshot) so a
|
|
130
|
+
scenario can pin them. Every branch of the original gets a scenario whose pinned
|
|
131
|
+
input drives exactly that path — named for the case it reproduces
|
|
132
|
+
("critically-low", "poor-forecast", "already-ran-today").
|
|
133
|
+
Also set Input `defaults` for every required field needed by the studio's plain
|
|
134
|
+
Run button, or explicitly report that plain Run is intentionally unsupported.
|
|
135
|
+
Scenarios prove branches; defaults prove the unscoped authoring run works.
|
|
136
|
+
|
|
137
|
+
**Model both worlds: real implementations AND mocks.** The ported node
|
|
138
|
+
implementations call the real dependencies (the actual query, the actual API,
|
|
139
|
+
the actual model) — verbatim porting means real code, not canned data in the
|
|
140
|
+
implementation. Then, in the same change, author the sidecar `mocks` maps so
|
|
141
|
+
the model also runs in mock mode: a top-level `"mocks": { "<nodeId>": <output> }`
|
|
142
|
+
covering every `traceKind` infra node, plus per-scenario `mocks` where a branch
|
|
143
|
+
needs different canned data. Source the mock values from realistic outputs of
|
|
144
|
+
the original system (a captured response, a representative row) so mock runs
|
|
145
|
+
exercise the same shapes the real system produces. An infra node with no mock
|
|
146
|
+
fails the mock run loudly; `doctor` reports it as `missing-node-mock`. A model
|
|
147
|
+
that only runs against live infrastructure isn't executable documentation —
|
|
148
|
+
and one that only runs mocked was never proven faithful.
|
|
149
|
+
|
|
150
|
+
## 6. Prove fidelity
|
|
151
|
+
|
|
152
|
+
Run both: plain Run and every scenario — in mock mode always, and against a
|
|
153
|
+
real environment when one exists. Confirm the flow reaches the same decision
|
|
154
|
+
the real system would for each input. Where you have reference outputs from the
|
|
155
|
+
source system, assert the flow matches them. Run `npx emberflow doctor <opId>`
|
|
156
|
+
and clear every finding (including `missing-node-mock`). Then run
|
|
157
|
+
**emberflow-review-workflow**.
|
|
158
|
+
|
|
159
|
+
## At subsystem scale (migrating a worker / pipeline / job family)
|
|
160
|
+
|
|
161
|
+
When the source is a whole subsystem, the per-operation discipline above still
|
|
162
|
+
governs each port — these rules govern the campaign around it. The core
|
|
163
|
+
lesson from real migrations: **the serious defects live in the wiring between
|
|
164
|
+
correct nodes, not in the ported logic** — an inputMap reading a field the
|
|
165
|
+
producer never emits, a single object wired where the consumer maps an array,
|
|
166
|
+
`timestamp` vs `date` keys, an identity (signalId, token) dropped between
|
|
167
|
+
phases — and a wrongly-shaped mock masks every one of them.
|
|
168
|
+
|
|
169
|
+
- **Scout first, completely.** Before designing, write down: entry points and
|
|
170
|
+
triggers (cron/queue/HTTP), the phases in execution order with file:line,
|
|
171
|
+
every constant in a table (that table becomes the fidelity checklist all
|
|
172
|
+
reviews check against), external dependencies per phase, and the data
|
|
173
|
+
shapes phases pass each other.
|
|
174
|
+
- **One operation per process boundary.** A queue or cron hop in the source is
|
|
175
|
+
an operation boundary in the model — do NOT collapse an async boundary into
|
|
176
|
+
a synchronous `Subflow` (the fan-out op models the *send*; the per-job op is
|
|
177
|
+
invoked separately). A sub-procedure called in-process IS a `Subflow`.
|
|
178
|
+
- **Inventory existing nodes before authoring new ones** — migrations often
|
|
179
|
+
land where partial models already exist. Reuse and extend.
|
|
180
|
+
- **Producers own their output contracts.** If a consumer needs rows, the
|
|
181
|
+
producer emits rows — inputMap cannot compose objects or wrap arrays. When
|
|
182
|
+
a seam mismatch surfaces, fix the producer node, never the mock.
|
|
183
|
+
- **Mocks are truthful.** A mock is the producer's real emittable output,
|
|
184
|
+
verbatim — never the shape the consumer wishes for; that is exactly how
|
|
185
|
+
wiring bugs go green. A `Subflow` mock uses whatever the child's Result
|
|
186
|
+
terminals actually emit (run the child; don't imagine an aggregate).
|
|
187
|
+
- **Drive branch scenarios through real logic.** Prefer feeding the REAL
|
|
188
|
+
detector/gate a crafted infra mock (a series that genuinely promotes, spend
|
|
189
|
+
that genuinely overspends) over mocking the compute node itself — a
|
|
190
|
+
scenario that cans the detector it claims to prove proves only wiring.
|
|
191
|
+
- **Verify the seams, then the system.** Check every inputMap sourceField
|
|
192
|
+
against the producer's outputSchema as you wire (doctor's
|
|
193
|
+
`inputmap-schema-mismatch` catches misnamed fields but NOT type/shape drift
|
|
194
|
+
on a correctly-named field — that check is yours). After per-op reviews
|
|
195
|
+
(emberflow-review-workflow on every op), run one whole-system review with
|
|
196
|
+
fresh eyes over the full diff, tracing infra-node inputs through REAL node
|
|
197
|
+
implementations with fixture data — mock-green proves nothing about real
|
|
198
|
+
mode. Finish with a real-mode spot check against a real environment (safe
|
|
199
|
+
mode on); if credentials block it, report "real-mode pending" — never imply
|
|
200
|
+
live parity you didn't prove.
|
|
201
|
+
|
|
202
|
+
## The bar
|
|
203
|
+
|
|
204
|
+
Someone who knows the original system should read the flow and recognise it
|
|
205
|
+
exactly — same steps, same decisions, same constants, same effects — with every
|
|
206
|
+
divergence explicitly marked. If they'd be surprised by a branch, you modeled it wrong.
|
|
207
|
+
For a subsystem migration the same bar holds system-wide, and both worlds must
|
|
208
|
+
work: every scenario green in mock mode, and the real path proven or explicitly
|
|
209
|
+
pending.
|