blokctl 0.6.9 → 0.6.11
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.
|
@@ -17,7 +17,7 @@ const exec = util.promisify(child_process.exec);
|
|
|
17
17
|
const HOME_DIR = `${os.homedir()}/.blok`;
|
|
18
18
|
const GITHUB_REPO_LOCAL = `${HOME_DIR}/blok`;
|
|
19
19
|
const GITHUB_REPO_REMOTE = "https://github.com/well-prado/blok.git";
|
|
20
|
-
const GITHUB_REPO_RELEASE_TAG = "v0.6.
|
|
20
|
+
const GITHUB_REPO_RELEASE_TAG = "v0.6.11";
|
|
21
21
|
fsExtra.ensureDirSync(HOME_DIR);
|
|
22
22
|
const options = {
|
|
23
23
|
baseDir: HOME_DIR,
|
|
@@ -264,15 +264,15 @@ export async function createProject(opts, version, currentPath = false, localRep
|
|
|
264
264
|
const triggerConfigs = selectedTriggers.map((kind) => createTriggerConfig(kind));
|
|
265
265
|
const mountedOnHttp = new Set();
|
|
266
266
|
if (selectedTriggers.includes("http")) {
|
|
267
|
-
for (const kind of ["sse", "websocket"]) {
|
|
267
|
+
for (const kind of ["sse", "websocket", "webhook"]) {
|
|
268
268
|
if (selectedTriggers.includes(kind))
|
|
269
269
|
mountedOnHttp.add(kind);
|
|
270
270
|
}
|
|
271
271
|
}
|
|
272
272
|
const spawnedTriggerConfigs = triggerConfigs.filter((tc) => !mountedOnHttp.has(tc.kind));
|
|
273
273
|
const primaryTrigger = selectedTriggers[0];
|
|
274
|
-
const primaryTriggerDir = primaryTrigger === "pubsub" || primaryTrigger === "queue"
|
|
275
|
-
? `${repoSource}/triggers/${primaryTrigger === "queue" ? "worker" : primaryTrigger}/template`
|
|
274
|
+
const primaryTriggerDir = primaryTrigger === "pubsub" || primaryTrigger === "queue" || primaryTrigger === "worker"
|
|
275
|
+
? `${repoSource}/triggers/${primaryTrigger === "queue" || primaryTrigger === "worker" ? "worker" : primaryTrigger}/template`
|
|
276
276
|
: `${repoSource}/triggers/${primaryTrigger}`;
|
|
277
277
|
const baseFiles = ["package.json", "tsconfig.json", ".env.example", ".gitignore", "vitest.config.ts"];
|
|
278
278
|
for (const file of baseFiles) {
|
|
@@ -318,8 +318,8 @@ export async function createProject(opts, version, currentPath = false, localRep
|
|
|
318
318
|
for (const triggerKind of selectedTriggers) {
|
|
319
319
|
const triggerDestDir = `${dirPath}/src/triggers/${triggerKind}`;
|
|
320
320
|
fsExtra.ensureDirSync(triggerDestDir);
|
|
321
|
-
if (triggerKind === "pubsub" || triggerKind === "queue") {
|
|
322
|
-
const templatePkgDir = triggerKind === "queue" ? "worker" : triggerKind;
|
|
321
|
+
if (triggerKind === "pubsub" || triggerKind === "queue" || triggerKind === "worker") {
|
|
322
|
+
const templatePkgDir = triggerKind === "queue" || triggerKind === "worker" ? "worker" : triggerKind;
|
|
323
323
|
const templateDir = `${repoSource}/triggers/${templatePkgDir}/template/src`;
|
|
324
324
|
if (fsExtra.existsSync(templateDir)) {
|
|
325
325
|
fsExtra.copySync(templateDir, triggerDestDir);
|
|
@@ -332,7 +332,7 @@ export async function createProject(opts, version, currentPath = false, localRep
|
|
|
332
332
|
if (triggerKind === "pubsub") {
|
|
333
333
|
updatePubSubProvider(triggerDestDir, pubsubProvider);
|
|
334
334
|
}
|
|
335
|
-
else if (triggerKind === "queue") {
|
|
335
|
+
else if (triggerKind === "queue" || triggerKind === "worker") {
|
|
336
336
|
updateQueueProvider(triggerDestDir, queueProvider);
|
|
337
337
|
}
|
|
338
338
|
}
|
|
@@ -383,7 +383,14 @@ export async function createProject(opts, version, currentPath = false, localRep
|
|
|
383
383
|
fsExtra.writeFileSync(`${dirPath}/src/Nodes.ts`, sharedNodesContent);
|
|
384
384
|
const sharedWorkflowsContent = generateSharedWorkflowsFile(selectedTriggers);
|
|
385
385
|
fsExtra.writeFileSync(`${dirPath}/src/Workflows.ts`, sharedWorkflowsContent);
|
|
386
|
+
const triggersWithRealTemplate = new Set(["worker", "queue", "pubsub"]);
|
|
386
387
|
for (const triggerKind of selectedTriggers) {
|
|
388
|
+
if (triggersWithRealTemplate.has(triggerKind)) {
|
|
389
|
+
continue;
|
|
390
|
+
}
|
|
391
|
+
if (mountedOnHttp.has(triggerKind)) {
|
|
392
|
+
continue;
|
|
393
|
+
}
|
|
387
394
|
const entryContent = generateTriggerEntryFile(triggerKind, selectedTriggers);
|
|
388
395
|
fsExtra.writeFileSync(`${dirPath}/src/triggers/${triggerKind}/index.ts`, entryContent);
|
|
389
396
|
}
|
|
@@ -426,7 +433,7 @@ export async function createProject(opts, version, currentPath = false, localRep
|
|
|
426
433
|
fsExtra.ensureDirSync(`${dirPath}/infra/metrics`);
|
|
427
434
|
fsExtra.copySync(`${repoSource}/infra/metrics`, `${dirPath}/infra/metrics`);
|
|
428
435
|
fsExtra.removeSync(`${dirPath}/public/metric`);
|
|
429
|
-
if (selectedTriggers.includes("queue")) {
|
|
436
|
+
if (selectedTriggers.includes("queue") || selectedTriggers.includes("worker")) {
|
|
430
437
|
fsExtra.ensureDirSync(`${dirPath}/infra/development`);
|
|
431
438
|
fsExtra.copySync(`${repoSource}/infra/development`, `${dirPath}/infra/development`);
|
|
432
439
|
}
|
|
@@ -478,7 +485,7 @@ export async function createProject(opts, version, currentPath = false, localRep
|
|
|
478
485
|
"@blokjs/trigger-websocket": "triggers/websocket",
|
|
479
486
|
"@blokjs/trigger-worker": "triggers/worker",
|
|
480
487
|
};
|
|
481
|
-
const BLOKJS_DEP_RANGE = "^0.6.
|
|
488
|
+
const BLOKJS_DEP_RANGE = "^0.6.11";
|
|
482
489
|
for (const depGroup of ["dependencies", "devDependencies", "peerDependencies"]) {
|
|
483
490
|
const deps = packageJsonContent[depGroup];
|
|
484
491
|
if (!deps)
|
|
@@ -547,7 +554,8 @@ export async function createProject(opts, version, currentPath = false, localRep
|
|
|
547
554
|
? `file:${path.resolve(repoSource, "triggers/pubsub")}`
|
|
548
555
|
: BLOKJS_DEP_RANGE;
|
|
549
556
|
}
|
|
550
|
-
|
|
557
|
+
const needsTriggerWorker = selectedTriggers.includes("queue") || selectedTriggers.includes("worker") || examples;
|
|
558
|
+
if (needsTriggerWorker) {
|
|
551
559
|
triggerPackageDeps["@blokjs/trigger-worker"] = localRepoPath
|
|
552
560
|
? `file:${path.resolve(repoSource, "triggers/worker")}`
|
|
553
561
|
: BLOKJS_DEP_RANGE;
|
|
@@ -646,6 +654,23 @@ export async function createProject(opts, version, currentPath = false, localRep
|
|
|
646
654
|
"# The plain /chat demo works without Redis; only /chat-memory needs it.",
|
|
647
655
|
"REDIS_URL=redis://127.0.0.1:6379",
|
|
648
656
|
"",
|
|
657
|
+
"# Webhook router demo (--examples + --triggers webhook) — secrets per provider.",
|
|
658
|
+
"# Stripe: copy from https://dashboard.stripe.com/webhooks (`whsec_…`).",
|
|
659
|
+
"# GitHub: set in repo Settings → Webhooks → secret field.",
|
|
660
|
+
"# Linear: workspace settings → API → Webhooks → signing secret.",
|
|
661
|
+
"# Until set, signature verification fails with 401 — that's the gate working.",
|
|
662
|
+
"STRIPE_WEBHOOK_SECRET=",
|
|
663
|
+
"GITHUB_WEBHOOK_SECRET=",
|
|
664
|
+
"LINEAR_WEBHOOK_SECRET=",
|
|
665
|
+
"",
|
|
666
|
+
"# Worker fan-out demo (--examples + --triggers worker) — POST /fanout/jobs with",
|
|
667
|
+
"# `{items: [...], tenantId?: '...'}` enqueues N worker jobs onto `fanout-jobs`.",
|
|
668
|
+
"# in-memory adapter works single-process; for cross-process set BLOK_WORKER_ADAPTER",
|
|
669
|
+
"# to nats / redis / bullmq / rabbitmq / sqs / pg-boss / kafka and supply the matching",
|
|
670
|
+
"# connection env (e.g. NATS_SERVERS=nats://127.0.0.1:4222, or REDIS_URL above).",
|
|
671
|
+
"BLOK_WORKER_ADAPTER=in-memory",
|
|
672
|
+
"NATS_SERVERS=nats://127.0.0.1:4222",
|
|
673
|
+
"",
|
|
649
674
|
].join("\n");
|
|
650
675
|
fsExtra.appendFileSync(envLocal, chatEnvBlock);
|
|
651
676
|
}
|
|
@@ -704,7 +729,7 @@ export async function createProject(opts, version, currentPath = false, localRep
|
|
|
704
729
|
console.log(` ${rc.label}: http://localhost:${rc.port}/health`);
|
|
705
730
|
}
|
|
706
731
|
}
|
|
707
|
-
if (selectedTriggers.includes("queue") && queueProvider === "redis") {
|
|
732
|
+
if ((selectedTriggers.includes("queue") || selectedTriggers.includes("worker")) && queueProvider === "redis") {
|
|
708
733
|
console.log(color.cyan("\n📦 Redis Setup (for Queue trigger):"));
|
|
709
734
|
console.log(" Start Redis with Docker:");
|
|
710
735
|
console.log(color.dim(" cd infra/development"));
|
|
@@ -712,7 +737,7 @@ export async function createProject(opts, version, currentPath = false, localRep
|
|
|
712
737
|
console.log(color.dim(" docker compose up -d redis redis-commander"));
|
|
713
738
|
console.log(" Redis Commander UI: http://localhost:8081");
|
|
714
739
|
}
|
|
715
|
-
if (selectedTriggers.includes("queue") && queueProvider === "nats") {
|
|
740
|
+
if ((selectedTriggers.includes("queue") || selectedTriggers.includes("worker")) && queueProvider === "nats") {
|
|
716
741
|
console.log(color.cyan("\n📦 NATS JetStream Setup (for Queue trigger):"));
|
|
717
742
|
console.log(" Start NATS with Docker:");
|
|
718
743
|
console.log(color.dim(" cd infra/development"));
|
|
@@ -782,8 +807,8 @@ function generateSharedWorkflowsFile(triggers) {
|
|
|
782
807
|
imports.push('import OnPubSubMessage from "./workflows/pubsub/messages/on-message";');
|
|
783
808
|
workflowEntries.push('\t"on-pubsub-message": OnPubSubMessage,');
|
|
784
809
|
}
|
|
785
|
-
else if (trigger === "queue") {
|
|
786
|
-
imports.push(
|
|
810
|
+
else if (trigger === "queue" || trigger === "worker") {
|
|
811
|
+
imports.push(`import ProcessJob from "./workflows/${trigger}/jobs/process-job";`);
|
|
787
812
|
workflowEntries.push('\t"process-job": ProcessJob,');
|
|
788
813
|
}
|
|
789
814
|
}
|
|
@@ -803,12 +828,14 @@ function generateTriggerEntryFile(triggerKind, selectedTriggers = [triggerKind])
|
|
|
803
828
|
if (triggerKind === "http") {
|
|
804
829
|
const sseAlsoSelected = selectedTriggers.includes("sse");
|
|
805
830
|
const wsAlsoSelected = selectedTriggers.includes("websocket");
|
|
806
|
-
const
|
|
831
|
+
const webhookAlsoSelected = selectedTriggers.includes("webhook");
|
|
832
|
+
const needsShared = sseAlsoSelected || wsAlsoSelected || webhookAlsoSelected;
|
|
807
833
|
const sharedHelperImports = needsShared
|
|
808
834
|
? `\nimport { NodeMap, WorkflowRegistry } from "@blokjs/runner";\nimport sharedNodes from "../../Nodes";\nimport sharedWorkflows from "../../Workflows";`
|
|
809
835
|
: "";
|
|
810
836
|
const sseImports = sseAlsoSelected ? `\nimport SSETrigger from "@blokjs/trigger-sse";` : "";
|
|
811
837
|
const wsImports = wsAlsoSelected ? `\nimport WebSocketTrigger from "@blokjs/trigger-websocket";` : "";
|
|
838
|
+
const webhookImports = webhookAlsoSelected ? `\nimport WebhookTrigger from "@blokjs/trigger-webhook";` : "";
|
|
812
839
|
const sharedBootstrapPrelude = needsShared
|
|
813
840
|
? `\n\n // Build a NodeMap from the shared Nodes record; both SSE and
|
|
814
841
|
// WebSocket triggers consume this via setNodeMap so they can
|
|
@@ -831,15 +858,15 @@ function generateTriggerEntryFile(triggerKind, selectedTriggers = [triggerKind])
|
|
|
831
858
|
for (const [name, wf] of Object.entries(sharedWorkflows)) {
|
|
832
859
|
const w = wf as {
|
|
833
860
|
name?: string;
|
|
834
|
-
trigger?: { sse?: unknown; websocket?: unknown };
|
|
835
|
-
_config?: { name?: string; trigger?: { sse?: unknown; websocket?: unknown } };
|
|
861
|
+
trigger?: { sse?: unknown; websocket?: unknown; webhook?: unknown };
|
|
862
|
+
_config?: { name?: string; trigger?: { sse?: unknown; websocket?: unknown; webhook?: unknown } };
|
|
836
863
|
};
|
|
837
864
|
const triggerCfg = w._config?.trigger ?? w.trigger;
|
|
838
865
|
if (!triggerCfg) continue;
|
|
839
|
-
if (!triggerCfg.sse && !triggerCfg.websocket) continue;
|
|
866
|
+
if (!triggerCfg.sse && !triggerCfg.websocket && !triggerCfg.webhook) continue;
|
|
840
867
|
const resolvedName = w._config?.name ?? w.name ?? name;
|
|
841
868
|
if (registry.get(resolvedName)) continue;
|
|
842
|
-
const kind = triggerCfg.sse ? "sse" : "websocket";
|
|
869
|
+
const kind = triggerCfg.sse ? "sse" : triggerCfg.websocket ? "websocket" : "webhook";
|
|
843
870
|
registry.register({
|
|
844
871
|
name: resolvedName,
|
|
845
872
|
source: \`\${kind}:\${name}\`,
|
|
@@ -876,10 +903,26 @@ function generateTriggerEntryFile(triggerKind, selectedTriggers = [triggerKind])
|
|
|
876
903
|
});
|
|
877
904
|
await wsTrigger.listen();`
|
|
878
905
|
: "";
|
|
879
|
-
const
|
|
906
|
+
const webhookBootstrap = webhookAlsoSelected
|
|
907
|
+
? `\n // Mount Webhook trigger on the HTTP process's shared Hono app.
|
|
908
|
+
// WebhookTrigger.constructor(app, httpTrigger?) mirrors SSE / WS —
|
|
909
|
+
// when an HttpTrigger is supplied, the webhook trigger registers
|
|
910
|
+
// its /webhooks/<provider> routes inside addPreCatchAllHook so they
|
|
911
|
+
// win Hono's first-match dispatch over the legacy workflow catch-
|
|
912
|
+
// all. The shared @blokjs/trigger-webhook singleton this creates
|
|
913
|
+
// is also what @blokjs/hmac-verify (and other webhook-aware helpers)
|
|
914
|
+
// look up at run time.
|
|
915
|
+
const webhookTrigger = new WebhookTrigger(this.httpTrigger.getApp(), this.httpTrigger);
|
|
916
|
+
webhookTrigger.setNodeMap({
|
|
917
|
+
nodes: subTriggerNodeMap,
|
|
918
|
+
workflows: sharedWorkflows as unknown as Parameters<typeof webhookTrigger.setNodeMap>[0]["workflows"],
|
|
919
|
+
});
|
|
920
|
+
await webhookTrigger.listen();`
|
|
921
|
+
: "";
|
|
922
|
+
const fullBootstrap = `${sharedBootstrapPrelude}${sseBootstrap}${wsBootstrap}${webhookBootstrap}`;
|
|
880
923
|
return `import { DefaultLogger } from "@blokjs/runner";
|
|
881
924
|
import { type Span, metrics, trace } from "@opentelemetry/api";
|
|
882
|
-
import HttpTrigger from "./runner/HttpTrigger";${sharedHelperImports}${sseImports}${wsImports}
|
|
925
|
+
import HttpTrigger from "./runner/HttpTrigger";${sharedHelperImports}${sseImports}${wsImports}${webhookImports}
|
|
883
926
|
|
|
884
927
|
export default class App {
|
|
885
928
|
private httpTrigger: HttpTrigger = <HttpTrigger>{};
|
|
@@ -1353,7 +1396,7 @@ function fixRunnerImportPaths(triggerDestDir, triggerKind) {
|
|
|
1353
1396
|
else if (triggerKind === "pubsub") {
|
|
1354
1397
|
fileFixes.push({ file: `${triggerDestDir}/runner/PubSubServer.ts`, up: "../../../" });
|
|
1355
1398
|
}
|
|
1356
|
-
else if (triggerKind === "queue") {
|
|
1399
|
+
else if (triggerKind === "queue" || triggerKind === "worker") {
|
|
1357
1400
|
fileFixes.push({ file: `${triggerDestDir}/runner/WorkerServer.ts`, up: "../../../" });
|
|
1358
1401
|
}
|
|
1359
1402
|
for (const { file, up } of fileFixes) {
|
|
@@ -13,7 +13,7 @@ declare const package_dev_dependencies: {
|
|
|
13
13
|
"@types/pg": string;
|
|
14
14
|
};
|
|
15
15
|
declare const python3_file = "\nfrom core.blok import BlokService\nfrom core.types.context import Context\nfrom core.types.blok_response import BlokResponse\nfrom core.types.global_error import GlobalError\nfrom typing import Any, Dict\nimport traceback\n\nclass Node(BlokService):\n def __init__(self):\n BlokService.__init__(self)\n self.input_schema = {}\n self.output_schema = {}\n\n async def handle(self, ctx: Context, inputs: Dict[str, Any]) -> BlokResponse:\n response = BlokResponse()\n\n try:\n response.setSuccess({ \"message\": \"Hello World from Python3!\" })\n except Exception as error:\n err = GlobalError(error)\n err.setCode(500)\n err.setName(self.name)\n\n stack_trace = traceback.format_exc()\n err.setStack(stack_trace)\n response.success = False\n response.setError(err)\n\n return response\n";
|
|
16
|
-
declare const examples_url = "\nExamples:\n1- Open \"workflow-docs.json\" in your browser at http://localhost:4000/workflow-docs\n2- Open \"db-manager.json\" in your browser at http://localhost:4000/db-manager\n3- Open \"dashboard-gen.json\" in your browser at http://localhost:4000/dashboard-gen\n4- Open \"countries.json\" in your browser at http://localhost:4000/countries\n5- Open \"chat.json\" in your browser at http://localhost:4000/chat (set OPENROUTER_API_KEY first)\n6- Open \"chat-memory.json\" in your browser at http://localhost:4000/chat-memory (needs OPENROUTER_API_KEY + Redis at REDIS_URL)\n\nFor more documentation, visit src/nodes/examples/README.md. The first three examples require a PostgreSQL database to function.\n";
|
|
16
|
+
declare const examples_url = "\nExamples:\n1- Open \"workflow-docs.json\" in your browser at http://localhost:4000/workflow-docs\n2- Open \"db-manager.json\" in your browser at http://localhost:4000/db-manager\n3- Open \"dashboard-gen.json\" in your browser at http://localhost:4000/dashboard-gen\n4- Open \"countries.json\" in your browser at http://localhost:4000/countries\n5- Open \"chat.json\" in your browser at http://localhost:4000/chat (set OPENROUTER_API_KEY first)\n6- Open \"chat-memory.json\" in your browser at http://localhost:4000/chat-memory (needs OPENROUTER_API_KEY + Redis at REDIS_URL)\n7- Webhook router: POST /webhooks/{stripe,github,linear} with signed bodies \u2014 set the matching *_WEBHOOK_SECRET env vars (needs --triggers webhook)\n8- LLM agent w/ tool calls: open http://localhost:4000/agent \u2014 model picks between get_weather and calculate tools (needs OPENROUTER_API_KEY + Redis)\n9- Worker fan-out: POST /fanout/jobs with body '{items:[...], tenantId?:\"...\"}' to enqueue N worker jobs (needs --triggers worker; BLOK_WORKER_ADAPTER=in-memory works single-process)\n\nFor more documentation, visit src/nodes/examples/README.md. The first three examples require a PostgreSQL database to function.\n";
|
|
17
17
|
declare const workflow_template = "\n{\n\t\"name\": \"My Workflow\",\n\t\"description\": \"What this workflow does\",\n\t\"version\": \"1.0.0\",\n\t\"trigger\": {\n\t\t\"http\": {\n\t\t\t\"method\": \"GET\",\n\t\t\t\"accept\": \"application/json\"\n\t\t}\n\t},\n\t\"steps\": [\n\t\t{\n\t\t\t\"id\": \"echo\",\n\t\t\t\"use\": \"@blokjs/respond\",\n\t\t\t\"inputs\": {\n\t\t\t\t\"body\": \"$.req.body\"\n\t\t\t}\n\t\t}\n\t]\n}\n";
|
|
18
18
|
declare const supervisord_nodejs = "\n[supervisord]\nnodaemon=true\n\n[program:nodejs_app]\ncommand=npm start\ndirectory=/app\nautostart=true\nautorestart=true\nstderr_logfile=/var/log/nodejs.err.log\nstdout_logfile=/var/log/nodejs.out.log\n";
|
|
19
19
|
declare const supervisord_python = "\n[program:python_app]\ncommand=python3 /app/.blok/runtimes/python3/server.py\ndirectory=/app\nautostart=true\nautorestart=true\nstderr_logfile=/var/log/python.err.log\nstdout_logfile=/var/log/python.out.log\n";
|
|
@@ -71,6 +71,9 @@ Examples:
|
|
|
71
71
|
4- Open "countries.json" in your browser at http://localhost:4000/countries
|
|
72
72
|
5- Open "chat.json" in your browser at http://localhost:4000/chat (set OPENROUTER_API_KEY first)
|
|
73
73
|
6- Open "chat-memory.json" in your browser at http://localhost:4000/chat-memory (needs OPENROUTER_API_KEY + Redis at REDIS_URL)
|
|
74
|
+
7- Webhook router: POST /webhooks/{stripe,github,linear} with signed bodies — set the matching *_WEBHOOK_SECRET env vars (needs --triggers webhook)
|
|
75
|
+
8- LLM agent w/ tool calls: open http://localhost:4000/agent — model picks between get_weather and calculate tools (needs OPENROUTER_API_KEY + Redis)
|
|
76
|
+
9- Worker fan-out: POST /fanout/jobs with body '{items:[...], tenantId?:"..."}' to enqueue N worker jobs (needs --triggers worker; BLOK_WORKER_ADAPTER=in-memory works single-process)
|
|
74
77
|
|
|
75
78
|
For more documentation, visit src/nodes/examples/README.md. The first three examples require a PostgreSQL database to function.
|
|
76
79
|
`;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "blokctl",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.11",
|
|
4
4
|
"author": "Deskree Technologies Inc.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"description": "cli for blok",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"keywords": ["blokctl", "cli", "blok", "blok"],
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@ai-sdk/openai": "^1.3.22",
|
|
33
|
-
"@blokjs/runner": "^0.6.
|
|
33
|
+
"@blokjs/runner": "^0.6.11",
|
|
34
34
|
"@clack/prompts": "^1.0.0",
|
|
35
35
|
"ai": "^4.3.16",
|
|
36
36
|
"better-sqlite3": "^12.6.2",
|