diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 14028b3..17b96e8 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -117,7 +117,7 @@ Service ports are fixed (`3000/3100/3200/3300/4000`); container-to-container URL - **Modular monolith over microservices** — one database, one deploy unit set, hard module boundaries enforced by convention (service interfaces only, no shared repositories). Scale comes from splitting *binaries*, not codebases. - **Transactional outbox** for anything that must not be lost; plain pub/sub for anything that may be. -- **Opinionated workflow model** — no loops, no sub-workflows, no expression language. Flows stay simple, readable, and predictable for non-developer users; that constraint is a feature. +- **Opinionated workflow model** — batch is the default: every node takes an array in and returns an array out, and references resolve per item (`$node.field`), by position (`$node[2].field`) or across the whole set (`$node[*].field`). What the canvas deliberately lacks is control-flow machinery — no loop construct, no sub-workflows, no expression language. Flows stay simple, readable, and predictable for non-developer users; that constraint is a feature. - **stdlib-first Go** — `log/slog`, `database/sql`, small focused packages in `go-packages` instead of frameworks. - **Runtime env for the UI** — one image per release, environment decided at container start. - **Scale-out infrastructure is opt-in** — Redis buys cross-instance coordination, which a fresh single-instance install does not need. Every Redis-backed subsystem ships an in-process provider and defaults to it; Compose only starts what is configured. diff --git a/README.md b/README.md index f70a1f3..3e2e846 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ It is built for end users, not just developers: flows stay simple, readable and ## Highlights -- **Visual flow builder** — compose workflows on an intuitive drag-and-drop canvas. Flows are deliberately simple: no loops, no sub-workflows, no expression language to learn. +- **Visual flow builder** — compose workflows on an intuitive drag-and-drop canvas. Nodes take arrays in and return arrays out, so a node fed ten items runs ten times and emits ten results — batch work needs no loop construct. What is missing is deliberate: no `while`/`for` on the canvas, no sub-workflows, no expression language to learn. - **Describe it, don't configure it** — give a node plain-language instructions and an LLM fills in its parameters at run time. Anything you set explicitly always wins over what the model infers. This one and flow generation are the only features that need a key of your own (Gemini); both are off by default. Everything else — the canvas, triggers, the MCP server, every integration node — works on a fresh install. - **Built-in MCP server** — every integration node doubles as an [MCP](https://modelcontextprotocol.io) tool. Point Claude (or any MCP client) at your BlockNext server with an API key and use your connected services from chat. - **AI-powered nodes** — LLMs and generative AI (text, image, audio, video) as first-class building blocks, alongside integrations for the tools you already use. diff --git a/apps/platform-api/prompts/workflow-generation-system-instruction.md b/apps/platform-api/prompts/workflow-generation-system-instruction.md index cbda0fa..771dfc6 100644 --- a/apps/platform-api/prompts/workflow-generation-system-instruction.md +++ b/apps/platform-api/prompts/workflow-generation-system-instruction.md @@ -247,11 +247,11 @@ ALWAYS emit the full default block on every executable (core) node: { "maxRetries": 0, "retryDelay": 1000, - "timeout": 30000, + "timeout": 0, "continueOnError": false, "disabled": false } -Override individual values only when the user explicitly asks for retries, longer timeouts, or "keep running on error". Never omit the block. +"timeout": 0 means no time limit, and that is the default: generation nodes (video, music, image) legitimately run for minutes and a timeout would kill them mid-job. Override individual values only when the user explicitly asks for retries, a time limit, or "keep running on error". Never omit the block. The starter node does NOT carry settings. === credentials FIELD === @@ -361,7 +361,7 @@ Correct output: "settings": { "maxRetries": 0, "retryDelay": 1000, - "timeout": 30000, + "timeout": 0, "continueOnError": false, "disabled": false }, @@ -381,7 +381,7 @@ Correct output: "settings": { "maxRetries": 0, "retryDelay": 1000, - "timeout": 30000, + "timeout": 0, "continueOnError": false, "disabled": false }, diff --git a/apps/platform/public/locales/en/ui.json b/apps/platform/public/locales/en/ui.json index ee1afd8..d429a22 100644 --- a/apps/platform/public/locales/en/ui.json +++ b/apps/platform/public/locales/en/ui.json @@ -481,6 +481,8 @@ "ui.text.maxSupply": "Max Supply", "ui.text.maxSupplyHelp": "Enter the maximum supply in wei (18 decimals)", "ui.text.mcpConnectionUrl": "Connection URL", + "ui.text.mcpClientConfig": "Client config", + "ui.text.mcpApiKeyHint": "Create an API key with the mcp:invoke scope and put it in X-API-Key.", "ui.text.mcpCopyUrl": "Copy URL", "ui.text.mcpNoSearchResults": "No servers match your search.", "ui.text.mcpNoServersAvailable": "No MCP servers available yet.", @@ -824,7 +826,7 @@ "ui.text.theme": "Theme", "ui.text.thisItemIsFree": "This item is free", "ui.text.timeout": "Timeout (ms)", - "ui.text.timeoutDescription": "Maximum execution time in milliseconds", + "ui.text.timeoutDescription": "Maximum execution time in milliseconds; 0 means no limit", "ui.text.timeset": "Timeset", "ui.text.timezone": "Timezone", "ui.text.title": "Title", diff --git a/apps/platform/src/components/shared/code-block.tsx b/apps/platform/src/components/shared/code-block.tsx new file mode 100644 index 0000000..67a3505 --- /dev/null +++ b/apps/platform/src/components/shared/code-block.tsx @@ -0,0 +1,36 @@ +import { Check, Copy } from 'lucide-react' +import { useTranslation } from 'react-i18next' +import { Button } from '@/components/ui/button' +import { useCopy } from '@/hooks/use-copy' + +export function CodeBlock({ code }: { code: string }) { + const { t } = useTranslation() + const { copied, copy } = useCopy() + const label = t('ui.text.copy', 'Copy') + + return ( +
+ {code}
+
+
- {code}
-
-