blokctl 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/build/index.d.ts +2 -0
- package/dist/commands/build/index.js +210 -0
- package/dist/commands/config/index.d.ts +1 -0
- package/dist/commands/config/index.js +46 -0
- package/dist/commands/cost/index.d.ts +1 -0
- package/dist/commands/cost/index.js +74 -0
- package/dist/commands/create/node.d.ts +2 -0
- package/dist/commands/create/node.js +541 -0
- package/dist/commands/create/project.d.ts +2 -0
- package/dist/commands/create/project.js +941 -0
- package/dist/commands/create/utils/Examples.d.ts +39 -0
- package/dist/commands/create/utils/Examples.js +983 -0
- package/dist/commands/create/workflow.d.ts +2 -0
- package/dist/commands/create/workflow.js +109 -0
- package/dist/commands/deploy/index.d.ts +2 -0
- package/dist/commands/deploy/index.js +176 -0
- package/dist/commands/dev/index.d.ts +2 -0
- package/dist/commands/dev/index.js +190 -0
- package/dist/commands/generate/GenerationAnalytics.d.ts +61 -0
- package/dist/commands/generate/GenerationAnalytics.js +162 -0
- package/dist/commands/generate/GenerationAnalytics.test.d.ts +1 -0
- package/dist/commands/generate/GenerationAnalytics.test.js +407 -0
- package/dist/commands/generate/NodeFileWriter.d.ts +5 -0
- package/dist/commands/generate/NodeFileWriter.js +240 -0
- package/dist/commands/generate/NodeGenerator.d.ts +20 -0
- package/dist/commands/generate/NodeGenerator.js +181 -0
- package/dist/commands/generate/NodeGenerator.test.d.ts +1 -0
- package/dist/commands/generate/NodeGenerator.test.js +101 -0
- package/dist/commands/generate/PromptVersioning.d.ts +25 -0
- package/dist/commands/generate/PromptVersioning.js +71 -0
- package/dist/commands/generate/PromptVersioning.test.d.ts +1 -0
- package/dist/commands/generate/PromptVersioning.test.js +120 -0
- package/dist/commands/generate/RegisterNode.d.ts +3 -0
- package/dist/commands/generate/RegisterNode.js +37 -0
- package/dist/commands/generate/RuntimeGenerator.d.ts +40 -0
- package/dist/commands/generate/RuntimeGenerator.js +369 -0
- package/dist/commands/generate/RuntimeGenerator.test.d.ts +1 -0
- package/dist/commands/generate/RuntimeGenerator.test.js +553 -0
- package/dist/commands/generate/TriggerGenerator.d.ts +22 -0
- package/dist/commands/generate/TriggerGenerator.js +220 -0
- package/dist/commands/generate/TriggerGenerator.test.d.ts +1 -0
- package/dist/commands/generate/TriggerGenerator.test.js +209 -0
- package/dist/commands/generate/WorkflowGenerator.d.ts +20 -0
- package/dist/commands/generate/WorkflowGenerator.js +131 -0
- package/dist/commands/generate/WorkflowGenerator.test.d.ts +1 -0
- package/dist/commands/generate/WorkflowGenerator.test.js +77 -0
- package/dist/commands/generate/e2e/NodeGenerator.e2e.test.d.ts +1 -0
- package/dist/commands/generate/e2e/NodeGenerator.e2e.test.js +216 -0
- package/dist/commands/generate/e2e/RuntimeGenerator.e2e.test.d.ts +1 -0
- package/dist/commands/generate/e2e/RuntimeGenerator.e2e.test.js +759 -0
- package/dist/commands/generate/e2e/TriggerGenerator.e2e.test.d.ts +1 -0
- package/dist/commands/generate/e2e/TriggerGenerator.e2e.test.js +295 -0
- package/dist/commands/generate/e2e/WorkflowGenerator.e2e.test.d.ts +1 -0
- package/dist/commands/generate/e2e/WorkflowGenerator.e2e.test.js +353 -0
- package/dist/commands/generate/index.d.ts +1 -0
- package/dist/commands/generate/index.js +418 -0
- package/dist/commands/generate/prompts/create-fn-node.system.d.ts +5 -0
- package/dist/commands/generate/prompts/create-fn-node.system.js +256 -0
- package/dist/commands/generate/prompts/create-node-manifest.system.d.ts +4 -0
- package/dist/commands/generate/prompts/create-node-manifest.system.js +41 -0
- package/dist/commands/generate/prompts/create-node.system.d.ts +5 -0
- package/dist/commands/generate/prompts/create-node.system.js +114 -0
- package/dist/commands/generate/prompts/create-readme.system.d.ts +4 -0
- package/dist/commands/generate/prompts/create-readme.system.js +83 -0
- package/dist/commands/generate/prompts/create-runtime.system.d.ts +5 -0
- package/dist/commands/generate/prompts/create-runtime.system.js +284 -0
- package/dist/commands/generate/prompts/create-trigger.system.d.ts +5 -0
- package/dist/commands/generate/prompts/create-trigger.system.js +293 -0
- package/dist/commands/generate/prompts/create-workflow.system.d.ts +5 -0
- package/dist/commands/generate/prompts/create-workflow.system.js +476 -0
- package/dist/commands/generate/prompts/register-node.system.d.ts +4 -0
- package/dist/commands/generate/prompts/register-node.system.js +26 -0
- package/dist/commands/generate/validators/CompilationValidator.d.ts +9 -0
- package/dist/commands/generate/validators/CompilationValidator.js +86 -0
- package/dist/commands/generate/validators/CompilationValidator.test.d.ts +1 -0
- package/dist/commands/generate/validators/CompilationValidator.test.js +161 -0
- package/dist/commands/generate/validators/NodeValidator.d.ts +18 -0
- package/dist/commands/generate/validators/NodeValidator.js +217 -0
- package/dist/commands/generate/validators/NodeValidator.test.d.ts +1 -0
- package/dist/commands/generate/validators/NodeValidator.test.js +281 -0
- package/dist/commands/generate/validators/WorkflowValidator.d.ts +6 -0
- package/dist/commands/generate/validators/WorkflowValidator.js +301 -0
- package/dist/commands/generate/validators/WorkflowValidator.test.d.ts +1 -0
- package/dist/commands/generate/validators/WorkflowValidator.test.js +647 -0
- package/dist/commands/generate/validators/index.d.ts +4 -0
- package/dist/commands/generate/validators/index.js +2 -0
- package/dist/commands/graph/index.d.ts +1 -0
- package/dist/commands/graph/index.js +69 -0
- package/dist/commands/install/index.d.ts +1 -0
- package/dist/commands/install/index.js +4 -0
- package/dist/commands/install/node.d.ts +4 -0
- package/dist/commands/install/node.js +136 -0
- package/dist/commands/install/workflow.d.ts +4 -0
- package/dist/commands/install/workflow.js +62 -0
- package/dist/commands/login/index.d.ts +2 -0
- package/dist/commands/login/index.js +77 -0
- package/dist/commands/logout/index.d.ts +2 -0
- package/dist/commands/logout/index.js +20 -0
- package/dist/commands/marketplace/runtime.d.ts +54 -0
- package/dist/commands/marketplace/runtime.js +350 -0
- package/dist/commands/migrate/index.d.ts +1 -0
- package/dist/commands/migrate/index.js +14 -0
- package/dist/commands/migrate/node.d.ts +2 -0
- package/dist/commands/migrate/node.js +110 -0
- package/dist/commands/monitor/index.d.ts +1 -0
- package/dist/commands/monitor/index.js +28 -0
- package/dist/commands/monitor/monitor-component.d.ts +1 -0
- package/dist/commands/monitor/monitor-component.js +271 -0
- package/dist/commands/monitor/static/index.html +2124 -0
- package/dist/commands/monitor/static-web-server.d.ts +1 -0
- package/dist/commands/monitor/static-web-server.js +89 -0
- package/dist/commands/profile/index.d.ts +1 -0
- package/dist/commands/profile/index.js +112 -0
- package/dist/commands/publish/index.d.ts +1 -0
- package/dist/commands/publish/index.js +4 -0
- package/dist/commands/publish/node.d.ts +4 -0
- package/dist/commands/publish/node.js +231 -0
- package/dist/commands/publish/workflow.d.ts +4 -0
- package/dist/commands/publish/workflow.js +165 -0
- package/dist/commands/search/docs.d.ts +17 -0
- package/dist/commands/search/docs.js +179 -0
- package/dist/commands/search/index.d.ts +1 -0
- package/dist/commands/search/index.js +5 -0
- package/dist/commands/search/indexer.d.ts +10 -0
- package/dist/commands/search/indexer.js +265 -0
- package/dist/commands/search/nodes.d.ts +4 -0
- package/dist/commands/search/nodes.js +101 -0
- package/dist/commands/search/workflow.d.ts +4 -0
- package/dist/commands/search/workflow.js +100 -0
- package/dist/commands/trace/index.d.ts +1 -0
- package/dist/commands/trace/index.js +26 -0
- package/dist/commands/trace/startStudio.d.ts +8 -0
- package/dist/commands/trace/startStudio.js +116 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.js +186 -0
- package/dist/services/commander.d.ts +9 -0
- package/dist/services/commander.js +20 -0
- package/dist/services/constants.d.ts +1 -0
- package/dist/services/constants.js +3 -0
- package/dist/services/local-token-manager.d.ts +14 -0
- package/dist/services/local-token-manager.js +99 -0
- package/dist/services/non-interactive.d.ts +5 -0
- package/dist/services/non-interactive.js +30 -0
- package/dist/services/package-manager.d.ts +35 -0
- package/dist/services/package-manager.js +111 -0
- package/dist/services/posthog.d.ts +31 -0
- package/dist/services/posthog.js +159 -0
- package/dist/services/registry-manager.d.ts +9 -0
- package/dist/services/registry-manager.js +26 -0
- package/dist/services/runtime-detector.d.ts +23 -0
- package/dist/services/runtime-detector.js +181 -0
- package/dist/services/runtime-setup.d.ts +36 -0
- package/dist/services/runtime-setup.js +250 -0
- package/dist/services/utils.d.ts +2 -0
- package/dist/services/utils.js +29 -0
- package/dist/services/workflow-loader.d.ts +30 -0
- package/dist/services/workflow-loader.js +46 -0
- package/dist/studio-dist/assets/charts-Dso0hPUR.js +68 -0
- package/dist/studio-dist/assets/graph-CsV2nWGn.js +23 -0
- package/dist/studio-dist/assets/icons-zP8LLgPh.js +311 -0
- package/dist/studio-dist/assets/index-CLyEkXMx.css +1 -0
- package/dist/studio-dist/assets/index-CNXFX_ar.js +27 -0
- package/dist/studio-dist/assets/react-vendor--Eh9ivFN.js +17 -0
- package/dist/studio-dist/assets/tanstack-query-CiM1U6F5.js +1 -0
- package/dist/studio-dist/assets/tanstack-router-Btjy0MKq.js +25 -0
- package/dist/studio-dist/assets/tanstack-table-DhwRvuH2.js +22 -0
- package/dist/studio-dist/favicon.svg +5 -0
- package/dist/studio-dist/index.html +21 -0
- package/package.json +75 -0
|
@@ -0,0 +1,983 @@
|
|
|
1
|
+
const node_file = `import ApiCall from "@blok/api-call";
|
|
2
|
+
import IfElse from "@blok/if-else";
|
|
3
|
+
import type { NodeBase } from "@blok/shared";
|
|
4
|
+
import ChainInit from "./nodes/chain-init/index";
|
|
5
|
+
import ChainVerify from "./nodes/chain-verify/index";
|
|
6
|
+
import ExampleNodes from "./nodes/examples/index";
|
|
7
|
+
import RuntimeBridge from "./nodes/runtime-bridge/index";
|
|
8
|
+
|
|
9
|
+
const nodes: {
|
|
10
|
+
[key: string]: NodeBase;
|
|
11
|
+
} = {
|
|
12
|
+
"@blok/api-call": ApiCall,
|
|
13
|
+
"@blok/if-else": IfElse,
|
|
14
|
+
"chain-init": ChainInit,
|
|
15
|
+
"chain-verify": ChainVerify,
|
|
16
|
+
"runtime-bridge": RuntimeBridge,
|
|
17
|
+
...ExampleNodes,
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export default nodes;
|
|
21
|
+
`;
|
|
22
|
+
const package_dependencies = {
|
|
23
|
+
ai: "^4.1.50",
|
|
24
|
+
"@ai-sdk/openai": "^1.2.0",
|
|
25
|
+
ejs: "^3.1.10",
|
|
26
|
+
pg: "^8.13.3",
|
|
27
|
+
mongodb: "^6.14.2",
|
|
28
|
+
};
|
|
29
|
+
const package_dev_dependencies = {
|
|
30
|
+
"@types/ejs": "^3.1.5",
|
|
31
|
+
"@types/pg": "^8.11.11",
|
|
32
|
+
};
|
|
33
|
+
const python3_file = `
|
|
34
|
+
from core.blok import BlokService
|
|
35
|
+
from core.types.context import Context
|
|
36
|
+
from core.types.blok_response import BlokResponse
|
|
37
|
+
from core.types.global_error import GlobalError
|
|
38
|
+
from typing import Any, Dict
|
|
39
|
+
import traceback
|
|
40
|
+
|
|
41
|
+
class Node(BlokService):
|
|
42
|
+
def __init__(self):
|
|
43
|
+
BlokService.__init__(self)
|
|
44
|
+
self.input_schema = {}
|
|
45
|
+
self.output_schema = {}
|
|
46
|
+
|
|
47
|
+
async def handle(self, ctx: Context, inputs: Dict[str, Any]) -> BlokResponse:
|
|
48
|
+
response = BlokResponse()
|
|
49
|
+
|
|
50
|
+
try:
|
|
51
|
+
response.setSuccess({ "message": "Hello World from Python3!" })
|
|
52
|
+
except Exception as error:
|
|
53
|
+
err = GlobalError(error)
|
|
54
|
+
err.setCode(500)
|
|
55
|
+
err.setName(self.name)
|
|
56
|
+
|
|
57
|
+
stack_trace = traceback.format_exc()
|
|
58
|
+
err.setStack(stack_trace)
|
|
59
|
+
response.success = False
|
|
60
|
+
response.setError(err)
|
|
61
|
+
|
|
62
|
+
return response
|
|
63
|
+
`;
|
|
64
|
+
const examples_url = `
|
|
65
|
+
Examples:
|
|
66
|
+
1- Open "workflow-docs.json" in your browser at http://localhost:4000/workflow-docs
|
|
67
|
+
2- Open "db-manager.json" in your browser at http://localhost:4000/db-manager
|
|
68
|
+
3- Open "dashboard-gen.json" in your browser at http://localhost:4000/dashboard-gen
|
|
69
|
+
4- Open "countries.json" in your browser at http://localhost:4000/countries
|
|
70
|
+
|
|
71
|
+
For more documentation, visit src/nodes/examples/README.md. The first three examples require a PostgreSQL database to function.
|
|
72
|
+
`;
|
|
73
|
+
const workflow_template = `
|
|
74
|
+
{
|
|
75
|
+
"name": "",
|
|
76
|
+
"description": "",
|
|
77
|
+
"version": "1.0.0",
|
|
78
|
+
"trigger": {
|
|
79
|
+
"http": {
|
|
80
|
+
"method": "GET",
|
|
81
|
+
"path": "/",
|
|
82
|
+
"accept": "application/json"
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
"steps": [
|
|
86
|
+
{
|
|
87
|
+
"name": "node-name",
|
|
88
|
+
"node": "node-module-name",
|
|
89
|
+
"type": "module"
|
|
90
|
+
}
|
|
91
|
+
],
|
|
92
|
+
"nodes": {
|
|
93
|
+
"name": {
|
|
94
|
+
"inputs": {
|
|
95
|
+
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
`;
|
|
101
|
+
const supervisord_nodejs = `
|
|
102
|
+
[supervisord]
|
|
103
|
+
nodaemon=true
|
|
104
|
+
|
|
105
|
+
[program:nodejs_app]
|
|
106
|
+
command=npm start
|
|
107
|
+
directory=/app
|
|
108
|
+
autostart=true
|
|
109
|
+
autorestart=true
|
|
110
|
+
stderr_logfile=/var/log/nodejs.err.log
|
|
111
|
+
stdout_logfile=/var/log/nodejs.out.log
|
|
112
|
+
`;
|
|
113
|
+
const supervisord_python = `
|
|
114
|
+
[program:python_app]
|
|
115
|
+
command=python3 /app/.blok/runtimes/python3/server.py
|
|
116
|
+
directory=/app
|
|
117
|
+
autostart=true
|
|
118
|
+
autorestart=true
|
|
119
|
+
stderr_logfile=/var/log/python.err.log
|
|
120
|
+
stdout_logfile=/var/log/python.out.log
|
|
121
|
+
`;
|
|
122
|
+
const go_node_file = `package main
|
|
123
|
+
|
|
124
|
+
import (
|
|
125
|
+
"github.com/blok/sdk"
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
type HelloWorldNode struct{}
|
|
129
|
+
|
|
130
|
+
func (n *HelloWorldNode) Execute(ctx *sdk.Context, config map[string]interface{}) (*sdk.ExecutionResult, error) {
|
|
131
|
+
// Access request body
|
|
132
|
+
name := "World"
|
|
133
|
+
if body, ok := ctx.Request.Body.(map[string]interface{}); ok {
|
|
134
|
+
if nameVal, ok := body["name"].(string); ok {
|
|
135
|
+
name = nameVal
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Access configuration
|
|
140
|
+
prefix := "Hello"
|
|
141
|
+
if prefixVal, ok := config["prefix"].(string); ok {
|
|
142
|
+
prefix = prefixVal
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Store result in context for downstream nodes
|
|
146
|
+
ctx.Vars["greeting"] = prefix + ", " + name + "!"
|
|
147
|
+
|
|
148
|
+
// Return successful result
|
|
149
|
+
return &sdk.ExecutionResult{
|
|
150
|
+
Success: true,
|
|
151
|
+
Data: map[string]interface{}{
|
|
152
|
+
"message": prefix + ", " + name + "!",
|
|
153
|
+
"timestamp": sdk.GetCurrentTimestamp(),
|
|
154
|
+
"language": "Go",
|
|
155
|
+
},
|
|
156
|
+
Errors: nil,
|
|
157
|
+
}, nil
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
func main() {
|
|
161
|
+
// Register node
|
|
162
|
+
registry := sdk.NewNodeRegistry()
|
|
163
|
+
registry.Register("{{NODE_NAME}}", &HelloWorldNode{})
|
|
164
|
+
|
|
165
|
+
// Start HTTP server
|
|
166
|
+
server := sdk.NewHTTPServer(registry, ":8080")
|
|
167
|
+
if err := server.Start(); err != nil {
|
|
168
|
+
panic(err)
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
`;
|
|
172
|
+
const go_mod_file = `module github.com/blok/nodes/{{NODE_NAME}}
|
|
173
|
+
|
|
174
|
+
go 1.21
|
|
175
|
+
|
|
176
|
+
require github.com/blok/sdk v1.0.0
|
|
177
|
+
`;
|
|
178
|
+
const go_dockerfile = `FROM golang:1.21-alpine AS builder
|
|
179
|
+
|
|
180
|
+
WORKDIR /app
|
|
181
|
+
COPY go.mod go.sum ./
|
|
182
|
+
RUN go mod download
|
|
183
|
+
|
|
184
|
+
COPY . .
|
|
185
|
+
RUN CGO_ENABLED=0 GOOS=linux go build -o /node main.go
|
|
186
|
+
|
|
187
|
+
FROM alpine:latest
|
|
188
|
+
RUN apk --no-cache add ca-certificates
|
|
189
|
+
WORKDIR /root/
|
|
190
|
+
COPY --from=builder /node .
|
|
191
|
+
|
|
192
|
+
EXPOSE 8080
|
|
193
|
+
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \\
|
|
194
|
+
CMD wget --no-verbose --tries=1 --spider http://localhost:8080/health || exit 1
|
|
195
|
+
|
|
196
|
+
CMD ["./node"]
|
|
197
|
+
`;
|
|
198
|
+
const java_node_file = `package com.blok.nodes;
|
|
199
|
+
|
|
200
|
+
import com.blok.runtime.Blok;
|
|
201
|
+
import com.blok.runtime.NodeRegistry;
|
|
202
|
+
import com.blok.server.RuntimeServer;
|
|
203
|
+
import java.util.HashMap;
|
|
204
|
+
import java.util.Map;
|
|
205
|
+
|
|
206
|
+
public class HelloWorldNode implements Blok.NodeHandler {
|
|
207
|
+
@Override
|
|
208
|
+
public Blok.ExecutionResult execute(Blok.Context ctx, Map<String, Object> config) {
|
|
209
|
+
try {
|
|
210
|
+
// Access request body
|
|
211
|
+
String name = "World";
|
|
212
|
+
if (ctx.request.body != null && ctx.request.body.containsKey("name")) {
|
|
213
|
+
name = (String) ctx.request.body.get("name");
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Access configuration
|
|
217
|
+
String prefix = "Hello";
|
|
218
|
+
if (config != null && config.containsKey("prefix")) {
|
|
219
|
+
prefix = (String) config.get("prefix");
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Store result in context for downstream nodes
|
|
223
|
+
String greeting = prefix + ", " + name + "!";
|
|
224
|
+
ctx.vars.put("greeting", greeting);
|
|
225
|
+
|
|
226
|
+
// Build response data
|
|
227
|
+
Map<String, Object> data = new HashMap<>();
|
|
228
|
+
data.put("message", greeting);
|
|
229
|
+
data.put("timestamp", System.currentTimeMillis());
|
|
230
|
+
data.put("language", "Java");
|
|
231
|
+
|
|
232
|
+
// Return successful result
|
|
233
|
+
return new Blok.ExecutionResult(true, data, null, null, null);
|
|
234
|
+
} catch (Exception e) {
|
|
235
|
+
return new Blok.ExecutionResult(false, null, e.getMessage(), null, null);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
public static void main(String[] args) {
|
|
240
|
+
try {
|
|
241
|
+
// Register node
|
|
242
|
+
NodeRegistry registry = new NodeRegistry();
|
|
243
|
+
registry.register("{{NODE_NAME}}", new HelloWorldNode());
|
|
244
|
+
|
|
245
|
+
// Start HTTP server
|
|
246
|
+
RuntimeServer server = new RuntimeServer(registry, 8080);
|
|
247
|
+
server.start();
|
|
248
|
+
} catch (Exception e) {
|
|
249
|
+
e.printStackTrace();
|
|
250
|
+
System.exit(1);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
`;
|
|
255
|
+
const java_pom_file = `<?xml version="1.0" encoding="UTF-8"?>
|
|
256
|
+
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
|
257
|
+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
258
|
+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
|
|
259
|
+
http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
|
260
|
+
<modelVersion>4.0.0</modelVersion>
|
|
261
|
+
|
|
262
|
+
<groupId>com.blok</groupId>
|
|
263
|
+
<artifactId>{{NODE_NAME}}</artifactId>
|
|
264
|
+
<version>1.0.0</version>
|
|
265
|
+
|
|
266
|
+
<properties>
|
|
267
|
+
<maven.compiler.source>17</maven.compiler.source>
|
|
268
|
+
<maven.compiler.target>17</maven.compiler.target>
|
|
269
|
+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
|
270
|
+
</properties>
|
|
271
|
+
|
|
272
|
+
<dependencies>
|
|
273
|
+
<dependency>
|
|
274
|
+
<groupId>com.google.code.gson</groupId>
|
|
275
|
+
<artifactId>gson</artifactId>
|
|
276
|
+
<version>2.10.1</version>
|
|
277
|
+
</dependency>
|
|
278
|
+
</dependencies>
|
|
279
|
+
|
|
280
|
+
<build>
|
|
281
|
+
<plugins>
|
|
282
|
+
<plugin>
|
|
283
|
+
<groupId>org.apache.maven.plugins</groupId>
|
|
284
|
+
<artifactId>maven-shade-plugin</artifactId>
|
|
285
|
+
<version>3.5.0</version>
|
|
286
|
+
<executions>
|
|
287
|
+
<execution>
|
|
288
|
+
<phase>package</phase>
|
|
289
|
+
<goals>
|
|
290
|
+
<goal>shade</goal>
|
|
291
|
+
</goals>
|
|
292
|
+
<configuration>
|
|
293
|
+
<transformers>
|
|
294
|
+
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
|
|
295
|
+
<mainClass>com.blok.nodes.HelloWorldNode</mainClass>
|
|
296
|
+
</transformer>
|
|
297
|
+
</transformers>
|
|
298
|
+
</configuration>
|
|
299
|
+
</execution>
|
|
300
|
+
</executions>
|
|
301
|
+
</plugin>
|
|
302
|
+
</plugins>
|
|
303
|
+
</build>
|
|
304
|
+
</project>
|
|
305
|
+
`;
|
|
306
|
+
const java_dockerfile = `FROM maven:3.9-eclipse-temurin-17 AS builder
|
|
307
|
+
|
|
308
|
+
WORKDIR /app
|
|
309
|
+
COPY pom.xml .
|
|
310
|
+
RUN mvn dependency:go-offline
|
|
311
|
+
|
|
312
|
+
COPY src ./src
|
|
313
|
+
RUN mvn clean package -DskipTests
|
|
314
|
+
|
|
315
|
+
FROM eclipse-temurin:17-jre-alpine
|
|
316
|
+
|
|
317
|
+
WORKDIR /root/
|
|
318
|
+
COPY --from=builder /app/target/{{NODE_NAME}}-1.0.0.jar ./app.jar
|
|
319
|
+
|
|
320
|
+
EXPOSE 8080
|
|
321
|
+
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \\
|
|
322
|
+
CMD wget --no-verbose --tries=1 --spider http://localhost:8080/health || exit 1
|
|
323
|
+
|
|
324
|
+
CMD ["java", "-jar", "app.jar"]
|
|
325
|
+
`;
|
|
326
|
+
const rust_node_file = `use async_trait::async_trait;
|
|
327
|
+
use blok::{NodeHandler, NodeRegistry, Context};
|
|
328
|
+
use std::collections::HashMap;
|
|
329
|
+
|
|
330
|
+
/// {{NODE_NAME}} - A Blok node implemented in Rust
|
|
331
|
+
struct {{NODE_NAME_PASCAL}};
|
|
332
|
+
|
|
333
|
+
#[async_trait]
|
|
334
|
+
impl NodeHandler for {{NODE_NAME_PASCAL}} {
|
|
335
|
+
async fn execute(
|
|
336
|
+
&self,
|
|
337
|
+
ctx: &mut Context,
|
|
338
|
+
config: &HashMap<String, serde_json::Value>,
|
|
339
|
+
) -> Result<serde_json::Value, Box<dyn std::error::Error + Send + Sync>> {
|
|
340
|
+
// Access request body
|
|
341
|
+
let name = ctx.request.body
|
|
342
|
+
.get("name")
|
|
343
|
+
.and_then(|v| v.as_str())
|
|
344
|
+
.unwrap_or("World");
|
|
345
|
+
|
|
346
|
+
// Access configuration
|
|
347
|
+
let prefix = config
|
|
348
|
+
.get("prefix")
|
|
349
|
+
.and_then(|v| v.as_str())
|
|
350
|
+
.unwrap_or("Hello");
|
|
351
|
+
|
|
352
|
+
let message = format!("{}, {}!", prefix, name);
|
|
353
|
+
|
|
354
|
+
// Store in context vars for downstream nodes
|
|
355
|
+
ctx.vars.insert(
|
|
356
|
+
"greeting".to_string(),
|
|
357
|
+
serde_json::Value::String(message.clone()),
|
|
358
|
+
);
|
|
359
|
+
|
|
360
|
+
// Return response
|
|
361
|
+
Ok(serde_json::json!({
|
|
362
|
+
"message": message,
|
|
363
|
+
"language": "Rust"
|
|
364
|
+
}))
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
#[tokio::main]
|
|
369
|
+
async fn main() {
|
|
370
|
+
// Initialize tracing
|
|
371
|
+
tracing_subscriber::fmt::init();
|
|
372
|
+
|
|
373
|
+
// Register nodes
|
|
374
|
+
let mut registry = NodeRegistry::new("1.0.0");
|
|
375
|
+
registry.register("{{NODE_NAME}}", {{NODE_NAME_PASCAL}});
|
|
376
|
+
|
|
377
|
+
// Start HTTP server
|
|
378
|
+
blok::server::serve(registry, 8080).await.unwrap();
|
|
379
|
+
}
|
|
380
|
+
`;
|
|
381
|
+
const rust_cargo_file = `[package]
|
|
382
|
+
name = "{{NODE_NAME}}"
|
|
383
|
+
version = "1.0.0"
|
|
384
|
+
edition = "2021"
|
|
385
|
+
|
|
386
|
+
[[bin]]
|
|
387
|
+
name = "{{NODE_NAME}}"
|
|
388
|
+
path = "src/main.rs"
|
|
389
|
+
|
|
390
|
+
[dependencies]
|
|
391
|
+
blok = { path = "../../sdk" }
|
|
392
|
+
tokio = { version = "1", features = ["full"] }
|
|
393
|
+
serde = { version = "1", features = ["derive"] }
|
|
394
|
+
serde_json = "1"
|
|
395
|
+
async-trait = "0.1"
|
|
396
|
+
tracing = "0.1"
|
|
397
|
+
tracing-subscriber = "0.3"
|
|
398
|
+
`;
|
|
399
|
+
const rust_dockerfile = `FROM rust:1.77-alpine AS builder
|
|
400
|
+
|
|
401
|
+
RUN apk add --no-cache musl-dev
|
|
402
|
+
|
|
403
|
+
WORKDIR /app
|
|
404
|
+
COPY Cargo.toml Cargo.lock ./
|
|
405
|
+
RUN mkdir -p src && echo 'fn main() {}' > src/main.rs && \\
|
|
406
|
+
cargo build --release 2>/dev/null || true && rm -rf src
|
|
407
|
+
|
|
408
|
+
COPY . .
|
|
409
|
+
RUN cargo build --release
|
|
410
|
+
|
|
411
|
+
FROM alpine:latest
|
|
412
|
+
RUN apk --no-cache add ca-certificates
|
|
413
|
+
WORKDIR /root/
|
|
414
|
+
COPY --from=builder /app/target/release/{{NODE_NAME}} .
|
|
415
|
+
|
|
416
|
+
EXPOSE 8080
|
|
417
|
+
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \\
|
|
418
|
+
CMD wget --no-verbose --tries=1 --spider http://localhost:8080/health || exit 1
|
|
419
|
+
|
|
420
|
+
CMD ["./{{NODE_NAME}}"]
|
|
421
|
+
`;
|
|
422
|
+
const csharp_node_file = `using System.Text.Json;
|
|
423
|
+
using Blok.Runtime;
|
|
424
|
+
|
|
425
|
+
namespace Blok.Runtime.Nodes;
|
|
426
|
+
|
|
427
|
+
public class {{NODE_NAME_PASCAL}}Node : INodeHandler
|
|
428
|
+
{
|
|
429
|
+
public Task<JsonElement> ExecuteAsync(Context ctx, Dictionary<string, JsonElement> config)
|
|
430
|
+
{
|
|
431
|
+
// Access request body
|
|
432
|
+
var name = "World";
|
|
433
|
+
if (ctx.Request.Body.ValueKind == JsonValueKind.Object &&
|
|
434
|
+
ctx.Request.Body.TryGetProperty("name", out var nameEl) &&
|
|
435
|
+
nameEl.ValueKind == JsonValueKind.String)
|
|
436
|
+
{
|
|
437
|
+
name = nameEl.GetString() ?? "World";
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
// Access configuration
|
|
441
|
+
var prefix = "Hello";
|
|
442
|
+
if (config.TryGetValue("prefix", out var prefixEl) &&
|
|
443
|
+
prefixEl.ValueKind == JsonValueKind.String)
|
|
444
|
+
{
|
|
445
|
+
prefix = prefixEl.GetString() ?? "Hello";
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
var message = $"{prefix}, {name}!";
|
|
449
|
+
|
|
450
|
+
// Store in context for downstream nodes
|
|
451
|
+
ctx.Vars["greeting"] = JsonSerializer.SerializeToElement(message);
|
|
452
|
+
|
|
453
|
+
// Return response
|
|
454
|
+
var result = JsonSerializer.SerializeToElement(new
|
|
455
|
+
{
|
|
456
|
+
message,
|
|
457
|
+
timestamp = DateTime.UtcNow.ToString("o"),
|
|
458
|
+
language = "C#"
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
return Task.FromResult(result);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
`;
|
|
465
|
+
const csharp_csproj_file = `<Project Sdk="Microsoft.NET.Sdk.Web">
|
|
466
|
+
<PropertyGroup>
|
|
467
|
+
<TargetFramework>net8.0</TargetFramework>
|
|
468
|
+
<RootNamespace>Blok.Runtime</RootNamespace>
|
|
469
|
+
<ImplicitUsings>enable</ImplicitUsings>
|
|
470
|
+
<Nullable>enable</Nullable>
|
|
471
|
+
</PropertyGroup>
|
|
472
|
+
</Project>
|
|
473
|
+
`;
|
|
474
|
+
const csharp_dockerfile = `FROM mcr.microsoft.com/dotnet/sdk:8.0 AS builder
|
|
475
|
+
WORKDIR /app
|
|
476
|
+
COPY *.csproj .
|
|
477
|
+
RUN dotnet restore
|
|
478
|
+
COPY . .
|
|
479
|
+
RUN dotnet publish -c Release -o /out
|
|
480
|
+
|
|
481
|
+
FROM mcr.microsoft.com/dotnet/aspnet:8.0-alpine
|
|
482
|
+
WORKDIR /app
|
|
483
|
+
COPY --from=builder /out .
|
|
484
|
+
|
|
485
|
+
EXPOSE 8080
|
|
486
|
+
ENV PORT=8080
|
|
487
|
+
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \\
|
|
488
|
+
CMD wget --no-verbose --tries=1 --spider http://localhost:8080/health || exit 1
|
|
489
|
+
|
|
490
|
+
CMD ["dotnet", "BlokRuntime.dll"]
|
|
491
|
+
`;
|
|
492
|
+
const php_node_file = `<?php
|
|
493
|
+
|
|
494
|
+
namespace Blok\\Nodes;
|
|
495
|
+
|
|
496
|
+
use Blok\\NodeHandler;
|
|
497
|
+
use Blok\\Context;
|
|
498
|
+
|
|
499
|
+
class {{NODE_NAME_PASCAL}}Node implements NodeHandler
|
|
500
|
+
{
|
|
501
|
+
public function execute(Context $ctx, array $config): mixed
|
|
502
|
+
{
|
|
503
|
+
// Access request body
|
|
504
|
+
$name = $ctx->request->body['name'] ?? 'World';
|
|
505
|
+
|
|
506
|
+
// Access configuration
|
|
507
|
+
$prefix = $config['prefix'] ?? 'Hello';
|
|
508
|
+
|
|
509
|
+
$message = "$prefix, $name!";
|
|
510
|
+
|
|
511
|
+
// Store in context for downstream nodes
|
|
512
|
+
$ctx->vars['greeting'] = $message;
|
|
513
|
+
|
|
514
|
+
// Return response
|
|
515
|
+
return [
|
|
516
|
+
'message' => $message,
|
|
517
|
+
'timestamp' => date('c'),
|
|
518
|
+
'language' => 'PHP',
|
|
519
|
+
];
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
`;
|
|
523
|
+
const php_composer_file = `{
|
|
524
|
+
"name": "blok/{{NODE_NAME}}",
|
|
525
|
+
"type": "project",
|
|
526
|
+
"require": {
|
|
527
|
+
"php": ">=8.2",
|
|
528
|
+
"react/http": "^1.9",
|
|
529
|
+
"react/socket": "^1.15"
|
|
530
|
+
},
|
|
531
|
+
"autoload": {
|
|
532
|
+
"psr-4": {
|
|
533
|
+
"Blok\\\\": "src/"
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
`;
|
|
538
|
+
const php_dockerfile = `FROM php:8.2-cli-alpine AS builder
|
|
539
|
+
WORKDIR /app
|
|
540
|
+
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
|
|
541
|
+
COPY composer.json .
|
|
542
|
+
RUN composer install --no-dev --optimize-autoloader
|
|
543
|
+
COPY . .
|
|
544
|
+
|
|
545
|
+
FROM php:8.2-cli-alpine
|
|
546
|
+
WORKDIR /app
|
|
547
|
+
COPY --from=builder /app .
|
|
548
|
+
|
|
549
|
+
EXPOSE 8080
|
|
550
|
+
ENV PORT=8080
|
|
551
|
+
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \\
|
|
552
|
+
CMD wget --no-verbose --tries=1 --spider http://localhost:8080/health || exit 1
|
|
553
|
+
|
|
554
|
+
CMD ["php", "index.php"]
|
|
555
|
+
`;
|
|
556
|
+
const ruby_node_file = `require_relative '../../lib/blok'
|
|
557
|
+
|
|
558
|
+
module Blok
|
|
559
|
+
module Nodes
|
|
560
|
+
class {{NODE_NAME_PASCAL}}Node < Blok::NodeHandler
|
|
561
|
+
def execute(ctx, config)
|
|
562
|
+
# Access request body
|
|
563
|
+
name = ctx.request.body.is_a?(Hash) ? ctx.request.body['name'] : nil
|
|
564
|
+
name ||= 'World'
|
|
565
|
+
|
|
566
|
+
# Access configuration
|
|
567
|
+
prefix = config['prefix'] || 'Hello'
|
|
568
|
+
|
|
569
|
+
message = "#{prefix}, #{name}!"
|
|
570
|
+
|
|
571
|
+
# Store in context for downstream nodes
|
|
572
|
+
ctx.vars['greeting'] = message
|
|
573
|
+
|
|
574
|
+
# Return response
|
|
575
|
+
{
|
|
576
|
+
'message' => message,
|
|
577
|
+
'timestamp' => Time.now.utc.iso8601,
|
|
578
|
+
'language' => 'Ruby'
|
|
579
|
+
}
|
|
580
|
+
end
|
|
581
|
+
end
|
|
582
|
+
end
|
|
583
|
+
end
|
|
584
|
+
`;
|
|
585
|
+
const ruby_gemfile = `source 'https://rubygems.org'
|
|
586
|
+
|
|
587
|
+
ruby '>= 3.1'
|
|
588
|
+
|
|
589
|
+
gem 'sinatra', '~> 4.0'
|
|
590
|
+
gem 'puma', '~> 6.4'
|
|
591
|
+
gem 'rackup', '~> 2.1'
|
|
592
|
+
`;
|
|
593
|
+
const ruby_dockerfile = `FROM ruby:3.2-alpine AS builder
|
|
594
|
+
RUN apk add --no-cache build-base
|
|
595
|
+
WORKDIR /app
|
|
596
|
+
COPY Gemfile Gemfile.lock ./
|
|
597
|
+
RUN bundle install --without development test
|
|
598
|
+
|
|
599
|
+
FROM ruby:3.2-alpine
|
|
600
|
+
RUN apk --no-cache add ca-certificates wget
|
|
601
|
+
WORKDIR /app
|
|
602
|
+
COPY --from=builder /usr/local/bundle /usr/local/bundle
|
|
603
|
+
COPY . .
|
|
604
|
+
|
|
605
|
+
EXPOSE 8080
|
|
606
|
+
ENV PORT=8080
|
|
607
|
+
ENV RACK_ENV=production
|
|
608
|
+
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \\
|
|
609
|
+
CMD wget --no-verbose --tries=1 --spider http://localhost:8080/health || exit 1
|
|
610
|
+
|
|
611
|
+
CMD ["bundle", "exec", "puma", "-b", "tcp://0.0.0.0:8080"]
|
|
612
|
+
`;
|
|
613
|
+
const agents_md = `# Blok Project
|
|
614
|
+
|
|
615
|
+
Blok is a TypeScript-first workflow orchestration framework. It executes declarative workflows (JSON or TypeScript DSL) composed of steps (nodes) that run across 8 language runtimes: NodeJS, Python3, Go, Rust, Java, C#, PHP, and Ruby.
|
|
616
|
+
|
|
617
|
+
## Project Structure
|
|
618
|
+
|
|
619
|
+
\`\`\`
|
|
620
|
+
├── src/
|
|
621
|
+
│ └── nodes/ # TypeScript node implementations
|
|
622
|
+
├── runtimes/ # Non-NodeJS runtime nodes (Go, Python3, etc.)
|
|
623
|
+
│ └── {lang}/nodes/ # Language-specific node implementations
|
|
624
|
+
├── workflows/
|
|
625
|
+
│ ├── json/ # Workflow definitions (JSON)
|
|
626
|
+
│ ├── yaml/ # Workflow definitions (YAML)
|
|
627
|
+
│ └── toml/ # Workflow definitions (TOML)
|
|
628
|
+
├── .blok/
|
|
629
|
+
│ ├── config.json # Runtime configuration (ports, start commands)
|
|
630
|
+
│ └── runtimes/ # Auto-generated runtime scaffolds
|
|
631
|
+
├── .env.local # Environment variables (ports, paths)
|
|
632
|
+
└── supervisord.conf # Process management config
|
|
633
|
+
\`\`\`
|
|
634
|
+
|
|
635
|
+
## Commands
|
|
636
|
+
|
|
637
|
+
\`\`\`bash
|
|
638
|
+
npm run dev # Start dev server (or blokctl dev for multi-runtime)
|
|
639
|
+
npm run build # Build project
|
|
640
|
+
npm test # Run tests
|
|
641
|
+
blokctl create node <name> # Scaffold a new node
|
|
642
|
+
blokctl create workflow <n># Scaffold a new workflow
|
|
643
|
+
blokctl trace # Open Blok Studio (trace visualization)
|
|
644
|
+
blokctl studio # Alias for blokctl trace
|
|
645
|
+
\`\`\`
|
|
646
|
+
|
|
647
|
+
## Context — Critical Data Flow
|
|
648
|
+
|
|
649
|
+
The Context type is the central execution state passed through every step.
|
|
650
|
+
|
|
651
|
+
\`\`\`typescript
|
|
652
|
+
type Context = {
|
|
653
|
+
id: string; // Unique request ID
|
|
654
|
+
request: RequestContext; // Incoming request (body, headers, params, query)
|
|
655
|
+
response: ResponseContext; // Current step output — OVERWRITTEN every step
|
|
656
|
+
vars?: VarsContext; // Persistent variables — PERSISTS across workflow
|
|
657
|
+
config: ConfigContext; // Node config (inputs resolved by Mapper)
|
|
658
|
+
env?: EnvContext; // process.env access
|
|
659
|
+
logger: LoggerContext;
|
|
660
|
+
error: ErrorContext;
|
|
661
|
+
};
|
|
662
|
+
\`\`\`
|
|
663
|
+
|
|
664
|
+
### The Two Critical Rules
|
|
665
|
+
|
|
666
|
+
**Rule 1: \\\`ctx.response.data\\\` is OVERWRITTEN after every step.**
|
|
667
|
+
Each step's output replaces the previous \\\`ctx.response.data\\\`. If you need a step's output later, store it in \\\`ctx.vars\\\`.
|
|
668
|
+
|
|
669
|
+
**Rule 2: \\\`ctx.vars\\\` PERSISTS across the entire workflow.**
|
|
670
|
+
Use \\\`set_var: true\\\` on a step to auto-store its output in \\\`ctx.vars[stepName]\\\`. Downstream steps access it via \\\`ctx.vars['step-name']\\\`.
|
|
671
|
+
|
|
672
|
+
### Data Flow Example
|
|
673
|
+
|
|
674
|
+
\`\`\`
|
|
675
|
+
Step 1: "fetch-user" (set_var: true)
|
|
676
|
+
→ ctx.response.data = { id: "123", name: "Alice" }
|
|
677
|
+
→ ctx.vars["fetch-user"] = { id: "123", name: "Alice" }
|
|
678
|
+
|
|
679
|
+
Step 2: "transform"
|
|
680
|
+
→ ctx.response.data = { result: "done" } ← Step 1 output GONE from response
|
|
681
|
+
→ ctx.vars["fetch-user"] still available
|
|
682
|
+
|
|
683
|
+
Step 3: "output"
|
|
684
|
+
→ Can read ctx.vars["fetch-user"].name ← still "Alice"
|
|
685
|
+
\`\`\`
|
|
686
|
+
|
|
687
|
+
### Blueprint Mapper — Expression Resolution
|
|
688
|
+
|
|
689
|
+
Node inputs support dynamic expressions resolved BEFORE node execution:
|
|
690
|
+
|
|
691
|
+
\`\`\`json
|
|
692
|
+
{
|
|
693
|
+
"inputs": {
|
|
694
|
+
"userId": "js/ctx.request.body.userId",
|
|
695
|
+
"chain": "js/ctx.vars['previous-step'].chain",
|
|
696
|
+
"previous": "js/ctx.response.data.result"
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
\`\`\`
|
|
700
|
+
|
|
701
|
+
Available in js/ expressions: \\\`ctx\\\`, \\\`data\\\` (ctx.response.data), \\\`func\\\` (ctx.func), \\\`vars\\\` (ctx.vars)
|
|
702
|
+
|
|
703
|
+
---
|
|
704
|
+
|
|
705
|
+
## Creating Nodes with defineNode
|
|
706
|
+
|
|
707
|
+
Use \\\`defineNode()\\\` for all new nodes. Never use the legacy class-based pattern.
|
|
708
|
+
|
|
709
|
+
\`\`\`typescript
|
|
710
|
+
import { defineNode } from "@blok/runner";
|
|
711
|
+
import { z } from "zod";
|
|
712
|
+
|
|
713
|
+
export default defineNode({
|
|
714
|
+
name: "fetch-user",
|
|
715
|
+
description: "Fetches user by ID",
|
|
716
|
+
|
|
717
|
+
input: z.object({
|
|
718
|
+
userId: z.string().uuid(),
|
|
719
|
+
}),
|
|
720
|
+
|
|
721
|
+
output: z.object({
|
|
722
|
+
user: z.object({
|
|
723
|
+
id: z.string(),
|
|
724
|
+
name: z.string(),
|
|
725
|
+
email: z.string().email(),
|
|
726
|
+
}),
|
|
727
|
+
}),
|
|
728
|
+
|
|
729
|
+
async execute(ctx, input) {
|
|
730
|
+
const user = await fetchUser(input.userId);
|
|
731
|
+
return { user };
|
|
732
|
+
},
|
|
733
|
+
});
|
|
734
|
+
\`\`\`
|
|
735
|
+
|
|
736
|
+
### Key Behaviors
|
|
737
|
+
|
|
738
|
+
- Zod input/output validation runs automatically
|
|
739
|
+
- ZodError is mapped to GlobalError with HTTP 400
|
|
740
|
+
- \\\`flow: true\\\` nodes return NodeBase[] for conditional execution
|
|
741
|
+
- \\\`contentType\\\` sets response Content-Type (e.g., "text/html")
|
|
742
|
+
- Always \\\`export default defineNode(...)\\\`
|
|
743
|
+
|
|
744
|
+
---
|
|
745
|
+
|
|
746
|
+
## Workflow Structure (JSON)
|
|
747
|
+
|
|
748
|
+
\`\`\`json
|
|
749
|
+
{
|
|
750
|
+
"name": "My Workflow",
|
|
751
|
+
"version": "1.0.0",
|
|
752
|
+
"trigger": {
|
|
753
|
+
"http": { "method": "POST", "path": "/api/process", "accept": "application/json" }
|
|
754
|
+
},
|
|
755
|
+
"steps": [
|
|
756
|
+
{ "name": "fetch", "node": "@blok/api-call", "type": "module" },
|
|
757
|
+
{ "name": "process", "node": "my-node", "type": "module", "set_var": true },
|
|
758
|
+
{ "name": "go-step", "node": "chain-test", "type": "runtime.go" }
|
|
759
|
+
],
|
|
760
|
+
"nodes": {
|
|
761
|
+
"fetch": { "inputs": { "url": "https://api.example.com", "method": "GET" } },
|
|
762
|
+
"process": { "inputs": { "data": "js/ctx.response.data" } },
|
|
763
|
+
"go-step": { "inputs": { "processed": "js/ctx.vars['process']" } }
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
\`\`\`
|
|
767
|
+
|
|
768
|
+
### Step Types
|
|
769
|
+
|
|
770
|
+
| Type | Description |
|
|
771
|
+
|------|-------------|
|
|
772
|
+
| \\\`module\\\` | TypeScript node from registered modules |
|
|
773
|
+
| \\\`local\\\` | TypeScript node from filesystem (NODES_PATH) |
|
|
774
|
+
| \\\`runtime.python3\\\` | Python3 SDK container (port 9007) |
|
|
775
|
+
| \\\`runtime.go\\\` | Go SDK container (port 9001) |
|
|
776
|
+
| \\\`runtime.rust\\\` | Rust SDK container (port 9002) |
|
|
777
|
+
| \\\`runtime.java\\\` | Java SDK container (port 9003) |
|
|
778
|
+
| \\\`runtime.csharp\\\` | C# SDK container (port 9004) |
|
|
779
|
+
| \\\`runtime.php\\\` | PHP SDK container (port 9005) |
|
|
780
|
+
| \\\`runtime.ruby\\\` | Ruby SDK container (port 9006) |
|
|
781
|
+
|
|
782
|
+
### Conditional Workflow (if-else)
|
|
783
|
+
|
|
784
|
+
\`\`\`json
|
|
785
|
+
{
|
|
786
|
+
"nodes": {
|
|
787
|
+
"filter": {
|
|
788
|
+
"conditions": [
|
|
789
|
+
{
|
|
790
|
+
"type": "if",
|
|
791
|
+
"condition": "ctx.request.query.active === \\\\"true\\\\"",
|
|
792
|
+
"steps": [{ "name": "active-path", "node": "handle-active", "type": "module" }]
|
|
793
|
+
},
|
|
794
|
+
{
|
|
795
|
+
"type": "else",
|
|
796
|
+
"steps": [{ "name": "default-path", "node": "handle-default", "type": "module" }]
|
|
797
|
+
}
|
|
798
|
+
]
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
\`\`\`
|
|
803
|
+
|
|
804
|
+
---
|
|
805
|
+
|
|
806
|
+
## Trigger Types
|
|
807
|
+
|
|
808
|
+
| Trigger | Example Config |
|
|
809
|
+
|---------|---------------|
|
|
810
|
+
| \\\`http\\\` | \\\`{ "method": "GET", "path": "/", "accept": "application/json" }\\\` |
|
|
811
|
+
| \\\`grpc\\\` | \\\`{ "service": "UserService", "method": "GetUser" }\\\` |
|
|
812
|
+
| \\\`cron\\\` | \\\`{ "schedule": "0 * * * *", "timezone": "UTC" }\\\` |
|
|
813
|
+
| \\\`queue\\\` | \\\`{ "provider": "kafka", "topic": "events" }\\\` |
|
|
814
|
+
| \\\`pubsub\\\` | \\\`{ "provider": "gcp", "topic": "updates" }\\\` |
|
|
815
|
+
| \\\`webhook\\\` | \\\`{ "source": "github", "events": ["push"] }\\\` |
|
|
816
|
+
| \\\`websocket\\\` | \\\`{ "events": ["message"], "path": "/ws" }\\\` |
|
|
817
|
+
| \\\`sse\\\` | \\\`{ "events": ["update"], "path": "/stream" }\\\` |
|
|
818
|
+
| \\\`worker\\\` | \\\`{ "queue": "jobs", "concurrency": 5 }\\\` |
|
|
819
|
+
|
|
820
|
+
---
|
|
821
|
+
|
|
822
|
+
## Runtime Adapter System
|
|
823
|
+
|
|
824
|
+
All non-NodeJS SDKs communicate via HTTP:
|
|
825
|
+
- **POST /execute** — Execute node with context
|
|
826
|
+
- **GET /health** — Health check
|
|
827
|
+
|
|
828
|
+
Environment variables: \\\`RUNTIME_{LANG}_HOST\\\` / \\\`RUNTIME_{LANG}_PORT\\\`
|
|
829
|
+
|
|
830
|
+
Runtime nodes auto-save \\\`result.data\\\` to \\\`ctx.vars[stepName]\\\`.
|
|
831
|
+
|
|
832
|
+
---
|
|
833
|
+
|
|
834
|
+
## Blok Studio
|
|
835
|
+
|
|
836
|
+
Real-time workflow trace visualization UI.
|
|
837
|
+
|
|
838
|
+
- Launch: \\\`blokctl trace\\\` or \\\`blokctl studio\\\`
|
|
839
|
+
- API: \\\`/__blok/runs\\\`, \\\`/__blok/runs/:id\\\`, \\\`/__blok/runs/:id/stream\\\` (SSE)
|
|
840
|
+
- Disable: \\\`BLOK_TRACE_ENABLED=false\\\`
|
|
841
|
+
|
|
842
|
+
---
|
|
843
|
+
|
|
844
|
+
## Do NOT
|
|
845
|
+
|
|
846
|
+
- Do NOT rely on \\\`ctx.response.data\\\` for data from non-previous steps — it gets overwritten
|
|
847
|
+
- Do NOT create class-based nodes — use \\\`defineNode()\\\` instead
|
|
848
|
+
- Do NOT use \\\`any\\\` type — use \\\`unknown\\\` and narrow with Zod
|
|
849
|
+
- Do NOT hardcode runtime ports — use environment variables
|
|
850
|
+
- Do NOT skip Zod input/output schemas
|
|
851
|
+
- Do NOT edit files in \\\`.blok/runtimes/\\\` — they are auto-generated
|
|
852
|
+
|
|
853
|
+
## Do
|
|
854
|
+
|
|
855
|
+
- Use \\\`ctx.vars\\\` with \\\`set_var: true\\\` to pass data between non-adjacent steps
|
|
856
|
+
- Use \\\`js/ctx.vars['step-name'].field\\\` in workflow inputs for data flow
|
|
857
|
+
- Use Zod schemas for all input/output validation
|
|
858
|
+
- Use \\\`defineNode()\\\` for all new nodes
|
|
859
|
+
- Handle errors via GlobalError with appropriate HTTP status codes
|
|
860
|
+
- Keep nodes focused — one responsibility per node
|
|
861
|
+
`;
|
|
862
|
+
const claude_md = `# Blok Project — Claude Code Guide
|
|
863
|
+
|
|
864
|
+
Read \\\`AGENTS.md\\\` for full architecture and API details. This file contains Claude-specific guidance.
|
|
865
|
+
|
|
866
|
+
## Quick Commands
|
|
867
|
+
|
|
868
|
+
\\\`\\\`\\\`bash
|
|
869
|
+
npm run dev # Start dev server
|
|
870
|
+
blokctl dev # Multi-runtime dev server
|
|
871
|
+
blokctl create node <name> # Scaffold new node
|
|
872
|
+
blokctl create workflow <name> # Scaffold new workflow
|
|
873
|
+
blokctl trace # Open Blok Studio
|
|
874
|
+
npm test # Run tests
|
|
875
|
+
\\\`\\\`\\\`
|
|
876
|
+
|
|
877
|
+
## Context Rules (Memorize These)
|
|
878
|
+
|
|
879
|
+
1. **\\\`ctx.response.data\\\` is OVERWRITTEN every step.** Previous output GONE unless stored in vars.
|
|
880
|
+
2. **\\\`ctx.vars\\\` PERSISTS across the workflow.** Use \\\`set_var: true\\\` or \\\`js/ctx.vars['step']\\\`.
|
|
881
|
+
3. **Blueprint Mapper resolves \\\`js/\\\` expressions BEFORE node execution.**
|
|
882
|
+
|
|
883
|
+
When users have data flow issues, check these three things first.
|
|
884
|
+
|
|
885
|
+
## Debugging Workflows
|
|
886
|
+
|
|
887
|
+
1. **Verify structure**: Every \\\`steps[].name\\\` must match a key in \\\`nodes\\\`
|
|
888
|
+
2. **Trace data flow**: Which steps have \\\`set_var: true\\\`? Do \\\`js/\\\` expressions reference correct step names?
|
|
889
|
+
3. **Check runtimes**: SDK containers running? \\\`GET http://localhost:{port}/health\\\`
|
|
890
|
+
4. **Check Studio traces**: \\\`/__blok/runs/:id\\\` shows step-by-step inputs/outputs/errors
|
|
891
|
+
|
|
892
|
+
### Common Errors
|
|
893
|
+
|
|
894
|
+
| Error | Fix |
|
|
895
|
+
|-------|-----|
|
|
896
|
+
| \\\`Node type X not found\\\` | Wrong \\\`type\\\` in step — use module, local, or runtime.* |
|
|
897
|
+
| \\\`Validation failed\\\` | Zod schema mismatch — check input schema vs actual data |
|
|
898
|
+
| \\\`Runtime execution error\\\` | SDK container not running — check health endpoint |
|
|
899
|
+
| \\\`ctx.vars['X'] undefined\\\` | Source step missing \\\`set_var: true\\\` or name mismatch |
|
|
900
|
+
|
|
901
|
+
## Generating Code
|
|
902
|
+
|
|
903
|
+
Always use \\\`defineNode()\\\`. Never class-based BlokService.
|
|
904
|
+
|
|
905
|
+
\\\`\\\`\\\`typescript
|
|
906
|
+
import { defineNode } from "@blok/runner";
|
|
907
|
+
import { z } from "zod";
|
|
908
|
+
|
|
909
|
+
export default defineNode({
|
|
910
|
+
name: "node-name",
|
|
911
|
+
description: "What this node does",
|
|
912
|
+
input: z.object({ /* Zod schema */ }),
|
|
913
|
+
output: z.object({ /* Zod schema */ }),
|
|
914
|
+
async execute(ctx, input) {
|
|
915
|
+
return { /* must match output schema */ };
|
|
916
|
+
},
|
|
917
|
+
});
|
|
918
|
+
\\\`\\\`\\\`
|
|
919
|
+
|
|
920
|
+
### Checklist:
|
|
921
|
+
- Zod input schema covers all inputs
|
|
922
|
+
- Zod output schema matches execute() return
|
|
923
|
+
- Node name matches workflow references
|
|
924
|
+
- No \\\`any\\\` types — use \\\`z.unknown()\\\` if dynamic
|
|
925
|
+
- \\\`export default defineNode(...)\\\`
|
|
926
|
+
|
|
927
|
+
## Blok Studio Help
|
|
928
|
+
|
|
929
|
+
- Launch: \\\`blokctl trace\\\` or navigate to \\\`/__blok\\\`
|
|
930
|
+
- "No output" → Node not returning data or Zod output validation failed
|
|
931
|
+
- "Step error" → Expand error — check if 400 (validation) or 500 (runtime)
|
|
932
|
+
- "Vars not passing" → Source step needs \\\`set_var: true\\\`, target needs \\\`js/ctx.vars['name']\\\`
|
|
933
|
+
|
|
934
|
+
## Do NOT
|
|
935
|
+
|
|
936
|
+
- Do NOT suggest class-based BlokService for new nodes
|
|
937
|
+
- Do NOT generate code with \\\`any\\\` types
|
|
938
|
+
- Do NOT assume \\\`ctx.response.data\\\` persists across steps
|
|
939
|
+
- Do NOT skip Zod schemas when creating nodes
|
|
940
|
+
- Do NOT edit files in \\\`.blok/runtimes/\\\`
|
|
941
|
+
`;
|
|
942
|
+
const function_first_node_file = `import { defineNode } from "@blok/runner";
|
|
943
|
+
import { z } from "zod";
|
|
944
|
+
|
|
945
|
+
/**
|
|
946
|
+
* A function-first node that demonstrates the modern defineNode pattern.
|
|
947
|
+
* This node is type-safe, validated, and requires 60% less boilerplate.
|
|
948
|
+
*/
|
|
949
|
+
export default defineNode({
|
|
950
|
+
name: "{{NODE_NAME}}",
|
|
951
|
+
description: "A function-first node with Zod validation",
|
|
952
|
+
|
|
953
|
+
// Input schema using Zod - automatically validated
|
|
954
|
+
input: z.object({
|
|
955
|
+
message: z.string().optional().default("Hello World"),
|
|
956
|
+
}),
|
|
957
|
+
|
|
958
|
+
// Output schema using Zod - automatically validated
|
|
959
|
+
output: z.object({
|
|
960
|
+
message: z.string(),
|
|
961
|
+
timestamp: z.string(),
|
|
962
|
+
}),
|
|
963
|
+
|
|
964
|
+
// Execute function - type-safe with inferred types from Zod schemas
|
|
965
|
+
async execute(ctx, input) {
|
|
966
|
+
// Your business logic here
|
|
967
|
+
// - ctx.vars: Access workflow variables
|
|
968
|
+
// - ctx.request: Access HTTP request data
|
|
969
|
+
// - ctx.logger: Log messages
|
|
970
|
+
// - ctx.env: Access environment variables
|
|
971
|
+
|
|
972
|
+
// Example: Store data for downstream nodes
|
|
973
|
+
ctx.vars["processed-message"] = input.message;
|
|
974
|
+
|
|
975
|
+
// Return type-safe output (validated automatically)
|
|
976
|
+
return {
|
|
977
|
+
message: \`Processed: \${input.message}\`,
|
|
978
|
+
timestamp: new Date().toISOString(),
|
|
979
|
+
};
|
|
980
|
+
},
|
|
981
|
+
});
|
|
982
|
+
`;
|
|
983
|
+
export { node_file, package_dependencies, package_dev_dependencies, python3_file, examples_url, workflow_template, supervisord_nodejs, supervisord_python, go_node_file, go_mod_file, go_dockerfile, java_node_file, java_pom_file, java_dockerfile, rust_node_file, rust_cargo_file, rust_dockerfile, csharp_node_file, csharp_csproj_file, csharp_dockerfile, php_node_file, php_composer_file, php_dockerfile, ruby_node_file, ruby_gemfile, ruby_dockerfile, function_first_node_file, agents_md, claude_md, };
|