blokctl 0.6.21 → 1.0.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 (81) hide show
  1. package/dist/__tests__/modular-observability.capstone.e2e.test.js +72 -0
  2. package/dist/commands/create/node.js +46 -66
  3. package/dist/commands/create/project.js +60 -9
  4. package/dist/commands/create/utils/Examples.d.ts +11 -23
  5. package/dist/commands/create/utils/Examples.js +206 -421
  6. package/dist/commands/dev/index.js +40 -1
  7. package/dist/commands/generate/NodeGenerator.d.ts +0 -2
  8. package/dist/commands/generate/NodeGenerator.js +0 -20
  9. package/dist/commands/generate/RuntimeGenerator.d.ts +0 -2
  10. package/dist/commands/generate/RuntimeGenerator.js +0 -19
  11. package/dist/commands/generate/RuntimeGenerator.test.js +0 -29
  12. package/dist/commands/generate/TriggerGenerator.d.ts +0 -2
  13. package/dist/commands/generate/TriggerGenerator.js +0 -19
  14. package/dist/commands/generate/WorkflowGenerator.d.ts +0 -2
  15. package/dist/commands/generate/WorkflowGenerator.js +4 -23
  16. package/dist/commands/generate/WorkflowGenerator.test.js +2 -2
  17. package/dist/commands/generate/e2e/NodeGenerator.e2e.test.js +0 -12
  18. package/dist/commands/generate/e2e/RuntimeGenerator.e2e.test.js +0 -12
  19. package/dist/commands/generate/e2e/TriggerGenerator.e2e.test.js +0 -14
  20. package/dist/commands/generate/prompts/create-fn-node.system.js +10 -17
  21. package/dist/commands/generate/prompts/create-workflow.system.js +114 -375
  22. package/dist/commands/generate/validators/WorkflowValidator.js +80 -5
  23. package/dist/commands/generate/validators/WorkflowValidator.test.js +66 -0
  24. package/dist/commands/monitor/monitor-component.js +5 -5
  25. package/dist/commands/observability/add.d.ts +2 -0
  26. package/dist/commands/observability/add.js +113 -0
  27. package/dist/commands/observability/alerting-module.test.js +43 -0
  28. package/dist/commands/observability/apply.d.ts +10 -0
  29. package/dist/commands/observability/apply.js +11 -0
  30. package/dist/commands/observability/descriptor.d.ts +37 -0
  31. package/dist/commands/observability/descriptor.js +203 -0
  32. package/dist/commands/observability/descriptor.test.d.ts +1 -0
  33. package/dist/commands/observability/descriptor.test.js +40 -0
  34. package/dist/commands/observability/index.d.ts +1 -0
  35. package/dist/commands/observability/index.js +53 -0
  36. package/dist/commands/observability/list.d.ts +2 -0
  37. package/dist/commands/observability/list.js +45 -0
  38. package/dist/commands/observability/logging-module.test.d.ts +1 -0
  39. package/dist/commands/observability/logging-module.test.js +43 -0
  40. package/dist/commands/observability/obs-stack-module.test.d.ts +1 -0
  41. package/dist/commands/observability/obs-stack-module.test.js +33 -0
  42. package/dist/commands/observability/remove.d.ts +2 -0
  43. package/dist/commands/observability/remove.js +62 -0
  44. package/dist/commands/observability/shared.d.ts +6 -0
  45. package/dist/commands/observability/shared.js +23 -0
  46. package/dist/commands/observability/status.d.ts +2 -0
  47. package/dist/commands/observability/status.js +36 -0
  48. package/dist/commands/observability/tracing-module.test.d.ts +1 -0
  49. package/dist/commands/observability/tracing-module.test.js +42 -0
  50. package/dist/commands/profile/index.js +7 -10
  51. package/dist/commands/watch/format.d.ts +23 -0
  52. package/dist/commands/watch/format.js +60 -0
  53. package/dist/commands/watch/index.d.ts +1 -0
  54. package/dist/commands/watch/index.js +53 -0
  55. package/dist/commands/watch/sse.d.ts +16 -0
  56. package/dist/commands/watch/sse.js +82 -0
  57. package/dist/index.d.ts +2 -0
  58. package/dist/index.js +4 -0
  59. package/dist/services/obs-setup.d.ts +5 -0
  60. package/dist/services/obs-setup.js +68 -0
  61. package/dist/services/obs-setup.test.d.ts +1 -0
  62. package/dist/services/obs-setup.test.js +71 -0
  63. package/dist/services/obs-tiers.d.ts +9 -0
  64. package/dist/services/obs-tiers.js +16 -0
  65. package/dist/services/observability-mutations.d.ts +4 -0
  66. package/dist/services/observability-mutations.js +46 -0
  67. package/dist/services/observability-mutations.test.d.ts +1 -0
  68. package/dist/services/observability-mutations.test.js +57 -0
  69. package/dist/services/runtime-setup.d.ts +12 -1
  70. package/dist/services/runtime-setup.js +274 -14
  71. package/dist/studio-dist/assets/{index-BD8_9YPN.js → index-CnFqCRQe.js} +17 -17
  72. package/dist/studio-dist/index.html +1 -1
  73. package/package.json +3 -3
  74. package/dist/commands/generate/GenerationAnalytics.d.ts +0 -61
  75. package/dist/commands/generate/GenerationAnalytics.js +0 -163
  76. package/dist/commands/generate/GenerationAnalytics.test.js +0 -407
  77. package/dist/commands/generate/PromptVersioning.d.ts +0 -25
  78. package/dist/commands/generate/PromptVersioning.js +0 -71
  79. package/dist/commands/generate/PromptVersioning.test.js +0 -120
  80. /package/dist/{commands/generate/GenerationAnalytics.test.d.ts → __tests__/modular-observability.capstone.e2e.test.d.ts} +0 -0
  81. /package/dist/commands/{generate/PromptVersioning.test.d.ts → observability/alerting-module.test.d.ts} +0 -0
@@ -1,9 +1,12 @@
1
1
  import { spawn } from "node:child_process";
2
+ import child_process from "node:child_process";
2
3
  import path from "node:path";
4
+ import util from "node:util";
3
5
  import fsExtra from "fs-extra";
4
6
  import { waitForGrpcPort } from "../../services/health-probe.js";
5
7
  import { detectRr } from "../../services/runtime-detector.js";
6
- import { readProjectConfig, validateProjectRuntimes } from "../../services/runtime-setup.js";
8
+ import { generateCSharpNodeRegistry, generateGoNodeRegistry, generateJavaNodeRegistry, generateRustNodeRegistry, readProjectConfig, validateProjectRuntimes, } from "../../services/runtime-setup.js";
9
+ const exec = util.promisify(child_process.exec);
7
10
  const runningProcesses = [];
8
11
  function spawnProcess(cmd, args, name, currentPath, cwd, env) {
9
12
  const child = spawn(cmd, args, {
@@ -98,6 +101,42 @@ export async function devProject(opts) {
98
101
  HOST: "0.0.0.0",
99
102
  BLOK_TRANSPORT: "grpc",
100
103
  };
104
+ if (rt.kind === "python3" || rt.kind === "ruby" || rt.kind === "php") {
105
+ env.BLOK_NODES_DIR = path.resolve(currentPath, "runtimes", rt.kind, "nodes");
106
+ }
107
+ if (rt.kind === "go") {
108
+ try {
109
+ generateGoNodeRegistry(currentPath);
110
+ }
111
+ catch (err) {
112
+ console.log(` Warning: Go user-node codegen failed: ${err.message}`);
113
+ }
114
+ }
115
+ if (rt.kind === "rust") {
116
+ try {
117
+ generateRustNodeRegistry(currentPath);
118
+ }
119
+ catch (err) {
120
+ console.log(` Warning: Rust user-node codegen failed: ${err.message}`);
121
+ }
122
+ }
123
+ if (rt.kind === "csharp") {
124
+ try {
125
+ generateCSharpNodeRegistry(currentPath);
126
+ }
127
+ catch (err) {
128
+ console.log(` Warning: C# user-node codegen failed: ${err.message}`);
129
+ }
130
+ }
131
+ if (rt.kind === "java") {
132
+ try {
133
+ generateJavaNodeRegistry(currentPath);
134
+ await exec("mvn package -q -DskipTests", { cwd: runtimeCwd, timeout: 300000 });
135
+ }
136
+ catch (err) {
137
+ console.log(` Warning: Java user-node codegen/build failed: ${err.message}`);
138
+ }
139
+ }
101
140
  runtimeDefs.push({
102
141
  cmd,
103
142
  args,
@@ -7,8 +7,6 @@ type NodeInformation = {
7
7
  errors: string[];
8
8
  warnings: string[];
9
9
  attempts: number;
10
- promptVersion?: string;
11
- durationMs?: number;
12
10
  };
13
11
  };
14
12
  export type { NodeInformation };
@@ -1,8 +1,6 @@
1
1
  import * as fs from "node:fs";
2
2
  import { createOpenAI } from "@ai-sdk/openai";
3
3
  import { generateText } from "ai";
4
- import { GenerationAnalytics } from "./GenerationAnalytics.js";
5
- import { getVersionStamp, registerPromptContent } from "./PromptVersioning.js";
6
4
  import createFnNodeSystemPrompt from "./prompts/create-fn-node.system.js";
7
5
  import createNodeSystemPrompt from "./prompts/create-node.system.js";
8
6
  import * as CompilationValidator from "./validators/CompilationValidator.js";
@@ -10,10 +8,6 @@ import * as NodeValidator from "./validators/NodeValidator.js";
10
8
  export default class NodeGenerator {
11
9
  MAX_VALIDATION_ATTEMPTS = 3;
12
10
  async generateNode(nodeName, userPrompt, apiKey, update = false, nodeStyle = "function") {
13
- const analytics = GenerationAnalytics.getInstance();
14
- const getElapsed = analytics.startTimer();
15
- const promptId = nodeStyle === "function" ? "create-fn-node" : "create-node";
16
- const promptVersion = getVersionStamp(promptId);
17
11
  const openai = createOpenAI({
18
12
  compatibility: "strict",
19
13
  apiKey: apiKey,
@@ -21,7 +15,6 @@ export default class NodeGenerator {
21
15
  const promptTemplate = nodeStyle === "function" ? createFnNodeSystemPrompt : createNodeSystemPrompt;
22
16
  let prompt = promptTemplate.prompt;
23
17
  let existingCode = null;
24
- registerPromptContent(promptId, prompt);
25
18
  if (update) {
26
19
  const dirName = nodeName.toLowerCase().replace(/\s+/g, "-");
27
20
  const dirPath = process.cwd();
@@ -72,17 +65,6 @@ export default class NodeGenerator {
72
65
  console.log(`⚠️ Validation failed (attempt ${attempts}/${this.MAX_VALIDATION_ATTEMPTS}). Retrying with feedback...`);
73
66
  }
74
67
  }
75
- const durationMs = getElapsed();
76
- analytics.recordEvent({
77
- type: "node",
78
- subtype: nodeStyle,
79
- name: nodeName,
80
- success: isValid,
81
- attempts,
82
- durationMs,
83
- errors: allErrors,
84
- promptVersion,
85
- });
86
68
  return {
87
69
  nodeName,
88
70
  userPrompt,
@@ -92,8 +74,6 @@ export default class NodeGenerator {
92
74
  errors: validationErrors,
93
75
  warnings: validationWarnings,
94
76
  attempts,
95
- promptVersion,
96
- durationMs,
97
77
  },
98
78
  };
99
79
  }
@@ -11,8 +11,6 @@ export type RuntimeInformation = {
11
11
  errors: string[];
12
12
  warnings: string[];
13
13
  attempts: number;
14
- promptVersion?: string;
15
- durationMs?: number;
16
14
  };
17
15
  };
18
16
  declare const SUPPORTED_LANGUAGES: readonly ["go", "java", "rust", "python", "csharp", "php", "ruby"];
@@ -1,8 +1,6 @@
1
1
  import * as fs from "node:fs";
2
2
  import { createOpenAI } from "@ai-sdk/openai";
3
3
  import { generateText } from "ai";
4
- import { GenerationAnalytics } from "./GenerationAnalytics.js";
5
- import { getVersionStamp, registerPromptContent } from "./PromptVersioning.js";
6
4
  import createRuntimeSystemPrompt from "./prompts/create-runtime.system.js";
7
5
  const SUPPORTED_LANGUAGES = ["go", "java", "rust", "python", "csharp", "php", "ruby"];
8
6
  export function isSupportedLanguage(lang) {
@@ -11,15 +9,11 @@ export function isSupportedLanguage(lang) {
11
9
  export default class RuntimeGenerator {
12
10
  MAX_VALIDATION_ATTEMPTS = 3;
13
11
  async generateRuntime(language, userPrompt, apiKey, update = false, existingPath) {
14
- const analytics = GenerationAnalytics.getInstance();
15
- const getElapsed = analytics.startTimer();
16
- const promptVersion = getVersionStamp("create-runtime");
17
12
  const openai = createOpenAI({
18
13
  compatibility: "strict",
19
14
  apiKey: apiKey,
20
15
  });
21
16
  let prompt = createRuntimeSystemPrompt.prompt;
22
- registerPromptContent("create-runtime", prompt);
23
17
  if (update && existingPath) {
24
18
  const existingContent = this.readExistingRuntime(existingPath);
25
19
  prompt = `${createRuntimeSystemPrompt.updatePrompt}\n\n${existingContent}`;
@@ -54,17 +48,6 @@ export default class RuntimeGenerator {
54
48
  }
55
49
  }
56
50
  const files = this.parseFiles(generatedCode, language);
57
- const durationMs = getElapsed();
58
- analytics.recordEvent({
59
- type: "node",
60
- subtype: `runtime-${language}`,
61
- name: `runtime-${language}`,
62
- success: isValid,
63
- attempts,
64
- durationMs,
65
- errors: allErrors,
66
- promptVersion,
67
- });
68
51
  return {
69
52
  language,
70
53
  userPrompt,
@@ -75,8 +58,6 @@ export default class RuntimeGenerator {
75
58
  errors: validationErrors,
76
59
  warnings: validationWarnings,
77
60
  attempts,
78
- promptVersion,
79
- durationMs,
80
61
  },
81
62
  };
82
63
  }
@@ -6,7 +6,6 @@ vi.mock("ai", () => ({
6
6
  generateText: vi.fn(),
7
7
  }));
8
8
  import { generateText } from "ai";
9
- import { GenerationAnalytics } from "./GenerationAnalytics.js";
10
9
  import RuntimeGenerator, { isSupportedLanguage } from "./RuntimeGenerator.js";
11
10
  const mockedGenerateText = vi.mocked(generateText);
12
11
  describe("RuntimeGenerator", () => {
@@ -15,7 +14,6 @@ describe("RuntimeGenerator", () => {
15
14
  generator = new RuntimeGenerator();
16
15
  vi.clearAllMocks();
17
16
  vi.spyOn(console, "log").mockImplementation(() => { });
18
- GenerationAnalytics.resetInstance();
19
17
  });
20
18
  afterEach(() => {
21
19
  vi.restoreAllMocks();
@@ -433,39 +431,12 @@ func main() {
433
431
  expect(result.validationResult?.attempts).toBe(3);
434
432
  expect(result.validationResult?.errors.length).toBeGreaterThan(0);
435
433
  });
436
- it("should record analytics event", async () => {
437
- mockedGenerateText.mockResolvedValueOnce({ text: validGoRuntime });
438
- await generator.generateRuntime("go", "test", "test-key");
439
- const analytics = GenerationAnalytics.getInstance();
440
- const stats = analytics.getStats();
441
- expect(stats.totalGenerations).toBe(1);
442
- expect(stats.successCount).toBe(1);
443
- });
444
- it("should record failed analytics event", async () => {
445
- mockedGenerateText.mockResolvedValue({ text: "invalid" });
446
- await generator.generateRuntime("java", "test", "test-key");
447
- const analytics = GenerationAnalytics.getInstance();
448
- const stats = analytics.getStats();
449
- expect(stats.totalGenerations).toBe(1);
450
- expect(stats.failureCount).toBe(1);
451
- });
452
434
  it("should strip markdown fences from LLM output", async () => {
453
435
  const wrappedCode = `\`\`\`go\n${validGoRuntime}\n\`\`\``;
454
436
  mockedGenerateText.mockResolvedValueOnce({ text: wrappedCode });
455
437
  const result = await generator.generateRuntime("go", "test", "test-key");
456
438
  expect(result.validationResult?.valid).toBe(true);
457
439
  });
458
- it("should include prompt version in validation result", async () => {
459
- mockedGenerateText.mockResolvedValueOnce({ text: validGoRuntime });
460
- const result = await generator.generateRuntime("go", "test", "test-key");
461
- expect(result.validationResult?.promptVersion).toBe("create-runtime@1.0.0");
462
- });
463
- it("should include duration in validation result", async () => {
464
- mockedGenerateText.mockResolvedValueOnce({ text: validGoRuntime });
465
- const result = await generator.generateRuntime("go", "test", "test-key");
466
- expect(result.validationResult?.durationMs).toBeDefined();
467
- expect(typeof result.validationResult?.durationMs).toBe("number");
468
- });
469
440
  });
470
441
  describe("language-specific validation", () => {
471
442
  it("should warn Go code without go.mod", () => {
@@ -8,8 +8,6 @@ export type TriggerInformation = {
8
8
  errors: string[];
9
9
  warnings: string[];
10
10
  attempts: number;
11
- promptVersion?: string;
12
- durationMs?: number;
13
11
  };
14
12
  };
15
13
  export default class TriggerGenerator {
@@ -1,22 +1,16 @@
1
1
  import * as fs from "node:fs";
2
2
  import { createOpenAI } from "@ai-sdk/openai";
3
3
  import { generateText } from "ai";
4
- import { GenerationAnalytics } from "./GenerationAnalytics.js";
5
- import { getVersionStamp, registerPromptContent } from "./PromptVersioning.js";
6
4
  import createTriggerSystemPrompt from "./prompts/create-trigger.system.js";
7
5
  import * as CompilationValidator from "./validators/CompilationValidator.js";
8
6
  export default class TriggerGenerator {
9
7
  MAX_VALIDATION_ATTEMPTS = 3;
10
8
  async generateTrigger(triggerName, triggerType, userPrompt, apiKey, update = false, existingTriggerPath) {
11
- const analytics = GenerationAnalytics.getInstance();
12
- const getElapsed = analytics.startTimer();
13
- const promptVersion = getVersionStamp("create-trigger");
14
9
  const openai = createOpenAI({
15
10
  compatibility: "strict",
16
11
  apiKey: apiKey,
17
12
  });
18
13
  let prompt = createTriggerSystemPrompt.prompt;
19
- registerPromptContent("create-trigger", prompt);
20
14
  if (update && existingTriggerPath) {
21
15
  const existingContent = fs.readFileSync(existingTriggerPath, "utf8");
22
16
  prompt = `${createTriggerSystemPrompt.updatePrompt}\n\n${existingContent}`;
@@ -58,17 +52,6 @@ export default class TriggerGenerator {
58
52
  console.log(`⚠️ Trigger validation failed (attempt ${attempts}/${this.MAX_VALIDATION_ATTEMPTS}). Retrying with feedback...`);
59
53
  }
60
54
  }
61
- const durationMs = getElapsed();
62
- analytics.recordEvent({
63
- type: "trigger",
64
- subtype: triggerType,
65
- name: triggerName,
66
- success: isValid,
67
- attempts,
68
- durationMs,
69
- errors: allErrors,
70
- promptVersion,
71
- });
72
55
  return {
73
56
  triggerName,
74
57
  triggerType,
@@ -79,8 +62,6 @@ export default class TriggerGenerator {
79
62
  errors: validationErrors,
80
63
  warnings: validationWarnings,
81
64
  attempts,
82
- promptVersion,
83
- durationMs,
84
65
  },
85
66
  };
86
67
  }
@@ -8,8 +8,6 @@ export type WorkflowInformation = {
8
8
  errors: string[];
9
9
  warnings: string[];
10
10
  attempts: number;
11
- promptVersion?: string;
12
- durationMs?: number;
13
11
  };
14
12
  };
15
13
  export default class WorkflowGenerator {
@@ -1,22 +1,16 @@
1
1
  import * as fs from "node:fs";
2
2
  import { createOpenAI } from "@ai-sdk/openai";
3
3
  import { generateText } from "ai";
4
- import { GenerationAnalytics } from "./GenerationAnalytics.js";
5
- import { getVersionStamp, registerPromptContent } from "./PromptVersioning.js";
6
4
  import createWorkflowSystemPrompt from "./prompts/create-workflow.system.js";
7
5
  import * as WorkflowValidator from "./validators/WorkflowValidator.js";
8
6
  export default class WorkflowGenerator {
9
7
  MAX_VALIDATION_ATTEMPTS = 3;
10
8
  async generateWorkflow(workflowName, userPrompt, apiKey, triggerType, update = false, existingWorkflowPath) {
11
- const analytics = GenerationAnalytics.getInstance();
12
- const getElapsed = analytics.startTimer();
13
- const promptVersion = getVersionStamp("create-workflow");
14
9
  const openai = createOpenAI({
15
10
  compatibility: "strict",
16
11
  apiKey: apiKey,
17
12
  });
18
13
  let prompt = createWorkflowSystemPrompt.prompt;
19
- registerPromptContent("create-workflow", prompt);
20
14
  if (update && existingWorkflowPath) {
21
15
  const existingContent = fs.readFileSync(existingWorkflowPath, "utf8");
22
16
  prompt = `${createWorkflowSystemPrompt.updatePrompt}\n\n${existingContent}`;
@@ -50,17 +44,6 @@ export default class WorkflowGenerator {
50
44
  console.log(`⚠️ Workflow validation failed (attempt ${attempts}/${this.MAX_VALIDATION_ATTEMPTS}). Retrying with feedback...`);
51
45
  }
52
46
  }
53
- const durationMs = getElapsed();
54
- analytics.recordEvent({
55
- type: "workflow",
56
- subtype: triggerType,
57
- name: workflowName,
58
- success: isValid,
59
- attempts,
60
- durationMs,
61
- errors: allErrors,
62
- promptVersion,
63
- });
64
47
  return {
65
48
  workflowName,
66
49
  userPrompt,
@@ -71,8 +54,6 @@ export default class WorkflowGenerator {
71
54
  errors: validationErrors,
72
55
  warnings: validationWarnings,
73
56
  attempts,
74
- promptVersion,
75
- durationMs,
76
57
  },
77
58
  };
78
59
  }
@@ -120,11 +101,11 @@ export default class WorkflowGenerator {
120
101
  "```",
121
102
  "",
122
103
  "Please fix these errors and regenerate the workflow JSON. Common fixes:",
123
- "- Ensure every step name has a matching entry in the nodes object",
104
+ "- Ensure every step has a unique `id` and a `use` (or a `branch`) — inputs live inline; there is NO `nodes` map",
124
105
  "- Ensure the trigger has exactly one trigger type with valid configuration",
125
- "- Ensure condition expressions use ctx.* properties",
126
- "- Ensure all conditional branches have name, node, and type fields",
127
- "- Add an else branch to conditional routing for error handling",
106
+ "- Ensure branch `when` is a raw `ctx.*` expression (never `js/` or `$.`)",
107
+ "- Reference earlier outputs with `$.state.<id>` and request data with `$.req.*`",
108
+ "- Use a single step with `branch: { when, then, else }` for conditionals (not a `conditions` array)",
128
109
  ].join("\n");
129
110
  return feedback;
130
111
  }
@@ -70,8 +70,8 @@ describe("WorkflowGenerator", () => {
70
70
  });
71
71
  it("should include common fix suggestions", () => {
72
72
  const result = createFeedback("Test", "{}", ["Missing node entry"]);
73
- expect(result).toContain("step name has a matching entry");
74
- expect(result).toContain("else branch");
73
+ expect(result).toContain("unique `id`");
74
+ expect(result).toContain("branch: { when, then, else }");
75
75
  });
76
76
  });
77
77
  });
@@ -115,18 +115,6 @@ describe("NodeGenerator E2E", () => {
115
115
  const callArgs = mockedGenerateText.mock.calls[0][0];
116
116
  expect(callArgs.temperature).toBe(0.2);
117
117
  });
118
- it("should include prompt version in validation result", async () => {
119
- mockedGenerateText.mockResolvedValueOnce({ text: VALID_FUNCTION_FIRST_NODE });
120
- mockValidPass();
121
- const result = await generator.generateNode("test-node", "Create a test node", "test-api-key", false, "function");
122
- expect(result.validationResult.promptVersion).toContain("create-fn-node@");
123
- });
124
- it("should include duration in validation result", async () => {
125
- mockedGenerateText.mockResolvedValueOnce({ text: VALID_FUNCTION_FIRST_NODE });
126
- mockValidPass();
127
- const result = await generator.generateNode("test-node", "Create a test node", "test-api-key", false, "function");
128
- expect(result.validationResult.durationMs).toBeGreaterThanOrEqual(0);
129
- });
130
118
  });
131
119
  describe("validation feedback loop", () => {
132
120
  it("should retry with feedback on first failure and succeed on second attempt", async () => {
@@ -645,18 +645,6 @@ describe("RuntimeGenerator E2E", () => {
645
645
  expect(mockedGenerateText).toHaveBeenCalledTimes(1);
646
646
  });
647
647
  });
648
- describe("analytics integration", () => {
649
- it("should include prompt version in validation result", async () => {
650
- mockedGenerateText.mockResolvedValueOnce({ text: VALID_GO_RUNTIME });
651
- const result = await generator.generateRuntime("go", "Create a runtime", "test-api-key");
652
- expect(result.validationResult.promptVersion).toContain("create-runtime@");
653
- });
654
- it("should include duration in validation result", async () => {
655
- mockedGenerateText.mockResolvedValueOnce({ text: VALID_GO_RUNTIME });
656
- const result = await generator.generateRuntime("go", "Create a runtime", "test-api-key");
657
- expect(result.validationResult.durationMs).toBeGreaterThanOrEqual(0);
658
- });
659
- });
660
648
  describe("multi-file parsing", () => {
661
649
  it("should parse files separated by // FILE: markers", async () => {
662
650
  const code = [
@@ -278,18 +278,4 @@ describe("TriggerGenerator E2E", () => {
278
278
  expect(system).toContain("loadNodes");
279
279
  });
280
280
  });
281
- describe("analytics integration", () => {
282
- it("should include prompt version in validation result", async () => {
283
- mockedGenerateText.mockResolvedValueOnce({ text: VALID_QUEUE_TRIGGER });
284
- mockedValidateCode.mockReturnValue({ success: true, errors: [], warnings: [] });
285
- const result = await generator.generateTrigger("test-trigger", "queue", "Create a test trigger", "test-api-key");
286
- expect(result.validationResult.promptVersion).toContain("create-trigger@");
287
- });
288
- it("should include duration in validation result", async () => {
289
- mockedGenerateText.mockResolvedValueOnce({ text: VALID_QUEUE_TRIGGER });
290
- mockedValidateCode.mockReturnValue({ success: true, errors: [], warnings: [] });
291
- const result = await generator.generateTrigger("test-trigger", "queue", "Create a test trigger", "test-api-key");
292
- expect(result.validationResult.durationMs).toBeGreaterThanOrEqual(0);
293
- });
294
- });
295
281
  });
@@ -9,7 +9,7 @@ What to return:
9
9
  1. Proper imports:
10
10
  * \`z\` from \`zod\`
11
11
  * \`Context\` from \`@blokjs/shared\`
12
- * \`defineNode\` from \`@blokjs/runner\`
12
+ * \`defineNode\` from \`@blokjs/core\`
13
13
  2. A clear and structured \`input\` schema using Zod (z.object with proper types).
14
14
  3. A matching \`output\` schema using Zod.
15
15
  4. A single exported node instance created via \`defineNode\` with:
@@ -26,11 +26,11 @@ Constraints:
26
26
  * The Zod \`output\` schema must fully describe the object returned by \`execute\`.
27
27
  * Inside \`execute(ctx, input)\`:
28
28
  * Use the strongly-typed \`input\`, which is automatically inferred from the Zod schema.
29
- * Use \`ctx\` to access request data, configuration, and cross-node state when needed:
29
+ * Use \`ctx\` to access request data, configuration, and logging when needed:
30
30
  * \`ctx.request.body\`, \`ctx.request.query\`, \`ctx.request.params\` for HTTP data
31
- * \`ctx.vars\` for reading/writing values shared between nodes
32
31
  * \`ctx.logger\` for logging
33
32
  * \`ctx.env\` for environment variables
33
+ * Do **not** write to \`ctx.state\`/\`ctx.vars\` from a node — just RETURN your output; the runner auto-persists it to \`ctx.state[<step-id>]\` for downstream steps. Upstream values arrive as \`input.*\` (mapped in the workflow), not by reading \`ctx.vars\`.
34
34
  * Do **not** construct or return \`BlokResponse\` here; just return a plain object matching the output schema. The wrapper created by \`defineNode\` will call \`setSuccess\` / \`setError\` and handle \`GlobalError\`.
35
35
  * On validation errors or runtime errors, you do NOT manually throw \`GlobalError\`; throw/rethrow normal errors. The \`defineNode\` wrapper will catch them and map them to \`GlobalError\` consistently with proper error codes:
36
36
  * Zod validation errors → 400 Bad Request
@@ -52,7 +52,7 @@ Real-World Examples to Guide You:
52
52
  \`\`\`typescript
53
53
  import type { Context } from "@blokjs/shared";
54
54
  import { z } from "zod";
55
- import { defineNode } from "@blokjs/runner";
55
+ import { defineNode } from "@blokjs/core";
56
56
 
57
57
  export default defineNode({
58
58
  name: "api-call",
@@ -102,10 +102,6 @@ export default defineNode({
102
102
 
103
103
  ctx.logger.log(\`Request completed in \${duration.toFixed(2)}ms with status \${response.status}\`);
104
104
 
105
- if (ctx.vars) {
106
- ctx.vars["api-response"] = { status: response.status, data };
107
- }
108
-
109
105
  return {
110
106
  status: response.status,
111
107
  statusText: response.statusText,
@@ -122,7 +118,7 @@ export default defineNode({
122
118
  \`\`\`typescript
123
119
  import type { Context } from "@blokjs/shared";
124
120
  import { z } from "zod";
125
- import { defineNode } from "@blokjs/runner";
121
+ import { defineNode } from "@blokjs/core";
126
122
 
127
123
  export default defineNode({
128
124
  name: "fetch-user",
@@ -149,11 +145,8 @@ export default defineNode({
149
145
  // Simulate database fetch
150
146
  const user = await fetchUserFromDatabase(input.userId, input.includeMetadata);
151
147
 
152
- // Store in context for downstream nodes
153
- if (ctx.vars) {
154
- ctx.vars["current-user"] = user;
155
- }
156
-
148
+ // Just return the runner persists this to ctx.state["fetch-user"]
149
+ // for any downstream step that maps it in as an input.
157
150
  return { user };
158
151
  },
159
152
  });
@@ -176,7 +169,7 @@ Template to follow (adapt and fill based on the user's request):
176
169
 
177
170
  import type { Context } from "@blokjs/shared";
178
171
  import { z } from "zod";
179
- import { defineNode } from "@blokjs/runner";
172
+ import { defineNode } from "@blokjs/core";
180
173
 
181
174
  /**
182
175
  * [Brief description of what this node does]
@@ -210,11 +203,11 @@ export default defineNode({
210
203
  //
211
204
  // Common patterns:
212
205
  // - Read HTTP params: const id = ctx.request.params.id;
213
- // - Read previous node output: const prev = ctx.vars["previous-node-key"];
214
- // - Write for future nodes: ctx.vars["this-node-key"] = someValue;
206
+ // - Read upstream values via input.* (mapped from prior steps in the workflow) — NOT ctx.vars
215
207
  // - Use input.* fields that match the input schema (TypeScript infers the type automatically)
216
208
  // - Log messages: ctx.logger.log("Processing request");
217
209
  // - Access environment: const apiKey = ctx.env.API_KEY;
210
+ // - Do NOT write ctx.state/ctx.vars here — just return; the runner persists your output.
218
211
 
219
212
  // TODO: Implement business logic here
220
213