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.
Files changed (169) hide show
  1. package/dist/commands/build/index.d.ts +2 -0
  2. package/dist/commands/build/index.js +210 -0
  3. package/dist/commands/config/index.d.ts +1 -0
  4. package/dist/commands/config/index.js +46 -0
  5. package/dist/commands/cost/index.d.ts +1 -0
  6. package/dist/commands/cost/index.js +74 -0
  7. package/dist/commands/create/node.d.ts +2 -0
  8. package/dist/commands/create/node.js +541 -0
  9. package/dist/commands/create/project.d.ts +2 -0
  10. package/dist/commands/create/project.js +941 -0
  11. package/dist/commands/create/utils/Examples.d.ts +39 -0
  12. package/dist/commands/create/utils/Examples.js +983 -0
  13. package/dist/commands/create/workflow.d.ts +2 -0
  14. package/dist/commands/create/workflow.js +109 -0
  15. package/dist/commands/deploy/index.d.ts +2 -0
  16. package/dist/commands/deploy/index.js +176 -0
  17. package/dist/commands/dev/index.d.ts +2 -0
  18. package/dist/commands/dev/index.js +190 -0
  19. package/dist/commands/generate/GenerationAnalytics.d.ts +61 -0
  20. package/dist/commands/generate/GenerationAnalytics.js +162 -0
  21. package/dist/commands/generate/GenerationAnalytics.test.d.ts +1 -0
  22. package/dist/commands/generate/GenerationAnalytics.test.js +407 -0
  23. package/dist/commands/generate/NodeFileWriter.d.ts +5 -0
  24. package/dist/commands/generate/NodeFileWriter.js +240 -0
  25. package/dist/commands/generate/NodeGenerator.d.ts +20 -0
  26. package/dist/commands/generate/NodeGenerator.js +181 -0
  27. package/dist/commands/generate/NodeGenerator.test.d.ts +1 -0
  28. package/dist/commands/generate/NodeGenerator.test.js +101 -0
  29. package/dist/commands/generate/PromptVersioning.d.ts +25 -0
  30. package/dist/commands/generate/PromptVersioning.js +71 -0
  31. package/dist/commands/generate/PromptVersioning.test.d.ts +1 -0
  32. package/dist/commands/generate/PromptVersioning.test.js +120 -0
  33. package/dist/commands/generate/RegisterNode.d.ts +3 -0
  34. package/dist/commands/generate/RegisterNode.js +37 -0
  35. package/dist/commands/generate/RuntimeGenerator.d.ts +40 -0
  36. package/dist/commands/generate/RuntimeGenerator.js +369 -0
  37. package/dist/commands/generate/RuntimeGenerator.test.d.ts +1 -0
  38. package/dist/commands/generate/RuntimeGenerator.test.js +553 -0
  39. package/dist/commands/generate/TriggerGenerator.d.ts +22 -0
  40. package/dist/commands/generate/TriggerGenerator.js +220 -0
  41. package/dist/commands/generate/TriggerGenerator.test.d.ts +1 -0
  42. package/dist/commands/generate/TriggerGenerator.test.js +209 -0
  43. package/dist/commands/generate/WorkflowGenerator.d.ts +20 -0
  44. package/dist/commands/generate/WorkflowGenerator.js +131 -0
  45. package/dist/commands/generate/WorkflowGenerator.test.d.ts +1 -0
  46. package/dist/commands/generate/WorkflowGenerator.test.js +77 -0
  47. package/dist/commands/generate/e2e/NodeGenerator.e2e.test.d.ts +1 -0
  48. package/dist/commands/generate/e2e/NodeGenerator.e2e.test.js +216 -0
  49. package/dist/commands/generate/e2e/RuntimeGenerator.e2e.test.d.ts +1 -0
  50. package/dist/commands/generate/e2e/RuntimeGenerator.e2e.test.js +759 -0
  51. package/dist/commands/generate/e2e/TriggerGenerator.e2e.test.d.ts +1 -0
  52. package/dist/commands/generate/e2e/TriggerGenerator.e2e.test.js +295 -0
  53. package/dist/commands/generate/e2e/WorkflowGenerator.e2e.test.d.ts +1 -0
  54. package/dist/commands/generate/e2e/WorkflowGenerator.e2e.test.js +353 -0
  55. package/dist/commands/generate/index.d.ts +1 -0
  56. package/dist/commands/generate/index.js +418 -0
  57. package/dist/commands/generate/prompts/create-fn-node.system.d.ts +5 -0
  58. package/dist/commands/generate/prompts/create-fn-node.system.js +256 -0
  59. package/dist/commands/generate/prompts/create-node-manifest.system.d.ts +4 -0
  60. package/dist/commands/generate/prompts/create-node-manifest.system.js +41 -0
  61. package/dist/commands/generate/prompts/create-node.system.d.ts +5 -0
  62. package/dist/commands/generate/prompts/create-node.system.js +114 -0
  63. package/dist/commands/generate/prompts/create-readme.system.d.ts +4 -0
  64. package/dist/commands/generate/prompts/create-readme.system.js +83 -0
  65. package/dist/commands/generate/prompts/create-runtime.system.d.ts +5 -0
  66. package/dist/commands/generate/prompts/create-runtime.system.js +284 -0
  67. package/dist/commands/generate/prompts/create-trigger.system.d.ts +5 -0
  68. package/dist/commands/generate/prompts/create-trigger.system.js +293 -0
  69. package/dist/commands/generate/prompts/create-workflow.system.d.ts +5 -0
  70. package/dist/commands/generate/prompts/create-workflow.system.js +476 -0
  71. package/dist/commands/generate/prompts/register-node.system.d.ts +4 -0
  72. package/dist/commands/generate/prompts/register-node.system.js +26 -0
  73. package/dist/commands/generate/validators/CompilationValidator.d.ts +9 -0
  74. package/dist/commands/generate/validators/CompilationValidator.js +86 -0
  75. package/dist/commands/generate/validators/CompilationValidator.test.d.ts +1 -0
  76. package/dist/commands/generate/validators/CompilationValidator.test.js +161 -0
  77. package/dist/commands/generate/validators/NodeValidator.d.ts +18 -0
  78. package/dist/commands/generate/validators/NodeValidator.js +217 -0
  79. package/dist/commands/generate/validators/NodeValidator.test.d.ts +1 -0
  80. package/dist/commands/generate/validators/NodeValidator.test.js +281 -0
  81. package/dist/commands/generate/validators/WorkflowValidator.d.ts +6 -0
  82. package/dist/commands/generate/validators/WorkflowValidator.js +301 -0
  83. package/dist/commands/generate/validators/WorkflowValidator.test.d.ts +1 -0
  84. package/dist/commands/generate/validators/WorkflowValidator.test.js +647 -0
  85. package/dist/commands/generate/validators/index.d.ts +4 -0
  86. package/dist/commands/generate/validators/index.js +2 -0
  87. package/dist/commands/graph/index.d.ts +1 -0
  88. package/dist/commands/graph/index.js +69 -0
  89. package/dist/commands/install/index.d.ts +1 -0
  90. package/dist/commands/install/index.js +4 -0
  91. package/dist/commands/install/node.d.ts +4 -0
  92. package/dist/commands/install/node.js +136 -0
  93. package/dist/commands/install/workflow.d.ts +4 -0
  94. package/dist/commands/install/workflow.js +62 -0
  95. package/dist/commands/login/index.d.ts +2 -0
  96. package/dist/commands/login/index.js +77 -0
  97. package/dist/commands/logout/index.d.ts +2 -0
  98. package/dist/commands/logout/index.js +20 -0
  99. package/dist/commands/marketplace/runtime.d.ts +54 -0
  100. package/dist/commands/marketplace/runtime.js +350 -0
  101. package/dist/commands/migrate/index.d.ts +1 -0
  102. package/dist/commands/migrate/index.js +14 -0
  103. package/dist/commands/migrate/node.d.ts +2 -0
  104. package/dist/commands/migrate/node.js +110 -0
  105. package/dist/commands/monitor/index.d.ts +1 -0
  106. package/dist/commands/monitor/index.js +28 -0
  107. package/dist/commands/monitor/monitor-component.d.ts +1 -0
  108. package/dist/commands/monitor/monitor-component.js +271 -0
  109. package/dist/commands/monitor/static/index.html +2124 -0
  110. package/dist/commands/monitor/static-web-server.d.ts +1 -0
  111. package/dist/commands/monitor/static-web-server.js +89 -0
  112. package/dist/commands/profile/index.d.ts +1 -0
  113. package/dist/commands/profile/index.js +112 -0
  114. package/dist/commands/publish/index.d.ts +1 -0
  115. package/dist/commands/publish/index.js +4 -0
  116. package/dist/commands/publish/node.d.ts +4 -0
  117. package/dist/commands/publish/node.js +231 -0
  118. package/dist/commands/publish/workflow.d.ts +4 -0
  119. package/dist/commands/publish/workflow.js +165 -0
  120. package/dist/commands/search/docs.d.ts +17 -0
  121. package/dist/commands/search/docs.js +179 -0
  122. package/dist/commands/search/index.d.ts +1 -0
  123. package/dist/commands/search/index.js +5 -0
  124. package/dist/commands/search/indexer.d.ts +10 -0
  125. package/dist/commands/search/indexer.js +265 -0
  126. package/dist/commands/search/nodes.d.ts +4 -0
  127. package/dist/commands/search/nodes.js +101 -0
  128. package/dist/commands/search/workflow.d.ts +4 -0
  129. package/dist/commands/search/workflow.js +100 -0
  130. package/dist/commands/trace/index.d.ts +1 -0
  131. package/dist/commands/trace/index.js +26 -0
  132. package/dist/commands/trace/startStudio.d.ts +8 -0
  133. package/dist/commands/trace/startStudio.js +116 -0
  134. package/dist/index.d.ts +17 -0
  135. package/dist/index.js +186 -0
  136. package/dist/services/commander.d.ts +9 -0
  137. package/dist/services/commander.js +20 -0
  138. package/dist/services/constants.d.ts +1 -0
  139. package/dist/services/constants.js +3 -0
  140. package/dist/services/local-token-manager.d.ts +14 -0
  141. package/dist/services/local-token-manager.js +99 -0
  142. package/dist/services/non-interactive.d.ts +5 -0
  143. package/dist/services/non-interactive.js +30 -0
  144. package/dist/services/package-manager.d.ts +35 -0
  145. package/dist/services/package-manager.js +111 -0
  146. package/dist/services/posthog.d.ts +31 -0
  147. package/dist/services/posthog.js +159 -0
  148. package/dist/services/registry-manager.d.ts +9 -0
  149. package/dist/services/registry-manager.js +26 -0
  150. package/dist/services/runtime-detector.d.ts +23 -0
  151. package/dist/services/runtime-detector.js +181 -0
  152. package/dist/services/runtime-setup.d.ts +36 -0
  153. package/dist/services/runtime-setup.js +250 -0
  154. package/dist/services/utils.d.ts +2 -0
  155. package/dist/services/utils.js +29 -0
  156. package/dist/services/workflow-loader.d.ts +30 -0
  157. package/dist/services/workflow-loader.js +46 -0
  158. package/dist/studio-dist/assets/charts-Dso0hPUR.js +68 -0
  159. package/dist/studio-dist/assets/graph-CsV2nWGn.js +23 -0
  160. package/dist/studio-dist/assets/icons-zP8LLgPh.js +311 -0
  161. package/dist/studio-dist/assets/index-CLyEkXMx.css +1 -0
  162. package/dist/studio-dist/assets/index-CNXFX_ar.js +27 -0
  163. package/dist/studio-dist/assets/react-vendor--Eh9ivFN.js +17 -0
  164. package/dist/studio-dist/assets/tanstack-query-CiM1U6F5.js +1 -0
  165. package/dist/studio-dist/assets/tanstack-router-Btjy0MKq.js +25 -0
  166. package/dist/studio-dist/assets/tanstack-table-DhwRvuH2.js +22 -0
  167. package/dist/studio-dist/favicon.svg +5 -0
  168. package/dist/studio-dist/index.html +21 -0
  169. package/package.json +75 -0
@@ -0,0 +1,941 @@
1
+ import child_process from "node:child_process";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ import util from "node:util";
5
+ import * as p from "@clack/prompts";
6
+ import figlet from "figlet";
7
+ import fsExtra from "fs-extra";
8
+ import color from "picocolors";
9
+ import simpleGit from "simple-git";
10
+ import { isNonInteractive, parseCommaSeparated, resolveOrThrow } from "../../services/non-interactive.js";
11
+ import { manager as pm } from "../../services/package-manager.js";
12
+ import { detectRuntimes } from "../../services/runtime-detector.js";
13
+ import { createTriggerConfig, generateRuntimeEnvVars, generateSupervisordConfig, generateTriggerEnvVars, generateTriggerSupervisordConfig, setupRuntime, writeProjectConfig, } from "../../services/runtime-setup.js";
14
+ import { agents_md, claude_md, examples_url, node_file, package_dependencies, package_dev_dependencies, } from "./utils/Examples.js";
15
+ const exec = util.promisify(child_process.exec);
16
+ const HOME_DIR = `${os.homedir()}/.blok`;
17
+ const GITHUB_REPO_LOCAL = `${HOME_DIR}/blok`;
18
+ const GITHUB_REPO_REMOTE = "https://github.com/well-prado/blok.git";
19
+ const GITHUB_REPO_RELEASE_TAG = "v0.0.1-beta.5";
20
+ fsExtra.ensureDirSync(HOME_DIR);
21
+ const options = {
22
+ baseDir: HOME_DIR,
23
+ binary: "git",
24
+ maxConcurrentProcesses: 6,
25
+ trimmed: false,
26
+ };
27
+ const git = simpleGit(options);
28
+ export async function createProject(opts, version, currentPath = false, localRepoPath) {
29
+ const availableManagers = await pm.getAvailableManagers();
30
+ let manager = await pm.getManager();
31
+ const nonInteractive = isNonInteractive();
32
+ const isDefault = opts.name !== undefined;
33
+ const skipPrompts = isDefault || nonInteractive;
34
+ let projectName = opts.name ? opts.name : "";
35
+ let selectedTriggers = opts.triggers
36
+ ? parseCommaSeparated(opts.triggers)
37
+ : opts.trigger
38
+ ? [opts.trigger]
39
+ : ["http"];
40
+ let examples = opts.examples ?? false;
41
+ let selectedRuntimeKinds = opts.runtimes ? parseCommaSeparated(opts.runtimes) : ["node"];
42
+ let selectedManager = opts.packageManager || "npm";
43
+ let pubsubProvider = opts.pubsubProvider || "gcp";
44
+ let queueProvider = opts.queueProvider || "kafka";
45
+ let detectedRuntimes = [];
46
+ if (!skipPrompts) {
47
+ console.log(figlet.textSync("blok CLI".toUpperCase(), {
48
+ font: "Digital",
49
+ horizontalLayout: "default",
50
+ verticalLayout: "default",
51
+ width: 100,
52
+ whitespaceBreak: true,
53
+ }));
54
+ console.log("");
55
+ p.intro(color.inverse(" Create a New Project "));
56
+ const detectSpinner = p.spinner();
57
+ detectSpinner.start("Detecting installed language runtimes...");
58
+ detectedRuntimes = await detectRuntimes();
59
+ detectSpinner.stop("Runtime detection complete.");
60
+ const resolveProjectName = async () => {
61
+ if (projectName !== "") {
62
+ return projectName;
63
+ }
64
+ return (await p.text({
65
+ message: "Please provide a name for the project",
66
+ placeholder: "blok-service",
67
+ defaultValue: "blok-service",
68
+ }));
69
+ };
70
+ const resolveSelectedManager = async () => {
71
+ if (opts.packageManager) {
72
+ return opts.packageManager;
73
+ }
74
+ if (availableManagers.length === 1) {
75
+ return availableManagers[0];
76
+ }
77
+ return (await p.select({
78
+ message: "Select the package manager",
79
+ options: availableManagers.map((manager) => ({
80
+ label: manager,
81
+ value: manager,
82
+ })),
83
+ }));
84
+ };
85
+ const runtimeOptions = [
86
+ { label: "NodeJS", value: "node", hint: "always included" },
87
+ ...detectedRuntimes.map((rt) => {
88
+ let hint;
89
+ if (rt.available) {
90
+ hint = `${rt.toolchain} ${rt.version || ""} detected`.trim();
91
+ }
92
+ else if (rt.secondaryTool && !rt.secondaryTool.available) {
93
+ hint = `${rt.secondaryTool.name} not found - will be skipped`;
94
+ }
95
+ else {
96
+ hint = `${rt.toolchain} not found - will be skipped`;
97
+ }
98
+ return { label: rt.label, value: rt.kind, hint };
99
+ }),
100
+ ];
101
+ const blokctlProject = await p.group({
102
+ projectName: () => resolveProjectName(),
103
+ triggers: () => opts.triggers || opts.trigger
104
+ ? Promise.resolve(opts.triggers ? parseCommaSeparated(opts.triggers) : [opts.trigger])
105
+ : p.multiselect({
106
+ message: "Select triggers to install",
107
+ options: [
108
+ { label: "HTTP", value: "http", hint: "REST APIs (port 4000)" },
109
+ { label: "SSE", value: "sse", hint: "Real-time push (port 4001)" },
110
+ { label: "Queue", value: "queue", hint: "Kafka/RabbitMQ/SQS/Redis (port 4005)" },
111
+ { label: "Pub/Sub", value: "pubsub", hint: "GCP/AWS/Azure messaging (port 4006)" },
112
+ ],
113
+ initialValues: ["http"],
114
+ required: true,
115
+ }),
116
+ pubsubProvider: ({ results }) => opts.pubsubProvider
117
+ ? Promise.resolve(opts.pubsubProvider)
118
+ : results.triggers?.includes("pubsub")
119
+ ? p.select({
120
+ message: "Select Pub/Sub provider",
121
+ options: [
122
+ { label: "Google Cloud Pub/Sub", value: "gcp" },
123
+ { label: "AWS SNS/SQS", value: "aws" },
124
+ { label: "Azure Service Bus", value: "azure" },
125
+ ],
126
+ })
127
+ : Promise.resolve(null),
128
+ queueProvider: ({ results }) => opts.queueProvider
129
+ ? Promise.resolve(opts.queueProvider)
130
+ : results.triggers?.includes("queue")
131
+ ? p.select({
132
+ message: "Select Queue provider",
133
+ options: [
134
+ { label: "Apache Kafka", value: "kafka" },
135
+ { label: "RabbitMQ", value: "rabbitmq" },
136
+ { label: "AWS SQS", value: "sqs" },
137
+ { label: "Redis/BullMQ", value: "redis" },
138
+ ],
139
+ })
140
+ : Promise.resolve(null),
141
+ runtimes: () => opts.runtimes
142
+ ? Promise.resolve(parseCommaSeparated(opts.runtimes))
143
+ : p.multiselect({
144
+ message: "Select the runtimes to install",
145
+ options: runtimeOptions,
146
+ initialValues: ["node"],
147
+ required: true,
148
+ }),
149
+ selectedManager: () => resolveSelectedManager(),
150
+ }, {
151
+ onCancel: () => {
152
+ p.cancel("Operation canceled.");
153
+ process.exit(0);
154
+ },
155
+ });
156
+ projectName = blokctlProject.projectName;
157
+ selectedTriggers = blokctlProject.triggers;
158
+ pubsubProvider = blokctlProject.pubsubProvider || "gcp";
159
+ queueProvider = blokctlProject.queueProvider || "kafka";
160
+ selectedRuntimeKinds = blokctlProject.runtimes;
161
+ selectedManager = blokctlProject.selectedManager;
162
+ const unavailableSelected = selectedRuntimeKinds.filter((kind) => {
163
+ if (kind === "node")
164
+ return false;
165
+ const rt = detectedRuntimes.find((r) => r.kind === kind);
166
+ return rt && !rt.available;
167
+ });
168
+ if (unavailableSelected.length > 0) {
169
+ console.log("");
170
+ for (const kind of unavailableSelected) {
171
+ const rt = detectedRuntimes.find((r) => r.kind === kind);
172
+ if (rt) {
173
+ console.log(color.yellow(` ${rt.label}: ${rt.toolchain} not found. Skipping setup.`));
174
+ if (rt.secondaryTool && !rt.secondaryTool.available) {
175
+ console.log(color.yellow(` ${rt.secondaryTool.installHint}`));
176
+ }
177
+ else {
178
+ console.log(color.yellow(` ${rt.installHint}`));
179
+ }
180
+ }
181
+ }
182
+ console.log("");
183
+ selectedRuntimeKinds = selectedRuntimeKinds.filter((kind) => {
184
+ if (kind === "node")
185
+ return true;
186
+ const rt = detectedRuntimes.find((r) => r.kind === kind);
187
+ return rt?.available ?? false;
188
+ });
189
+ }
190
+ const blokctlExamplesProject = await p.group({
191
+ examples: () => opts.examples !== undefined
192
+ ? Promise.resolve(opts.examples)
193
+ : p.select({
194
+ message: "Install the examples?",
195
+ options: [
196
+ { label: "NO", value: false, hint: "recommended" },
197
+ { label: "YES", value: true },
198
+ ],
199
+ }),
200
+ }, {
201
+ onCancel: () => {
202
+ p.cancel("Operation canceled.");
203
+ process.exit(0);
204
+ },
205
+ });
206
+ examples = blokctlExamplesProject.examples;
207
+ }
208
+ else if (nonInteractive) {
209
+ projectName = resolveOrThrow("name", opts.name);
210
+ if (selectedRuntimeKinds.some((k) => k !== "node")) {
211
+ detectedRuntimes = await detectRuntimes();
212
+ }
213
+ }
214
+ const s = p.spinner();
215
+ if (!skipPrompts)
216
+ s.start("Creating the project...");
217
+ try {
218
+ const dirPath = !currentPath ? path.join(process.cwd(), projectName) : process.cwd();
219
+ if (!skipPrompts)
220
+ s.message("Gathering project files");
221
+ const repoSource = localRepoPath ? path.resolve(localRepoPath) : GITHUB_REPO_LOCAL;
222
+ if (localRepoPath) {
223
+ if (!fsExtra.existsSync(repoSource)) {
224
+ throw new Error(`Local repo path not found: ${repoSource}`);
225
+ }
226
+ console.log(color.dim(` Using local repo: ${repoSource}`));
227
+ }
228
+ else {
229
+ const githubLocalExists = fsExtra.existsSync(GITHUB_REPO_LOCAL);
230
+ if (githubLocalExists) {
231
+ fsExtra.removeSync(GITHUB_REPO_LOCAL);
232
+ }
233
+ if (GITHUB_REPO_RELEASE_TAG) {
234
+ await git.clone(GITHUB_REPO_REMOTE, GITHUB_REPO_LOCAL, ["--branch", GITHUB_REPO_RELEASE_TAG, "--depth", "1"]);
235
+ }
236
+ else {
237
+ await git.clone(GITHUB_REPO_REMOTE, GITHUB_REPO_LOCAL);
238
+ }
239
+ }
240
+ if (!skipPrompts)
241
+ s.message("Copying project files...");
242
+ if (!currentPath) {
243
+ const projectDirExists = fsExtra.existsSync(dirPath);
244
+ if (projectDirExists) {
245
+ throw new Error("A project already exists in the current directory. Please remove it and try again.");
246
+ }
247
+ }
248
+ fsExtra.ensureDirSync(dirPath);
249
+ fsExtra.ensureDirSync(`${dirPath}/src`);
250
+ fsExtra.ensureDirSync(`${dirPath}/src/triggers`);
251
+ fsExtra.ensureDirSync(`${dirPath}/src/nodes`);
252
+ fsExtra.ensureDirSync(`${dirPath}/src/workflows`);
253
+ const triggerConfigs = selectedTriggers.map((kind) => createTriggerConfig(kind));
254
+ const primaryTrigger = selectedTriggers[0];
255
+ const primaryTriggerDir = primaryTrigger === "pubsub" || primaryTrigger === "queue"
256
+ ? `${repoSource}/triggers/${primaryTrigger}/template`
257
+ : `${repoSource}/triggers/${primaryTrigger}`;
258
+ const baseFiles = ["package.json", "tsconfig.json", ".env.example", ".gitignore", "vitest.config.ts"];
259
+ for (const file of baseFiles) {
260
+ const src = `${primaryTriggerDir}/${file}`;
261
+ if (fsExtra.existsSync(src)) {
262
+ fsExtra.copySync(src, `${dirPath}/${file}`);
263
+ }
264
+ }
265
+ if (fsExtra.existsSync(`${primaryTriggerDir}/Dockerfile`)) {
266
+ fsExtra.copySync(`${primaryTriggerDir}/Dockerfile`, `${dirPath}/Dockerfile`);
267
+ }
268
+ if (fsExtra.existsSync(`${primaryTriggerDir}/Dockerfile.dev`)) {
269
+ fsExtra.copySync(`${primaryTriggerDir}/Dockerfile.dev`, `${dirPath}/Dockerfile.dev`);
270
+ }
271
+ for (const triggerKind of selectedTriggers) {
272
+ const triggerDestDir = `${dirPath}/src/triggers/${triggerKind}`;
273
+ fsExtra.ensureDirSync(triggerDestDir);
274
+ if (triggerKind === "pubsub" || triggerKind === "queue") {
275
+ const templateDir = `${repoSource}/triggers/${triggerKind}/template/src`;
276
+ if (fsExtra.existsSync(templateDir)) {
277
+ fsExtra.copySync(templateDir, triggerDestDir);
278
+ if (fsExtra.existsSync(`${templateDir}/workflows`)) {
279
+ fsExtra.copySync(`${templateDir}/workflows`, `${dirPath}/src/workflows/${triggerKind}`);
280
+ fsExtra.removeSync(`${triggerDestDir}/workflows`);
281
+ }
282
+ fsExtra.removeSync(`${triggerDestDir}/Nodes.ts`);
283
+ fsExtra.removeSync(`${triggerDestDir}/Workflows.ts`);
284
+ if (triggerKind === "pubsub") {
285
+ updatePubSubProvider(triggerDestDir, pubsubProvider);
286
+ }
287
+ else if (triggerKind === "queue") {
288
+ updateQueueProvider(triggerDestDir, queueProvider);
289
+ }
290
+ }
291
+ }
292
+ else {
293
+ const triggerSrcDir = `${repoSource}/triggers/${triggerKind}/src`;
294
+ if (fsExtra.existsSync(`${triggerSrcDir}/runner`)) {
295
+ fsExtra.copySync(`${triggerSrcDir}/runner`, `${triggerDestDir}/runner`);
296
+ }
297
+ if (fsExtra.existsSync(`${triggerSrcDir}/AppRoutes.ts`)) {
298
+ fsExtra.copySync(`${triggerSrcDir}/AppRoutes.ts`, `${triggerDestDir}/AppRoutes.ts`);
299
+ }
300
+ if (fsExtra.existsSync(`${triggerSrcDir}/workflows`)) {
301
+ fsExtra.copySync(`${triggerSrcDir}/workflows`, `${dirPath}/src/workflows/${triggerKind}`);
302
+ }
303
+ if (triggerKind === "sse" && fsExtra.existsSync(`${triggerSrcDir}/SSETrigger.ts`)) {
304
+ fsExtra.copySync(`${triggerSrcDir}/SSETrigger.ts`, `${triggerDestDir}/SSETrigger.ts`);
305
+ }
306
+ if (fsExtra.existsSync(`${triggerSrcDir}/lib.ts`)) {
307
+ fsExtra.copySync(`${triggerSrcDir}/lib.ts`, `${triggerDestDir}/lib.ts`);
308
+ }
309
+ }
310
+ }
311
+ for (const triggerKind of selectedTriggers) {
312
+ const triggerDestDir = `${dirPath}/src/triggers/${triggerKind}`;
313
+ fixRunnerImportPaths(triggerDestDir, triggerKind);
314
+ }
315
+ const sharedNodesContent = generateSharedNodesFile(selectedTriggers, repoSource);
316
+ fsExtra.writeFileSync(`${dirPath}/src/Nodes.ts`, sharedNodesContent);
317
+ const sharedWorkflowsContent = generateSharedWorkflowsFile(selectedTriggers);
318
+ fsExtra.writeFileSync(`${dirPath}/src/Workflows.ts`, sharedWorkflowsContent);
319
+ for (const triggerKind of selectedTriggers) {
320
+ const entryContent = generateTriggerEntryFile(triggerKind);
321
+ fsExtra.writeFileSync(`${dirPath}/src/triggers/${triggerKind}/index.ts`, entryContent);
322
+ }
323
+ for (const triggerKind of selectedTriggers) {
324
+ const triggerNodesDir = `${repoSource}/triggers/${triggerKind}/src/nodes`;
325
+ if (fsExtra.existsSync(triggerNodesDir)) {
326
+ fsExtra.copySync(triggerNodesDir, `${dirPath}/src/nodes`);
327
+ }
328
+ }
329
+ if (!skipPrompts) {
330
+ s.message("Installing example workflows and nodes");
331
+ }
332
+ const nodesDir = `${dirPath}/src/nodes`;
333
+ const workflowsDir = `${dirPath}/workflows`;
334
+ fsExtra.ensureDirSync(nodesDir);
335
+ fsExtra.copySync(`${repoSource}/workflows`, workflowsDir);
336
+ try {
337
+ fsExtra.chownSync(dirPath, os.userInfo().uid, os.userInfo().gid);
338
+ }
339
+ catch (error) {
340
+ console.error(`Failed to change ownership of directory ${dirPath}:`, error);
341
+ }
342
+ fsExtra.ensureDirSync(`${dirPath}/infra`);
343
+ fsExtra.ensureDirSync(`${dirPath}/infra/metrics`);
344
+ fsExtra.copySync(`${repoSource}/infra/metrics`, `${dirPath}/infra/metrics`);
345
+ fsExtra.removeSync(`${dirPath}/public/metric`);
346
+ if (!examples) {
347
+ fsExtra.removeSync(`${nodesDir}/examples`);
348
+ fsExtra.removeSync(`${workflowsDir}`);
349
+ fsExtra.ensureDirSync(`${workflowsDir}`);
350
+ fsExtra.ensureDirSync(`${workflowsDir}/json`);
351
+ fsExtra.ensureDirSync(`${workflowsDir}/yaml`);
352
+ fsExtra.ensureDirSync(`${workflowsDir}/toml`);
353
+ }
354
+ else {
355
+ fsExtra.ensureDirSync(`${dirPath}/infra/postgresql`);
356
+ fsExtra.ensureDirSync(`${dirPath}/infra/milvus`);
357
+ fsExtra.copySync(`${repoSource}/infra/development`, `${dirPath}/infra/postgresql`);
358
+ fsExtra.copySync(`${repoSource}/infra/milvus`, `${dirPath}/infra/milvus`);
359
+ fsExtra.writeFileSync(`${dirPath}/src/Nodes.ts`, node_file);
360
+ fsExtra.copySync(`${repoSource}/sdk`, `${dirPath}/public/sdk`);
361
+ }
362
+ const envExample = `${dirPath}/.env.example`;
363
+ const envLocal = `${dirPath}/.env.local`;
364
+ const envContent = fsExtra.readFileSync(envExample, "utf8");
365
+ const result = envContent.replaceAll("PROJECT_PATH", dirPath);
366
+ fsExtra.writeFileSync(envLocal, result);
367
+ const packageJson = `${dirPath}/package.json`;
368
+ const packageJsonContent = JSON.parse(fsExtra.readFileSync(packageJson, "utf8"));
369
+ packageJsonContent.name = projectName;
370
+ packageJsonContent.version = "1.0.0";
371
+ packageJsonContent.author = "";
372
+ const workspacePackageMap = {
373
+ "@blok/api-call": "nodes/web/api-call@1.0.0",
374
+ "@blok/helper": "core/workflow-helper",
375
+ "@blok/if-else": "nodes/control-flow/if-else@1.0.0",
376
+ "@blok/runner": "core/runner",
377
+ "@blok/shared": "core/shared",
378
+ "@blok/trigger-pubsub": "triggers/pubsub",
379
+ "@blok/trigger-queue": "triggers/queue",
380
+ };
381
+ for (const depGroup of ["dependencies", "devDependencies", "peerDependencies"]) {
382
+ const deps = packageJsonContent[depGroup];
383
+ if (!deps)
384
+ continue;
385
+ for (const [pkg, ver] of Object.entries(deps)) {
386
+ if (typeof ver === "string" && ver.startsWith("workspace:")) {
387
+ if (localRepoPath && workspacePackageMap[pkg]) {
388
+ deps[pkg] = `file:${path.resolve(repoSource, workspacePackageMap[pkg])}`;
389
+ }
390
+ else {
391
+ deps[pkg] = "^0.1.0";
392
+ }
393
+ }
394
+ }
395
+ }
396
+ if (localRepoPath) {
397
+ const fileLinks = {};
398
+ for (const [pkg, relativePath] of Object.entries(workspacePackageMap)) {
399
+ fileLinks[pkg] = `file:${path.resolve(repoSource, relativePath)}`;
400
+ }
401
+ packageJsonContent.overrides = fileLinks;
402
+ packageJsonContent.resolutions = fileLinks;
403
+ }
404
+ manager = await pm.getManager(selectedManager);
405
+ const triggerScripts = {
406
+ dev: "blokctl dev",
407
+ };
408
+ for (const tc of triggerConfigs) {
409
+ triggerScripts[`start:${tc.kind}`] = tc.startCmd;
410
+ }
411
+ packageJsonContent.scripts = {
412
+ ...packageJsonContent.scripts,
413
+ ...triggerScripts,
414
+ };
415
+ const blokctlRef = localRepoPath ? `file:${path.resolve(repoSource, "packages/cli")}` : `^${version}`;
416
+ packageJsonContent.devDependencies = {
417
+ ...packageJsonContent.devDependencies,
418
+ blokctl: blokctlRef,
419
+ };
420
+ const providerDeps = getProviderDependencies(selectedTriggers, pubsubProvider, queueProvider);
421
+ if (Object.keys(providerDeps).length > 0) {
422
+ packageJsonContent.dependencies = {
423
+ ...packageJsonContent.dependencies,
424
+ ...providerDeps,
425
+ };
426
+ }
427
+ const triggerPackageDeps = {};
428
+ if (selectedTriggers.includes("pubsub")) {
429
+ const pubsubRef = localRepoPath ? `file:${path.resolve(repoSource, "triggers/pubsub")}` : "@blok/trigger-pubsub";
430
+ triggerPackageDeps["@blok/trigger-pubsub"] = localRepoPath ? pubsubRef : "workspace:*";
431
+ }
432
+ if (selectedTriggers.includes("queue")) {
433
+ const queueRef = localRepoPath ? `file:${path.resolve(repoSource, "triggers/queue")}` : "@blok/trigger-queue";
434
+ triggerPackageDeps["@blok/trigger-queue"] = localRepoPath ? queueRef : "workspace:*";
435
+ }
436
+ if (Object.keys(triggerPackageDeps).length > 0) {
437
+ packageJsonContent.dependencies = {
438
+ ...packageJsonContent.dependencies,
439
+ ...triggerPackageDeps,
440
+ };
441
+ }
442
+ const nonNodeRuntimes = selectedRuntimeKinds.filter((kind) => kind !== "node");
443
+ const runtimeConfigs = [];
444
+ if (nonNodeRuntimes.length > 0) {
445
+ for (const kind of nonNodeRuntimes) {
446
+ const rt = detectedRuntimes.find((r) => r.kind === kind);
447
+ if (!rt)
448
+ continue;
449
+ try {
450
+ const config = await setupRuntime(rt, repoSource, dirPath, s);
451
+ runtimeConfigs.push(config);
452
+ }
453
+ catch (error) {
454
+ console.log(color.yellow(`\n Warning: Failed to setup ${rt.label} runtime: ${error.message}`));
455
+ console.log(color.yellow(" You can set it up manually later.\n"));
456
+ }
457
+ }
458
+ if (runtimeConfigs.length > 0) {
459
+ const envVars = generateRuntimeEnvVars(runtimeConfigs);
460
+ fsExtra.appendFileSync(envLocal, envVars);
461
+ }
462
+ }
463
+ writeProjectConfig(dirPath, runtimeConfigs, triggerConfigs);
464
+ if (triggerConfigs.length > 0) {
465
+ const triggerEnvVars = generateTriggerEnvVars(triggerConfigs);
466
+ fsExtra.appendFileSync(envLocal, triggerEnvVars);
467
+ }
468
+ const providerEnvVars = getProviderEnvVars(selectedTriggers, pubsubProvider, queueProvider);
469
+ if (providerEnvVars) {
470
+ fsExtra.appendFileSync(envLocal, providerEnvVars);
471
+ }
472
+ if (examples) {
473
+ packageJsonContent.dependencies = {
474
+ ...packageJsonContent.dependencies,
475
+ ...package_dependencies,
476
+ };
477
+ packageJsonContent.devDependencies = {
478
+ ...packageJsonContent.devDependencies,
479
+ ...package_dev_dependencies,
480
+ };
481
+ }
482
+ fsExtra.writeFileSync(packageJson, JSON.stringify(packageJsonContent, null, 2));
483
+ const supervisordConfPath = `${dirPath}/supervisord.conf`;
484
+ let supervisordConfContent = "[supervisord]\nnodaemon=true\n";
485
+ if (triggerConfigs.length > 0) {
486
+ supervisordConfContent += generateTriggerSupervisordConfig(triggerConfigs);
487
+ }
488
+ if (runtimeConfigs.length > 0) {
489
+ supervisordConfContent += generateSupervisordConfig(runtimeConfigs);
490
+ }
491
+ fsExtra.writeFileSync(supervisordConfPath, supervisordConfContent);
492
+ fsExtra.writeFileSync(`${dirPath}/AGENTS.md`, agents_md.trimStart());
493
+ fsExtra.writeFileSync(`${dirPath}/CLAUDE.md`, claude_md.trimStart());
494
+ s.message("Installing packages...");
495
+ const cmd_install_ts_response = await exec(manager.INSTALL, { cwd: dirPath });
496
+ s.message("Packages installed successfully!");
497
+ console.log("\n", cmd_install_ts_response.stdout);
498
+ if (!fsExtra.existsSync(`${dirPath}/node_modules`)) {
499
+ throw new Error("Failed to install packages. Please check your internet connection and try again.");
500
+ }
501
+ if (!skipPrompts)
502
+ s.stop(`Project "${projectName}" created successfully.`);
503
+ const triggerNames = triggerConfigs.map((tc) => tc.label).join(", ");
504
+ console.log(`\nTriggers: ${triggerNames}`);
505
+ const installedRuntimes = ["NodeJS", ...runtimeConfigs.map((rc) => rc.label)];
506
+ console.log(`Runtimes: ${installedRuntimes.join(", ")}\n`);
507
+ if (!currentPath)
508
+ console.log(`Change to the project directory: cd ${projectName}`);
509
+ console.log(`Run the command "npm run dev" to start the development server.`);
510
+ console.log("\nTrigger endpoints:");
511
+ for (const tc of triggerConfigs) {
512
+ console.log(` ${tc.label}: http://localhost:${tc.port}/health-check`);
513
+ }
514
+ if (runtimeConfigs.length > 0) {
515
+ console.log("\nRuntime health checks:");
516
+ for (const rc of runtimeConfigs) {
517
+ console.log(` ${rc.label}: http://localhost:${rc.port}/health`);
518
+ }
519
+ }
520
+ console.log("\nFor more documentation, visit https://blok.build/");
521
+ if (examples) {
522
+ console.log(examples_url);
523
+ }
524
+ }
525
+ catch (error) {
526
+ if (!skipPrompts)
527
+ s.stop(error.message);
528
+ if (skipPrompts)
529
+ console.log(error.message);
530
+ }
531
+ }
532
+ function generateSharedNodesFile(triggers, _repoSource) {
533
+ const nodeImports = new Set();
534
+ const nodeExports = new Map();
535
+ nodeImports.add('import ApiCall from "@blok/api-call";');
536
+ nodeImports.add('import IfElse from "@blok/if-else";');
537
+ nodeImports.add('import type { BlokService } from "@blok/runner";');
538
+ nodeExports.set("@blok/api-call", "ApiCall");
539
+ nodeExports.set("@blok/if-else", "IfElse");
540
+ for (const trigger of triggers) {
541
+ if (trigger === "sse") {
542
+ nodeImports.add('import WelcomeMessage from "./nodes/welcome-message/index";');
543
+ nodeExports.set("welcome-message", "WelcomeMessage");
544
+ }
545
+ }
546
+ const importLines = Array.from(nodeImports).join("\n");
547
+ const exportEntries = Array.from(nodeExports.entries())
548
+ .map(([key, value]) => `\t"${key}": ${value},`)
549
+ .join("\n");
550
+ return `${importLines}
551
+
552
+ const nodes: Record<string, BlokService<unknown>> = {
553
+ ${exportEntries}
554
+ };
555
+
556
+ export default nodes;
557
+ `;
558
+ }
559
+ function generateSharedWorkflowsFile(triggers) {
560
+ const imports = [];
561
+ const workflowEntries = [];
562
+ for (const trigger of triggers) {
563
+ if (trigger === "http") {
564
+ imports.push("// Import HTTP workflows here");
565
+ }
566
+ else if (trigger === "sse") {
567
+ imports.push('import OnConnect from "./workflows/sse/notifications/on-connect";');
568
+ imports.push('import OnSubscribe from "./workflows/sse/notifications/on-subscribe";');
569
+ workflowEntries.push('\t"on-connect": OnConnect,');
570
+ workflowEntries.push('\t"on-subscribe": OnSubscribe,');
571
+ }
572
+ else if (trigger === "pubsub") {
573
+ imports.push('import OnPubSubMessage from "./workflows/pubsub/messages/on-message";');
574
+ workflowEntries.push('\t"on-pubsub-message": OnPubSubMessage,');
575
+ }
576
+ else if (trigger === "queue") {
577
+ imports.push('import OnQueueMessage from "./workflows/queue/messages/on-message";');
578
+ workflowEntries.push('\t"on-queue-message": OnQueueMessage,');
579
+ }
580
+ }
581
+ const importSection = imports.length > 0 ? `${imports.join("\n")}\n` : "";
582
+ const entriesSection = workflowEntries.length > 0 ? workflowEntries.join("\n") : "\t// Add your workflows here";
583
+ return `import type { HelperResponse } from "@blok/helper";
584
+
585
+ ${importSection}
586
+ const workflows: Record<string, HelperResponse> = {
587
+ ${entriesSection}
588
+ };
589
+
590
+ export default workflows;
591
+ `;
592
+ }
593
+ function generateTriggerEntryFile(triggerKind) {
594
+ if (triggerKind === "http") {
595
+ return `import { DefaultLogger } from "@blok/runner";
596
+ import { type Span, metrics, trace } from "@opentelemetry/api";
597
+ import HttpTrigger from "./runner/HttpTrigger";
598
+
599
+ export default class App {
600
+ private httpTrigger: HttpTrigger = <HttpTrigger>{};
601
+ protected trigger_initializer = 0;
602
+ protected initializer = 0;
603
+ protected tracer = trace.getTracer(
604
+ process.env.PROJECT_NAME || "trigger-http-server",
605
+ process.env.PROJECT_VERSION || "0.0.1",
606
+ );
607
+ private logger = new DefaultLogger();
608
+ protected app_cold_start = metrics.getMeter("default").createGauge("initialization", {
609
+ description: "Application cold start",
610
+ });
611
+
612
+ constructor() {
613
+ this.initializer = performance.now();
614
+ this.httpTrigger = new HttpTrigger();
615
+ }
616
+
617
+ async run() {
618
+ this.tracer.startActiveSpan("initialization", async (span: Span) => {
619
+ await this.httpTrigger.listen();
620
+ this.initializer = performance.now() - this.initializer;
621
+
622
+ this.logger.log(\`Server initialized in \${(this.initializer).toFixed(2)}ms\`);
623
+ this.app_cold_start.record(this.initializer, {
624
+ pid: process.pid,
625
+ env: process.env.NODE_ENV,
626
+ app: process.env.APP_NAME,
627
+ });
628
+ span.end();
629
+ });
630
+ }
631
+
632
+ getHttpApp() {
633
+ return this.httpTrigger.getApp();
634
+ }
635
+ }
636
+
637
+ if (process.env.DISABLE_TRIGGER_RUN !== "true") {
638
+ new App().run();
639
+ }
640
+ `;
641
+ }
642
+ if (triggerKind === "sse") {
643
+ return `import { DefaultLogger } from "@blok/runner";
644
+ import { type Span, metrics, trace } from "@opentelemetry/api";
645
+ import SSEServer from "./runner/SSEServer";
646
+
647
+ export default class App {
648
+ private sseServer: SSEServer = <SSEServer>{};
649
+ protected trigger_initializer = 0;
650
+ protected initializer = 0;
651
+ protected tracer = trace.getTracer(
652
+ process.env.PROJECT_NAME || "trigger-sse-server",
653
+ process.env.PROJECT_VERSION || "0.0.1",
654
+ );
655
+ private logger = new DefaultLogger();
656
+ protected app_cold_start = metrics.getMeter("default").createGauge("initialization", {
657
+ description: "Application cold start",
658
+ });
659
+
660
+ constructor() {
661
+ this.initializer = performance.now();
662
+ this.sseServer = new SSEServer();
663
+ }
664
+
665
+ async run() {
666
+ this.tracer.startActiveSpan("initialization", async (span: Span) => {
667
+ await this.sseServer.listen();
668
+ this.initializer = performance.now() - this.initializer;
669
+
670
+ this.logger.log(\`Server initialized in \${(this.initializer).toFixed(2)}ms\`);
671
+ this.app_cold_start.record(this.initializer, {
672
+ pid: process.pid,
673
+ env: process.env.NODE_ENV,
674
+ app: process.env.APP_NAME,
675
+ });
676
+ span.end();
677
+ });
678
+ }
679
+
680
+ getApp() {
681
+ return this.sseServer.getApp();
682
+ }
683
+ }
684
+
685
+ {
686
+ new App().run();
687
+ }
688
+ `;
689
+ }
690
+ if (triggerKind === "pubsub") {
691
+ return `import { DefaultLogger } from "@blok/runner";
692
+ import { type Span, metrics, trace } from "@opentelemetry/api";
693
+ import PubSubServer from "./runner/PubSubServer";
694
+
695
+ export default class App {
696
+ private pubsubServer: PubSubServer = <PubSubServer>{};
697
+ protected trigger_initializer = 0;
698
+ protected initializer = 0;
699
+ protected tracer = trace.getTracer(
700
+ process.env.PROJECT_NAME || "trigger-pubsub-server",
701
+ process.env.PROJECT_VERSION || "0.0.1",
702
+ );
703
+ private logger = new DefaultLogger();
704
+ protected app_cold_start = metrics.getMeter("default").createGauge("initialization", {
705
+ description: "Application cold start",
706
+ });
707
+
708
+ constructor() {
709
+ this.initializer = performance.now();
710
+ this.pubsubServer = new PubSubServer();
711
+ }
712
+
713
+ async run() {
714
+ this.tracer.startActiveSpan("initialization", async (span: Span) => {
715
+ await this.pubsubServer.listen();
716
+ this.initializer = performance.now() - this.initializer;
717
+
718
+ this.logger.log(\`Pub/Sub trigger initialized in \${(this.initializer).toFixed(2)}ms\`);
719
+ this.app_cold_start.record(this.initializer, {
720
+ pid: process.pid,
721
+ env: process.env.NODE_ENV,
722
+ app: process.env.APP_NAME,
723
+ });
724
+ span.end();
725
+ });
726
+ }
727
+ }
728
+
729
+ if (process.env.DISABLE_TRIGGER_RUN !== "true") {
730
+ new App().run();
731
+ }
732
+ `;
733
+ }
734
+ if (triggerKind === "queue") {
735
+ return `import { DefaultLogger } from "@blok/runner";
736
+ import { type Span, metrics, trace } from "@opentelemetry/api";
737
+ import QueueServer from "./runner/QueueServer";
738
+
739
+ export default class App {
740
+ private queueServer: QueueServer = <QueueServer>{};
741
+ protected trigger_initializer = 0;
742
+ protected initializer = 0;
743
+ protected tracer = trace.getTracer(
744
+ process.env.PROJECT_NAME || "trigger-queue-server",
745
+ process.env.PROJECT_VERSION || "0.0.1",
746
+ );
747
+ private logger = new DefaultLogger();
748
+ protected app_cold_start = metrics.getMeter("default").createGauge("initialization", {
749
+ description: "Application cold start",
750
+ });
751
+
752
+ constructor() {
753
+ this.initializer = performance.now();
754
+ this.queueServer = new QueueServer();
755
+ }
756
+
757
+ async run() {
758
+ this.tracer.startActiveSpan("initialization", async (span: Span) => {
759
+ await this.queueServer.listen();
760
+ this.initializer = performance.now() - this.initializer;
761
+
762
+ this.logger.log(\`Queue trigger initialized in \${(this.initializer).toFixed(2)}ms\`);
763
+ this.app_cold_start.record(this.initializer, {
764
+ pid: process.pid,
765
+ env: process.env.NODE_ENV,
766
+ app: process.env.APP_NAME,
767
+ });
768
+ span.end();
769
+ });
770
+ }
771
+ }
772
+
773
+ if (process.env.DISABLE_TRIGGER_RUN !== "true") {
774
+ new App().run();
775
+ }
776
+ `;
777
+ }
778
+ return `// Entry point for ${triggerKind} trigger
779
+ // Implement trigger-specific initialization here
780
+ console.log("${triggerKind} trigger not yet implemented");
781
+ `;
782
+ }
783
+ function fixRunnerImportPaths(triggerDestDir, triggerKind) {
784
+ const filesToFix = [];
785
+ if (triggerKind === "http") {
786
+ filesToFix.push(`${triggerDestDir}/runner/HttpTrigger.ts`);
787
+ }
788
+ else if (triggerKind === "sse") {
789
+ filesToFix.push(`${triggerDestDir}/runner/SSEServer.ts`);
790
+ }
791
+ else if (triggerKind === "pubsub") {
792
+ filesToFix.push(`${triggerDestDir}/runner/PubSubServer.ts`);
793
+ }
794
+ else if (triggerKind === "queue") {
795
+ filesToFix.push(`${triggerDestDir}/runner/QueueServer.ts`);
796
+ }
797
+ for (const filePath of filesToFix) {
798
+ if (!fsExtra.existsSync(filePath))
799
+ continue;
800
+ let content = fsExtra.readFileSync(filePath, "utf8");
801
+ content = content.replace(/from ["']\.\.\/Nodes["']/g, 'from "../../../Nodes"');
802
+ content = content.replace(/from ["']\.\.\/Workflows["']/g, 'from "../../../Workflows"');
803
+ fsExtra.writeFileSync(filePath, content);
804
+ }
805
+ }
806
+ function updatePubSubProvider(triggerDestDir, provider) {
807
+ const serverPath = `${triggerDestDir}/runner/PubSubServer.ts`;
808
+ if (!fsExtra.existsSync(serverPath))
809
+ return;
810
+ let content = fsExtra.readFileSync(serverPath, "utf8");
811
+ const adapterConfigs = {
812
+ gcp: {
813
+ importName: "GCPPubSubAdapter",
814
+ init: `new GCPPubSubAdapter({
815
+ projectId: process.env.GCP_PROJECT_ID || "my-project",
816
+ })`,
817
+ },
818
+ aws: {
819
+ importName: "AWSSNSAdapter",
820
+ init: `new AWSSNSAdapter({
821
+ region: process.env.AWS_REGION || "us-east-1",
822
+ })`,
823
+ },
824
+ azure: {
825
+ importName: "AzureServiceBusAdapter",
826
+ init: `new AzureServiceBusAdapter({
827
+ connectionString: process.env.AZURE_SERVICE_BUS_CONNECTION_STRING || "",
828
+ })`,
829
+ },
830
+ };
831
+ const config = adapterConfigs[provider];
832
+ if (!config)
833
+ return;
834
+ content = content.replace(/import \{ PubSubTrigger, \w+ \} from "@blok\/trigger-pubsub";/, `import { PubSubTrigger, ${config.importName} } from "@blok/trigger-pubsub";`);
835
+ content = content.replace(/(export default class \w+ extends PubSubTrigger \{[\s\S]*?)\n\tprotected adapter = new \w+\(\{[\s\S]*?\}\);/, `$1\n\tprotected adapter = ${config.init};`);
836
+ fsExtra.writeFileSync(serverPath, content);
837
+ }
838
+ function updateQueueProvider(triggerDestDir, provider) {
839
+ const serverPath = `${triggerDestDir}/runner/QueueServer.ts`;
840
+ if (!fsExtra.existsSync(serverPath))
841
+ return;
842
+ let content = fsExtra.readFileSync(serverPath, "utf8");
843
+ const adapterConfigs = {
844
+ kafka: {
845
+ importName: "KafkaAdapter",
846
+ init: `new KafkaAdapter({
847
+ brokers: (process.env.KAFKA_BROKERS || "localhost:9092").split(","),
848
+ clientId: process.env.KAFKA_CLIENT_ID || "blok-queue-trigger",
849
+ })`,
850
+ },
851
+ rabbitmq: {
852
+ importName: "RabbitMQAdapter",
853
+ init: `new RabbitMQAdapter({
854
+ url: process.env.RABBITMQ_URL || "amqp://localhost",
855
+ })`,
856
+ },
857
+ sqs: {
858
+ importName: "SQSAdapter",
859
+ init: `new SQSAdapter({
860
+ region: process.env.AWS_REGION || "us-east-1",
861
+ })`,
862
+ },
863
+ redis: {
864
+ importName: "RedisAdapter",
865
+ init: `new RedisAdapter({
866
+ host: process.env.REDIS_HOST || "localhost",
867
+ port: Number(process.env.REDIS_PORT) || 6379,
868
+ })`,
869
+ },
870
+ };
871
+ const config = adapterConfigs[provider];
872
+ if (!config)
873
+ return;
874
+ content = content.replace(/import \{ QueueTrigger, \w+ \} from "@blok\/trigger-queue";/, `import { QueueTrigger, ${config.importName} } from "@blok/trigger-queue";`);
875
+ content = content.replace(/(export default class \w+ extends QueueTrigger \{[\s\S]*?)\n\tprotected adapter = new \w+\(\{[\s\S]*?\}\);/, `$1\n\tprotected adapter = ${config.init};`);
876
+ fsExtra.writeFileSync(serverPath, content);
877
+ }
878
+ function getProviderDependencies(triggers, pubsubProvider, queueProvider) {
879
+ const deps = {};
880
+ const pubsubProviderDeps = {
881
+ gcp: { "@google-cloud/pubsub": "^4.0.0" },
882
+ aws: { "@aws-sdk/client-sns": "^3.980.0", "@aws-sdk/client-sqs": "^3.980.0" },
883
+ azure: { "@azure/service-bus": "^7.9.5" },
884
+ };
885
+ const queueProviderDeps = {
886
+ kafka: { kafkajs: "^2.2.4" },
887
+ rabbitmq: { amqplib: "^0.10.9" },
888
+ sqs: { "@aws-sdk/client-sqs": "^3.980.0" },
889
+ redis: { ioredis: "^5.9.2", bullmq: "^5.67.2" },
890
+ };
891
+ if (triggers.includes("pubsub") && pubsubProviderDeps[pubsubProvider]) {
892
+ Object.assign(deps, pubsubProviderDeps[pubsubProvider]);
893
+ }
894
+ if (triggers.includes("queue") && queueProviderDeps[queueProvider]) {
895
+ Object.assign(deps, queueProviderDeps[queueProvider]);
896
+ }
897
+ return deps;
898
+ }
899
+ function getProviderEnvVars(triggers, pubsubProvider, queueProvider) {
900
+ const lines = [];
901
+ const pubsubEnvVars = {
902
+ gcp: `
903
+ # Google Cloud Pub/Sub
904
+ GCP_PROJECT_ID=my-project
905
+ GOOGLE_APPLICATION_CREDENTIALS=./credentials.json`,
906
+ aws: `
907
+ # AWS SNS/SQS
908
+ AWS_REGION=us-east-1
909
+ AWS_ACCESS_KEY_ID=
910
+ AWS_SECRET_ACCESS_KEY=`,
911
+ azure: `
912
+ # Azure Service Bus
913
+ AZURE_SERVICE_BUS_CONNECTION_STRING=`,
914
+ };
915
+ const queueEnvVars = {
916
+ kafka: `
917
+ # Apache Kafka
918
+ KAFKA_BROKERS=localhost:9092
919
+ KAFKA_CLIENT_ID=blok-queue-trigger
920
+ KAFKA_GROUP_ID=blok-consumer-group`,
921
+ rabbitmq: `
922
+ # RabbitMQ
923
+ RABBITMQ_URL=amqp://localhost`,
924
+ sqs: `
925
+ # AWS SQS
926
+ AWS_REGION=us-east-1
927
+ SQS_QUEUE_URL=`,
928
+ redis: `
929
+ # Redis/BullMQ
930
+ REDIS_HOST=localhost
931
+ REDIS_PORT=6379
932
+ REDIS_PASSWORD=`,
933
+ };
934
+ if (triggers.includes("pubsub") && pubsubEnvVars[pubsubProvider]) {
935
+ lines.push(pubsubEnvVars[pubsubProvider]);
936
+ }
937
+ if (triggers.includes("queue") && queueEnvVars[queueProvider]) {
938
+ lines.push(queueEnvVars[queueProvider]);
939
+ }
940
+ return lines.join("\n");
941
+ }