blokctl 0.6.20 → 0.7.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/__tests__/modular-observability.capstone.e2e.test.js +72 -0
- package/dist/commands/create/node.js +46 -66
- package/dist/commands/create/project.js +55 -9
- package/dist/commands/create/utils/Examples.d.ts +8 -20
- package/dist/commands/create/utils/Examples.js +138 -412
- package/dist/commands/dev/index.js +40 -1
- package/dist/commands/gen/appTypes.js +40 -1
- package/dist/commands/generate/NodeGenerator.d.ts +0 -2
- package/dist/commands/generate/NodeGenerator.js +0 -20
- package/dist/commands/generate/RuntimeGenerator.d.ts +0 -2
- package/dist/commands/generate/RuntimeGenerator.js +0 -19
- package/dist/commands/generate/RuntimeGenerator.test.js +0 -29
- package/dist/commands/generate/TriggerGenerator.d.ts +0 -2
- package/dist/commands/generate/TriggerGenerator.js +0 -19
- package/dist/commands/generate/WorkflowGenerator.d.ts +0 -2
- package/dist/commands/generate/WorkflowGenerator.js +0 -19
- package/dist/commands/generate/e2e/NodeGenerator.e2e.test.js +0 -12
- package/dist/commands/generate/e2e/RuntimeGenerator.e2e.test.js +0 -12
- package/dist/commands/generate/e2e/TriggerGenerator.e2e.test.js +0 -14
- package/dist/commands/monitor/monitor-component.js +5 -5
- package/dist/commands/observability/add.d.ts +2 -0
- package/dist/commands/observability/add.js +113 -0
- package/dist/commands/observability/alerting-module.test.js +43 -0
- package/dist/commands/observability/apply.d.ts +10 -0
- package/dist/commands/observability/apply.js +11 -0
- package/dist/commands/observability/descriptor.d.ts +37 -0
- package/dist/commands/observability/descriptor.js +203 -0
- package/dist/commands/observability/descriptor.test.d.ts +1 -0
- package/dist/commands/observability/descriptor.test.js +40 -0
- package/dist/commands/observability/index.d.ts +1 -0
- package/dist/commands/observability/index.js +53 -0
- package/dist/commands/observability/list.d.ts +2 -0
- package/dist/commands/observability/list.js +45 -0
- package/dist/commands/observability/logging-module.test.d.ts +1 -0
- package/dist/commands/observability/logging-module.test.js +43 -0
- package/dist/commands/observability/obs-stack-module.test.d.ts +1 -0
- package/dist/commands/observability/obs-stack-module.test.js +33 -0
- package/dist/commands/observability/remove.d.ts +2 -0
- package/dist/commands/observability/remove.js +62 -0
- package/dist/commands/observability/shared.d.ts +6 -0
- package/dist/commands/observability/shared.js +23 -0
- package/dist/commands/observability/status.d.ts +2 -0
- package/dist/commands/observability/status.js +36 -0
- package/dist/commands/observability/tracing-module.test.d.ts +1 -0
- package/dist/commands/observability/tracing-module.test.js +42 -0
- package/dist/commands/profile/index.js +7 -10
- package/dist/commands/watch/format.d.ts +23 -0
- package/dist/commands/watch/format.js +60 -0
- package/dist/commands/watch/index.d.ts +1 -0
- package/dist/commands/watch/index.js +53 -0
- package/dist/commands/watch/sse.d.ts +16 -0
- package/dist/commands/watch/sse.js +82 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +4 -0
- package/dist/services/obs-setup.d.ts +5 -0
- package/dist/services/obs-setup.js +68 -0
- package/dist/services/obs-setup.test.d.ts +1 -0
- package/dist/services/obs-setup.test.js +71 -0
- package/dist/services/obs-tiers.d.ts +9 -0
- package/dist/services/obs-tiers.js +16 -0
- package/dist/services/observability-mutations.d.ts +4 -0
- package/dist/services/observability-mutations.js +46 -0
- package/dist/services/observability-mutations.test.d.ts +1 -0
- package/dist/services/observability-mutations.test.js +57 -0
- package/dist/services/runtime-setup.d.ts +12 -1
- package/dist/services/runtime-setup.js +274 -14
- package/dist/studio-dist/assets/{index-BD8_9YPN.js → index-CnFqCRQe.js} +17 -17
- package/dist/studio-dist/index.html +1 -1
- package/package.json +3 -3
- package/dist/commands/generate/GenerationAnalytics.d.ts +0 -61
- package/dist/commands/generate/GenerationAnalytics.js +0 -163
- package/dist/commands/generate/GenerationAnalytics.test.js +0 -407
- package/dist/commands/generate/PromptVersioning.d.ts +0 -25
- package/dist/commands/generate/PromptVersioning.js +0 -71
- package/dist/commands/generate/PromptVersioning.test.js +0 -120
- /package/dist/{commands/generate/GenerationAnalytics.test.d.ts → __tests__/modular-observability.capstone.e2e.test.d.ts} +0 -0
- /package/dist/commands/{generate/PromptVersioning.test.d.ts → observability/alerting-module.test.d.ts} +0 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
5
|
+
import { parse as parseYaml } from "yaml";
|
|
6
|
+
import { resolveObservabilitySelection } from "../commands/observability/apply.js";
|
|
7
|
+
import { setupObservabilityStack } from "../services/obs-setup.js";
|
|
8
|
+
import { rewriteObservabilityEnvBlock } from "../services/observability-mutations.js";
|
|
9
|
+
import { readProjectConfig, writeProjectConfig } from "../services/runtime-setup.js";
|
|
10
|
+
function findRepoRoot() {
|
|
11
|
+
let dir = import.meta.dirname;
|
|
12
|
+
for (let i = 0; i < 8; i++) {
|
|
13
|
+
if (fs.existsSync(path.join(dir, "infra", "metrics", "docker-compose.yml")))
|
|
14
|
+
return dir;
|
|
15
|
+
const up = path.dirname(dir);
|
|
16
|
+
if (up === dir)
|
|
17
|
+
break;
|
|
18
|
+
dir = up;
|
|
19
|
+
}
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
const REPO = findRepoRoot();
|
|
23
|
+
describe("capstone — modular observability footprint", () => {
|
|
24
|
+
let tmp;
|
|
25
|
+
let envLocal;
|
|
26
|
+
beforeEach(() => {
|
|
27
|
+
tmp = fs.mkdtempSync(path.join(os.tmpdir(), "blok-capstone-"));
|
|
28
|
+
envLocal = path.join(tmp, ".env.local");
|
|
29
|
+
});
|
|
30
|
+
afterEach(() => fs.rmSync(tmp, { recursive: true, force: true }));
|
|
31
|
+
function scaffold(modules, tier) {
|
|
32
|
+
const sel = resolveObservabilitySelection(modules.filter((m) => m !== "obs-stack"), { addedAt: "2026-01-01T00:00:00.000Z", version: "0.6.0", projectDir: tmp });
|
|
33
|
+
const obsConfig = Object.keys(sel.configMap).length > 0 ? sel.configMap : undefined;
|
|
34
|
+
writeProjectConfig(tmp, [], [], obsConfig);
|
|
35
|
+
fs.writeFileSync(envLocal, rewriteObservabilityEnvBlock("", sel.envBlocks));
|
|
36
|
+
if (REPO)
|
|
37
|
+
setupObservabilityStack(REPO, tmp, tier);
|
|
38
|
+
return { added: sel.added };
|
|
39
|
+
}
|
|
40
|
+
it("a metrics+tracing subset enables ONLY those — unselected modules leave no footprint", () => {
|
|
41
|
+
scaffold(["metrics", "tracing"], "none");
|
|
42
|
+
expect(Object.keys(readProjectConfig(tmp)?.observability ?? {}).sort()).toEqual(["metrics", "tracing"]);
|
|
43
|
+
const env = fs.readFileSync(envLocal, "utf8");
|
|
44
|
+
expect(env).toContain("BLOK_METRICS_DISABLED");
|
|
45
|
+
expect(env).toContain("OTEL_EXPORTER_OTLP_ENDPOINT");
|
|
46
|
+
expect(env).not.toContain("CONSOLE_LOG_ACTIVE");
|
|
47
|
+
expect(env).not.toContain("SENTRY_DSN");
|
|
48
|
+
expect(env).not.toContain("BLOK_TRACE_STORE");
|
|
49
|
+
expect(env).not.toContain("BLOK_ALERTING_ENABLED");
|
|
50
|
+
});
|
|
51
|
+
it("selecting alerting auto-pulls its metrics dependency", () => {
|
|
52
|
+
const { added } = scaffold(["alerting"], "none");
|
|
53
|
+
expect(added).toEqual(["metrics"]);
|
|
54
|
+
expect(Object.keys(readProjectConfig(tmp)?.observability ?? {}).sort()).toEqual(["alerting", "metrics"]);
|
|
55
|
+
});
|
|
56
|
+
it("BLOK_METRICS_DISABLED round-trips inert (commented) — metrics stay ON by default", () => {
|
|
57
|
+
scaffold(["metrics"], "none");
|
|
58
|
+
for (const line of fs.readFileSync(envLocal, "utf8").split("\n")) {
|
|
59
|
+
if (/BLOK_METRICS_DISABLED=/.test(line))
|
|
60
|
+
expect(line.trim().startsWith("#")).toBe(true);
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
it("obs-stack=none copies no infra/metrics", () => {
|
|
64
|
+
scaffold(["metrics"], "none");
|
|
65
|
+
expect(fs.existsSync(path.join(tmp, "infra", "metrics"))).toBe(false);
|
|
66
|
+
});
|
|
67
|
+
it.skipIf(!REPO)("obs-stack=lite copies exactly prometheus + grafana", () => {
|
|
68
|
+
scaffold(["metrics"], "lite");
|
|
69
|
+
const compose = parseYaml(fs.readFileSync(path.join(tmp, "infra", "metrics", "docker-compose.yml"), "utf8"));
|
|
70
|
+
expect(Object.keys(compose.services ?? {}).sort()).toEqual(["grafana", "prometheus"]);
|
|
71
|
+
});
|
|
72
|
+
});
|
|
@@ -8,7 +8,7 @@ import fsExtra from "fs-extra";
|
|
|
8
8
|
import color from "picocolors";
|
|
9
9
|
import { isNonInteractive, resolveOrThrow } from "../../services/non-interactive.js";
|
|
10
10
|
import { manager as pm } from "../../services/package-manager.js";
|
|
11
|
-
import {
|
|
11
|
+
import { csharp_node_file, function_first_node_file, go_node_file, java_node_file, php_node_file, python3_file, ruby_node_file, rust_node_file, } from "./utils/Examples.js";
|
|
12
12
|
const exec = util.promisify(child_process.exec);
|
|
13
13
|
const HOME_DIR = `${os.homedir()}/.blok`;
|
|
14
14
|
function toPascalCase(name) {
|
|
@@ -252,7 +252,8 @@ export async function createNode(opts, currentPath = false) {
|
|
|
252
252
|
throw new Error("ops2");
|
|
253
253
|
}
|
|
254
254
|
fsExtra.ensureDirSync(dirPath);
|
|
255
|
-
|
|
255
|
+
const pythonNodeContent = python3_file.replace(/\{\{NODE_NAME\}\}/g, nodeName);
|
|
256
|
+
fsExtra.writeFileSync(`${dirPath}/node.py`, pythonNodeContent);
|
|
256
257
|
fsExtra.writeFileSync(`${dirPath}/__init__.py`, "");
|
|
257
258
|
}
|
|
258
259
|
if (node_runtime === "go") {
|
|
@@ -283,14 +284,13 @@ export async function createNode(opts, currentPath = false) {
|
|
|
283
284
|
throw new Error("ops2");
|
|
284
285
|
}
|
|
285
286
|
fsExtra.ensureDirSync(dirPath);
|
|
286
|
-
const
|
|
287
|
-
const
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
fsExtra.writeFileSync(`${dirPath}/go
|
|
292
|
-
|
|
293
|
-
const readmeContent = `# ${nodeName}\n\nGo-based Blok node.\n\n## Build\n\n\`\`\`bash\ndocker build -t blok-${nodeName}:latest .\n\`\`\`\n\n## Run\n\n\`\`\`bash\ndocker run -p 8080:8080 blok-${nodeName}:latest\n\`\`\`\n`;
|
|
287
|
+
const goPkg = nodeName.replace(/[^a-z0-9]/gi, "").toLowerCase();
|
|
288
|
+
const goNodeContent = go_node_file
|
|
289
|
+
.replace(/\{\{NODE_NAME_PASCAL\}\}/g, toPascalCase(nodeName))
|
|
290
|
+
.replace(/\{\{NODE_PKG\}\}/g, goPkg)
|
|
291
|
+
.replace(/\{\{NODE_NAME\}\}/g, nodeName);
|
|
292
|
+
fsExtra.writeFileSync(`${dirPath}/node.go`, goNodeContent);
|
|
293
|
+
const readmeContent = `# ${nodeName}\n\nGo-based Blok node, served by the Go runtime over gRPC.\n\nRun \`blokctl dev\` — the node is discovered under \`runtimes/go/nodes/\` and registered automatically.\n`;
|
|
294
294
|
fsExtra.writeFileSync(`${dirPath}/README.md`, readmeContent);
|
|
295
295
|
}
|
|
296
296
|
if (node_runtime === "java") {
|
|
@@ -321,15 +321,14 @@ export async function createNode(opts, currentPath = false) {
|
|
|
321
321
|
throw new Error("ops2");
|
|
322
322
|
}
|
|
323
323
|
fsExtra.ensureDirSync(dirPath);
|
|
324
|
-
const srcDir = `${dirPath}/src/main/java/com/blok/nodes`;
|
|
324
|
+
const srcDir = `${dirPath}/src/main/java/com/blok/blok/nodes`;
|
|
325
325
|
fsExtra.ensureDirSync(srcDir);
|
|
326
|
-
const
|
|
327
|
-
const
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
fsExtra.writeFileSync(`${
|
|
331
|
-
|
|
332
|
-
const readmeContent = `# ${nodeName}\n\nJava-based Blok node.\n\n## Build\n\n\`\`\`bash\ndocker build -t blok-${nodeName}:latest .\n\`\`\`\n\n## Run\n\n\`\`\`bash\ndocker run -p 8080:8080 blok-${nodeName}:latest\n\`\`\`\n`;
|
|
326
|
+
const pascalName = toPascalCase(nodeName);
|
|
327
|
+
const javaNodeContent = java_node_file
|
|
328
|
+
.replace(/\{\{NODE_NAME_PASCAL\}\}/g, pascalName)
|
|
329
|
+
.replace(/\{\{NODE_NAME\}\}/g, nodeName);
|
|
330
|
+
fsExtra.writeFileSync(`${srcDir}/${pascalName}Node.java`, javaNodeContent);
|
|
331
|
+
const readmeContent = `# ${nodeName}\n\nJava-based Blok node, compiled into the Java runtime jar.\n\nRun \`blokctl dev\` — the node is discovered under \`runtimes/java/nodes/\` and registered automatically.\n`;
|
|
333
332
|
fsExtra.writeFileSync(`${dirPath}/README.md`, readmeContent);
|
|
334
333
|
}
|
|
335
334
|
if (node_runtime === "rust") {
|
|
@@ -360,18 +359,12 @@ export async function createNode(opts, currentPath = false) {
|
|
|
360
359
|
throw new Error("ops2");
|
|
361
360
|
}
|
|
362
361
|
fsExtra.ensureDirSync(dirPath);
|
|
363
|
-
const srcDir = `${dirPath}/src`;
|
|
364
|
-
fsExtra.ensureDirSync(srcDir);
|
|
365
362
|
const pascalName = toPascalCase(nodeName);
|
|
366
363
|
const rustNodeContent = rust_node_file
|
|
367
|
-
.replace(/\{\{
|
|
368
|
-
.replace(/\{\{
|
|
369
|
-
|
|
370
|
-
const
|
|
371
|
-
fsExtra.writeFileSync(`${srcDir}/main.rs`, rustNodeContent);
|
|
372
|
-
fsExtra.writeFileSync(`${dirPath}/Cargo.toml`, rustCargoContent);
|
|
373
|
-
fsExtra.writeFileSync(`${dirPath}/Dockerfile`, rustDockerContent);
|
|
374
|
-
const readmeContent = `# ${nodeName}\n\nRust-based Blok node.\n\n## Build\n\n\`\`\`bash\ndocker build -t blok-${nodeName}:latest .\n\`\`\`\n\n## Run\n\n\`\`\`bash\ndocker run -p 8080:8080 blok-${nodeName}:latest\n\`\`\`\n`;
|
|
364
|
+
.replace(/\{\{NODE_NAME_PASCAL\}\}/g, pascalName)
|
|
365
|
+
.replace(/\{\{NODE_NAME\}\}/g, nodeName);
|
|
366
|
+
fsExtra.writeFileSync(`${dirPath}/node.rs`, rustNodeContent);
|
|
367
|
+
const readmeContent = `# ${nodeName}\n\nRust-based Blok node, served by the Rust runtime over gRPC.\n\nRun \`blokctl dev\` — the node is discovered under \`runtimes/rust/nodes/\` and registered automatically.\n`;
|
|
375
368
|
fsExtra.writeFileSync(`${dirPath}/README.md`, readmeContent);
|
|
376
369
|
}
|
|
377
370
|
if (node_runtime === "csharp") {
|
|
@@ -382,7 +375,15 @@ export async function createNode(opts, currentPath = false) {
|
|
|
382
375
|
fsExtra.ensureDirSync(currentDir);
|
|
383
376
|
}
|
|
384
377
|
const currentNodesDir = `${currentDir}/nodes`;
|
|
385
|
-
|
|
378
|
+
if (!skipPrompts) {
|
|
379
|
+
fsExtra.ensureDirSync(currentNodesDir);
|
|
380
|
+
}
|
|
381
|
+
else {
|
|
382
|
+
const nodeDirExists = fsExtra.existsSync(currentNodesDir);
|
|
383
|
+
if (!nodeDirExists) {
|
|
384
|
+
fsExtra.ensureDirSync(currentNodesDir);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
386
387
|
dirPath = path.join(currentNodesDir, nodeName);
|
|
387
388
|
}
|
|
388
389
|
if (!skipPrompts)
|
|
@@ -392,16 +393,12 @@ export async function createNode(opts, currentPath = false) {
|
|
|
392
393
|
throw new Error("ops2");
|
|
393
394
|
}
|
|
394
395
|
fsExtra.ensureDirSync(dirPath);
|
|
395
|
-
const srcDir = `${dirPath}/src/Nodes`;
|
|
396
|
-
fsExtra.ensureDirSync(srcDir);
|
|
397
396
|
const pascalName = toPascalCase(nodeName);
|
|
398
|
-
const csNodeContent = csharp_node_file
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
fsExtra.writeFileSync(`${
|
|
402
|
-
|
|
403
|
-
fsExtra.writeFileSync(`${dirPath}/Dockerfile`, csDockerContent);
|
|
404
|
-
const readmeContent = `# ${nodeName}\n\nC#/.NET-based Blok node.\n\n## Build\n\n\`\`\`bash\ndocker build -t blok-${nodeName}:latest .\n\`\`\`\n\n## Run\n\n\`\`\`bash\ndocker run -p 8080:8080 blok-${nodeName}:latest\n\`\`\`\n`;
|
|
397
|
+
const csNodeContent = csharp_node_file
|
|
398
|
+
.replace(/\{\{NODE_NAME_PASCAL\}\}/g, pascalName)
|
|
399
|
+
.replace(/\{\{NODE_NAME\}\}/g, nodeName);
|
|
400
|
+
fsExtra.writeFileSync(`${dirPath}/${pascalName}Node.cs`, csNodeContent);
|
|
401
|
+
const readmeContent = `# ${nodeName}\n\nC#/.NET-based Blok node, served by the C# runtime over gRPC.\n\nRun \`blokctl dev\` — the node is discovered under \`runtimes/csharp/nodes/\` and registered automatically.\n`;
|
|
405
402
|
fsExtra.writeFileSync(`${dirPath}/README.md`, readmeContent);
|
|
406
403
|
}
|
|
407
404
|
if (node_runtime === "php") {
|
|
@@ -428,12 +425,8 @@ export async function createNode(opts, currentPath = false) {
|
|
|
428
425
|
const phpNodeContent = php_node_file
|
|
429
426
|
.replace(/\{\{NODE_NAME\}\}/g, nodeName)
|
|
430
427
|
.replace(/\{\{NODE_NAME_PASCAL\}\}/g, pascalName);
|
|
431
|
-
const phpComposerContent = php_composer_file.replace(/\{\{NODE_NAME\}\}/g, nodeName);
|
|
432
|
-
const phpDockerContent = php_dockerfile.replace(/\{\{NODE_NAME\}\}/g, nodeName);
|
|
433
428
|
fsExtra.writeFileSync(`${srcDir}/${pascalName}Node.php`, phpNodeContent);
|
|
434
|
-
|
|
435
|
-
fsExtra.writeFileSync(`${dirPath}/Dockerfile`, phpDockerContent);
|
|
436
|
-
const readmeContent = `# ${nodeName}\n\nPHP-based Blok node.\n\n## Build\n\n\`\`\`bash\ndocker build -t blok-${nodeName}:latest .\n\`\`\`\n\n## Run\n\n\`\`\`bash\ndocker run -p 8080:8080 blok-${nodeName}:latest\n\`\`\`\n`;
|
|
429
|
+
const readmeContent = `# ${nodeName}\n\nPHP-based Blok node, served by the PHP runtime over gRPC.\n\nRun \`blokctl dev\` — the node is discovered under \`runtimes/php/nodes/\` and registered automatically.\n`;
|
|
437
430
|
fsExtra.writeFileSync(`${dirPath}/README.md`, readmeContent);
|
|
438
431
|
}
|
|
439
432
|
if (node_runtime === "ruby") {
|
|
@@ -454,18 +447,11 @@ export async function createNode(opts, currentPath = false) {
|
|
|
454
447
|
throw new Error("ops2");
|
|
455
448
|
}
|
|
456
449
|
fsExtra.ensureDirSync(dirPath);
|
|
457
|
-
const libDir = `${dirPath}/lib/nodes`;
|
|
458
|
-
fsExtra.ensureDirSync(libDir);
|
|
459
|
-
const pascalName = toPascalCase(nodeName);
|
|
460
450
|
const rubyNodeContent = ruby_node_file
|
|
461
|
-
.replace(/\{\{
|
|
462
|
-
.replace(/\{\{
|
|
463
|
-
|
|
464
|
-
const
|
|
465
|
-
fsExtra.writeFileSync(`${libDir}/${nodeName.replace(/-/g, "_")}.rb`, rubyNodeContent);
|
|
466
|
-
fsExtra.writeFileSync(`${dirPath}/Gemfile`, rubyGemContent);
|
|
467
|
-
fsExtra.writeFileSync(`${dirPath}/Dockerfile`, rubyDockerContent);
|
|
468
|
-
const readmeContent = `# ${nodeName}\n\nRuby-based Blok node.\n\n## Build\n\n\`\`\`bash\ndocker build -t blok-${nodeName}:latest .\n\`\`\`\n\n## Run\n\n\`\`\`bash\ndocker run -p 8080:8080 blok-${nodeName}:latest\n\`\`\`\n`;
|
|
451
|
+
.replace(/\{\{NODE_NAME_PASCAL\}\}/g, toPascalCase(nodeName))
|
|
452
|
+
.replace(/\{\{NODE_NAME\}\}/g, nodeName);
|
|
453
|
+
fsExtra.writeFileSync(`${dirPath}/node.rb`, rubyNodeContent);
|
|
454
|
+
const readmeContent = `# ${nodeName}\n\nRuby-based Blok node, served by the Ruby runtime over gRPC.\n\nRun \`blokctl dev\` — the node is discovered under \`runtimes/ruby/nodes/\` and registered automatically.\n`;
|
|
469
455
|
fsExtra.writeFileSync(`${dirPath}/README.md`, readmeContent);
|
|
470
456
|
}
|
|
471
457
|
if (!skipPrompts)
|
|
@@ -486,33 +472,27 @@ export async function createNode(opts, currentPath = false) {
|
|
|
486
472
|
}
|
|
487
473
|
if (!currentPath && node_runtime === "go") {
|
|
488
474
|
console.log(`\nNavigate to the node directory by running: cd runtimes/go/nodes/${nodeName}`);
|
|
489
|
-
console.log(`\
|
|
490
|
-
console.log(`Run the container: docker run -p 8080:8080 blok-${nodeName}:latest`);
|
|
475
|
+
console.log(`\nRun "blokctl dev" — the Go runtime discovers and registers this node automatically.`);
|
|
491
476
|
}
|
|
492
477
|
if (!currentPath && node_runtime === "java") {
|
|
493
478
|
console.log(`\nNavigate to the node directory by running: cd runtimes/java/nodes/${nodeName}`);
|
|
494
|
-
console.log(`\
|
|
495
|
-
console.log(`Run the container: docker run -p 8080:8080 blok-${nodeName}:latest`);
|
|
479
|
+
console.log(`\nRun "blokctl dev" — the Java runtime discovers and registers this node automatically.`);
|
|
496
480
|
}
|
|
497
481
|
if (!currentPath && node_runtime === "rust") {
|
|
498
482
|
console.log(`\nNavigate to the node directory by running: cd runtimes/rust/nodes/${nodeName}`);
|
|
499
|
-
console.log(`\
|
|
500
|
-
console.log(`Run the container: docker run -p 8080:8080 blok-${nodeName}:latest`);
|
|
483
|
+
console.log(`\nRun "blokctl dev" — the Rust runtime discovers and registers this node automatically.`);
|
|
501
484
|
}
|
|
502
485
|
if (!currentPath && node_runtime === "csharp") {
|
|
503
486
|
console.log(`\nNavigate to the node directory by running: cd runtimes/csharp/nodes/${nodeName}`);
|
|
504
|
-
console.log(`\
|
|
505
|
-
console.log(`Run the container: docker run -p 8080:8080 blok-${nodeName}:latest`);
|
|
487
|
+
console.log(`\nRun "blokctl dev" — the C# runtime discovers and registers this node automatically.`);
|
|
506
488
|
}
|
|
507
489
|
if (!currentPath && node_runtime === "php") {
|
|
508
490
|
console.log(`\nNavigate to the node directory by running: cd runtimes/php/nodes/${nodeName}`);
|
|
509
|
-
console.log(`\
|
|
510
|
-
console.log(`Run the container: docker run -p 8080:8080 blok-${nodeName}:latest`);
|
|
491
|
+
console.log(`\nRun "blokctl dev" — the PHP runtime discovers and registers this node automatically.`);
|
|
511
492
|
}
|
|
512
493
|
if (!currentPath && node_runtime === "ruby") {
|
|
513
494
|
console.log(`\nNavigate to the node directory by running: cd runtimes/ruby/nodes/${nodeName}`);
|
|
514
|
-
console.log(`\
|
|
515
|
-
console.log(`Run the container: docker run -p 8080:8080 blok-${nodeName}:latest`);
|
|
495
|
+
console.log(`\nRun "blokctl dev" — the Ruby runtime discovers and registers this node automatically.`);
|
|
516
496
|
}
|
|
517
497
|
console.log("\nFor more documentation, visit https://blok.build/docs/d/core-concepts/nodes");
|
|
518
498
|
}
|
|
@@ -8,16 +8,21 @@ import fsExtra from "fs-extra";
|
|
|
8
8
|
import color from "picocolors";
|
|
9
9
|
import simpleGit from "simple-git";
|
|
10
10
|
import { isNonInteractive, parseCommaSeparated, resolveOrThrow } from "../../services/non-interactive.js";
|
|
11
|
+
import { setupObservabilityStack } from "../../services/obs-setup.js";
|
|
12
|
+
import { parseObsTier } from "../../services/obs-tiers.js";
|
|
13
|
+
import { rewriteObservabilityEnvBlock } from "../../services/observability-mutations.js";
|
|
11
14
|
import { manager as pm } from "../../services/package-manager.js";
|
|
12
15
|
import { detectRuntimes } from "../../services/runtime-detector.js";
|
|
13
16
|
import { createTriggerConfig, generateRuntimeEnvVars, generateSupervisordConfig, generateTriggerEnvVars, generateTriggerSupervisordConfig, setupRuntime, writeProjectConfig, } from "../../services/runtime-setup.js";
|
|
14
17
|
import { computeDefaultConstraint } from "../../services/semver-utils.js";
|
|
18
|
+
import { resolveObservabilitySelection } from "../observability/apply.js";
|
|
19
|
+
import { OBSERVABILITY_MODULE_IDS, allObservabilityModules, getObservabilityModule, } from "../observability/descriptor.js";
|
|
15
20
|
import { agents_md, claude_md, examples_url, node_file, package_dependencies, package_dev_dependencies, } from "./utils/Examples.js";
|
|
16
21
|
const exec = util.promisify(child_process.exec);
|
|
17
22
|
const HOME_DIR = `${os.homedir()}/.blok`;
|
|
18
23
|
const GITHUB_REPO_LOCAL = `${HOME_DIR}/blok`;
|
|
19
24
|
const GITHUB_REPO_REMOTE = "https://github.com/well-prado/blok.git";
|
|
20
|
-
const GITHUB_REPO_RELEASE_TAG = "v0.
|
|
25
|
+
const GITHUB_REPO_RELEASE_TAG = "v0.7.0";
|
|
21
26
|
const RUNTIME_HELLO_EXAMPLES = {
|
|
22
27
|
go: "runtime-go-hello.ts",
|
|
23
28
|
rust: "runtime-rust-hello.ts",
|
|
@@ -53,6 +58,15 @@ export async function createProject(opts, version, currentPath = false, localRep
|
|
|
53
58
|
let pubsubProvider = opts.pubsubProvider || "gcp";
|
|
54
59
|
let queueProvider = opts.queueProvider || "kafka";
|
|
55
60
|
let explicitQueueProvider = Boolean(opts.queueProvider);
|
|
61
|
+
let selectedObsTier = opts.obsStack ? parseObsTier(opts.obsStack) : "none";
|
|
62
|
+
let selectedObsModules = opts.observability
|
|
63
|
+
? parseCommaSeparated(opts.observability).map((s) => s.trim().toLowerCase())
|
|
64
|
+
: [];
|
|
65
|
+
for (const id of selectedObsModules) {
|
|
66
|
+
if (id !== "obs-stack" && !getObservabilityModule(id)) {
|
|
67
|
+
throw new Error(`Invalid --observability "${id}". Known: ${OBSERVABILITY_MODULE_IDS.join(", ")}.`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
56
70
|
let detectedRuntimes = [];
|
|
57
71
|
if (!skipPrompts) {
|
|
58
72
|
console.log(figlet.textSync("blok CLI".toUpperCase(), {
|
|
@@ -168,6 +182,27 @@ export async function createProject(opts, version, currentPath = false, localRep
|
|
|
168
182
|
initialValues: ["node"],
|
|
169
183
|
required: true,
|
|
170
184
|
}),
|
|
185
|
+
obsStack: () => opts.obsStack
|
|
186
|
+
? Promise.resolve(opts.obsStack)
|
|
187
|
+
: p.select({
|
|
188
|
+
message: "Observability dev stack?",
|
|
189
|
+
options: [
|
|
190
|
+
{ label: "None", value: "none", hint: "no infra/metrics — boots standalone (default)" },
|
|
191
|
+
{ label: "Lite", value: "lite", hint: "Prometheus + Grafana only" },
|
|
192
|
+
{ label: "Full", value: "full", hint: "Prometheus, Grafana, Loki, Tempo, Alloy, …" },
|
|
193
|
+
],
|
|
194
|
+
initialValue: "none",
|
|
195
|
+
}),
|
|
196
|
+
observability: () => opts.observability
|
|
197
|
+
? Promise.resolve(parseCommaSeparated(opts.observability))
|
|
198
|
+
: p.multiselect({
|
|
199
|
+
message: "Observability modules (optional — none by default)",
|
|
200
|
+
options: allObservabilityModules()
|
|
201
|
+
.filter((m) => m.id !== "obs-stack")
|
|
202
|
+
.map((m) => ({ value: m.id, label: m.label, hint: m.description })),
|
|
203
|
+
initialValues: [],
|
|
204
|
+
required: false,
|
|
205
|
+
}),
|
|
171
206
|
selectedManager: () => resolveSelectedManager(),
|
|
172
207
|
}, {
|
|
173
208
|
onCancel: () => {
|
|
@@ -181,6 +216,8 @@ export async function createProject(opts, version, currentPath = false, localRep
|
|
|
181
216
|
explicitQueueProvider = blokctlProject.queueProvider != null;
|
|
182
217
|
queueProvider = blokctlProject.queueProvider || "kafka";
|
|
183
218
|
selectedRuntimeKinds = blokctlProject.runtimes;
|
|
219
|
+
selectedObsTier = parseObsTier(blokctlProject.obsStack);
|
|
220
|
+
selectedObsModules = blokctlProject.observability ?? [];
|
|
184
221
|
selectedManager = blokctlProject.selectedManager;
|
|
185
222
|
const unavailableSelected = selectedRuntimeKinds.filter((kind) => {
|
|
186
223
|
if (kind === "node")
|
|
@@ -286,7 +323,7 @@ export async function createProject(opts, version, currentPath = false, localRep
|
|
|
286
323
|
const primaryTriggerDir = primaryTrigger === "pubsub" || primaryTrigger === "queue" || primaryTrigger === "worker"
|
|
287
324
|
? `${repoSource}/triggers/${primaryTrigger === "queue" || primaryTrigger === "worker" ? "worker" : primaryTrigger}/template`
|
|
288
325
|
: `${repoSource}/triggers/${primaryTrigger}`;
|
|
289
|
-
const baseFiles = ["package.json", "tsconfig.json", ".env.example", ".gitignore"
|
|
326
|
+
const baseFiles = ["package.json", "tsconfig.json", ".env.example", ".gitignore"];
|
|
290
327
|
for (const file of baseFiles) {
|
|
291
328
|
const src = `${primaryTriggerDir}/${file}`;
|
|
292
329
|
if (fsExtra.existsSync(src)) {
|
|
@@ -441,9 +478,7 @@ export async function createProject(opts, version, currentPath = false, localRep
|
|
|
441
478
|
catch (error) {
|
|
442
479
|
console.error(`Failed to change ownership of directory ${dirPath}:`, error);
|
|
443
480
|
}
|
|
444
|
-
|
|
445
|
-
fsExtra.ensureDirSync(`${dirPath}/infra/metrics`);
|
|
446
|
-
fsExtra.copySync(`${repoSource}/infra/metrics`, `${dirPath}/infra/metrics`);
|
|
481
|
+
setupObservabilityStack(repoSource, dirPath, selectedObsTier);
|
|
447
482
|
fsExtra.removeSync(`${dirPath}/public/metric`);
|
|
448
483
|
if (selectedTriggers.includes("queue") || selectedTriggers.includes("worker")) {
|
|
449
484
|
fsExtra.ensureDirSync(`${dirPath}/infra/development`);
|
|
@@ -515,7 +550,7 @@ export async function createProject(opts, version, currentPath = false, localRep
|
|
|
515
550
|
"@blokjs/trigger-websocket": "triggers/websocket",
|
|
516
551
|
"@blokjs/trigger-worker": "triggers/worker",
|
|
517
552
|
};
|
|
518
|
-
const BLOKJS_DEP_RANGE = "^0.
|
|
553
|
+
const BLOKJS_DEP_RANGE = "^0.7.0";
|
|
519
554
|
for (const depGroup of ["dependencies", "devDependencies", "peerDependencies"]) {
|
|
520
555
|
const deps = packageJsonContent[depGroup];
|
|
521
556
|
if (!deps)
|
|
@@ -571,6 +606,8 @@ export async function createProject(opts, version, currentPath = false, localRep
|
|
|
571
606
|
...packageJsonContent.devDependencies,
|
|
572
607
|
blokctl: blokctlRef,
|
|
573
608
|
};
|
|
609
|
+
packageJsonContent.scripts = Object.fromEntries(Object.entries(packageJsonContent.scripts).filter(([s]) => s !== "test" && s !== "test:dev"));
|
|
610
|
+
packageJsonContent.devDependencies = Object.fromEntries(Object.entries(packageJsonContent.devDependencies).filter(([d]) => d !== "vitest" && !d.startsWith("@vitest/")));
|
|
574
611
|
const providerDeps = getProviderDependencies(selectedTriggers, pubsubProvider, queueProvider, explicitQueueProvider);
|
|
575
612
|
if (Object.keys(providerDeps).length > 0) {
|
|
576
613
|
packageJsonContent.dependencies = {
|
|
@@ -673,7 +710,16 @@ export async function createProject(opts, version, currentPath = false, localRep
|
|
|
673
710
|
fsExtra.appendFileSync(envLocal, envVars);
|
|
674
711
|
}
|
|
675
712
|
}
|
|
676
|
-
|
|
713
|
+
const obsSelection = resolveObservabilitySelection(selectedObsModules.filter((id) => id !== "obs-stack"), { addedAt: new Date().toISOString(), version, projectDir: dirPath });
|
|
714
|
+
if (obsSelection.added.length > 0) {
|
|
715
|
+
p.log.info(`Auto-enabling required observability dependencies: ${obsSelection.added.join(", ")}`);
|
|
716
|
+
}
|
|
717
|
+
const obsConfigMap = Object.keys(obsSelection.configMap).length > 0 ? obsSelection.configMap : undefined;
|
|
718
|
+
writeProjectConfig(dirPath, runtimeConfigs, spawnedTriggerConfigs, obsConfigMap);
|
|
719
|
+
if (obsSelection.envBlocks.some((b) => b.trim())) {
|
|
720
|
+
const current = fsExtra.existsSync(envLocal) ? fsExtra.readFileSync(envLocal, "utf8") : "";
|
|
721
|
+
fsExtra.writeFileSync(envLocal, rewriteObservabilityEnvBlock(current, obsSelection.envBlocks));
|
|
722
|
+
}
|
|
677
723
|
if (triggerConfigs.length > 0) {
|
|
678
724
|
const triggerEnvVars = generateTriggerEnvVars(triggerConfigs);
|
|
679
725
|
fsExtra.appendFileSync(envLocal, triggerEnvVars);
|
|
@@ -868,10 +914,10 @@ function generateSharedWorkflowsFile(triggers, runtimeKinds = [], examples = fal
|
|
|
868
914
|
}
|
|
869
915
|
const importSection = imports.length > 0 ? `${imports.join("\n")}\n` : "";
|
|
870
916
|
const entriesSection = workflowEntries.length > 0 ? workflowEntries.join("\n") : "\t// Add your workflows here";
|
|
871
|
-
return `import type {
|
|
917
|
+
return `import type { WorkflowV2Builder } from "@blokjs/helper";
|
|
872
918
|
|
|
873
919
|
${importSection}
|
|
874
|
-
const workflows: Record<string,
|
|
920
|
+
const workflows: Record<string, WorkflowV2Builder> = {
|
|
875
921
|
${entriesSection}
|
|
876
922
|
};
|
|
877
923
|
|