flujo-ai 3.16.0 → 3.17.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 (200) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/app-build-manifest.json +36 -36
  3. package/.next/app-path-routes-manifest.json +7 -7
  4. package/.next/build-manifest.json +2 -2
  5. package/.next/prerender-manifest.json +6 -6
  6. package/.next/server/app/_not-found/page.js +1 -1
  7. package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  8. package/.next/server/app/_not-found.html +1 -1
  9. package/.next/server/app/_not-found.rsc +1 -1
  10. package/.next/server/app/api/backup/route.js +1 -1
  11. package/.next/server/app/api/backup/route.js.nft.json +1 -1
  12. package/.next/server/app/api/backup/route_client-reference-manifest.js +1 -1
  13. package/.next/server/app/api/browse/route_client-reference-manifest.js +1 -1
  14. package/.next/server/app/api/cwd/route_client-reference-manifest.js +1 -1
  15. package/.next/server/app/api/encryption/secure/route.js.nft.json +1 -1
  16. package/.next/server/app/api/encryption/secure/route_client-reference-manifest.js +1 -1
  17. package/.next/server/app/api/env/route_client-reference-manifest.js +1 -1
  18. package/.next/server/app/api/flow/[id]/route.js +1 -1
  19. package/.next/server/app/api/flow/[id]/route.js.nft.json +1 -1
  20. package/.next/server/app/api/flow/[id]/route_client-reference-manifest.js +1 -1
  21. package/.next/server/app/api/flow/compile/route.js +1 -1
  22. package/.next/server/app/api/flow/compile/route.js.nft.json +1 -1
  23. package/.next/server/app/api/flow/compile/route_client-reference-manifest.js +1 -1
  24. package/.next/server/app/api/flow/generate/route.js +4 -2
  25. package/.next/server/app/api/flow/generate/route.js.nft.json +1 -1
  26. package/.next/server/app/api/flow/generate/route_client-reference-manifest.js +1 -1
  27. package/.next/server/app/api/flow/prompt-renderer/route.js +2 -2
  28. package/.next/server/app/api/flow/prompt-renderer/route.js.nft.json +1 -1
  29. package/.next/server/app/api/flow/prompt-renderer/route_client-reference-manifest.js +1 -1
  30. package/.next/server/app/api/flow/route.js +1 -1
  31. package/.next/server/app/api/flow/route.js.nft.json +1 -1
  32. package/.next/server/app/api/flow/route_client-reference-manifest.js +1 -1
  33. package/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
  34. package/.next/server/app/api/init/route.js.nft.json +1 -1
  35. package/.next/server/app/api/init/route_client-reference-manifest.js +1 -1
  36. package/.next/server/app/api/local-models/capability/route_client-reference-manifest.js +1 -1
  37. package/.next/server/app/api/local-models/pull/route_client-reference-manifest.js +1 -1
  38. package/.next/server/app/api/local-models/suggest/route_client-reference-manifest.js +1 -1
  39. package/.next/server/app/api/mcp/cancel/route.js.nft.json +1 -1
  40. package/.next/server/app/api/mcp/cancel/route_client-reference-manifest.js +1 -1
  41. package/.next/server/app/api/mcp/servers/[name]/prompts/get/route.js.nft.json +1 -1
  42. package/.next/server/app/api/mcp/servers/[name]/prompts/get/route_client-reference-manifest.js +1 -1
  43. package/.next/server/app/api/mcp/servers/[name]/prompts/route.js.nft.json +1 -1
  44. package/.next/server/app/api/mcp/servers/[name]/prompts/route_client-reference-manifest.js +1 -1
  45. package/.next/server/app/api/mcp/servers/[name]/resources/read/route.js.nft.json +1 -1
  46. package/.next/server/app/api/mcp/servers/[name]/resources/read/route_client-reference-manifest.js +1 -1
  47. package/.next/server/app/api/mcp/servers/[name]/resources/route.js.nft.json +1 -1
  48. package/.next/server/app/api/mcp/servers/[name]/resources/route_client-reference-manifest.js +1 -1
  49. package/.next/server/app/api/mcp/servers/[name]/route.js.nft.json +1 -1
  50. package/.next/server/app/api/mcp/servers/[name]/route_client-reference-manifest.js +1 -1
  51. package/.next/server/app/api/mcp/servers/[name]/status/route.js.nft.json +1 -1
  52. package/.next/server/app/api/mcp/servers/[name]/status/route_client-reference-manifest.js +1 -1
  53. package/.next/server/app/api/mcp/servers/[name]/tools/[toolName]/route.js.nft.json +1 -1
  54. package/.next/server/app/api/mcp/servers/[name]/tools/[toolName]/route_client-reference-manifest.js +1 -1
  55. package/.next/server/app/api/mcp/servers/[name]/tools/route.js.nft.json +1 -1
  56. package/.next/server/app/api/mcp/servers/[name]/tools/route_client-reference-manifest.js +1 -1
  57. package/.next/server/app/api/mcp/servers/route.js.nft.json +1 -1
  58. package/.next/server/app/api/mcp/servers/route_client-reference-manifest.js +1 -1
  59. package/.next/server/app/api/mcp/test-connection/route.js.nft.json +1 -1
  60. package/.next/server/app/api/mcp/test-connection/route_client-reference-manifest.js +1 -1
  61. package/.next/server/app/api/mcp/test-connection/stream/route.js.nft.json +1 -1
  62. package/.next/server/app/api/mcp/test-connection/stream/route_client-reference-manifest.js +1 -1
  63. package/.next/server/app/api/mcp-registry/route_client-reference-manifest.js +1 -1
  64. package/.next/server/app/api/mcp-registry/spotlight/route_client-reference-manifest.js +1 -1
  65. package/.next/server/app/api/model/[id]/route.js.nft.json +1 -1
  66. package/.next/server/app/api/model/[id]/route_client-reference-manifest.js +1 -1
  67. package/.next/server/app/api/model/provider/route.js.nft.json +1 -1
  68. package/.next/server/app/api/model/provider/route_client-reference-manifest.js +1 -1
  69. package/.next/server/app/api/model/route.js.nft.json +1 -1
  70. package/.next/server/app/api/model/route_client-reference-manifest.js +1 -1
  71. package/.next/server/app/api/model/test/route.js.nft.json +1 -1
  72. package/.next/server/app/api/model/test/route_client-reference-manifest.js +1 -1
  73. package/.next/server/app/api/oauth/callback/route_client-reference-manifest.js +1 -1
  74. package/.next/server/app/api/oauth/initiate/route_client-reference-manifest.js +1 -1
  75. package/.next/server/app/api/oauth/reset/route.js.nft.json +1 -1
  76. package/.next/server/app/api/oauth/reset/route_client-reference-manifest.js +1 -1
  77. package/.next/server/app/api/planned-executions/[id]/route.js.nft.json +1 -1
  78. package/.next/server/app/api/planned-executions/[id]/route_client-reference-manifest.js +1 -1
  79. package/.next/server/app/api/planned-executions/[id]/run/route.js.nft.json +1 -1
  80. package/.next/server/app/api/planned-executions/[id]/run/route_client-reference-manifest.js +1 -1
  81. package/.next/server/app/api/planned-executions/[id]/runs/route.js.nft.json +1 -1
  82. package/.next/server/app/api/planned-executions/[id]/runs/route_client-reference-manifest.js +1 -1
  83. package/.next/server/app/api/planned-executions/preview-schedule/route_client-reference-manifest.js +1 -1
  84. package/.next/server/app/api/planned-executions/route.js.nft.json +1 -1
  85. package/.next/server/app/api/planned-executions/route_client-reference-manifest.js +1 -1
  86. package/.next/server/app/api/restore/route.js +1 -1
  87. package/.next/server/app/api/restore/route.js.nft.json +1 -1
  88. package/.next/server/app/api/restore/route_client-reference-manifest.js +1 -1
  89. package/.next/server/app/api/storage/route_client-reference-manifest.js +1 -1
  90. package/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
  91. package/.next/server/app/api/webhooks/[id]/route.js.nft.json +1 -1
  92. package/.next/server/app/api/webhooks/[id]/route_client-reference-manifest.js +1 -1
  93. package/.next/server/app/chat/page_client-reference-manifest.js +1 -1
  94. package/.next/server/app/chat.html +1 -1
  95. package/.next/server/app/chat.rsc +1 -1
  96. package/.next/server/app/docs/page.js +2 -1
  97. package/.next/server/app/docs/page_client-reference-manifest.js +1 -1
  98. package/.next/server/app/docs.html +1 -1
  99. package/.next/server/app/docs.rsc +2 -2
  100. package/.next/server/app/executions/page.js +1 -1
  101. package/.next/server/app/executions/page_client-reference-manifest.js +1 -1
  102. package/.next/server/app/executions.html +1 -1
  103. package/.next/server/app/executions.rsc +1 -1
  104. package/.next/server/app/flows/page.js +2 -2
  105. package/.next/server/app/flows/page_client-reference-manifest.js +1 -1
  106. package/.next/server/app/flows.html +1 -1
  107. package/.next/server/app/flows.rsc +2 -2
  108. package/.next/server/app/index.html +1 -1
  109. package/.next/server/app/index.rsc +1 -1
  110. package/.next/server/app/mcp/page_client-reference-manifest.js +1 -1
  111. package/.next/server/app/mcp-flows/route.js +2 -2
  112. package/.next/server/app/mcp-flows/route.js.nft.json +1 -1
  113. package/.next/server/app/mcp-flows/route_client-reference-manifest.js +1 -1
  114. package/.next/server/app/mcp-proxy/[server]/route.js +1 -1
  115. package/.next/server/app/mcp-proxy/[server]/route.js.nft.json +1 -1
  116. package/.next/server/app/mcp-proxy/[server]/route_client-reference-manifest.js +1 -1
  117. package/.next/server/app/mcp.html +1 -1
  118. package/.next/server/app/mcp.rsc +1 -1
  119. package/.next/server/app/models/page.js +1 -1
  120. package/.next/server/app/models/page.js.nft.json +1 -1
  121. package/.next/server/app/models/page_client-reference-manifest.js +1 -1
  122. package/.next/server/app/page.js +1 -1
  123. package/.next/server/app/page_client-reference-manifest.js +1 -1
  124. package/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  125. package/.next/server/app/settings.html +1 -1
  126. package/.next/server/app/settings.rsc +1 -1
  127. package/.next/server/app/v1/chat/completions/route.js +1 -2
  128. package/.next/server/app/v1/chat/completions/route.js.nft.json +1 -1
  129. package/.next/server/app/v1/chat/completions/route_client-reference-manifest.js +1 -1
  130. package/.next/server/app/v1/chat/conversations/[conversationId]/breakpoints/route.js +2 -1
  131. package/.next/server/app/v1/chat/conversations/[conversationId]/breakpoints/route.js.nft.json +1 -1
  132. package/.next/server/app/v1/chat/conversations/[conversationId]/breakpoints/route_client-reference-manifest.js +1 -1
  133. package/.next/server/app/v1/chat/conversations/[conversationId]/cancel/route.js +2 -2
  134. package/.next/server/app/v1/chat/conversations/[conversationId]/cancel/route.js.nft.json +1 -1
  135. package/.next/server/app/v1/chat/conversations/[conversationId]/cancel/route_client-reference-manifest.js +1 -1
  136. package/.next/server/app/v1/chat/conversations/[conversationId]/debug/continue/route.js +1 -1
  137. package/.next/server/app/v1/chat/conversations/[conversationId]/debug/continue/route.js.nft.json +1 -1
  138. package/.next/server/app/v1/chat/conversations/[conversationId]/debug/continue/route_client-reference-manifest.js +1 -1
  139. package/.next/server/app/v1/chat/conversations/[conversationId]/debug/step/route.js +1 -1
  140. package/.next/server/app/v1/chat/conversations/[conversationId]/debug/step/route.js.nft.json +1 -1
  141. package/.next/server/app/v1/chat/conversations/[conversationId]/debug/step/route_client-reference-manifest.js +1 -1
  142. package/.next/server/app/v1/chat/conversations/[conversationId]/edit-state/route.js +2 -1
  143. package/.next/server/app/v1/chat/conversations/[conversationId]/edit-state/route.js.nft.json +1 -1
  144. package/.next/server/app/v1/chat/conversations/[conversationId]/edit-state/route_client-reference-manifest.js +1 -1
  145. package/.next/server/app/v1/chat/conversations/[conversationId]/events/route.js +2 -2
  146. package/.next/server/app/v1/chat/conversations/[conversationId]/events/route.js.nft.json +1 -1
  147. package/.next/server/app/v1/chat/conversations/[conversationId]/events/route_client-reference-manifest.js +1 -1
  148. package/.next/server/app/v1/chat/conversations/[conversationId]/respond/route.js +1 -1
  149. package/.next/server/app/v1/chat/conversations/[conversationId]/respond/route.js.nft.json +1 -1
  150. package/.next/server/app/v1/chat/conversations/[conversationId]/respond/route_client-reference-manifest.js +1 -1
  151. package/.next/server/app/v1/chat/conversations/[conversationId]/route.js +2 -2
  152. package/.next/server/app/v1/chat/conversations/[conversationId]/route.js.nft.json +1 -1
  153. package/.next/server/app/v1/chat/conversations/[conversationId]/route_client-reference-manifest.js +1 -1
  154. package/.next/server/app/v1/chat/conversations/route.js +2 -2
  155. package/.next/server/app/v1/chat/conversations/route.js.nft.json +1 -1
  156. package/.next/server/app/v1/chat/conversations/route_client-reference-manifest.js +1 -1
  157. package/.next/server/app/v1/models/route.js.nft.json +1 -1
  158. package/.next/server/app/v1/models/route_client-reference-manifest.js +1 -1
  159. package/.next/server/app-paths-manifest.json +7 -7
  160. package/.next/server/chunks/1403.js +2 -2
  161. package/.next/server/chunks/1519.js +1 -0
  162. package/.next/server/chunks/1877.js +2 -1
  163. package/.next/server/chunks/2776.js +1 -1
  164. package/.next/server/chunks/2859.js +3 -3
  165. package/.next/server/chunks/3243.js +1 -1
  166. package/.next/server/chunks/{9688.js → 3309.js} +2 -2
  167. package/.next/server/chunks/3370.js +38 -0
  168. package/.next/server/chunks/3984.js +4 -4
  169. package/.next/server/chunks/{8367.js → 407.js} +6 -2
  170. package/.next/server/chunks/4681.js +1 -1
  171. package/.next/server/chunks/5504.js +1 -1
  172. package/.next/server/chunks/6936.js +4 -4
  173. package/.next/server/chunks/7214.js +1 -1
  174. package/.next/server/chunks/724.js +1 -1
  175. package/.next/server/chunks/7362.js +3 -3
  176. package/.next/server/chunks/7506.js +1 -1
  177. package/.next/server/chunks/7635.js +2 -2
  178. package/.next/server/chunks/7821.js +1 -0
  179. package/.next/server/chunks/7982.js +1 -0
  180. package/.next/server/chunks/9509.js +2 -2
  181. package/.next/server/chunks/9957.js +1 -1
  182. package/.next/server/instrumentation.js.nft.json +1 -1
  183. package/.next/server/pages/404.html +1 -1
  184. package/.next/server/pages/500.html +1 -1
  185. package/.next/server/pages-manifest.json +1 -1
  186. package/.next/server/server-reference-manifest.js +1 -1
  187. package/.next/server/server-reference-manifest.json +1 -1
  188. package/.next/static/chunks/app/docs/page-fca894f60b9174e9.js +1 -0
  189. package/.next/static/chunks/app/flows/page-4e1ec20653e5dc86.js +1 -0
  190. package/.next/trace +7 -7
  191. package/README.md +1 -1
  192. package/package.json +1 -1
  193. package/.next/server/chunks/6195.js +0 -37
  194. package/.next/server/chunks/8126.js +0 -1
  195. package/.next/server/chunks/8312.js +0 -5
  196. package/.next/server/chunks/9989.js +0 -1
  197. package/.next/static/chunks/app/docs/page-1d349e2d82f7b104.js +0 -1
  198. package/.next/static/chunks/app/flows/page-375150edff9134a3.js +0 -1
  199. /package/.next/static/{z7fsG7kblb_yxkUhOxn-G → KMEKXonxBXI3WzCfozXx4}/_buildManifest.js +0 -0
  200. /package/.next/static/{z7fsG7kblb_yxkUhOxn-G → KMEKXonxBXI3WzCfozXx4}/_ssgManifest.js +0 -0
package/README.md CHANGED
@@ -37,7 +37,7 @@ For *anything* that you struggle with (MCP Installation, Application Issues, Usa
37
37
  # FLUJO
38
38
 
39
39
  [![MIT License](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
40
- [![Version](https://img.shields.io/badge/version-3.16.0-green.svg)](package.json)
40
+ [![Version](https://img.shields.io/badge/version-3.17.0-green.svg)](package.json)
41
41
 
42
42
  FLUJO is an open-source, local-first platform for building **MCP-powered AI workflows**. It brings together model management, Model-Context-Protocol (MCP) servers, a visual flow builder, and a chat interface in one app — so you can wire models and tools together, run them headlessly on triggers, and expose the result to other apps, without giving up control of your keys and data.
43
43
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "flujo-ai",
3
- "version": "3.16.0",
3
+ "version": "3.17.0",
4
4
  "bin": {
5
5
  "flujo": "bin/flujo.mjs"
6
6
  },
@@ -1,37 +0,0 @@
1
- "use strict";exports.id=6195,exports.ids=[6195],exports.modules={76195:(a,b,c)=>{c.d(b,{internalCallTool:()=>ac,internalToolDefinitions:()=>R});var d=c(33873),e=c.n(d),f=c(79646),g=c(18230),h=c(72440),i=c(12763),j=c(71188),k=c(67463),l=c(63129);let m=`A FlowSpec is a JSON object describing a FLUJO flow semantically — node keys and edges, no coordinates, no ids, no layout:
2
- {
3
- "name": "short_flow_name", // letters/digits/_/- only
4
- "description": "what the flow does",
5
- "nodes": [ ... ],
6
- "edges": [ ... ]
7
- }
8
-
9
- NODE TYPES:
10
- - { "key": "unique_key", "type": "start", "label": "...", "prompt": "system-level instructions for the whole flow" }
11
- - { "key": "...", "type": "process", "label": "...", "description": "what this step does",
12
- "model": "<model id or name from the catalog>",
13
- "prompt": "instructions for this step",
14
- "servers": [ { "name": "<server name>", "tools": ["tool_a"] } ], // optional; omit "tools" to enable all
15
- "inputMode": "full-history" | "latest-message" | "isolated", // optional, default full-history
16
- "isolatedPrompt": "..." } // only with inputMode "isolated"
17
- - { "key": "...", "type": "subflow", "label": "...", "flow": "<existing flow name or id>",
18
- "inputMode": "full-history" | "latest-message" | "isolated",
19
- "outputMode": "steps" | "final-only" }
20
- - { "key": "...", "type": "finish", "label": "..." }
21
-
22
- EDGES: { "from": "<node key>", "to": "<node key>", "bidirectional": true|false }
23
-
24
- RULES:
25
- 1. Exactly ONE start node; at least one finish node reachable from it.
26
- 2. Every process node MUST reference a configured model (by id, display name, or name).
27
- 3. A process step uses MCP tools ONLY via its "servers" list — never emit nodes of type "mcp".
28
- 4. Do not embed \${tool:...} or \${resource:...} references in prompts — tools are wired through "servers".
29
- 5. Branching: give a process node multiple outgoing edges; its model decides where to hand off at runtime. "bidirectional": true lets the target hand back to the source (agent <-> agent).
30
- 6. A subflow node may have only ONE outgoing edge, and its "flow" must name an existing flow.
31
- 7. Keep flows minimal — only the steps the task needs. Write clear, specific prompts and labels; fill "description" on process nodes.`;var n=c(89850),o=c(8336);let p={type:"arrowclosed",width:20,height:20,color:"#1976d2"},q={stroke:"#1976d2",strokeWidth:2},r=new Set(["full-history","latest-message","isolated"]),s=new Set(["steps","final-only"]);function t(a){let b=(0,o.A$)(a);if(0===b.length)return{text:a,stripped:!1};let c="",d=0;for(let e of b)c+=a.slice(d,e.index)+e.name,d=e.index+e.fullMatch.length;return{text:c+=a.slice(d),stripped:!0}}function u(a,b){let c=b.filter(a=>"error"===a.severity).length;return{flow:a,issues:b,errorCount:c,warningCount:b.length-c}}var v=c(54898),w=c(99924);let x=(0,g.h)("backend/services/flow/generationContext");function y(a,b){let c=String(a).replace(/\s+/g," ").trim();return c.length<=b?c:`${c.slice(0,b).trimEnd()}…`}async function z(){let a=await i.modelService.loadModels(),b=a.map(a=>({id:a.id,name:a.name,...a.displayName?{displayName:a.displayName}:{},...a.description?{description:y(a.description,100)}:{}})),c=[],d={};try{let a=await w.mcpService.loadServerConfigs();if(Array.isArray(a))for(let b of a){if(b.disabled)continue;let a=!1;try{let c=await w.mcpService.getServerStatus(b.name);a=c?.status==="connected"}catch(a){x.debug(`getServerStatus failed for ${b.name}; listing name only`,a)}if(!a){c.push({name:b.name,connected:!1});continue}let{tools:e}=await w.mcpService.listServerTools(b.name),f=(e??[]).filter(a=>a.name).map(a=>({name:a.name,...a.description?{description:y(a.description,100)}:{}}));d[b.name]=f.map(a=>a.name),c.push({name:b.name,connected:!0,tools:f})}}catch(a){x.warn("Could not load MCP servers for the authoring context",a)}let e=[];try{e=await h.flowService.loadFlows()}catch(a){x.warn("Could not load flows for the authoring context",a)}let f={models:b,servers:c,flows:e.map(a=>({id:a.id,name:a.name,...a.description?{description:y(a.description,300)}:{},nodeCount:a.nodes?.length??0}))};return{blocks:f,compile:{models:a.map(a=>({id:a.id,name:a.name,displayName:a.displayName})),servers:c.map(a=>({name:a.name})),serverTools:d,flows:e.map(a=>({id:a.id,name:a.name}))},catalog:function(a){let b=[];for(let c of(b.push("AVAILABLE MODELS (reference by id or name):"),0===a.models.length&&b.push(" (none configured)"),a.models)){let a=c.displayName&&c.displayName!==c.name?`"${c.displayName}" (${c.name})`:c.name,d=c.description?` — ${c.description}`:"";b.push(` - id: ${c.id} — ${a}${d}`)}for(let c of(b.push("",'AVAILABLE MCP SERVERS (attach to process nodes via "servers"):'),0===a.servers.length&&b.push(" (none configured)"),a.servers)){if(!c.connected){b.push(` - ${c.name} (offline — tools unknown; may still be used)`);continue}b.push(` - ${c.name}:`);let a=(c.tools??[]).slice(0,20);for(let c of a){let a=c.description?` — ${c.description}`:"";b.push(` - ${c.name}${a}`)}let d=(c.tools?.length??0)-a.length;d>0&&b.push(` - …and ${d} more`)}for(let c of(b.push("",'EXISTING FLOWS (usable as subflow targets via "flow"):'),0===a.flows.length&&b.push(" (none)"),a.flows.slice(0,15))){let a=c.description?` — ${c.description}`:` — ${c.nodeCount} nodes`;b.push(` - "${c.name}"${a}`)}return a.flows.length>15&&b.push(` - …and ${a.flows.length-15} more`),b.join("\n")}(f),validatorServers:c.map(a=>({name:a.name,status:a.connected?"connected":"disconnected"}))}}let A=(0,g.h)("backend/services/flow/compileFlow");async function B(a,b={}){if(!a||"object"!=typeof a||Array.isArray(a))return{success:!1,error:"The spec must be a JSON object (a FlowSpec)",statusCode:400};let c=await z(),d=function(a,b={}){let c=[],d=(a,b,d)=>c.push({severity:"error",code:a,message:b,nodeKey:d}),e=(a,b,d)=>c.push({severity:"warning",code:a,message:b,nodeKey:d}),f=b.models??[],g=new Set((b.servers??[]).map(a=>a.name)),h=b.serverTools??{},i=b.flows??[],j=Array.isArray(a?.nodes)?a.nodes:[],k=Array.isArray(a?.edges)?a.edges:[],l=new Map,m=[],o=[];for(let a of j){let b=a?.key;if(!b||"string"!=typeof b){d("node-missing-key",'A node is missing its "key" — every node needs a unique key.');continue}if(l.has(b)){d("node-duplicate-key",`Duplicate node key "${b}" — keys must be unique; the later node was dropped.`,b);continue}let c=a.type;if("mcp"===c){d("mcp-node-not-allowed",`Node "${b}": do not emit "mcp" nodes — attach servers to a process node via its "servers" list instead.`,b);continue}if("start"!==c&&"process"!==c&&"finish"!==c&&"subflow"!==c){d("unknown-node-type",`Node "${b}" has unknown type "${String(c)}".`,b);continue}let j={},k="string"==typeof a.prompt?a.prompt:void 0;if("start"===c){let{text:a,stripped:c}=t(k??"");c&&e("pill-stripped",`Node "${b}": binding pills are not supported in generated prompts and were replaced with plain names.`,b),j.promptTemplate=a}else if("process"===c){let{text:c,stripped:d}=t(k??"");if(d&&e("pill-stripped",`Node "${b}": binding pills are not supported in generated prompts and were replaced with plain names.`,b),j.promptTemplate=c,a.model){let c=function(a,b){if(b.some(b=>b.id===a))return a;let c=a.toLowerCase(),d=b.find(a=>a.displayName?.toLowerCase()===c);if(d)return d.id;let e=b.find(a=>a.name?.toLowerCase()===c);return e?e.id:null}(a.model,f);c?j.boundModel=c:(j.boundModel=a.model,e("model-unresolved",`Node "${b}": model "${a.model}" does not match any configured model (by id, display name, or name).`,b))}void 0!==a.inputMode&&(r.has(a.inputMode)?(j.inputMode=a.inputMode,"isolated"===a.inputMode&&"string"==typeof a.isolatedPrompt&&(j.isolatedPrompt=a.isolatedPrompt)):e("invalid-input-mode",`Node "${b}": inputMode "${String(a.inputMode)}" is not valid (full-history | latest-message | isolated); omitted.`,b))}else if("subflow"===c){if(a.flow){let c=function(a,b){if(b.some(b=>b.id===a))return a;let c=a.toLowerCase(),d=b.find(a=>a.name.toLowerCase()===c);return d?d.id:null}(a.flow,i);c?j.subflowId=c:d("subflow-unresolved",`Node "${b}": flow "${a.flow}" does not match any existing flow (by id or name).`,b)}else d("subflow-missing-flow",`Node "${b}": subflow nodes need a "flow" (name or id of an existing flow).`,b);if(void 0!==a.inputMode&&(r.has(a.inputMode)?j.inputMode=a.inputMode:e("invalid-input-mode",`Node "${b}": inputMode "${String(a.inputMode)}" is not valid (full-history | latest-message | isolated); omitted.`,b)),void 0!==a.outputMode&&(s.has(a.outputMode)?j.outputMode=a.outputMode:e("invalid-output-mode",`Node "${b}": outputMode "${String(a.outputMode)}" is not valid (steps | final-only); omitted.`,b)),void 0!==k){let{text:a,stripped:c}=t(k);c&&e("pill-stripped",`Node "${b}": binding pills are not supported in generated prompts and were replaced with plain names.`,b),j.promptTemplate=a}}let p={id:(0,n.A)(),type:c,position:{x:0,y:0},data:{label:a.label||`${c.charAt(0).toUpperCase()}${c.slice(1)} Node`,type:c,...a.description?{description:a.description}:{},properties:j}};if(l.set(b,p),m.push(p),"process"===c&&Array.isArray(a.servers)){let c=new Set;for(let d of a.servers){let a,f=d?.name;if(!f||"string"!=typeof f){e("server-missing-name",`Node "${b}": a server reference is missing its "name"; skipped.`,b);continue}if(c.has(f))continue;c.add(f),g.has(f)||e("server-unknown",`Node "${b}": MCP server "${f}" is not configured in FLUJO.`,b);let i=h[f];if(Array.isArray(d.tools)){if(a=d.tools.filter(a=>"string"==typeof a&&a),i){let c=a.filter(a=>!i.includes(a));c.length>0&&e("tool-unknown",`Node "${b}": server "${f}" does not report tool(s): ${c.join(", ")}.`,b)}}else a=i?[...i]:[];let j={id:(0,n.A)(),type:"mcp",position:{x:0,y:0},data:{label:f,type:"mcp",properties:{boundServer:f,enabledTools:a}}};m.push(j),o.push({processKey:b,mcpNode:j})}}else"process"!==c&&Array.isArray(a.servers)&&a.servers.length>0&&e("servers-on-non-process",`Node "${b}": only process nodes can have "servers"; ignored.`,b)}if(0===l.size)return d("no-usable-nodes","No usable nodes — the spec must contain at least a start node and one step."),u(null,c);let v=[],w=new Set;for(let a of k){let b=a?.from,c=a?.to,f=b?l.get(b):void 0,g=c?l.get(c):void 0;if(!f||!g){d("edge-unknown-node",`Edge "${String(b)}" -> "${String(c)}" references a node key that does not exist (or was dropped).`);continue}if(f===g){d("edge-self-loop",`Edge "${b}" -> "${c}": a node cannot connect to itself.`);continue}if("start"===g.type){d("edge-into-start",`Edge "${b}" -> "${c}": nothing may connect INTO a start node.`);continue}if("finish"===f.type){d("edge-out-of-finish",`Edge "${b}" -> "${c}": nothing may connect OUT OF a finish node.`);continue}let h=`${b}->${c}`;if(w.has(h)){e("edge-duplicate",`Edge "${b}" -> "${c}" appears more than once; kept the first.`);continue}w.add(h);let i=!0===a.bidirectional;i&&("start"===f.type||"finish"===g.type)&&(e("bidirectional-illegal",`Edge "${b}" -> "${c}" cannot be bidirectional; downgraded to one-way.`),i=!1),v.push(function(a,b,c){let d=`${a.type}-bottom`,e=`${b.type}-top`;return{id:`${a.id}:${d}->${b.id}:${e}`,source:a.id,sourceHandle:d,target:b.id,targetHandle:e,type:"custom",data:{edgeType:"standard",...c?{bidirectional:!0}:{}},animated:!0}}(f,g,i))}for(let{processKey:a,mcpNode:b}of o){let c=l.get(a);v.push(function(a,b){let c="process-right-mcp",d="mcp-left";return{id:`${a.id}:${c}->${b.id}:${d}`,source:a.id,sourceHandle:c,target:b.id,targetHandle:d,type:"mcpEdge",data:{edgeType:"mcp"},animated:!1,markerEnd:{...p},markerStart:{...p},style:{...q}}}(c,b))}!function(a,b,c,d){let e=new Map;for(let a of c)a.data?.edgeType!=="mcp"&&(e.has(a.source)||e.set(a.source,[]),e.get(a.source).push(a.target));let f=[...b.values()],g=f.filter(a=>"start"===a.type),h=g.length>0?g:f.slice(0,1),i=new Map,j=h.map(a=>({id:a.id,depth:0}));for(let a of j)i.set(a.id,0);for(;j.length;){let{id:a,depth:b}=j.shift();for(let c of e.get(a)??[])i.has(c)||(i.set(c,b+1),j.push({id:c,depth:b+1}))}let k=0;for(let a of i.values())k=Math.max(k,a);for(let a of f)i.has(a.id)||i.set(a.id,k+1);let l=new Map;for(let a of f){let b=i.get(a.id),c=l.get(b)??0;l.set(b,c+1),a.position={x:250+280*c,y:150+170*b}}let m=new Map;for(let{processKey:a,mcpNode:c}of d){let d=b.get(a),e=m.get(a)??0;m.set(a,e+1),c.position={x:d.position.x+320,y:d.position.y+150*e}}}(0,l,v,o);let x=i.map(a=>a.name);return u({id:(0,n.A)(),name:function(a,b){let c=(a??"").trim().replace(/[^\w-]+/g,"_").replace(/^_+|_+$/g,"");c||(c="generated_flow");let d=new Set(b.map(a=>a.toLowerCase()));if(!d.has(c.toLowerCase()))return c;for(let a=2;;a++){let b=`${c}_${a}`;if(!d.has(b.toLowerCase()))return b}}(a?.name,x),...a?.description?{description:a.description}:{},nodes:m,edges:v},c)}(a,c.compile);if(!d.flow)return{success:!1,error:"The spec could not be compiled into a flow",statusCode:422,issues:d.issues.map(a=>({severity:a.severity,code:a.code,message:a.message}))};let e=function(a,b){let c=[...a.map(a=>({severity:a.severity,code:a.code,message:a.message})),...b.issues],d=c.filter(a=>"error"===a.severity).length;return{issues:c,errorCount:d,warningCount:c.length-d,isRunnable:0===d}}(d.issues,(0,v.nC)(d.flow,{models:c.compile.models,servers:c.validatorServers,serverTools:c.compile.serverTools})),f=!1;return b.save&&0===e.errorCount?(await h.flowService.saveFlow(d.flow),f=!0,A.info(`Compiled and saved flow "${d.flow.name}" (${d.flow.id})`)):b.save&&A.info(`Compiled flow "${d.flow.name}" has ${e.errorCount} error(s); NOT saved`),{success:!0,flow:d.flow,validation:e,saved:f}}var C=c(12700),D=c(79861);let E=(0,g.h)("backend/services/mcp/registryInstall"),F="/v0.1/servers";async function G(a,b=8){let c=new URL(C.jD+F);c.searchParams.set("version","latest"),c.searchParams.set("limit",String(Math.min(Math.max(b,1),30))),a&&c.searchParams.set("search",a);let d=await (0,C.Ws)(c,15e3);return(Array.isArray(d?.servers)?d.servers:[]).map(a=>(function(a){let b=(0,D.AH)(a),c=b[0];return{name:a.name,...a.title?{title:a.title}:{},...a.description?{description:a.description}:{},installable:b.length>0,requiredEnv:c?(0,D.jg)(c):[]}})(a.server))}async function H(a){let b=new URL(C.jD+F);b.searchParams.set("version","latest"),b.searchParams.set("limit","10"),b.searchParams.set("search",a);let c=await (0,C.Ws)(b,15e3),d=Array.isArray(c?.servers)?c.servers:[],e=d.find(b=>b.server?.name===a);return(e??d[0])?.server??null}async function I(a,b){let c;if(!a||"string"!=typeof a)return{installed:!1,error:"A registry server name is required"};try{c=await H(a)}catch(a){return{installed:!1,error:`Registry lookup failed: ${a instanceof Error?a.message:String(a)}`}}if(!c)return{installed:!1,error:`No registry entry found for "${a}"`};let d=(0,D.AH)(c)[0];if(!d)return{installed:!1,error:`"${c.name}" has no install method FLUJO supports (stdio package or HTTP remote)`};let e=(0,D.jg)(d,b);if(e.length>0)return{installed:!1,needsEnv:e,error:`"${c.name}" requires values for: ${e.join(", ")}`};let f=(0,D.cy)((0,D.Yr)(c,d),b),g=f.name,h=await w.mcpService.loadServerConfigs();if(Array.isArray(h)&&h.some(a=>a.name===g)){E.info(`installRegistryServer: "${g}" already configured; reusing`);let{tools:a,error:b}=await w.mcpService.listServerTools(g);return{installed:!0,alreadyExisted:!0,serverName:g,tools:(a??[]).map(a=>({name:a.name,...a.description?{description:a.description}:{}})),...b?{error:b}:{}}}E.info(`installRegistryServer: installing "${c.name}" as "${g}" (${d.kind})`);let i=await w.mcpService.updateServerConfig(g,f);if(!Array.isArray(i)&&i&&"success"in i&&!1===i.success)return{installed:!1,error:`Saving the server failed: ${i.error??"unknown error"}`};let{tools:j,error:k}=await w.mcpService.listServerTools(g);return{installed:!0,serverName:g,tools:(j??[]).map(a=>({name:a.name,...a.description?{description:a.description}:{}})),...k?{error:k}:{}}}let J=(0,g.h)("backend/services/mcp/flowAuthoringTools"),K=["list_flow_building_blocks","validate_flow_spec","create_flow","search_mcp_marketplace","install_mcp_server"];function L(){return{type:"object",properties:{spec:{type:"object",description:"The FlowSpec object (see the tool description for the format)."}},required:["spec"]}}function M(a,b=!1){return{content:[{type:"text",text:JSON.stringify(a,null,2)}],...b?{isError:!0}:{}}}async function N(a,b){try{if("list_flow_building_blocks"===a){let a=await z();return M(a.blocks)}if("search_mcp_marketplace"===a){let a="string"==typeof b?.query?b.query:"",c=await G(a);return M(c)}if("install_mcp_server"===a){let a="string"==typeof b?.name?b.name:"",c=b?.env&&"object"==typeof b.env&&!Array.isArray(b.env)?b.env:void 0,d=await I(a,c);return M(d,!d.installed)}if("validate_flow_spec"===a||"create_flow"===a){let c=function(a){let b=a?.spec;if("string"==typeof b)try{return JSON.parse(b)}catch{return null}return b??null}(b);if(!c)return M({error:'Provide a "spec" argument: a FlowSpec object (or a JSON string of one).'},!0);let d=await B(c,{save:"create_flow"===a});if(!d.success)return M({error:d.error,issues:d.issues??[]},!0);let e={flowId:d.flow.id,flowName:d.flow.name,nodeCount:d.flow.nodes.length,edgeCount:d.flow.edges.length,validation:d.validation,..."create_flow"===a?{saved:d.saved,...d.saved?{note:`Flow "${d.flow.name}" was created. It is callable as a tool on this MCP server (after a fresh tools/list) and as model "flow-${d.flow.name}" on the OpenAI-compatible endpoint.`}:{note:"NOT saved: validation found errors. Fix the issues and call create_flow again."}}:{}};return M(e,"create_flow"===a&&!d.saved)}return M({error:`Unknown authoring tool: ${a}`},!0)}catch(b){return J.error("authoringCallTool failed",{toolName:a,err:b}),M({error:`Authoring tool failed: ${b instanceof Error?b.message:String(b)}`},!0)}}var O=c(92018);let P=(0,g.h)("backend/services/mcp/internalTools");function Q(a,b=!1){return{content:[{type:"text",text:"string"==typeof a?a:JSON.stringify(a,null,2)}],...b?{isError:!0}:{}}}function R(){return[{name:"list_flow_building_blocks",description:'List everything a FlowSpec may reference in this FLUJO instance: configured models (bind to process steps), MCP servers and their tools (attach via "servers"), and existing flows (usable as subflow targets). Call this before authoring a spec.',inputSchema:{type:"object",properties:{}}},{name:"validate_flow_spec",description:`Compile and validate a FlowSpec WITHOUT saving. Returns the compiled flow's validation result (errors block saving; warnings are advisory) so you can iterate until clean, then call create_flow.
32
-
33
- ${m}`,inputSchema:L()},{name:"create_flow",description:`Create a new FLUJO flow from a FlowSpec. The spec is compiled deterministically (layout, ids, and wiring are generated for you) and saved ONLY when validation finds zero errors — otherwise the issues are returned to fix. Use list_flow_building_blocks first to see the models, MCP servers/tools, and existing flows you may reference.
34
-
35
- ${m}`,inputSchema:L()},{name:"search_mcp_marketplace",description:"Search the public MCP server registry for new capabilities (voice, browsing, files, email, vision, …). The registry matches the query against server NAMES only (substring) — use short single terms and try several. Returns name, description, whether FLUJO can install it, and which env vars/keys it would require.",inputSchema:{type:"object",properties:{query:{type:"string",description:"Short search term matched against server names."}},required:["query"]}},{name:"install_mcp_server",description:'Install an MCP server from the public registry (by the exact name returned by search_mcp_marketplace) and connect it — this DOWNLOADS AND RUNS a third-party package on the FLUJO host. Returns the FLUJO server name (reference it in FlowSpec "servers") and the tools it provides, or needsEnv when required keys are missing (supply values via the optional "env" argument, or pick a keyless alternative). First install can take minutes (package download).',inputSchema:{type:"object",properties:{name:{type:"string",description:'Exact registry name from search results, e.g. "ai.keenable/web-search".'},env:{type:"object",description:"Optional env var values for the server (e.g. required API keys).",additionalProperties:{type:"string"}}},required:["name"]}},{name:"execute_flow",description:"Run another FLUJO flow (by name or id) with the given input and return its final output. The run is ephemeral (no chat conversation is created). Use list_flow_building_blocks to see the available flows. Nested runs are limited in depth — a flow cannot recurse through itself indefinitely.",inputSchema:{type:"object",properties:{flow:{type:"string",description:"Flow name or flow id."},input:{type:"string",description:"The message to send to the flow as the user turn."}},required:["flow","input"]}},{name:"delete_flow",description:"PERMANENTLY delete a FLUJO flow (by name or id). This cannot be undone. Verify the target with list_flow_building_blocks first.",inputSchema:{type:"object",properties:{flow:{type:"string",description:"Flow name or flow id."}},required:["flow"]}},{name:"list_mcp_servers",description:"List the MCP servers configured in this FLUJO instance with their transport, enabled/disabled state and live connection status. Config details (env vars, headers, credentials) are never included.",inputSchema:{type:"object",properties:{}}},{name:"list_mcp_server_tools",description:"List the tools of one configured MCP server (name, description, input schema). Use together with call_mcp_tool for servers that are not bound to this flow.",inputSchema:{type:"object",properties:{server:{type:"string",description:"The FLUJO server name (see list_mcp_servers)."}},required:["server"]}},{name:"call_mcp_tool",description:"Call a tool on any configured MCP server by server name + tool name. This lets you use servers that are not bound to the current flow. Check the tool's input schema with list_mcp_server_tools first.",inputSchema:{type:"object",properties:{server:{type:"string",description:"The FLUJO server name."},tool:{type:"string",description:"The tool name on that server."},args:{type:"object",description:"Arguments for the tool (per its input schema)."},timeout:{type:"number",description:"Optional timeout in seconds."}},required:["server","tool"]}},{name:"restart_mcp_server",description:"Force-reconnect a configured MCP server (tears the connection down and rebuilds it). Useful when a server is in an error state after a config or environment change.",inputSchema:{type:"object",properties:{server:{type:"string",description:"The FLUJO server name."}},required:["server"]}},{name:"set_mcp_server_enabled",description:"Enable or disable a configured MCP server. Disabling disconnects it and prevents any further use; enabling connects it.",inputSchema:{type:"object",properties:{server:{type:"string",description:"The FLUJO server name."},enabled:{type:"boolean",description:"true to enable + connect, false to disable + disconnect."}},required:["server","enabled"]}},{name:"list_models",description:"List the models configured in this FLUJO instance (id, name, display name, description, provider, base URL, context window). API keys are never included. Reference models by id or name in FlowSpecs.",inputSchema:{type:"object",properties:{}}},{name:"list_planned_executions",description:"List the planned (scheduled/triggered) executions in this FLUJO instance with their trigger type, enabled state, armed status and last run outcome.",inputSchema:{type:"object",properties:{}}},{name:"run_planned_execution",description:"Fire a planned execution immediately (by id, see list_planned_executions) and return the run record with its output.",inputSchema:{type:"object",properties:{id:{type:"string",description:"The planned execution id."}},required:["id"]}},{name:"terminal",description:'Run a shell command on the FLUJO host and return its combined stdout/stderr plus exit code. Runs through a shell, so pipes and chained commands (e.g. "npm install && npm run build") work. Use it to install dependencies, build a cloned MCP server, inspect the filesystem, or run diagnostics. Working directory defaults to the FLUJO data directory (where cloned servers live under mcp-servers/); pass "cwd" to run elsewhere. The command is killed if it exceeds the timeout, and very large output is truncated.',inputSchema:{type:"object",properties:{command:{type:"string",description:'The shell command line to execute, e.g. "npm install && npm run build".'},cwd:{type:"string",description:"Optional working directory. Relative paths resolve against the FLUJO data directory. Defaults to the data directory."},timeout:{type:"number",description:"Optional timeout in seconds (default 60, max 600). The command is killed if it runs longer."}},required:["command"]}}]}async function S(a){let b=await h.flowService.loadFlows();return b.find(b=>b.id===a)??b.find(b=>b.name===a)}async function T(a){let b=String(a?.flow??"").trim();if(!b)return Q({error:'Provide "flow": a flow name or id.'},!0);let c=await S(b);if(!c)return Q({error:`No flow named or with id "${b}". Use list_flow_building_blocks to see the available flows.`},!0);let d=global.__flujo_internal_flow_depth??0;if(d>=4)return Q({error:`execute_flow nesting limit (4) reached — refusing to start "${c.name}" to prevent runaway recursion.`},!0);global.__flujo_internal_flow_depth=d+1;try{let b=await (0,k.runFlow)({flowId:c.id,prompt:String(a?.input??""),mode:"ephemeral",flujo:!0,requireApproval:!1});if(b.flowNotFound)return Q({error:`Flow not found: ${b.flowNotFound.name}`},!0);if("error"===b.status)return Q({error:b.error?.message??"Unknown error during flow execution."},!0);return Q(b.outputText??"")}finally{global.__flujo_internal_flow_depth=d}}async function U(a){let b=String(a?.flow??"").trim();if(!b)return Q({error:'Provide "flow": a flow name or id.'},!0);let c=await S(b);if(!c)return Q({error:`No flow named or with id "${b}".`},!0);let d=await h.flowService.deleteFlow(c.id);return d.success?Q({deleted:!0,flowId:c.id,flowName:c.name}):Q({error:d.error??`Failed to delete flow "${c.name}".`},!0)}async function V(a){let b=await a.loadServerConfigs();return Array.isArray(b)?Q(await Promise.all(b.map(async b=>{let c="unknown";try{c=(await a.getServerStatus(b.name)).status}catch(a){P.debug(`list_mcp_servers: getServerStatus failed for ${b.name}`,a)}return{name:b.name,transport:b.transport,enabled:!b.disabled,status:c,...b.builtIn?{builtIn:!0}:{}}}))):Q({error:b.error??"Failed to load server configs."},!0)}async function W(a,b){let c=String(b?.server??"").trim();if(!c)return Q({error:'Provide "server": a FLUJO server name.'},!0);if(c===O._)return Q(R().map(a=>({name:a.name,description:a.description,inputSchema:a.inputSchema})));let{tools:d,error:e}=await a.listServerTools(c);return e?Q({error:e},!0):Q(d.map(a=>({name:a.name,description:a.description,inputSchema:a.inputSchema})))}async function X(a,b){let c=String(b?.server??"").trim(),d=String(b?.tool??"").trim();if(!c||!d)return Q({error:'Provide "server" and "tool".'},!0);if(c===O._)return Q({error:`"${O._}" is this server — call its tools directly instead of through call_mcp_tool.`},!0);let e=b?.args&&"object"==typeof b.args&&!Array.isArray(b.args)?b.args:{},f="number"==typeof b?.timeout?b.timeout:void 0,g=await a.callTool(c,d,e,f);if(!g.success)return Q({error:g.error??`Tool call failed on ${c}.`},!0);let h=g.data;return h&&Array.isArray(h.content)?h:Q(h??{ok:!0})}async function Y(a,b){let c=String(b?.server??"").trim();if(!c)return Q({error:'Provide "server": a FLUJO server name.'},!0);if(c===O._)return Q({ok:!0,note:`"${O._}" is the built-in server — it is always running.`});let d=await a.forceReconnect(c);if(!d.success)return Q({error:d.error??`Failed to restart ${c}.`},!0);let e=await a.getServerStatus(c);return Q({restarted:!0,server:c,status:e.status})}async function Z(a,b){let c=String(b?.server??"").trim(),d=b?.enabled;if(!c||"boolean"!=typeof d)return Q({error:'Provide "server" (string) and "enabled" (boolean).'},!0);if(c===O._)return Q({error:`The built-in "${O._}" server cannot be disabled.`},!0);let e=await a.updateServerConfig(c,{disabled:!d});return"error"in e?Q({error:e.error},!0):Q({server:c,enabled:d})}async function $(){return Q((await i.modelService.loadModels()).map(a=>({id:a.id,name:a.name,...a.displayName?{displayName:a.displayName}:{},...a.description?{description:a.description}:{},...a.provider?{provider:a.provider}:{},...a.baseUrl?{baseUrl:a.baseUrl}:{},...a.contextWindow?{contextWindow:a.contextWindow}:{}})))}async function _(){return Q((await (0,j.z)().list()).map(({execution:a,status:b,lastRun:c})=>({id:a.id,name:a.name,enabled:a.enabled,flowId:a.flowId,triggerType:a.trigger?.type,armed:b?.armed??!1,...c?{lastRun:{status:c.status,firedAt:c.firedAt,...c.error?{error:c.error}:{}}}:{}})))}async function aa(a){let b=String(a?.id??"").trim();if(!b)return Q({error:'Provide "id": a planned execution id (see list_planned_executions).'},!0);let{record:c,error:d}=await (0,j.z)().runNow(b);return d||!c?Q({error:d??"Run failed."},!0):Q({runId:c.runId,status:c.status,firedAt:c.firedAt,...c.finishedAt?{finishedAt:c.finishedAt}:{},...c.outputText?{outputText:c.outputText}:{},...c.error?{error:c.error}:{}})}async function ab(a){let b=String(a?.command??"").trim();if(!b)return Q({error:'Provide "command": a shell command line to run.'},!0);let c=(0,l.ok)(),d="string"==typeof a?.cwd?a.cwd.trim():"",g=d?e().isAbsolute(d)?d:e().join(c,d):c,h=Math.min(1e3*("number"==typeof a?.timeout&&a.timeout>0?a.timeout:60),6e5);return await new Promise(a=>{let c,d="",e=!1,i=!1,j=!1,k=a=>{!e&&(d+=a).length>1e5&&(d=d.slice(0,1e5)+"\n…[output truncated]",e=!0)};try{c=(0,f.spawn)(b,{cwd:g,shell:!0,env:process.env})}catch(b){a(Q({error:`Failed to start command: ${b instanceof Error?b.message:String(b)}`,cwd:g},!0));return}let l=setTimeout(()=>{j=!0;try{c.kill()}catch{}},h),m=b=>{i||(i=!0,clearTimeout(l),a(b))};c.stdout?.on("data",a=>k(a.toString())),c.stderr?.on("data",a=>k(a.toString())),c.on("error",a=>{k(`
36
- ${a.message}`),m(Q({error:`Command failed to start: ${a.message}`,cwd:g,output:d},!0))}),c.on("close",a=>{if(j)return void m(Q({timedOut:!0,cwd:g,exitCode:a,output:`${d}
37
- [killed after ${h/1e3}s timeout]`},!0));m(Q({exitCode:a,cwd:g,output:d},0!==a))})})}async function ac(a,b,c){try{if(K.includes(b))return await N(b,c);switch(b){case"execute_flow":return await T(c);case"delete_flow":return await U(c);case"list_mcp_servers":return await V(a);case"list_mcp_server_tools":return await W(a,c);case"call_mcp_tool":return await X(a,c);case"restart_mcp_server":return await Y(a,c);case"set_mcp_server_enabled":return await Z(a,c);case"list_models":return await $();case"list_planned_executions":return await _();case"run_planned_execution":return await aa(c);case"terminal":return await ab(c);default:return Q({error:`Unknown tool on the built-in FLUJO server: ${b}`},!0)}}catch(a){return P.error("internalCallTool failed",{toolName:b,err:a}),Q({error:`Tool failed: ${a instanceof Error?a.message:String(a)}`},!0)}}}};
@@ -1 +0,0 @@
1
- exports.id=8126,exports.ids=[8126,9989],exports.modules={947:(a,b,c)=>{"use strict";c.r(b),c.d(b,{"40520b537d1d43f510acbccd60e3e7d42ce215c157":()=>d.c,"60de87c4c5950c080550cb766970e88ae9c9a0ca25":()=>d.G});var d=c(73456)},3676:(a,b,c)=>{"use strict";c.d(b,{Wj:()=>h,ln:()=>g});var d=c(10641),e=c(58739);let f="encryption_locked";async function g(){return(0,e.isEncryptionLocked)()}async function h(a){return await g()?a?.openai?d.NextResponse.json({error:{message:"Encryption is locked. Unlock FLUJO to continue.",type:f,code:f,param:null}},{status:423}):d.NextResponse.json({error:f},{status:423}):null}},73456:(a,b,c)=>{"use strict";c.d(b,{G:()=>l,c:()=>k});var d=c(75879);c(68633);var e=c(50846),f=c(92372),g=c(46480),h=c(58739),i=c(51669);let j=(0,e.h)("backend/utils/resolveGlobalVars");async function k(a){j.debug("Entering resolveGlobalVars method");let b=await (0,f.YM)(g.Z.GLOBAL_ENV_VARS,{}),c=function(a){if(!a||"object"!=typeof a)return!1;let b=Object.keys(a);if(0===b.length)return!0;let c=a[b[0]];return c&&"object"==typeof c&&"value"in c&&"metadata"in c&&"object"==typeof c.metadata&&"isSecret"in c.metadata}(b);j.debug(`Loaded ${Object.keys(b).length} global environment variables (${c?"new":"old"} format)`);let d={};if(c)for(let[a,c]of Object.entries(b))d[a]=c.value;else d={...b};let e=async a=>{let b=a.match(/\$\{global:([^}]+)\}/g);if(!b)return a;let c=a;for(let a of b){let b=a.substring(9,a.length-1);if(void 0!==d[b]){let e=d[b];if(e.startsWith("encrypted:")){j.debug(`Decrypting encrypted global variable: ${b}`);try{let a=e.substring(10),c=await (0,h.decryptWithPassword)(a);if(c)e=c,j.debug(`Successfully decrypted global variable: ${b}`);else{j.warn(`Failed to decrypt global variable: ${b}`);continue}}catch(a){j.error(`Error decrypting global variable: ${b}`,a);continue}}else if(e.startsWith("encrypted_failed:")){j.warn(`Skipping failed encrypted global variable: ${b}`);continue}j.debug(`Resolved global variable: ${b}`),c=c.replace(a,e)}else j.warn(`Global variable not found: ${b}`)}return c},i=async a=>{if("string"==typeof a)return await e(a);if(Array.isArray(a))return await Promise.all(a.map(a=>i(a)));if(null!==a&&"object"==typeof a){let b={};for(let[c,d]of Object.entries(a))b[c]=await i(d);return b}return a},k=await i(a);return"object"==typeof a&&null!==a&&j.debug("Resolved global variables result:",JSON.stringify(k,null,2)),j.debug("Completed global variable resolution"),k}async function l(a,b=0){if(j.debug(`Entering resolveAndDecryptApiKey method (depth: ${b})`),!a)return j.warn("Empty value provided"),null;if(b>=10)return j.warn(`Maximum resolution depth reached (10) for value: ${a}`),a;let c=a;if(c.startsWith("encrypted:")){j.debug(`Decrypting encrypted value at depth ${b}`);try{let a=c.substring(10),d=await (0,h.decryptWithPassword)(a);if(!d)return j.warn(`Failed to decrypt value at depth ${b}`),null;j.debug(`Successfully decrypted value at depth ${b}`),j.verbose(d),c=d}catch(a){return j.error(`Error decrypting value at depth ${b}:`,a),null}}else c.startsWith("encrypted_failed:")&&(j.warn(`Skipping failed encrypted value at depth ${b}`),c=c.substring(17));if(c.includes("${global:")){j.debug(`Resolving global variables at depth ${b}`);try{let a=await k(c);if(j.verbose(a),a!==c)return j.debug(`Value changed after resolving globals at depth ${b}, processing recursively`),l(a,b+1);c=a}catch(a){return j.error(`Error resolving global variables at depth ${b}:`,a),null}}return j.debug(`Completed resolveAndDecryptApiKey at depth ${b}`),c}(0,i.D)([k,l]),(0,d.A)(k,"40520b537d1d43f510acbccd60e3e7d42ce215c157",null),(0,d.A)(l,"60de87c4c5950c080550cb766970e88ae9c9a0ca25",null)},78335:()=>{},79989:(a,b,c)=>{"use strict";c.d(b,{flowService:()=>k});var d=c(32524),e=c(92372),f=c(46480);let g=(0,c(50846).h)("backend/services/flow/index"),h=f.Z.FLOWS;async function i(){return global.__flujo_flowsMigration||(global.__flujo_flowsMigration=(async()=>{try{await (0,e.w5)(f.Z.FLOWS,h,a=>a.id)}catch(a){g.error("Flow storage migration failed",a),global.__flujo_flowsMigration=void 0}})()),global.__flujo_flowsMigration}class j{get flowsCache(){return global.__flujo_flowsCache??null}set flowsCache(a){global.__flujo_flowsCache=a}async loadFlows(){try{if(this.flowsCache)return g.debug("Using cached flows"),this.flowsCache;g.debug("Loading flows from storage"),await i();let a=await (0,e.pL)(h);return this.flowsCache=a,g.info("Loaded flows from storage",{count:a.length}),a}catch(a){return g.error("Failed to load flows",a),[]}}async getFlow(a){try{g.debug(`Getting flow by ID: ${a}`);let b=this.flowsCache?.find(b=>b.id===a)||null;if(b)return g.debug(`Flow ${a} found in cache`),b;await i();let c=null;try{c=await (0,e.EI)(h,a,null)}catch(b){g.debug(`getFlow: could not load flow ${a}`,b),c=null}if(c&&this.flowsCache){let a=this.flowsCache,b=a.findIndex(a=>a.id===c.id);b>=0?a[b]=c:a.push(c),this.flowsCache=a}return g.debug(`Flow ${a} ${c?"found":"not found"}`),c}catch(b){return g.error(`Failed to get flow ${a}`,b),null}}async invalidateExecutionCache(a){try{let{FlowExecutor:b}=await Promise.all([c.e(6996),c.e(1223),c.e(5734),c.e(1373),c.e(5504),c.e(724),c.e(1403)]).then(c.bind(c,91403));b.clearFlowCache(a),g.debug("Invalidated execution flow cache",{flowId:a??"all"})}catch(a){g.warn("Failed to invalidate execution flow cache",a)}}async saveFlow(a){try{g.debug(`Saving flow: ${a.id}`,{name:a.name}),(0,e.ln)(a.id),await i(),await (0,e.Vt)(h,a.id,a);let b=this.flowsCache;if(b){let c=b.findIndex(b=>b.id===a.id);c>=0?b[c]=a:b.push(a),this.flowsCache=b}return await this.invalidateExecutionCache(a.id),g.info(`Flow ${a.id} saved successfully`),{success:!0}}catch(a){return g.error("Failed to save flow",a),{success:!1,error:a instanceof Error?a.message:"Failed to save flow"}}}async deleteFlow(a){try{g.debug(`Deleting flow: ${a}`),await i(),await (0,e.bB)(h,a);let b=this.flowsCache;return b&&(this.flowsCache=b.filter(b=>b.id!==a)),await this.invalidateExecutionCache(a),g.info(`Flow ${a} deleted successfully`),{success:!0}}catch(b){return g.error(`Failed to delete flow: ${a}`,b),{success:!1,error:b instanceof Error?b.message:"Failed to delete flow"}}}createNode(a,b){g.debug(`Creating new node of type: ${a}`,{position:b});let c={id:(0,d.A)(),type:a,position:b,data:{label:`${"mcp"===a?"MCP":a.charAt(0).toUpperCase()+a.slice(1)} Node`,type:a,properties:{}}};return g.debug(`Node created with ID: ${c.id}`),c}createNewFlow(a="NewFlow"){g.debug(`Creating new flow: ${a}`);let b={id:(0,d.A)(),type:"start",position:{x:250,y:150},data:{label:"Start Node",type:"start",properties:{promptTemplate:""}}},c={id:(0,d.A)(),name:a,nodes:[b],edges:[]};return g.info(`New flow created with ID: ${c.id}`,{name:a}),c}createHistoryEntry(a,b){return g.debug("Creating history entry",{nodeCount:a.length,edgeCount:b.length}),{nodes:[...a],edges:[...b]}}generateSampleFlow(a="Sample Flow"){g.debug(`Generating sample flow: ${a}`);let b={id:(0,d.A)(),name:a,nodes:[{id:"1",type:"start",position:{x:250,y:50},data:{label:"Start Node",type:"start",properties:{prompt:"Enter your query here",systemMessage:"You are a helpful assistant",temperature:0}}},{id:"2",type:"process",position:{x:250,y:200},data:{label:"Process Node",type:"process",properties:{operation:"transform",enabled:!0}}},{id:"3",type:"finish",position:{x:250,y:350},data:{label:"Finish Node",type:"finish",properties:{format:"json",template:'{ "result": {{data}} }'}}},{id:"4",type:"mcp",position:{x:450,y:200},data:{label:"MCP Node",type:"mcp",properties:{channels:2,mode:"parallel"}}}],edges:[{id:"e1-2",source:"1",target:"2",type:"custom"},{id:"e2-3",source:"2",target:"3",type:"custom"}]};return g.info(`Sample flow generated with ID: ${b.id}`,{name:a}),b}async listFlows(){g.debug("listFlows: Entering method");try{let a=await this.loadFlows();return{success:!0,flows:a}}catch(a){return g.warn("listFlows: Failed to list flows:",a),{success:!1,error:a instanceof Error?a.message:"Failed to list flows"}}}}let k=new j},96487:()=>{}};
@@ -1,5 +0,0 @@
1
- "use strict";exports.id=8312,exports.ids=[8312],exports.modules={48312:(a,b,c)=>{c.d(b,{cA:()=>p});var d=c(10641),e=c(50846),f=c(91403),g=c(38291),h=c(99978),i=c(57214),j=c(14214),k=c(20433);let l=(0,e.h)("app/v1/chat/completions/chatCompletionService");function m(a){return Math.ceil((a||"").length/4)}let n=g.n;async function o(a,b,c,e,f,g=!1,j=!1){let k=Date.now();l.info("Processing chat completion request",{model:a.model,messageCount:a.messages?.length||0,stream:a.stream,flujo:b,requireApproval:c,flujodebug:e,conversationId:f});let n=await (0,i.runFlow)({modelName:a.model,messages:a.messages,processNodeId:a.processNodeId,mode:"conversation",conversationId:f,flujo:b,requireApproval:c,debug:e,continueDebug:g,userTurn:j});if(n.flowNotFound)return d.NextResponse.json({error:{message:`Flow not found: ${n.flowNotFound.name}`,type:"invalid_request_error",code:"flow_not_found"}},{status:400});if("paused_debug"===n.status)return l.info(`Returning paused debug state for conv ${n.conversationId}`),d.NextResponse.json({status:"paused_debug",conversation_id:n.conversationId,debugState:n.sharedState});if("error"===n.status){let a=n.error?.message??"Unknown error during execution",b=n.error?.details??{message:a},c=n.error?.statusCode??500;return l.error(`Returning error response for conv ${n.conversationId}`,{errorMessage:a,errorDetails:b,statusCode:c}),d.NextResponse.json({error:{message:a,type:b.type||"api_error",code:b.code||"internal_error",param:b.param,details:b}},{status:c})}let p={role:"assistant",content:n.outputText,tool_calls:n.toolCalls},q="stop";"awaiting_tool_approval"===n.sharedState.status?q="stop":n.finalAction===h.Jp&&p.tool_calls&&p.tool_calls.length>0?q="tool_calls":n.finalAction===h.E5&&(q="length");let r=m(n.messages.map(a=>a.content||"").join("\n")),s=m(n.outputText),t={id:`chatcmpl-${Date.now()}`,object:"chat.completion",created:Math.floor(k/1e3),model:a.model,choices:[{index:0,message:p,finish_reason:q}],usage:{prompt_tokens:r,completion_tokens:s,total_tokens:r+s},messages:n.messages,conversation_id:n.conversationId,status:n.sharedState.status||(n.finalAction===h.g4?"completed":"running"),pendingToolCalls:n.sharedState.pendingToolCalls};return l.info(`Returning success response for conv ${n.conversationId}`,{action:n.finalAction,status:t.status,flujo:b,requireApproval:c,flujodebug:e,finish_reason:q}),l.verbose(`Final response data for conv ${n.conversationId}`,t),d.NextResponse.json(t)}async function p(a,b,c,d,e,g=!1,h=!1){if("string"==typeof a.model&&a.model.startsWith("model-"))return(b||c||d||e)&&l.debug("Ignoring flow-only flags on a direct model completion",{flujo:b,requireApproval:c,flujodebug:d,conversationId:e}),q(a);if(!0!==a.stream)return o(a,b,c,d,e,g,h);{let i=e||crypto.randomUUID();return l.info(`Streaming requested for conversation ${i}. Starting async processing.`),o(a,b,c,d,i,g,h).catch(a=>{l.error(`Error in background processing for conversation ${i}:`,a);let b=f.FlowExecutor.conversationStates.get(i);b&&(b.status="error",b.lastResponse={success:!1,error:a instanceof Error?a.message:String(a)},f.FlowExecutor.conversationStates.set(i,b),n(`conversations/${i}`,b).catch(a=>{l.error(`Failed to save error state for conversation ${i}:`,a)})),j.$.emitterFor(i)({type:"run:done",status:"error"})}),function(a,b){let c=new TextEncoder,d=`chatcmpl-${Date.now()}`,e=Math.floor(Date.now()/1e3);return l.debug("create streaming response (event-bus driven)",{conversationId:b}),new Response(new ReadableStream({start(g){let h=!1,i=null,k=-1,l=a=>{g.enqueue(c.encode(`data: ${JSON.stringify(a)}
2
-
3
- `))},m=(b,c)=>({id:d,object:"chat.completion.chunk",created:e,model:a,choices:[{index:0,delta:b,finish_reason:c}]}),n=a=>{if(h)return;h=!0;let d="error"===a?"error":"stop";l(m({content:"",conversation:f.FlowExecutor.conversationStates.get(b)},d)),l(m({},d)),g.enqueue(c.encode("data: [DONE]\n\n")),i&&i();try{g.close()}catch{}},o=a=>{if(!h&&!(a.seq<=k))if(k=a.seq,"message"===a.type){let b=a.message;b&&"assistant"===b.role&&"string"==typeof b.content&&b.content.length>0&&l(m({content:b.content},null))}else"run:done"===a.type?n("error"===a.status?"error":"completed"):("run:awaiting_approval"===a.type||"run:paused"===a.type)&&n("stop")};for(let a of(l(m({role:"assistant",content:""},null)),i=j.$.subscribe(b,o),j.$.getBufferedSince(b,0)))o(a);if(!h){let a=f.FlowExecutor.conversationStates.get(b);a&&("completed"===a.status||"error"===a.status)&&n("error"===a.status?"error":"completed")}}}),{headers:{"Content-Type":"text/event-stream","Cache-Control":"no-cache",Connection:"keep-alive"}})}(a.model,i)}}async function q(a){let b=a.model.slice(6);l.info("Processing direct model completion",{model:a.model,messageCount:a.messages?.length||0,stream:a.stream,hasTools:!!(a.tools&&a.tools.length>0)});let c=await k.modelService.generateChatCompletion({modelIdentifier:b,messages:a.messages,temperature:a.temperature,tools:a.tools});return c.success?!0===a.stream?function(a,b){let c=new TextEncoder,d=b.id||`chatcmpl-${Date.now()}`,e=b.created||Math.floor(Date.now()/1e3),f=b.choices?.[0],g=f?.finish_reason??"stop";return new Response(new ReadableStream({start(b){let h=a=>{b.enqueue(c.encode(`data: ${JSON.stringify(a)}
4
-
5
- `))},i=(b,c)=>({id:d,object:"chat.completion.chunk",created:e,model:a,choices:[{index:0,delta:b,finish_reason:c}]});h(i({role:"assistant",content:""},null));let j="string"==typeof f?.message?.content?f.message.content:"";j.length>0&&h(i({content:j},null));let k=f?.message?.tool_calls;k&&k.length>0&&h(i({tool_calls:k.map((a,b)=>({index:b,...a}))},null)),h(i({},g)),b.enqueue(c.encode("data: [DONE]\n\n")),b.close()}}),{headers:{"Content-Type":"text/event-stream","Cache-Control":"no-cache",Connection:"keep-alive"}})}(a.model,c.completion):d.NextResponse.json(c.completion):(l.warn("Direct model completion failed",{model:a.model,code:c.error.code,statusCode:c.statusCode}),d.NextResponse.json({error:{message:c.error.message,type:c.error.type,code:c.error.code,param:c.error.param??null}},{status:c.statusCode}))}}};
@@ -1 +0,0 @@
1
- "use strict";exports.id=9989,exports.ids=[9989],exports.modules={79989:(a,b,c)=>{c.d(b,{flowService:()=>k});var d=c(32524),e=c(92372),f=c(46480);let g=(0,c(50846).h)("backend/services/flow/index"),h=f.Z.FLOWS;async function i(){return global.__flujo_flowsMigration||(global.__flujo_flowsMigration=(async()=>{try{await (0,e.w5)(f.Z.FLOWS,h,a=>a.id)}catch(a){g.error("Flow storage migration failed",a),global.__flujo_flowsMigration=void 0}})()),global.__flujo_flowsMigration}class j{get flowsCache(){return global.__flujo_flowsCache??null}set flowsCache(a){global.__flujo_flowsCache=a}async loadFlows(){try{if(this.flowsCache)return g.debug("Using cached flows"),this.flowsCache;g.debug("Loading flows from storage"),await i();let a=await (0,e.pL)(h);return this.flowsCache=a,g.info("Loaded flows from storage",{count:a.length}),a}catch(a){return g.error("Failed to load flows",a),[]}}async getFlow(a){try{g.debug(`Getting flow by ID: ${a}`);let b=this.flowsCache?.find(b=>b.id===a)||null;if(b)return g.debug(`Flow ${a} found in cache`),b;await i();let c=null;try{c=await (0,e.EI)(h,a,null)}catch(b){g.debug(`getFlow: could not load flow ${a}`,b),c=null}if(c&&this.flowsCache){let a=this.flowsCache,b=a.findIndex(a=>a.id===c.id);b>=0?a[b]=c:a.push(c),this.flowsCache=a}return g.debug(`Flow ${a} ${c?"found":"not found"}`),c}catch(b){return g.error(`Failed to get flow ${a}`,b),null}}async invalidateExecutionCache(a){try{let{FlowExecutor:b}=await Promise.all([c.e(6996),c.e(1223),c.e(5734),c.e(1373),c.e(5504),c.e(724),c.e(1403)]).then(c.bind(c,91403));b.clearFlowCache(a),g.debug("Invalidated execution flow cache",{flowId:a??"all"})}catch(a){g.warn("Failed to invalidate execution flow cache",a)}}async saveFlow(a){try{g.debug(`Saving flow: ${a.id}`,{name:a.name}),(0,e.ln)(a.id),await i(),await (0,e.Vt)(h,a.id,a);let b=this.flowsCache;if(b){let c=b.findIndex(b=>b.id===a.id);c>=0?b[c]=a:b.push(a),this.flowsCache=b}return await this.invalidateExecutionCache(a.id),g.info(`Flow ${a.id} saved successfully`),{success:!0}}catch(a){return g.error("Failed to save flow",a),{success:!1,error:a instanceof Error?a.message:"Failed to save flow"}}}async deleteFlow(a){try{g.debug(`Deleting flow: ${a}`),await i(),await (0,e.bB)(h,a);let b=this.flowsCache;return b&&(this.flowsCache=b.filter(b=>b.id!==a)),await this.invalidateExecutionCache(a),g.info(`Flow ${a} deleted successfully`),{success:!0}}catch(b){return g.error(`Failed to delete flow: ${a}`,b),{success:!1,error:b instanceof Error?b.message:"Failed to delete flow"}}}createNode(a,b){g.debug(`Creating new node of type: ${a}`,{position:b});let c={id:(0,d.A)(),type:a,position:b,data:{label:`${"mcp"===a?"MCP":a.charAt(0).toUpperCase()+a.slice(1)} Node`,type:a,properties:{}}};return g.debug(`Node created with ID: ${c.id}`),c}createNewFlow(a="NewFlow"){g.debug(`Creating new flow: ${a}`);let b={id:(0,d.A)(),type:"start",position:{x:250,y:150},data:{label:"Start Node",type:"start",properties:{promptTemplate:""}}},c={id:(0,d.A)(),name:a,nodes:[b],edges:[]};return g.info(`New flow created with ID: ${c.id}`,{name:a}),c}createHistoryEntry(a,b){return g.debug("Creating history entry",{nodeCount:a.length,edgeCount:b.length}),{nodes:[...a],edges:[...b]}}generateSampleFlow(a="Sample Flow"){g.debug(`Generating sample flow: ${a}`);let b={id:(0,d.A)(),name:a,nodes:[{id:"1",type:"start",position:{x:250,y:50},data:{label:"Start Node",type:"start",properties:{prompt:"Enter your query here",systemMessage:"You are a helpful assistant",temperature:0}}},{id:"2",type:"process",position:{x:250,y:200},data:{label:"Process Node",type:"process",properties:{operation:"transform",enabled:!0}}},{id:"3",type:"finish",position:{x:250,y:350},data:{label:"Finish Node",type:"finish",properties:{format:"json",template:'{ "result": {{data}} }'}}},{id:"4",type:"mcp",position:{x:450,y:200},data:{label:"MCP Node",type:"mcp",properties:{channels:2,mode:"parallel"}}}],edges:[{id:"e1-2",source:"1",target:"2",type:"custom"},{id:"e2-3",source:"2",target:"3",type:"custom"}]};return g.info(`Sample flow generated with ID: ${b.id}`,{name:a}),b}async listFlows(){g.debug("listFlows: Entering method");try{let a=await this.loadFlows();return{success:!0,flows:a}}catch(a){return g.warn("listFlows: Failed to list flows:",a),{success:!1,error:a instanceof Error?a.message:"Failed to list flows"}}}}let k=new j}};
@@ -1 +0,0 @@
1
- (self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[9040],{32651:(e,t,a)=>{Promise.resolve().then(a.bind(a,71113))},71113:(e,t,a)=>{"use strict";a.r(t),a.d(t,{default:()=>x});var o=a(95155),s=a(94049),n=a(12115),r=a(28548),i=a(2300),d=a(25635),l=a(97322),p=a(9562),c=a(56422),m=a(21067),u=a(32683),h=a(51611);let y=[{id:"openai",name:"Chat — OpenAI-compatible",description:'A drop-in OpenAI-compatible surface. In OpenAI-compatible clients (Roo Code, Cline, Cursor, the OpenAI SDK, …) set the Base URL to this instance + "/v1" (e.g. http://localhost:4200/v1) and the model to a flow id (format flow-<NAME>, as listed by /v1/models).',endpoints:[{method:"POST",alsoMethods:["GET","OPTIONS"],path:"/v1/chat/completions",summary:"OpenAI-compatible chat completion. Runs a FLUJO flow as the model. Supports streaming and non-streaming. CORS-enabled; rate-limited (~6000 req/min).",paramsLabel:"Body",params:[{name:"model",type:"string",required:!0,description:"Flow to run, as flow-<NAME>."},{name:"messages",type:"ChatMessage[]",required:!0,description:"Conversation messages (OpenAI shape)."},{name:"stream",type:"boolean",description:"Stream the response as SSE chunks."},{name:"flujo",type:"boolean",description:"Enable FLUJO-specific behavior."},{name:"conversation_id",type:"string",description:"Reuse/continue an existing conversation."},{name:"requireApproval",type:"boolean",description:"Pause for manual tool-call approval."},{name:"flujodebug",type:"boolean",description:"Run in the visual debugger (single-step)."}],response:"OpenAI ChatCompletion object, or an SSE stream of chunks when stream=true."},{method:"GET",path:"/v1/models",summary:"List all flows as OpenAI-style models.",response:'{ object: "list", data: [{ id: "flow-<NAME>", object: "model" }, ...] }'}]},{id:"conversations",name:"Conversations & Execution",description:"Manage conversations and drive flow execution: live event streaming (SSE), tool-call approval, cancellation, and visual-debugger stepping.",endpoints:[{method:"GET",path:"/v1/chat/conversations",summary:"List all conversations (id, title, flowId, createdAt, updatedAt, status), newest first.",response:"Conversation summary array."},{method:"POST",path:"/v1/chat/conversations",summary:"Create a new conversation and initialize its execution state.",paramsLabel:"Body",params:[{name:"id",type:"string",required:!0,description:"Client-generated conversation id."},{name:"title",type:"string",required:!0,description:"Display title."},{name:"flowId",type:"string",required:!0,description:"Flow bound to this conversation."},{name:"createdAt",type:"number",required:!0,description:"Unix ms timestamp."},{name:"updatedAt",type:"number",required:!0,description:"Unix ms timestamp."}]},{method:"GET",alsoMethods:["PATCH","DELETE"],path:"/v1/chat/conversations/{conversationId}",summary:"GET the displayed conversation (messages + metadata); PATCH updates flowId; DELETE removes the conversation and its event log.",paramsLabel:"Body",params:[{name:"flowId",type:"string",description:"New flow id (PATCH only)."}],response:"Conversation with messages, plus usage (token totals, per-node breakdown), status, and contextInfo (latest prompt-token count + the bound model's context window — what the chat token counter and context meter display).",notes:["Messages are a projection of the append-only conversation log when one exists (legacy conversations without a log are served as stored). Node system prompts are never included.","Subflow steps appear inline as depth-tagged, display-only messages (nested in the chat UI); they are never part of the parent's model context."]},{method:"GET",path:"/v1/chat/conversations/{conversationId}/events",summary:"Server-Sent Events stream of execution events (node:enter/exit, message, usage, run:*). Supports Last-Event-ID reconnect.",paramsLabel:"Query",params:[{name:"fromSeq",type:"number",description:"Replay events from this sequence number."}],response:"text/event-stream of ExecutionEvent objects with monotonic seq ids."},{method:"POST",path:"/v1/chat/conversations/{conversationId}/respond",summary:"Approve or reject a pending tool call; resumes execution when all pending calls are handled.",paramsLabel:"Body",params:[{name:"action",type:'"approve" | "reject"',required:!0,description:"Decision for the tool call."},{name:"toolCallId",type:"string",required:!0,description:"Which pending tool call."}]},{method:"POST",path:"/v1/chat/conversations/{conversationId}/cancel",summary:"Request cancellation of an in-progress run (sets the isCancelled flag)."},{method:"PUT",path:"/v1/chat/conversations/{conversationId}/breakpoints",summary:"Set the visual-debugger breakpoint node ids.",paramsLabel:"Body",params:[{name:"breakpoints",type:"string[]",required:!0,description:"Node ids to break on."}]},{method:"POST",path:"/v1/chat/conversations/{conversationId}/debug/step",summary:"Execute exactly one productive step (only when status is paused_debug); pauses after advancing."},{method:"POST",path:"/v1/chat/conversations/{conversationId}/debug/continue",summary:"Resume a debug session — run freely to the next pause, tool approval, or completion."},{method:"PATCH",path:"/v1/chat/conversations/{conversationId}/edit-state",summary:"Edit paused debug state (a message's content, or the next node to run). Only when paused_debug.",paramsLabel:"Body",params:[{name:"messageId",type:"string",description:"Message to edit."},{name:"content",type:"string",description:"New message content."},{name:"currentNodeId",type:"string",description:"Override the next node."}]}]},{id:"model",name:"Models",description:"CRUD for model configurations and provider model discovery. API keys are encrypted at rest and never returned to the browser in clear text.",endpoints:[{method:"GET",alsoMethods:["POST"],path:"/api/model",summary:"GET lists all models; POST creates a model (409 on duplicate name, 201 on success).",paramsLabel:"Body",params:[{name:"model",type:"Model",required:!0,description:"Model config (POST). Secrets are stored encrypted."}],notes:["Stored API keys are masked before reaching the frontend — never expect a clear-text key in a response."]},{method:"GET",alsoMethods:["PUT","DELETE"],path:"/api/model/{id}",summary:"GET / PUT / DELETE a single model. The path id is authoritative on PUT. DELETE returns 204."},{method:"POST",path:"/api/model/provider",summary:"Fetch available models from a provider, using a supplied or stored API key (resolved server-side).",paramsLabel:"Body",params:[{name:"baseUrl",type:"string",required:!0,description:"Provider base URL."},{name:"modelId",type:"string",description:"Existing model whose stored key to reuse."},{name:"searchTerm",type:"string",description:"Filter the returned model list."}],notes:["Accepts an apiKey only inbound to query the provider; keys are never echoed back to the client."]}]},{id:"flow",name:"Flows",description:"CRUD for flow definitions, programmatic flow authoring (FlowSpec), LLM flow generation, and prompt rendering for individual nodes. For creating flows programmatically, prefer POST /api/flow/compile with a FlowSpec — the raw Flow format (ReactFlow JSON) is the internal canvas representation and easy to get subtly wrong.",endpoints:[{method:"POST",path:"/api/flow/compile",summary:"Compile a FlowSpec (the semantic authoring format — see below) into a complete flow, validate it, and optionally save it. Deterministic, no LLM: layout, ids, handles, and MCP wiring are generated for you. THE recommended way for external apps and agents to create flows. Also exposed as MCP tools (create_flow / validate_flow_spec) on /mcp-flows.",paramsLabel:"Body",params:[{name:"spec",type:"FlowSpec",required:!0,description:"The flow specification (format below)."},{name:"save",type:"boolean",description:"Persist the flow — only happens when validation finds ZERO errors."}],response:"{ flow, validation: { issues, errorCount, warningCount, isRunnable }, saved } — 201 when saved, 200 otherwise. Iterate on the issues until clean.",details:'A FlowSpec is a JSON object describing a FLUJO flow semantically — node keys and edges, no coordinates, no ids, no layout:\n{\n "name": "short_flow_name", // letters/digits/_/- only\n "description": "what the flow does",\n "nodes": [ ... ],\n "edges": [ ... ]\n}\n\nNODE TYPES:\n- { "key": "unique_key", "type": "start", "label": "...", "prompt": "system-level instructions for the whole flow" }\n- { "key": "...", "type": "process", "label": "...", "description": "what this step does",\n "model": "<model id or name from the catalog>",\n "prompt": "instructions for this step",\n "servers": [ { "name": "<server name>", "tools": ["tool_a"] } ], // optional; omit "tools" to enable all\n "inputMode": "full-history" | "latest-message" | "isolated", // optional, default full-history\n "isolatedPrompt": "..." } // only with inputMode "isolated"\n- { "key": "...", "type": "subflow", "label": "...", "flow": "<existing flow name or id>",\n "inputMode": "full-history" | "latest-message" | "isolated",\n "outputMode": "steps" | "final-only" }\n- { "key": "...", "type": "finish", "label": "..." }\n\nEDGES: { "from": "<node key>", "to": "<node key>", "bidirectional": true|false }\n\nRULES:\n1. Exactly ONE start node; at least one finish node reachable from it.\n2. Every process node MUST reference a configured model (by id, display name, or name).\n3. A process step uses MCP tools ONLY via its "servers" list — never emit nodes of type "mcp".\n4. Do not embed ${tool:...} or ${resource:...} references in prompts — tools are wired through "servers".\n5. Branching: give a process node multiple outgoing edges; its model decides where to hand off at runtime. "bidirectional": true lets the target hand back to the source (agent <-> agent).\n6. A subflow node may have only ONE outgoing edge, and its "flow" must name an existing flow.\n7. Keep flows minimal — only the steps the task needs. Write clear, specific prompts and labels; fill "description" on process nodes.'},{method:"POST",path:"/api/flow/generate",summary:"Generate a draft flow from a natural-language description using one of your configured models. The model authors a FlowSpec, FLUJO compiles + validates it and feeds problems back for a bounded number of repair rounds. The generator can always SEARCH the public MCP registry; with allowInstall it can also INSTALL servers it needs. Returns an UNSAVED draft (the UI opens it in the builder for review); persist via the normal save path or /api/flow/compile.",paramsLabel:"Body",params:[{name:"description",type:"string",required:!0,description:"What the flow should do, in plain language."},{name:"modelId",type:"string",required:!0,description:"Configured model that does the generating."},{name:"maxRepairs",type:"number",description:"Repair rounds after the first attempt (default 1, max 2)."},{name:"allowInstall",type:"boolean",description:"Let the generator install MCP servers from the public registry (downloads + RUNS third-party packages on this machine). Off by default."}],response:"{ flow, validation, attempts, installedServers } — the flow is a draft; nothing flow-related is persisted by this endpoint (installed servers ARE persisted).",notes:["allowInstall executes third-party code with this machine's user permissions — opt in deliberately."]},{method:"GET",alsoMethods:["POST"],path:"/api/flow",summary:"GET lists all flow definitions; POST creates a flow (create-only; 409 if the id exists). POST takes the raw internal Flow format — prefer /api/flow/compile for programmatic creation.",paramsLabel:"Body",params:[{name:"flow",type:"Flow",required:!0,description:"Flow object including id (POST)."}]},{method:"GET",alsoMethods:["PUT","DELETE"],path:"/api/flow/{id}",summary:"GET / PUT / DELETE a single flow. On PUT the path id wins over the body id (404 if missing)."},{method:"POST",path:"/api/flow/prompt-renderer",summary:"Render the assembled prompt for a node within a flow.",paramsLabel:"Body",params:[{name:"flowId",type:"string",required:!0,description:"Flow containing the node."},{name:"nodeId",type:"string",required:!0,description:"Node to render."},{name:"options",type:"object",description:"Render options."}]}]},{id:"planned-executions",name:"Planned Executions",description:"Run flows headlessly on triggers: schedules (cron), inbound webhooks, and more. Managed from the Executions page; each run is recorded in a per-execution run history.",endpoints:[{method:"GET",alsoMethods:["POST","PATCH"],path:"/api/planned-executions",summary:"GET lists all planned executions with live trigger status and last run; POST creates one; PATCH toggles the global pause switch ({ paused: boolean }).",response:"GET: { paused, executions: [{ execution, status, lastRun }] }."},{method:"GET",alsoMethods:["PATCH","DELETE"],path:"/api/planned-executions/{id}",summary:"GET / PATCH / DELETE a single planned execution. PATCH takes a partial config and re-arms the trigger."},{method:"POST",path:"/api/planned-executions/{id}/run",summary:"Run now: fire the execution immediately (even while disabled or paused) and wait for the run.",response:"{ record: RunRecord } with status, output preview, and token usage."},{method:"GET",path:"/api/planned-executions/{id}/runs",summary:"The execution’s run history (newest 100 runs, oldest first)."},{method:"POST",path:"/api/planned-executions/preview-schedule",summary:"Validate a cron pattern and preview its next fire times (editor helper).",paramsLabel:"Body",params:[{name:"cron",type:"string",required:!0,description:"Cron pattern (5 or 6 fields)."},{name:"timezone",type:"string",description:"IANA timezone name."}],response:"{ valid, error?, nextRuns: string[] }."},{method:"POST",path:"/api/webhooks/{id}",summary:"Inbound webhook trigger. Fires the planned execution with the request body as trigger context and responds 202 { runId } immediately (the flow runs in the background). For a synchronous answer use /v1/chat/completions instead.",paramsLabel:"Query",params:[{name:"token",type:"string",required:!0,description:"Per-execution secret (alternatively send the X-Flujo-Token header)."}],notes:["Localhost-only by default; external callers must be explicitly allowed per execution.","The webhook body is untrusted input to the flow — prompts should treat it as data."]}]},{id:"mcp",name:"MCP Servers",description:"Manage MCP server configurations, connection state, tool discovery, and tool invocation.",endpoints:[{method:"GET",alsoMethods:["POST"],path:"/api/mcp/servers",summary:"GET lists all MCP server configs; POST registers a new one (409 on duplicate name).",paramsLabel:"Body",params:[{name:"config",type:"MCPServerConfig",required:!0,description:"Server config (POST)."}]},{method:"GET",alsoMethods:["PUT","DELETE"],path:"/api/mcp/servers/{name}",summary:"GET / PUT / DELETE a server config. PUT merges fields; toggling disabled connects/disconnects; a new name triggers a validated rename."},{method:"GET",path:"/api/mcp/servers/{name}/status",summary:"Live connection status. Always 200, even when the server is down.",response:"{ status, ... }"},{method:"GET",path:"/api/mcp/servers/{name}/tools",summary:"List tools exposed by a connected server.",response:"{ tools, error? } (empty tools + error when disconnected)."},{method:"POST",path:"/api/mcp/servers/{name}/tools/{toolName}",summary:"Invoke a tool on a server. Status may reflect the tool itself (408 on timeout).",paramsLabel:"Body",params:[{name:"args",type:"Record<string, unknown>",required:!0,description:"Tool arguments."},{name:"timeout",type:"number",description:"Optional timeout (ms)."}]},{method:"POST",path:"/api/mcp/test-connection",summary:"Run a real MCP handshake against an unsaved config (tests custom CAs and headers) without registering it.",paramsLabel:"Body",params:[{name:"config",type:"MCPServerConfig",required:!0,description:"Config to test."}]},{method:"POST",path:"/api/mcp/cancel",summary:"Cancel an in-progress tool execution; force-reconnects if no token is supplied.",paramsLabel:"Body",params:[{name:"reason",type:"string",required:!0,description:"Cancellation reason."}]}]},{id:"mcp-proxy",name:"FLUJO as an MCP server (proxy)",description:'Re-expose a configured MCP server to EXTERNAL MCP clients (Claude Desktop, Cursor, Cline, mcp-inspector, …) so you configure the server once in FLUJO and reach it everywhere. Enable per server via the "Expose to external apps" toggle on its card. In your MCP client, add a Streamable-HTTP server with the URL below. One downstream server per endpoint, so tool names are unchanged. Localhost-only for now (no auth token in this version — see the security roadmap).',endpoints:[{method:"POST",alsoMethods:["GET","DELETE"],path:"/mcp-proxy/{server}",summary:'A Streamable-HTTP MCP endpoint that forwards tools/list and tools/call to the named downstream MCP server. {server} is the configured server name; it must have "Expose to external apps" enabled (otherwise 404). Rejects non-localhost requests.',response:"MCP JSON-RPC over Streamable HTTP (handled by your MCP client, not called directly)."}]},{id:"mcp-flows",name:"FLUJO as an MCP server (flows + authoring)",description:"A Streamable-HTTP MCP endpoint that exposes FLUJO itself to external MCP clients. Every saved flow is a callable tool (running it ephemerally and returning its output), and three authoring tools let an external agent CREATE flows by sending a semantic FlowSpec — no raw ReactFlow JSON required. Localhost-only (same posture as the proxy).",endpoints:[{method:"POST",alsoMethods:["GET","DELETE"],path:"/mcp-flows",summary:'MCP server with two tool families. Flow tools: one per saved flow (name = slug of the flow name; input = a single "input" string sent as the user turn; runs are ephemeral and never appear in the chat sidebar). Authoring tools: list_flow_building_blocks (models, MCP servers + tools, and existing flows a spec may reference — call first), validate_flow_spec (compile + validate without saving; iterate on the returned issues), create_flow (compile + validate + save; only saves when validation finds zero errors), plus capability acquisition: search_mcp_marketplace (search the public registry) and install_mcp_server (install + connect a registry server — downloads and RUNS third-party packages on the FLUJO host; required keys can be passed via its env argument). The FlowSpec format is documented on POST /api/flow/compile above and inside the tools\' own descriptions.',response:"MCP JSON-RPC over Streamable HTTP (handled by your MCP client, not called directly)."}]},{id:"oauth",name:"OAuth (for MCP)",description:"OAuth authorization flow for MCP servers that require it. Tokens are stored server-side and never sent to the browser.",endpoints:[{method:"POST",path:"/api/oauth/initiate",summary:"Begin the OAuth flow for a server; dynamically registers a client and returns an authorization URL.",paramsLabel:"Body",params:[{name:"serverName",type:"string",required:!0,description:"MCP server to authorize."}]},{method:"GET",alsoMethods:["POST"],path:"/api/oauth/callback",summary:"OAuth redirect target; exchanges the code for tokens, updates the config, and redirects to /mcp.",notes:["Tokens are persisted server-side only."]},{method:"POST",path:"/api/oauth/reset",summary:"Clear a server's OAuth tokens and force re-authentication on the next connection.",paramsLabel:"Body",params:[{name:"serverName",type:"string",required:!0,description:"Server to reset."}]}]},{id:"env",name:"Environment & Secrets",description:"Global environment variables and server-side encryption operations. Secret values are encrypted at rest and masked as ******** in responses.",endpoints:[{method:"GET",alsoMethods:["POST"],path:"/api/env",summary:"GET reads env vars (secrets masked); POST sets/deletes vars (secrets encrypted server-side).",paramsLabel:"Body",params:[{name:"action",type:'"set" | "setAll" | "delete"',required:!0,description:"Operation."},{name:"key",type:"string",description:"Variable name (set/delete)."},{name:"value",type:"string",description:"Value; the placeholder ******** is ignored."},{name:"metadata",type:"{ isSecret: boolean }",description:"Marks a value for encryption."}],notes:["GET masks secret values unless an internal includeSecrets flag is set; the browser never receives clear-text secrets."]},{method:"POST",path:"/api/encryption/secure",summary:"Server-side encryption operations: initialize, authenticate, change_password, encrypt, verify_password, check_initialized, and status queries.",paramsLabel:"Body",params:[{name:"action",type:"string",required:!0,description:"One of the encryption actions."}],notes:["Passwords and tokens are handled here and stay server-side — never log or surface them in the UI."]}]},{id:"storage",name:"Storage & Backup",description:"Generic key-value storage plus zip backup/restore of selected data.",endpoints:[{method:"GET",alsoMethods:["POST","DELETE"],path:"/api/storage",summary:"GET loads an item by StorageKey (with optional default); POST saves; DELETE clears.",paramsLabel:"Query",params:[{name:"key",type:"StorageKey",required:!0,description:"Storage key."},{name:"defaultValue",type:"JSON",description:"Returned when the key is missing (GET)."}]},{method:"POST",path:"/api/backup",summary:"Create a zip backup of selected items and download it.",paramsLabel:"Body",params:[{name:"selections",type:"string[]",required:!0,description:"e.g. models, mcpServers, flows, chatHistory, settings."}],notes:["A backup may include the encryption key and secrets — handle the downloaded file carefully."]},{method:"POST",path:"/api/restore",summary:"Restore data from a backup zip.",paramsLabel:"Form data",params:[{name:"file",type:"File (zip)",required:!0,description:"Backup archive."},{name:"selections",type:"JSON string[]",required:!0,description:"Which items to restore."}]}]},{id:"system",name:"System & Updates",description:"App initialization, working-directory info, repository operations, and self-update.",endpoints:[{method:"GET",path:"/api/init",summary:"Server-side initialization: verify storage and start enabled MCP servers."},{method:"GET",path:"/api/cwd",summary:"Return the current working directory and the mcp-servers directory path."},{method:"GET",path:"/api/browse",summary:"List a directory of the machine running FLUJO, for the folder-picker dialogs (file-watch triggers, MCP server paths). Defaults to the home directory; includes available drives on Windows.",paramsLabel:"Query",params:[{name:"path",type:"string",description:"Absolute directory path to list."}],response:"{ path, parent, home, sep, drives, entries: [{ name, path, isDirectory }] }",notes:["Exposes backend filesystem listings — like the rest of the API it assumes a single trusted user."]},{method:"POST",path:"/api/git",summary:"Repository operations for MCP servers: clone, install, build, run, exists, readFile, listDir, list, checkUpdates, checkUpdatesBatch, pullUpdates.",paramsLabel:"Body",params:[{name:"action",type:"string",required:!0,description:"Operation to perform."}]},{method:"GET",alsoMethods:["POST"],path:"/api/update",summary:'GET checks for a newer version (git fetch + behind count); POST { action: "apply" } pulls, rebuilds, and restarts.',response:"{ success, isGitRepo, updateAvailable, behindBy?, branch?, currentVersion }"}]}],f={GET:"#2e7d32",POST:"#1565c0",PUT:"#ed6c02",PATCH:"#9c27b0",DELETE:"#c62828",OPTIONS:"#616161"};function g(e){let{method:t}=e;return(0,o.jsx)(r.A,{label:t,size:"small",sx:{bgcolor:f[t],color:"#fff",fontWeight:700,fontFamily:"var(--font-geist-mono), monospace",fontSize:"0.7rem",height:22}})}function v(e){var t,a,n,r;let{endpoint:l}=e;return(0,o.jsxs)(i.A,{variant:"outlined",sx:{p:2,mb:1.5},children:[(0,o.jsxs)(s.default,{sx:{display:"flex",alignItems:"center",gap:1,flexWrap:"wrap",mb:1},children:[(0,o.jsx)(g,{method:l.method}),null==(t=l.alsoMethods)?void 0:t.map(e=>(0,o.jsx)(g,{method:e},e)),(0,o.jsx)(d.default,{component:"code",sx:{fontFamily:"var(--font-geist-mono), monospace",fontSize:"0.9rem",wordBreak:"break-all"},children:l.path})]}),(0,o.jsx)(d.default,{variant:"body2",color:"text.secondary",sx:{mb:1.5*(null!=(a=l.params)&&!!a.length)},children:l.summary}),l.params&&l.params.length>0&&(0,o.jsxs)(s.default,{sx:{mb:1},children:[(0,o.jsx)(d.default,{variant:"caption",sx:{fontWeight:700,textTransform:"uppercase",color:"text.secondary"},children:null!=(r=l.paramsLabel)?r:"Parameters"}),(0,o.jsx)(s.default,{component:"ul",sx:{m:0,mt:.5,pl:2.5},children:l.params.map(e=>(0,o.jsx)(s.default,{component:"li",sx:{mb:.25},children:(0,o.jsxs)(d.default,{variant:"body2",component:"span",children:[(0,o.jsx)(s.default,{component:"code",sx:{fontFamily:"var(--font-geist-mono), monospace",fontWeight:600},children:e.name}),(0,o.jsxs)(s.default,{component:"span",sx:{color:"text.secondary"},children:[" ",e.type,e.required?" \xb7 required":""," — ",e.description]})]})},e.name))})]}),l.response&&(0,o.jsxs)(d.default,{variant:"body2",sx:{mt:.5},children:[(0,o.jsxs)(s.default,{component:"span",sx:{fontWeight:600},children:["Response:"," "]}),(0,o.jsx)(s.default,{component:"span",sx:{color:"text.secondary"},children:l.response})]}),null==(n=l.notes)?void 0:n.map((e,t)=>(0,o.jsxs)(d.default,{variant:"caption",sx:{display:"block",mt:1,color:"warning.main"},children:["⚠ ",e]},t)),l.details&&(0,o.jsx)(s.default,{component:"pre",sx:{mt:1.5,p:1.5,bgcolor:"action.hover",borderRadius:1,fontSize:"0.75rem",fontFamily:"monospace",whiteSpace:"pre-wrap",overflowX:"auto",m:0,mb:0},children:l.details})]})}function b(){let[e,t]=(0,n.useState)(""),[a,r]=(0,n.useState)("");n.useEffect(()=>{r(window.location.origin)},[]);let f=(0,n.useMemo)(()=>{let t=e.trim().toLowerCase();return t?y.map(e=>{let a=e.endpoints.filter(a=>{var o;return"".concat(a.method," ").concat((null!=(o=a.alsoMethods)?o:[]).join(" ")," ").concat(a.path," ").concat(a.summary).toLowerCase().includes(t)||e.name.toLowerCase().includes(t)});return{...e,endpoints:a}}).filter(e=>e.endpoints.length>0):y},[e]);return(0,o.jsxs)(l.A,{maxWidth:"md",sx:{py:5},children:[(0,o.jsx)(d.default,{variant:"h4",gutterBottom:!0,children:"API Documentation"}),(0,o.jsx)(d.default,{variant:"body1",color:"text.secondary",sx:{mb:2},children:"FLUJO exposes an OpenAI-compatible chat API and a REST surface for managing models, MCP servers, flows, and conversations. All endpoints are served from this instance."}),(0,o.jsxs)(i.A,{variant:"outlined",sx:{p:2,mb:3},children:[(0,o.jsx)(d.default,{variant:"subtitle2",gutterBottom:!0,children:"Base URL"}),(0,o.jsx)(s.default,{component:"code",sx:{fontFamily:"var(--font-geist-mono), monospace",fontSize:"0.9rem"},children:a||"http://localhost:4200"}),(0,o.jsxs)(d.default,{variant:"body2",color:"text.secondary",sx:{mt:1},children:["Point any OpenAI SDK at"," ",(0,o.jsx)(s.default,{component:"code",sx:{fontFamily:"var(--font-geist-mono), monospace"},children:(a||"http://localhost:4200")+"/v1"})," ","and set the model to"," ",(0,o.jsx)(s.default,{component:"code",sx:{fontFamily:"var(--font-geist-mono), monospace"},children:"flow-<NAME>"}),". Any API key value is accepted locally."]})]}),(0,o.jsx)(p.A,{fullWidth:!0,size:"small",placeholder:"Search endpoints (path, method, description)…",value:e,onChange:e=>t(e.target.value),sx:{mb:3},InputProps:{startAdornment:(0,o.jsx)(c.A,{position:"start",children:(0,o.jsx)(h.A,{fontSize:"small"})})}}),0===f.length&&(0,o.jsxs)(d.default,{color:"text.secondary",children:["No endpoints match “",e,"”."]}),f.map(e=>(0,o.jsxs)(s.default,{sx:{mb:4},id:e.id,children:[(0,o.jsx)(d.default,{variant:"h6",gutterBottom:!0,children:e.name}),(0,o.jsx)(d.default,{variant:"body2",color:"text.secondary",sx:{mb:2},children:e.description}),e.endpoints.map(e=>(0,o.jsx)(v,{endpoint:e},"".concat(e.method," ").concat(e.path)))]},e.id)),(0,o.jsx)(m.A,{sx:{my:3}}),(0,o.jsxs)(d.default,{variant:"caption",color:"text.secondary",children:["Secrets (API keys, encryption passwords, OAuth tokens) are encrypted at rest and never returned to the browser in clear text. See the project"," ",(0,o.jsx)(u.A,{href:"https://github.com/mario-andreschak/FLUJO",target:"_blank",rel:"noopener",children:"repository"})," ","for source-level details."]})]})}let w=(0,a(87021).h)("app/docs/page");function x(){return w.debug("Rendering DocsPage"),(0,o.jsx)(s.default,{component:"main",sx:{minHeight:"100vh"},children:(0,o.jsx)(b,{})})}},87021:(e,t,a)=>{"use strict";a.d(t,{h:()=>d});let o={LOG_LEVEL:3},s={VERBOSE:-1,DEBUG:0,INFO:1,WARN:2,ERROR:3},n="number"==typeof o.LOG_LEVEL?o.LOG_LEVEL:s.ERROR;function r(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0;if(t>6)return String(e);if(!(e instanceof Error))return e;let a={name:e.name,message:e.message};for(let o of Object.getOwnPropertyNames(e)){if("name"===o||"message"===o)continue;let s=e[o];"function"!=typeof s&&("stack"===o?a.stack=s:"cause"===o?a.cause=r(s,t+1):"errors"===o&&Array.isArray(s)?a.errors=s.map(e=>r(e,t+1)):a[o]=s)}return a}function i(e,t,a,o,i){if(e>=("number"==typeof i?i:n)){if("function"==typeof o)try{o=o()}catch(e){o="[lazy log data threw]"}let n=new Date().toISOString(),i="[".concat(n,"] [").concat(t,"]"),d="".concat(i," ").concat(a);if(void 0!==o)if(o instanceof Error||"object"==typeof o)try{let e=o instanceof Error?r(o):o,t=JSON.stringify(e,(e,t)=>t instanceof Error?r(t):t,2);d+=":\n".concat(t)}catch(e){d+=": [Object cannot be stringified]"}else d+=": ".concat(o);switch(e){case s.VERBOSE:console.debug("[VERBOSE] ".concat(d));break;case s.DEBUG:console.debug(d);break;case s.INFO:console.info(d);break;case s.WARN:console.warn(d);break;case s.ERROR:console.error(d);break;default:console.log(d)}}}function d(e,t){let a,o=((a=e.replace(/^src\//,"")).startsWith("/")&&(a=a.substring(1)),a);return{verbose:(e,a)=>{i(s.VERBOSE,o,e,a,t)},debug:(e,a)=>{i(s.DEBUG,o,e,a,t)},info:(e,a)=>{i(s.INFO,o,e,a,t)},warn:(e,a)=>{i(s.WARN,o,e,a,t)},error:(e,a)=>{i(s.ERROR,o,e,a,t)}}}}},e=>{e.O(0,[2678,4754,9244,8548,9562,488,8989,8441,1255,7358],()=>e(e.s=32651)),_N_E=e.O()}]);